2 # Tcl ignores the next line -*- tcl -*- \
3 exec wish "$0" -- "${1+$@}"
5 # Copyright (C) 2005 Paul Mackerras. All rights reserved.
6 # This program is free software; it may be used, copied, modified
7 # and distributed under the terms of the GNU General Public Licence,
8 # either version 2, or (at your option) any later version.
10 # CVS $Revision: 1.3 $
17 switch -regexp -- $arg {
19 "^-d" { set datemode 1 }
20 "^-b" { set boldnames 1 }
22 puts stderr "unrecognized option $arg"
26 lappend revtreeargs $arg
31 proc getcommits {rargs} {
32 global commits parents cdate nparents children nchildren
37 foreach c [split [eval exec git-rev-tree $rargs] "\n"] {
44 set id [lindex [split $f :] 0]
45 if {![info exists nchildren($id)]} {
56 lappend parents($cid) $id
59 lappend children($id) $cid
67 proc readcommit {id} {
68 global commitinfo commitsummary
76 foreach line [split [exec git-cat-file commit $id] "\n"] {
81 set tag [lindex $line 0]
82 if {$tag == "author"} {
83 set x [expr {[llength $line] - 2}]
84 set audate [lindex $line $x]
85 set auname [lrange $line 1 [expr {$x - 1}]]
86 } elseif {$tag == "committer"} {
87 set x [expr {[llength $line] - 2}]
88 set comdate [lindex $line $x]
89 set comname [lrange $line 1 [expr {$x - 1}]]
102 set audate [clock format $audate -format "%Y-%m-%d %H:%M:%S"]
104 if {$comdate != {}} {
105 set comdate [clock format $comdate -format "%Y-%m-%d %H:%M:%S"]
107 set commitinfo($id) [list $comment $auname $audate $comname $comdate]
108 set commitsummary($id) [list $headline $auname $audate]
111 proc gettreediffs {id} {
112 global treediffs parents
113 set p [lindex $parents($id) 0]
115 foreach line [split [exec git-diff-tree -r $p $id] "\n"] {
116 set type [lindex $line 1]
117 set file [lindex $line 3]
118 if {$type == "blob"} {
122 set treediffs($id) $diff
126 global canv linespc charspc ctext cflist
127 panedwindow .ctop -orient vertical
129 set canv .ctop.clist.canv
130 canvas $canv -height [expr 30 * $linespc + 4] -width [expr 90 * $charspc] \
131 -bg white -relief sunk -bd 1 \
132 -yscrollincr $linespc -yscrollcommand ".ctop.clist.csb set"
133 scrollbar .ctop.clist.csb -command "$canv yview" -highlightthickness 0
134 pack .ctop.clist.csb -side right -fill y
135 pack $canv -side bottom -fill both -expand 1
136 .ctop add .ctop.clist
137 #pack .ctop.clist -side top -fill both -expand 1
138 panedwindow .ctop.cdet -orient horizontal
140 set ctext .ctop.cdet.ctext
141 text $ctext -bg white -state disabled
142 .ctop.cdet add $ctext
143 #pack $ctext -side top -fill x -expand 1
144 set cflist .ctop.cdet.cfiles
145 listbox $cflist -width 30 -bg white
146 .ctop.cdet add $cflist
147 pack .ctop -side top -fill both -expand 1
149 bind $canv <1> {selcanvline %x %y}
150 bind $canv <B1-Motion> {selcanvline %x %y}
151 bind $canv <ButtonRelease-4> "$canv yview scroll -5 u"
152 bind $canv <ButtonRelease-5> "$canv yview scroll 5 u"
153 bind $canv <2> "$canv scan mark 0 %y"
154 bind $canv <B2-Motion> "$canv scan dragto 0 %y"
155 bind . <Key-Prior> "$canv yview scroll -1 p"
156 bind . <Key-Next> "$canv yview scroll 1 p"
157 bind . <Key-Delete> "$canv yview scroll -1 p"
158 bind . <Key-BackSpace> "$canv yview scroll -1 p"
159 bind . <Key-space> "$canv yview scroll 1 p"
160 bind . <Key-Up> "selnextline -1"
161 bind . <Key-Down> "selnextline 1"
162 bind . Q "set stopped 1; destroy ."
165 proc truncatetofit {str width font} {
166 if {[font measure $font $str] <= $width} {
170 set bad [string length $str]
172 while {$best < $bad - 1} {
173 set try [expr {int(($best + $bad) / 2)}]
174 set tmp "[string range $str 0 [expr $try-1]]..."
175 if {[font measure $font $tmp] <= $width} {
184 proc drawgraph {start} {
185 global parents children nparents nchildren commits
186 global canv mainfont namefont canvx0 canvy0 canvy linespc namex datex
187 global datemode cdate
188 global lineid linehtag linentag linedtag commitsummary
190 set colors {green red blue magenta darkgrey brown orange}
191 set ncolors [llength $colors]
193 set colormap($start) [lindex $colors 0]
194 foreach id $commits {
195 set ncleft($id) $nchildren($id)
197 set todo [list $start]
200 set linestarty(0) $canvy
205 set nlines [llength $todo]
206 set id [lindex $todo $level]
207 set lineid($lineno) $id
209 foreach p $parents($id) {
210 if {[info exists ncleft($p)]} {
212 lappend actualparents $p
215 if {![info exists commitsummary($id)]} {
218 set x [expr $canvx0 + $level * $linespc]
219 set y2 [expr $canvy + $linespc]
220 if {$linestarty($level) < $canvy} {
221 set t [$canv create line $x $linestarty($level) $x $canvy \
222 -width 2 -fill $colormap($id)]
224 set linestarty($level) $canvy
226 set t [$canv create oval [expr $x - 4] [expr $canvy - 4] \
227 [expr $x + 3] [expr $canvy + 3] \
228 -fill blue -outline black -width 1]
230 set xt [expr $canvx0 + $nlines * $linespc]
231 set headline [lindex $commitsummary($id) 0]
232 set name [lindex $commitsummary($id) 1]
233 set date [lindex $commitsummary($id) 2]
234 set headline [truncatetofit $headline [expr $namex-$xt-$linespc] \
236 set linehtag($lineno) [$canv create text $xt $canvy -anchor w \
237 -text $headline -font $mainfont ]
238 set name [truncatetofit $name [expr $datex-$namex-$linespc] $namefont]
239 set linentag($lineno) [$canv create text $namex $canvy -anchor w \
240 -text $name -font $namefont]
241 set linedtag($lineno) [$canv create text $datex $canvy -anchor w \
242 -text $date -font $mainfont]
243 if {!$datemode && [llength $actualparents] == 1} {
244 set p [lindex $actualparents 0]
245 if {$ncleft($p) == 0 && [lsearch -exact $todo $p] < 0} {
246 set todo [lreplace $todo $level $level $p]
247 set colormap($p) $colormap($id)
249 $canv conf -scrollregion [list 0 0 0 $canvy]
258 for {set i 0} {$i < $nlines} {incr i} {
259 if {[lindex $todo $i] == {}} continue
260 set oldstarty($i) $linestarty($i)
262 lappend lines [list $i [lindex $todo $i]]
266 if {$nullentry >= 0} {
267 set todo [lreplace $todo $nullentry $nullentry]
268 if {$nullentry < $level} {
273 set badcolors [list $colormap($id)]
274 foreach p $actualparents {
275 if {[info exists colormap($p)]} {
276 lappend badcolors $colormap($p)
279 set todo [lreplace $todo $level $level]
280 if {$nullentry > $level} {
284 foreach p $actualparents {
285 set k [lsearch -exact $todo $p]
287 set todo [linsert $todo $i $p]
288 if {$nullentry >= $i} {
291 if {$nparents($id) == 1 && $nparents($p) == 1
292 && $nchildren($p) == 1} {
293 set colormap($p) $colormap($id)
295 for {set j 0} {$j <= $ncolors} {incr j} {
296 if {[incr nextcolor] >= $ncolors} {
299 set c [lindex $colors $nextcolor]
300 # make sure the incoming and outgoing colors differ
301 if {[lsearch -exact $badcolors $c] < 0} break
307 lappend lines [list $oldlevel $p]
310 # choose which one to do next time around
311 set todol [llength $todo]
314 for {set k $todol} {[incr k -1] >= 0} {} {
315 set p [lindex $todo $k]
316 if {$p == {}} continue
317 if {$ncleft($p) == 0} {
319 if {$latest == {} || $cdate($p) > $latest} {
321 set latest $cdate($p)
331 puts "ERROR: none of the pending commits can be done yet:"
339 # If we are reducing, put in a null entry
340 if {$todol < $nlines} {
341 if {$nullentry >= 0} {
344 && [lindex $oldtodo $i] == [lindex $todo $i]} {
357 set todo [linsert $todo $nullentry {}]
368 set dst [lindex $l 1]
369 set j [lsearch -exact $todo $dst]
371 set linestarty($i) $oldstarty($i)
374 set xi [expr {$canvx0 + $i * $linespc}]
375 set xj [expr {$canvx0 + $j * $linespc}]
377 if {$oldstarty($i) < $canvy} {
378 lappend coords $xi $oldstarty($i)
380 lappend coords $xi $canvy
382 lappend coords [expr $xj + $linespc] $canvy
383 } elseif {$j > $i + 1} {
384 lappend coords [expr $xj - $linespc] $canvy
386 lappend coords $xj $y2
387 set t [$canv create line $coords -width 2 -fill $colormap($dst)]
389 if {![info exists linestarty($j)]} {
390 set linestarty($j) $y2
394 $canv conf -scrollregion [list 0 0 0 $canvy]
399 proc selcanvline {x y} {
400 global canv canvy0 ctext linespc selectedline
401 global lineid linehtag linentag linedtag commitinfo
402 set ymax [lindex [$canv cget -scrollregion] 3]
403 set yfrac [lindex [$canv yview] 0]
404 set y [expr {$y + $yfrac * $ymax}]
405 set l [expr {int(($y - $canvy0) / $linespc + 0.5)}]
409 if {[info exists selectedline] && $selectedline == $l} return
413 proc selectline {l} {
414 global canv ctext commitinfo selectedline lineid linehtag
415 global canvy canvy0 linespc nparents
416 global cflist treediffs
417 if {![info exists lineid($l)] || ![info exists linehtag($l)]} return
419 $canv select from $linehtag($l) 0
420 $canv select to $linehtag($l) end
421 set y [expr {$canvy0 + $l * $linespc}]
422 set ytop [expr {($y - $linespc / 2.0) / $canvy}]
423 set ybot [expr {($y + $linespc / 2.0) / $canvy}]
424 set wnow [$canv yview]
425 if {$ytop < [lindex $wnow 0]} {
426 $canv yview moveto $ytop
427 } elseif {$ybot > [lindex $wnow 1]} {
428 set wh [expr {[lindex $wnow 1] - [lindex $wnow 0]}]
429 $canv yview moveto [expr {$ybot - $wh}]
434 $ctext conf -state normal
435 $ctext delete 0.0 end
436 set info $commitinfo($id)
437 $ctext insert end "Author: [lindex $info 1] \t[lindex $info 2]\n"
438 $ctext insert end "Committer: [lindex $info 3] \t[lindex $info 4]\n"
439 $ctext insert end "\n"
440 $ctext insert end [lindex $info 0]
441 $ctext conf -state disabled
444 if {$nparents($id) == 1} {
445 if {![info exists treediffs($id)]} {
448 foreach f $treediffs($id) {
449 $cflist insert end $f
455 proc selnextline {dir} {
457 if {![info exists selectedline]} return
458 set l [expr $selectedline + $dir]
462 getcommits $revtreeargs
464 set mainfont {Helvetica 9}
465 set namefont $mainfont
467 lappend namefont bold
469 set linespc [font metrics $mainfont -linespace]
470 set charspc [font measure $mainfont "m"]
472 set canvy0 [expr 3 + 0.5 * $linespc]
473 set canvx0 [expr 3 + 0.5 * $linespc]
474 set namex [expr 45 * $charspc]
475 set datex [expr 75 * $charspc]
480 foreach id $commits {
481 if {$nchildren($id) == 0} {