git-gui: Refactor 'exec git subcmd' idiom.
[gitweb.git] / git-gui.sh
index 53e890af0fd7358b14ea5332949b7ddfadf15c6a..7ecb98b900576ed6abc8b4e4af4333a209769ba6 100755 (executable)
@@ -2,7 +2,7 @@
 # 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.
 
@@ -46,7 +46,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"
                }
        }
@@ -202,14 +202,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 config --global --unset $name}
+                               catch {git config --global --unset $name}
                        } else {
                                regsub -all "\[{}\]" $value {"} value
-                               exec git config --global $name $value
+                               git config --global $name $value
                        }
                        set global_config($name) $value
                        if {$value eq $repo_config($name)} {
-                               catch {exec git config --unset $name}
+                               catch {git config --unset $name}
                                set repo_config($name) $value
                        }
                }
@@ -219,16 +219,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 config --unset $name}
+                               catch {git config --unset $name}
                        } else {
                                regsub -all "\[{}\]" $value {"} value
-                               exec git 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 {}} {
@@ -292,7 +300,7 @@ proc ask_popup {msg} {
 ## 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
@@ -365,7 +373,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)/)? \
@@ -374,7 +382,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
@@ -402,7 +410,7 @@ proc PARENT {} {
                return $p
        }
        if {$empty_tree eq {}} {
-               set empty_tree [exec git mktree << {}]
+               set empty_tree [git mktree << {}]
        }
        return $empty_tree
 }
@@ -1042,7 +1050,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 {}
                }
@@ -1275,7 +1283,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.
@@ -1894,7 +1902,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 \
@@ -1904,7 +1912,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 \
@@ -1921,7 +1929,7 @@ proc do_create_branch_action {w} {
        tracking {set rev $create_branch_trackinghead}
        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 \
@@ -2100,7 +2108,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 \
@@ -2114,10 +2122,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
@@ -2155,7 +2163,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]
@@ -2366,7 +2374,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.
@@ -3245,6 +3253,15 @@ proc show_blame {commit path} {
        pack $w.path -side top -fill x
 
        frame $w.out
+       text $w.out.loaded_t \
+               -background white -borderwidth 0 \
+               -state disabled \
+               -wrap none \
+               -height 40 \
+               -width 1 \
+               -font font_diff
+       $w.out.loaded_t tag conf annotated -background grey
+
        text $w.out.linenumber_t \
                -background white -borderwidth 0 \
                -state disabled \
@@ -3252,7 +3269,6 @@ proc show_blame {commit path} {
                -height 40 \
                -width 5 \
                -font font_diff
-       $w.out.linenumber_t tag conf annotated -background grey
        $w.out.linenumber_t tag conf linenumber -justify right
 
        text $w.out.file_t \
@@ -3267,12 +3283,18 @@ proc show_blame {commit path} {
        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.file_t $w.out.sby -sticky nsew
-       grid conf $w.out.sbx -column 1 -sticky we
-       grid columnconfigure $w.out 1 -weight 1
+       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
 
@@ -3306,22 +3328,26 @@ proc show_blame {commit path} {
                -font font_ui \
                -command "blame_copycommit $w \$cursorW @\$cursorX,\$cursorY"
 
-       foreach i [list $w.out.linenumber_t $w.out.file_t] {
+       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 [list \
+                       $w.out.loaded_t \
                        $w.out.linenumber_t \
                        $w.out.file_t \
                        ] yview $w.out.sby]
                bind $i <Button-1> "
-                       blame_highlight {$w} \\
+                       blame_click {$w} \\
                                $w.cm.t \\
                                $w.out.linenumber_t \\
                                $w.out.file_t \\
                                $i @%x,%y
-                       break
+                       focus $i
                "
                bind_button3 $i "
                        set cursorX %x
@@ -3331,6 +3357,7 @@ proc show_blame {commit path} {
                "
        }
 
+       bind $w.cm.t <Button-1> "focus $w.cm.t"
        bind $tl <Visibility> "focus $tl"
        bind $tl <Destroy> "
                array unset blame_status {$w}
@@ -3338,19 +3365,22 @@ proc show_blame {commit path} {
        "
        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 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 \
-               $w.cm.t $w.out.linenumber_t $w.out.file_t]
+               $w.cm.t $w.out.loaded_t $w.out.linenumber_t $w.out.file_t]
 }
 
-proc read_blame_catfile {fd w commit path w_cmit w_line 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]} {
@@ -3359,14 +3389,17 @@ proc read_blame_catfile {fd w commit path w_cmit w_line w_file} {
        }
 
        set n $blame_data($w,total_lines)
+       $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_load insert end "\n"
                $w_line insert end "$n\n" linenumber
                $w_file insert end "$line\n"
        }
+       $w_load conf -state disabled
        $w_line conf -state disabled
        $w_file conf -state disabled
        set blame_data($w,total_lines) $n
@@ -3379,11 +3412,11 @@ proc read_blame_catfile {fd w commit path w_cmit w_line w_file} {
                set fd [open "| $cmd" r]
                fconfigure $fd -blocking 0 -translation lf -encoding binary
                fileevent $fd readable [list read_blame_incremental $fd $w \
-                       $w_cmit $w_line $w_file]
+                       $w_load $w_cmit $w_line $w_file]
        }
 }
 
