+proc addtocflist {id} {
+ global currentid treediffs cflist treepending
+ if {$id != $currentid} {
+ gettreediffs $currentid
+ return
+ }
+ $cflist insert end "All files"
+ foreach f $treediffs($currentid) {
+ $cflist insert end $f
+ }
+ getblobdiffs $id
+}
+
+proc gettreediffs {id} {
+ global treediffs parents treepending
+ set treepending $id
+ set treediffs($id) {}
+ set p [lindex $parents($id) 0]
+ if [catch {set gdtf [open "|git-diff-tree -r $p $id" r]}] return
+ fconfigure $gdtf -blocking 0
+ fileevent $gdtf readable "gettreediffline $gdtf $id"
+}
+
+proc gettreediffline {gdtf id} {
+ global treediffs treepending
+ set n [gets $gdtf line]
+ if {$n < 0} {
+ if {![eof $gdtf]} return
+ close $gdtf
+ unset treepending
+ addtocflist $id
+ return
+ }
+ set type [lindex $line 1]
+ set file [lindex $line 3]
+ if {$type == "blob"} {
+ lappend treediffs($id) $file
+ }
+}
+
+proc getblobdiffs {id} {
+ global parents diffopts blobdifffd env curdifftag curtagstart
+ global diffindex difffilestart
+ set p [lindex $parents($id) 0]
+ set env(GIT_DIFF_OPTS) $diffopts
+ if [catch {set bdf [open "|git-diff-tree -r -p $p $id" r]} err] {
+ puts "error getting diffs: $err"
+ return
+ }
+ fconfigure $bdf -blocking 0
+ set blobdifffd($id) $bdf
+ set curdifftag Comments
+ set curtagstart 0.0
+ set diffindex 0
+ catch {unset difffilestart}
+ fileevent $bdf readable "getblobdiffline $bdf $id"
+}
+
+proc getblobdiffline {bdf id} {
+ global currentid blobdifffd ctext curdifftag curtagstart seenfile
+ global diffnexthead diffnextnote diffindex difffilestart
+ set n [gets $bdf line]
+ if {$n < 0} {
+ if {[eof $bdf]} {
+ close $bdf
+ if {$id == $currentid && $bdf == $blobdifffd($id)} {
+ $ctext tag add $curdifftag $curtagstart end
+ set seenfile($curdifftag) 1
+ }
+ }
+ return
+ }
+ if {$id != $currentid || $bdf != $blobdifffd($id)} {
+ return
+ }
+ $ctext conf -state normal
+ if {[regexp {^---[ \t]+([^/])*/(.*)} $line match s1 fname]} {
+ # start of a new file
+ $ctext insert end "\n"
+ $ctext tag add $curdifftag $curtagstart end
+ set seenfile($curdifftag) 1
+ set curtagstart [$ctext index "end - 1c"]
+ set header $fname
+ if {[info exists diffnexthead]} {
+ set fname $diffnexthead
+ set header "$diffnexthead ($diffnextnote)"
+ unset diffnexthead
+ }
+ set difffilestart($diffindex) [$ctext index "end - 1c"]
+ incr diffindex
+ set curdifftag "f:$fname"
+ $ctext tag delete $curdifftag
+ set l [expr {(78 - [string length $header]) / 2}]
+ set pad [string range "----------------------------------------" 1 $l]
+ $ctext insert end "$pad $header $pad\n" filesep
+ } elseif {[string range $line 0 2] == "+++"} {
+ # no need to do anything with this
+ } elseif {[regexp {^Created: (.*) \((mode: *[0-7]*)\)} $line match fn m]} {
+ set diffnexthead $fn
+ set diffnextnote "created, mode $m"
+ } elseif {[string range $line 0 8] == "Deleted: "} {
+ set diffnexthead [string range $line 9 end]
+ set diffnextnote "deleted"
+ } elseif {[regexp {^diff --git a/(.*) b/} $line match fn]} {
+ # save the filename in case the next thing is "new file mode ..."
+ set diffnexthead $fn
+ set diffnextnote "modified"
+ } elseif {[regexp {^new file mode ([0-7]+)} $line match m]} {
+ set diffnextnote "new file, mode $m"
+ } elseif {[string range $line 0 11] == "deleted file"} {
+ set diffnextnote "deleted"
+ } elseif {[regexp {^@@ -([0-9]+),([0-9]+) \+([0-9]+),([0-9]+) @@(.*)} \
+ $line match f1l f1c f2l f2c rest]} {
+ $ctext insert end "\t" hunksep
+ $ctext insert end " $f1l " d0 " $f2l " d1
+ $ctext insert end " $rest \n" hunksep
+ } else {
+ set x [string range $line 0 0]
+ if {$x == "-" || $x == "+"} {
+ set tag [expr {$x == "+"}]
+ set line [string range $line 1 end]
+ $ctext insert end "$line\n" d$tag
+ } elseif {$x == " "} {
+ set line [string range $line 1 end]
+ $ctext insert end "$line\n"
+ } elseif {$x == "\\"} {
+ # e.g. "\ No newline at end of file"
+ $ctext insert end "$line\n" filesep
+ } else {
+ # Something else we don't recognize
+ if {$curdifftag != "Comments"} {
+ $ctext insert end "\n"
+ $ctext tag add $curdifftag $curtagstart end
+ set seenfile($curdifftag) 1
+ set curtagstart [$ctext index "end - 1c"]
+ set curdifftag Comments
+ }
+ $ctext insert end "$line\n" filesep
+ }
+ }
+ $ctext conf -state disabled
+}
+
+proc nextfile {} {
+ global difffilestart ctext
+ set here [$ctext index @0,0]
+ for {set i 0} {[info exists difffilestart($i)]} {incr i} {
+ if {[$ctext compare $difffilestart($i) > $here]} {
+ $ctext yview $difffilestart($i)
+ break
+ }
+ }
+}
+
+proc listboxsel {} {
+ global ctext cflist currentid treediffs seenfile
+ if {![info exists currentid]} return
+ set sel [$cflist curselection]
+ if {$sel == {} || [lsearch -exact $sel 0] >= 0} {
+ # show everything
+ $ctext tag conf Comments -elide 0
+ foreach f $treediffs($currentid) {
+ if [info exists seenfile(f:$f)] {
+ $ctext tag conf "f:$f" -elide 0
+ }
+ }
+ } else {
+ # just show selected files
+ $ctext tag conf Comments -elide 1
+ set i 1
+ foreach f $treediffs($currentid) {
+ set elide [expr {[lsearch -exact $sel $i] < 0}]
+ if [info exists seenfile(f:$f)] {
+ $ctext tag conf "f:$f" -elide $elide
+ }
+ incr i
+ }
+ }
+}
+
+proc setcoords {} {
+ global linespc charspc canvx0 canvy0 mainfont
+ set linespc [font metrics $mainfont -linespace]
+ set charspc [font measure $mainfont "m"]
+ set canvy0 [expr 3 + 0.5 * $linespc]
+ set canvx0 [expr 3 + 0.5 * $linespc]
+}
+
+proc redisplay {} {
+ global selectedline stopped redisplaying phase
+ if {$stopped > 1} return
+ if {$phase == "getcommits"} return
+ set redisplaying 1
+ if {$phase == "drawgraph"} {
+ set stopped 1
+ } else {
+ drawgraph
+ }
+}
+
+proc incrfont {inc} {
+ global mainfont namefont textfont selectedline ctext canv phase
+ global stopped
+ unmarkmatches
+ set mainfont [lreplace $mainfont 1 1 [expr {[lindex $mainfont 1] + $inc}]]
+ set namefont [lreplace $namefont 1 1 [expr {[lindex $namefont 1] + $inc}]]
+ set textfont [lreplace $textfont 1 1 [expr {[lindex $textfont 1] + $inc}]]
+ setcoords
+ $ctext conf -font $textfont
+ $ctext tag conf filesep -font [concat $textfont bold]
+ if {$phase == "getcommits"} {
+ $canv itemconf textitems -font $mainfont
+ }
+ redisplay
+}
+
+proc doquit {} {
+ global stopped
+ set stopped 100
+ destroy .
+}
+
+# defaults...
+set datemode 0
+set boldnames 0
+set diffopts "-U 5 -p"