diff options
Diffstat (limited to 'git-gui/lib/blame.tcl')
-rw-r--r-- | git-gui/lib/blame.tcl | 299 |
1 files changed, 154 insertions, 145 deletions
diff --git a/git-gui/lib/blame.tcl b/git-gui/lib/blame.tcl index 6d894e52d4..8b032d9590 100644 --- a/git-gui/lib/blame.tcl +++ b/git-gui/lib/blame.tcl @@ -1,18 +1,42 @@ # git-gui blame viewer # Copyright (C) 2006, 2007 Shawn Pearce -proc show_blame {commit path} { - global next_browser_id blame_status blame_data - - if {[winfo ismapped .]} { - set w .browser[incr next_browser_id] - set tl $w - toplevel $w - } else { - set w {} - set tl . - } - set blame_status($w) {Loading current file content...} +class blame { + +field commit ; # input commit to blame +field path ; # input filename to view in $commit + +field w +field w_line +field w_load +field w_file +field w_cmit +field status + +field highlight_line -1 ; # current line selected +field highlight_commit {} ; # sha1 of commit selected + +field total_lines 0 ; # total length of file +field blame_lines 0 ; # number of lines computed +field commit_count 0 ; # number of commits in $commit_list +field commit_list {} ; # list of commit sha1 in receipt order +field order ; # array commit -> receipt order +field header ; # array commit,key -> header field +field line_commit ; # array line -> sha1 commit +field line_file ; # array line -> file name + +field r_commit ; # commit currently being parsed +field r_orig_line ; # original line number +field r_final_line ; # final line number +field r_line_count ; # lines in this region + +constructor new {i_commit i_path} { + set commit $i_commit + set path $i_path + + make_toplevel top w + wm title $top "[appname] ([reponame]): File Viewer" + set status "Loading $commit:$path..." label $w.path -text "$commit:$path" \ -anchor w \ @@ -68,7 +92,8 @@ proc show_blame {commit path} { grid rowconfigure $w.out 0 -weight 1 pack $w.out -fill both -expand 1 - label $w.status -textvariable blame_status($w) \ + label $w.status \ + -textvariable @status \ -anchor w \ -justify left \ -borderwidth 1 \ @@ -93,8 +118,14 @@ proc show_blame {commit path} { pack $w.cm -side bottom -fill x menu $w.ctxm -tearoff 0 - $w.ctxm add command -label "Copy Commit" \ - -command "blame_copycommit $w \$cursorW @\$cursorX,\$cursorY" + $w.ctxm add command \ + -label "Copy Commit" \ + -command [cb _copycommit] + + set w_line $w.out.linenumber_t + set w_load $w.out.loaded_t + set w_file $w.out.file_t + set w_cmit $w.cm.t foreach i [list \ $w.out.loaded_t \ @@ -109,14 +140,7 @@ proc show_blame {commit path} { $w.out.linenumber_t \ $w.out.file_t \ ] yview $w.out.sby] - bind $i <Button-1> " - blame_click {$w} \\ - $w.cm.t \\ - $w.out.linenumber_t \\ - $w.out.file_t \\ - $i @%x,%y - focus $i - " + bind $i <Button-1> "[cb _click $i @%x,%y]; focus $i" bind_button3 $i " set cursorX %x set cursorY %y @@ -142,184 +166,161 @@ proc show_blame {commit path} { bind $i <Control-Key-f> {catch {%W yview scroll 1 pages};break} } - bind $w.cm.t <Button-1> "focus $w.cm.t" - bind $tl <Visibility> "focus $tl" - bind $tl <Destroy> " - array unset blame_status {$w} - array unset blame_data $w,* - " - 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.loaded_t $w.out.linenumber_t $w.out.file_t] -} - -proc read_blame_catfile {fd w commit path w_cmit w_load w_line w_file} { - global blame_status blame_data + bind $w.cm.t <Button-1> [list focus $w.cm.t] + bind $top <Visibility> [list focus $top] + bind $top <Destroy> [list delete_this $this] - if {![winfo exists $w_file]} { - catch {close $fd} - return + if {$commit eq {}} { + set fd [open $path r] + } else { + 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 [cb _read_file $fd] +} - set n $blame_data($w,total_lines) +method _read_file {fd} { $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 + incr total_lines $w_load insert end "\n" - $w_line insert end "$n\n" linenumber + $w_line insert end "$total_lines\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 if {[eof $fd]} { close $fd - blame_incremental_status $w + _status $this set cmd [list git blame -M -C --incremental] - lappend cmd $commit -- $path + 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 [list read_blame_incremental $fd $w \ - $w_load $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]} { - catch {close $fd} - return + fileevent $fd readable [cb _read_blame $fd] } +} ifdeleted { catch {close $fd} } +method _read_blame {fd} { while {[gets $fd line] >= 0} { if {[regexp {^([a-z0-9]{40}) (\d+) (\d+) (\d+)$} $line line \ cmit original_line final_line line_count]} { - set blame_data($w,commit) $cmit - set blame_data($w,original_line) $original_line - set blame_data($w,final_line) $final_line - set blame_data($w,line_count) $line_count + set r_commit $cmit + set r_orig_line $original_line + set r_final_line $final_line + set r_line_count $line_count - if {[catch {set g $blame_data($w,$cmit,order)}]} { + if {[catch {set g $order($cmit)}]} { $w_line tag conf g$cmit $w_file tag conf g$cmit $w_line tag raise in_sel $w_file tag raise in_sel $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 + set order($cmit) $commit_count + incr commit_count + lappend commit_list $cmit } } elseif {[string match {filename *} $line]} { set file [string range $line 9 end] - set n $blame_data($w,line_count) - set lno $blame_data($w,final_line) - set cmit $blame_data($w,commit) + set n $r_line_count + set lno $r_final_line + set cmit $r_commit while {$n > 0} { - if {[catch {set g g$blame_data($w,line$lno,commit)}]} { - $w_load tag add annotated $lno.0 "$lno.0 lineend + 1c" + set lno_e "$lno.0 lineend + 1c" + if {[catch {set g g$line_commit($lno)}]} { + $w_load tag add annotated $lno.0 $lno_e } 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" + $w_line tag remove g$g $lno.0 $lno_e + $w_file tag remove g$g $lno.0 $lno_e } - set blame_data($w,line$lno,commit) $cmit - set blame_data($w,line$lno,file) $file - $w_line tag add g$cmit $lno.0 "$lno.0 lineend + 1c" - $w_file tag add g$cmit $lno.0 "$lno.0 lineend + 1c" + set line_commit($lno) $cmit + set line_file($lno) $file + $w_line tag add g$cmit $lno.0 $lno_e + $w_file tag add g$cmit $lno.0 $lno_e - if {$blame_data($w,highlight_line) == -1} { + if {$highlight_line == -1} { if {[lindex [$w_file yview] 0] == 0} { $w_file see $lno.0 - blame_showcommit $w $w_cmit $w_line $w_file $lno + _showcommit $this $lno } - } elseif {$blame_data($w,highlight_line) == $lno} { - blame_showcommit $w $w_cmit $w_line $w_file $lno + } elseif {$highlight_line == $lno} { + _showcommit $this $lno } incr n -1 incr lno - incr blame_data($w,blame_lines) + incr blame_lines } - set hc $blame_data($w,highlight_commit) + set hc $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) + && [expr {$order($hc) + 1}] == $order($cmit)} { + _showcommit $this $highlight_line } - } elseif {[regexp {^([a-z-]+) (.*)$} $line line header data]} { - set blame_data($w,$blame_data($w,commit),$header) $data + } elseif {[regexp {^([a-z-]+) (.*)$} $line line key data]} { + set header($r_commit,$key) $data } } if {[eof $fd]} { close $fd - set blame_status($w) {Annotation complete.} + set status {Annotation complete.} } else { - blame_incremental_status $w + _status $this } -} +} ifdeleted { catch {close $fd} } -proc blame_incremental_status {w} { - global blame_status blame_data - - set have $blame_data($w,blame_lines) - set total $blame_data($w,total_lines) +method _status {} { + set have $blame_lines + set total $total_lines set pdone 0 if {$total} {set pdone [expr {100 * $have / $total}]} - set blame_status($w) [format \ + set status [format \ "Loading annotations... %i of %i lines annotated (%2i%%)" \ $have $total $pdone] } -proc blame_click {w w_cmit w_line w_file cur_w pos} { +method _click {cur_w pos} { set lno [lindex [split [$cur_w index $pos] .] 0] if {$lno eq {}} return + set lno_e "$lno.0 + 1 line" $w_line tag remove in_sel 0.0 end $w_file tag remove in_sel 0.0 end - $w_line tag add in_sel $lno.0 "$lno.0 + 1 line" - $w_file tag add in_sel $lno.0 "$lno.0 + 1 line" + $w_line tag add in_sel $lno.0 $lno_e + $w_file tag add in_sel $lno.0 $lno_e - blame_showcommit $w $w_cmit $w_line $w_file $lno + _showcommit $this $lno } -set blame_colors { +variable blame_colors { #ff4040 #ff40ff #4040ff } -proc blame_showcommit {w w_cmit w_line w_file lno} { - global blame_colors blame_data repo_config +method _showcommit {lno} { + global repo_config + variable blame_colors - set cmit $blame_data($w,highlight_commit) - if {$cmit ne {}} { - set idx $blame_data($w,$cmit,order) + if {$highlight_commit ne {}} { + set idx $order($highlight_commit) set i 0 foreach c $blame_colors { - set h [lindex $blame_data($w,commit_list) [expr {$idx - 1 + $i}]] + set h [lindex $commit_list [expr {$idx - 1 + $i}]] $w_line tag conf g$h -background white $w_file tag conf g$h -background white incr i @@ -328,14 +329,14 @@ proc blame_showcommit {w w_cmit w_line w_file lno} { $w_cmit conf -state normal $w_cmit delete 0.0 end - if {[catch {set cmit $blame_data($w,line$lno,commit)}]} { + if {[catch {set cmit $line_commit($lno)}]} { set cmit {} $w_cmit insert end "Loading annotation..." } else { - set idx $blame_data($w,$cmit,order) + set idx $order($cmit) set i 0 foreach c $blame_colors { - set h [lindex $blame_data($w,commit_list) [expr {$idx - 1 + $i}]] + set h [lindex $commit_list [expr {$idx - 1 + $i}]] $w_line tag conf g$h -background $c $w_file tag conf g$h -background $c incr i @@ -344,18 +345,24 @@ proc blame_showcommit {w w_cmit w_line w_file lno} { set author_name {} set author_email {} set author_time {} - catch {set author_name $blame_data($w,$cmit,author)} - catch {set author_email $blame_data($w,$cmit,author-mail)} - catch {set author_time [clock format $blame_data($w,$cmit,author-time)]} + catch {set author_name $header($cmit,author)} + catch {set author_email $header($cmit,author-mail)} + catch {set author_time [clock format \ + $header($cmit,author-time) \ + -format {%Y-%m-%d %H:%M:%S} + ]} set committer_name {} set committer_email {} set committer_time {} - catch {set committer_name $blame_data($w,$cmit,committer)} - catch {set committer_email $blame_data($w,$cmit,committer-mail)} - catch {set committer_time [clock format $blame_data($w,$cmit,committer-time)]} - - if {[catch {set msg $blame_data($w,$cmit,message)}]} { + catch {set committer_name $header($cmit,committer)} + catch {set committer_email $header($cmit,committer-mail)} + catch {set committer_time [clock format \ + $header($cmit,committer-time) \ + -format {%Y-%m-%d %H:%M:%S} + ]} + + if {[catch {set msg $header($cmit,message)}]} { set msg {} catch { set fd [open "| git cat-file commit $cmit" r] @@ -375,29 +382,29 @@ proc blame_showcommit {w w_cmit w_line w_file lno} { set author_name [encoding convertfrom $enc $author_name] set committer_name [encoding convertfrom $enc $committer_name] - set blame_data($w,$cmit,author) $author_name - set blame_data($w,$cmit,committer) $committer_name + set header($cmit,author) $author_name + set header($cmit,committer) $committer_name } - set blame_data($w,$cmit,message) $msg + set header($cmit,message) $msg } - $w_cmit insert end "commit $cmit\n" - $w_cmit insert end "Author: $author_name $author_email $author_time\n" - $w_cmit insert end "Committer: $committer_name $committer_email $committer_time\n" - $w_cmit insert end "Original File: [escape_path $blame_data($w,line$lno,file)]\n" - $w_cmit insert end "\n" - $w_cmit insert end $msg + $w_cmit insert end "commit $cmit +Author: $author_name $author_email $author_time +Committer: $committer_name $committer_email $committer_time +Original File: [escape_path $line_file($lno)] + +$msg" } $w_cmit conf -state disabled - set blame_data($w,highlight_line) $lno - set blame_data($w,highlight_commit) $cmit + set highlight_line $lno + set highlight_commit $cmit } -proc blame_copycommit {w i pos} { - global blame_data - set lno [lindex [split [$i index $pos] .] 0] - if {![catch {set commit $blame_data($w,line$lno,commit)}]} { +method _copycommit {} { + set pos @$::cursorX,$::cursorY + set lno [lindex [split [$::cursorW index $pos] .] 0] + if {![catch {set commit $line_commit($lno)}]} { clipboard clear clipboard append \ -format STRING \ @@ -405,3 +412,5 @@ proc blame_copycommit {w i pos} { -- $commit } } + +} |