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
431 # when we make a key binding for the toplevel, make sure
432 # it doesn't get triggered when that key is pressed in the
433 # find string entry widget.
434 proc bindkey {ev script} {
437 set escript [bind Entry $ev]
438 if {$escript == {}} {
439 set escript [bind Entry <Key>]
442 bind $e $ev "$escript; break"
446 # set the focus back to the toplevel for any click outside
457 global canv canv2 canv3 ctext cflist mainfont textfont
459 if {$stuffsaved} return
460 if {![winfo viewable .]} return
462 set f [open "~/.gitk-new" w]
463 puts $f "set mainfont {$mainfont}"
464 puts $f "set textfont {$textfont}"
465 puts $f "set geometry(width) [winfo width .ctop]"
466 puts $f "set geometry(height) [winfo height .ctop]"
467 puts $f "set geometry(canv1) [expr [winfo width $canv]-2]"
468 puts $f "set geometry(canv2) [expr [winfo width $canv2]-2]"
469 puts $f "set geometry(canv3) [expr [winfo width $canv3]-2]"
470 puts $f "set geometry(canvh) [expr [winfo height $canv]-2]"
471 set wid [expr {([winfo width $ctext] - 8) \
472 / [font measure $textfont "0"]}]
473 puts $f "set geometry(ctextw) $wid"
474 set wid [expr {([winfo width $cflist] - 11) \
475 / [font measure [$cflist cget -font] "0"]}]
476 puts $f "set geometry(cflistw) $wid"
478 file rename -force "~/.gitk-new" "~/.gitk"
483 proc resizeclistpanes {win w} {
485 if [info exists oldwidth($win)] {
486 set s0 [$win sash coord 0]
487 set s1 [$win sash coord 1]
489 set sash0 [expr {int($w/2 - 2)}]
490 set sash1 [expr {int($w*5/6 - 2)}]
492 set factor [expr {1.0 * $w / $oldwidth($win)}]
493 set sash0 [expr {int($factor * [lindex $s0 0])}]
494 set sash1 [expr {int($factor * [lindex $s1 0])}]
498 if {$sash1 < $sash0 + 20} {
499 set sash1 [expr $sash0 + 20]
501 if {$sash1 > $w - 10} {
502 set sash1 [expr $w - 10]
503 if {$sash0 > $sash1 - 20} {
504 set sash0 [expr $sash1 - 20]
508 $win sash place 0 $sash0 [lindex $s0 1]
509 $win sash place 1 $sash1 [lindex $s1 1]
511 set oldwidth($win) $w
514 proc resizecdetpanes {win w} {
516 if [info exists oldwidth($win)] {
517 set s0 [$win sash coord 0]
519 set sash0 [expr {int($w*3/4 - 2)}]
521 set factor [expr {1.0 * $w / $oldwidth($win)}]
522 set sash0 [expr {int($factor * [lindex $s0 0])}]
526 if {$sash0 > $w - 15} {
527 set sash0 [expr $w - 15]
530 $win sash place 0 $sash0 [lindex $s0 1]
532 set oldwidth($win) $w
536 global canv canv2 canv3
542 proc bindall {event action} {
543 global canv canv2 canv3
544 bind $canv $event $action
545 bind $canv2 $event $action
546 bind $canv3 $event $action
551 if {[winfo exists $w]} {
556 wm title $w "About gitk"
560 Copyright © 2005 Paul Mackerras
562 Use and redistribute under the terms of the GNU General Public License} \
563 -justify center -aspect 400
564 pack $w.m -side top -fill x -padx 20 -pady 20
565 button $w.ok -text Close -command "destroy $w"
566 pack $w.ok -side bottom
569 proc assigncolor {id} {
570 global commitinfo colormap commcolors colors nextcolor
571 global parents nparents children nchildren
572 global cornercrossings crossings
574 if [info exists colormap($id)] return
575 set ncolors [llength $colors]
576 if {$nparents($id) <= 1 && $nchildren($id) == 1} {
577 set child [lindex $children($id) 0]
578 if {[info exists colormap($child)]
579 && $nparents($child) == 1} {
580 set colormap($id) $colormap($child)
585 if {[info exists cornercrossings($id)]} {
586 foreach x $cornercrossings($id) {
587 if {[info exists colormap($x)]
588 && [lsearch -exact $badcolors $colormap($x)] < 0} {
589 lappend badcolors $colormap($x)
592 if {[llength $badcolors] >= $ncolors} {
596 set origbad $badcolors
597 if {[llength $badcolors] < $ncolors - 1} {
598 if {[info exists crossings($id)]} {
599 foreach x $crossings($id) {
600 if {[info exists colormap($x)]
601 && [lsearch -exact $badcolors $colormap($x)] < 0} {
602 lappend badcolors $colormap($x)
605 if {[llength $badcolors] >= $ncolors} {
606 set badcolors $origbad
609 set origbad $badcolors
611 if {[llength $badcolors] < $ncolors - 1} {
612 foreach child $children($id) {
613 if {[info exists colormap($child)]
614 && [lsearch -exact $badcolors $colormap($child)] < 0} {
615 lappend badcolors $colormap($child)
617 if {[info exists parents($child)]} {
618 foreach p $parents($child) {
619 if {[info exists colormap($p)]
620 && [lsearch -exact $badcolors $colormap($p)] < 0} {
621 lappend badcolors $colormap($p)
626 if {[llength $badcolors] >= $ncolors} {
627 set badcolors $origbad
630 for {set i 0} {$i <= $ncolors} {incr i} {
631 set c [lindex $colors $nextcolor]
632 if {[incr nextcolor] >= $ncolors} {
635 if {[lsearch -exact $badcolors $c]} break
641 global canvy canvy0 lineno numcommits lthickness nextcolor linespc
642 global mainline sidelines
643 global nchildren ncleft
650 set lthickness [expr {int($linespc / 9) + 1}]
651 catch {unset mainline}
652 catch {unset sidelines}
653 foreach id [array names nchildren] {
654 set ncleft($id) $nchildren($id)
658 proc bindline {t id} {
661 $canv bind $t <Enter> "lineenter %x %y $id"
662 $canv bind $t <Motion> "linemotion %x %y $id"
663 $canv bind $t <Leave> "lineleave $id"
664 $canv bind $t <Button-1> "lineclick %x %y $id"
667 proc drawcommitline {level} {
668 global parents children nparents nchildren todo
669 global canv canv2 canv3 mainfont namefont canvx0 canvy linespc
670 global lineid linehtag linentag linedtag commitinfo
671 global colormap numcommits currentparents dupparents
672 global oldlevel oldnlines oldtodo
673 global idtags idline idheads
674 global lineno lthickness mainline sidelines
675 global commitlisted rowtextx idpos
679 set id [lindex $todo $level]
680 set lineid($lineno) $id
681 set idline($id) $lineno
682 set ofill [expr {[info exists commitlisted($id)]? "blue": "white"}]
683 if {![info exists commitinfo($id)]} {
685 if {![info exists commitinfo($id)]} {
686 set commitinfo($id) {"No commit information available"}
691 set currentparents {}
693 if {[info exists commitlisted($id)] && [info exists parents($id)]} {
694 foreach p $parents($id) {
695 if {[lsearch -exact $currentparents $p] < 0} {
696 lappend currentparents $p
698 # remember that this parent was listed twice
699 lappend dupparents $p
703 set x [expr $canvx0 + $level * $linespc]
705 set canvy [expr $canvy + $linespc]
706 allcanvs conf -scrollregion \
707 [list 0 0 0 [expr $y1 + 0.5 * $linespc + 2]]
708 if {[info exists mainline($id)]} {
709 lappend mainline($id) $x $y1
710 set t [$canv create line $mainline($id) \
711 -width $lthickness -fill $colormap($id)]
715 if {[info exists sidelines($id)]} {
716 foreach ls $sidelines($id) {
717 set coords [lindex $ls 0]
718 set thick [lindex $ls 1]
719 set t [$canv create line $coords -fill $colormap($id) \
720 -width [expr {$thick * $lthickness}]]
725 set orad [expr {$linespc / 3}]
726 set t [$canv create oval [expr $x - $orad] [expr $y1 - $orad] \
727 [expr $x + $orad - 1] [expr $y1 + $orad - 1] \
728 -fill $ofill -outline black -width 1]
730 $canv bind $t <1> {selcanvline {} %x %y}
731 set xt [expr $canvx0 + [llength $todo] * $linespc]
732 if {[llength $currentparents] > 2} {
733 set xt [expr {$xt + ([llength $currentparents] - 2) * $linespc}]
735 set rowtextx($lineno) $xt
736 set idpos($id) [list $x $xt $y1]
737 if {[info exists idtags($id)] || [info exists idheads($id)]} {
738 set xt [drawtags $id $x $xt $y1]
740 set headline [lindex $commitinfo($id) 0]
741 set name [lindex $commitinfo($id) 1]
742 set date [lindex $commitinfo($id) 2]
743 set linehtag($lineno) [$canv create text $xt $y1 -anchor w \
744 -text $headline -font $mainfont ]
745 $canv bind $linehtag($lineno) <Button-3> "rowmenu %X %Y $id"
746 set linentag($lineno) [$canv2 create text 3 $y1 -anchor w \
747 -text $name -font $namefont]
748 set linedtag($lineno) [$canv3 create text 3 $y1 -anchor w \
749 -text $date -font $mainfont]
752 proc drawtags {id x xt y1} {
753 global idtags idheads
754 global linespc lthickness
759 if {[info exists idtags($id)]} {
760 set marks $idtags($id)
761 set ntags [llength $marks]
763 if {[info exists idheads($id)]} {
764 set marks [concat $marks $idheads($id)]
770 set delta [expr {int(0.5 * ($linespc - $lthickness))}]
771 set yt [expr $y1 - 0.5 * $linespc]
772 set yb [expr $yt + $linespc - 1]
776 set wid [font measure $mainfont $tag]
779 set xt [expr {$xt + $delta + $wid + $lthickness + $linespc}]
781 set t [$canv create line $x $y1 [lindex $xvals end] $y1 \
782 -width $lthickness -fill black -tags tag.$id]
784 foreach tag $marks x $xvals wid $wvals {
785 set xl [expr $x + $delta]
786 set xr [expr $x + $delta + $wid + $lthickness]
787 if {[incr ntags -1] >= 0} {
789 $canv create polygon $x [expr $yt + $delta] $xl $yt\
790 $xr $yt $xr $yb $xl $yb $x [expr $yb - $delta] \
791 -width 1 -outline black -fill yellow -tags tag.$id
794 set xl [expr $xl - $delta/2]
795 $canv create polygon $x $yt $xr $yt $xr $yb $x $yb \
796 -width 1 -outline black -fill green -tags tag.$id
798 $canv create text $xl $y1 -anchor w -text $tag \
799 -font $mainfont -tags tag.$id
804 proc updatetodo {level noshortcut} {
805 global currentparents ncleft todo
806 global mainline oldlevel oldtodo oldnlines
807 global canvx0 canvy linespc mainline
812 set oldnlines [llength $todo]
813 if {!$noshortcut && [llength $currentparents] == 1} {
814 set p [lindex $currentparents 0]
815 if {$ncleft($p) == 1 && [lsearch -exact $todo $p] < 0} {
817 set x [expr $canvx0 + $level * $linespc]
818 set y [expr $canvy - $linespc]
819 set mainline($p) [list $x $y]
820 set todo [lreplace $todo $level $level $p]
825 set todo [lreplace $todo $level $level]
827 foreach p $currentparents {
829 set k [lsearch -exact $todo $p]
831 set todo [linsert $todo $i $p]
838 proc notecrossings {id lo hi corner} {
839 global oldtodo crossings cornercrossings
841 for {set i $lo} {[incr i] < $hi} {} {
842 set p [lindex $oldtodo $i]
843 if {$p == {}} continue
845 if {![info exists cornercrossings($id)]
846 || [lsearch -exact $cornercrossings($id) $p] < 0} {
847 lappend cornercrossings($id) $p
849 if {![info exists cornercrossings($p)]
850 || [lsearch -exact $cornercrossings($p) $id] < 0} {
851 lappend cornercrossings($p) $id
854 if {![info exists crossings($id)]
855 || [lsearch -exact $crossings($id) $p] < 0} {
856 lappend crossings($id) $p
858 if {![info exists crossings($p)]
859 || [lsearch -exact $crossings($p) $id] < 0} {
860 lappend crossings($p) $id
867 global canv mainline sidelines canvx0 canvy linespc
868 global oldlevel oldtodo todo currentparents dupparents
869 global lthickness linespc canvy colormap
871 set y1 [expr $canvy - $linespc]
874 foreach id $oldtodo {
876 if {$id == {}} continue
877 set xi [expr {$canvx0 + $i * $linespc}]
878 if {$i == $oldlevel} {
879 foreach p $currentparents {
880 set j [lsearch -exact $todo $p]
881 set coords [list $xi $y1]
882 set xj [expr {$canvx0 + $j * $linespc}]
884 lappend coords [expr $xj + $linespc] $y1
885 notecrossings $p $j $i [expr {$j + 1}]
886 } elseif {$j > $i + 1} {
887 lappend coords [expr $xj - $linespc] $y1
888 notecrossings $p $i $j [expr {$j - 1}]
890 if {[lsearch -exact $dupparents $p] >= 0} {
891 # draw a double-width line to indicate the doubled parent
892 lappend coords $xj $y2
893 lappend sidelines($p) [list $coords 2]
894 if {![info exists mainline($p)]} {
895 set mainline($p) [list $xj $y2]
898 # normal case, no parent duplicated
899 if {![info exists mainline($p)]} {
901 lappend coords $xj $y2
903 set mainline($p) $coords
905 lappend coords $xj $y2
906 lappend sidelines($p) [list $coords 1]
910 } elseif {[lindex $todo $i] != $id} {
911 set j [lsearch -exact $todo $id]
912 set xj [expr {$canvx0 + $j * $linespc}]
913 lappend mainline($id) $xi $y1 $xj $y2
918 proc decidenext {{noread 0}} {
919 global parents children nchildren ncleft todo
920 global canv canv2 canv3 mainfont namefont canvx0 canvy linespc
921 global datemode cdate
923 global currentparents oldlevel oldnlines oldtodo
924 global lineno lthickness
926 # remove the null entry if present
927 set nullentry [lsearch -exact $todo {}]
928 if {$nullentry >= 0} {
929 set todo [lreplace $todo $nullentry $nullentry]
932 # choose which one to do next time around
933 set todol [llength $todo]
936 for {set k $todol} {[incr k -1] >= 0} {} {
937 set p [lindex $todo $k]
938 if {$ncleft($p) == 0} {
940 if {![info exists commitinfo($p)]} {
946 if {$latest == {} || $cdate($p) > $latest} {
948 set latest $cdate($p)
958 puts "ERROR: none of the pending commits can be done yet:"
960 puts " $p ($ncleft($p))"
966 # If we are reducing, put in a null entry
967 if {$todol < $oldnlines} {
968 if {$nullentry >= 0} {
971 && [lindex $oldtodo $i] == [lindex $todo $i]} {
981 set todo [linsert $todo $i {}]
990 proc drawcommit {id} {
991 global phase todo nchildren datemode nextupdate
994 if {$phase != "incrdraw"} {
1000 updatetodo 0 $datemode
1002 if {$nchildren($id) == 0} {
1004 lappend startcommits $id
1006 set level [decidenext 1]
1007 if {$level == {} || $id != [lindex $todo $level]} {
1012 drawcommitline $level
1013 if {[updatetodo $level $datemode]} {
1014 set level [decidenext 1]
1015 if {$level == {}} break
1017 set id [lindex $todo $level]
1018 if {![info exists commitlisted($id)]} {
1021 if {[clock clicks -milliseconds] >= $nextupdate} {
1029 proc finishcommits {} {
1032 global canv mainfont ctext maincursor textcursor
1034 if {$phase != "incrdraw"} {
1036 $canv create text 3 3 -anchor nw -text "No commits selected" \
1037 -font $mainfont -tags textitems
1041 set level [decidenext]
1042 drawrest $level [llength $startcommits]
1044 . config -cursor $maincursor
1045 $ctext config -cursor $textcursor
1049 global nextupdate startmsecs startcommits todo
1051 if {$startcommits == {}} return
1052 set startmsecs [clock clicks -milliseconds]
1053 set nextupdate [expr $startmsecs + 100]
1055 set todo [lindex $startcommits 0]
1059 proc drawrest {level startix} {
1060 global phase stopped redisplaying selectedline
1061 global datemode currentparents todo
1063 global nextupdate startmsecs startcommits idline
1067 set startid [lindex $startcommits $startix]
1069 if {$startid != {}} {
1070 set startline $idline($startid)
1074 drawcommitline $level
1075 set hard [updatetodo $level $datemode]
1076 if {$numcommits == $startline} {
1077 lappend todo $startid
1080 set startid [lindex $startcommits $startix]
1082 if {$startid != {}} {
1083 set startline $idline($startid)
1087 set level [decidenext]
1088 if {$level < 0} break
1091 if {[clock clicks -milliseconds] >= $nextupdate} {
1098 set drawmsecs [expr [clock clicks -milliseconds] - $startmsecs]
1099 #puts "overall $drawmsecs ms for $numcommits commits"
1100 if {$redisplaying} {
1101 if {$stopped == 0 && [info exists selectedline]} {
1102 selectline $selectedline
1104 if {$stopped == 1} {
1106 after idle drawgraph
1113 proc findmatches {f} {
1114 global findtype foundstring foundstrlen
1115 if {$findtype == "Regexp"} {
1116 set matches [regexp -indices -all -inline $foundstring $f]
1118 if {$findtype == "IgnCase"} {
1119 set str [string tolower $f]
1125 while {[set j [string first $foundstring $str $i]] >= 0} {
1126 lappend matches [list $j [expr $j+$foundstrlen-1]]
1127 set i [expr $j + $foundstrlen]
1134 global findtype findloc findstring markedmatches commitinfo
1135 global numcommits lineid linehtag linentag linedtag
1136 global mainfont namefont canv canv2 canv3 selectedline
1137 global matchinglines foundstring foundstrlen
1140 set matchinglines {}
1141 set fldtypes {Headline Author Date Committer CDate Comment}
1142 if {$findtype == "IgnCase"} {
1143 set foundstring [string tolower $findstring]
1145 set foundstring $findstring
1147 set foundstrlen [string length $findstring]
1148 if {$foundstrlen == 0} return
1149 if {![info exists selectedline]} {
1152 set oldsel $selectedline
1155 for {set l 0} {$l < $numcommits} {incr l} {
1157 set info $commitinfo($id)
1159 foreach f $info ty $fldtypes {
1160 if {$findloc != "All fields" && $findloc != $ty} {
1163 set matches [findmatches $f]
1164 if {$matches == {}} continue
1166 if {$ty == "Headline"} {
1167 markmatches $canv $l $f $linehtag($l) $matches $mainfont
1168 } elseif {$ty == "Author"} {
1169 markmatches $canv2 $l $f $linentag($l) $matches $namefont
1170 } elseif {$ty == "Date"} {
1171 markmatches $canv3 $l $f $linedtag($l) $matches $mainfont
1175 lappend matchinglines $l
1176 if {!$didsel && $l > $oldsel} {
1182 if {$matchinglines == {}} {
1184 } elseif {!$didsel} {
1185 findselectline [lindex $matchinglines 0]
1189 proc findselectline {l} {
1190 global findloc commentend ctext
1192 if {$findloc == "All fields" || $findloc == "Comments"} {
1193 # highlight the matches in the comments
1194 set f [$ctext get 1.0 $commentend]
1195 set matches [findmatches $f]
1196 foreach match $matches {
1197 set start [lindex $match 0]
1198 set end [expr [lindex $match 1] + 1]
1199 $ctext tag add found "1.0 + $start c" "1.0 + $end c"
1205 global matchinglines selectedline
1206 if {![info exists matchinglines]} {
1210 if {![info exists selectedline]} return
1211 foreach l $matchinglines {
1212 if {$l > $selectedline} {
1221 global matchinglines selectedline
1222 if {![info exists matchinglines]} {
1226 if {![info exists selectedline]} return
1228 foreach l $matchinglines {
1229 if {$l >= $selectedline} break
1233 findselectline $prev
1239 proc markmatches {canv l str tag matches font} {
1240 set bbox [$canv bbox $tag]
1241 set x0 [lindex $bbox 0]
1242 set y0 [lindex $bbox 1]
1243 set y1 [lindex $bbox 3]
1244 foreach match $matches {
1245 set start [lindex $match 0]
1246 set end [lindex $match 1]
1247 if {$start > $end} continue
1248 set xoff [font measure $font [string range $str 0 [expr $start-1]]]
1249 set xlen [font measure $font [string range $str 0 [expr $end]]]
1250 set t [$canv create rect [expr $x0+$xoff] $y0 [expr $x0+$xlen+2] $y1 \
1251 -outline {} -tags matches -fill yellow]
1256 proc unmarkmatches {} {
1257 global matchinglines
1258 allcanvs delete matches
1259 catch {unset matchinglines}
1262 proc selcanvline {w x y} {
1263 global canv canvy0 ctext linespc selectedline
1264 global lineid linehtag linentag linedtag rowtextx
1265 set ymax [lindex [$canv cget -scrollregion] 3]
1266 if {$ymax == {}} return
1267 set yfrac [lindex [$canv yview] 0]
1268 set y [expr {$y + $yfrac * $ymax}]
1269 set l [expr {int(($y - $canvy0) / $linespc + 0.5)}]
1274 if {![info exists rowtextx($l)] || $x < $rowtextx($l)} return
1280 proc selectline {l} {
1281 global canv canv2 canv3 ctext commitinfo selectedline
1282 global lineid linehtag linentag linedtag
1283 global canvy0 linespc parents nparents
1284 global cflist currentid sha1entry diffids
1285 global commentend seenfile idtags
1287 if {![info exists lineid($l)] || ![info exists linehtag($l)]} return
1289 set t [eval $canv create rect [$canv bbox $linehtag($l)] -outline {{}} \
1290 -tags secsel -fill [$canv cget -selectbackground]]
1292 $canv2 delete secsel
1293 set t [eval $canv2 create rect [$canv2 bbox $linentag($l)] -outline {{}} \
1294 -tags secsel -fill [$canv2 cget -selectbackground]]
1296 $canv3 delete secsel
1297 set t [eval $canv3 create rect [$canv3 bbox $linedtag($l)] -outline {{}} \
1298 -tags secsel -fill [$canv3 cget -selectbackground]]
1300 set y [expr {$canvy0 + $l * $linespc}]
1301 set ymax [lindex [$canv cget -scrollregion] 3]
1302 set ytop [expr {$y - $linespc - 1}]
1303 set ybot [expr {$y + $linespc + 1}]
1304 set wnow [$canv yview]
1305 set wtop [expr [lindex $wnow 0] * $ymax]
1306 set wbot [expr [lindex $wnow 1] * $ymax]
1307 set wh [expr {$wbot - $wtop}]
1309 if {$ytop < $wtop} {
1310 if {$ybot < $wtop} {
1311 set newtop [expr {$y - $wh / 2.0}]
1314 if {$newtop > $wtop - $linespc} {
1315 set newtop [expr {$wtop - $linespc}]
1318 } elseif {$ybot > $wbot} {
1319 if {$ytop > $wbot} {
1320 set newtop [expr {$y - $wh / 2.0}]
1322 set newtop [expr {$ybot - $wh}]
1323 if {$newtop < $wtop + $linespc} {
1324 set newtop [expr {$wtop + $linespc}]
1328 if {$newtop != $wtop} {
1332 allcanvs yview moveto [expr $newtop * 1.0 / $ymax]
1338 set diffids [concat $id $parents($id)]
1339 $sha1entry delete 0 end
1340 $sha1entry insert 0 $id
1341 $sha1entry selection from 0
1342 $sha1entry selection to end
1344 $ctext conf -state normal
1345 $ctext delete 0.0 end
1346 $ctext mark set fmark.0 0.0
1347 $ctext mark gravity fmark.0 left
1348 set info $commitinfo($id)
1349 $ctext insert end "Author: [lindex $info 1] [lindex $info 2]\n"
1350 $ctext insert end "Committer: [lindex $info 3] [lindex $info 4]\n"
1351 if {[info exists idtags($id)]} {
1352 $ctext insert end "Tags:"
1353 foreach tag $idtags($id) {
1354 $ctext insert end " $tag"
1356 $ctext insert end "\n"
1358 $ctext insert end "\n"
1359 $ctext insert end [lindex $info 5]
1360 $ctext insert end "\n"
1361 $ctext tag delete Comments
1362 $ctext tag remove found 1.0 end
1363 $ctext conf -state disabled
1364 set commentend [$ctext index "end - 1c"]
1366 $cflist delete 0 end
1367 $cflist insert end "Comments"
1368 if {$nparents($id) == 1} {
1371 catch {unset seenfile}
1375 global treediffs diffids treepending
1377 if {![info exists treediffs($diffids)]} {
1378 if {![info exists treepending]} {
1379 gettreediffs $diffids
1382 addtocflist $diffids
1386 proc selnextline {dir} {
1388 if {![info exists selectedline]} return
1389 set l [expr $selectedline + $dir]
1394 proc addtocflist {ids} {
1395 global diffids treediffs cflist
1396 if {$ids != $diffids} {
1397 gettreediffs $diffids
1400 foreach f $treediffs($ids) {
1401 $cflist insert end $f
1406 proc gettreediffs {ids} {
1407 global treediffs parents treepending
1408 set treepending $ids
1409 set treediffs($ids) {}
1410 set id [lindex $ids 0]
1411 set p [lindex $ids 1]
1412 if [catch {set gdtf [open "|git-diff-tree -r $p $id" r]}] return
1413 fconfigure $gdtf -blocking 0
1414 fileevent $gdtf readable "gettreediffline $gdtf {$ids}"
1417 proc gettreediffline {gdtf ids} {
1418 global treediffs treepending
1419 set n [gets $gdtf line]
1421 if {![eof $gdtf]} return
1427 set file [lindex $line 5]
1428 lappend treediffs($ids) $file
1431 proc getblobdiffs {ids} {
1432 global diffopts blobdifffd env curdifftag curtagstart
1433 global diffindex difffilestart nextupdate
1435 set id [lindex $ids 0]
1436 set p [lindex $ids 1]
1437 set env(GIT_DIFF_OPTS) $diffopts
1438 if [catch {set bdf [open "|git-diff-tree -r -p $p $id" r]} err] {
1439 puts "error getting diffs: $err"
1442 fconfigure $bdf -blocking 0
1443 set blobdifffd($ids) $bdf
1444 set curdifftag Comments
1447 catch {unset difffilestart}
1448 fileevent $bdf readable "getblobdiffline $bdf {$ids}"
1449 set nextupdate [expr {[clock clicks -milliseconds] + 100}]
1452 proc getblobdiffline {bdf ids} {
1453 global diffids blobdifffd ctext curdifftag curtagstart seenfile
1454 global diffnexthead diffnextnote diffindex difffilestart
1457 set n [gets $bdf line]
1461 if {$ids == $diffids && $bdf == $blobdifffd($ids)} {
1462 $ctext tag add $curdifftag $curtagstart end
1463 set seenfile($curdifftag) 1
1468 if {$ids != $diffids || $bdf != $blobdifffd($ids)} {
1471 $ctext conf -state normal
1472 if {[regexp {^---[ \t]+([^/])*/(.*)} $line match s1 fname]} {
1473 # start of a new file
1474 $ctext insert end "\n"
1475 $ctext tag add $curdifftag $curtagstart end
1476 set seenfile($curdifftag) 1
1477 set curtagstart [$ctext index "end - 1c"]
1479 if {[info exists diffnexthead]} {
1480 set fname $diffnexthead
1481 set header "$diffnexthead ($diffnextnote)"
1484 set here [$ctext index "end - 1c"]
1485 set difffilestart($diffindex) $here
1487 # start mark names at fmark.1 for first file
1488 $ctext mark set fmark.$diffindex $here
1489 $ctext mark gravity fmark.$diffindex left
1490 set curdifftag "f:$fname"
1491 $ctext tag delete $curdifftag
1492 set l [expr {(78 - [string length $header]) / 2}]
1493 set pad [string range "----------------------------------------" 1 $l]
1494 $ctext insert end "$pad $header $pad\n" filesep
1495 } elseif {[string range $line 0 2] == "+++"} {
1496 # no need to do anything with this
1497 } elseif {[regexp {^Created: (.*) \((mode: *[0-7]*)\)} $line match fn m]} {
1498 set diffnexthead $fn
1499 set diffnextnote "created, mode $m"
1500 } elseif {[string range $line 0 8] == "Deleted: "} {
1501 set diffnexthead [string range $line 9 end]
1502 set diffnextnote "deleted"
1503 } elseif {[regexp {^diff --git a/(.*) b/} $line match fn]} {
1504 # save the filename in case the next thing is "new file mode ..."
1505 set diffnexthead $fn
1506 set diffnextnote "modified"
1507 } elseif {[regexp {^new file mode ([0-7]+)} $line match m]} {
1508 set diffnextnote "new file, mode $m"
1509 } elseif {[string range $line 0 11] == "deleted file"} {
1510 set diffnextnote "deleted"
1511 } elseif {[regexp {^@@ -([0-9]+),([0-9]+) \+([0-9]+),([0-9]+) @@(.*)} \
1512 $line match f1l f1c f2l f2c rest]} {
1513 $ctext insert end "\t" hunksep
1514 $ctext insert end " $f1l " d0 " $f2l " d1
1515 $ctext insert end " $rest \n" hunksep
1517 set x [string range $line 0 0]
1518 if {$x == "-" || $x == "+"} {
1519 set tag [expr {$x == "+"}]
1520 set line [string range $line 1 end]
1521 $ctext insert end "$line\n" d$tag
1522 } elseif {$x == " "} {
1523 set line [string range $line 1 end]
1524 $ctext insert end "$line\n"
1525 } elseif {$x == "\\"} {
1526 # e.g. "\ No newline at end of file"
1527 $ctext insert end "$line\n" filesep
1529 # Something else we don't recognize
1530 if {$curdifftag != "Comments"} {
1531 $ctext insert end "\n"
1532 $ctext tag add $curdifftag $curtagstart end
1533 set seenfile($curdifftag) 1
1534 set curtagstart [$ctext index "end - 1c"]
1535 set curdifftag Comments
1537 $ctext insert end "$line\n" filesep
1540 $ctext conf -state disabled
1541 if {[clock clicks -milliseconds] >= $nextupdate} {
1543 fileevent $bdf readable {}
1545 fileevent $bdf readable "getblobdiffline $bdf {$ids}"
1550 global difffilestart ctext
1551 set here [$ctext index @0,0]
1552 for {set i 0} {[info exists difffilestart($i)]} {incr i} {
1553 if {[$ctext compare $difffilestart($i) > $here]} {
1554 $ctext yview $difffilestart($i)
1560 proc listboxsel {} {
1561 global ctext cflist currentid treediffs seenfile
1562 if {![info exists currentid]} return
1563 set sel [lsort [$cflist curselection]]
1564 if {$sel eq {}} return
1565 set first [lindex $sel 0]
1566 catch {$ctext yview fmark.$first}
1570 global linespc charspc canvx0 canvy0 mainfont
1571 set linespc [font metrics $mainfont -linespace]
1572 set charspc [font measure $mainfont "m"]
1573 set canvy0 [expr 3 + 0.5 * $linespc]
1574 set canvx0 [expr 3 + 0.5 * $linespc]
1578 global selectedline stopped redisplaying phase
1579 if {$stopped > 1} return
1580 if {$phase == "getcommits"} return
1582 if {$phase == "drawgraph" || $phase == "incrdraw"} {
1589 proc incrfont {inc} {
1590 global mainfont namefont textfont selectedline ctext canv phase
1591 global stopped entries
1593 set mainfont [lreplace $mainfont 1 1 [expr {[lindex $mainfont 1] + $inc}]]
1594 set namefont [lreplace $namefont 1 1 [expr {[lindex $namefont 1] + $inc}]]
1595 set textfont [lreplace $textfont 1 1 [expr {[lindex $textfont 1] + $inc}]]
1597 $ctext conf -font $textfont
1598 $ctext tag conf filesep -font [concat $textfont bold]
1599 foreach e $entries {
1600 $e conf -font $mainfont
1602 if {$phase == "getcommits"} {
1603 $canv itemconf textitems -font $mainfont
1609 global sha1entry sha1string
1610 if {[string length $sha1string] == 40} {
1611 $sha1entry delete 0 end
1615 proc sha1change {n1 n2 op} {
1616 global sha1string currentid sha1but
1617 if {$sha1string == {}
1618 || ([info exists currentid] && $sha1string == $currentid)} {
1623 if {[$sha1but cget -state] == $state} return
1624 if {$state == "normal"} {
1625 $sha1but conf -state normal -relief raised -text "Goto: "
1627 $sha1but conf -state disabled -relief flat -text "SHA1 ID: "
1631 proc gotocommit {} {
1632 global sha1string currentid idline tagids
1633 if {$sha1string == {}
1634 || ([info exists currentid] && $sha1string == $currentid)} return
1635 if {[info exists tagids($sha1string)]} {
1636 set id $tagids($sha1string)
1638 set id [string tolower $sha1string]
1640 if {[info exists idline($id)]} {
1641 selectline $idline($id)
1644 if {[regexp {^[0-9a-fA-F]{40}$} $sha1string]} {
1649 error_popup "$type $sha1string is not known"
1652 proc lineenter {x y id} {
1653 global hoverx hovery hoverid hovertimer
1654 global commitinfo canv
1656 if {![info exists commitinfo($id)]} return
1660 if {[info exists hovertimer]} {
1661 after cancel $hovertimer
1663 set hovertimer [after 500 linehover]
1667 proc linemotion {x y id} {
1668 global hoverx hovery hoverid hovertimer
1670 if {[info exists hoverid] && $id == $hoverid} {
1673 if {[info exists hovertimer]} {
1674 after cancel $hovertimer
1676 set hovertimer [after 500 linehover]
1680 proc lineleave {id} {
1681 global hoverid hovertimer canv
1683 if {[info exists hoverid] && $id == $hoverid} {
1685 if {[info exists hovertimer]} {
1686 after cancel $hovertimer
1694 global hoverx hovery hoverid hovertimer
1695 global canv linespc lthickness
1696 global commitinfo mainfont
1698 set text [lindex $commitinfo($hoverid) 0]
1699 set ymax [lindex [$canv cget -scrollregion] 3]
1700 if {$ymax == {}} return
1701 set yfrac [lindex [$canv yview] 0]
1702 set x [expr {$hoverx + 2 * $linespc}]
1703 set y [expr {$hovery + $yfrac * $ymax - $linespc / 2}]
1704 set x0 [expr {$x - 2 * $lthickness}]
1705 set y0 [expr {$y - 2 * $lthickness}]
1706 set x1 [expr {$x + [font measure $mainfont $text] + 2 * $lthickness}]
1707 set y1 [expr {$y + $linespc + 2 * $lthickness}]
1708 set t [$canv create rectangle $x0 $y0 $x1 $y1 \
1709 -fill \#ffff80 -outline black -width 1 -tags hover]
1711 set t [$canv create text $x $y -anchor nw -text $text -tags hover]
1715 proc lineclick {x y id} {
1716 global ctext commitinfo children cflist canv
1720 # fill the details pane with info about this line
1721 $ctext conf -state normal
1722 $ctext delete 0.0 end
1723 $ctext insert end "Parent:\n "
1724 catch {destroy $ctext.$id}
1725 button $ctext.$id -text "Go:" -command "selbyid $id" \
1727 $ctext window create end -window $ctext.$id -align center
1728 set info $commitinfo($id)
1729 $ctext insert end "\t[lindex $info 0]\n"
1730 $ctext insert end "\tAuthor:\t[lindex $info 1]\n"
1731 $ctext insert end "\tDate:\t[lindex $info 2]\n"
1732 $ctext insert end "\tID:\t$id\n"
1733 if {[info exists children($id)]} {
1734 $ctext insert end "\nChildren:"
1735 foreach child $children($id) {
1736 $ctext insert end "\n "
1737 catch {destroy $ctext.$child}
1738 button $ctext.$child -text "Go:" -command "selbyid $child" \
1740 $ctext window create end -window $ctext.$child -align center
1741 set info $commitinfo($child)
1742 $ctext insert end "\t[lindex $info 0]"
1745 $ctext conf -state disabled
1747 $cflist delete 0 end
1752 if {[info exists idline($id)]} {
1753 selectline $idline($id)
1759 if {![info exists startmstime]} {
1760 set startmstime [clock clicks -milliseconds]
1762 return [format "%.3f" [expr {([clock click -milliseconds] - $startmstime) / 1000.0}]]
1765 proc rowmenu {x y id} {
1766 global rowctxmenu idline selectedline rowmenuid
1768 if {![info exists selectedline] || $idline($id) eq $selectedline} {
1773 $rowctxmenu entryconfigure 0 -state $state
1774 $rowctxmenu entryconfigure 1 -state $state
1775 $rowctxmenu entryconfigure 2 -state $state
1777 tk_popup $rowctxmenu $x $y
1780 proc diffvssel {dirn} {
1781 global rowmenuid selectedline lineid
1783 global diffids commitinfo
1785 if {![info exists selectedline]} return
1787 set oldid $lineid($selectedline)
1788 set newid $rowmenuid
1790 set oldid $rowmenuid
1791 set newid $lineid($selectedline)
1793 $ctext conf -state normal
1794 $ctext delete 0.0 end
1795 $ctext mark set fmark.0 0.0
1796 $ctext mark gravity fmark.0 left
1797 $cflist delete 0 end
1798 $cflist insert end "Top"
1799 $ctext insert end "From $oldid\n "
1800 $ctext insert end [lindex $commitinfo($oldid) 0]
1801 $ctext insert end "\n\nTo $newid\n "
1802 $ctext insert end [lindex $commitinfo($newid) 0]
1803 $ctext insert end "\n"
1804 $ctext conf -state disabled
1805 $ctext tag delete Comments
1806 $ctext tag remove found 1.0 end
1807 set diffids [list $newid $oldid]
1812 global rowmenuid currentid commitinfo patchtop patchnum
1814 if {![info exists currentid]} return
1815 set oldid $currentid
1816 set oldhead [lindex $commitinfo($oldid) 0]
1817 set newid $rowmenuid
1818 set newhead [lindex $commitinfo($newid) 0]
1821 catch {destroy $top}
1823 label $top.title -text "Generate patch"
1825 label $top.from -text "From:"
1826 entry $top.fromsha1 -width 40
1827 $top.fromsha1 insert 0 $oldid
1828 $top.fromsha1 conf -state readonly
1829 grid $top.from $top.fromsha1 -sticky w
1830 entry $top.fromhead -width 60
1831 $top.fromhead insert 0 $oldhead
1832 $top.fromhead conf -state readonly
1833 grid x $top.fromhead -sticky w
1834 label $top.to -text "To:"
1835 entry $top.tosha1 -width 40
1836 $top.tosha1 insert 0 $newid
1837 $top.tosha1 conf -state readonly
1838 grid $top.to $top.tosha1 -sticky w
1839 entry $top.tohead -width 60
1840 $top.tohead insert 0 $newhead
1841 $top.tohead conf -state readonly
1842 grid x $top.tohead -sticky w
1843 button $top.rev -text "Reverse" -command mkpatchrev -padx 5
1844 grid $top.rev x -pady 10
1845 label $top.flab -text "Output file:"
1846 entry $top.fname -width 60
1847 $top.fname insert 0 [file normalize "patch$patchnum.patch"]
1849 grid $top.flab $top.fname -sticky w
1851 button $top.buts.gen -text "Generate" -command mkpatchgo
1852 button $top.buts.can -text "Cancel" -command mkpatchcan
1853 grid $top.buts.gen $top.buts.can
1854 grid columnconfigure $top.buts 0 -weight 1 -uniform a
1855 grid columnconfigure $top.buts 1 -weight 1 -uniform a
1856 grid $top.buts - -pady 10 -sticky ew
1860 proc mkpatchrev {} {
1863 set oldid [$patchtop.fromsha1 get]
1864 set oldhead [$patchtop.fromhead get]
1865 set newid [$patchtop.tosha1 get]
1866 set newhead [$patchtop.tohead get]
1867 foreach e [list fromsha1 fromhead tosha1 tohead] \
1868 v [list $newid $newhead $oldid $oldhead] {
1869 $patchtop.$e conf -state normal
1870 $patchtop.$e delete 0 end
1871 $patchtop.$e insert 0 $v
1872 $patchtop.$e conf -state readonly
1879 set oldid [$patchtop.fromsha1 get]
1880 set newid [$patchtop.tosha1 get]
1881 set fname [$patchtop.fname get]
1882 if {[catch {exec git-diff-tree -p $oldid $newid >$fname &} err]} {
1883 error_popup "Error creating patch: $err"
1885 catch {destroy $patchtop}
1889 proc mkpatchcan {} {
1892 catch {destroy $patchtop}
1897 global rowmenuid mktagtop commitinfo
1901 catch {destroy $top}
1903 label $top.title -text "Create tag"
1905 label $top.id -text "ID:"
1906 entry $top.sha1 -width 40
1907 $top.sha1 insert 0 $rowmenuid
1908 $top.sha1 conf -state readonly
1909 grid $top.id $top.sha1 -sticky w
1910 entry $top.head -width 40
1911 $top.head insert 0 [lindex $commitinfo($rowmenuid) 0]
1912 $top.head conf -state readonly
1913 grid x $top.head -sticky w
1914 label $top.tlab -text "Tag name:"
1915 entry $top.tag -width 40
1916 grid $top.tlab $top.tag -sticky w
1918 button $top.buts.gen -text "Create" -command mktaggo
1919 button $top.buts.can -text "Cancel" -command mktagcan
1920 grid $top.buts.gen $top.buts.can
1921 grid columnconfigure $top.buts 0 -weight 1 -uniform a
1922 grid columnconfigure $top.buts 1 -weight 1 -uniform a
1923 grid $top.buts - -pady 10 -sticky ew
1928 global mktagtop env tagids idtags
1929 global idpos idline linehtag canv selectedline
1931 set id [$mktagtop.sha1 get]
1932 set tag [$mktagtop.tag get]
1934 error_popup "No tag name specified"
1937 if {[info exists tagids($tag)]} {
1938 error_popup "Tag \"$tag\" already exists"
1943 if {[info exists env(GIT_DIR)]} {
1944 set dir $env(GIT_DIR)
1946 set fname [file join $dir "refs/tags" $tag]
1947 set f [open $fname w]
1951 error_popup "Error creating tag: $err"
1955 set tagids($tag) $id
1956 lappend idtags($id) $tag
1957 $canv delete tag.$id
1958 set xt [eval drawtags $id $idpos($id)]
1959 $canv coords $linehtag($idline($id)) $xt [lindex $idpos($id) 2]
1960 if {[info exists selectedline] && $selectedline == $idline($id)} {
1961 selectline $selectedline
1968 catch {destroy $mktagtop}
1986 set diffopts "-U 5 -p"
1988 set mainfont {Helvetica 9}
1989 set textfont {Courier 9}
1991 set colors {green red blue magenta darkgrey brown orange}
1993 catch {source ~/.gitk}
1995 set namefont $mainfont
1997 lappend namefont bold
2002 switch -regexp -- $arg {
2004 "^-b" { set boldnames 1 }
2005 "^-d" { set datemode 1 }
2007 lappend revtreeargs $arg
2019 getcommits $revtreeargs