Browse Source

git-gui: Run blame twice on the same file and display both outputs

We now perform two passes over any input file given to the blame
viewer.  Our first pass is a quick "git-blame" with no options,
getting the details of how each line arrived into this file.  We
are specifically ignoring/omitting the rename detection logic as
this first pass is to determine why things got into the state they
are in.

Once the first pass is complete and is displayed in the UI we run
a second pass, using the much more CPU intensive "-M -C -C" options
to perform extensive rename/movement detection.  The output of this
second pass is shown in a different column, allowing the user to see
for any given line how it got to be, and if it came from somewhere
else, where that is.

This is actually very instructive when run on our own lib/branch.tcl
script.  That file grew recently out of a very large block of code
in git-gui.sh.  The first pass shows when I created that file, while
the second pass shows the original commit information.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
maint
Shawn O. Pearce 18 years ago
parent
commit
172c92b475
  1. 176
      lib/blame.tcl

176
lib/blame.tcl

@ -18,6 +18,7 @@ field w_path ; # label showing the current file path @@ -18,6 +18,7 @@ field w_path ; # label showing the current file path
field w_columns ; # list of all column widgets in the viewer
field w_line ; # text column: all line numbers
field w_amov ; # text column: annotations + move tracking
field w_asim ; # text column: annotations (simple computation)
field w_file ; # text column: actual file data
field w_cviewer ; # pane showing commit message
field status ; # text variable bound to status bar
@ -39,6 +40,7 @@ field path ; # input filename to view in $commit @@ -39,6 +40,7 @@ field path ; # input filename to view in $commit

field current_fd {} ; # background process running
field highlight_line -1 ; # current line selected
field highlight_column {} ; # current commit column selected
field highlight_commit {} ; # sha1 of commit selected
field old_bgcolor {} ; # background of current selection

@ -46,6 +48,7 @@ field total_lines 0 ; # total length of file @@ -46,6 +48,7 @@ field total_lines 0 ; # total length of file
field blame_lines 0 ; # number of lines computed
field have_commit ; # array commit -> 1
field amov_data ; # list of {commit origfile origline}
field asim_data ; # list of {commit origfile origline}

