X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=git-gui%2Flib%2Fblame.tcl;h=827c85d67f0b0f18d22b1399624e6819cf93f23f;hb=dde4af4313c6fba5269df384d97ef4fa3ab5971d;hp=00ecf21333c976d4c6002cd66a055803362f3523;hpb=e98d6df75260312e906278705f78f2eee39cc8fc;p=git.git diff --git a/git-gui/lib/blame.tcl b/git-gui/lib/blame.tcl index 00ecf2133..827c85d67 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 @@ -65,7 +58,7 @@ field tooltip_t {} ; # Text widget in $tooltip_wm field tooltip_timer {} ; # Current timer event for our tooltip field tooltip_commit {} ; # Commit(s) in tooltip -constructor new {i_commit i_path} { +constructor new {i_commit i_path i_jump} { global cursor_ptr variable active_color variable group_colors @@ -80,6 +73,7 @@ constructor new {i_commit i_path} { label $w.header.commit_l \ -text [mc "Commit:"] \ -background gold \ + -foreground black \ -anchor w \ -justify left set w_back $w.header.commit_b @@ -89,6 +83,7 @@ constructor new {i_commit i_path} { -relief flat \ -state disabled \ -background gold \ + -foreground black \ -activebackground gold bind $w_back " if {\[$w_back cget -state\] eq {normal}} { @@ -98,16 +93,19 @@ constructor new {i_commit i_path} { label $w.header.commit \ -textvariable @commit \ -background gold \ + -foreground black \ -anchor w \ -justify left label $w.header.path_l \ -text [mc "File:"] \ -background gold \ + -foreground black \ -anchor w \ -justify left set w_path $w.header.path label $w_path \ -background gold \ + -foreground black \ -anchor w \ -justify left pack $w.header.commit_l -side left @@ -135,7 +133,9 @@ constructor new {i_commit i_path} { -takefocus 0 \ -highlightthickness 0 \ -padx 0 -pady 0 \ - -background white -borderwidth 0 \ + -background white \ + -foreground black \ + -borderwidth 0 \ -state disabled \ -wrap none \ -height 40 \ @@ -148,7 +148,9 @@ constructor new {i_commit i_path} { -takefocus 0 \ -highlightthickness 0 \ -padx 0 -pady 0 \ - -background white -borderwidth 0 \ + -background white \ + -foreground black \ + -borderwidth 0 \ -state disabled \ -wrap none \ -height 40 \ @@ -166,7 +168,9 @@ constructor new {i_commit i_path} { -takefocus 0 \ -highlightthickness 0 \ -padx 0 -pady 0 \ - -background white -borderwidth 0 \ + -background white \ + -foreground black \ + -borderwidth 0 \ -state disabled \ -wrap none \ -height 40 \ @@ -184,7 +188,9 @@ constructor new {i_commit i_path} { -takefocus 0 \ -highlightthickness 0 \ -padx 0 -pady 0 \ - -background white -borderwidth 0 \ + -background white \ + -foreground black \ + -borderwidth 0 \ -state disabled \ -wrap none \ -height 40 \ @@ -213,7 +219,9 @@ constructor new {i_commit i_path} { set w_cviewer $w.file_pane.cm.t text $w_cviewer \ - -background white -borderwidth 0 \ + -background white \ + -foreground black \ + -borderwidth 0 \ -state disabled \ -wrap none \ -height 10 \ @@ -248,6 +256,15 @@ 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] + $w.ctxm add command \ + -label [mc "Show History Context"] \ + -command [cb _gitkcommit] + $w.ctxm add command \ + -label [mc "Blame Parent Commit"] \ + -command [cb _blameparent] foreach i $w_columns { for {set g 0} {$g < [llength $group_colors]} {incr g} { @@ -318,7 +335,18 @@ constructor new {i_commit i_path} { bind $w.file_pane \ "if {{$w.file_pane} eq {%W}} {[cb _resize %h]}" - _load $this {} + wm protocol $top WM_DELETE_WINDOW "destroy $top" + bind $top [cb _kill] + + _load $this $i_jump +} + +method _kill {} { + if {$current_fd ne {}} { + kill_file_process $current_fd + catch {close $current_fd} + set current_fd {} + } } method _load {jump} { @@ -327,10 +355,7 @@ method _load {jump} { _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 @@ -496,7 +521,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} @@ -669,6 +693,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..."] @@ -681,6 +717,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 @@ -691,19 +793,27 @@ method _load_commit {cur_w cur_d pos} { set lno [lindex [split [$cur_w index $pos] .] 0] set dat [lindex $line_data $lno] if {$dat ne {}} { - lappend history [list \ - $commit $path \ - $highlight_column \ - $highlight_line \ - [lindex [$w_file xview] 0] \ - [lindex [$w_file yview] 0] \ - ] - set commit [lindex $dat 0] - set path [lindex $dat 1] - _load $this [list [lindex $dat 2]] + _load_new_commit $this \ + [lindex $dat 0] \ + [lindex $dat 1] \ + [list [lindex $dat 2]] } } +method _load_new_commit {new_commit new_path jump} { + lappend history [list \ + $commit $path \ + $highlight_column \ + $highlight_line \ + [lindex [$w_file xview] 0] \ + [lindex [$w_file yview] 0] \ + ] + + set commit $new_commit + set path $new_path + _load $this $jump +} + method _showcommit {cur_w lno} { global repo_config variable active_color @@ -809,10 +919,14 @@ method _showcommit {cur_w lno} { } } -method _copycommit {} { +method _get_click_amov_info {} { set pos @$::cursorX,$::cursorY set lno [lindex [split [$::cursorW index $pos] .] 0] - set dat [lindex $amov_data $lno] + return [lindex $amov_data $lno] +} + +method _copycommit {} { + set dat [_get_click_amov_info $this] if {$dat ne {}} { clipboard clear clipboard append \ @@ -822,6 +936,124 @@ method _copycommit {} { } } +method _format_offset_date {base offset} { + set exval [expr {$base + $offset*24*60*60}] + return [clock format $exval -format {%Y-%m-%d}] +} + +method _gitkcommit {} { + set dat [_get_click_amov_info $this] + if {$dat ne {}} { + set cmit [lindex $dat 0] + set radius [get_config gui.blamehistoryctx] + set cmdline [list --select-commit=$cmit] + + if {$radius > 0} { + set author_time {} + set committer_time {} + + catch {set author_time $header($cmit,author-time)} + catch {set committer_time $header($cmit,committer-time)} + + if {$committer_time eq {}} { + set committer_time $author_time + } + + set after_time [_format_offset_date $this $committer_time [expr {-$radius}]] + set before_time [_format_offset_date $this $committer_time $radius] + + lappend cmdline --after=$after_time --before=$before_time + } + + lappend cmdline $cmit + + set base_rev "HEAD" + if {$commit ne {}} { + set base_rev $commit + } + + if {$base_rev ne $cmit} { + lappend cmdline $base_rev + } + + do_gitk $cmdline + } +} + +method _blameparent {} { + set dat [_get_click_amov_info $this] + if {$dat ne {}} { + set cmit [lindex $dat 0] + set new_path [lindex $dat 1] + + if {[catch {set cparent [git rev-parse --verify "$cmit^"]}]} { + error_popup [strcat [mc "Cannot find parent commit:"] "\n\n$err"] + return; + } + + _kill $this + + # Generate a diff between the commit and its parent, + # and use the hunks to update the line number. + # Request zero context to simplify calculations. + if {[catch {set fd [eval git_read diff-tree \ + --unified=0 $cparent $cmit $new_path]} err]} { + $status stop [mc "Unable to display parent"] + error_popup [strcat [mc "Error loading diff:"] "\n\n$err"] + return + } + + set r_orig_line [lindex $dat 2] + + fconfigure $fd \ + -blocking 0 \ + -encoding binary \ + -translation binary + fileevent $fd readable [cb _read_diff_load_commit \ + $fd $cparent $new_path $r_orig_line] + set current_fd $fd + } +} + +method _read_diff_load_commit {fd cparent new_path tline} { + if {$fd ne $current_fd} { + catch {close $fd} + return + } + + while {[gets $fd line] >= 0} { + if {[regexp {^@@ -(\d+)(,(\d+))? \+(\d+)(,(\d+))? @@} $line line \ + old_line osz old_size new_line nsz new_size]} { + + if {$osz eq {}} { set old_size 1 } + if {$nsz eq {}} { set new_size 1 } + + if {$new_line <= $tline} { + if {[expr {$new_line + $new_size}] > $tline} { + # Target line within the hunk + set line_shift [expr { + ($new_size-$old_size)*($tline-$new_line)/$new_size + }] + } else { + set line_shift [expr {$new_size-$old_size}] + } + + set r_orig_line [expr {$r_orig_line - $line_shift}] + } + } + } + + if {[eof $fd]} { + close $fd; + set current_fd {} + + _load_new_commit $this \ + $cparent \ + $new_path \ + [list $r_orig_line] + } +} ifdeleted { catch {close $fd} } + method _show_tooltip {cur_w pos} { if {$tooltip_wm ne {}} { _open_tooltip $this $cur_w