Code

git-gui: Don't crash in citool mode on initial commit.
[git.git] / git-gui.sh
index 09c1b74e753a26094612a35cd6b40a86f131def3..f84ba3382b3078ee6de45bddfacd650973068303 100755 (executable)
@@ -2,9 +2,9 @@
 # Tcl ignores the next line -*- tcl -*- \
 exec wish "$0" -- "$@"
 
-set appvers {@@GIT_VERSION@@}
+set appvers {@@GITGUI_VERSION@@}
 set copyright {
-Copyright © 2006, 2007 Shawn Pearce, Paul Mackerras.
+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
@@ -19,6 +19,9 @@ GNU General Public License for more details.
 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA}
+set gitgui_credits {
+Paul Mackerras
+}
 
 ######################################################################
 ##
@@ -46,7 +49,7 @@ proc gitdir {args} {
 proc gitexec {args} {
        global _gitexec
        if {$_gitexec eq {}} {
-               if {[catch {set _gitexec [exec git --exec-path]} err]} {
+               if {[catch {set _gitexec [git --exec-path]} err]} {
                        error "Git not installed?\n\n$err"
                }
        }
@@ -93,6 +96,22 @@ proc is_Cygwin {} {
        return $_iscygwin
 }
 
+proc is_enabled {option} {
+       global enabled_options
+       if {[catch {set on $enabled_options($option)}]} {return 0}
+       return $on
+}
+
+proc enable_option {option} {
+       global enabled_options
+       set enabled_options($option) 1
+}
+
+proc disable_option {option} {
+       global enabled_options
+       set enabled_options($option) 0
+}
+
 ######################################################################
 ##
 ## config
@@ -124,7 +143,7 @@ proc load_config {include_global} {
        array unset global_config
        if {$include_global} {
                catch {
-                       set fd_rc [open "| git repo-config --global --list" r]
+                       set fd_rc [open "| git config --global --list" r]
                        while {[gets $fd_rc line] >= 0} {
                                if {[regexp {^([^=]+)=(.*)$} $line line name value]} {
                                        if {[is_many_config $name]} {
@@ -140,7 +159,7 @@ proc load_config {include_global} {
 
        array unset repo_config
        catch {
-               set fd_rc [open "| git repo-config --list" r]
+               set fd_rc [open "| git config --list" r]
                while {[gets $fd_rc line] >= 0} {
                        if {[regexp {^([^=]+)=(.*)$} $line line name value]} {
                                if {[is_many_config $name]} {
@@ -186,14 +205,14 @@ proc save_config {} {
                set value $global_config_new($name)
                if {$value ne $global_config($name)} {
                        if {$value eq $default_config($name)} {
-                               catch {exec git repo-config --global --unset $name}
+                               catch {git config --global --unset $name}
                        } else {
                                regsub -all "\[{}\]" $value {"} value
-                               exec git repo-config --global $name $value
+                               git config --global $name $value
                        }
                        set global_config($name) $value
                        if {$value eq $repo_config($name)} {
-                               catch {exec git repo-config --unset $name}
+                               catch {git config --unset $name}
                                set repo_config($name) $value
                        }
                }
@@ -203,16 +222,24 @@ proc save_config {} {
                set value $repo_config_new($name)
                if {$value ne $repo_config($name)} {
                        if {$value eq $global_config($name)} {
-                               catch {exec git repo-config --unset $name}
+                               catch {git config --unset $name}
                        } else {
                                regsub -all "\[{}\]" $value {"} value
-                               exec git repo-config $name $value
+                               git config $name $value
                        }
                        set repo_config($name) $value
                }
        }
 }
 
+######################################################################
+##
+## handy utils
+
+proc git {args} {
+       return [eval exec git $args]
+}
+
 proc error_popup {msg} {
        set title [appname]
        if {[reponame] ne {}} {
@@ -271,12 +298,44 @@ proc ask_popup {msg} {
                -message $msg]
 }
 
+######################################################################
+##
+## version check
+
+set req_maj 1
+set req_min 5
+
+if {[catch {set v [git --version]} err]} {
+       catch {wm withdraw .}
+       error_popup "Cannot determine Git version:
+
+$err
+
+[appname] requires Git $req_maj.$req_min or later."
+       exit 1
+}
+if {[regexp {^git version (\d+)\.(\d+)} $v _junk act_maj act_min]} {
+       if {$act_maj < $req_maj
+               || ($act_maj == $req_maj && $act_min < $req_min)} {
+               catch {wm withdraw .}
+               error_popup "[appname] requires Git $req_maj.$req_min or later.
+
+You are using $v."
+               exit 1
+       }
+} else {
+       catch {wm withdraw .}
+       error_popup "Cannot parse Git version string:\n\n$v"
+       exit 1
+}
+unset -nocomplain v _junk act_maj act_min req_maj req_min
+
 ######################################################################
 ##
 ## repository setup
 
 if {   [catch {set _gitdir $env(GIT_DIR)}]
-       && [catch {set _gitdir [exec git rev-parse --git-dir]} err]} {
+       && [catch {set _gitdir [git rev-parse --git-dir]} err]} {
        catch {wm withdraw .}
        error_popup "Cannot find the git directory:\n\n$err"
        exit 1
@@ -303,10 +362,23 @@ set _reponame [lindex [file split \
        [file normalize [file dirname $_gitdir]]] \
        end]
 
-set single_commit 0
-if {[appname] eq {git-citool}} {
-       set single_commit 1
-}
+######################################################################
+##
+## global init
+
+set current_diff_path {}
+set current_diff_side {}
+set diff_actions [list]
+set ui_status_value {Initializing...}
+
+set HEAD {}
+set PARENT {}
+set MERGE_HEAD [list]
+set commit_type {}
+set empty_tree {}
+set current_branch {}
+set current_diff_path {}
+set selected_commit_type new
 
 ######################################################################
 ##
@@ -354,7 +426,7 @@ proc repository_state {ctvar hdvar mhvar} {
 
        set mh [list]
 
-       if {[catch {set current_branch [exec git symbolic-ref HEAD]}]} {
+       if {[catch {set current_branch [git symbolic-ref HEAD]}]} {
                set current_branch {}
        } else {
                regsub ^refs/((heads|tags|remotes)/)? \
@@ -363,7 +435,7 @@ proc repository_state {ctvar hdvar mhvar} {
                        current_branch
        }
 
-       if {[catch {set hd [exec git rev-parse --verify HEAD]}]} {
+       if {[catch {set hd [git rev-parse --verify HEAD]}]} {
                set hd {}
                set ct initial
                return
@@ -391,7 +463,7 @@ proc PARENT {} {
                return $p
        }
        if {$empty_tree eq {}} {
-               set empty_tree [exec git mktree << {}]
+               set empty_tree [git mktree << {}]
        }
        return $empty_tree
 }
@@ -427,6 +499,11 @@ proc rescan {after {honor_trustmtime 1}} {
                $ui_comm edit modified false
        }
 
+       if {[is_enabled branch]} {
+               load_all_heads
+               populate_branch_menu
+       }
+
        if {$honor_trustmtime && $repo_config(gui.trustmtime) eq {true}} {
                rescan_stage2 {} $after
        } else {
@@ -626,8 +703,9 @@ proc reshow_diff {} {
        global current_diff_path current_diff_side
 
        set p $current_diff_path
-       if {$p eq {}
-               || $current_diff_side eq {}
+       if {$p eq {}} {
+               # No diff is being shown.
+       } elseif {$current_diff_side eq {}
                || [catch {set s $file_states($p)}]
                || [lsearch -sorted -exact $file_lists($current_diff_side) $p] == -1} {
                clear_diff
@@ -1026,7 +1104,7 @@ proc committer_ident {} {
        global GIT_COMMITTER_IDENT
 
        if {$GIT_COMMITTER_IDENT eq {}} {
-               if {[catch {set me [exec git var GIT_COMMITTER_IDENT]} err]} {
+               if {[catch {set me [git var GIT_COMMITTER_IDENT]} err]} {
                        error_popup "Unable to obtain your identity:\n\n$err"
                        return {}
                }
@@ -1176,7 +1254,7 @@ proc commit_writetree {curHEAD msg} {
 
 proc commit_committree {fd_wt curHEAD msg} {
        global HEAD PARENT MERGE_HEAD commit_type
-       global single_commit all_heads current_branch
+       global all_heads current_branch
        global ui_status_value ui_comm selected_commit_type
        global file_states selected_paths rescan_active
        global repo_config
@@ -1240,14 +1318,6 @@ proc commit_committree {fd_wt curHEAD msg} {
                return
        }
 
-       # -- Make sure our current branch exists.
-       #
-       if {$commit_type eq {initial}} {
-               lappend all_heads $current_branch
-               set all_heads [lsort -unique $all_heads]
-               populate_branch_menu
-       }
-
        # -- Cleanup after ourselves.
        #
        catch {file delete $msg_p}
@@ -1259,7 +1329,7 @@ proc commit_committree {fd_wt curHEAD msg} {
        # -- Let rerere do its thing.
        #
        if {[file isdirectory [gitdir rr-cache]]} {
-               catch {exec git rerere}
+               catch {git rerere}
        }
 
        # -- Run the post-commit hook.
@@ -1281,7 +1351,15 @@ proc commit_committree {fd_wt curHEAD msg} {
        $ui_comm edit reset
        $ui_comm edit modified false
 
-       if {$single_commit} do_quit
+       if {[is_enabled singlecommit]} do_quit
+
+       # -- Make sure our current branch exists.
+       #
+       if {$commit_type eq {initial}} {
+               lappend all_heads $current_branch
+               set all_heads [lsort -unique $all_heads]
+               populate_branch_menu
+       }
 
        # -- Update in memory status
        #
@@ -1376,6 +1454,7 @@ proc mapdesc {state path} {
 }
 
 proc escape_path {path} {
+       regsub -all {\\} $path "\\\\" path
        regsub -all "\n" $path "\\n" path
        return $path
 }
@@ -1859,11 +1938,24 @@ proc all_tracking_branches {} {
        return [lsort -unique $all_trackings]
 }
 
+proc load_all_tags {} {
+       set all_tags [list]
+       set fd [open "| git for-each-ref --format=%(refname) refs/tags" r]
+       while {[gets $fd line] > 0} {
+               if {![regsub ^refs/tags/ $line {} name]} continue
+               lappend all_tags $name
+       }
+       close $fd
+
+       return [lsort $all_tags]
+}
+
 proc do_create_branch_action {w} {
        global all_heads null_sha1 repo_config
        global create_branch_checkout create_branch_revtype
        global create_branch_head create_branch_trackinghead
        global create_branch_name create_branch_revexp
+       global create_branch_tag
 
        set newbranch $create_branch_name
        if {$newbranch eq {}
@@ -1877,7 +1969,7 @@ proc do_create_branch_action {w} {
                focus $w.desc.name_t
                return
        }
-       if {![catch {exec git show-ref --verify -- "refs/heads/$newbranch"}]} {
+       if {![catch {git show-ref --verify -- "refs/heads/$newbranch"}]} {
                tk_messageBox \
                        -icon error \
                        -type ok \
@@ -1887,7 +1979,7 @@ proc do_create_branch_action {w} {
                focus $w.desc.name_t
                return
        }
-       if {[catch {exec git check-ref-format "heads/$newbranch"}]} {
+       if {[catch {git check-ref-format "heads/$newbranch"}]} {
                tk_messageBox \
                        -icon error \
                        -type ok \
@@ -1902,9 +1994,10 @@ proc do_create_branch_action {w} {
        switch -- $create_branch_revtype {
        head {set rev $create_branch_head}
        tracking {set rev $create_branch_trackinghead}
+       tag {set rev $create_branch_tag}
        expression {set rev $create_branch_revexp}
        }
-       if {[catch {set cmt [exec git rev-parse --verify "${rev}^0"]}]} {
+       if {[catch {set cmt [git rev-parse --verify "${rev}^0"]}]} {
                tk_messageBox \
                        -icon error \
                        -type ok \
@@ -1947,6 +2040,8 @@ trace add variable create_branch_head write \
        [list radio_selector create_branch_revtype head]
 trace add variable create_branch_trackinghead write \
        [list radio_selector create_branch_revtype tracking]
+trace add variable create_branch_tag write \
+       [list radio_selector create_branch_revtype tag]
 
 trace add variable delete_branch_head write \
        [list radio_selector delete_branch_checktype head]
@@ -1958,6 +2053,7 @@ proc do_create_branch {} {
        global create_branch_checkout create_branch_revtype
        global create_branch_head create_branch_trackinghead
        global create_branch_name create_branch_revexp
+       global create_branch_tag
 
        set w .branch_editor
        toplevel $w
@@ -2021,6 +2117,19 @@ proc do_create_branch {} {
                        $all_trackings
                grid $w.from.tracking_r $w.from.tracking_m -sticky w
        }
+       set all_tags [load_all_tags]
+       if {$all_tags ne {}} {
+               set create_branch_tag [lindex $all_tags 0]
+               radiobutton $w.from.tag_r \
+                       -text {Tag:} \
+                       -value tag \
+                       -variable create_branch_revtype \
+                       -font font_ui
+               eval tk_optionMenu $w.from.tag_m \
+                       create_branch_tag \
+                       $all_tags
+               grid $w.from.tag_r $w.from.tag_m -sticky w
+       }
        radiobutton $w.from.exp_r \
                -text {Revision Expression:} \
                -value expression \
@@ -2083,7 +2192,7 @@ proc do_delete_branch_action {w} {
        }
        if {$check_rev eq {:none}} {
                set check_cmt {}
-       } elseif {[catch {set check_cmt [exec git rev-parse --verify "${check_rev}^0"]}]} {
+       } elseif {[catch {set check_cmt [git rev-parse --verify "${check_rev}^0"]}]} {
                tk_messageBox \
                        -icon error \
                        -type ok \
@@ -2097,10 +2206,10 @@ proc do_delete_branch_action {w} {
        set not_merged [list]
        foreach i [$w.list.l curselection] {
                set b [$w.list.l get $i]
-               if {[catch {set o [exec git rev-parse --verify $b]}]} continue
+               if {[catch {set o [git rev-parse --verify $b]}]} continue
                if {$check_cmt ne {}} {
                        if {$b eq $check_rev} continue
-                       if {[catch {set m [exec git merge-base $o $check_cmt]}]} continue
+                       if {[catch {set m [git merge-base $o $check_cmt]}]} continue
                        if {$o ne $m} {
                                lappend not_merged $b
                                continue
@@ -2138,7 +2247,7 @@ Delete the selected branches?}
        foreach i $to_delete {
                set b [lindex $i 0]
                set o [lindex $i 1]
-               if {[catch {exec git update-ref -d "refs/heads/$b" $o} err]} {
+               if {[catch {git update-ref -d "refs/heads/$b" $o} err]} {
                        append failed " - $b: $err\n"
                } else {
                        set x [lsearch -sorted -exact $all_heads $b]
@@ -2349,7 +2458,7 @@ Staying on branch '$current_branch'."
        #    here, it Just Works(tm).  If it doesn't we are in some really ugly
        #    state that is difficult to recover from within git-gui.
        #
-       if {[catch {exec git symbolic-ref HEAD "refs/heads/$new_branch"} err]} {
+       if {[catch {git symbolic-ref HEAD "refs/heads/$new_branch"} err]} {
                error_popup "Failed to set current branch.
 
 This working directory is only partially switched.
@@ -2859,14 +2968,16 @@ proc do_local_merge {} {
        pack $w.source -fill both -expand 1 -pady 5 -padx 5
 
        set cmd [list git for-each-ref]
-       lappend cmd {--format=%(objectname) %(refname)}
+       lappend cmd {--format=%(objectname) %(*objectname) %(refname)}
        lappend cmd refs/heads
        lappend cmd refs/remotes
+       lappend cmd refs/tags
        set fr_fd [open "| $cmd" r]
        fconfigure $fr_fd -translation binary
        while {[gets $fr_fd line] > 0} {
                set line [split $line { }]
-               set sha1([lindex $line 0]) [lindex $line 1]
+               set sha1([lindex $line 0]) [lindex $line 2]
+               set sha1([lindex $line 1]) [lindex $line 2]
        }
        close $fr_fd
 
@@ -2874,7 +2985,7 @@ proc do_local_merge {} {
        set fr_fd [open "| git rev-list --all --not HEAD"]
        while {[gets $fr_fd line] > 0} {
                if {[catch {set ref $sha1($line)}]} continue
-               regsub ^refs/(heads|remotes)/ $ref {} ref
+               regsub ^refs/(heads|remotes|tags)/ $ref {} ref
                lappend to_show $ref
        }
        close $fr_fd
@@ -2952,10 +3063,17 @@ proc reset_hard_wait {fd} {
 set next_browser_id 0
 
 proc new_browser {commit} {
-       global next_browser_id cursor_ptr
+       global next_browser_id cursor_ptr M1B
        global browser_commit browser_status browser_stack browser_path browser_busy
 
-       set w .browser[incr next_browser_id]
+       if {[winfo ismapped .]} {
+               set w .browser[incr next_browser_id]
+               set tl $w
+               toplevel $w
+       } else {
+               set w {}
+               set tl .
+       }
        set w_list $w.list.l
        set browser_commit($w_list) $commit
        set browser_status($w_list) {Starting...}
@@ -2963,7 +3081,6 @@ proc new_browser {commit} {
        set browser_path($w_list) $browser_commit($w_list):
        set browser_busy($w_list) 1
 
-       toplevel $w
        label $w.path -textvariable browser_path($w_list) \
                -anchor w \
                -justify left \
@@ -3002,9 +3119,19 @@ proc new_browser {commit} {
 
        bind $w_list <Button-1>        "browser_click 0 $w_list @%x,%y;break"
        bind $w_list <Double-Button-1> "browser_click 1 $w_list @%x,%y;break"
-
-       bind $w <Visibility> "focus $w"
-       bind $w <Destroy> "
+       bind $w_list <$M1B-Up>         "browser_parent $w_list;break"
+       bind $w_list <$M1B-Left>       "browser_parent $w_list;break"
+       bind $w_list <Up>              "browser_move -1 $w_list;break"
+       bind $w_list <Down>            "browser_move 1 $w_list;break"
+       bind $w_list <$M1B-Right>      "browser_enter $w_list;break"
+       bind $w_list <Return>          "browser_enter $w_list;break"
+       bind $w_list <Prior>           "browser_page -1 $w_list;break"
+       bind $w_list <Next>            "browser_page 1 $w_list;break"
+       bind $w_list <Left>            break
+       bind $w_list <Right>           break
+
+       bind $tl <Visibility> "focus $w"
+       bind $tl <Destroy> "
                array unset browser_buffer $w_list
                array unset browser_files $w_list
                array unset browser_status $w_list
@@ -3013,56 +3140,104 @@ proc new_browser {commit} {
                array unset browser_commit $w_list
                array unset browser_busy $w_list
        "
-       wm title $w "[appname] ([reponame]): File Browser"
+       wm title $tl "[appname] ([reponame]): File Browser"
        ls_tree $w_list $browser_commit($w_list) {}
 }
 
-proc browser_click {was_double_click w pos} {
+proc browser_move {dir w} {
+       global browser_files browser_busy
+
+       if {$browser_busy($w)} return
+       set lno [lindex [split [$w index in_sel.first] .] 0]
+       incr lno $dir
+       if {[lindex $browser_files($w) [expr {$lno - 1}]] ne {}} {
+               $w tag remove in_sel 0.0 end
+               $w tag add in_sel $lno.0 [expr {$lno + 1}].0
+               $w see $lno.0
+       }
+}
+
+proc browser_page {dir w} {
+       global browser_files browser_busy
+
+       if {$browser_busy($w)} return
+       $w yview scroll $dir pages
+       set lno [expr {int(
+                 [lindex [$w yview] 0]
+               * [llength $browser_files($w)]
+               + 1)}]
+       if {[lindex $browser_files($w) [expr {$lno - 1}]] ne {}} {
+               $w tag remove in_sel 0.0 end
+               $w tag add in_sel $lno.0 [expr {$lno + 1}].0
+               $w see $lno.0
+       }
+}
+
+proc browser_parent {w} {
+       global browser_files browser_status browser_path
+       global browser_stack browser_busy
+
+       if {$browser_busy($w)} return
+       set info [lindex $browser_files($w) 0]
+       if {[lindex $info 0] eq {parent}} {
+               set parent [lindex $browser_stack($w) end-1]
+               set browser_stack($w) [lrange $browser_stack($w) 0 end-2]
+               if {$browser_stack($w) eq {}} {
+                       regsub {:.*$} $browser_path($w) {:} browser_path($w)
+               } else {
+                       regsub {/[^/]+$} $browser_path($w) {} browser_path($w)
+               }
+               set browser_status($w) "Loading $browser_path($w)..."
+               ls_tree $w [lindex $parent 0] [lindex $parent 1]
+       }
+}
+
+proc browser_enter {w} {
        global browser_files browser_status browser_path
        global browser_commit browser_stack browser_busy
 
        if {$browser_busy($w)} return
-       set lno [lindex [split [$w index $pos] .] 0]
+       set lno [lindex [split [$w index in_sel.first] .] 0]
        set info [lindex $browser_files($w) [expr {$lno - 1}]]
-
-       $w conf -state normal
-       $w tag remove sel 0.0 end
-       $w tag remove in_sel 0.0 end
        if {$info ne {}} {
+               switch -- [lindex $info 0] {
+               parent {
+                       browser_parent $w
+               }
+               tree {
+                       set name [lindex $info 2]
+                       set escn [escape_path $name]
+                       set browser_status($w) "Loading $escn..."
+                       append browser_path($w) $escn
+                       ls_tree $w [lindex $info 1] $name
+               }
+               blob {
+                       set name [lindex $info 2]
+                       set p {}
+                       foreach n $browser_stack($w) {
+                               append p [lindex $n 1]
+                       }
+                       append p $name
+                       show_blame $browser_commit($w) $p
+               }
+               }
+       }
+}
+
+proc browser_click {was_double_click w pos} {
+       global browser_files browser_busy
+
+       if {$browser_busy($w)} return
+       set lno [lindex [split [$w index $pos] .] 0]
+       focus $w
+
+       if {[lindex $browser_files($w) [expr {$lno - 1}]] ne {}} {
+               $w tag remove in_sel 0.0 end
                $w tag add in_sel $lno.0 [expr {$lno + 1}].0
                if {$was_double_click} {
-                       switch -- [lindex $info 0] {
-                       parent {
-                               set parent [lindex $browser_stack($w) end-1]
-                               set browser_stack($w) [lrange $browser_stack($w) 0 end-2]
-                               if {$browser_stack($w) eq {}} {
-                                       regsub {:.*$} $browser_path($w) {:} browser_path($w)
-                               } else {
-                                       regsub {/[^/]+$} $browser_path($w) {} browser_path($w)
-                               }
-                               set browser_status($w) "Loading $browser_path($w)..."
-                               ls_tree $w [lindex $parent 0] [lindex $parent 1]
-                       }
-                       tree {
-                               set name [lindex $info 2]
-                               set escn [escape_path $name]
-                               set browser_status($w) "Loading $escn..."
-                               append browser_path($w) $escn
-                               ls_tree $w [lindex $info 1] $name
-                       }
-                       blob {
-                               set name [lindex $info 2]
-                               set p {}
-                               foreach n $browser_stack($w) {
-                                       append p [lindex $n 1]
-                               }
-                               append p $name
-                               show_blame $browser_commit($w) $p
-                       }
-                       }
+                       browser_enter $w
                }
        }
-       $w conf -state disabled
 }
 
 proc ls_tree {w tree_id name} {
@@ -3074,7 +3249,6 @@ proc ls_tree {w tree_id name} {
 
        $w conf -state normal
        $w tag remove in_sel 0.0 end
-       $w tag remove sel 0.0 end
        $w delete 0.0 end
        if {$browser_stack($w) ne {}} {
                $w image create end \
@@ -3087,7 +3261,8 @@ proc ls_tree {w tree_id name} {
        lappend browser_stack($w) [list $tree_id $name]
        $w conf -state disabled
 
-       set fd [open "| git ls-tree -z $tree_id" r]
+       set cmd [list git ls-tree -z $tree_id]
+       set fd [open "| $cmd" r]
        fconfigure $fd -blocking 0 -translation binary -encoding binary
        fileevent $fd readable [list read_ls_tree $fd $w]
 }
@@ -3141,18 +3316,25 @@ proc read_ls_tree {fd w} {
                set browser_status($w) Ready.
                set browser_busy($w) 0
                array unset browser_buffer $w
+               if {$n > 0} {
+                       $w tag add in_sel 1.0 2.0
+                       focus -force $w
+               }
        }
 }
 
 proc show_blame {commit path} {
        global next_browser_id blame_status blame_data
 
-       set w .browser[incr next_browser_id]
+       if {[winfo ismapped .]} {
+               set w .browser[incr next_browser_id]
+               set tl $w
+               toplevel $w
+       } else {
+               set w {}
+               set tl .
+       }
        set blame_status($w) {Loading current file content...}
-       set texts [list]
-
-       toplevel $w
-       panedwindow $w.out -orient horizontal
 
        label $w.path -text "$commit:$path" \
                -anchor w \
@@ -3160,53 +3342,53 @@ proc show_blame {commit path} {
                -borderwidth 1 \
                -relief sunken \
                -font font_uibold
-       pack $w.path -anchor w -side top -fill x
+       pack $w.path -side top -fill x
 
-       text $w.out.commit -background white -borderwidth 0 \
+       frame $w.out
+       text $w.out.loaded_t \
+               -background white -borderwidth 0 \
                -state disabled \
                -wrap none \
                -height 40 \
-               -width 8 \
+               -width 1 \
                -font font_diff
-       $w.out add $w.out.commit
-       lappend texts $w.out.commit
+       $w.out.loaded_t tag conf annotated -background grey
 
-       text $w.out.author -background white -borderwidth 0 \
-               -state disabled \
-               -wrap none \
-               -height 40 \
-               -width 20 \
-               -font font_diff
-       $w.out add $w.out.author
-       lappend texts $w.out.author
-
-       text $w.out.date -background white -borderwidth 0 \
-               -state disabled \
-               -wrap none \
-               -height 40 \
-               -width [string length "yyyy-mm-dd hh:mm:ss"] \
-               -font font_diff
-       $w.out add $w.out.date
-       lappend texts $w.out.date
-
-       text $w.out.linenumber -background white -borderwidth 0 \
+       text $w.out.linenumber_t \
+               -background white -borderwidth 0 \
                -state disabled \
                -wrap none \
                -height 40 \
                -width 5 \
                -font font_diff
-       $w.out.linenumber tag conf linenumber -justify right
-       $w.out add $w.out.linenumber
-       lappend texts $w.out.linenumber
+       $w.out.linenumber_t tag conf linenumber -justify right
 
-       text $w.out.file -background white -borderwidth 0 \
+       text $w.out.file_t \
+               -background white -borderwidth 0 \
                -state disabled \
                -wrap none \
                -height 40 \
                -width 80 \
+               -xscrollcommand [list $w.out.sbx set] \
                -font font_diff
-       $w.out add $w.out.file
-       lappend texts $w.out.file
+
+       scrollbar $w.out.sbx -orient h -command [list $w.out.file_t xview]
+       scrollbar $w.out.sby -orient v \
+               -command [list scrollbar2many [list \
+               $w.out.loaded_t \
+               $w.out.linenumber_t \
+               $w.out.file_t \
+               ] yview]
+       grid \
+               $w.out.linenumber_t \
+               $w.out.loaded_t \
+               $w.out.file_t \
+               $w.out.sby \
+               -sticky nsew
+       grid conf $w.out.sbx -column 2 -sticky we
+       grid columnconfigure $w.out 2 -weight 1
+       grid rowconfigure $w.out 0 -weight 1
+       pack $w.out -fill both -expand 1
 
        label $w.status -textvariable blame_status($w) \
                -anchor w \
@@ -3214,23 +3396,51 @@ proc show_blame {commit path} {
                -borderwidth 1 \
                -relief sunken \
                -font font_ui
-       pack $w.status -anchor w -side bottom -fill x
+       pack $w.status -side bottom -fill x
 
-       scrollbar $w.sby -orient v -command [list scrollbar2many $texts yview]
-       pack $w.sby -side right -fill y
-       pack $w.out -side left -fill both -expand 1
+       frame $w.cm
+       text $w.cm.t \
+               -background white -borderwidth 0 \
+               -state disabled \
+               -wrap none \
+               -height 10 \
+               -width 80 \
+               -xscrollcommand [list $w.cm.sbx set] \
+               -yscrollcommand [list $w.cm.sby set] \
+               -font font_diff
+       scrollbar $w.cm.sbx -orient h -command [list $w.cm.t xview]
+       scrollbar $w.cm.sby -orient v -command [list $w.cm.t yview]
+       pack $w.cm.sby -side right -fill y
+       pack $w.cm.sbx -side bottom -fill x
+       pack $w.cm.t -expand 1 -fill both
+       pack $w.cm -side bottom -fill x
 
        menu $w.ctxm -tearoff 0
        $w.ctxm add command -label "Copy Commit" \
                -font font_ui \
                -command "blame_copycommit $w \$cursorW @\$cursorX,\$cursorY"
 
-       foreach i $texts {
+       foreach i [list \
+               $w.out.loaded_t \
+               $w.out.linenumber_t \
+               $w.out.file_t] {
                $i tag conf in_sel \
                        -background [$i cget -foreground] \
                        -foreground [$i cget -background]
-               $i conf -yscrollcommand [list many2scrollbar $texts yview $w.sby]
-               bind $i <Button-1> "blame_highlight $i @%x,%y $texts;break"
+               $i conf -yscrollcommand \
+                       [list many2scrollbar [list \
+                       $w.out.loaded_t \
+                       $w.out.linenumber_t \
+                       $w.out.file_t \
+                       ] yview $w.out.sby]
+               bind $i <Button-1> "
+                       blame_click {$w} \\
+                               $w.cm.t \\
+                               $w.out.linenumber_t \\
+                               $w.out.file_t \\
+                               $i @%x,%y
+                       focus $i
+               "
                bind_button3 $i "
                        set cursorX %x
                        set cursorY %y
@@ -3239,21 +3449,30 @@ proc show_blame {commit path} {
                "
        }
 
-       bind $w <Visibility> "focus $w"
-       bind $w <Destroy> "
-               array unset blame_status $w
+       bind $w.cm.t <Button-1> "focus $w.cm.t"
+       bind $tl <Visibility> "focus $tl"
+       bind $tl <Destroy> "
+               array unset blame_status {$w}
                array unset blame_data $w,*
        "
-       wm title $w "[appname] ([reponame]): File Viewer"
+       wm title $tl "[appname] ([reponame]): File Viewer"
 
+       set blame_data($w,commit_count) 0
+       set blame_data($w,commit_list) {}
        set blame_data($w,total_lines) 0
-       set fd [open "| git cat-file blob $commit:$path" r]
+       set blame_data($w,blame_lines) 0
+       set blame_data($w,highlight_commit) {}
+       set blame_data($w,highlight_line) -1
+
+       set cmd [list git cat-file blob "$commit:$path"]
+       set fd [open "| $cmd" r]
        fconfigure $fd -blocking 0 -translation lf -encoding binary
-       fileevent $fd readable [list read_blame_catfile $fd $w $commit $path \
-               $texts $w.out.linenumber $w.out.file]
+       fileevent $fd readable [list read_blame_catfile \
+               $fd $w $commit $path \
+               $w.cm.t $w.out.loaded_t $w.out.linenumber_t $w.out.file_t]
 }
 
-proc read_blame_catfile {fd w commit path texts w_lno w_file} {
+proc read_blame_catfile {fd w commit path w_cmit w_load w_line w_file} {
        global blame_status blame_data
 
        if {![winfo exists $w_file]} {
@@ -3262,100 +3481,216 @@ proc read_blame_catfile {fd w commit path texts w_lno w_file} {
        }
 
        set n $blame_data($w,total_lines)
-       foreach i $texts {$i conf -state normal}
+       $w_load conf -state normal
+       $w_line conf -state normal
+       $w_file conf -state normal
        while {[gets $fd line] >= 0} {
                regsub "\r\$" $line {} line
                incr n
-               $w_lno insert end $n linenumber
-               $w_file insert end $line
-               foreach i $texts {$i insert end "\n"}
+               $w_load insert end "\n"
+               $w_line insert end "$n\n" linenumber
+               $w_file insert end "$line\n"
        }
-       foreach i $texts {$i conf -state disabled}
+       $w_load conf -state disabled
+       $w_line conf -state disabled
+       $w_file conf -state disabled
        set blame_data($w,total_lines) $n
 
        if {[eof $fd]} {
                close $fd
-               set blame_status($w) {Loading annotations...}
-               set fd [open "| git blame --incremental $commit -- $path" r]
+               blame_incremental_status $w
+               set cmd [list git blame -M -C --incremental]
+               lappend cmd $commit -- $path
+               set fd [open "| $cmd" r]
                fconfigure $fd -blocking 0 -translation lf -encoding binary
-               fileevent $fd readable "read_blame_incremental $fd $w $texts"
+               fileevent $fd readable [list read_blame_incremental $fd $w \
+                       $w_load $w_cmit $w_line $w_file]
        }
 }
 
-proc read_blame_incremental {fd w w_commit w_author w_date w_lno w_file} {
+proc read_blame_incremental {fd w w_load w_cmit w_line w_file} {
        global blame_status blame_data
 
-       if {![winfo exists $w_commit]} {
+       if {![winfo exists $w_file]} {
                catch {close $fd}
                return
        }
 
-       $w_commit conf -state normal
-       $w_author conf -state normal
-       $w_date conf -state normal
-
        while {[gets $fd line] >= 0} {
                if {[regexp {^([a-z0-9]{40}) (\d+) (\d+) (\d+)$} $line line \
-                       commit original_line final_line line_count]} {
-                       set blame_data($w,commit) $commit
+                       cmit original_line final_line line_count]} {
+                       set blame_data($w,commit) $cmit
                        set blame_data($w,original_line) $original_line
                        set blame_data($w,final_line) $final_line
                        set blame_data($w,line_count) $line_count
+
+                       if {[catch {set g $blame_data($w,$cmit,order)}]} {
+                               $w_line tag conf g$cmit
+                               $w_file tag conf g$cmit
+                               $w_line tag raise in_sel
+                               $w_file tag raise in_sel
+                               $w_file tag raise sel
+                               set blame_data($w,$cmit,order) $blame_data($w,commit_count)
+                               incr blame_data($w,commit_count)
+                               lappend blame_data($w,commit_list) $cmit
+                       }
                } elseif {[string match {filename *} $line]} {
+                       set file [string range $line 9 end]
                        set n $blame_data($w,line_count)
                        set lno $blame_data($w,final_line)
-                       set file [string range $line 9 end]
-                       set commit $blame_data($w,commit)
-                       set abbrev [string range $commit 0 8]
-
-                       if {[catch {set author $blame_data($w,$commit,author)} err]} {
-                       puts $err
-                               set author {}
-                       }
-
-                       if {[catch {set atime $blame_data($w,$commit,author-time)}]} {
-                               set atime {}
-                       } else {
-                               set atime [clock format $atime -format {%Y-%m-%d %T}]
-                       }
+                       set cmit $blame_data($w,commit)
 
                        while {$n > 0} {
-                               $w_commit delete $lno.0 "$lno.0 lineend"
-                               $w_author delete $lno.0 "$lno.0 lineend"
-                               $w_date delete $lno.0 "$lno.0 lineend"
+                               if {[catch {set g g$blame_data($w,line$lno,commit)}]} {
+                                       $w_load tag add annotated $lno.0 "$lno.0 lineend + 1c"
+                               } else {
+                                       $w_line tag remove g$g $lno.0 "$lno.0 lineend + 1c"
+                                       $w_file tag remove g$g $lno.0 "$lno.0 lineend + 1c"
+                               }
+
+                               set blame_data($w,line$lno,commit) $cmit
+                               set blame_data($w,line$lno,file) $file
+                               $w_line tag add g$cmit $lno.0 "$lno.0 lineend + 1c"
+                               $w_file tag add g$cmit $lno.0 "$lno.0 lineend + 1c"
 
-                               $w_commit insert $lno.0 $abbrev
-                               $w_author insert $lno.0 $author
-                               $w_date insert $lno.0 $atime
-                               set blame_data($w,line$lno,commit) $commit
+                               if {$blame_data($w,highlight_line) == -1} {
+                                       if {[lindex [$w_file yview] 0] == 0} {
+                                               $w_file see $lno.0
+                                               blame_showcommit $w $w_cmit $w_line $w_file $lno
+                                       }
+                               } elseif {$blame_data($w,highlight_line) == $lno} {
+                                       blame_showcommit $w $w_cmit $w_line $w_file $lno
+                               }
 
                                incr n -1
                                incr lno
+                               incr blame_data($w,blame_lines)
+                       }
+
+                       set hc $blame_data($w,highlight_commit)
+                       if {$hc ne {}
+                               && [expr {$blame_data($w,$hc,order) + 1}]
+                                       == $blame_data($w,$cmit,order)} {
+                               blame_showcommit $w $w_cmit $w_line $w_file \
+                                       $blame_data($w,highlight_line)
                        }
                } elseif {[regexp {^([a-z-]+) (.*)$} $line line header data]} {
                        set blame_data($w,$blame_data($w,commit),$header) $data
                }
        }
 
-       $w_commit conf -state disabled
-       $w_author conf -state disabled
-       $w_date conf -state disabled
-
        if {[eof $fd]} {
                close $fd
                set blame_status($w) {Annotation complete.}
+       } else {
+               blame_incremental_status $w
        }
 }
 
-proc blame_highlight {w pos args} {
-       set lno [lindex [split [$w index $pos] .] 0]
-       foreach i $args {
-               $i tag remove in_sel 0.0 end
-       }
+proc blame_incremental_status {w} {
+       global blame_status blame_data
+
+       set blame_status($w) [format \
+               "Loading annotations... %i of %i lines annotated (%2i%%)" \
+               $blame_data($w,blame_lines) \
+               $blame_data($w,total_lines) \
+               [expr {100 * $blame_data($w,blame_lines)
+                       / $blame_data($w,total_lines)}]]
+}
+
+proc blame_click {w w_cmit w_line w_file cur_w pos} {
+       set lno [lindex [split [$cur_w index $pos] .] 0]
        if {$lno eq {}} return
-       foreach i $args {
-               $i tag add in_sel $lno.0 "$lno.0 + 1 line"
+
+       $w_line tag remove in_sel 0.0 end
+       $w_file tag remove in_sel 0.0 end
+       $w_line tag add in_sel $lno.0 "$lno.0 + 1 line"
+       $w_file tag add in_sel $lno.0 "$lno.0 + 1 line"
+
+       blame_showcommit $w $w_cmit $w_line $w_file $lno
+}
+
+set blame_colors {
+       #ff4040
+       #ff40ff
+       #4040ff
+}
+
+proc blame_showcommit {w w_cmit w_line w_file lno} {
+       global blame_colors blame_data repo_config
+
+       set cmit $blame_data($w,highlight_commit)
+       if {$cmit ne {}} {
+               set idx $blame_data($w,$cmit,order)
+               set i 0
+               foreach c $blame_colors {
+                       set h [lindex $blame_data($w,commit_list) [expr {$idx - 1 + $i}]]
+                       $w_line tag conf g$h -background white
+                       $w_file tag conf g$h -background white
+                       incr i
+               }
        }
+
+       $w_cmit conf -state normal
+       $w_cmit delete 0.0 end
+       if {[catch {set cmit $blame_data($w,line$lno,commit)}]} {
+               set cmit {}
+               $w_cmit insert end "Loading annotation..."
+       } else {
+               set idx $blame_data($w,$cmit,order)
+               set i 0
+               foreach c $blame_colors {
+                       set h [lindex $blame_data($w,commit_list) [expr {$idx - 1 + $i}]]
+                       $w_line tag conf g$h -background $c
+                       $w_file tag conf g$h -background $c
+                       incr i
+               }
+
+               if {[catch {set msg $blame_data($w,$cmit,message)}]} {
+                       set msg {}
+                       catch {
+                               set fd [open "| git cat-file commit $cmit" r]
+                               fconfigure $fd -encoding binary -translation lf
+                               if {[catch {set enc $repo_config(i18n.commitencoding)}]} {
+                                       set enc utf-8
+                               }
+                               while {[gets $fd line] > 0} {
+                                       if {[string match {encoding *} $line]} {
+                                               set enc [string tolower [string range $line 9 end]]
+                                       }
+                               }
+                               fconfigure $fd -encoding $enc
+                               set msg [string trim [read $fd]]
+                               close $fd
+                       }
+                       set blame_data($w,$cmit,message) $msg
+               }
+
+               set author_name {}
+               set author_email {}
+               set author_time {}
+               catch {set author_name $blame_data($w,$cmit,author)}
+               catch {set author_email $blame_data($w,$cmit,author-mail)}
+               catch {set author_time [clock format $blame_data($w,$cmit,author-time)]}
+
+               set committer_name {}
+               set committer_email {}
+               set committer_time {}
+               catch {set committer_name $blame_data($w,$cmit,committer)}
+               catch {set committer_email $blame_data($w,$cmit,committer-mail)}
+               catch {set committer_time [clock format $blame_data($w,$cmit,committer-time)]}
+
+               $w_cmit insert end "commit $cmit\n"
+               $w_cmit insert end "Author: $author_name $author_email $author_time\n"
+               $w_cmit insert end "Committer: $committer_name $committer_email $committer_time\n"
+               $w_cmit insert end "Original File: [escape_path $blame_data($w,line$lno,file)]\n"
+               $w_cmit insert end "\n"
+               $w_cmit insert end $msg
+       }
+       $w_cmit conf -state disabled
+
+       set blame_data($w,highlight_line) $lno
+       set blame_data($w,highlight_commit) $cmit
 }
 
 proc blame_copycommit {w i pos} {
@@ -3780,14 +4115,6 @@ set starting_gitk_msg {Starting gitk... please wait...}
 proc do_gitk {revs} {
        global env ui_status_value starting_gitk_msg
 
-       # -- On Windows gitk is severly broken, and right now it seems like
-       #    nobody cares about fixing it.  The only known workaround is to
-       #    always delete ~/.gitk before starting the program.
-       #
-       if {[is_Windows]} {
-               catch {file delete [file join $env(HOME) .gitk]}
-       }
-
        # -- Always start gitk through whatever we were loaded with.  This
        #    lets us bypass using shell process on Windows systems.
        #
@@ -3906,34 +4233,36 @@ proc do_quit {} {
        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 {[winfo exists $ui_comm]} {
+               # -- 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
+                       }
+               } else {
+                       catch {file delete $save}
                }
-       } else {
-               catch {file delete $save}
-       }
 
-       # -- Stash our current window geometry into this repository.
-       #
-       set cfg_geometry [list]
-       lappend cfg_geometry [wm geometry .]
-       lappend cfg_geometry [lindex [.vpane sash coord 0] 1]
-       lappend cfg_geometry [lindex [.vpane.files sash coord 0] 0]
-       if {[catch {set rc_geometry $repo_config(gui.geometry)}]} {
-               set rc_geometry {}
-       }
-       if {$cfg_geometry ne $rc_geometry} {
-               catch {exec git repo-config gui.geometry $cfg_geometry}
+               # -- Stash our current window geometry into this repository.
+               #
+               set cfg_geometry [list]
+               lappend cfg_geometry [wm geometry .]
+               lappend cfg_geometry [lindex [.vpane sash coord 0] 1]
+               lappend cfg_geometry [lindex [.vpane.files sash coord 0] 0]
+               if {[catch {set rc_geometry $repo_config(gui.geometry)}]} {
+                       set rc_geometry {}
+               }
+               if {$cfg_geometry ne $rc_geometry} {
+                       catch {git config gui.geometry $cfg_geometry}
+               }
        }
 
        destroy .
@@ -4151,6 +4480,61 @@ proc do_commit {} {
        commit_tree
 }
 
+proc do_credits {} {
+       global gitgui_credits
+
+       set w .credits_dialog
+
+       toplevel $w
+       wm geometry $w "+[winfo rootx .]+[winfo rooty .]"
+
+       label $w.header -text {git-gui Contributors} -font font_uibold
+       pack $w.header -side top -fill x
+
+       frame $w.buttons
+       button $w.buttons.close -text {Close} \
+               -font font_ui \
+               -command [list destroy $w]
+       pack $w.buttons.close -side right
+       pack $w.buttons -side bottom -fill x -pady 10 -padx 10
+
+       frame $w.credits
+       text $w.credits.t \
+               -background [$w.header cget -background] \
+               -yscrollcommand [list $w.credits.sby set] \
+               -width 20 \
+               -height 10 \
+               -wrap none \
+               -borderwidth 1 \
+               -relief solid \
+               -padx 5 -pady 5 \
+               -font font_ui
+       scrollbar $w.credits.sby -command [list $w.credits.t yview]
+       pack $w.credits.sby -side right -fill y
+       pack $w.credits.t -fill both -expand 1
+       pack $w.credits -side top -fill both -expand 1 -padx 5 -pady 5
+
+       label $w.desc \
+               -text "All portions are copyrighted by their respective authors
+and are distributed under the GNU General Public License." \
+               -padx 5 -pady 5 \
+               -justify left \
+               -anchor w \
+               -borderwidth 1 \
+               -relief solid \
+               -font font_ui
+       pack $w.desc -side top -fill x -padx 5 -pady 5
+
+       $w.credits.t insert end "[string trim $gitgui_credits]\n"
+       $w.credits.t conf -state disabled
+       $w.credits.t see 1.0
+
+       bind $w <Visibility> "grab $w; focus $w"
+       bind $w <Key-Escape> [list destroy $w]
+       wm title $w [$w.header cget -text]
+       tkwait window $w
+}
+
 proc do_about {} {
        global appvers copyright
        global tcl_patchLevel tk_patchLevel
@@ -4167,11 +4551,15 @@ proc do_about {} {
        button $w.buttons.close -text {Close} \
                -font font_ui \
                -command [list destroy $w]
+       button $w.buttons.credits -text {Contributors} \
+               -font font_ui \
+               -command do_credits
+       pack $w.buttons.credits -side left
        pack $w.buttons.close -side right
        pack $w.buttons -side bottom -fill x -pady 10 -padx 10
 
        label $w.desc \
-               -text "[appname] - a commit creation tool for Git.
+               -text "git-gui - a graphical user interface for Git.
 $copyright" \
                -padx 5 -pady 5 \
                -justify left \
@@ -4182,8 +4570,8 @@ $copyright" \
        pack $w.desc -side top -fill x -padx 5 -pady 5
 
        set v {}
-       append v "[appname] version $appvers\n"
-       append v "[exec git version]\n"
+       append v "git-gui version $appvers\n"
+       append v "[git version]\n"
        append v "\n"
        if {$tcl_patchLevel eq $tk_patchLevel} {
                append v "Tcl/Tk version $tcl_patchLevel"
@@ -4242,7 +4630,7 @@ proc do_options {} {
        toplevel $w
        wm geometry $w "+[winfo rootx .]+[winfo rooty .]"
 
-       label $w.header -text "[appname] Options" \
+       label $w.header -text "Options" \
                -font font_uibold
        pack $w.header -side top -fill x
 
@@ -4694,20 +5082,63 @@ set font_descs {
 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 {
+--version -
+version -
+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
 
+set ui_comm {}
+
 # -- Menu Bar
 #
 menu .mbar -tearoff 0
 .mbar add cascade -label Repository -menu .mbar.repository
 .mbar add cascade -label Edit -menu .mbar.edit
-if {!$single_commit} {
+if {[is_enabled branch]} {
        .mbar add cascade -label Branch -menu .mbar.branch
 }
-.mbar add cascade -label Commit -menu .mbar.commit
-if {!$single_commit} {
+if {[is_enabled multicommit] || [is_enabled singlecommit]} {
+       .mbar add cascade -label Commit -menu .mbar.commit
+}
+if {[is_enabled transport]} {
        .mbar add cascade -label Merge -menu .mbar.merge
        .mbar add cascade -label Fetch -menu .mbar.fetch
        .mbar add cascade -label Push -menu .mbar.push
@@ -4722,19 +5153,21 @@ menu .mbar.repository
        -label {Browse Current Branch} \
        -command {new_browser $current_branch} \
        -font font_ui
+trace add variable current_branch write ".mbar.repository entryconf [.mbar.repository index last] -label \"Browse \$current_branch\" ;#"
 .mbar.repository add separator
 
 .mbar.repository add command \
        -label {Visualize Current Branch} \
-       -command {do_gitk {}} \
+       -command {do_gitk $current_branch} \
        -font font_ui
+trace add variable current_branch write ".mbar.repository entryconf [.mbar.repository index last] -label \"Visualize \$current_branch\" ;#"
 .mbar.repository add command \
        -label {Visualize All Branches} \
-       -command {do_gitk {--all}} \
+       -command {do_gitk --all} \
        -font font_ui
 .mbar.repository add separator
 
-if {!$single_commit} {
+if {[is_enabled multicommit]} {
        .mbar.repository add command -label {Database Statistics} \
                -command do_stats \
                -font font_ui
@@ -4808,7 +5241,7 @@ menu .mbar.edit
 
 # -- Branch Menu
 #
-if {!$single_commit} {
+if {[is_enabled branch]} {
        menu .mbar.branch
 
        .mbar.branch add command -label {Create...} \
@@ -4827,73 +5260,75 @@ if {!$single_commit} {
 
 # -- Commit Menu
 #
-menu .mbar.commit
-
-.mbar.commit add radiobutton \
-       -label {New Commit} \
-       -command do_select_commit_type \
-       -variable selected_commit_type \
-       -value new \
-       -font font_ui
-lappend disable_on_lock \
-       [list .mbar.commit entryconf [.mbar.commit index last] -state]
+if {[is_enabled multicommit] || [is_enabled singlecommit]} {
+       menu .mbar.commit
+
+       .mbar.commit add radiobutton \
+               -label {New Commit} \
+               -command do_select_commit_type \
+               -variable selected_commit_type \
+               -value new \
+               -font font_ui
+       lappend disable_on_lock \
+               [list .mbar.commit entryconf [.mbar.commit index last] -state]
 
-.mbar.commit add radiobutton \
-       -label {Amend Last Commit} \
-       -command do_select_commit_type \
-       -variable selected_commit_type \
-       -value amend \
-       -font font_ui
-lappend disable_on_lock \
-       [list .mbar.commit entryconf [.mbar.commit index last] -state]
+       .mbar.commit add radiobutton \
+               -label {Amend Last Commit} \
+               -command do_select_commit_type \
+               -variable selected_commit_type \
+               -value amend \
+               -font font_ui
+       lappend disable_on_lock \
+               [list .mbar.commit entryconf [.mbar.commit index last] -state]
 
-.mbar.commit add separator
+       .mbar.commit add separator
 
-.mbar.commit add command -label Rescan \
-       -command do_rescan \
-       -accelerator F5 \
-       -font font_ui
-lappend disable_on_lock \
-       [list .mbar.commit entryconf [.mbar.commit index last] -state]
+       .mbar.commit add command -label Rescan \
+               -command do_rescan \
+               -accelerator F5 \
+               -font font_ui
+       lappend disable_on_lock \
+               [list .mbar.commit entryconf [.mbar.commit index last] -state]
 
-.mbar.commit add command -label {Add To Commit} \
-       -command do_add_selection \
-       -font font_ui
-lappend disable_on_lock \
-       [list .mbar.commit entryconf [.mbar.commit index last] -state]
+       .mbar.commit add command -label {Add To Commit} \
+               -command do_add_selection \
+               -font font_ui
+       lappend disable_on_lock \
+               [list .mbar.commit entryconf [.mbar.commit index last] -state]
 
-.mbar.commit add command -label {Add All To Commit} \
-       -command do_add_all \
-       -accelerator $M1T-I \
-       -font font_ui
-lappend disable_on_lock \
-       [list .mbar.commit entryconf [.mbar.commit index last] -state]
+       .mbar.commit add command -label {Add Existing To Commit} \
+               -command do_add_all \
+               -accelerator $M1T-I \
+               -font font_ui
+       lappend disable_on_lock \
+               [list .mbar.commit entryconf [.mbar.commit index last] -state]
 
-.mbar.commit add command -label {Unstage From Commit} \
-       -command do_unstage_selection \
-       -font font_ui
-lappend disable_on_lock \
-       [list .mbar.commit entryconf [.mbar.commit index last] -state]
+       .mbar.commit add command -label {Unstage From Commit} \
+               -command do_unstage_selection \
+               -font font_ui
+       lappend disable_on_lock \
+               [list .mbar.commit entryconf [.mbar.commit index last] -state]
 
-.mbar.commit add command -label {Revert Changes} \
-       -command do_revert_selection \
-       -font font_ui
-lappend disable_on_lock \
-       [list .mbar.commit entryconf [.mbar.commit index last] -state]
+       .mbar.commit add command -label {Revert Changes} \
+               -command do_revert_selection \
+               -font font_ui
+       lappend disable_on_lock \
+               [list .mbar.commit entryconf [.mbar.commit index last] -state]
 
-.mbar.commit add separator
+       .mbar.commit add separator
 
-.mbar.commit add command -label {Sign Off} \
-       -command do_signoff \
-       -accelerator $M1T-S \
-       -font font_ui
+       .mbar.commit add command -label {Sign Off} \
+               -command do_signoff \
+               -accelerator $M1T-S \
+               -font font_ui
 
-.mbar.commit add command -label Commit \
-       -command do_commit \
-       -accelerator $M1T-Return \
-       -font font_ui
-lappend disable_on_lock \
-       [list .mbar.commit entryconf [.mbar.commit index last] -state]
+       .mbar.commit add command -label Commit \
+               -command do_commit \
+               -accelerator $M1T-Return \
+               -font font_ui
+       lappend disable_on_lock \
+               [list .mbar.commit entryconf [.mbar.commit index last] -state]
+}
 
 if {[is_MacOSX]} {
        # -- Apple Menu (Mac OS X only)
@@ -4904,7 +5339,7 @@ if {[is_MacOSX]} {
        .mbar.apple add command -label "About [appname]" \
                -command do_about \
                -font font_ui
-       .mbar.apple add command -label "[appname] Options..." \
+       .mbar.apple add command -label "Options..." \
                -command do_options \
                -font font_ui
 } else {
@@ -4963,7 +5398,7 @@ set doc_path [file dirname [gitexec]]
 set doc_path [file join $doc_path Documentation index.html]
 
 if {[is_Cygwin]} {
-       set doc_path [exec cygpath --windows $doc_path]
+       set doc_path [exec cygpath --mixed $doc_path]
 }
 
 if {$browser eq {}} {
@@ -4996,6 +5431,58 @@ if {$browser ne {}} {
 }
 unset browser doc_path doc_url
 
+# -- Standard bindings
+#
+bind .   <Destroy> do_quit
+bind all <$M1B-Key-q> do_quit
+bind all <$M1B-Key-Q> do_quit
+bind all <$M1B-Key-w> {destroy [winfo toplevel %W]}
+bind all <$M1B-Key-W> {destroy [winfo toplevel %W]}
+
+# -- Not a normal commit type invocation?  Do that instead!
+#
+switch -- $subcommand {
+--version -
+version {
+       puts "git-gui version $appvers"
+       exit
+}
+browser {
+       if {[llength $argv] != 1} {
+               puts stderr "usage: $argv0 browser commit"
+               exit 1
+       }
+       set current_branch [lindex $argv 0]
+       new_browser $current_branch
+       return
+}
+blame {
+       if {[llength $argv] != 2} {
+               puts stderr "usage: $argv0 blame commit path"
+               exit 1
+       }
+       set current_branch [lindex $argv 0]
+       show_blame $current_branch [lindex $argv 1]
+       return
+}
+citool -
+gui {
+       if {[llength $argv] != 0} {
+               puts -nonewline stderr "usage: $argv0"
+               if {$subcommand ne {gui} && [appname] ne "git-$subcommand"} {
+                       puts -nonewline stderr " $subcommand"
+               }
+               puts stderr {}
+               exit 1
+       }
+       # fall through to setup UI for commits
+}
+default {
+       puts stderr "usage: $argv0 \[{blame|browser|citool}\]"
+       exit 1
+}
+}
+
 # -- Branch Control
 #
 frame .branch \
@@ -5015,7 +5502,7 @@ pack .branch.l1 -side left
 pack .branch.cb -side left -fill x
 pack .branch -side top -fill x
 
-if {!$single_commit} {
+if {[is_enabled branch]} {
        menu .mbar.merge
        .mbar.merge add command -label {Local Merge...} \
                -command do_local_merge \
@@ -5122,7 +5609,7 @@ pack .vpane.lower.commarea.buttons.rescan -side top -fill x
 lappend disable_on_lock \
        {.vpane.lower.commarea.buttons.rescan conf -state}
 
-button .vpane.lower.commarea.buttons.incall -text {Add All} \
+button .vpane.lower.commarea.buttons.incall -text {Add Existing} \
        -command do_add_all \
        -font font_ui
 pack .vpane.lower.commarea.buttons.incall -side top -fill x
@@ -5241,9 +5728,6 @@ bind_button3 $ui_comm "tk_popup $ctxm %X %Y"
 
 # -- Diff Header
 #
-set current_diff_path {}
-set current_diff_side {}
-set diff_actions [list]
 proc trace_current_diff_path {varname args} {
        global current_diff_path diff_actions file_states
        if {$current_diff_path eq {}} {
@@ -5436,7 +5920,6 @@ unset ui_diff_applyhunk
 
 # -- Status Bar
 #
-set ui_status_value {Initializing...}
 label .status -textvariable ui_status_value \
        -anchor w \
        -justify left \
@@ -5487,12 +5970,11 @@ bind $ui_diff <Key-Left>   {catch {%W xview scroll -1 units};break}
 bind $ui_diff <Key-Right>  {catch {%W xview scroll  1 units};break}
 bind $ui_diff <Button-1>   {focus %W}
 
-if {!$single_commit} {
+if {[is_enabled branch]} {
        bind . <$M1B-Key-n> do_create_branch
        bind . <$M1B-Key-N> do_create_branch
 }
 
-bind .   <Destroy> do_quit
 bind all <Key-F5> do_rescan
 bind all <$M1B-Key-r> do_rescan
 bind all <$M1B-Key-R> do_rescan
@@ -5501,10 +5983,6 @@ bind .   <$M1B-Key-S> do_signoff
 bind .   <$M1B-Key-i> do_add_all
 bind .   <$M1B-Key-I> do_add_all
 bind .   <$M1B-Key-Return> do_commit
-bind all <$M1B-Key-q> do_quit
-bind all <$M1B-Key-Q> do_quit
-bind all <$M1B-Key-w> {destroy [winfo toplevel %W]}
-bind all <$M1B-Key-W> {destroy [winfo toplevel %W]}
 foreach i [list $ui_index $ui_workdir] {
        bind $i <Button-1>       "toggle_or_diff         $i %x %y; break"
        bind $i <$M1B-Button-1>  "add_one_to_selection   $i %x %y; break"
@@ -5515,15 +5993,6 @@ unset i
 set file_lists($ui_index) [list]
 set file_lists($ui_workdir) [list]
 
-set HEAD {}
-set PARENT {}
-set MERGE_HEAD [list]
-set commit_type {}
-set empty_tree {}
-set current_branch {}
-set current_diff_path {}
-set selected_commit_type new
-
 wm title . "[appname] ([file normalize [file dirname [gitdir]]])"
 focus -force $ui_comm
 
@@ -5584,7 +6053,7 @@ user.email settings into your personal
 
 # -- Only initialize complex UI if we are going to stay running.
 #
-if {!$single_commit} {
+if {[is_enabled transport]} {
        load_all_remotes
        load_all_heads
 
@@ -5595,10 +6064,10 @@ if {!$single_commit} {
 
 # -- Only suggest a gc run if we are going to stay running.
 #
-if {!$single_commit} {
+if {[is_enabled multicommit]} {
        set object_limit 2000
        if {[is_Windows]} {set object_limit 200}
-       regexp {^([0-9]+) objects,} [exec git count-objects] _junk objects_current
+       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.