field r_commit ; # commit currently being parsed
field r_orig_line ; # original line number
@ -142,15 +145,32 @@ constructor new {i_commit i_path} { @@ -142,15 +145,32 @@ constructor new {i_commit i_path} {
-state disabled \
-wrap none \
-height 40 \
-width 4 \
-width 5 \
-font font_diff
$w_amov tag conf author_abbr -justify right -rmargin 5
$w_amov tag conf curr_commit
$w_amov tag conf prior_commit \
-foreground blue \
-underline 1
$w_amov tag conf prior_commit -foreground blue -underline 1
$w_amov tag bind prior_commit \
<Button-1> \
"[cb _load_commit @%x,%y];break"
"[cb _load_commit $w_amov @amov_data @%x,%y];break"

set w_asim $w.file_pane.out.asimple_t
text $w_asim \
-takefocus 0 \
-highlightthickness 0 \
-padx 0 -pady 0 \
-background white -borderwidth 0 \
-state disabled \
-wrap none \
-height 40 \
-width 4 \
-font font_diff
$w_asim tag conf author_abbr -justify right
$w_asim tag conf curr_commit
$w_asim tag conf prior_commit -foreground blue -underline 1
$w_asim tag bind prior_commit \
<Button-1> \
"[cb _load_commit $w_asim @asim_data @%x,%y];break"

set w_file $w.file_pane.out.file_t
text $w_file \
@ -165,7 +185,7 @@ constructor new {i_commit i_path} { @@ -165,7 +185,7 @@ constructor new {i_commit i_path} {
-xscrollcommand [list $w.file_pane.out.sbx set] \
-font font_diff

set w_columns [list $w_amov $w_line $w_file]
set w_columns [list $w_amov $w_asim $w_line $w_file]

scrollbar $w.file_pane.out.sbx \
-orient h \
@ -312,9 +332,9 @@ method _load {} { @@ -312,9 +332,9 @@ method _load {} {
}

set highlight_line -1
set highlight_column {}
set highlight_commit {}
set total_lines 0
set blame_lines 0
array unset have_commit
}

@ -343,6 +363,7 @@ method _load {} { @@ -343,6 +363,7 @@ method _load {} {
# git-blame output and with Tk's text widget.
#
set amov_data [list [list]]
set asim_data [list [list]]

set status "Loading $commit:[escape_path $path]..."
$w_path conf -text [escape_path $path]
@ -410,6 +431,7 @@ method _read_file {fd} { @@ -410,6 +431,7 @@ method _read_file {fd} {
regsub "\r\$" $line {} line
incr total_lines
lappend amov_data {}
lappend asim_data {}

if {$total_lines > 1} {
foreach i $w_columns {$i insert end "\n"}
@ -428,29 +450,37 @@ method _read_file {fd} { @@ -428,29 +450,37 @@ method _read_file {fd} {

if {[eof $fd]} {
close $fd

_status $this
set cmd {nice git blame -M -C --incremental}
if {$commit eq {}} {
lappend cmd --contents $path
} else {
lappend cmd $commit
}
lappend cmd -- $path
set fd [open "| $cmd" r]
fconfigure $fd -blocking 0 -translation lf -encoding binary
fileevent $fd readable [cb _read_blame $fd]
set current_fd $fd
_exec_blame $this $w_asim @asim_data [list] {}
}
} ifdeleted { catch {close $fd} }

method _read_blame {fd} {
method _exec_blame {cur_w cur_d options cur_s} {
set cmd [list nice git blame]
set cmd [concat $cmd $options]
lappend cmd --incremental
if {$commit eq {}} {
lappend cmd --contents $path
} else {
lappend cmd $commit
}
lappend cmd -- $path
set fd [open "| $cmd" r]
fconfigure $fd -blocking 0 -translation lf -encoding binary
fileevent $fd readable [cb _read_blame $fd $cur_w $cur_d $cur_s]
set current_fd $fd
set blame_lines 0
_status $this $cur_s
}

method _read_blame {fd cur_w cur_d cur_s} {
upvar #0 $cur_d line_data

if {$fd ne $current_fd} {
catch {close $fd}
return
}

$w_amov conf -state normal
$cur_w conf -state normal
while {[gets $fd line] >= 0} {
if {[regexp {^([a-z0-9]{40}) (\d+) (\d+) (\d+)$} $line line \
cmit original_line final_line line_count]} {
@ -482,7 +512,7 @@ method _read_blame {fd} { @@ -482,7 +512,7 @@ method _read_blame {fd} {
set commit_type curr_commit
} else {
set commit_type prior_commit
set commit_abbr [string range $cmit 0 4]
set commit_abbr [string range $cmit 0 3]
}

set author_abbr {}
@ -500,51 +530,50 @@ method _read_blame {fd} { @@ -500,51 +530,50 @@ method _read_blame {fd} {
set author_abbr { |}
} else {
set author_abbr [string range $author_abbr 0 3]
while {[string length $author_abbr] < 4} {
set author_abbr " $author_abbr"
}
}
unset a_name

set first_lno $lno
while {
$first_lno > 1
&& $cmit eq [lindex $amov_data [expr {$first_lno - 1}] 0]
&& $file eq [lindex $amov_data [expr {$first_lno - 1}] 1]
&& $cmit eq [lindex $line_data [expr {$first_lno - 1}] 0]
&& $file eq [lindex $line_data [expr {$first_lno - 1}] 1]
} {
incr first_lno -1
}

while {$n > 0} {
set lno_e "$lno.0 lineend + 1c"
if {[lindex $amov_data $lno] ne {}} {
set g [lindex $amov_data $lno 0]
if {[lindex $line_data $lno] ne {}} {
set g [lindex $line_data $lno 0]
foreach i $w_columns {
$i tag remove g$g $lno.0 $lno_e
}
}
lset amov_data $lno [list $cmit $file]
lset line_data $lno [list $cmit $file]

$w_amov delete $lno.0 "$lno.0 lineend"
$cur_w delete $lno.0 "$lno.0 lineend"
if {$lno == $first_lno} {
$w_amov insert $lno.0 $commit_abbr $commit_type
$cur_w insert $lno.0 $commit_abbr $commit_type
} elseif {$lno == [expr {$first_lno + 1}]} {
$w_amov insert $lno.0 $author_abbr
$cur_w insert $lno.0 $author_abbr author_abbr
} else {
$w_amov insert $lno.0 { |}
$cur_w insert $lno.0 { |}
}

foreach i $w_columns {
$i tag add g$cmit $lno.0 $lno_e
}

if {$highlight_line == -1} {
if {[lindex [$w_file yview] 0] == 0} {
if {$highlight_column eq $cur_w} {
if {$highlight_line == -1
&& [lindex [$w_file yview] 0] == 0} {
$w_file see $lno.0
_showcommit $this $lno
set highlight_line $lno
}
if {$highlight_line == $lno} {
_showcommit $this $cur_w $lno
}
} elseif {$highlight_line == $lno} {
_showcommit $this $lno
}

incr n -1
@ -553,17 +582,17 @@ method _read_blame {fd} { @@ -553,17 +582,17 @@ method _read_blame {fd} {
}

while {
$cmit eq [lindex $amov_data $lno 0]
&& $file eq [lindex $amov_data $lno 1]
$cmit eq [lindex $line_data $lno 0]
&& $file eq [lindex $line_data $lno 1]
} {
$w_amov delete $lno.0 "$lno.0 lineend"
$cur_w delete $lno.0 "$lno.0 lineend"

if {$lno == $first_lno} {
$w_amov insert $lno.0 $commit_abbr $commit_type
$cur_w insert $lno.0 $commit_abbr $commit_type
} elseif {$lno == [expr {$first_lno + 1}]} {
$w_amov insert $lno.0 $author_abbr
$cur_w insert $lno.0 $author_abbr author_abbr
} else {
$w_amov insert $lno.0 { |}
$cur_w insert $lno.0 { |}
}
incr lno
}
@ -572,39 +601,45 @@ method _read_blame {fd} { @@ -572,39 +601,45 @@ method _read_blame {fd} {
set header($r_commit,$key) $data
}
}
$w_amov conf -state disabled
$cur_w conf -state disabled

if {[eof $fd]} {
close $fd
set current_fd {}
set status {Annotation complete.}
destroy $w.status.c
if {$cur_w eq $w_asim} {
_exec_blame $this $w_amov @amov_data \
[list -M -C -C] \
{ move/copy tracking}
} else {
set current_fd {}
set status {Annotation complete.}
destroy $w.status.c
}
} else {
_status $this
_status $this $cur_s
}
} ifdeleted { catch {close $fd} }

method _status {} {
method _status {cur_s} {
set have $blame_lines
set total $total_lines
set pdone 0
if {$total} {set pdone [expr {100 * $have / $total}]}

set status [format \
"Loading annotations... %i of %i lines annotated (%2i%%)" \
$have $total $pdone]
"Loading%s annotations... %i of %i lines annotated (%2i%%)" \
$cur_s $have $total $pdone]
$w.status.c coords bar 0 0 $pdone 20
}

method _click {cur_w pos} {
set lno [lindex [split [$cur_w index $pos] .] 0]
if {$lno eq {}} return
_showcommit $this $lno
_showcommit $this $cur_w $lno
}

method _load_commit {pos} {
set lno [lindex [split [$w_amov index $pos] .] 0]
set dat [lindex $amov_data $lno]
method _load_commit {cur_w cur_d pos} {
upvar #0 $cur_d line_data
set lno [lindex [split [$cur_w index $pos] .] 0]
set dat [lindex $line_data $lno]
if {$dat ne {}} {
set commit [lindex $dat 0]
set path [lindex $dat 1]
@ -612,7 +647,7 @@ method _load_commit {pos} { @@ -612,7 +647,7 @@ method _load_commit {pos} {
}
}

method _showcommit {lno} {
method _showcommit {cur_w lno} {
global repo_config

if {$highlight_commit ne {}} {
@ -621,10 +656,17 @@ method _showcommit {lno} { @@ -621,10 +656,17 @@ method _showcommit {lno} {
}
}

if {$cur_w eq $w_amov} {
set dat [lindex $amov_data $lno]
set highlight_column $w_amov
} else {
set dat [lindex $asim_data $lno]
set highlight_column $w_asim
}

$w_cviewer conf -state normal
$w_cviewer delete 0.0 end

set dat [lindex $amov_data $lno]
if {$dat eq {}} {
set cmit {}
$w_cviewer insert end "Loading annotation..." still_loading
@ -724,7 +766,11 @@ method _copycommit {} { @@ -724,7 +766,11 @@ method _copycommit {} {

method _show_tooltip {cur_w pos} {
set lno [lindex [split [$cur_w index $pos] .] 0]
set dat [lindex $amov_data $lno]
if {$cur_w eq $w_amov} {
set dat [lindex $amov_data $lno]
} else {
set dat [lindex $asim_data $lno]
}
if {$dat eq {}} {
_hide_tooltip $this
return
@ -758,7 +804,11 @@ method _open_tooltip {cur_w} { @@ -758,7 +804,11 @@ method _open_tooltip {cur_w} {
[expr {$pos_x - [winfo rootx $cur_w]}] \
[expr {$pos_y - [winfo rooty $cur_w]}]] ,]
set lno [lindex [split [$cur_w index $pos] .] 0]
set dat [lindex $amov_data $lno]
if {$cur_w eq $w_amov} {
set dat [lindex $amov_data $lno]
} else {
set dat [lindex $asim_data $lno]
}
set cmit [lindex $dat 0]
set file [lindex $dat 1]


Loading…
Cancel
Save