Code

Merge branch 'maint'
[git.git] / git-gui.sh
index 5635eeae360932b45bcf7fd44bad5ecbea010ef3..ca391136c8df2236f0de91617e6a417ece29bb9e 100755 (executable)
@@ -6,11 +6,12 @@
        echo 'git-gui version @@GITGUI_VERSION@@'; \
        exit; \
  fi; \
- exec wish "$0" -- "$@"
+ argv0=$0; \
+ exec wish "$argv0" -- "$@"
 
 set appvers {@@GITGUI_VERSION@@}
 set copyright {
-Copyright © 2006, 2007 Shawn Pearce, et. al.
+Copyright Â© 2006, 2007 Shawn Pearce, et. al.
 
 This program is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
@@ -37,11 +38,13 @@ if {[catch {package require Tcl 8.4} err]
        tk_messageBox \
                -icon error \
                -type ok \
-               -title "git-gui: fatal error" \
+               -title [mc "git-gui: fatal error"] \
                -message $err
        exit 1
 }
 
+catch {rename send {}} ; # What an evil concept...
+
 ######################################################################
 ##
 ## locate our library
@@ -84,7 +87,20 @@ if {![catch {set _verbose $env(GITGUI_VERBOSE)}]} {
 ## http://www.gnu.org/software/gettext/manual/html_node/Tcl.html
 
 package require msgcat
-namespace import ::msgcat::mc
+
+proc mc {fmt args} {
+       set fmt [::msgcat::mc $fmt]
+       set cmk [string first @@ $fmt]
+       if {$cmk > 0} {
+               set fmt [string range $fmt 0 [expr {$cmk - 1}]]
+       }
+       return [eval [list format $fmt] $args]
+}
+
+proc strcat {args} {
+       return [join $args {}]
+}
+
 ::msgcat::mcload $oguimsg
 unset oguimsg
 
@@ -92,7 +108,7 @@ unset oguimsg
 ##
 ## read only globals
 
-set _appname [lindex [file split $argv0] end]
+set _appname {Git Gui}
 set _gitdir {}
 set _gitexec {}
 set _reponame {}
@@ -289,7 +305,7 @@ proc _git_cmd {name} {
                        set s [gets $f]
                        close $f
 
-                       switch -glob -- $s {
+                       switch -glob -- [lindex $s 0] {
                        #!*sh     { set i sh     }
                        #!*perl   { set i perl   }
                        #!*python { set i python }
@@ -303,7 +319,7 @@ proc _git_cmd {name} {
                        if {$interp eq {}} {
                                error "git-$name requires $i (not in PATH)"
                        }
-                       set v [list $interp $p]
+                       set v [concat [list $interp] [lrange $s 1 end] [list $p]]
                } else {
                        # Assume it is builtin to git somehow and we
                        # aren't actually able to see a file for it.
@@ -319,7 +335,7 @@ proc _which {what} {
        global env _search_exe _search_path
 
        if {$_search_path eq {}} {
-               if {[is_Cygwin]} {
+               if {[is_Cygwin] && [regexp {^(/|\.:)} $env(PATH)]} {
                        set _search_path [split [exec cygpath \
                                --windows \
                                --path \
@@ -495,6 +511,113 @@ proc tk_optionMenu {w varName args} {
        return $m
 }
 
+proc rmsel_tag {text} {
+       $text tag conf sel \
+               -background [$text cget -background] \
+               -foreground [$text cget -foreground] \
+               -borderwidth 0
+       $text tag conf in_sel -background lightgray
+       bind $text <Motion> break
+       return $text
+}
+
+set root_exists 0
+bind . <Visibility> {
+       bind . <Visibility> {}
+       set root_exists 1
+}
+
+if {[is_Windows]} {
+       wm iconbitmap . -default $oguilib/git-gui.ico
+}
+
+######################################################################
+##
+## config defaults
+
+set cursor_ptr arrow
+font create font_diff -family Courier -size 10
+font create font_ui
+catch {
+       label .dummy
+       eval font configure font_ui [font actual [.dummy cget -font]]
+       destroy .dummy
+}
+
+font create font_uiitalic
+font create font_uibold
+font create font_diffbold
+font create font_diffitalic
+
+foreach class {Button Checkbutton Entry Label
+               Labelframe Listbox Menu Message
+               Radiobutton Spinbox Text} {
+       option add *$class.font font_ui
+}
+unset class
+
+if {[is_Windows] || [is_MacOSX]} {
+       option add *Menu.tearOff 0
+}
+
+if {[is_MacOSX]} {
+       set M1B M1
+       set M1T Cmd
+} else {
+       set M1B Control
+       set M1T Ctrl
+}
+
+proc bind_button3 {w cmd} {
+       bind $w <Any-Button-3> $cmd
+       if {[is_MacOSX]} {
+               # Mac OS X sends Button-2 on right click through three-button mouse,
+               # or through trackpad right-clicking (two-finger touch + click).
+               bind $w <Any-Button-2> $cmd
+               bind $w <Control-Button-1> $cmd
+       }
+}
+
+proc apply_config {} {
+       global repo_config font_descs
+
+       foreach option $font_descs {
+               set name [lindex $option 0]
+               set font [lindex $option 1]
+               if {[catch {
+                       foreach {cn cv} $repo_config(gui.$name) {
+                               font configure $font $cn $cv -weight normal
+                       }
+                       } err]} {
+                       error_popup [strcat [mc "Invalid font specified in %s:" "gui.$name"] "\n\n$err"]
+               }
+               foreach {cn cv} [font configure $font] {
+                       font configure ${font}bold $cn $cv
+                       font configure ${font}italic $cn $cv
+               }
+               font configure ${font}bold -weight bold
+               font configure ${font}italic -slant italic
+       }
+}
+
+set default_config(merge.diffstat) true
+set default_config(merge.summary) false
+set default_config(merge.verbosity) 2
+set default_config(user.name) {}
+set default_config(user.email) {}
+
+set default_config(gui.matchtrackingbranch) false
+set default_config(gui.pruneduringfetch) false
+set default_config(gui.trustmtime) false
+set default_config(gui.diffcontext) 5
+set default_config(gui.newbranchtemplate) {}
+set default_config(gui.fontui) [font configure font_ui]
+set default_config(gui.fontdiff) [font configure font_diff]
+set font_descs {
+       {fontui   font_ui   {mc "Main Font"}}
+       {fontdiff font_diff {mc "Diff/Console Font"}}
+}
+
 ######################################################################
 ##
 ## find git
@@ -502,7 +625,11 @@ proc tk_optionMenu {w varName args} {
 set _git  [_which git]
 if {$_git eq {}} {
        catch {wm withdraw .}
-       error_popup [mc "Cannot find git in PATH."]
+       tk_messageBox \
+               -icon error \
+               -type ok \
+               -title [mc "git-gui: fatal error"] \
+               -message [mc "Cannot find git in PATH."]
        exit 1
 }
 
@@ -515,7 +642,7 @@ if {[catch {set _git_version [git --version]} err]} {
        tk_messageBox \
                -icon error \
                -type ok \
-               -title "git-gui: fatal error" \
+               -title [mc "git-gui: fatal error"] \
                -message "Cannot determine Git version:
 
 $err
@@ -528,8 +655,8 @@ if {![regsub {^git version } $_git_version {} _git_version]} {
        tk_messageBox \
                -icon error \
                -type ok \
-               -title "git-gui: fatal error" \
-               -message [append [mc "Cannot parse Git version string:"] "\n\n$_git_version"]
+               -title [mc "git-gui: fatal error"] \
+               -message [strcat [mc "Cannot parse Git version string:"] "\n\n$_git_version"]
        exit 1
 }
 
@@ -538,6 +665,7 @@ 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
+regsub {\.[a-zA-Z]+\.[0-9]+$} $_git_version {} _git_version
 
 if {![regexp {^[1-9]+(\.[0-9]+)+$} $_git_version]} {
        catch {wm withdraw .}
@@ -610,7 +738,7 @@ if {[git-version < 1.5]} {
        tk_messageBox \
                -icon error \
                -type ok \
-               -title "git-gui: fatal error" \
+               -title [mc "git-gui: fatal error"] \
                -message "[appname] requires Git 1.5.0 or later.
 
 You are using [git-version]:
@@ -629,7 +757,7 @@ if {[catch {set fd [open $idx r]} err]} {
        tk_messageBox \
                -icon error \
                -type ok \
-               -title "git-gui: fatal error" \
+               -title [mc "git-gui: fatal error"] \
                -message $err
        exit 1
 }
@@ -662,7 +790,7 @@ unset -nocomplain idx fd
 ##
 ## feature option selection
 
-if {[regexp {^git-(.+)$} [appname] _junk subcommand]} {
+if {[regexp {^git-(.+)$} [file tail $argv0] _junk subcommand]} {
        unset _junk
 } else {
        set subcommand gui
@@ -710,35 +838,35 @@ if {[catch {
                set _gitdir [git rev-parse --git-dir]
                set _prefix [git rev-parse --show-prefix]
        } err]} {
-       catch {wm withdraw .}
-       error_popup [append [mc "Cannot find the git directory:"] "\n\n$err"]
-       exit 1
+       load_config 1
+       apply_config
+       choose_repository::pick
 }
 if {![file isdirectory $_gitdir] && [is_Cygwin]} {
-       catch {set _gitdir [exec cygpath --unix $_gitdir]}
+       catch {set _gitdir [exec cygpath --windows $_gitdir]}
 }
 if {![file isdirectory $_gitdir]} {
        catch {wm withdraw .}
-       error_popup [append [mc "Git directory not found:"] "\n\n$_gitdir"]
+       error_popup [strcat [mc "Git directory not found:"] "\n\n$_gitdir"]
        exit 1
 }
 if {$_prefix ne {}} {
        regsub -all {[^/]+/} $_prefix ../ cdup
        if {[catch {cd $cdup} err]} {
                catch {wm withdraw .}
-               error_popup "Cannot move to top of working directory:\n\n$err"
+               error_popup [strcat [mc "Cannot move to top of working directory:"] "\n\n$err"]
                exit 1
        }
        unset cdup
 } elseif {![is_enabled bare]} {
        if {[lindex [file split $_gitdir] end] ne {.git}} {
                catch {wm withdraw .}
-               error_popup [append [mc "Cannot use funny .git directory:"] "\n\n$_gitdir"]
+               error_popup [strcat [mc "Cannot use funny .git directory:"] "\n\n$_gitdir"]
                exit 1
        }
        if {[catch {cd [file dirname $_gitdir]} err]} {
                catch {wm withdraw .}
-               error_popup [append [mc "No working directory"] " [file dirname $_gitdir]:\n\n$err"]
+               error_popup [strcat [mc "No working directory"] " [file dirname $_gitdir]:\n\n$err"]
                exit 1
        }
 }
@@ -898,6 +1026,35 @@ proc rescan {after {honor_trustmtime 1}} {
        }
 }
 
+if {[is_Cygwin]} {
+       set is_git_info_link {}
+       set is_git_info_exclude {}
+       proc have_info_exclude {} {
+               global is_git_info_link is_git_info_exclude
+
+               if {$is_git_info_link eq {}} {
+                       set is_git_info_link [file isfile [gitdir info.lnk]]
+               }
+
+               if {$is_git_info_link} {
+                       if {$is_git_info_exclude eq {}} {
+                               if {[catch {exec test -f [gitdir info exclude]}]} {
+                                       set is_git_info_exclude 0
+                               } else {
+                                       set is_git_info_exclude 1
+                               }
+                       }
+                       return $is_git_info_exclude
+               } else {
+                       return [file readable [gitdir info exclude]]
+               }
+       }
+} else {
+       proc have_info_exclude {} {
+               return [file readable [gitdir info exclude]]
+       }
+}
+
 proc rescan_stage2 {fd after} {
        global rescan_active buf_rdi buf_rdf buf_rlo
 
@@ -908,9 +1065,8 @@ proc rescan_stage2 {fd after} {
        }
 
        set ls_others [list --exclude-per-directory=.gitignore]
-       set info_exclude [gitdir info exclude]
-       if {[file readable $info_exclude]} {
-               lappend ls_others "--exclude-from=$info_exclude"
+       if {[have_info_exclude]} {
+               lappend ls_others "--exclude-from=[gitdir info exclude]"
        }
        set user_exclude [get_config core.excludesfile]
        if {$user_exclude ne {} && [file readable $user_exclude]} {
@@ -1027,7 +1183,11 @@ proc read_ls_others {fd after} {
        set pck [split $buf_rlo "\0"]
        set buf_rlo [lindex $pck end]
        foreach p [lrange $pck 0 end-1] {
-               merge_state [encoding convertfrom $p] ?O
+               set p [encoding convertfrom $p]
+               if {[string index $p end] eq {/}} {
+                       set p [string range $p 0 end-1]
+               }
+               merge_state $p ?O
        }
        rescan_done $fd buf_rlo $after
 }
@@ -1084,11 +1244,17 @@ proc mapdesc {state path} {
 }
 
 proc ui_status {msg} {
-       $::main_status show $msg
+       global main_status
+       if {[info exists main_status]} {
+               $main_status show $msg
+       }
 }
 
 proc ui_ready {{test {}}} {
-       $::main_status show [mc "Ready."] $test
+       global main_status
+       if {[info exists main_status]} {
+               $main_status show [mc "Ready."] $test
+       }
 }
 
 proc escape_path {path} {
@@ -1386,16 +1552,6 @@ unset i
 ##
 ## util
 
-proc bind_button3 {w cmd} {
-       bind $w <Any-Button-3> $cmd
-       if {[is_MacOSX]} {
-               # Mac OS X sends Button-2 on right click through three-button mouse,
-               # or through trackpad right-clicking (two-finger touch + click).
-               bind $w <Any-Button-2> $cmd
-               bind $w <Control-Button-1> $cmd
-       }
-}
-
 proc scrollbar2many {list mode args} {
        foreach w $list {eval $w $mode $args}
 }
@@ -1428,7 +1584,27 @@ proc do_gitk {revs} {
        if {! [file exists $exe]} {
                error_popup [mc "Unable to start gitk:\n\n%s does not exist" $exe]
        } else {
+               global env
+
+               if {[info exists env(GIT_DIR)]} {
+                       set old_GIT_DIR $env(GIT_DIR)
+               } else {
+                       set old_GIT_DIR {}
+               }
+
+               set pwd [pwd]
+               cd [file dirname [gitdir]]
+               set env(GIT_DIR) [file tail [gitdir]]
+
                eval exec $cmd $revs &
+
+               if {$old_GIT_DIR eq {}} {
+                       unset env(GIT_DIR)
+               } else {
+                       set env(GIT_DIR) $old_GIT_DIR
+               }
+               cd $pwd
+
                ui_status $::starting_gitk_msg
                after 10000 {
                        ui_ready $starting_gitk_msg
@@ -1597,87 +1773,10 @@ proc add_range_to_selection {w x y} {
 
 ######################################################################
 ##
-## config defaults
-
-set cursor_ptr arrow
-font create font_diff -family Courier -size 10
-font create font_ui
-catch {
-       label .dummy
-       eval font configure font_ui [font actual [.dummy cget -font]]
-       destroy .dummy
-}
-
-font create font_uiitalic
-font create font_uibold
-font create font_diffbold
-font create font_diffitalic
-
-foreach class {Button Checkbutton Entry Label
-               Labelframe Listbox Menu Message
-               Radiobutton Spinbox Text} {
-       option add *$class.font font_ui
-}
-unset class
-
-if {[is_Windows] || [is_MacOSX]} {
-       option add *Menu.tearOff 0
-}
-
-if {[is_MacOSX]} {
-       set M1B M1
-       set M1T Cmd
-} else {
-       set M1B Control
-       set M1T Ctrl
-}
-
-proc apply_config {} {
-       global repo_config font_descs
-
-       foreach option $font_descs {
-               set name [lindex $option 0]
-               set font [lindex $option 1]
-               if {[catch {
-                       foreach {cn cv} $repo_config(gui.$name) {
-                               font configure $font $cn $cv
-                       }
-                       } err]} {
-                       error_popup [append [mc "Invalid font specified in %s:" "gui.$name"] "\n\n$err"]
-               }
-               foreach {cn cv} [font configure $font] {
-                       font configure ${font}bold $cn $cv
-                       font configure ${font}italic $cn $cv
-               }
-               font configure ${font}bold -weight bold
-               font configure ${font}italic -slant italic
-       }
-}
-
-set default_config(merge.diffstat) true
-set default_config(merge.summary) false
-set default_config(merge.verbosity) 2
-set default_config(user.name) {}
-set default_config(user.email) {}
+## ui construction
 
-set default_config(gui.matchtrackingbranch) false
-set default_config(gui.pruneduringfetch) false
-set default_config(gui.trustmtime) false
-set default_config(gui.diffcontext) 5
-set default_config(gui.newbranchtemplate) {}
-set default_config(gui.fontui) [font configure font_ui]
-set default_config(gui.fontdiff) [font configure font_diff]
-set font_descs {
-       {fontui   font_ui   {mc "Main Font"}}
-       {fontdiff font_diff {mc "Diff/Console Font"}}
-}
 load_config 0
 apply_config
-
-######################################################################
-##
-## ui construction
-
 set ui_comm {}
 
 # -- Menu Bar
@@ -1689,7 +1788,7 @@ if {[is_enabled branch]} {
        .mbar add cascade -label [mc Branch] -menu .mbar.branch
 }
 if {[is_enabled multicommit] || [is_enabled singlecommit]} {
-       .mbar add cascade -label [mc Commit] -menu .mbar.commit
+       .mbar add cascade -label [mc Commit@@noun] -menu .mbar.commit
 }
 if {[is_enabled transport]} {
        .mbar add cascade -label [mc Merge] -menu .mbar.merge
@@ -1876,7 +1975,7 @@ if {[is_enabled multicommit] || [is_enabled singlecommit]} {
                -command do_signoff \
                -accelerator $M1T-S
 
-       .mbar.commit add command -label [mc Commit] \
+       .mbar.commit add command -label [mc Commit@@verb] \
                -command do_commit \
                -accelerator $M1T-Return
        lappend disable_on_lock \
@@ -1977,12 +2076,6 @@ if {$browser ne {}} {
 }
 unset browser doc_path doc_url
 
-set root_exists 0
-bind . <Visibility> {
-       bind . <Visibility> {}
-       set root_exists 1
-}
-
 # -- Standard bindings
 #
 wm protocol . WM_DELETE_WINDOW do_quit
@@ -2062,7 +2155,7 @@ blame {
        }
        blame   {
                if {$head eq {} && ![file exists $path]} {
-                       puts stderr "fatal: cannot stat path $path: No such file or directory"
+                       puts stderr [mc "fatal: cannot stat path %s: No such file or directory" $path]
                        exit 1
                }
                blame::new $head $path
@@ -2074,7 +2167,8 @@ citool -
 gui {
        if {[llength $argv] != 0} {
                puts -nonewline stderr "usage: $argv0"
-               if {$subcommand ne {gui} && [appname] ne "git-$subcommand"} {
+               if {$subcommand ne {gui}
+                       && [file tail $argv0] ne "git-$subcommand"} {
                        puts -nonewline stderr " $subcommand"
                }
                puts stderr {}
@@ -2153,8 +2247,8 @@ pack $ui_workdir -side left -fill both -expand 1
 .vpane.files add .vpane.files.workdir -sticky nsew
 
 foreach i [list $ui_index $ui_workdir] {
-       $i tag conf in_diff -background lightgray
-       $i tag conf in_sel  -background lightgray
+       rmsel_tag $i
+       $i tag conf in_diff -background [$i tag cget in_sel -background]
 }
 unset i
 
@@ -2192,7 +2286,7 @@ button .vpane.lower.commarea.buttons.signoff -text [mc "Sign Off"] \
        -command do_signoff
 pack .vpane.lower.commarea.buttons.signoff -side top -fill x
 
-button .vpane.lower.commarea.buttons.commit -text [mc Commit] \
+button .vpane.lower.commarea.buttons.commit -text [mc Commit@@verb] \
        -command do_commit
 pack .vpane.lower.commarea.buttons.commit -side top -fill x
 lappend disable_on_lock \
@@ -2461,20 +2555,17 @@ proc popup_diff_menu {ctxm x y X Y} {
        set ::cursorX $x
        set ::cursorY $y
        if {$::ui_index eq $::current_diff_side} {
-               set s normal
                set l [mc "Unstage Hunk From Commit"]
        } else {
-               if {$current_diff_path eq {}
-                       || ![info exists file_states($current_diff_path)]
-                       || {_O} eq [lindex $file_states($current_diff_path) 0]} {
-                       set s disabled
-               } else {
-                       set s normal
-               }
                set l [mc "Stage Hunk For Commit"]
        }
-       if {$::is_3way_diff} {
+       if {$::is_3way_diff
+               || $current_diff_path eq {}
+               || ![info exists file_states($current_diff_path)]
+               || {_O} eq [lindex $file_states($current_diff_path) 0]} {
                set s disabled
+       } else {
+               set s normal
        }
        $ctxm entryconf $::ui_diff_applyhunk -state $s -label $l
        tk_popup $ctxm $X $Y
@@ -2576,13 +2667,13 @@ focus -force $ui_comm
 if {[is_Cygwin]} {
        set ignored_env 0
        set suggest_user {}
-       set msg "Possible environment issues exist.
+       set msg [mc "Possible environment issues exist.
 
 The following environment variables are probably
 going to be ignored by any Git subprocess run
-by [appname]:
+by %s:
 
-"
+" [appname]]
        foreach name [array names env] {
                switch -regexp -- $name {
                {^GIT_INDEX_FILE$} -
@@ -2606,18 +2697,18 @@ by [appname]:
                }
        }
        if {$ignored_env > 0} {
-               append msg "
+               append msg [mc "
 This is due to a known issue with the
-Tcl binary distributed by Cygwin."
+Tcl binary distributed by Cygwin."]
 
                if {$suggest_user ne {}} {
-                       append msg "
+                       append msg [mc "
 
-A good replacement for $suggest_user
+A good replacement for %s
 is placing values for the user.name and
 user.email settings into your personal
 ~/.gitconfig file.
-"
+" $suggest_user]
                }
                warn_popup $msg
        }