Code

git gui: show diffs with a minimum of 1 context line
[git.git] / git-gui.sh
index 7c25bb98089ec4c0a57993020a30d2d1afad8dae..86402d49f72c56d793f2f1958a9e3d3f8300f367 100755 (executable)
@@ -52,7 +52,11 @@ catch {rename send {}} ; # What an evil concept...
 set oguilib {@@GITGUI_LIBDIR@@}
 set oguirel {@@GITGUI_RELATIVE@@}
 if {$oguirel eq {1}} {
-       set oguilib [file dirname [file dirname [file normalize $argv0]]]
+       set oguilib [file dirname [file normalize $argv0]]
+       if {[file tail $oguilib] eq {git-core}} {
+               set oguilib [file dirname $oguilib]
+       }
+       set oguilib [file dirname $oguilib]
        set oguilib [file join $oguilib share git-gui lib]
        set oguimsg [file join $oguilib msgs]
 } elseif {[string match @@* $oguirel]} {
@@ -122,6 +126,14 @@ set _reponame {}
 set _iscygwin {}
 set _search_path {}
 
+set _trace [lsearch -exact $argv --trace]
+if {$_trace >= 0} {
+       set argv [lreplace $argv $_trace $_trace]
+       set _trace 1
+} else {
+       set _trace 0
+}
+
 proc appname {} {
        global _appname
        return $_appname
@@ -245,6 +257,21 @@ proc get_config {name} {
 ##
 ## handy utils
 
+proc _trace_exec {cmd} {
+       if {!$::_trace} return
+       set d {}
+       foreach v $cmd {
+               if {$d ne {}} {
+                       append d { }
+               }
+               if {[regexp {[ \t\r\n'"$?*]} $v]} {
+                       set v [sq $v]
+               }
+               append d $v
+       }
+       puts stderr $d
+}
+
 proc _git_cmd {name} {
        global _git_cmd_path
 
@@ -294,7 +321,7 @@ proc _git_cmd {name} {
        return $v
 }
 
-proc _which {what} {
+proc _which {what args} {
        global env _search_exe _search_path
 
        if {$_search_path eq {}} {
@@ -317,8 +344,14 @@ proc _which {what} {
                }
        }
 
+       if {[is_Windows] && [lsearch -exact $args -script] >= 0} {
+               set suffix {}
+       } else {
+               set suffix $_search_exe
+       }
+
        foreach p $_search_path {
-               set p [file join $p $what$_search_exe]
+               set p [file join $p $what$suffix]
                if {[file exists $p]} {
                        return [file normalize $p]
                }
@@ -339,7 +372,7 @@ proc _lappend_nice {cmd_var} {
 }
 
 proc git {args} {
-       set opt [list exec]
+       set opt [list]
 
        while {1} {
                switch -- [lindex $args 0] {
@@ -359,12 +392,18 @@ proc git {args} {
        set cmdp [_git_cmd [lindex $args 0]]
        set args [lrange $args 1 end]
 
-       return [eval $opt $cmdp $args]
+       _trace_exec [concat $opt $cmdp $args]
+       set result [eval exec $opt $cmdp $args]
+       if {$::_trace} {
+               puts stderr "< $result"
+       }
+       return $result
 }
 
 proc _open_stdout_stderr {cmd} {
+       _trace_exec $cmd
        if {[catch {
-                       set fd [open $cmd r]
+                       set fd [open [concat [list | ] $cmd] r]
                } err]} {
                if {   [lindex $cmd end] eq {2>@1}
                    && $err eq {can not find channel named "1"}
@@ -375,6 +414,7 @@ proc _open_stdout_stderr {cmd} {
                        # to try to start it a second time.
                        #
                        set fd [open [concat \
+                               [list | ] \
                                [lrange $cmd 0 end-1] \
                                [list |& cat] \
                                ] r]
@@ -387,7 +427,7 @@ proc _open_stdout_stderr {cmd} {
 }
 
 proc git_read {args} {
-       set opt [list |]
+       set opt [list]
 
        while {1} {
                switch -- [lindex $args 0] {
@@ -415,7 +455,7 @@ proc git_read {args} {
 }
 
 proc git_write {args} {
-       set opt [list |]
+       set opt [list]
 
        while {1} {
                switch -- [lindex $args 0] {
@@ -435,17 +475,18 @@ proc git_write {args} {
        set cmdp [_git_cmd [lindex $args 0]]
        set args [lrange $args 1 end]
 
-       return [open [concat $opt $cmdp $args] w]
+       _trace_exec [concat $opt $cmdp $args]
+       return [open [concat [list | ] $opt $cmdp $args] w]
 }
 
 proc githook_read {hook_name args} {
        set pchook [gitdir hooks $hook_name]
        lappend args 2>@1
 
-       # On Cygwin [file executable] might lie so we need to ask
+       # On Windows [file executable] might lie so we need to ask
        # the shell if the hook is executable.  Yes that's annoying.
        #
-       if {[is_Cygwin]} {
+       if {[is_Windows]} {
                upvar #0 _sh interp
                if {![info exists interp]} {
                        set interp [_which sh]
@@ -455,17 +496,31 @@ proc githook_read {hook_name args} {
                }
 
                set scr {if test -x "$1";then exec "$@";fi}
-               set sh_c [list $interp -c $scr $interp $pchook]
+               set sh_c [list $interp -c $scr $interp $pchook]
                return [_open_stdout_stderr [concat $sh_c $args]]
        }
 
        if {[file executable $pchook]} {
-               return [_open_stdout_stderr [concat [list $pchook] $args]]
+               return [_open_stdout_stderr [concat [list $pchook] $args]]
        }
 
        return {}
 }
 
+proc kill_file_process {fd} {
+       set process [pid $fd]
+
+       catch {
+               if {[is_Windows]} {
+                       # Use a Cygwin-specific flag to allow killing
+                       # native Windows processes
+                       exec kill -f $process
+               } else {
+                       exec kill $process
+               }
+       }
+}
+
 proc sq {value} {
        regsub -all ' $value "'\\''" value
        return "'$value'"
@@ -601,6 +656,7 @@ proc apply_config {} {
        }
 }
 
+set default_config(branch.autosetupmerge) true
 set default_config(merge.diffstat) true
 set default_config(merge.summary) false
 set default_config(merge.verbosity) 2
@@ -610,6 +666,8 @@ 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.fastcopyblame) false
+set default_config(gui.copyblamethreshold) 40
 set default_config(gui.diffcontext) 5
 set default_config(gui.commitmsgwidth) 75
 set default_config(gui.newbranchtemplate) {}
@@ -1095,27 +1153,18 @@ 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]]
-               }
+               global is_git_info_exclude
 
-               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
-                               }
+               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]]
                }
+               return $is_git_info_exclude
        }
 } else {
        proc have_info_exclude {} {
@@ -1647,10 +1696,10 @@ proc do_gitk {revs} {
        # -- Always start gitk through whatever we were loaded with.  This
        #    lets us bypass using shell process on Windows systems.
        #
-       set exe [file join [file dirname $::_git] gitk]
+       set exe [_which gitk -script]
        set cmd [list [info nameofexecutable] $exe]
-       if {! [file exists $exe]} {
-               error_popup [mc "Unable to start gitk:\n\n%s does not exist" $exe]
+       if {$exe eq {}} {
+               error_popup [mc "Couldn't find gitk in PATH"]
        } else {
                global env
 
@@ -1751,6 +1800,11 @@ proc do_commit {} {
        commit_tree
 }
 
+proc next_diff {} {
+       global next_diff_p next_diff_w next_diff_i
+       show_diff $next_diff_p $next_diff_w $next_diff_i
+}
+
 proc toggle_or_diff {w x y} {
        global file_states file_lists current_diff_path ui_index ui_workdir
        global last_clicked selected_paths
@@ -1769,12 +1823,34 @@ proc toggle_or_diff {w x y} {
        $ui_index tag remove in_sel 0.0 end
        $ui_workdir tag remove in_sel 0.0 end
 
-       if {$col == 0} {
-               if {$current_diff_path eq $path} {
+       if {$col == 0 && $y > 1} {
+               set i [expr {$lno-1}]
+               set ll [expr {[llength $file_lists($w)]-1}]
+
+               if {$i == $ll && $i == 0} {
                        set after {reshow_diff;}
                } else {
-                       set after {}
+                       global next_diff_p next_diff_w next_diff_i
+
+                       set next_diff_w $w
+
+                       if {$i < $ll} {
+                               set i [expr {$i + 1}]
+                               set next_diff_i $i
+                       } else {
+                               set next_diff_i $i
+                               set i [expr {$i - 1}]
+                       }
+
+                       set next_diff_p [lindex $file_lists($w) $i]
+
+                       if {$next_diff_p ne {} && $current_diff_path ne {}} {
+                               set after {next_diff;}
+                       } else {
+                               set after {}
+                       }
                }
+
                if {$w eq $ui_index} {
                        update_indexinfo \
                                "Unstaging [short_path $path] from commit" \
@@ -1856,7 +1932,7 @@ proc show_more_context {} {
 
 proc show_less_context {} {
        global repo_config
-       if {$repo_config(gui.diffcontext) >= 1} {
+       if {$repo_config(gui.diffcontext) > 1} {
                incr repo_config(gui.diffcontext) -1
                reshow_diff
        }
@@ -1945,9 +2021,13 @@ if {[is_enabled multicommit]} {
        }
 }
 
-.mbar.repository add command -label [mc Quit] \
-       -command do_quit \
-       -accelerator $M1T-Q
+if {[is_MacOSX]} {
+       proc ::tk::mac::Quit {args} { do_quit }
+} else {
+       .mbar.repository add command -label [mc Quit] \
+               -command do_quit \
+               -accelerator $M1T-Q
+}
 
 # -- Edit Menu
 #
@@ -2616,6 +2696,11 @@ $ctxm add command \
        -command {apply_hunk $cursorX $cursorY}
 set ui_diff_applyhunk [$ctxm index last]
 lappend diff_actions [list $ctxm entryconf $ui_diff_applyhunk -state]
+$ctxm add command \
+       -label [mc "Apply/Reverse Line"] \
+       -command {apply_line $cursorX $cursorY; do_rescan}
+set ui_diff_applyline [$ctxm index last]
+lappend diff_actions [list $ctxm entryconf $ui_diff_applyline -state]
 $ctxm add separator
 $ctxm add command \
        -label [mc "Show Less Context"] \
@@ -2664,8 +2749,10 @@ proc popup_diff_menu {ctxm x y X Y} {
        set ::cursorY $y
        if {$::ui_index eq $::current_diff_side} {
                set l [mc "Unstage Hunk From Commit"]
+               set t [mc "Unstage Line From Commit"]
        } else {
                set l [mc "Stage Hunk For Commit"]
+               set t [mc "Stage Line For Commit"]
        }
        if {$::is_3way_diff
                || $current_diff_path eq {}
@@ -2676,6 +2763,7 @@ proc popup_diff_menu {ctxm x y X Y} {
                set s normal
        }
        $ctxm entryconf $::ui_diff_applyhunk -state $s -label $l
+       $ctxm entryconf $::ui_diff_applyline -state $s -label $t
        tk_popup $ctxm $X $Y
 }
 bind_button3 $ui_diff [list popup_diff_menu $ctxm %x %y %X %Y]
@@ -2847,6 +2935,7 @@ if {[is_enabled transport]} {
        populate_fetch_menu
        set n [expr {[.mbar.remote index end] - $n}]
        if {$n > 0} {
+               if {[.mbar.remote type 0] eq "tearoff"} { incr n }
                .mbar.remote insert $n separator
        }
        unset n