-proc read_blame_incremental {fd w w_cmit w_line 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_file]} {
@@ -3399,12 +3432,15 @@ proc read_blame_incremental {fd w w_cmit w_line w_file} {
                        set blame_data($w,final_line) $final_line
                        set blame_data($w,line_count) $line_count
 
-                       if {[catch {set g $blame_data($w,$cmit,seen)}]} {
+                       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
-                               set blame_data($w,$cmit,seen) 1
+                               $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]
@@ -3414,7 +3450,7 @@ proc read_blame_incremental {fd w w_cmit w_line w_file} {
 
                        while {$n > 0} {
                                if {[catch {set g g$blame_data($w,line$lno,commit)}]} {
-                                       $w_line tag add annotated $lno.0 "$lno.0 lineend + 1c"
+                                       $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"
@@ -3438,6 +3474,14 @@ proc read_blame_incremental {fd w w_cmit w_line w_file} {
                                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
                }
@@ -3462,7 +3506,7 @@ proc blame_incremental_status {w} {
                        / $blame_data($w,total_lines)}]]
 }
 
-proc blame_highlight {w w_cmit w_line w_file cur_w pos} {
+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
 
@@ -3474,25 +3518,41 @@ proc blame_highlight {w w_cmit w_line w_file cur_w pos} {
        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_data repo_config
+       global blame_colors blame_data repo_config
 
        set cmit $blame_data($w,highlight_commit)
        if {$cmit ne {}} {
-               $w_line tag conf g$cmit -background white
-               $w_file tag conf g$cmit -background white
-               $w_line tag raise annotated g$cmit
+               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 "Computing..."
+               $w_cmit insert end "Loading annotation..."
        } else {
-               $w_line tag conf g$cmit -background yellow
-               $w_file tag conf g$cmit -background yellow
-               $w_line tag raise g$cmit annotated
+               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 {}
@@ -3963,14 +4023,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.
        #
@@ -4117,7 +4169,7 @@ proc do_quit {} {
                        set rc_geometry {}
                }
                if {$cfg_geometry ne $rc_geometry} {
-                       catch {exec git config gui.geometry $cfg_geometry}
+                       catch {git config gui.geometry $cfg_geometry}
                }
        }
 
@@ -4368,7 +4420,7 @@ $copyright" \
 
        set v {}
        append v "[appname] version $appvers\n"
-       append v "[exec git version]\n"
+       append v "[git version]\n"
        append v "\n"
        if {$tcl_patchLevel eq $tk_patchLevel} {
                append v "Tcl/Tk version $tcl_patchLevel"
@@ -5860,7 +5912,7 @@ if {[is_enabled transport]} {
 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.