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.5 $
16 set mainfont
{Helvetica
9}
17 set namefont
$mainfont
21 catch
{source ~
/.gitk
}
24 switch
-regexp -- $arg {
26 "^-d" { set datemode
1 }
27 "^-b" { set boldnames
1 }
29 puts stderr
"unrecognized option $arg"
33 lappend revtreeargs
$arg
38 proc getcommits
{rargs
} {
39 global commits parents cdate nparents children nchildren
44 foreach c
[split [eval exec git-rev-tree
$rargs] "\n"] {
51 set id
[lindex
[split $f :] 0]
52 if {![info exists nchildren
($id)]} {
63 lappend parents
($cid) $id
66 lappend children
($id) $cid
74 proc readcommit
{id
} {
75 global commitinfo commitsummary
83 foreach line
[split [exec git-cat-file commit
$id] "\n"] {
88 set tag
[lindex
$line 0]
89 if {$tag == "author"} {
90 set x
[expr {[llength
$line] - 2}]
91 set audate
[lindex
$line $x]
92 set auname
[lrange
$line 1 [expr {$x - 1}]]
93 } elseif
{$tag == "committer"} {
94 set x
[expr {[llength
$line] - 2}]
95 set comdate
[lindex
$line $x]
96 set comname
[lrange
$line 1 [expr {$x - 1}]]
100 if {$comment == {}} {
109 set audate
[clock format
$audate -format "%Y-%m-%d %H:%M:%S"]
111 if {$comdate != {}} {
112 set comdate
[clock format
$comdate -format "%Y-%m-%d %H:%M:%S"]
114 set commitinfo
($id) [list
$comment $auname $audate $comname $comdate]
115 set commitsummary
($id) [list
$headline $auname $audate]
119 global canv canv2 canv3 linespc charspc ctext cflist
120 panedwindow .ctop
-orient vertical
121 panedwindow .ctop.clist
-orient horizontal
-sashpad 0 -handlesize 4
122 .ctop add .ctop.clist
123 set canv .ctop.clist.canv
124 set cscroll .ctop.clist.dates.csb
125 canvas
$canv -height [expr 30 * $linespc + 4] -width [expr 45 * $charspc] \
127 -yscrollincr $linespc -yscrollcommand "$cscroll set"
128 .ctop.clist add
$canv
129 set canv2 .ctop.clist.canv2
130 canvas
$canv2 -height [expr 30 * $linespc +4] -width [expr 30 * $charspc] \
131 -bg white
-bd 0 -yscrollincr $linespc
132 .ctop.clist add
$canv2
133 frame .ctop.clist.dates
134 .ctop.clist add .ctop.clist.dates
135 set canv3 .ctop.clist.dates.canv3
136 canvas
$canv3 -height [expr 30 * $linespc +4] -width [expr 15 * $charspc] \
137 -bg white
-bd 0 -yscrollincr $linespc
138 scrollbar
$cscroll -command {allcanvs yview
} -highlightthickness 0
139 pack .ctop.clist.dates.csb
-side right
-fill y
140 pack
$canv3 -side left
-fill both
-expand 1
142 panedwindow .ctop.cdet
-orient horizontal
144 frame .ctop.cdet.left
145 set ctext .ctop.cdet.left.ctext
146 text
$ctext -bg white
-state disabled \
147 -yscrollcommand ".ctop.cdet.left.sb set"
148 scrollbar .ctop.cdet.left.sb
-command "$ctext yview"
149 pack .ctop.cdet.left.sb
-side right
-fill y
150 pack
$ctext -side left
-fill both
-expand 1
151 .ctop.cdet add .ctop.cdet.left
153 frame .ctop.cdet.right
154 set cflist .ctop.cdet.right.cfiles
155 listbox
$cflist -width 30 -bg white \
156 -yscrollcommand ".ctop.cdet.right.sb set"
157 scrollbar .ctop.cdet.right.sb
-command "$cflist yview"
158 pack .ctop.cdet.right.sb
-side right
-fill y
159 pack
$cflist -side left
-fill both
-expand 1
160 .ctop.cdet add .ctop.cdet.right
162 pack .ctop
-side top
-fill both
-expand 1
164 bindall
<1> {selcanvline
%x
%y
}
165 bindall
<B1-Motion
> {selcanvline
%x
%y
}
166 bindall
<ButtonRelease-4
> "allcanvs yview scroll -5 u"
167 bindall
<ButtonRelease-5
> "allcanvs yview scroll 5 u"
168 bindall
<2> "allcanvs scan mark 0 %y"
169 bindall
<B2-Motion
> "allcanvs scan dragto 0 %y"
170 bind .
<Key-Prior
> "allcanvs yview scroll -1 p"
171 bind .
<Key-Next
> "allcanvs yview scroll 1 p"
172 bind .
<Key-Delete
> "allcanvs yview scroll -1 p"
173 bind .
<Key-BackSpace
> "allcanvs yview scroll -1 p"
174 bind .
<Key-space
> "allcanvs yview scroll 1 p"
175 bind .
<Key-Up
> "selnextline -1"
176 bind .
<Key-Down
> "selnextline 1"
177 bind . Q
"set stopped 1; destroy ."
181 global canv canv2 canv3
187 proc bindall
{event action
} {
188 global canv canv2 canv3
189 bind $canv $event $action
190 bind $canv2 $event $action
191 bind $canv3 $event $action
194 proc truncatetofit
{str width font
} {
195 if {[font measure
$font $str] <= $width} {
199 set bad
[string length
$str]
201 while {$best < $bad - 1} {
202 set try
[expr {int
(($best + $bad) / 2)}]
203 set tmp
"[string range $str 0 [expr $try-1]]..."
204 if {[font measure
$font $tmp] <= $width} {
213 proc drawgraph
{start
} {
214 global parents children nparents nchildren commits
215 global canv canv2 canv3 mainfont namefont canvx0 canvy0 canvy linespc
216 global datemode cdate
217 global lineid linehtag linentag linedtag commitsummary
219 set colors
{green red blue magenta darkgrey brown orange
}
220 set ncolors
[llength
$colors]
222 set colormap
($start) [lindex
$colors 0]
223 foreach id
$commits {
224 set ncleft
($id) $nchildren($id)
226 set todo
[list
$start]
229 set linestarty
(0) $canvy0
234 allcanvs conf
-scrollregion [list
0 0 0 $canvy]
237 set nlines
[llength
$todo]
238 set id
[lindex
$todo $level]
239 set lineid
($lineno) $id
241 foreach p
$parents($id) {
242 if {[info exists ncleft
($p)]} {
244 lappend actualparents
$p
247 if {![info exists commitsummary
($id)]} {
250 set x
[expr $canvx0 + $level * $linespc]
251 set y2
[expr $canvy + $linespc]
252 if {$linestarty($level) < $canvy} {
253 set t
[$canv create line
$x $linestarty($level) $x $canvy \
254 -width 2 -fill $colormap($id)]
256 set linestarty
($level) $canvy
258 set t
[$canv create oval
[expr $x - 4] [expr $canvy - 4] \
259 [expr $x + 3] [expr $canvy + 3] \
260 -fill blue
-outline black
-width 1]
262 set xt
[expr $canvx0 + $nlines * $linespc]
263 set headline
[lindex
$commitsummary($id) 0]
264 set name
[lindex
$commitsummary($id) 1]
265 set date [lindex
$commitsummary($id) 2]
266 set linehtag
($lineno) [$canv create text
$xt $canvy -anchor w \
267 -text $headline -font $mainfont ]
268 set linentag
($lineno) [$canv2 create text
3 $canvy -anchor w \
269 -text $name -font $namefont]
270 set linedtag
($lineno) [$canv3 create text
3 $canvy -anchor w \
271 -text $date -font $mainfont]
272 if {!$datemode && [llength
$actualparents] == 1} {
273 set p
[lindex
$actualparents 0]
274 if {$ncleft($p) == 0 && [lsearch
-exact $todo $p] < 0} {
275 set todo
[lreplace
$todo $level $level $p]
276 set colormap
($p) $colormap($id)
284 for {set i
0} {$i < $nlines} {incr i
} {
285 if {[lindex
$todo $i] == {}} continue
286 set oldstarty
($i) $linestarty($i)
288 lappend lines
[list
$i [lindex
$todo $i]]
292 if {$nullentry >= 0} {
293 set todo
[lreplace
$todo $nullentry $nullentry]
294 if {$nullentry < $level} {
299 set badcolors
[list
$colormap($id)]
300 foreach p
$actualparents {
301 if {[info exists colormap
($p)]} {
302 lappend badcolors
$colormap($p)
305 set todo
[lreplace
$todo $level $level]
306 if {$nullentry > $level} {
310 foreach p
$actualparents {
311 set k
[lsearch
-exact $todo $p]
313 set todo
[linsert
$todo $i $p]
314 if {$nullentry >= $i} {
317 if {$nparents($id) == 1 && $nparents($p) == 1
318 && $nchildren($p) == 1} {
319 set colormap
($p) $colormap($id)
321 for {set j
0} {$j <= $ncolors} {incr j
} {
322 if {[incr nextcolor
] >= $ncolors} {
325 set c
[lindex
$colors $nextcolor]
326 # make sure the incoming and outgoing colors differ
327 if {[lsearch
-exact $badcolors $c] < 0} break
333 lappend lines
[list
$oldlevel $p]
336 # choose which one to do next time around
337 set todol
[llength
$todo]
340 for {set k
$todol} {[incr k
-1] >= 0} {} {
341 set p
[lindex
$todo $k]
342 if {$p == {}} continue
343 if {$ncleft($p) == 0} {
345 if {$latest == {} ||
$cdate($p) > $latest} {
347 set latest
$cdate($p)
357 puts
"ERROR: none of the pending commits can be done yet:"
365 # If we are reducing, put in a null entry
366 if {$todol < $nlines} {
367 if {$nullentry >= 0} {
370 && [lindex
$oldtodo $i] == [lindex
$todo $i]} {
383 set todo
[linsert
$todo $nullentry {}]
394 set dst
[lindex
$l 1]
395 set j
[lsearch
-exact $todo $dst]
397 set linestarty
($i) $oldstarty($i)
400 set xi
[expr {$canvx0 + $i * $linespc}]
401 set xj
[expr {$canvx0 + $j * $linespc}]
403 if {$oldstarty($i) < $canvy} {
404 lappend coords
$xi $oldstarty($i)
406 lappend coords
$xi $canvy
408 lappend coords
[expr $xj + $linespc] $canvy
409 } elseif
{$j > $i + 1} {
410 lappend coords
[expr $xj - $linespc] $canvy
412 lappend coords
$xj $y2
413 set t
[$canv create line
$coords -width 2 -fill $colormap($dst)]
415 if {![info exists linestarty
($j)]} {
416 set linestarty
($j) $y2
422 proc selcanvline
{x y
} {
423 global canv canvy0 ctext linespc selectedline
424 global lineid linehtag linentag linedtag commitinfo
425 set ymax
[lindex
[$canv cget
-scrollregion] 3]
426 set yfrac
[lindex
[$canv yview
] 0]
427 set y
[expr {$y + $yfrac * $ymax}]
428 set l
[expr {int
(($y - $canvy0) / $linespc + 0.5)}]
432 if {[info exists selectedline
] && $selectedline == $l} return
436 proc selectline
{l
} {
437 global canv canv2 canv3 ctext commitinfo selectedline
438 global lineid linehtag linentag linedtag
439 global canvy canvy0 linespc nparents treepending
440 global cflist treediffs currentid
441 if {![info exists lineid
($l)] ||
![info exists linehtag
($l)]} return
443 set t
[eval $canv create rect
[$canv bbox
$linehtag($l)] -outline {{}} \
444 -tags secsel
-fill [$canv cget
-selectbackground]]
447 set t
[eval $canv2 create rect
[$canv2 bbox
$linentag($l)] -outline {{}} \
448 -tags secsel
-fill [$canv2 cget
-selectbackground]]
451 set t
[eval $canv3 create rect
[$canv3 bbox
$linedtag($l)] -outline {{}} \
452 -tags secsel
-fill [$canv3 cget
-selectbackground]]
454 set y
[expr {$canvy0 + $l * $linespc}]
455 set ytop
[expr {($y - $linespc / 2.0) / $canvy}]
456 set ybot
[expr {($y + $linespc / 2.0) / $canvy}]
457 set wnow
[$canv yview
]
458 if {$ytop < [lindex
$wnow 0]} {
459 allcanvs yview moveto
$ytop
460 } elseif
{$ybot > [lindex
$wnow 1]} {
461 set wh
[expr {[lindex
$wnow 1] - [lindex
$wnow 0]}]
462 allcanvs yview moveto
[expr {$ybot - $wh}]
467 $ctext conf
-state normal
468 $ctext delete
0.0 end
469 set info
$commitinfo($id)
470 $ctext insert end
"Author: [lindex $info 1] [lindex $info 2]\n"
471 $ctext insert end
"Committer: [lindex $info 3] [lindex $info 4]\n"
472 $ctext insert end
"\n"
473 $ctext insert end
[lindex
$info 0]
474 $ctext conf
-state disabled
478 if {$nparents($id) == 1} {
479 if {![info exists treediffs
($id)]} {
480 if {![info exists treepending
]} {
490 proc addtocflist
{id
} {
491 global currentid treediffs cflist treepending
492 if {$id != $currentid} {
493 gettreediffs
$currentid
496 foreach f
$treediffs($currentid) {
497 $cflist insert end
$f
501 proc gettreediffs
{id
} {
502 global treediffs parents treepending
504 set treediffs
($id) {}
505 set p
[lindex
$parents($id) 0]
506 if [catch
{set gdtf
[open
"|git-diff-tree -r $p $id" r
]}] return
507 fconfigure
$gdtf -blocking 0
508 fileevent
$gdtf readable
"gettreediffline $gdtf $id"
511 proc gettreediffline
{gdtf id
} {
512 global treediffs treepending
513 set n
[gets
$gdtf line
]
515 if {![eof
$gdtf]} return
521 set type [lindex
$line 1]
522 set file [lindex
$line 3]
523 if {$type == "blob"} {
524 lappend treediffs
($id) $file
528 proc selnextline
{dir
} {
530 if {![info exists selectedline
]} return
531 set l
[expr $selectedline + $dir]
535 getcommits
$revtreeargs
537 set linespc
[font metrics
$mainfont -linespace]
538 set charspc
[font measure
$mainfont "m"]
540 set canvy0
[expr 3 + 0.5 * $linespc]
541 set canvx0
[expr 3 + 0.5 * $linespc]
542 set namex
[expr 45 * $charspc]
543 set datex
[expr 75 * $charspc]
548 foreach id
$commits {
549 if {$nchildren($id) == 0} {