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 proc getcommits
{rargs
} {
11 global commits commfd phase canv mainfont env
12 global startmsecs nextupdate
13 global ctext maincursor textcursor leftover
15 # check that we can find a .git directory somewhere...
16 if {[info exists env
(GIT_DIR
)]} {
17 set gitdir
$env(GIT_DIR
)
21 if {![file isdirectory
$gitdir]} {
22 error_popup
"Cannot find the git directory \"$gitdir\"."
27 set startmsecs
[clock clicks
-milliseconds]
28 set nextupdate
[expr $startmsecs + 100]
30 set parse_args
[concat
--default HEAD
$rargs]
31 set parsed_args
[split [eval exec git-rev-parse
$parse_args] "\n"]
33 # if git-rev-parse failed for some reason...
37 set parsed_args
$rargs
40 set commfd
[open
"|git-rev-list --header --merge-order $parsed_args" r
]
42 puts stderr
"Error executing git-rev-list: $err"
46 fconfigure
$commfd -blocking 0 -translation binary
47 fileevent
$commfd readable
"getcommitlines $commfd"
49 $canv create text
3 3 -anchor nw
-text "Reading commits..." \
50 -font $mainfont -tags textitems
51 . config
-cursor watch
52 $ctext config
-cursor watch
55 proc getcommitlines
{commfd
} {
56 global commits parents cdate children nchildren
57 global commitlisted phase commitinfo nextupdate
58 global stopped redisplaying leftover
60 set stuff
[read $commfd]
62 if {![eof
$commfd]} return
63 # this works around what is apparently a bug in Tcl...
64 fconfigure
$commfd -blocking 1
65 if {![catch
{close
$commfd} err
]} {
66 after idle finishcommits
69 if {[string range
$err 0 4] == "usage"} {
71 {Gitk
: error reading commits
: bad arguments to git-rev-list.
72 (Note
: arguments to gitk are passed to git-rev-list
73 to allow selection of commits to be displayed.
)}
75 set err
"Error reading commits: $err"
82 set i
[string first
"\0" $stuff $start]
84 append leftover
[string range
$stuff $start end
]
87 set cmit
[string range
$stuff $start [expr {$i - 1}]]
89 set cmit
"$leftover$cmit"
92 set start
[expr {$i + 1}]
93 if {![regexp
{^
([0-9a-f]{40})\n} $cmit match id
]} {
95 if {[string length
$shortcmit] > 80} {
96 set shortcmit
"[string range $shortcmit 0 80]..."
98 error_popup
"Can't parse git-rev-list output: {$shortcmit}"
101 set cmit
[string range
$cmit 41 end
]
103 set commitlisted
($id) 1
104 parsecommit
$id $cmit 1
106 if {[clock clicks
-milliseconds] >= $nextupdate} {
109 while {$redisplaying} {
113 set phase
"getcommits"
114 foreach id
$commits {
117 if {[clock clicks
-milliseconds] >= $nextupdate} {
127 global commfd nextupdate
130 fileevent
$commfd readable
{}
132 fileevent
$commfd readable
"getcommitlines $commfd"
135 proc readcommit
{id
} {
136 if [catch
{set contents
[exec git-cat-file commit
$id]}] return
137 parsecommit
$id $contents 0
140 proc parsecommit
{id contents listed
} {
141 global commitinfo children nchildren parents nparents cdate ncleft
150 if {![info exists nchildren
($id)]} {
157 foreach line
[split $contents "\n"] {
162 set tag
[lindex
$line 0]
163 if {$tag == "parent"} {
164 set p
[lindex
$line 1]
165 if {![info exists nchildren
($p)]} {
170 lappend parents
($id) $p
172 # sometimes we get a commit that lists a parent twice...
173 if {$listed && [lsearch
-exact $children($p) $id] < 0} {
174 lappend children
($p) $id
178 } elseif
{$tag == "author"} {
179 set x
[expr {[llength
$line] - 2}]
180 set audate
[lindex
$line $x]
181 set auname
[lrange
$line 1 [expr {$x - 1}]]
182 } elseif
{$tag == "committer"} {
183 set x
[expr {[llength
$line] - 2}]
184 set comdate
[lindex
$line $x]
185 set comname
[lrange
$line 1 [expr {$x - 1}]]
189 if {$comment == {}} {
190 set headline
[string trim
$line]
195 # git-rev-list indents the comment by 4 spaces;
196 # if we got this via git-cat-file, add the indentation
203 set audate
[clock format
$audate -format "%Y-%m-%d %H:%M:%S"]
205 if {$comdate != {}} {
206 set cdate
($id) $comdate
207 set comdate
[clock format
$comdate -format "%Y-%m-%d %H:%M:%S"]
209 set commitinfo
($id) [list
$headline $auname $audate \
210 $comname $comdate $comment]
214 global tagids idtags headids idheads
215 set tags
[glob
-nocomplain -types f .git
/refs
/tags
/*]
220 if {[regexp
{^
[0-9a-f]{40}} $line id
]} {
221 set direct
[file tail $f]
222 set tagids
($direct) $id
223 lappend idtags
($id) $direct
224 set contents
[split [exec git-cat-file tag
$id] "\n"]
228 foreach l
$contents {
230 switch
-- [lindex
$l 0] {
231 "object" {set obj
[lindex
$l 1]}
232 "type" {set type [lindex
$l 1]}
233 "tag" {set tag
[string range
$l 4 end
]}
236 if {$obj != {} && $type == "commit" && $tag != {}} {
237 set tagids
($tag) $obj
238 lappend idtags
($obj) $tag
244 set heads
[glob
-nocomplain -types f .git
/refs
/heads
/*]
248 set line
[read $fd 40]
249 if {[regexp
{^
[0-9a-f]{40}} $line id
]} {
250 set head [file tail $f]
251 set headids
($head) $line
252 lappend idheads
($line) $head
259 proc error_popup msg
{
263 message
$w.m
-text $msg -justify center
-aspect 400
264 pack
$w.m
-side top
-fill x
-padx 20 -pady 20
265 button
$w.ok
-text OK
-command "destroy $w"
266 pack
$w.ok
-side bottom
-fill x
267 bind $w <Visibility
> "grab $w; focus $w"
272 global canv canv2 canv3 linespc charspc ctext cflist textfont
273 global findtype findloc findstring fstring geometry
274 global entries sha1entry sha1string sha1but
275 global maincursor textcursor
279 .bar add cascade
-label "File" -menu .bar.
file
281 .bar.
file add
command -label "Quit" -command doquit
283 .bar add cascade
-label "Help" -menu .bar.
help
284 .bar.
help add
command -label "About gitk" -command about
285 . configure
-menu .bar
287 if {![info exists geometry
(canv1
)]} {
288 set geometry
(canv1
) [expr 45 * $charspc]
289 set geometry
(canv2
) [expr 30 * $charspc]
290 set geometry
(canv3
) [expr 15 * $charspc]
291 set geometry
(canvh
) [expr 25 * $linespc + 4]
292 set geometry
(ctextw
) 80
293 set geometry
(ctexth
) 30
294 set geometry
(cflistw
) 30
296 panedwindow .ctop
-orient vertical
297 if {[info exists geometry
(width
)]} {
298 .ctop conf
-width $geometry(width
) -height $geometry(height
)
299 set texth
[expr {$geometry(height
) - $geometry(canvh
) - 56}]
300 set geometry
(ctexth
) [expr {($texth - 8) /
301 [font metrics
$textfont -linespace]}]
305 pack .ctop.top.bar
-side bottom
-fill x
306 set cscroll .ctop.top.csb
307 scrollbar
$cscroll -command {allcanvs yview
} -highlightthickness 0
308 pack
$cscroll -side right
-fill y
309 panedwindow .ctop.top.clist
-orient horizontal
-sashpad 0 -handlesize 4
310 pack .ctop.top.clist
-side top
-fill both
-expand 1
312 set canv .ctop.top.clist.canv
313 canvas
$canv -height $geometry(canvh
) -width $geometry(canv1
) \
315 -yscrollincr $linespc -yscrollcommand "$cscroll set"
316 .ctop.top.clist add
$canv
317 set canv2 .ctop.top.clist.canv2
318 canvas
$canv2 -height $geometry(canvh
) -width $geometry(canv2
) \
319 -bg white
-bd 0 -yscrollincr $linespc
320 .ctop.top.clist add
$canv2
321 set canv3 .ctop.top.clist.canv3
322 canvas
$canv3 -height $geometry(canvh
) -width $geometry(canv3
) \
323 -bg white
-bd 0 -yscrollincr $linespc
324 .ctop.top.clist add
$canv3
325 bind .ctop.top.clist
<Configure
> {resizeclistpanes
%W
%w
}
327 set sha1entry .ctop.top.bar.sha1
328 set entries
$sha1entry
329 set sha1but .ctop.top.bar.sha1label
330 button
$sha1but -text "SHA1 ID: " -state disabled
-relief flat \
331 -command gotocommit
-width 8
332 $sha1but conf
-disabledforeground [$sha1but cget
-foreground]
333 pack .ctop.top.bar.sha1label
-side left
334 entry
$sha1entry -width 40 -font $textfont -textvariable sha1string
335 trace add variable sha1string
write sha1change
336 pack
$sha1entry -side left
-pady 2
337 button .ctop.top.bar.findbut
-text "Find" -command dofind
338 pack .ctop.top.bar.findbut
-side left
340 set fstring .ctop.top.bar.findstring
341 lappend entries
$fstring
342 entry
$fstring -width 30 -font $textfont -textvariable findstring
343 pack
$fstring -side left
-expand 1 -fill x
345 tk_optionMenu .ctop.top.bar.findtype findtype Exact IgnCase Regexp
346 set findloc
"All fields"
347 tk_optionMenu .ctop.top.bar.findloc findloc
"All fields" Headline \
348 Comments Author Committer
349 pack .ctop.top.bar.findloc
-side right
350 pack .ctop.top.bar.findtype
-side right
352 panedwindow .ctop.cdet
-orient horizontal
354 frame .ctop.cdet.left
355 set ctext .ctop.cdet.left.ctext
356 text
$ctext -bg white
-state disabled
-font $textfont \
357 -width $geometry(ctextw
) -height $geometry(ctexth
) \
358 -yscrollcommand ".ctop.cdet.left.sb set"
359 scrollbar .ctop.cdet.left.sb
-command "$ctext yview"
360 pack .ctop.cdet.left.sb
-side right
-fill y
361 pack
$ctext -side left
-fill both
-expand 1
362 .ctop.cdet add .ctop.cdet.left
364 $ctext tag conf filesep
-font [concat
$textfont bold
]
365 $ctext tag conf hunksep
-back blue
-fore white
366 $ctext tag conf d0
-back "#ff8080"
367 $ctext tag conf d1
-back green
368 $ctext tag conf found
-back yellow
370 frame .ctop.cdet.right
371 set cflist .ctop.cdet.right.cfiles
372 listbox
$cflist -bg white
-selectmode extended
-width $geometry(cflistw
) \
373 -yscrollcommand ".ctop.cdet.right.sb set"
374 scrollbar .ctop.cdet.right.sb
-command "$cflist yview"
375 pack .ctop.cdet.right.sb
-side right
-fill y
376 pack
$cflist -side left
-fill both
-expand 1
377 .ctop.cdet add .ctop.cdet.right
378 bind .ctop.cdet
<Configure
> {resizecdetpanes
%W
%w
}
380 pack .ctop
-side top
-fill both
-expand 1
382 bindall
<1> {selcanvline
%W
%x
%y
}
383 #bindall <B1-Motion> {selcanvline %W %x %y}
384 bindall
<ButtonRelease-4
> "allcanvs yview scroll -5 units"
385 bindall
<ButtonRelease-5
> "allcanvs yview scroll 5 units"
386 bindall
<2> "allcanvs scan mark 0 %y"
387 bindall
<B2-Motion
> "allcanvs scan dragto 0 %y"
388 bind .
<Key-Up
> "selnextline -1"
389 bind .
<Key-Down
> "selnextline 1"
390 bind .
<Key-Prior
> "allcanvs yview scroll -1 pages"
391 bind .
<Key-Next
> "allcanvs yview scroll 1 pages"
392 bindkey
<Key-Delete
> "$ctext yview scroll -1 pages"
393 bindkey
<Key-BackSpace
> "$ctext yview scroll -1 pages"
394 bindkey
<Key-space
> "$ctext yview scroll 1 pages"
395 bindkey p
"selnextline -1"
396 bindkey n
"selnextline 1"
397 bindkey b
"$ctext yview scroll -1 pages"
398 bindkey d
"$ctext yview scroll 18 units"
399 bindkey u
"$ctext yview scroll -18 units"
403 bind .
<Control-q
> doquit
404 bind .
<Control-f
> dofind
405 bind .
<Control-g
> findnext
406 bind .
<Control-r
> findprev
407 bind .
<Control-equal
> {incrfont
1}
408 bind .
<Control-KP_Add
> {incrfont
1}
409 bind .
<Control-minus
> {incrfont
-1}
410 bind .
<Control-KP_Subtract
> {incrfont
-1}
411 bind $cflist <<ListboxSelect>> listboxsel
412 bind . <Destroy> {savestuff %W}
413 bind . <Button-1> "click %W"
414 bind $fstring <Key-Return> dofind
415 bind $sha1entry <Key-Return> gotocommit
416 bind $sha1entry <<PasteSelection>> clearsha1
418 set maincursor [. cget -cursor]
419 set textcursor [$ctext cget -cursor]
421 set rowctxmenu .rowctxmenu
422 menu $rowctxmenu -tearoff 0
423 $rowctxmenu add command -label "Diff this -> selected" \
424 -command {diffvssel 0}
425 $rowctxmenu add command -label "Diff selected -> this" \
426 -command {diffvssel 1}
427 $rowctxmenu add command -label "Make patch" -command mkpatch
428 $rowctxmenu add command -label "Create tag" -command mktag
429 $rowctxmenu add command -label "Write commit to file" -command writecommit
432 # when we make a key binding for the toplevel, make sure
433 # it doesn't get triggered when that key is pressed in the
434 # find string entry widget.
435 proc bindkey {ev script} {
438 set escript [bind Entry $ev]
439 if {$escript == {}} {
440 set escript [bind Entry <Key>]
443 bind $e $ev "$escript; break"
447 # set the focus back to the toplevel for any click outside
458 global canv canv2 canv3 ctext cflist mainfont textfont
460 if {$stuffsaved} return
461 if {![winfo viewable .]} return
463 set f [open "~/.gitk-new" w]
464 puts $f "set mainfont {$mainfont}"
465 puts $f "set textfont {$textfont}"
466 puts $f "set geometry(width) [winfo width .ctop]"
467 puts $f "set geometry(height) [winfo height .ctop]"
468 puts $f "set geometry(canv1) [expr [winfo width $canv]-2]"
469 puts $f "set geometry(canv2) [expr [winfo width $canv2]-2]"
470 puts $f "set geometry(canv3) [expr [winfo width $canv3]-2]"
471 puts $f "set geometry(canvh) [expr [winfo height $canv]-2]"
472 set wid [expr {([winfo width $ctext] - 8) \
473 / [font measure $textfont "0"]}]
474 puts $f "set geometry(ctextw) $wid"
475 set wid [expr {([winfo width $cflist] - 11) \
476 / [font measure [$cflist cget -font] "0"]}]
477 puts $f "set geometry(cflistw) $wid"
479 file rename -force "~/.gitk-new" "~/.gitk"
484 proc resizeclistpanes {win w} {
486 if [info exists oldwidth($win)] {
487 set s0 [$win sash coord 0]
488 set s1 [$win sash coord 1]
490 set sash0 [expr {int($w/2 - 2)}]
491 set sash1 [expr {int($w*5/6 - 2)}]
493 set factor [expr {1.0 * $w / $oldwidth($win)}]
494 set sash0 [expr {int($factor * [lindex $s0 0])}]
495 set sash1 [expr {int($factor * [lindex $s1 0])}]
499 if {$sash1 < $sash0 + 20} {
500 set sash1 [expr $sash0 + 20]
502 if {$sash1 > $w - 10} {
503 set sash1 [expr $w - 10]
504 if {$sash0 > $sash1 - 20} {
505 set sash0 [expr $sash1 - 20]
509 $win sash place 0 $sash0 [lindex $s0 1]
510 $win sash place 1 $sash1 [lindex $s1 1]
512 set oldwidth($win) $w
515 proc resizecdetpanes {win w} {
517 if [info exists oldwidth($win)] {
518 set s0 [$win sash coord 0]
520 set sash0 [expr {int($w*3/4 - 2)}]
522 set factor [expr {1.0 * $w / $oldwidth($win)}]
523 set sash0 [expr {int($factor * [lindex $s0 0])}]
527 if {$sash0 > $w - 15} {
528 set sash0 [expr $w - 15]
531 $win sash place 0 $sash0 [lindex $s0 1]
533 set oldwidth($win) $w
537 global canv canv2 canv3
543 proc bindall {event action} {
544 global canv canv2 canv3
545 bind $canv $event $action
546 bind $canv2 $event $action
547 bind $canv3 $event $action
552 if {[winfo exists $w]} {
557 wm title $w "About gitk"
561 Copyright © 2005 Paul Mackerras
563 Use and redistribute under the terms of the GNU General Public License} \
564 -justify center -aspect 400
565 pack $w.m -side top -fill x -padx 20 -pady 20
566 button $w.ok -text Close -command "destroy $w"
567 pack $w.ok -side bottom
570 proc assigncolor {id} {
571 global commitinfo colormap commcolors colors nextcolor
572 global parents nparents children nchildren
573 global cornercrossings crossings
575 if [info exists colormap($id)] return
576 set ncolors [llength $colors]
577 if {$nparents($id) <= 1 && $nchildren($id) == 1} {
578 set child [lindex $children($id) 0]
579 if {[info exists colormap($child)]
580 && $nparents($child) == 1} {
581 set colormap($id) $colormap($child)
586 if {[info exists cornercrossings($id)]} {
587 foreach x $cornercrossings($id) {
588 if {[info exists colormap($x)]
589 && [lsearch -exact $badcolors $colormap($x)] < 0} {
590 lappend badcolors $colormap($x)
593 if {[llength $badcolors] >= $ncolors} {
597 set origbad $badcolors
598 if {[llength $badcolors] < $ncolors - 1} {
599 if {[info exists crossings($id)]} {
600 foreach x $crossings($id) {
601 if {[info exists colormap($x)]
602 && [lsearch -exact $badcolors $colormap($x)] < 0} {
603 lappend badcolors $colormap($x)
606 if {[llength $badcolors] >= $ncolors} {
607 set badcolors $origbad
610 set origbad $badcolors
612 if {[llength $badcolors] < $ncolors - 1} {
613 foreach child $children($id) {
614 if {[info exists colormap($child)]
615 && [lsearch -exact $badcolors $colormap($child)] < 0} {
616 lappend badcolors $colormap($child)
618 if {[info exists parents($child)]} {
619 foreach p $parents($child) {
620 if {[info exists colormap($p)]
621 && [lsearch -exact $badcolors $colormap($p)] < 0} {
622 lappend badcolors $colormap($p)
627 if {[llength $badcolors] >= $ncolors} {
628 set badcolors $origbad
631 for {set i 0} {$i <= $ncolors} {incr i} {
632 set c [lindex $colors $nextcolor]
633 if {[incr nextcolor] >= $ncolors} {
636 if {[lsearch -exact $badcolors $c]} break
642 global canvy canvy0 lineno numcommits lthickness nextcolor linespc
643 global mainline sidelines
644 global nchildren ncleft
651 set lthickness [expr {int($linespc / 9) + 1}]
652 catch {unset mainline}
653 catch {unset sidelines}
654 foreach id [array names nchildren] {
655 set ncleft($id) $nchildren($id)
659 proc bindline {t id} {
662 $canv bind $t <Enter> "lineenter %x %y $id"
663 $canv bind $t <Motion> "linemotion %x %y $id"
664 $canv bind $t <Leave> "lineleave $id"
665 $canv bind $t <Button-1> "lineclick %x %y $id"
668 proc drawcommitline {level} {
669 global parents children nparents nchildren todo
670 global canv canv2 canv3 mainfont namefont canvx0 canvy linespc
671 global lineid linehtag linentag linedtag commitinfo
672 global colormap numcommits currentparents dupparents
673 global oldlevel oldnlines oldtodo
674 global idtags idline idheads
675 global lineno lthickness mainline sidelines
676 global commitlisted rowtextx idpos
680 set id [lindex $todo $level]
681 set lineid($lineno) $id
682 set idline($id) $lineno
683 set ofill [expr {[info exists commitlisted($id)]? "blue": "white"}]
684 if {![info exists commitinfo($id)]} {
686 if {![info exists commitinfo($id)]} {
687 set commitinfo($id) {"No commit information available"}
692 set currentparents {}
694 if {[info exists commitlisted($id)] && [info exists parents($id)]} {
695 foreach p $parents($id) {
696 if {[lsearch -exact $currentparents $p] < 0} {
697 lappend currentparents $p
699 # remember that this parent was listed twice
700 lappend dupparents $p
704 set x [expr $canvx0 + $level * $linespc]
706 set canvy [expr $canvy + $linespc]
707 allcanvs conf -scrollregion \
708 [list 0 0 0 [expr $y1 + 0.5 * $linespc + 2]]
709 if {[info exists mainline($id)]} {
710 lappend mainline($id) $x $y1
711 set t [$canv create line $mainline($id) \
712 -width $lthickness -fill $colormap($id)]
716 if {[info exists sidelines($id)]} {
717 foreach ls $sidelines($id) {
718 set coords [lindex $ls 0]
719 set thick [lindex $ls 1]
720 set t [$canv create line $coords -fill $colormap($id) \
721 -width [expr {$thick * $lthickness}]]
726 set orad [expr {$linespc / 3}]
727 set t [$canv create oval [expr $x - $orad] [expr $y1 - $orad] \
728 [expr $x + $orad - 1] [expr $y1 + $orad - 1] \
729 -fill $ofill -outline black -width 1]
731 $canv bind $t <1> {selcanvline {} %x %y}
732 set xt [expr $canvx0 + [llength $todo] * $linespc]
733 if {[llength $currentparents] > 2} {
734 set xt [expr {$xt + ([llength $currentparents] - 2) * $linespc}]
736 set rowtextx($lineno) $xt
737 set idpos($id) [list $x $xt $y1]
738 if {[info exists idtags($id)] || [info exists idheads($id)]} {
739 set xt [drawtags $id $x $xt $y1]
741 set headline [lindex $commitinfo($id) 0]
742 set name [lindex $commitinfo($id) 1]
743 set date [lindex $commitinfo($id) 2]
744 set linehtag($lineno) [$canv create text $xt $y1 -anchor w \
745 -text $headline -font $mainfont ]
746 $canv bind $linehtag($lineno) <Button-3> "rowmenu %X %Y $id"
747 set linentag($lineno) [$canv2 create text 3 $y1 -anchor w \
748 -text $name -font $namefont]
749 set linedtag($lineno) [$canv3 create text 3 $y1 -anchor w \
750 -text $date -font $mainfont]
753 proc drawtags {id x xt y1} {
754 global idtags idheads
755 global linespc lthickness
760 if {[info exists idtags($id)]} {
761 set marks $idtags($id)
762 set ntags [llength $marks]
764 if {[info exists idheads($id)]} {
765 set marks [concat $marks $idheads($id)]
771 set delta [expr {int(0.5 * ($linespc - $lthickness))}]
772 set yt [expr $y1 - 0.5 * $linespc]
773 set yb [expr $yt + $linespc - 1]
777 set wid [font measure $mainfont $tag]
780 set xt [expr {$xt + $delta + $wid + $lthickness + $linespc}]
782 set t [$canv create line $x $y1 [lindex $xvals end] $y1 \
783 -width $lthickness -fill black -tags tag.$id]
785 foreach tag $marks x $xvals wid $wvals {
786 set xl [expr $x + $delta]
787 set xr [expr $x + $delta + $wid + $lthickness]
788 if {[incr ntags -1] >= 0} {
790 $canv create polygon $x [expr $yt + $delta] $xl $yt\
791 $xr $yt $xr $yb $xl $yb $x [expr $yb - $delta] \
792 -width 1 -outline black -fill yellow -tags tag.$id
795 set xl [expr $xl - $delta/2]
796 $canv create polygon $x $yt $xr $yt $xr $yb $x $yb \
797 -width 1 -outline black -fill green -tags tag.$id
799 $canv create text $xl $y1 -anchor w -text $tag \
800 -font $mainfont -tags tag.$id
805 proc updatetodo {level noshortcut} {
806 global currentparents ncleft todo
807 global mainline oldlevel oldtodo oldnlines
808 global canvx0 canvy linespc mainline
813 set oldnlines [llength $todo]
814 if {!$noshortcut && [llength $currentparents] == 1} {
815 set p [lindex $currentparents 0]
816 if {$ncleft($p) == 1 && [lsearch -exact $todo $p] < 0} {
818 set x [expr $canvx0 + $level * $linespc]
819 set y [expr $canvy - $linespc]
820 set mainline($p) [list $x $y]
821 set todo [lreplace $todo $level $level $p]
826 set todo [lreplace $todo $level $level]
828 foreach p $currentparents {
830 set k [lsearch -exact $todo $p]
832 set todo [linsert $todo $i $p]
839 proc notecrossings {id lo hi corner} {
840 global oldtodo crossings cornercrossings
842 for {set i $lo} {[incr i] < $hi} {} {
843 set p [lindex $oldtodo $i]
844 if {$p == {}} continue
846 if {![info exists cornercrossings($id)]
847 || [lsearch -exact $cornercrossings($id) $p] < 0} {
848 lappend cornercrossings($id) $p
850 if {![info exists cornercrossings($p)]
851 || [lsearch -exact $cornercrossings($p) $id] < 0} {
852 lappend cornercrossings($p) $id
855 if {![info exists crossings($id)]
856 || [lsearch -exact $crossings($id) $p] < 0} {
857 lappend crossings($id) $p
859 if {![info exists crossings($p)]
860 || [lsearch -exact $crossings($p) $id] < 0} {
861 lappend crossings($p) $id
868 global canv mainline sidelines canvx0 canvy linespc
869 global oldlevel oldtodo todo currentparents dupparents
870 global lthickness linespc canvy colormap
872 set y1 [expr $canvy - $linespc]
875 foreach id $oldtodo {
877 if {$id == {}} continue
878 set xi [expr {$canvx0 + $i * $linespc}]
879 if {$i == $oldlevel} {
880 foreach p $currentparents {
881 set j [lsearch -exact $todo $p]
882 set coords [list $xi $y1]
883 set xj [expr {$canvx0 + $j * $linespc}]
885 lappend coords [expr $xj + $linespc] $y1
886 notecrossings $p $j $i [expr {$j + 1}]
887 } elseif {$j > $i + 1} {
888 lappend coords [expr $xj - $linespc] $y1
889 notecrossings $p $i $j [expr {$j - 1}]
891 if {[lsearch -exact $dupparents $p] >= 0} {
892 # draw a double-width line to indicate the doubled parent
893 lappend coords $xj $y2
894 lappend sidelines($p) [list $coords 2]
895 if {![info exists mainline($p)]} {
896 set mainline($p) [list $xj $y2]
899 # normal case, no parent duplicated
900 if {![info exists mainline($p)]} {
902 lappend coords $xj $y2
904 set mainline($p) $coords
906 lappend coords $xj $y2
907 lappend sidelines($p) [list $coords 1]
911 } elseif {[lindex $todo $i] != $id} {
912 set j [lsearch -exact $todo $id]
913 set xj [expr {$canvx0 + $j * $linespc}]
914 lappend mainline($id) $xi $y1 $xj $y2
919 proc decidenext {{noread 0}} {
920 global parents children nchildren ncleft todo
921 global canv canv2 canv3 mainfont namefont canvx0 canvy linespc
922 global datemode cdate
924 global currentparents oldlevel oldnlines oldtodo
925 global lineno lthickness
927 # remove the null entry if present
928 set nullentry [lsearch -exact $todo {}]
929 if {$nullentry >= 0} {
930 set todo [lreplace $todo $nullentry $nullentry]
933 # choose which one to do next time around
934 set todol [llength $todo]
937 for {set k $todol} {[incr k -1] >= 0} {} {
938 set p [lindex $todo $k]
939 if {$ncleft($p) == 0} {
941 if {![info exists commitinfo($p)]} {
947 if {$latest == {} || $cdate($p) > $latest} {
949 set latest $cdate($p)
959 puts "ERROR: none of the pending commits can be done yet:"
961 puts " $p ($ncleft($p))"
967 # If we are reducing, put in a null entry
968 if {$todol < $oldnlines} {
969 if {$nullentry >= 0} {
972 && [lindex $oldtodo $i] == [lindex $todo $i]} {
982 set todo [linsert $todo $i {}]
991 proc drawcommit {id} {
992 global phase todo nchildren datemode nextupdate
995 if {$phase != "incrdraw"} {
1001 updatetodo 0 $datemode
1003 if {$nchildren($id) == 0} {
1005 lappend startcommits $id
1007 set level [decidenext 1]
1008 if {$level == {} || $id != [lindex $todo $level]} {
1013 drawcommitline $level
1014 if {[updatetodo $level $datemode]} {
1015 set level [decidenext 1]
1016 if {$level == {}} break
1018 set id [lindex $todo $level]
1019 if {![info exists commitlisted($id)]} {
1022 if {[clock clicks -milliseconds] >= $nextupdate} {
1030 proc finishcommits {} {
1033 global canv mainfont ctext maincursor textcursor
1035 if {$phase != "incrdraw"} {
1037 $canv create text 3 3 -anchor nw -text "No commits selected" \
1038 -font $mainfont -tags textitems
1042 set level [decidenext]
1043 drawrest $level [llength $startcommits]
1045 . config -cursor $maincursor
1046 $ctext config -cursor $textcursor
1050 global nextupdate startmsecs startcommits todo
1052 if {$startcommits == {}} return
1053 set startmsecs [clock clicks -milliseconds]
1054 set nextupdate [expr $startmsecs + 100]
1056 set todo [lindex $startcommits 0]
1060 proc drawrest {level startix} {
1061 global phase stopped redisplaying selectedline
1062 global datemode currentparents todo
1064 global nextupdate startmsecs startcommits idline
1068 set startid [lindex $startcommits $startix]
1070 if {$startid != {}} {
1071 set startline $idline($startid)
1075 drawcommitline $level
1076 set hard [updatetodo $level $datemode]
1077 if {$numcommits == $startline} {
1078 lappend todo $startid
1081 set startid [lindex $startcommits $startix]
1083 if {$startid != {}} {
1084 set startline $idline($startid)
1088 set level [decidenext]
1089 if {$level < 0} break
1092 if {[clock clicks -milliseconds] >= $nextupdate} {
1099 set drawmsecs [expr [clock clicks -milliseconds] - $startmsecs]
1100 #puts "overall $drawmsecs ms for $numcommits commits"
1101 if {$redisplaying} {
1102 if {$stopped == 0 && [info exists selectedline]} {
1103 selectline $selectedline
1105 if {$stopped == 1} {
1107 after idle drawgraph
1114 proc findmatches {f} {
1115 global findtype foundstring foundstrlen
1116 if {$findtype == "Regexp"} {
1117 set matches [regexp -indices -all -inline $foundstring $f]
1119 if {$findtype == "IgnCase"} {
1120 set str [string tolower $f]
1126 while {[set j [string first $foundstring $str $i]] >= 0} {
1127 lappend matches [list $j [expr $j+$foundstrlen-1]]
1128 set i [expr $j + $foundstrlen]
1135 global findtype findloc findstring markedmatches commitinfo
1136 global numcommits lineid linehtag linentag linedtag
1137 global mainfont namefont canv canv2 canv3 selectedline
1138 global matchinglines foundstring foundstrlen
1141 set matchinglines {}
1142 set fldtypes {Headline Author Date Committer CDate Comment}
1143 if {$findtype == "IgnCase"} {
1144 set foundstring [string tolower $findstring]
1146 set foundstring $findstring
1148 set foundstrlen [string length $findstring]
1149 if {$foundstrlen == 0} return
1150 if {![info exists selectedline]} {
1153 set oldsel $selectedline
1156 for {set l 0} {$l < $numcommits} {incr l} {
1158 set info $commitinfo($id)
1160 foreach f $info ty $fldtypes {
1161 if {$findloc != "All fields" && $findloc != $ty} {
1164 set matches [findmatches $f]
1165 if {$matches == {}} continue
1167 if {$ty == "Headline"} {
1168 markmatches $canv $l $f $linehtag($l) $matches $mainfont
1169 } elseif {$ty == "Author"} {
1170 markmatches $canv2 $l $f $linentag($l) $matches $namefont
1171 } elseif {$ty == "Date"} {
1172 markmatches $canv3 $l $f $linedtag($l) $matches $mainfont
1176 lappend matchinglines $l
1177 if {!$didsel && $l > $oldsel} {
1183 if {$matchinglines == {}} {
1185 } elseif {!$didsel} {
1186 findselectline [lindex $matchinglines 0]
1190 proc findselectline {l} {
1191 global findloc commentend ctext
1193 if {$findloc == "All fields" || $findloc == "Comments"} {
1194 # highlight the matches in the comments
1195 set f [$ctext get 1.0 $commentend]
1196 set matches [findmatches $f]
1197 foreach match $matches {
1198 set start [lindex $match 0]
1199 set end [expr [lindex $match 1] + 1]
1200 $ctext tag add found "1.0 + $start c" "1.0 + $end c"
1206 global matchinglines selectedline
1207 if {![info exists matchinglines]} {
1211 if {![info exists selectedline]} return
1212 foreach l $matchinglines {
1213 if {$l > $selectedline} {
1222 global matchinglines selectedline
1223 if {![info exists matchinglines]} {
1227 if {![info exists selectedline]} return
1229 foreach l $matchinglines {
1230 if {$l >= $selectedline} break
1234 findselectline $prev
1240 proc markmatches {canv l str tag matches font} {
1241 set bbox [$canv bbox $tag]
1242 set x0 [lindex $bbox 0]
1243 set y0 [lindex $bbox 1]
1244 set y1 [lindex $bbox 3]
1245 foreach match $matches {
1246 set start [lindex $match 0]
1247 set end [lindex $match 1]
1248 if {$start > $end} continue
1249 set xoff [font measure $font [string range $str 0 [expr $start-1]]]
1250 set xlen [font measure $font [string range $str 0 [expr $end]]]
1251 set t [$canv create rect [expr $x0+$xoff] $y0 [expr $x0+$xlen+2] $y1 \
1252 -outline {} -tags matches -fill yellow]
1257 proc unmarkmatches {} {
1258 global matchinglines
1259 allcanvs delete matches
1260 catch {unset matchinglines}
1263 proc selcanvline {w x y} {
1264 global canv canvy0 ctext linespc selectedline
1265 global lineid linehtag linentag linedtag rowtextx
1266 set ymax [lindex [$canv cget -scrollregion] 3]
1267 if {$ymax == {}} return
1268 set yfrac [lindex [$canv yview] 0]
1269 set y [expr {$y + $yfrac * $ymax}]
1270 set l [expr {int(($y - $canvy0) / $linespc + 0.5)}]
1275 if {![info exists rowtextx($l)] || $x < $rowtextx($l)} return
1281 proc selectline {l} {
1282 global canv canv2 canv3 ctext commitinfo selectedline
1283 global lineid linehtag linentag linedtag
1284 global canvy0 linespc parents nparents
1285 global cflist currentid sha1entry diffids
1286 global commentend seenfile idtags
1288 if {![info exists lineid($l)] || ![info exists linehtag($l)]} return
1290 set t [eval $canv create rect [$canv bbox $linehtag($l)] -outline {{}} \
1291 -tags secsel -fill [$canv cget -selectbackground]]
1293 $canv2 delete secsel
1294 set t [eval $canv2 create rect [$canv2 bbox $linentag($l)] -outline {{}} \
1295 -tags secsel -fill [$canv2 cget -selectbackground]]
1297 $canv3 delete secsel
1298 set t [eval $canv3 create rect [$canv3 bbox $linedtag($l)] -outline {{}} \
1299 -tags secsel -fill [$canv3 cget -selectbackground]]
1301 set y [expr {$canvy0 + $l * $linespc}]
1302 set ymax [lindex [$canv cget -scrollregion] 3]
1303 set ytop [expr {$y - $linespc - 1}]
1304 set ybot [expr {$y + $linespc + 1}]
1305 set wnow [$canv yview]
1306 set wtop [expr [lindex $wnow 0] * $ymax]
1307 set wbot [expr [lindex $wnow 1] * $ymax]
1308 set wh [expr {$wbot - $wtop}]
1310 if {$ytop < $wtop} {
1311 if {$ybot < $wtop} {
1312 set newtop [expr {$y - $wh / 2.0}]
1315 if {$newtop > $wtop - $linespc} {
1316 set newtop [expr {$wtop - $linespc}]
1319 } elseif {$ybot > $wbot} {
1320 if {$ytop > $wbot} {
1321 set newtop [expr {$y - $wh / 2.0}]
1323 set newtop [expr {$ybot - $wh}]
1324 if {$newtop < $wtop + $linespc} {
1325 set newtop [expr {$wtop + $linespc}]
1329 if {$newtop != $wtop} {
1333 allcanvs yview moveto [expr $newtop * 1.0 / $ymax]
1339 set diffids [concat $id $parents($id)]
1340 $sha1entry delete 0 end
1341 $sha1entry insert 0 $id
1342 $sha1entry selection from 0
1343 $sha1entry selection to end
1345 $ctext conf -state normal
1346 $ctext delete 0.0 end
1347 $ctext mark set fmark.0 0.0
1348 $ctext mark gravity fmark.0 left
1349 set info $commitinfo($id)
1350 $ctext insert end "Author: [lindex $info 1] [lindex $info 2]\n"
1351 $ctext insert end "Committer: [lindex $info 3] [lindex $info 4]\n"
1352 if {[info exists idtags($id)]} {
1353 $ctext insert end "Tags:"
1354 foreach tag $idtags($id) {
1355 $ctext insert end " $tag"
1357 $ctext insert end "\n"
1359 $ctext insert end "\n"
1360 $ctext insert end [lindex $info 5]
1361 $ctext insert end "\n"
1362 $ctext tag delete Comments
1363 $ctext tag remove found 1.0 end
1364 $ctext conf -state disabled
1365 set commentend [$ctext index "end - 1c"]
1367 $cflist delete 0 end
1368 $cflist insert end "Comments"
1369 if {$nparents($id) == 1} {
1372 catch {unset seenfile}
1376 global treediffs diffids treepending
1378 if {![info exists treediffs($diffids)]} {
1379 if {![info exists treepending]} {
1380 gettreediffs $diffids
1383 addtocflist $diffids
1387 proc selnextline {dir} {
1389 if {![info exists selectedline]} return
1390 set l [expr $selectedline + $dir]
1395 proc addtocflist {ids} {
1396 global diffids treediffs cflist
1397 if {$ids != $diffids} {
1398 gettreediffs $diffids
1401 foreach f $treediffs($ids) {
1402 $cflist insert end $f
1407 proc gettreediffs {ids} {
1408 global treediffs parents treepending
1409 set treepending $ids
1410 set treediffs($ids) {}
1411 set id [lindex $ids 0]
1412 set p [lindex $ids 1]
1413 if [catch {set gdtf [open "|git-diff-tree -r $p $id" r]}] return
1414 fconfigure $gdtf -blocking 0
1415 fileevent $gdtf readable "gettreediffline $gdtf {$ids}"
1418 proc gettreediffline {gdtf ids} {
1419 global treediffs treepending
1420 set n [gets $gdtf line]
1422 if {![eof $gdtf]} return
1428 set file [lindex $line 5]
1429 lappend treediffs($ids) $file
1432 proc getblobdiffs {ids} {
1433 global diffopts blobdifffd env curdifftag curtagstart
1434 global diffindex difffilestart nextupdate
1436 set id [lindex $ids 0]
1437 set p [lindex $ids 1]
1438 set env(GIT_DIFF_OPTS) $diffopts
1439 if [catch {set bdf [open "|git-diff-tree -r -p $p $id" r]} err] {
1440 puts "error getting diffs: $err"
1443 fconfigure $bdf -blocking 0
1444 set blobdifffd($ids) $bdf
1445 set curdifftag Comments
1448 catch {unset difffilestart}
1449 fileevent $bdf readable "getblobdiffline $bdf {$ids}"
1450 set nextupdate [expr {[clock clicks -milliseconds] + 100}]
1453 proc getblobdiffline {bdf ids} {
1454 global diffids blobdifffd ctext curdifftag curtagstart seenfile
1455 global diffnexthead diffnextnote diffindex difffilestart
1458 set n [gets $bdf line]
1462 if {$ids == $diffids && $bdf == $blobdifffd($ids)} {
1463 $ctext tag add $curdifftag $curtagstart end
1464 set seenfile($curdifftag) 1
1469 if {$ids != $diffids || $bdf != $blobdifffd($ids)} {
1472 $ctext conf -state normal
1473 if {[regexp {^---[ \t]+([^/])*/(.*)} $line match s1 fname]} {
1474 # start of a new file
1475 $ctext insert end "\n"
1476 $ctext tag add $curdifftag $curtagstart end
1477 set seenfile($curdifftag) 1
1478 set curtagstart [$ctext index "end - 1c"]
1480 if {[info exists diffnexthead]} {
1481 set fname $diffnexthead
1482 set header "$diffnexthead ($diffnextnote)"
1485 set here [$ctext index "end - 1c"]
1486 set difffilestart($diffindex) $here
1488 # start mark names at fmark.1 for first file
1489 $ctext mark set fmark.$diffindex $here
1490 $ctext mark gravity fmark.$diffindex left
1491 set curdifftag "f:$fname"
1492 $ctext tag delete $curdifftag
1493 set l [expr {(78 - [string length $header]) / 2}]
1494 set pad [string range "----------------------------------------" 1 $l]
1495 $ctext insert end "$pad $header $pad\n" filesep
1496 } elseif {[string range $line 0 2] == "+++"} {
1497 # no need to do anything with this
1498 } elseif {[regexp {^Created: (.*) \((mode: *[0-7]*)\)} $line match fn m]} {
1499 set diffnexthead $fn
1500 set diffnextnote "created, mode $m"
1501 } elseif {[string range $line 0 8] == "Deleted: "} {
1502 set diffnexthead [string range $line 9 end]
1503 set diffnextnote "deleted"
1504 } elseif {[regexp {^diff --git a/(.*) b/} $line match fn]} {
1505 # save the filename in case the next thing is "new file mode ..."
1506 set diffnexthead $fn
1507 set diffnextnote "modified"
1508 } elseif {[regexp {^new file mode ([0-7]+)} $line match m]} {
1509 set diffnextnote "new file, mode $m"
1510 } elseif {[string range $line 0 11] == "deleted file"} {
1511 set diffnextnote "deleted"
1512 } elseif {[regexp {^@@ -([0-9]+),([0-9]+) \+([0-9]+),([0-9]+) @@(.*)} \
1513 $line match f1l f1c f2l f2c rest]} {
1514 $ctext insert end "\t" hunksep
1515 $ctext insert end " $f1l " d0 " $f2l " d1
1516 $ctext insert end " $rest \n" hunksep
1518 set x [string range $line 0 0]
1519 if {$x == "-" || $x == "+"} {
1520 set tag [expr {$x == "+"}]
1521 set line [string range $line 1 end]
1522 $ctext insert end "$line\n" d$tag
1523 } elseif {$x == " "} {
1524 set line [string range $line 1 end]
1525 $ctext insert end "$line\n"
1526 } elseif {$x == "\\"} {
1527 # e.g. "\ No newline at end of file"
1528 $ctext insert end "$line\n" filesep
1530 # Something else we don't recognize
1531 if {$curdifftag != "Comments"} {
1532 $ctext insert end "\n"
1533 $ctext tag add $curdifftag $curtagstart end
1534 set seenfile($curdifftag) 1
1535 set curtagstart [$ctext index "end - 1c"]
1536 set curdifftag Comments
1538 $ctext insert end "$line\n" filesep
1541 $ctext conf -state disabled
1542 if {[clock clicks -milliseconds] >= $nextupdate} {
1544 fileevent $bdf readable {}
1546 fileevent $bdf readable "getblobdiffline $bdf {$ids}"
1551 global difffilestart ctext
1552 set here [$ctext index @0,0]
1553 for {set i 0} {[info exists difffilestart($i)]} {incr i} {
1554 if {[$ctext compare $difffilestart($i) > $here]} {
1555 $ctext yview $difffilestart($i)
1561 proc listboxsel {} {
1562 global ctext cflist currentid treediffs seenfile
1563 if {![info exists currentid]} return
1564 set sel [lsort [$cflist curselection]]
1565 if {$sel eq {}} return
1566 set first [lindex $sel 0]
1567 catch {$ctext yview fmark.$first}
1571 global linespc charspc canvx0 canvy0 mainfont
1572 set linespc [font metrics $mainfont -linespace]
1573 set charspc [font measure $mainfont "m"]
1574 set canvy0 [expr 3 + 0.5 * $linespc]
1575 set canvx0 [expr 3 + 0.5 * $linespc]
1579 global selectedline stopped redisplaying phase
1580 if {$stopped > 1} return
1581 if {$phase == "getcommits"} return
1583 if {$phase == "drawgraph" || $phase == "incrdraw"} {
1590 proc incrfont {inc} {
1591 global mainfont namefont textfont selectedline ctext canv phase
1592 global stopped entries
1594 set mainfont [lreplace $mainfont 1 1 [expr {[lindex $mainfont 1] + $inc}]]
1595 set namefont [lreplace $namefont 1 1 [expr {[lindex $namefont 1] + $inc}]]
1596 set textfont [lreplace $textfont 1 1 [expr {[lindex $textfont 1] + $inc}]]
1598 $ctext conf -font $textfont
1599 $ctext tag conf filesep -font [concat $textfont bold]
1600 foreach e $entries {
1601 $e conf -font $mainfont
1603 if {$phase == "getcommits"} {
1604 $canv itemconf textitems -font $mainfont
1610 global sha1entry sha1string
1611 if {[string length $sha1string] == 40} {
1612 $sha1entry delete 0 end
1616 proc sha1change {n1 n2 op} {
1617 global sha1string currentid sha1but
1618 if {$sha1string == {}
1619 || ([info exists currentid] && $sha1string == $currentid)} {
1624 if {[$sha1but cget -state] == $state} return
1625 if {$state == "normal"} {
1626 $sha1but conf -state normal -relief raised -text "Goto: "
1628 $sha1but conf -state disabled -relief flat -text "SHA1 ID: "
1632 proc gotocommit {} {
1633 global sha1string currentid idline tagids
1634 if {$sha1string == {}
1635 || ([info exists currentid] && $sha1string == $currentid)} return
1636 if {[info exists tagids($sha1string)]} {
1637 set id $tagids($sha1string)
1639 set id [string tolower $sha1string]
1641 if {[info exists idline($id)]} {
1642 selectline $idline($id)
1645 if {[regexp {^[0-9a-fA-F]{40}$} $sha1string]} {
1650 error_popup "$type $sha1string is not known"
1653 proc lineenter {x y id} {
1654 global hoverx hovery hoverid hovertimer
1655 global commitinfo canv
1657 if {![info exists commitinfo($id)]} return
1661 if {[info exists hovertimer]} {
1662 after cancel $hovertimer
1664 set hovertimer [after 500 linehover]
1668 proc linemotion {x y id} {
1669 global hoverx hovery hoverid hovertimer
1671 if {[info exists hoverid] && $id == $hoverid} {
1674 if {[info exists hovertimer]} {
1675 after cancel $hovertimer
1677 set hovertimer [after 500 linehover]
1681 proc lineleave {id} {
1682 global hoverid hovertimer canv
1684 if {[info exists hoverid] && $id == $hoverid} {
1686 if {[info exists hovertimer]} {
1687 after cancel $hovertimer
1695 global hoverx hovery hoverid hovertimer
1696 global canv linespc lthickness
1697 global commitinfo mainfont
1699 set text [lindex $commitinfo($hoverid) 0]
1700 set ymax [lindex [$canv cget -scrollregion] 3]
1701 if {$ymax == {}} return
1702 set yfrac [lindex [$canv yview] 0]
1703 set x [expr {$hoverx + 2 * $linespc}]
1704 set y [expr {$hovery + $yfrac * $ymax - $linespc / 2}]
1705 set x0 [expr {$x - 2 * $lthickness}]
1706 set y0 [expr {$y - 2 * $lthickness}]
1707 set x1 [expr {$x + [font measure $mainfont $text] + 2 * $lthickness}]
1708 set y1 [expr {$y + $linespc + 2 * $lthickness}]
1709 set t [$canv create rectangle $x0 $y0 $x1 $y1 \
1710 -fill \#ffff80 -outline black -width 1 -tags hover]
1712 set t [$canv create text $x $y -anchor nw -text $text -tags hover]
1716 proc lineclick {x y id} {
1717 global ctext commitinfo children cflist canv
1721 # fill the details pane with info about this line
1722 $ctext conf -state normal
1723 $ctext delete 0.0 end
1724 $ctext insert end "Parent:\n "
1725 catch {destroy $ctext.$id}
1726 button $ctext.$id -text "Go:" -command "selbyid $id" \
1728 $ctext window create end -window $ctext.$id -align center
1729 set info $commitinfo($id)
1730 $ctext insert end "\t[lindex $info 0]\n"
1731 $ctext insert end "\tAuthor:\t[lindex $info 1]\n"
1732 $ctext insert end "\tDate:\t[lindex $info 2]\n"
1733 $ctext insert end "\tID:\t$id\n"
1734 if {[info exists children($id)]} {
1735 $ctext insert end "\nChildren:"
1736 foreach child $children($id) {
1737 $ctext insert end "\n "
1738 catch {destroy $ctext.$child}
1739 button $ctext.$child -text "Go:" -command "selbyid $child" \
1741 $ctext window create end -window $ctext.$child -align center
1742 set info $commitinfo($child)
1743 $ctext insert end "\t[lindex $info 0]"
1746 $ctext conf -state disabled
1748 $cflist delete 0 end
1753 if {[info exists idline($id)]} {
1754 selectline $idline($id)
1760 if {![info exists startmstime]} {
1761 set startmstime [clock clicks -milliseconds]
1763 return [format "%.3f" [expr {([clock click -milliseconds] - $startmstime) / 1000.0}]]
1766 proc rowmenu {x y id} {
1767 global rowctxmenu idline selectedline rowmenuid
1769 if {![info exists selectedline] || $idline($id) eq $selectedline} {
1774 $rowctxmenu entryconfigure 0 -state $state
1775 $rowctxmenu entryconfigure 1 -state $state
1776 $rowctxmenu entryconfigure 2 -state $state
1778 tk_popup $rowctxmenu $x $y
1781 proc diffvssel {dirn} {
1782 global rowmenuid selectedline lineid
1784 global diffids commitinfo
1786 if {![info exists selectedline]} return
1788 set oldid $lineid($selectedline)
1789 set newid $rowmenuid
1791 set oldid $rowmenuid
1792 set newid $lineid($selectedline)
1794 $ctext conf -state normal
1795 $ctext delete 0.0 end
1796 $ctext mark set fmark.0 0.0
1797 $ctext mark gravity fmark.0 left
1798 $cflist delete 0 end
1799 $cflist insert end "Top"
1800 $ctext insert end "From $oldid\n "
1801 $ctext insert end [lindex $commitinfo($oldid) 0]
1802 $ctext insert end "\n\nTo $newid\n "
1803 $ctext insert end [lindex $commitinfo($newid) 0]
1804 $ctext insert end "\n"
1805 $ctext conf -state disabled
1806 $ctext tag delete Comments
1807 $ctext tag remove found 1.0 end
1808 set diffids [list $newid $oldid]
1813 global rowmenuid currentid commitinfo patchtop patchnum
1815 if {![info exists currentid]} return
1816 set oldid $currentid
1817 set oldhead [lindex $commitinfo($oldid) 0]
1818 set newid $rowmenuid
1819 set newhead [lindex $commitinfo($newid) 0]
1822 catch {destroy $top}
1824 label $top.title -text "Generate patch"
1825 grid $top.title - -pady 10
1826 label $top.from -text "From:"
1827 entry $top.fromsha1 -width 40 -relief flat
1828 $top.fromsha1 insert 0 $oldid
1829 $top.fromsha1 conf -state readonly
1830 grid $top.from $top.fromsha1 -sticky w
1831 entry $top.fromhead -width 60 -relief flat
1832 $top.fromhead insert 0 $oldhead
1833 $top.fromhead conf -state readonly
1834 grid x $top.fromhead -sticky w
1835 label $top.to -text "To:"
1836 entry $top.tosha1 -width 40 -relief flat
1837 $top.tosha1 insert 0 $newid
1838 $top.tosha1 conf -state readonly
1839 grid $top.to $top.tosha1 -sticky w
1840 entry $top.tohead -width 60 -relief flat
1841 $top.tohead insert 0 $newhead
1842 $top.tohead conf -state readonly
1843 grid x $top.tohead -sticky w
1844 button $top.rev -text "Reverse" -command mkpatchrev -padx 5
1845 grid $top.rev x -pady 10
1846 label $top.flab -text "Output file:"
1847 entry $top.fname -width 60
1848 $top.fname insert 0 [file normalize "patch$patchnum.patch"]
1850 grid $top.flab $top.fname -sticky w
1852 button $top.buts.gen -text "Generate" -command mkpatchgo
1853 button $top.buts.can -text "Cancel" -command mkpatchcan
1854 grid $top.buts.gen $top.buts.can
1855 grid columnconfigure $top.buts 0 -weight 1 -uniform a
1856 grid columnconfigure $top.buts 1 -weight 1 -uniform a
1857 grid $top.buts - -pady 10 -sticky ew
1861 proc mkpatchrev {} {
1864 set oldid [$patchtop.fromsha1 get]
1865 set oldhead [$patchtop.fromhead get]
1866 set newid [$patchtop.tosha1 get]
1867 set newhead [$patchtop.tohead get]
1868 foreach e [list fromsha1 fromhead tosha1 tohead] \
1869 v [list $newid $newhead $oldid $oldhead] {
1870 $patchtop.$e conf -state normal
1871 $patchtop.$e delete 0 end
1872 $patchtop.$e insert 0 $v
1873 $patchtop.$e conf -state readonly
1880 set oldid [$patchtop.fromsha1 get]
1881 set newid [$patchtop.tosha1 get]
1882 set fname [$patchtop.fname get]
1883 if {[catch {exec git-diff-tree -p $oldid $newid >$fname &} err]} {
1884 error_popup "Error creating patch: $err"
1886 catch {destroy $patchtop}
1890 proc mkpatchcan {} {
1893 catch {destroy $patchtop}
1898 global rowmenuid mktagtop commitinfo
1902 catch {destroy $top}
1904 label $top.title -text "Create tag"
1905 grid $top.title - -pady 10
1906 label $top.id -text "ID:"
1907 entry $top.sha1 -width 40 -relief flat
1908 $top.sha1 insert 0 $rowmenuid
1909 $top.sha1 conf -state readonly
1910 grid $top.id $top.sha1 -sticky w
1911 entry $top.head -width 60 -relief flat
1912 $top.head insert 0 [lindex $commitinfo($rowmenuid) 0]
1913 $top.head conf -state readonly
1914 grid x $top.head -sticky w
1915 label $top.tlab -text "Tag name:"
1916 entry $top.tag -width 60
1917 grid $top.tlab $top.tag -sticky w
1919 button $top.buts.gen -text "Create" -command mktaggo
1920 button $top.buts.can -text "Cancel" -command mktagcan
1921 grid $top.buts.gen $top.buts.can
1922 grid columnconfigure $top.buts 0 -weight 1 -uniform a
1923 grid columnconfigure $top.buts 1 -weight 1 -uniform a
1924 grid $top.buts - -pady 10 -sticky ew
1929 global mktagtop env tagids idtags
1930 global idpos idline linehtag canv selectedline
1932 set id [$mktagtop.sha1 get]
1933 set tag [$mktagtop.tag get]
1935 error_popup "No tag name specified"
1938 if {[info exists tagids($tag)]} {
1939 error_popup "Tag \"$tag\" already exists"
1944 if {[info exists env(GIT_DIR)]} {
1945 set dir $env(GIT_DIR)
1947 set fname [file join $dir "refs/tags" $tag]
1948 set f [open $fname w]
1952 error_popup "Error creating tag: $err"
1956 set tagids($tag) $id
1957 lappend idtags($id) $tag
1958 $canv delete tag.$id
1959 set xt [eval drawtags $id $idpos($id)]
1960 $canv coords $linehtag($idline($id)) $xt [lindex $idpos($id) 2]
1961 if {[info exists selectedline] && $selectedline == $idline($id)} {
1962 selectline $selectedline
1969 catch {destroy $mktagtop}
1978 proc writecommit {} {
1979 global rowmenuid wrcomtop commitinfo wrcomcmd
1981 set top .writecommit
1983 catch {destroy $top}
1985 label $top.title -text "Write commit to file"
1986 grid $top.title - -pady 10
1987 label $top.id -text "ID:"
1988 entry $top.sha1 -width 40 -relief flat
1989 $top.sha1 insert 0 $rowmenuid
1990 $top.sha1 conf -state readonly
1991 grid $top.id $top.sha1 -sticky w
1992 entry $top.head -width 60 -relief flat
1993 $top.head insert 0 [lindex $commitinfo($rowmenuid) 0]
1994 $top.head conf -state readonly
1995 grid x $top.head -sticky w
1996 label $top.clab -text "Command:"
1997 entry $top.cmd -width 60 -textvariable wrcomcmd
1998 grid $top.clab $top.cmd -sticky w -pady 10
1999 label $top.flab -text "Output file:"
2000 entry $top.fname -width 60
2001 $top.fname insert 0 [file normalize "commit-[string range $rowmenuid 0 6]"]
2002 grid $top.flab $top.fname -sticky w
2004 button $top.buts.gen -text "Write" -command wrcomgo
2005 button $top.buts.can -text "Cancel" -command wrcomcan
2006 grid $top.buts.gen $top.buts.can
2007 grid columnconfigure $top.buts 0 -weight 1 -uniform a
2008 grid columnconfigure $top.buts 1 -weight 1 -uniform a
2009 grid $top.buts - -pady 10 -sticky ew
2016 set id [$wrcomtop.sha1 get]
2017 set cmd "echo $id | [$wrcomtop.cmd get]"
2018 set fname [$wrcomtop.fname get]
2019 if {[catch {exec sh -c $cmd >$fname &} err]} {
2020 error_popup "Error writing commit: $err"
2022 catch {destroy $wrcomtop}
2029 catch {destroy $wrcomtop}
2042 set diffopts "-U 5 -p"
2043 set wrcomcmd "git-diff-tree --stdin -p --pretty"
2045 set mainfont {Helvetica 9}
2046 set textfont {Courier 9}
2048 set colors {green red blue magenta darkgrey brown orange}
2050 catch {source ~/.gitk}
2052 set namefont $mainfont
2054 lappend namefont bold
2059 switch -regexp -- $arg {
2061 "^-b" { set boldnames 1 }
2062 "^-d" { set datemode 1 }
2064 lappend revtreeargs $arg
2076 getcommits $revtreeargs