}
proc reponame {} {
- global _reponame
- return $_reponame
+ return $::_reponame
}
proc is_MacOSX {} {
- global tcl_platform tk_library
if {[tk windowingsystem] eq {aqua}} {
return 1
}
}
proc is_Windows {} {
- global tcl_platform
- if {$tcl_platform(platform) eq {windows}} {
+ if {$::tcl_platform(platform) eq {windows}} {
return 1
}
return 0
}
proc is_Cygwin {} {
- global tcl_platform _iscygwin
+ global _iscygwin
if {$_iscygwin eq {}} {
- if {$tcl_platform(platform) eq {windows}} {
+ if {$::tcl_platform(platform) eq {windows}} {
if {[catch {set p [exec cygpath --windir]} err]} {
set _iscygwin 0
} else {
set p [gitexec git-$name$::_search_exe]
if {[file exists $p]} {
set v [list $p]
- } elseif {[is_Cygwin]} {
- # On Cygwin git is a proper Cygwin program and knows
- # how to properly restart the Cygwin environment and
- # spawn its non-.exe support program.
+ } elseif {[is_Windows] && [file exists [gitexec git-$name]]} {
+ # Try to determine what sort of magic will make
+ # git-$name go and do its thing, because native
+ # Tcl on Windows doesn't know it.
#
- set v [list $::_git $name]
- } elseif {[is_Windows]
- && $::_sh ne {}
- && [file exists [gitexec git-$name]]} {
- # Assume this is a UNIX shell script. We can
- # probably execute it through a Bourne shell.
- #
- set v [list $::_sh [gitexec git-$name]]
+ set p [gitexec git-$name]
+ set f [open $p r]
+ set s [gets $f]
+ close $f
+
+ switch -glob -- $s {
+ #!*sh { set i sh }
+ #!*perl { set i perl }
+ #!*python { set i python }
+ default { error "git-$name is not supported: $s" }
+ }
+
+ upvar #0 _$i interp
+ if {![info exists interp]} {
+ set interp [_which $i]
+ }
+ if {$interp eq {}} {
+ error "git-$name requires $i (not in PATH)"
+ }
+ set v [list $interp $p]
} else {
# Assume it is builtin to git somehow and we
# aren't actually able to see a file for it.
return {}
}
+proc _lappend_nice {cmd_var} {
+ global _nice
+ upvar $cmd_var cmd
+
+ if {![info exists _nice]} {
+ set _nice [_which nice]
+ }
+ if {$_nice ne {}} {
+ lappend cmd $_nice
+ }
+}
+
proc git {args} {
set opt [list exec]
while {1} {
switch -- [lindex $args 0] {
--nice {
- global _nice
- if {$_nice ne {}} {
- lappend opt $_nice
- }
+ _lappend_nice opt
}
default {
return [eval $opt $cmdp $args]
}
+proc _open_stdout_stderr {cmd} {
+ if {[catch {
+ set fd [open $cmd r]
+ } err]} {
+ if { [lindex $cmd end] eq {2>@1}
+ && $err eq {can not find channel named "1"}
+ } {
+ # Older versions of Tcl 8.4 don't have this 2>@1 IO
+ # redirect operator. Fallback to |& cat for those.
+ # The command was not actually started, so its safe
+ # to try to start it a second time.
+ #
+ set fd [open [concat \
+ [lrange $cmd 0 end-1] \
+ [list |& cat] \
+ ] r]
+ } else {
+ error $err
+ }
+ }
+ fconfigure $fd -eofchar {}
+ return $fd
+}
+
proc git_read {args} {
set opt [list |]
while {1} {
switch -- [lindex $args 0] {
--nice {
- global _nice
- if {$_nice ne {}} {
- lappend opt $_nice
- }
+ _lappend_nice opt
}
--stderr {
set cmdp [_git_cmd [lindex $args 0]]
set args [lrange $args 1 end]
- if {[catch {
- set fd [open [concat $opt $cmdp $args] r]
- } err]} {
- if { [lindex $args end] eq {2>@1}
- && $err eq {can not find channel named "1"}
- } {
- # Older versions of Tcl 8.4 don't have this 2>@1 IO
- # redirect operator. Fallback to |& cat for those.
- # The command was not actually started, so its safe
- # to try to start it a second time.
- #
- set fd [open [concat \
- $opt \
- $cmdp \
- [lrange $args 0 end-1] \
- [list |& cat] \
- ] r]
- } else {
- error $err
- }
- }
- return $fd
+ return [_open_stdout_stderr [concat $opt $cmdp $args]]
}
proc git_write {args} {
while {1} {
switch -- [lindex $args 0] {
--nice {
- global _nice
- if {$_nice ne {}} {
- lappend opt $_nice
- }
+ _lappend_nice opt
}
default {
return [open [concat $opt $cmdp $args] w]
}
+proc sq {value} {
+ regsub -all ' $value "'\\''" value
+ return "'$value'"
+}
+
proc load_current_branch {} {
global current_branch is_detached
error_popup "Cannot find git in PATH."
exit 1
}
-set _nice [_which nice]
-set _sh [_which sh]
######################################################################
##
error_popup "Cannot parse Git version string:\n\n$_git_version"
exit 1
}
+
+set _real_git_version $_git_version
+regsub -- {-dirty$} $_git_version {} _git_version
regsub {\.[0-9]+\.g[0-9a-f]+$} $_git_version {} _git_version
regsub {\.rc[0-9]+$} $_git_version {} _git_version
+regsub {\.GIT$} $_git_version {} _git_version
+
+if {![regexp {^[1-9]+(\.[0-9]+)+$} $_git_version]} {
+ catch {wm withdraw .}
+ if {[tk_messageBox \
+ -icon warning \
+ -type yesno \
+ -default no \
+ -title "[appname]: warning" \
+ -message "Git version cannot be determined.
+
+$_git claims it is version '$_real_git_version'.
+
+[appname] requires at least Git 1.5.0 or later.
+
+Assume '$_real_git_version' is version 1.5.0?
+"] eq {yes}} {
+ set _git_version 1.5.0
+ } else {
+ exit 1
+ }
+}
+unset _real_git_version
proc git-version {args} {
global _git_version
exit 1
}
+######################################################################
+##
+## feature option selection
+
+if {[regexp {^git-(.+)$} [appname] _junk subcommand]} {
+ unset _junk
+} else {
+ set subcommand gui
+}
+if {$subcommand eq {gui.sh}} {
+ set subcommand gui
+}
+if {$subcommand eq {gui} && [llength $argv] > 0} {
+ set subcommand [lindex $argv 0]
+ set argv [lrange $argv 1 end]
+}
+
+enable_option multicommit
+enable_option branch
+enable_option transport
+disable_option bare
+
+switch -- $subcommand {
+browser -
+blame {
+ enable_option bare
+
+ disable_option multicommit
+ disable_option branch
+ disable_option transport
+}
+citool {
+ enable_option singlecommit
+
+ disable_option multicommit
+ disable_option branch
+ disable_option transport
+}
+}
+
######################################################################
##
## repository setup
error_popup "Git directory not found:\n\n$_gitdir"
exit 1
}
-if {[lindex [file split $_gitdir] end] ne {.git}} {
- catch {wm withdraw .}
- error_popup "Cannot use funny .git directory:\n\n$_gitdir"
- exit 1
+if {![is_enabled bare]} {
+ if {[lindex [file split $_gitdir] end] ne {.git}} {
+ catch {wm withdraw .}
+ error_popup "Cannot use funny .git directory:\n\n$_gitdir"
+ exit 1
+ }
+ if {[catch {cd [file dirname $_gitdir]} err]} {
+ catch {wm withdraw .}
+ error_popup "No working directory [file dirname $_gitdir]:\n\n$err"
+ exit 1
+ }
}
-if {[catch {cd [file dirname $_gitdir]} err]} {
- catch {wm withdraw .}
- error_popup "No working directory [file dirname $_gitdir]:\n\n$err"
- exit 1
+set _reponame [file split [file normalize $_gitdir]]
+if {[lindex $_reponame end] eq {.git}} {
+ set _reponame [lindex $_reponame end-1]
+} else {
+ set _reponame [lindex $_reponame end]
}
-set _reponame [lindex [file split \
- [file normalize [file dirname $_gitdir]]] \
- end]
######################################################################
##
if {[catch {set fd [open $f r]}]} {
return 0
}
+ fconfigure $fd -eofchar {}
set content [string trim [read $fd]]
close $fd
regsub -all -line {[ \r\t]+$} $content {} content
proc do_quit {} {
global ui_comm is_quitting repo_config commit_type
+ global GITGUI_BCK_exists GITGUI_BCK_i
if {$is_quitting} return
set is_quitting 1
# -- Stash our current commit buffer.
#
set save [gitdir GITGUI_MSG]
- set msg [string trim [$ui_comm get 0.0 end]]
- regsub -all -line {[ \r\t]+$} $msg {} msg
- if {(![string match amend* $commit_type]
- || [$ui_comm edit modified])
- && $msg ne {}} {
- catch {
- set fd [open $save w]
- puts -nonewline $fd $msg
- close $fd
- }
+ if {$GITGUI_BCK_exists && ![$ui_comm edit modified]} {
+ file rename -force [gitdir GITGUI_BCK] $save
+ set GITGUI_BCK_exists 0
} else {
- catch {file delete $save}
+ set msg [string trim [$ui_comm get 0.0 end]]
+ regsub -all -line {[ \r\t]+$} $msg {} msg
+ if {(![string match amend* $commit_type]
+ || [$ui_comm edit modified])
+ && $msg ne {}} {
+ catch {
+ set fd [open $save w]
+ puts -nonewline $fd $msg
+ close $fd
+ }
+ } else {
+ catch {file delete $save}
+ }
+ }
+
+ # -- Remove our editor backup, its not needed.
+ #
+ after cancel $GITGUI_BCK_i
+ if {$GITGUI_BCK_exists} {
+ catch {file delete [gitdir GITGUI_BCK]}
}
# -- Stash our current window geometry into this repository.
load_config 0
apply_config
-######################################################################
-##
-## feature option selection
-
-if {[regexp {^git-(.+)$} [appname] _junk subcommand]} {
- unset _junk
-} else {
- set subcommand gui
-}
-if {$subcommand eq {gui.sh}} {
- set subcommand gui
-}
-if {$subcommand eq {gui} && [llength $argv] > 0} {
- set subcommand [lindex $argv 0]
- set argv [lrange $argv 1 end]
-}
-
-enable_option multicommit
-enable_option branch
-enable_option transport
-
-switch -- $subcommand {
-browser -
-blame {
- disable_option multicommit
- disable_option branch
- disable_option transport
-}
-citool {
- enable_option singlecommit
-
- disable_option multicommit
- disable_option branch
- disable_option transport
-}
-}
-
######################################################################
##
## ui construction
menu .mbar.repository
.mbar.repository add command \
- -label {Browse Current Branch} \
+ -label {Browse Current Branch's Files} \
-command {browser::new $current_branch}
-trace add variable current_branch write ".mbar.repository entryconf [.mbar.repository index last] -label \"Browse \$current_branch\" ;#"
+trace add variable current_branch write ".mbar.repository entryconf [.mbar.repository index last] -label \"Browse \$current_branch's Files\" ;#"
+.mbar.repository add command \
+ -label {Browse Branch Files...} \
+ -command browser_open::dialog
.mbar.repository add separator
.mbar.repository add command \
- -label {Visualize Current Branch} \
+ -label {Visualize Current Branch's History} \
-command {do_gitk $current_branch}
-trace add variable current_branch write ".mbar.repository entryconf [.mbar.repository index last] -label \"Visualize \$current_branch\" ;#"
+trace add variable current_branch write ".mbar.repository entryconf [.mbar.repository index last] -label \"Visualize \$current_branch's History\" ;#"
.mbar.repository add command \
- -label {Visualize All Branches} \
+ -label {Visualize All Branch History} \
-command {do_gitk --all}
.mbar.repository add separator
if {[is_enabled branch]} {
menu .mbar.merge
.mbar.merge add command -label {Local Merge...} \
- -command merge::dialog
+ -command merge::dialog \
+ -accelerator $M1T-M
lappend disable_on_lock \
[list .mbar.merge entryconf [.mbar.merge index last] -state]
.mbar.merge add command -label {Abort Merge...} \
-command merge::reset_hard
lappend disable_on_lock \
[list .mbar.merge entryconf [.mbar.merge index last] -state]
-
}
# -- Transport Menu
# -- Tools Menu
#
- if {[is_Cygwin] && [file exists /usr/local/miga/lib/gui-miga]} {
+ if {[is_Cygwin]
+ && [is_enabled multicommit]
+ && [file exists /usr/local/miga/lib/gui-miga]} {
proc do_miga {} {
if {![lock_index update]} return
set cmd [list sh --login -c "/usr/local/miga/lib/gui-miga \"[pwd]\""]
# -- Not a normal commit type invocation? Do that instead!
#
switch -- $subcommand {
-browser {
- set subcommand_args {rev?}
- switch [llength $argv] {
- 0 { load_current_branch }
- 1 {
- set current_branch [lindex $argv 0]
- if {[regexp {^[0-9a-f]{1,39}$} $current_branch]} {
- if {[catch {
- set current_branch \
- [git rev-parse --verify $current_branch]
- } err]} {
- puts stderr $err
- exit 1
- }
- }
- }
- default usage
- }
- browser::new $current_branch
- return
-}
+browser -
blame {
- set subcommand_args {rev? path?}
+ set subcommand_args {rev? path}
+ if {$argv eq {}} usage
set head {}
set path {}
set is_path 0
} elseif {$head eq {}} {
if {$head ne {}} usage
set head $a
+ set is_path 1
} else {
usage
}
}
unset is_path
+ if {$head ne {} && $path eq {}} {
+ set path $_prefix$head
+ set head {}
+ }
+
if {$head eq {}} {
load_current_branch
} else {
set current_branch $head
}
- if {$path eq {}} usage
- blame::new $head $path
+ switch -- $subcommand {
+ browser {
+ if {$head eq {}} {
+ if {$path ne {} && [file isdirectory $path]} {
+ set head $current_branch
+ } else {
+ set head $path
+ set path {}
+ }
+ }
+ browser::new $head $path
+ }
+ blame {
+ if {$head eq {} && ![file exists $path]} {
+ puts stderr "fatal: cannot stat path $path: No such file or directory"
+ exit 1
+ }
+ blame::new $head $path
+ }
+ }
return
}
citool -
bind . <$M1B-Key-N> branch_create::dialog
bind . <$M1B-Key-o> branch_checkout::dialog
bind . <$M1B-Key-O> branch_checkout::dialog
+ bind . <$M1B-Key-m> merge::dialog
+ bind . <$M1B-Key-M> merge::dialog
}
if {[is_enabled transport]} {
bind . <$M1B-Key-p> do_push_anywhere
populate_push_menu
}
-# -- Only suggest a gc run if we are going to stay running.
-#
-if {[is_enabled multicommit]} {
- set object_limit 2000
- if {[is_Windows]} {set object_limit 200}
- regexp {^([0-9]+) objects,} [git count-objects] _junk objects_current
- if {$objects_current >= $object_limit} {
- if {[ask_popup \
- "This repository currently has $objects_current loose objects.
+if {[winfo exists $ui_comm]} {
+ set GITGUI_BCK_exists [load_message GITGUI_BCK]
+
+ # -- If both our backup and message files exist use the
+ # newer of the two files to initialize the buffer.
+ #
+ if {$GITGUI_BCK_exists} {
+ set m [gitdir GITGUI_MSG]
+ if {[file isfile $m]} {
+ if {[file mtime [gitdir GITGUI_BCK]] > [file mtime $m]} {
+ catch {file delete [gitdir GITGUI_MSG]}
+ } else {
+ $ui_comm delete 0.0 end
+ $ui_comm edit reset
+ $ui_comm edit modified false
+ catch {file delete [gitdir GITGUI_BCK]}
+ set GITGUI_BCK_exists 0
+ }
+ }
+ unset m
+ }
+
+ proc backup_commit_buffer {} {
+ global ui_comm GITGUI_BCK_exists
-To maintain optimal performance it is strongly recommended that you compress the database when more than $object_limit loose objects exist.
+ set m [$ui_comm edit modified]
+ if {$m || $GITGUI_BCK_exists} {
+ set msg [string trim [$ui_comm get 0.0 end]]
+ regsub -all -line {[ \r\t]+$} $msg {} msg
+
+ if {$msg eq {}} {
+ if {$GITGUI_BCK_exists} {
+ catch {file delete [gitdir GITGUI_BCK]}
+ set GITGUI_BCK_exists 0
+ }
+ } elseif {$m} {
+ catch {
+ set fd [open [gitdir GITGUI_BCK] w]
+ puts -nonewline $fd $msg
+ close $fd
+ set GITGUI_BCK_exists 1
+ }
+ }
-Compress the database now?"] eq yes} {
- do_gc
+ $ui_comm edit modified false
}
+
+ set ::GITGUI_BCK_i [after 2000 backup_commit_buffer]
}
- unset object_limit _junk objects_current
+
+ backup_commit_buffer
}
lock_index begin-read
+if {![winfo ismapped .]} {
+ wm deiconify .
+}
after 1 do_rescan
+if {[is_enabled multicommit]} {
+ after 1000 hint_gc
+}