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
430 # when we make a key binding for the toplevel, make sure
431 # it doesn't get triggered when that key is pressed in the
432 # find string entry widget.
433 proc bindkey {ev script} {
436 set escript [bind Entry $ev]
437 if {$escript == {}} {
438 set escript [bind Entry <Key>]
441 bind $e $ev "$escript; break"
445 # set the focus back to the toplevel for any click outside
456 global canv canv2 canv3 ctext cflist mainfont textfont
458 if {$stuffsaved} return
459 if {![winfo viewable .]} return
461 set f [open "~/.gitk-new" w]
462 puts $f "set mainfont {$mainfont}"
463 puts $f "set textfont {$textfont}"
464 puts $f "set geometry(width) [winfo width .ctop]"
465 puts $f "set geometry(height) [winfo height .ctop]"
466 puts $f "set geometry(canv1) [expr [winfo width $canv]-2]"
467 puts $f "set geometry(canv2) [expr [winfo width $canv2]-2]"
468 puts $f "set geometry(canv3) [expr [winfo width $canv3]-2]"
469 puts $f "set geometry(canvh) [expr [winfo height $canv]-2]"
470 set wid [expr {([winfo width $ctext] - 8) \
471 / [font measure $textfont "0"]}]
472 puts $f "set geometry(ctextw) $wid"
473 set wid [expr {([winfo width $cflist] - 11) \
474 / [font measure [$cflist cget -font] "0"]}]
475 puts $f "set geometry(cflistw) $wid"
477 file rename -force "~/.gitk-new" "~/.gitk"
482 proc resizeclistpanes {win w} {
484 if [info exists oldwidth($win)] {
485 set s0 [$win sash coord 0]
486 set s1 [$win sash coord 1]
488 set sash0 [expr {int($w/2 - 2)}]
489 set sash1 [expr {int($w*5/6 - 2)}]
491 set factor [expr {1.0 * $w / $oldwidth($win)}]
492 set sash0 [expr {int($factor * [lindex $s0 0])}]
493 set sash1 [expr {int($factor * [lindex $s1 0])}]
497 if {$sash1 < $sash0 + 20} {
498 set sash1 [expr $sash0 + 20]
500 if {$sash1 > $w - 10} {
501 set sash1 [expr $w - 10]
502 if {$sash0 > $sash1 - 20} {
503 set sash0 [expr $sash1 - 20]
507 $win sash place 0 $sash0 [lindex $s0 1]
508 $win sash place 1 $sash1 [lindex $s1 1]
510 set oldwidth($win) $w
513 proc resizecdetpanes {win w} {
515 if [info exists oldwidth($win)] {
516 set s0 [$win sash coord 0]
518 set sash0 [expr {int($w*3/4 - 2)}]
520 set factor [expr {1.0 * $w / $oldwidth($win)}]
521 set sash0 [expr {int($factor * [lindex $s0 0])}]
525 if {$sash0 > $w - 15} {
526 set sash0 [expr $w - 15]
529 $win sash place 0 $sash0 [lindex $s0 1]
531 set oldwidth($win) $w
535 global canv canv2 canv3
541 proc bindall {event action} {
542 global canv canv2 canv3
543 bind $canv $event $action
544 bind $canv2 $event $action
545 bind $canv3 $event $action
550 if {[winfo exists $w]} {
555 wm title $w "About gitk"
559 Copyright © 2005 Paul Mackerras
561 Use and redistribute under the terms of the GNU General Public License} \
562 -justify center -aspect 400
563 pack $w.m -side top -fill x -padx 20 -pady 20
564 button $w.ok -text Close -command "destroy $w"
565 pack $w.ok -side bottom
568 proc assigncolor {id} {
569 global commitinfo colormap commcolors colors nextcolor
570 global parents nparents children nchildren
571 global cornercrossings crossings
573 if [info exists colormap($id)] return
574 set ncolors [llength $colors]
575 if {$nparents($id) <= 1 && $nchildren($id) == 1} {
576 set child [lindex $children($id) 0]
577 if {[info exists colormap($child)]
578 && $nparents($child) == 1} {
579 set colormap($id) $colormap($child)
584 if {[info exists cornercrossings($id)]} {
585 foreach x $cornercrossings($id) {
586 if {[info exists colormap($x)]
587 && [lsearch -exact $badcolors $colormap($x)] < 0} {
588 lappend badcolors $colormap($x)
591 if {[llength $badcolors] >= $ncolors} {
595 set origbad $badcolors
596 if {[llength $badcolors] < $ncolors - 1} {
597 if {[info exists crossings($id)]} {
598 foreach x $crossings($id) {
599 if {[info exists colormap($x)]
600 && [lsearch -exact $badcolors $colormap($x)] < 0} {
601 lappend badcolors $colormap($x)
604 if {[llength $badcolors] >= $ncolors} {
605 set badcolors $origbad
608 set origbad $badcolors
610 if {[llength $badcolors] < $ncolors - 1} {
611 foreach child $children($id) {
612 if {[info exists colormap($child)]
613 && [lsearch -exact $badcolors $colormap($child)] < 0} {
614 lappend badcolors $colormap($child)
616 if {[info exists parents($child)]} {
617 foreach p $parents($child) {
618 if {[info exists colormap($p)]
619 && [lsearch -exact $badcolors $colormap($p)] < 0} {
620 lappend badcolors $colormap($p)
625 if {[llength $badcolors] >= $ncolors} {
626 set badcolors $origbad
629 for {set i 0} {$i <= $ncolors} {incr i} {
630 set c [lindex $colors $nextcolor]
631 if {[incr nextcolor] >= $ncolors} {
634 if {[lsearch -exact $badcolors $c]} break
640 global canvy canvy0 lineno numcommits lthickness nextcolor linespc
641 global mainline sidelines
642 global nchildren ncleft
649 set lthickness [expr {int($linespc / 9) + 1}]
650 catch {unset mainline}
651 catch {unset sidelines}
652 foreach id [array names nchildren] {
653 set ncleft($id) $nchildren($id)
657 proc bindline {t id} {
660 $canv bind $t <Enter> "lineenter %x %y $id"
661 $canv bind $t <Motion> "linemotion %x %y $id"
662 $canv bind $t <Leave> "lineleave $id"
663 $canv bind $t <Button-1> "lineclick %x %y $id"
666 proc drawcommitline {level} {
667 global parents children nparents nchildren todo
668 global canv canv2 canv3 mainfont namefont canvx0 canvy linespc
669 global lineid linehtag linentag linedtag commitinfo
670 global colormap numcommits currentparents dupparents
671 global oldlevel oldnlines oldtodo
672 global idtags idline idheads
673 global lineno lthickness mainline sidelines
674 global commitlisted rowtextx
678 set id [lindex $todo $level]
679 set lineid($lineno) $id
680 set idline($id) $lineno
681 set ofill [expr {[info exists commitlisted($id)]? "blue": "white"}]
682 if {![info exists commitinfo($id)]} {
684 if {![info exists commitinfo($id)]} {
685 set commitinfo($id) {"No commit information available"}
690 set currentparents {}
692 if {[info exists commitlisted($id)] && [info exists parents($id)]} {
693 foreach p $parents($id) {
694 if {[lsearch -exact $currentparents $p] < 0} {
695 lappend currentparents $p
697 # remember that this parent was listed twice
698 lappend dupparents $p
702 set x [expr $canvx0 + $level * $linespc]
704 set canvy [expr $canvy + $linespc]
705 allcanvs conf -scrollregion \
706 [list 0 0 0 [expr $y1 + 0.5 * $linespc + 2]]
707 if {[info exists mainline($id)]} {
708 lappend mainline($id) $x $y1
709 set t [$canv create line $mainline($id) \
710 -width $lthickness -fill $colormap($id)]
714 if {[info exists sidelines($id)]} {
715 foreach ls $sidelines($id) {
716 set coords [lindex $ls 0]
717 set thick [lindex $ls 1]
718 set t [$canv create line $coords -fill $colormap($id) \
719 -width [expr {$thick * $lthickness}]]
724 set orad [expr {$linespc / 3}]
725 set t [$canv create oval [expr $x - $orad] [expr $y1 - $orad] \
726 [expr $x + $orad - 1] [expr $y1 + $orad - 1] \
727 -fill $ofill -outline black -width 1]
729 $canv bind $t <1> {selcanvline {} %x %y}
730 set xt [expr $canvx0 + [llength $todo] * $linespc]
731 if {[llength $currentparents] > 2} {
732 set xt [expr {$xt + ([llength $currentparents] - 2) * $linespc}]
734 set rowtextx($lineno) $xt
737 if {[info exists idtags($id)]} {
738 set marks $idtags($id)
739 set ntags [llength $marks]
741 if {[info exists idheads($id)]} {
742 set marks [concat $marks $idheads($id)]
745 set delta [expr {int(0.5 * ($linespc - $lthickness))}]
746 set yt [expr $y1 - 0.5 * $linespc]
747 set yb [expr $yt + $linespc - 1]
751 set wid [font measure $mainfont $tag]
754 set xt [expr {$xt + $delta + $wid + $lthickness + $linespc}]
756 set t [$canv create line $x $y1 [lindex $xvals end] $y1 \
757 -width $lthickness -fill black]
759 foreach tag $marks x $xvals wid $wvals {
760 set xl [expr $x + $delta]
761 set xr [expr $x + $delta + $wid + $lthickness]
762 if {[incr ntags -1] >= 0} {
764 $canv create polygon $x [expr $yt + $delta] $xl $yt\
765 $xr $yt $xr $yb $xl $yb $x [expr $yb - $delta] \
766 -width 1 -outline black -fill yellow
769 set xl [expr $xl - $delta/2]
770 $canv create polygon $x $yt $xr $yt $xr $yb $x $yb \
771 -width 1 -outline black -fill green
773 $canv create text $xl $y1 -anchor w -text $tag \
777 set headline [lindex $commitinfo($id) 0]
778 set name [lindex $commitinfo($id) 1]
779 set date [lindex $commitinfo($id) 2]
780 set linehtag($lineno) [$canv create text $xt $y1 -anchor w \
781 -text $headline -font $mainfont ]
782 $canv bind $linehtag($lineno) <Button-3> "rowmenu %X %Y $id"
783 set linentag($lineno) [$canv2 create text 3 $y1 -anchor w \
784 -text $name -font $namefont]
785 set linedtag($lineno) [$canv3 create text 3 $y1 -anchor w \
786 -text $date -font $mainfont]
789 proc updatetodo {level noshortcut} {
790 global currentparents ncleft todo
791 global mainline oldlevel oldtodo oldnlines
792 global canvx0 canvy linespc mainline
797 set oldnlines [llength $todo]
798 if {!$noshortcut && [llength $currentparents] == 1} {
799 set p [lindex $currentparents 0]
800 if {$ncleft($p) == 1 && [lsearch -exact $todo $p] < 0} {
802 set x [expr $canvx0 + $level * $linespc]
803 set y [expr $canvy - $linespc]
804 set mainline($p) [list $x $y]
805 set todo [lreplace $todo $level $level $p]
810 set todo [lreplace $todo $level $level]
812 foreach p $currentparents {
814 set k [lsearch -exact $todo $p]
816 set todo [linsert $todo $i $p]
823 proc notecrossings {id lo hi corner} {
824 global oldtodo crossings cornercrossings
826 for {set i $lo} {[incr i] < $hi} {} {
827 set p [lindex $oldtodo $i]
828 if {$p == {}} continue
830 if {![info exists cornercrossings($id)]
831 || [lsearch -exact $cornercrossings($id) $p] < 0} {
832 lappend cornercrossings($id) $p
834 if {![info exists cornercrossings($p)]
835 || [lsearch -exact $cornercrossings($p) $id] < 0} {
836 lappend cornercrossings($p) $id
839 if {![info exists crossings($id)]
840 || [lsearch -exact $crossings($id) $p] < 0} {
841 lappend crossings($id) $p
843 if {![info exists crossings($p)]
844 || [lsearch -exact $crossings($p) $id] < 0} {
845 lappend crossings($p) $id
852 global canv mainline sidelines canvx0 canvy linespc
853 global oldlevel oldtodo todo currentparents dupparents
854 global lthickness linespc canvy colormap
856 set y1 [expr $canvy - $linespc]
859 foreach id $oldtodo {
861 if {$id == {}} continue
862 set xi [expr {$canvx0 + $i * $linespc}]
863 if {$i == $oldlevel} {
864 foreach p $currentparents {
865 set j [lsearch -exact $todo $p]
866 set coords [list $xi $y1]
867 set xj [expr {$canvx0 + $j * $linespc}]
869 lappend coords [expr $xj + $linespc] $y1
870 notecrossings $p $j $i [expr {$j + 1}]
871 } elseif {$j > $i + 1} {
872 lappend coords [expr $xj - $linespc] $y1
873 notecrossings $p $i $j [expr {$j - 1}]
875 if {[lsearch -exact $dupparents $p] >= 0} {
876 # draw a double-width line to indicate the doubled parent
877 lappend coords $xj $y2
878 lappend sidelines($p) [list $coords 2]
879 if {![info exists mainline($p)]} {
880 set mainline($p) [list $xj $y2]
883 # normal case, no parent duplicated
884 if {![info exists mainline($p)]} {
886 lappend coords $xj $y2
888 set mainline($p) $coords
890 lappend coords $xj $y2
891 lappend sidelines($p) [list $coords 1]
895 } elseif {[lindex $todo $i] != $id} {
896 set j [lsearch -exact $todo $id]
897 set xj [expr {$canvx0 + $j * $linespc}]
898 lappend mainline($id) $xi $y1 $xj $y2
903 proc decidenext {{noread 0}} {
904 global parents children nchildren ncleft todo
905 global canv canv2 canv3 mainfont namefont canvx0 canvy linespc
906 global datemode cdate
908 global currentparents oldlevel oldnlines oldtodo
909 global lineno lthickness
911 # remove the null entry if present
912 set nullentry [lsearch -exact $todo {}]
913 if {$nullentry >= 0} {
914 set todo [lreplace $todo $nullentry $nullentry]
917 # choose which one to do next time around
918 set todol [llength $todo]
921 for {set k $todol} {[incr k -1] >= 0} {} {
922 set p [lindex $todo $k]
923 if {$ncleft($p) == 0} {
925 if {![info exists commitinfo($p)]} {
931 if {$latest == {} || $cdate($p) > $latest} {
933 set latest $cdate($p)
943 puts "ERROR: none of the pending commits can be done yet:"
945 puts " $p ($ncleft($p))"
951 # If we are reducing, put in a null entry
952 if {$todol < $oldnlines} {
953 if {$nullentry >= 0} {
956 && [lindex $oldtodo $i] == [lindex $todo $i]} {
966 set todo [linsert $todo $i {}]
975 proc drawcommit {id} {
976 global phase todo nchildren datemode nextupdate
979 if {$phase != "incrdraw"} {
985 updatetodo 0 $datemode
987 if {$nchildren($id) == 0} {
989 lappend startcommits $id
991 set level [decidenext 1]
992 if {$level == {} || $id != [lindex $todo $level]} {
997 drawcommitline $level
998 if {[updatetodo $level $datemode]} {
999 set level [decidenext 1]
1000 if {$level == {}} break
1002 set id [lindex $todo $level]
1003 if {![info exists commitlisted($id)]} {
1006 if {[clock clicks -milliseconds] >= $nextupdate} {
1014 proc finishcommits {} {
1017 global canv mainfont ctext maincursor textcursor
1019 if {$phase != "incrdraw"} {
1021 $canv create text 3 3 -anchor nw -text "No commits selected" \
1022 -font $mainfont -tags textitems
1026 set level [decidenext]
1027 drawrest $level [llength $startcommits]
1029 . config -cursor $maincursor
1030 $ctext config -cursor $textcursor
1034 global nextupdate startmsecs startcommits todo
1036 if {$startcommits == {}} return
1037 set startmsecs [clock clicks -milliseconds]
1038 set nextupdate [expr $startmsecs + 100]
1040 set todo [lindex $startcommits 0]
1044 proc drawrest {level startix} {
1045 global phase stopped redisplaying selectedline
1046 global datemode currentparents todo
1048 global nextupdate startmsecs startcommits idline
1052 set startid [lindex $startcommits $startix]
1054 if {$startid != {}} {
1055 set startline $idline($startid)
1059 drawcommitline $level
1060 set hard [updatetodo $level $datemode]
1061 if {$numcommits == $startline} {
1062 lappend todo $startid
1065 set startid [lindex $startcommits $startix]
1067 if {$startid != {}} {
1068 set startline $idline($startid)
1072 set level [decidenext]
1073 if {$level < 0} break
1076 if {[clock clicks -milliseconds] >= $nextupdate} {
1083 set drawmsecs [expr [clock clicks -milliseconds] - $startmsecs]
1084 #puts "overall $drawmsecs ms for $numcommits commits"
1085 if {$redisplaying} {
1086 if {$stopped == 0 && [info exists selectedline]} {
1087 selectline $selectedline
1089 if {$stopped == 1} {
1091 after idle drawgraph
1098 proc findmatches {f} {
1099 global findtype foundstring foundstrlen
1100 if {$findtype == "Regexp"} {
1101 set matches [regexp -indices -all -inline $foundstring $f]
1103 if {$findtype == "IgnCase"} {
1104 set str [string tolower $f]
1110 while {[set j [string first $foundstring $str $i]] >= 0} {
1111 lappend matches [list $j [expr $j+$foundstrlen-1]]
1112 set i [expr $j + $foundstrlen]
1119 global findtype findloc findstring markedmatches commitinfo
1120 global numcommits lineid linehtag linentag linedtag
1121 global mainfont namefont canv canv2 canv3 selectedline
1122 global matchinglines foundstring foundstrlen
1125 set matchinglines {}
1126 set fldtypes {Headline Author Date Committer CDate Comment}
1127 if {$findtype == "IgnCase"} {
1128 set foundstring [string tolower $findstring]
1130 set foundstring $findstring
1132 set foundstrlen [string length $findstring]
1133 if {$foundstrlen == 0} return
1134 if {![info exists selectedline]} {
1137 set oldsel $selectedline
1140 for {set l 0} {$l < $numcommits} {incr l} {
1142 set info $commitinfo($id)
1144 foreach f $info ty $fldtypes {
1145 if {$findloc != "All fields" && $findloc != $ty} {
1148 set matches [findmatches $f]
1149 if {$matches == {}} continue
1151 if {$ty == "Headline"} {
1152 markmatches $canv $l $f $linehtag($l) $matches $mainfont
1153 } elseif {$ty == "Author"} {
1154 markmatches $canv2 $l $f $linentag($l) $matches $namefont
1155 } elseif {$ty == "Date"} {
1156 markmatches $canv3 $l $f $linedtag($l) $matches $mainfont
1160 lappend matchinglines $l
1161 if {!$didsel && $l > $oldsel} {
1167 if {$matchinglines == {}} {
1169 } elseif {!$didsel} {
1170 findselectline [lindex $matchinglines 0]
1174 proc findselectline {l} {
1175 global findloc commentend ctext
1177 if {$findloc == "All fields" || $findloc == "Comments"} {
1178 # highlight the matches in the comments
1179 set f [$ctext get 1.0 $commentend]
1180 set matches [findmatches $f]
1181 foreach match $matches {
1182 set start [lindex $match 0]
1183 set end [expr [lindex $match 1] + 1]
1184 $ctext tag add found "1.0 + $start c" "1.0 + $end c"
1190 global matchinglines selectedline
1191 if {![info exists matchinglines]} {
1195 if {![info exists selectedline]} return
1196 foreach l $matchinglines {
1197 if {$l > $selectedline} {
1206 global matchinglines selectedline
1207 if {![info exists matchinglines]} {
1211 if {![info exists selectedline]} return
1213 foreach l $matchinglines {
1214 if {$l >= $selectedline} break
1218 findselectline $prev
1224 proc markmatches {canv l str tag matches font} {
1225 set bbox [$canv bbox $tag]
1226 set x0 [lindex $bbox 0]
1227 set y0 [lindex $bbox 1]
1228 set y1 [lindex $bbox 3]
1229 foreach match $matches {
1230 set start [lindex $match 0]
1231 set end [lindex $match 1]
1232 if {$start > $end} continue
1233 set xoff [font measure $font [string range $str 0 [expr $start-1]]]
1234 set xlen [font measure $font [string range $str 0 [expr $end]]]
1235 set t [$canv create rect [expr $x0+$xoff] $y0 [expr $x0+$xlen+2] $y1 \
1236 -outline {} -tags matches -fill yellow]
1241 proc unmarkmatches {} {
1242 global matchinglines
1243 allcanvs delete matches
1244 catch {unset matchinglines}
1247 proc selcanvline {w x y} {
1248 global canv canvy0 ctext linespc selectedline
1249 global lineid linehtag linentag linedtag rowtextx
1250 set ymax [lindex [$canv cget -scrollregion] 3]
1251 if {$ymax == {}} return
1252 set yfrac [lindex [$canv yview] 0]
1253 set y [expr {$y + $yfrac * $ymax}]
1254 set l [expr {int(($y - $canvy0) / $linespc + 0.5)}]
1259 if {![info exists rowtextx($l)] || $x < $rowtextx($l)} return
1265 proc selectline {l} {
1266 global canv canv2 canv3 ctext commitinfo selectedline
1267 global lineid linehtag linentag linedtag
1268 global canvy0 linespc parents nparents
1269 global cflist currentid sha1entry diffids
1270 global commentend seenfile idtags
1272 if {![info exists lineid($l)] || ![info exists linehtag($l)]} return
1274 set t [eval $canv create rect [$canv bbox $linehtag($l)] -outline {{}} \
1275 -tags secsel -fill [$canv cget -selectbackground]]
1277 $canv2 delete secsel
1278 set t [eval $canv2 create rect [$canv2 bbox $linentag($l)] -outline {{}} \
1279 -tags secsel -fill [$canv2 cget -selectbackground]]
1281 $canv3 delete secsel
1282 set t [eval $canv3 create rect [$canv3 bbox $linedtag($l)] -outline {{}} \
1283 -tags secsel -fill [$canv3 cget -selectbackground]]
1285 set y [expr {$canvy0 + $l * $linespc}]
1286 set ymax [lindex [$canv cget -scrollregion] 3]
1287 set ytop [expr {$y - $linespc - 1}]
1288 set ybot [expr {$y + $linespc + 1}]
1289 set wnow [$canv yview]
1290 set wtop [expr [lindex $wnow 0] * $ymax]
1291 set wbot [expr [lindex $wnow 1] * $ymax]
1292 set wh [expr {$wbot - $wtop}]
1294 if {$ytop < $wtop} {
1295 if {$ybot < $wtop} {
1296 set newtop [expr {$y - $wh / 2.0}]
1299 if {$newtop > $wtop - $linespc} {
1300 set newtop [expr {$wtop - $linespc}]
1303 } elseif {$ybot > $wbot} {
1304 if {$ytop > $wbot} {
1305 set newtop [expr {$y - $wh / 2.0}]
1307 set newtop [expr {$ybot - $wh}]
1308 if {$newtop < $wtop + $linespc} {
1309 set newtop [expr {$wtop + $linespc}]
1313 if {$newtop != $wtop} {
1317 allcanvs yview moveto [expr $newtop * 1.0 / $ymax]
1323 set diffids [concat $id $parents($id)]
1324 $sha1entry delete 0 end
1325 $sha1entry insert 0 $id
1326 $sha1entry selection from 0
1327 $sha1entry selection to end
1329 $ctext conf -state normal
1330 $ctext delete 0.0 end
1331 $ctext mark set fmark.0 0.0
1332 $ctext mark gravity fmark.0 left
1333 set info $commitinfo($id)
1334 $ctext insert end "Author: [lindex $info 1] [lindex $info 2]\n"
1335 $ctext insert end "Committer: [lindex $info 3] [lindex $info 4]\n"
1336 if {[info exists idtags($id)]} {
1337 $ctext insert end "Tags:"
1338 foreach tag $idtags($id) {
1339 $ctext insert end " $tag"
1341 $ctext insert end "\n"
1343 $ctext insert end "\n"
1344 $ctext insert end [lindex $info 5]
1345 $ctext insert end "\n"
1346 $ctext tag delete Comments
1347 $ctext tag remove found 1.0 end
1348 $ctext conf -state disabled
1349 set commentend [$ctext index "end - 1c"]
1351 $cflist delete 0 end
1352 $cflist insert end "Comments"
1353 if {$nparents($id) == 1} {
1356 catch {unset seenfile}
1360 global treediffs diffids treepending
1362 if {![info exists treediffs($diffids)]} {
1363 if {![info exists treepending]} {
1364 gettreediffs $diffids
1367 addtocflist $diffids
1371 proc selnextline {dir} {
1373 if {![info exists selectedline]} return
1374 set l [expr $selectedline + $dir]
1379 proc addtocflist {ids} {
1380 global diffids treediffs cflist
1381 if {$ids != $diffids} {
1382 gettreediffs $diffids
1385 foreach f $treediffs($ids) {
1386 $cflist insert end $f
1391 proc gettreediffs {ids} {
1392 global treediffs parents treepending
1393 set treepending $ids
1394 set treediffs($ids) {}
1395 set id [lindex $ids 0]
1396 set p [lindex $ids 1]
1397 if [catch {set gdtf [open "|git-diff-tree -r $p $id" r]}] return
1398 fconfigure $gdtf -blocking 0
1399 fileevent $gdtf readable "gettreediffline $gdtf {$ids}"
1402 proc gettreediffline {gdtf ids} {
1403 global treediffs treepending
1404 set n [gets $gdtf line]
1406 if {![eof $gdtf]} return
1412 set file [lindex $line 5]
1413 lappend treediffs($ids) $file
1416 proc getblobdiffs {ids} {
1417 global diffopts blobdifffd env curdifftag curtagstart
1418 global diffindex difffilestart nextupdate
1420 set id [lindex $ids 0]
1421 set p [lindex $ids 1]
1422 set env(GIT_DIFF_OPTS) $diffopts
1423 if [catch {set bdf [open "|git-diff-tree -r -p $p $id" r]} err] {
1424 puts "error getting diffs: $err"
1427 fconfigure $bdf -blocking 0
1428 set blobdifffd($ids) $bdf
1429 set curdifftag Comments
1432 catch {unset difffilestart}
1433 fileevent $bdf readable "getblobdiffline $bdf {$ids}"
1434 set nextupdate [expr {[clock clicks -milliseconds] + 100}]
1437 proc getblobdiffline {bdf ids} {
1438 global diffids blobdifffd ctext curdifftag curtagstart seenfile
1439 global diffnexthead diffnextnote diffindex difffilestart
1442 set n [gets $bdf line]
1446 if {$ids == $diffids && $bdf == $blobdifffd($ids)} {
1447 $ctext tag add $curdifftag $curtagstart end
1448 set seenfile($curdifftag) 1
1453 if {$ids != $diffids || $bdf != $blobdifffd($ids)} {
1456 $ctext conf -state normal
1457 if {[regexp {^---[ \t]+([^/])*/(.*)} $line match s1 fname]} {
1458 # start of a new file
1459 $ctext insert end "\n"
1460 $ctext tag add $curdifftag $curtagstart end
1461 set seenfile($curdifftag) 1
1462 set curtagstart [$ctext index "end - 1c"]
1464 if {[info exists diffnexthead]} {
1465 set fname $diffnexthead
1466 set header "$diffnexthead ($diffnextnote)"
1469 set here [$ctext index "end - 1c"]
1470 set difffilestart($diffindex) $here
1472 # start mark names at fmark.1 for first file
1473 $ctext mark set fmark.$diffindex $here
1474 $ctext mark gravity fmark.$diffindex left
1475 set curdifftag "f:$fname"
1476 $ctext tag delete $curdifftag
1477 set l [expr {(78 - [string length $header]) / 2}]
1478 set pad [string range "----------------------------------------" 1 $l]
1479 $ctext insert end "$pad $header $pad\n" filesep
1480 } elseif {[string range $line 0 2] == "+++"} {
1481 # no need to do anything with this
1482 } elseif {[regexp {^Created: (.*) \((mode: *[0-7]*)\)} $line match fn m]} {
1483 set diffnexthead $fn
1484 set diffnextnote "created, mode $m"
1485 } elseif {[string range $line 0 8] == "Deleted: "} {
1486 set diffnexthead [string range $line 9 end]
1487 set diffnextnote "deleted"
1488 } elseif {[regexp {^diff --git a/(.*) b/} $line match fn]} {
1489 # save the filename in case the next thing is "new file mode ..."
1490 set diffnexthead $fn
1491 set diffnextnote "modified"
1492 } elseif {[regexp {^new file mode ([0-7]+)} $line match m]} {
1493 set diffnextnote "new file, mode $m"
1494 } elseif {[string range $line 0 11] == "deleted file"} {
1495 set diffnextnote "deleted"
1496 } elseif {[regexp {^@@ -([0-9]+),([0-9]+) \+([0-9]+),([0-9]+) @@(.*)} \
1497 $line match f1l f1c f2l f2c rest]} {
1498 $ctext insert end "\t" hunksep
1499 $ctext insert end " $f1l " d0 " $f2l " d1
1500 $ctext insert end " $rest \n" hunksep
1502 set x [string range $line 0 0]
1503 if {$x == "-" || $x == "+"} {
1504 set tag [expr {$x == "+"}]
1505 set line [string range $line 1 end]
1506 $ctext insert end "$line\n" d$tag
1507 } elseif {$x == " "} {
1508 set line [string range $line 1 end]
1509 $ctext insert end "$line\n"
1510 } elseif {$x == "\\"} {
1511 # e.g. "\ No newline at end of file"
1512 $ctext insert end "$line\n" filesep
1514 # Something else we don't recognize
1515 if {$curdifftag != "Comments"} {
1516 $ctext insert end "\n"
1517 $ctext tag add $curdifftag $curtagstart end
1518 set seenfile($curdifftag) 1
1519 set curtagstart [$ctext index "end - 1c"]
1520 set curdifftag Comments
1522 $ctext insert end "$line\n" filesep
1525 $ctext conf -state disabled
1526 if {[clock clicks -milliseconds] >= $nextupdate} {
1528 fileevent $bdf readable {}
1530 fileevent $bdf readable "getblobdiffline $bdf {$ids}"
1535 global difffilestart ctext
1536 set here [$ctext index @0,0]
1537 for {set i 0} {[info exists difffilestart($i)]} {incr i} {
1538 if {[$ctext compare $difffilestart($i) > $here]} {
1539 $ctext yview $difffilestart($i)
1545 proc listboxsel {} {
1546 global ctext cflist currentid treediffs seenfile
1547 if {![info exists currentid]} return
1548 set sel [lsort [$cflist curselection]]
1549 if {$sel eq {}} return
1550 set first [lindex $sel 0]
1551 catch {$ctext yview fmark.$first}
1555 global linespc charspc canvx0 canvy0 mainfont
1556 set linespc [font metrics $mainfont -linespace]
1557 set charspc [font measure $mainfont "m"]
1558 set canvy0 [expr 3 + 0.5 * $linespc]
1559 set canvx0 [expr 3 + 0.5 * $linespc]
1563 global selectedline stopped redisplaying phase
1564 if {$stopped > 1} return
1565 if {$phase == "getcommits"} return
1567 if {$phase == "drawgraph" || $phase == "incrdraw"} {
1574 proc incrfont {inc} {
1575 global mainfont namefont textfont selectedline ctext canv phase
1576 global stopped entries
1578 set mainfont [lreplace $mainfont 1 1 [expr {[lindex $mainfont 1] + $inc}]]
1579 set namefont [lreplace $namefont 1 1 [expr {[lindex $namefont 1] + $inc}]]
1580 set textfont [lreplace $textfont 1 1 [expr {[lindex $textfont 1] + $inc}]]
1582 $ctext conf -font $textfont
1583 $ctext tag conf filesep -font [concat $textfont bold]
1584 foreach e $entries {
1585 $e conf -font $mainfont
1587 if {$phase == "getcommits"} {
1588 $canv itemconf textitems -font $mainfont
1594 global sha1entry sha1string
1595 if {[string length $sha1string] == 40} {
1596 $sha1entry delete 0 end
1600 proc sha1change {n1 n2 op} {
1601 global sha1string currentid sha1but
1602 if {$sha1string == {}
1603 || ([info exists currentid] && $sha1string == $currentid)} {
1608 if {[$sha1but cget -state] == $state} return
1609 if {$state == "normal"} {
1610 $sha1but conf -state normal -relief raised -text "Goto: "
1612 $sha1but conf -state disabled -relief flat -text "SHA1 ID: "
1616 proc gotocommit {} {
1617 global sha1string currentid idline tagids
1618 if {$sha1string == {}
1619 || ([info exists currentid] && $sha1string == $currentid)} return
1620 if {[info exists tagids($sha1string)]} {
1621 set id $tagids($sha1string)
1623 set id [string tolower $sha1string]
1625 if {[info exists idline($id)]} {
1626 selectline $idline($id)
1629 if {[regexp {^[0-9a-fA-F]{40}$} $sha1string]} {
1634 error_popup "$type $sha1string is not known"
1637 proc lineenter {x y id} {
1638 global hoverx hovery hoverid hovertimer
1639 global commitinfo canv
1641 if {![info exists commitinfo($id)]} return
1645 if {[info exists hovertimer]} {
1646 after cancel $hovertimer
1648 set hovertimer [after 500 linehover]
1652 proc linemotion {x y id} {
1653 global hoverx hovery hoverid hovertimer
1655 if {[info exists hoverid] && $id == $hoverid} {
1658 if {[info exists hovertimer]} {
1659 after cancel $hovertimer
1661 set hovertimer [after 500 linehover]
1665 proc lineleave {id} {
1666 global hoverid hovertimer canv
1668 if {[info exists hoverid] && $id == $hoverid} {
1670 if {[info exists hovertimer]} {
1671 after cancel $hovertimer
1679 global hoverx hovery hoverid hovertimer
1680 global canv linespc lthickness
1681 global commitinfo mainfont
1683 set text [lindex $commitinfo($hoverid) 0]
1684 set ymax [lindex [$canv cget -scrollregion] 3]
1685 if {$ymax == {}} return
1686 set yfrac [lindex [$canv yview] 0]
1687 set x [expr {$hoverx + 2 * $linespc}]
1688 set y [expr {$hovery + $yfrac * $ymax - $linespc / 2}]
1689 set x0 [expr {$x - 2 * $lthickness}]
1690 set y0 [expr {$y - 2 * $lthickness}]
1691 set x1 [expr {$x + [font measure $mainfont $text] + 2 * $lthickness}]
1692 set y1 [expr {$y + $linespc + 2 * $lthickness}]
1693 set t [$canv create rectangle $x0 $y0 $x1 $y1 \
1694 -fill \#ffff80 -outline black -width 1 -tags hover]
1696 set t [$canv create text $x $y -anchor nw -text $text -tags hover]
1700 proc lineclick {x y id} {
1701 global ctext commitinfo children cflist canv
1705 # fill the details pane with info about this line
1706 $ctext conf -state normal
1707 $ctext delete 0.0 end
1708 $ctext insert end "Parent:\n "
1709 catch {destroy $ctext.$id}
1710 button $ctext.$id -text "Go:" -command "selbyid $id" \
1712 $ctext window create end -window $ctext.$id -align center
1713 set info $commitinfo($id)
1714 $ctext insert end "\t[lindex $info 0]\n"
1715 $ctext insert end "\tAuthor:\t[lindex $info 1]\n"
1716 $ctext insert end "\tDate:\t[lindex $info 2]\n"
1717 $ctext insert end "\tID:\t$id\n"
1718 if {[info exists children($id)]} {
1719 $ctext insert end "\nChildren:"
1720 foreach child $children($id) {
1721 $ctext insert end "\n "
1722 catch {destroy $ctext.$child}
1723 button $ctext.$child -text "Go:" -command "selbyid $child" \
1725 $ctext window create end -window $ctext.$child -align center
1726 set info $commitinfo($child)
1727 $ctext insert end "\t[lindex $info 0]"
1730 $ctext conf -state disabled
1732 $cflist delete 0 end
1737 if {[info exists idline($id)]} {
1738 selectline $idline($id)
1744 if {![info exists startmstime]} {
1745 set startmstime [clock clicks -milliseconds]
1747 return [format "%.3f" [expr {([clock click -milliseconds] - $startmstime) / 1000.0}]]
1750 proc rowmenu {x y id} {
1751 global rowctxmenu idline selectedline rowmenuid
1753 if {![info exists selectedline] || $idline($id) eq $selectedline} {
1758 $rowctxmenu entryconfigure 0 -state $state
1759 $rowctxmenu entryconfigure 1 -state $state
1760 $rowctxmenu entryconfigure 2 -state $state
1762 tk_popup $rowctxmenu $x $y
1765 proc diffvssel {dirn} {
1766 global rowmenuid selectedline lineid
1768 global diffids commitinfo
1770 if {![info exists selectedline]} return
1772 set oldid $lineid($selectedline)
1773 set newid $rowmenuid
1775 set oldid $rowmenuid
1776 set newid $lineid($selectedline)
1778 $ctext conf -state normal
1779 $ctext delete 0.0 end
1780 $ctext mark set fmark.0 0.0
1781 $ctext mark gravity fmark.0 left
1782 $cflist delete 0 end
1783 $cflist insert end "Top"
1784 $ctext insert end "From $oldid\n "
1785 $ctext insert end [lindex $commitinfo($oldid) 0]
1786 $ctext insert end "\n\nTo $newid\n "
1787 $ctext insert end [lindex $commitinfo($newid) 0]
1788 $ctext insert end "\n"
1789 $ctext conf -state disabled
1790 $ctext tag delete Comments
1791 $ctext tag remove found 1.0 end
1792 set diffids [list $newid $oldid]
1797 global rowmenuid currentid commitinfo patchtop patchnum
1799 if {![info exists currentid]} return
1800 set oldid $currentid
1801 set oldhead [lindex $commitinfo($oldid) 0]
1802 set newid $rowmenuid
1803 set newhead [lindex $commitinfo($newid) 0]
1806 catch {destroy $top}
1808 label $top.title -text "Generate patch"
1810 label $top.from -text "From:"
1811 entry $top.fromsha1 -width 40
1812 $top.fromsha1 insert 0 $oldid
1813 $top.fromsha1 conf -state readonly
1814 grid $top.from $top.fromsha1 -sticky w
1815 entry $top.fromhead -width 60
1816 $top.fromhead insert 0 $oldhead
1817 $top.fromhead conf -state readonly
1818 grid x $top.fromhead -sticky w
1819 label $top.to -text "To:"
1820 entry $top.tosha1 -width 40
1821 $top.tosha1 insert 0 $newid
1822 $top.tosha1 conf -state readonly
1823 grid $top.to $top.tosha1 -sticky w
1824 entry $top.tohead -width 60
1825 $top.tohead insert 0 $newhead
1826 $top.tohead conf -state readonly
1827 grid x $top.tohead -sticky w
1828 button $top.rev -text "Reverse" -command mkpatchrev -padx 5
1829 grid $top.rev x -pady 10
1830 label $top.flab -text "Output file:"
1831 entry $top.fname -width 60
1832 $top.fname insert 0 [file normalize "patch$patchnum.patch"]
1834 grid $top.flab $top.fname
1836 button $top.buts.gen -text "Generate" -command mkpatchgo
1837 button $top.buts.can -text "Cancel" -command mkpatchcan
1838 grid $top.buts.gen $top.buts.can
1839 grid columnconfigure $top.buts 0 -weight 1 -uniform a
1840 grid columnconfigure $top.buts 1 -weight 1 -uniform a
1841 grid $top.buts - -pady 10 -sticky ew
1844 proc mkpatchrev {} {
1847 set oldid [$patchtop.fromsha1 get]
1848 set oldhead [$patchtop.fromhead get]
1849 set newid [$patchtop.tosha1 get]
1850 set newhead [$patchtop.tohead get]
1851 foreach e [list fromsha1 fromhead tosha1 tohead] \
1852 v [list $newid $newhead $oldid $oldhead] {
1853 $patchtop.$e conf -state normal
1854 $patchtop.$e delete 0 end
1855 $patchtop.$e insert 0 $v
1856 $patchtop.$e conf -state readonly
1863 set oldid [$patchtop.fromsha1 get]
1864 set newid [$patchtop.tosha1 get]
1865 set fname [$patchtop.fname get]
1866 if {[catch {exec git-diff-tree -p $oldid $newid >$fname &} err]} {
1867 error_popup "Error creating patch: $err"
1869 catch {destroy $patchtop}
1873 proc mkpatchcan {} {
1876 catch {destroy $patchtop}
1889 set diffopts "-U 5 -p"
1891 set mainfont {Helvetica 9}
1892 set textfont {Courier 9}
1894 set colors {green red blue magenta darkgrey brown orange}
1896 catch {source ~/.gitk}
1898 set namefont $mainfont
1900 lappend namefont bold
1905 switch -regexp -- $arg {
1907 "^-b" { set boldnames 1 }
1908 "^-d" { set datemode 1 }
1910 lappend revtreeargs $arg
1922 getcommits $revtreeargs