diff options
-rwxr-xr-x | git-gui/git-gui.sh | 26 | ||||
-rw-r--r-- | git-gui/lib/blame.tcl | 105 | ||||
-rw-r--r-- | git-gui/lib/diff.tcl | 64 | ||||
-rw-r--r-- | git-gui/lib/option.tcl | 2 | ||||
-rw-r--r-- | git-gui/macosx/AppMain.tcl | 2 |
5 files changed, 178 insertions, 21 deletions
diff --git a/git-gui/git-gui.sh b/git-gui/git-gui.sh index 940677cbd8..7c27a43a5d 100755 --- a/git-gui/git-gui.sh +++ b/git-gui/git-gui.sh @@ -473,10 +473,10 @@ 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] @@ -497,6 +497,20 @@ proc githook_read {hook_name 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'" @@ -642,6 +656,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) {} @@ -1670,10 +1686,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] 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 diff --git a/git-gui/lib/blame.tcl b/git-gui/lib/blame.tcl index 92fac1bad4..b6e42cbc8f 100644 --- a/git-gui/lib/blame.tcl +++ b/git-gui/lib/blame.tcl @@ -33,13 +33,6 @@ variable group_colors { #ececec } -# Switches for original location detection -# -variable original_options [list -C -C] -if {[git-version >= 1.5.3]} { - lappend original_options -w ; # ignore indentation changes -} - # Current blame data; cleared/reset on each load # field commit ; # input commit to blame @@ -263,6 +256,9 @@ constructor new {i_commit i_path} { $w.ctxm add command \ -label [mc "Copy Commit"] \ -command [cb _copycommit] + $w.ctxm add command \ + -label [mc "Do Full Copy Detection"] \ + -command [cb _fullcopyblame] foreach i $w_columns { for {set g 0} {$g < [llength $group_colors]} {incr g} { @@ -333,19 +329,27 @@ constructor new {i_commit i_path} { bind $w.file_pane <Configure> \ "if {{$w.file_pane} eq {%W}} {[cb _resize %h]}" + wm protocol $top WM_DELETE_WINDOW "destroy $top" + bind $top <Destroy> [cb _kill] + _load $this {} } +method _kill {} { + if {$current_fd ne {}} { + kill_file_process $current_fd + catch {close $current_fd} + set current_fd {} + } +} + method _load {jump} { variable group_colors _hide_tooltip $this if {$total_lines != 0 || $current_fd ne {}} { - if {$current_fd ne {}} { - catch {close $current_fd} - set current_fd {} - } + _kill $this foreach i $w_columns { $i conf -state normal @@ -511,7 +515,6 @@ method _exec_blame {cur_w cur_d options cur_s} { method _read_blame {fd cur_w cur_d} { upvar #0 $cur_d line_data variable group_colors - variable original_options if {$fd ne $current_fd} { catch {close $fd} @@ -684,6 +687,18 @@ method _read_blame {fd cur_w cur_d} { if {[eof $fd]} { close $fd if {$cur_w eq $w_asim} { + # Switches for original location detection + set threshold [get_config gui.copyblamethreshold] + set original_options [list "-C$threshold"] + + if {![is_config_true gui.fastcopyblame]} { + # thorough copy search; insert before the threshold + set original_options [linsert $original_options 0 -C] + } + if {[git-version >= 1.5.3]} { + lappend original_options -w ; # ignore indentation changes + } + _exec_blame $this $w_amov @amov_data \ $original_options \ [mc "Loading original location annotations..."] @@ -696,6 +711,72 @@ method _read_blame {fd cur_w cur_d} { } } ifdeleted { catch {close $fd} } +method _find_commit_bound {data_list start_idx delta} { + upvar #0 $data_list line_data + set pos $start_idx + set limit [expr {[llength $line_data] - 1}] + set base_commit [lindex $line_data $pos 0] + + while {$pos > 0 && $pos < $limit} { + set new_pos [expr {$pos + $delta}] + if {[lindex $line_data $new_pos 0] ne $base_commit} { + return $pos + } + + set pos $new_pos + } + + return $pos +} + +method _fullcopyblame {} { + if {$current_fd ne {}} { + tk_messageBox \ + -icon error \ + -type ok \ + -title [mc "Busy"] \ + -message [mc "Annotation process is already running."] + + return + } + + # Switches for original location detection + set threshold [get_config gui.copyblamethreshold] + set original_options [list -C -C "-C$threshold"] + + if {[git-version >= 1.5.3]} { + lappend original_options -w ; # ignore indentation changes + } + + # Find the line range + set pos @$::cursorX,$::cursorY + set lno [lindex [split [$::cursorW index $pos] .] 0] + set min_amov_lno [_find_commit_bound $this @amov_data $lno -1] + set max_amov_lno [_find_commit_bound $this @amov_data $lno 1] + set min_asim_lno [_find_commit_bound $this @asim_data $lno -1] + set max_asim_lno [_find_commit_bound $this @asim_data $lno 1] + + if {$min_asim_lno < $min_amov_lno} { + set min_amov_lno $min_asim_lno + } + + if {$max_asim_lno > $max_amov_lno} { + set max_amov_lno $max_asim_lno + } + + lappend original_options -L "$min_amov_lno,$max_amov_lno" + + # Clear lines + for {set i $min_amov_lno} {$i <= $max_amov_lno} {incr i} { + lset amov_data $i [list ] + } + + # Start the back-end process + _exec_blame $this $w_amov @amov_data \ + $original_options \ + [mc "Running thorough copy detection..."] +} + method _click {cur_w pos} { set lno [lindex [split [$cur_w index $pos] .] 0] _showcommit $this $cur_w $lno diff --git a/git-gui/lib/diff.tcl b/git-gui/lib/diff.tcl index 96ba94906c..77990c537c 100644 --- a/git-gui/lib/diff.tcl +++ b/git-gui/lib/diff.tcl @@ -411,6 +411,53 @@ proc apply_line {x y} { set hh [lindex [split $hh ,] 0] set hln [lindex [split $hh -] 1] + # There is a special situation to take care of. Consider this hunk: + # + # @@ -10,4 +10,4 @@ + # context before + # -old 1 + # -old 2 + # +new 1 + # +new 2 + # context after + # + # We used to keep the context lines in the order they appear in the + # hunk. But then it is not possible to correctly stage only + # "-old 1" and "+new 1" - it would result in this staged text: + # + # context before + # old 2 + # new 1 + # context after + # + # (By symmetry it is not possible to *un*stage "old 2" and "new 2".) + # + # We resolve the problem by introducing an asymmetry, namely, when + # a "+" line is *staged*, it is moved in front of the context lines + # that are generated from the "-" lines that are immediately before + # the "+" block. That is, we construct this patch: + # + # @@ -10,4 +10,5 @@ + # context before + # +new 1 + # old 1 + # old 2 + # context after + # + # But we do *not* treat "-" lines that are *un*staged in a special + # way. + # + # With this asymmetry it is possible to stage the change + # "old 1" -> "new 1" directly, and to stage the change + # "old 2" -> "new 2" by first staging the entire hunk and + # then unstaging the change "old 1" -> "new 1". + + # This is non-empty if and only if we are _staging_ changes; + # then it accumulates the consecutive "-" lines (after converting + # them to context lines) in order to be moved after the "+" change + # line. + set pre_context {} + set n 0 set i_l [$ui_diff index "$i_l + 1 lines"] set patch {} @@ -422,16 +469,27 @@ proc apply_line {x y} { [$ui_diff compare $the_l < $next_l]} { # the line to stage/unstage set ln [$ui_diff get $i_l $next_l] - set patch "$patch$ln" + if {$c1 eq {-}} { + set n [expr $n+1] + set patch "$patch$pre_context$ln" + } else { + set patch "$patch$ln$pre_context" + } + set pre_context {} } elseif {$c1 ne {-} && $c1 ne {+}} { # context line set ln [$ui_diff get $i_l $next_l] - set patch "$patch$ln" + set patch "$patch$pre_context$ln" set n [expr $n+1] + set pre_context {} } elseif {$c1 eq $to_context} { # turn change line into context line set ln [$ui_diff get "$i_l + 1 chars" $next_l] - set patch "$patch $ln" + if {$c1 eq {-}} { + set pre_context "$pre_context $ln" + } else { + set patch "$patch $ln" + } set n [expr $n+1] } set i_l $next_l diff --git a/git-gui/lib/option.tcl b/git-gui/lib/option.tcl index 9270512582..ffb3f00ff0 100644 --- a/git-gui/lib/option.tcl +++ b/git-gui/lib/option.tcl @@ -123,6 +123,8 @@ proc do_options {} { {b gui.trustmtime {mc "Trust File Modification Timestamps"}} {b gui.pruneduringfetch {mc "Prune Tracking Branches During Fetch"}} {b gui.matchtrackingbranch {mc "Match Tracking Branches"}} + {b gui.fastcopyblame {mc "Blame Copy Only On Changed Files"}} + {i-20..200 gui.copyblamethreshold {mc "Minimum Letters To Blame Copy On"}} {i-0..99 gui.diffcontext {mc "Number of Diff Context Lines"}} {i-0..99 gui.commitmsgwidth {mc "Commit Message Text Width"}} {t gui.newbranchtemplate {mc "New Branch Name Template"}} diff --git a/git-gui/macosx/AppMain.tcl b/git-gui/macosx/AppMain.tcl index 41ca08e2b7..ddbe6334a2 100644 --- a/git-gui/macosx/AppMain.tcl +++ b/git-gui/macosx/AppMain.tcl @@ -7,7 +7,7 @@ if {[string first -psn [lindex $argv 0]] == 0} { } if {[file tail [lindex $argv 0]] eq {gitk}} { - set argv0 [file join $gitexecdir gitk] + set argv0 [lindex $argv 0] set AppMain_source $argv0 } else { set argv0 [file join $gitexecdir [file tail [lindex $argv 0]]] |