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 --topo-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 # set it blocking so we wait for the process to terminate
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 findtypemenu findloc findstring fstring geometry
274 global entries sha1entry sha1string sha1but
275 global maincursor textcursor
276 global rowctxmenu gaudydiff mergemax
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 set findtypemenu
[tk_optionMenu .ctop.top.bar.findtype \
346 findtype Exact IgnCase Regexp
]
347 set findloc
"All fields"
348 tk_optionMenu .ctop.top.bar.findloc findloc
"All fields" Headline \
349 Comments Author Committer Files Pickaxe
350 pack .ctop.top.bar.findloc
-side right
351 pack .ctop.top.bar.findtype
-side right
352 # for making sure type==Exact whenever loc==Pickaxe
353 trace add variable findloc
write findlocchange
355 panedwindow .ctop.cdet
-orient horizontal
357 frame .ctop.cdet.left
358 set ctext .ctop.cdet.left.ctext
359 text
$ctext -bg white
-state disabled
-font $textfont \
360 -width $geometry(ctextw
) -height $geometry(ctexth
) \
361 -yscrollcommand ".ctop.cdet.left.sb set"
362 scrollbar .ctop.cdet.left.sb
-command "$ctext yview"
363 pack .ctop.cdet.left.sb
-side right
-fill y
364 pack
$ctext -side left
-fill both
-expand 1
365 .ctop.cdet add .ctop.cdet.left
367 $ctext tag conf filesep
-font [concat
$textfont bold
] -back "#aaaaaa"
369 $ctext tag conf hunksep
-back blue
-fore white
370 $ctext tag conf d0
-back "#ff8080"
371 $ctext tag conf d1
-back green
373 $ctext tag conf hunksep
-fore blue
374 $ctext tag conf d0
-fore red
375 $ctext tag conf d1
-fore "#00a000"
376 $ctext tag conf m0
-fore red
377 $ctext tag conf m1
-fore blue
378 $ctext tag conf m2
-fore green
379 $ctext tag conf m3
-fore purple
380 $ctext tag conf
m4 -fore brown
381 $ctext tag conf mmax
-fore darkgrey
383 $ctext tag conf mresult
-font [concat
$textfont bold
]
384 $ctext tag conf msep
-font [concat
$textfont bold
]
385 $ctext tag conf found
-back yellow
388 frame .ctop.cdet.right
389 set cflist .ctop.cdet.right.cfiles
390 listbox
$cflist -bg white
-selectmode extended
-width $geometry(cflistw
) \
391 -yscrollcommand ".ctop.cdet.right.sb set"
392 scrollbar .ctop.cdet.right.sb
-command "$cflist yview"
393 pack .ctop.cdet.right.sb
-side right
-fill y
394 pack
$cflist -side left
-fill both
-expand 1
395 .ctop.cdet add .ctop.cdet.right
396 bind .ctop.cdet
<Configure
> {resizecdetpanes
%W
%w
}
398 pack .ctop
-side top
-fill both
-expand 1
400 bindall
<1> {selcanvline
%W
%x
%y
}
401 #bindall <B1-Motion> {selcanvline %W %x %y}
402 bindall
<ButtonRelease-4
> "allcanvs yview scroll -5 units"
403 bindall
<ButtonRelease-5
> "allcanvs yview scroll 5 units"
404 bindall
<2> "allcanvs scan mark 0 %y"
405 bindall
<B2-Motion
> "allcanvs scan dragto 0 %y"
406 bind .
<Key-Up
> "selnextline -1"
407 bind .
<Key-Down
> "selnextline 1"
408 bind .
<Key-Prior
> "allcanvs yview scroll -1 pages"
409 bind .
<Key-Next
> "allcanvs yview scroll 1 pages"
410 bindkey
<Key-Delete
> "$ctext yview scroll -1 pages"
411 bindkey
<Key-BackSpace
> "$ctext yview scroll -1 pages"
412 bindkey
<Key-space
> "$ctext yview scroll 1 pages"
413 bindkey p
"selnextline -1"
414 bindkey n
"selnextline 1"
415 bindkey b
"$ctext yview scroll -1 pages"
416 bindkey d
"$ctext yview scroll 18 units"
417 bindkey u
"$ctext yview scroll -18 units"
418 bindkey
/ {findnext
1}
419 bindkey
<Key-Return
> {findnext
0}
422 bind .
<Control-q
> doquit
423 bind .
<Control-f
> dofind
424 bind .
<Control-g
> {findnext
0}
425 bind .
<Control-r
> findprev
426 bind .
<Control-equal
> {incrfont
1}
427 bind .
<Control-KP_Add
> {incrfont
1}
428 bind .
<Control-minus
> {incrfont
-1}
429 bind .
<Control-KP_Subtract
> {incrfont
-1}
430 bind $cflist <<ListboxSelect>> listboxsel
431 bind . <Destroy> {savestuff %W}
432 bind . <Button-1> "click %W"
433 bind $fstring <Key-Return> dofind
434 bind $sha1entry <Key-Return> gotocommit
435 bind $sha1entry <<PasteSelection>> clearsha1
437 set maincursor [. cget -cursor]
438 set textcursor [$ctext cget -cursor]
440 set rowctxmenu .rowctxmenu
441 menu $rowctxmenu -tearoff 0
442 $rowctxmenu add command -label "Diff this -> selected" \
443 -command {diffvssel 0}
444 $rowctxmenu add command -label "Diff selected -> this" \
445 -command {diffvssel 1}
446 $rowctxmenu add command -label "Make patch" -command mkpatch
447 $rowctxmenu add command -label "Create tag" -command mktag
448 $rowctxmenu add command -label "Write commit to file" -command writecommit
451 # when we make a key binding for the toplevel, make sure
452 # it doesn't get triggered when that key is pressed in the
453 # find string entry widget.
454 proc bindkey {ev script} {
457 set escript [bind Entry $ev]
458 if {$escript == {}} {
459 set escript [bind Entry <Key>]
462 bind $e $ev "$escript; break"
466 # set the focus back to the toplevel for any click outside
477 global canv canv2 canv3 ctext cflist mainfont textfont
478 global stuffsaved findmergefiles gaudydiff
480 if {$stuffsaved} return
481 if {![winfo viewable .]} return
483 set f [open "~/.gitk-new" w]
484 puts $f [list set mainfont $mainfont]
485 puts $f [list set textfont $textfont]
486 puts $f [list set findmergefiles $findmergefiles]
487 puts $f [list set gaudydiff $gaudydiff]
488 puts $f "set geometry(width) [winfo width .ctop]"
489 puts $f "set geometry(height) [winfo height .ctop]"
490 puts $f "set geometry(canv1) [expr [winfo width $canv]-2]"
491 puts $f "set geometry(canv2) [expr [winfo width $canv2]-2]"
492 puts $f "set geometry(canv3) [expr [winfo width $canv3]-2]"
493 puts $f "set geometry(canvh) [expr [winfo height $canv]-2]"
494 set wid [expr {([winfo width $ctext] - 8) \
495 / [font measure $textfont "0"]}]
496 puts $f "set geometry(ctextw) $wid"
497 set wid [expr {([winfo width $cflist] - 11) \
498 / [font measure [$cflist cget -font] "0"]}]
499 puts $f "set geometry(cflistw) $wid"
501 file rename -force "~/.gitk-new" "~/.gitk"
506 proc resizeclistpanes {win w} {
508 if [info exists oldwidth($win)] {
509 set s0 [$win sash coord 0]
510 set s1 [$win sash coord 1]
512 set sash0 [expr {int($w/2 - 2)}]
513 set sash1 [expr {int($w*5/6 - 2)}]
515 set factor [expr {1.0 * $w / $oldwidth($win)}]
516 set sash0 [expr {int($factor * [lindex $s0 0])}]
517 set sash1 [expr {int($factor * [lindex $s1 0])}]
521 if {$sash1 < $sash0 + 20} {
522 set sash1 [expr $sash0 + 20]
524 if {$sash1 > $w - 10} {
525 set sash1 [expr $w - 10]
526 if {$sash0 > $sash1 - 20} {
527 set sash0 [expr $sash1 - 20]
531 $win sash place 0 $sash0 [lindex $s0 1]
532 $win sash place 1 $sash1 [lindex $s1 1]
534 set oldwidth($win) $w
537 proc resizecdetpanes {win w} {
539 if [info exists oldwidth($win)] {
540 set s0 [$win sash coord 0]
542 set sash0 [expr {int($w*3/4 - 2)}]
544 set factor [expr {1.0 * $w / $oldwidth($win)}]
545 set sash0 [expr {int($factor * [lindex $s0 0])}]
549 if {$sash0 > $w - 15} {
550 set sash0 [expr $w - 15]
553 $win sash place 0 $sash0 [lindex $s0 1]
555 set oldwidth($win) $w
559 global canv canv2 canv3
565 proc bindall {event action} {
566 global canv canv2 canv3
567 bind $canv $event $action
568 bind $canv2 $event $action
569 bind $canv3 $event $action
574 if {[winfo exists $w]} {
579 wm title $w "About gitk"
583 Copyright © 2005 Paul Mackerras
585 Use and redistribute under the terms of the GNU General Public License} \
586 -justify center -aspect 400
587 pack $w.m -side top -fill x -padx 20 -pady 20
588 button $w.ok -text Close -command "destroy $w"
589 pack $w.ok -side bottom
592 proc assigncolor {id} {
593 global commitinfo colormap commcolors colors nextcolor
594 global parents nparents children nchildren
595 global cornercrossings crossings
597 if [info exists colormap($id)] return
598 set ncolors [llength $colors]
599 if {$nparents($id) <= 1 && $nchildren($id) == 1} {
600 set child [lindex $children($id) 0]
601 if {[info exists colormap($child)]
602 && $nparents($child) == 1} {
603 set colormap($id) $colormap($child)
608 if {[info exists cornercrossings($id)]} {
609 foreach x $cornercrossings($id) {
610 if {[info exists colormap($x)]
611 && [lsearch -exact $badcolors $colormap($x)] < 0} {
612 lappend badcolors $colormap($x)
615 if {[llength $badcolors] >= $ncolors} {
619 set origbad $badcolors
620 if {[llength $badcolors] < $ncolors - 1} {
621 if {[info exists crossings($id)]} {
622 foreach x $crossings($id) {
623 if {[info exists colormap($x)]
624 && [lsearch -exact $badcolors $colormap($x)] < 0} {
625 lappend badcolors $colormap($x)
628 if {[llength $badcolors] >= $ncolors} {
629 set badcolors $origbad
632 set origbad $badcolors
634 if {[llength $badcolors] < $ncolors - 1} {
635 foreach child $children($id) {
636 if {[info exists colormap($child)]
637 && [lsearch -exact $badcolors $colormap($child)] < 0} {
638 lappend badcolors $colormap($child)
640 if {[info exists parents($child)]} {
641 foreach p $parents($child) {
642 if {[info exists colormap($p)]
643 && [lsearch -exact $badcolors $colormap($p)] < 0} {
644 lappend badcolors $colormap($p)
649 if {[llength $badcolors] >= $ncolors} {
650 set badcolors $origbad
653 for {set i 0} {$i <= $ncolors} {incr i} {
654 set c [lindex $colors $nextcolor]
655 if {[incr nextcolor] >= $ncolors} {
658 if {[lsearch -exact $badcolors $c]} break
664 global canvy canvy0 lineno numcommits lthickness nextcolor linespc
665 global mainline sidelines
666 global nchildren ncleft
673 set lthickness [expr {int($linespc / 9) + 1}]
674 catch {unset mainline}
675 catch {unset sidelines}
676 foreach id [array names nchildren] {
677 set ncleft($id) $nchildren($id)
681 proc bindline {t id} {
684 $canv bind $t <Enter> "lineenter %x %y $id"
685 $canv bind $t <Motion> "linemotion %x %y $id"
686 $canv bind $t <Leave> "lineleave $id"
687 $canv bind $t <Button-1> "lineclick %x %y $id"
690 proc drawcommitline {level} {
691 global parents children nparents nchildren todo
692 global canv canv2 canv3 mainfont namefont canvx0 canvy linespc
693 global lineid linehtag linentag linedtag commitinfo
694 global colormap numcommits currentparents dupparents
695 global oldlevel oldnlines oldtodo
696 global idtags idline idheads
697 global lineno lthickness mainline sidelines
698 global commitlisted rowtextx idpos
702 set id [lindex $todo $level]
703 set lineid($lineno) $id
704 set idline($id) $lineno
705 set ofill [expr {[info exists commitlisted($id)]? "blue": "white"}]
706 if {![info exists commitinfo($id)]} {
708 if {![info exists commitinfo($id)]} {
709 set commitinfo($id) {"No commit information available"}
714 set currentparents {}
716 if {[info exists commitlisted($id)] && [info exists parents($id)]} {
717 foreach p $parents($id) {
718 if {[lsearch -exact $currentparents $p] < 0} {
719 lappend currentparents $p
721 # remember that this parent was listed twice
722 lappend dupparents $p
726 set x [expr $canvx0 + $level * $linespc]
728 set canvy [expr $canvy + $linespc]
729 allcanvs conf -scrollregion \
730 [list 0 0 0 [expr $y1 + 0.5 * $linespc + 2]]
731 if {[info exists mainline($id)]} {
732 lappend mainline($id) $x $y1
733 set t [$canv create line $mainline($id) \
734 -width $lthickness -fill $colormap($id)]
738 if {[info exists sidelines($id)]} {
739 foreach ls $sidelines($id) {
740 set coords [lindex $ls 0]
741 set thick [lindex $ls 1]
742 set t [$canv create line $coords -fill $colormap($id) \
743 -width [expr {$thick * $lthickness}]]
748 set orad [expr {$linespc / 3}]
749 set t [$canv create oval [expr $x - $orad] [expr $y1 - $orad] \
750 [expr $x + $orad - 1] [expr $y1 + $orad - 1] \
751 -fill $ofill -outline black -width 1]
753 $canv bind $t <1> {selcanvline {} %x %y}
754 set xt [expr $canvx0 + [llength $todo] * $linespc]
755 if {[llength $currentparents] > 2} {
756 set xt [expr {$xt + ([llength $currentparents] - 2) * $linespc}]
758 set rowtextx($lineno) $xt
759 set idpos($id) [list $x $xt $y1]
760 if {[info exists idtags($id)] || [info exists idheads($id)]} {
761 set xt [drawtags $id $x $xt $y1]
763 set headline [lindex $commitinfo($id) 0]
764 set name [lindex $commitinfo($id) 1]
765 set date [lindex $commitinfo($id) 2]
766 set linehtag($lineno) [$canv create text $xt $y1 -anchor w \
767 -text $headline -font $mainfont ]
768 $canv bind $linehtag($lineno) <Button-3> "rowmenu %X %Y $id"
769 set linentag($lineno) [$canv2 create text 3 $y1 -anchor w \
770 -text $name -font $namefont]
771 set linedtag($lineno) [$canv3 create text 3 $y1 -anchor w \
772 -text $date -font $mainfont]
775 proc drawtags {id x xt y1} {
776 global idtags idheads
777 global linespc lthickness
782 if {[info exists idtags($id)]} {
783 set marks $idtags($id)
784 set ntags [llength $marks]
786 if {[info exists idheads($id)]} {
787 set marks [concat $marks $idheads($id)]
793 set delta [expr {int(0.5 * ($linespc - $lthickness))}]
794 set yt [expr $y1 - 0.5 * $linespc]
795 set yb [expr $yt + $linespc - 1]
799 set wid [font measure $mainfont $tag]
802 set xt [expr {$xt + $delta + $wid + $lthickness + $linespc}]
804 set t [$canv create line $x $y1 [lindex $xvals end] $y1 \
805 -width $lthickness -fill black -tags tag.$id]
807 foreach tag $marks x $xvals wid $wvals {
808 set xl [expr $x + $delta]
809 set xr [expr $x + $delta + $wid + $lthickness]
810 if {[incr ntags -1] >= 0} {
812 $canv create polygon $x [expr $yt + $delta] $xl $yt\
813 $xr $yt $xr $yb $xl $yb $x [expr $yb - $delta] \
814 -width 1 -outline black -fill yellow -tags tag.$id
817 set xl [expr $xl - $delta/2]
818 $canv create polygon $x $yt $xr $yt $xr $yb $x $yb \
819 -width 1 -outline black -fill green -tags tag.$id
821 $canv create text $xl $y1 -anchor w -text $tag \
822 -font $mainfont -tags tag.$id
827 proc updatetodo {level noshortcut} {
828 global currentparents ncleft todo
829 global mainline oldlevel oldtodo oldnlines
830 global canvx0 canvy linespc mainline
835 set oldnlines [llength $todo]
836 if {!$noshortcut && [llength $currentparents] == 1} {
837 set p [lindex $currentparents 0]
838 if {$ncleft($p) == 1 && [lsearch -exact $todo $p] < 0} {
840 set x [expr $canvx0 + $level * $linespc]
841 set y [expr $canvy - $linespc]
842 set mainline($p) [list $x $y]
843 set todo [lreplace $todo $level $level $p]
848 set todo [lreplace $todo $level $level]
850 foreach p $currentparents {
852 set k [lsearch -exact $todo $p]
854 set todo [linsert $todo $i $p]
861 proc notecrossings {id lo hi corner} {
862 global oldtodo crossings cornercrossings
864 for {set i $lo} {[incr i] < $hi} {} {
865 set p [lindex $oldtodo $i]
866 if {$p == {}} continue
868 if {![info exists cornercrossings($id)]
869 || [lsearch -exact $cornercrossings($id) $p] < 0} {
870 lappend cornercrossings($id) $p
872 if {![info exists cornercrossings($p)]
873 || [lsearch -exact $cornercrossings($p) $id] < 0} {
874 lappend cornercrossings($p) $id
877 if {![info exists crossings($id)]
878 || [lsearch -exact $crossings($id) $p] < 0} {
879 lappend crossings($id) $p
881 if {![info exists crossings($p)]
882 || [lsearch -exact $crossings($p) $id] < 0} {
883 lappend crossings($p) $id
890 global canv mainline sidelines canvx0 canvy linespc
891 global oldlevel oldtodo todo currentparents dupparents
892 global lthickness linespc canvy colormap
894 set y1 [expr $canvy - $linespc]
897 foreach id $oldtodo {
899 if {$id == {}} continue
900 set xi [expr {$canvx0 + $i * $linespc}]
901 if {$i == $oldlevel} {
902 foreach p $currentparents {
903 set j [lsearch -exact $todo $p]
904 set coords [list $xi $y1]
905 set xj [expr {$canvx0 + $j * $linespc}]
907 lappend coords [expr $xj + $linespc] $y1
908 notecrossings $p $j $i [expr {$j + 1}]
909 } elseif {$j > $i + 1} {
910 lappend coords [expr $xj - $linespc] $y1
911 notecrossings $p $i $j [expr {$j - 1}]
913 if {[lsearch -exact $dupparents $p] >= 0} {
914 # draw a double-width line to indicate the doubled parent
915 lappend coords $xj $y2
916 lappend sidelines($p) [list $coords 2]
917 if {![info exists mainline($p)]} {
918 set mainline($p) [list $xj $y2]
921 # normal case, no parent duplicated
922 if {![info exists mainline($p)]} {
924 lappend coords $xj $y2
926 set mainline($p) $coords
928 lappend coords $xj $y2
929 lappend sidelines($p) [list $coords 1]
933 } elseif {[lindex $todo $i] != $id} {
934 set j [lsearch -exact $todo $id]
935 set xj [expr {$canvx0 + $j * $linespc}]
936 lappend mainline($id) $xi $y1 $xj $y2
941 proc decidenext {{noread 0}} {
942 global parents children nchildren ncleft todo
943 global canv canv2 canv3 mainfont namefont canvx0 canvy linespc
944 global datemode cdate
946 global currentparents oldlevel oldnlines oldtodo
947 global lineno lthickness
949 # remove the null entry if present
950 set nullentry [lsearch -exact $todo {}]
951 if {$nullentry >= 0} {
952 set todo [lreplace $todo $nullentry $nullentry]
955 # choose which one to do next time around
956 set todol [llength $todo]
959 for {set k $todol} {[incr k -1] >= 0} {} {
960 set p [lindex $todo $k]
961 if {$ncleft($p) == 0} {
963 if {![info exists commitinfo($p)]} {
969 if {$latest == {} || $cdate($p) > $latest} {
971 set latest $cdate($p)
981 puts "ERROR: none of the pending commits can be done yet:"
983 puts " $p ($ncleft($p))"
989 # If we are reducing, put in a null entry
990 if {$todol < $oldnlines} {
991 if {$nullentry >= 0} {
994 && [lindex $oldtodo $i] == [lindex $todo $i]} {
1004 set todo [linsert $todo $i {}]
1013 proc drawcommit {id} {
1014 global phase todo nchildren datemode nextupdate
1017 if {$phase != "incrdraw"} {
1020 set startcommits $id
1023 updatetodo 0 $datemode
1025 if {$nchildren($id) == 0} {
1027 lappend startcommits $id
1029 set level [decidenext 1]
1030 if {$level == {} || $id != [lindex $todo $level]} {
1035 drawcommitline $level
1036 if {[updatetodo $level $datemode]} {
1037 set level [decidenext 1]
1038 if {$level == {}} break
1040 set id [lindex $todo $level]
1041 if {![info exists commitlisted($id)]} {
1044 if {[clock clicks -milliseconds] >= $nextupdate} {
1052 proc finishcommits {} {
1055 global canv mainfont ctext maincursor textcursor
1057 if {$phase != "incrdraw"} {
1059 $canv create text 3 3 -anchor nw -text "No commits selected" \
1060 -font $mainfont -tags textitems
1064 set level [decidenext]
1065 drawrest $level [llength $startcommits]
1067 . config -cursor $maincursor
1068 $ctext config -cursor $textcursor
1072 global nextupdate startmsecs startcommits todo
1074 if {$startcommits == {}} return
1075 set startmsecs [clock clicks -milliseconds]
1076 set nextupdate [expr $startmsecs + 100]
1078 set todo [lindex $startcommits 0]
1082 proc drawrest {level startix} {
1083 global phase stopped redisplaying selectedline
1084 global datemode currentparents todo
1086 global nextupdate startmsecs startcommits idline
1090 set startid [lindex $startcommits $startix]
1092 if {$startid != {}} {
1093 set startline $idline($startid)
1097 drawcommitline $level
1098 set hard [updatetodo $level $datemode]
1099 if {$numcommits == $startline} {
1100 lappend todo $startid
1103 set startid [lindex $startcommits $startix]
1105 if {$startid != {}} {
1106 set startline $idline($startid)
1110 set level [decidenext]
1111 if {$level < 0} break
1114 if {[clock clicks -milliseconds] >= $nextupdate} {
1121 set drawmsecs [expr [clock clicks -milliseconds] - $startmsecs]
1122 #puts "overall $drawmsecs ms for $numcommits commits"
1123 if {$redisplaying} {
1124 if {$stopped == 0 && [info exists selectedline]} {
1125 selectline $selectedline
1127 if {$stopped == 1} {
1129 after idle drawgraph
1136 proc findmatches {f} {
1137 global findtype foundstring foundstrlen
1138 if {$findtype == "Regexp"} {
1139 set matches [regexp -indices -all -inline $foundstring $f]
1141 if {$findtype == "IgnCase"} {
1142 set str [string tolower $f]
1148 while {[set j [string first $foundstring $str $i]] >= 0} {
1149 lappend matches [list $j [expr $j+$foundstrlen-1]]
1150 set i [expr $j + $foundstrlen]
1157 global findtype findloc findstring markedmatches commitinfo
1158 global numcommits lineid linehtag linentag linedtag
1159 global mainfont namefont canv canv2 canv3 selectedline
1160 global matchinglines foundstring foundstrlen
1165 set matchinglines {}
1166 if {$findloc == "Pickaxe"} {
1170 if {$findtype == "IgnCase"} {
1171 set foundstring [string tolower $findstring]
1173 set foundstring $findstring
1175 set foundstrlen [string length $findstring]
1176 if {$foundstrlen == 0} return
1177 if {$findloc == "Files"} {
1181 if {![info exists selectedline]} {
1184 set oldsel $selectedline
1187 set fldtypes {Headline Author Date Committer CDate Comment}
1188 for {set l 0} {$l < $numcommits} {incr l} {
1190 set info $commitinfo($id)
1192 foreach f $info ty $fldtypes {
1193 if {$findloc != "All fields" && $findloc != $ty} {
1196 set matches [findmatches $f]
1197 if {$matches == {}} continue
1199 if {$ty == "Headline"} {
1200 markmatches $canv $l $f $linehtag($l) $matches $mainfont
1201 } elseif {$ty == "Author"} {
1202 markmatches $canv2 $l $f $linentag($l) $matches $namefont
1203 } elseif {$ty == "Date"} {
1204 markmatches $canv3 $l $f $linedtag($l) $matches $mainfont
1208 lappend matchinglines $l
1209 if {!$didsel && $l > $oldsel} {
1215 if {$matchinglines == {}} {
1217 } elseif {!$didsel} {
1218 findselectline [lindex $matchinglines 0]
1222 proc findselectline {l} {
1223 global findloc commentend ctext
1225 if {$findloc == "All fields" || $findloc == "Comments"} {
1226 # highlight the matches in the comments
1227 set f [$ctext get 1.0 $commentend]
1228 set matches [findmatches $f]
1229 foreach match $matches {
1230 set start [lindex $match 0]
1231 set end [expr [lindex $match 1] + 1]
1232 $ctext tag add found "1.0 + $start c" "1.0 + $end c"
1237 proc findnext {restart} {
1238 global matchinglines selectedline
1239 if {![info exists matchinglines]} {
1245 if {![info exists selectedline]} return
1246 foreach l $matchinglines {
1247 if {$l > $selectedline} {
1256 global matchinglines selectedline
1257 if {![info exists matchinglines]} {
1261 if {![info exists selectedline]} return
1263 foreach l $matchinglines {
1264 if {$l >= $selectedline} break
1268 findselectline $prev
1274 proc findlocchange {name ix op} {
1275 global findloc findtype findtypemenu
1276 if {$findloc == "Pickaxe"} {
1282 $findtypemenu entryconf 1 -state $state
1283 $findtypemenu entryconf 2 -state $state
1286 proc stopfindproc {{done 0}} {
1287 global findprocpid findprocfile findids
1288 global ctext findoldcursor phase maincursor textcursor
1289 global findinprogress
1291 catch {unset findids}
1292 if {[info exists findprocpid]} {
1294 catch {exec kill $findprocpid}
1296 catch {close $findprocfile}
1299 if {[info exists findinprogress]} {
1300 unset findinprogress
1301 if {$phase != "incrdraw"} {
1302 . config -cursor $maincursor
1303 $ctext config -cursor $textcursor
1308 proc findpatches {} {
1309 global findstring selectedline numcommits
1310 global findprocpid findprocfile
1311 global finddidsel ctext lineid findinprogress
1312 global findinsertpos
1314 if {$numcommits == 0} return
1316 # make a list of all the ids to search, starting at the one
1317 # after the selected line (if any)
1318 if {[info exists selectedline]} {
1324 for {set i 0} {$i < $numcommits} {incr i} {
1325 if {[incr l] >= $numcommits} {
1328 append inputids $lineid($l) "\n"
1332 set f [open [list | git-diff-tree --stdin -s -r -S$findstring \
1335 error_popup "Error starting search process: $err"
1339 set findinsertpos end
1341 set findprocpid [pid $f]
1342 fconfigure $f -blocking 0
1343 fileevent $f readable readfindproc
1345 . config -cursor watch
1346 $ctext config -cursor watch
1347 set findinprogress 1
1350 proc readfindproc {} {
1351 global findprocfile finddidsel
1352 global idline matchinglines findinsertpos
1354 set n [gets $findprocfile line]
1356 if {[eof $findprocfile]} {
1364 if {![regexp {^[0-9a-f]{40}} $line id]} {
1365 error_popup "Can't parse git-diff-tree output: $line"
1369 if {![info exists idline($id)]} {
1370 puts stderr "spurious id: $id"
1377 proc insertmatch {l id} {
1378 global matchinglines findinsertpos finddidsel
1380 if {$findinsertpos == "end"} {
1381 if {$matchinglines != {} && $l < [lindex $matchinglines 0]} {
1382 set matchinglines [linsert $matchinglines 0 $l]
1385 lappend matchinglines $l
1388 set matchinglines [linsert $matchinglines $findinsertpos $l]
1399 global selectedline numcommits lineid ctext
1400 global ffileline finddidsel parents nparents
1401 global findinprogress findstartline findinsertpos
1402 global treediffs fdiffids fdiffsneeded fdiffpos
1403 global findmergefiles
1405 if {$numcommits == 0} return
1407 if {[info exists selectedline]} {
1408 set l [expr {$selectedline + 1}]
1413 set findstartline $l
1418 if {$findmergefiles || $nparents($id) == 1} {
1419 foreach p $parents($id) {
1420 if {![info exists treediffs([list $id $p])]} {
1421 append diffsneeded "$id $p\n"
1422 lappend fdiffsneeded [list $id $p]
1426 if {[incr l] >= $numcommits} {
1429 if {$l == $findstartline} break
1432 # start off a git-diff-tree process if needed
1433 if {$diffsneeded ne {}} {
1435 set df [open [list | git-diff-tree -r --stdin << $diffsneeded] r]
1437 error_popup "Error starting search process: $err"
1440 catch {unset fdiffids}
1442 fconfigure $df -blocking 0
1443 fileevent $df readable [list readfilediffs $df]
1447 set findinsertpos end
1449 set p [lindex $parents($id) 0]
1450 . config -cursor watch
1451 $ctext config -cursor watch
1452 set findinprogress 1
1453 findcont [list $id $p]
1457 proc readfilediffs {df} {
1458 global findids fdiffids fdiffs
1460 set n [gets $df line]
1464 if {[catch {close $df} err]} {
1467 error_popup "Error in git-diff-tree: $err"
1468 } elseif {[info exists findids]} {
1472 error_popup "Couldn't find diffs for {$ids}"
1477 if {[regexp {^([0-9a-f]{40}) \(from ([0-9a-f]{40})\)} $line match id p]} {
1478 # start of a new string of diffs
1480 set fdiffids [list $id $p]
1482 } elseif {[string match ":*" $line]} {
1483 lappend fdiffs [lindex $line 5]
1487 proc donefilediff {} {
1488 global fdiffids fdiffs treediffs findids
1489 global fdiffsneeded fdiffpos
1491 if {[info exists fdiffids]} {
1492 while {[lindex $fdiffsneeded $fdiffpos] ne $fdiffids
1493 && $fdiffpos < [llength $fdiffsneeded]} {
1494 # git-diff-tree doesn't output anything for a commit
1495 # which doesn't change anything
1496 set nullids [lindex $fdiffsneeded $fdiffpos]
1497 set treediffs($nullids) {}
1498 if {[info exists findids] && $nullids eq $findids} {
1506 if {![info exists treediffs($fdiffids)]} {
1507 set treediffs($fdiffids) $fdiffs
1509 if {[info exists findids] && $fdiffids eq $findids} {
1516 proc findcont {ids} {
1517 global findids treediffs parents nparents
1518 global ffileline findstartline finddidsel
1519 global lineid numcommits matchinglines findinprogress
1520 global findmergefiles
1522 set id [lindex $ids 0]
1523 set p [lindex $ids 1]
1524 set pi [lsearch -exact $parents($id) $p]
1527 if {$findmergefiles || $nparents($id) == 1} {
1528 if {![info exists treediffs($ids)]} {
1534 foreach f $treediffs($ids) {
1535 set x [findmatches $f]
1543 set pi $nparents($id)
1546 set pi $nparents($id)
1548 if {[incr pi] >= $nparents($id)} {
1550 if {[incr l] >= $numcommits} {
1553 if {$l == $findstartline} break
1556 set p [lindex $parents($id) $pi]
1557 set ids [list $id $p]
1565 # mark a commit as matching by putting a yellow background
1566 # behind the headline
1567 proc markheadline {l id} {
1568 global canv mainfont linehtag commitinfo
1570 set bbox [$canv bbox $linehtag($l)]
1571 set t [$canv create rect $bbox -outline {} -tags matches -fill yellow]
1575 # mark the bits of a headline, author or date that match a find string
1576 proc markmatches {canv l str tag matches font} {
1577 set bbox [$canv bbox $tag]
1578 set x0 [lindex $bbox 0]
1579 set y0 [lindex $bbox 1]
1580 set y1 [lindex $bbox 3]
1581 foreach match $matches {
1582 set start [lindex $match 0]
1583 set end [lindex $match 1]
1584 if {$start > $end} continue
1585 set xoff [font measure $font [string range $str 0 [expr $start-1]]]
1586 set xlen [font measure $font [string range $str 0 [expr $end]]]
1587 set t [$canv create rect [expr $x0+$xoff] $y0 [expr $x0+$xlen+2] $y1 \
1588 -outline {} -tags matches -fill yellow]
1593 proc unmarkmatches {} {
1594 global matchinglines findids
1595 allcanvs delete matches
1596 catch {unset matchinglines}
1597 catch {unset findids}
1600 proc selcanvline {w x y} {
1601 global canv canvy0 ctext linespc selectedline
1602 global lineid linehtag linentag linedtag rowtextx
1603 set ymax [lindex [$canv cget -scrollregion] 3]
1604 if {$ymax == {}} return
1605 set yfrac [lindex [$canv yview] 0]
1606 set y [expr {$y + $yfrac * $ymax}]
1607 set l [expr {int(($y - $canvy0) / $linespc + 0.5)}]
1612 if {![info exists rowtextx($l)] || $x < $rowtextx($l)} return
1618 proc selectline {l} {
1619 global canv canv2 canv3 ctext commitinfo selectedline
1620 global lineid linehtag linentag linedtag
1621 global canvy0 linespc parents nparents
1622 global cflist currentid sha1entry
1623 global commentend idtags
1625 if {![info exists lineid($l)] || ![info exists linehtag($l)]} return
1627 set t [eval $canv create rect [$canv bbox $linehtag($l)] -outline {{}} \
1628 -tags secsel -fill [$canv cget -selectbackground]]
1630 $canv2 delete secsel
1631 set t [eval $canv2 create rect [$canv2 bbox $linentag($l)] -outline {{}} \
1632 -tags secsel -fill [$canv2 cget -selectbackground]]
1634 $canv3 delete secsel
1635 set t [eval $canv3 create rect [$canv3 bbox $linedtag($l)] -outline {{}} \
1636 -tags secsel -fill [$canv3 cget -selectbackground]]
1638 set y [expr {$canvy0 + $l * $linespc}]
1639 set ymax [lindex [$canv cget -scrollregion] 3]
1640 set ytop [expr {$y - $linespc - 1}]
1641 set ybot [expr {$y + $linespc + 1}]
1642 set wnow [$canv yview]
1643 set wtop [expr [lindex $wnow 0] * $ymax]
1644 set wbot [expr [lindex $wnow 1] * $ymax]
1645 set wh [expr {$wbot - $wtop}]
1647 if {$ytop < $wtop} {
1648 if {$ybot < $wtop} {
1649 set newtop [expr {$y - $wh / 2.0}]
1652 if {$newtop > $wtop - $linespc} {
1653 set newtop [expr {$wtop - $linespc}]
1656 } elseif {$ybot > $wbot} {
1657 if {$ytop > $wbot} {
1658 set newtop [expr {$y - $wh / 2.0}]
1660 set newtop [expr {$ybot - $wh}]
1661 if {$newtop < $wtop + $linespc} {
1662 set newtop [expr {$wtop + $linespc}]
1666 if {$newtop != $wtop} {
1670 allcanvs yview moveto [expr $newtop * 1.0 / $ymax]
1676 $sha1entry delete 0 end
1677 $sha1entry insert 0 $id
1678 $sha1entry selection from 0
1679 $sha1entry selection to end
1681 $ctext conf -state normal
1682 $ctext delete 0.0 end
1683 $ctext mark set fmark.0 0.0
1684 $ctext mark gravity fmark.0 left
1685 set info $commitinfo($id)
1686 $ctext insert end "Author: [lindex $info 1] [lindex $info 2]\n"
1687 $ctext insert end "Committer: [lindex $info 3] [lindex $info 4]\n"
1688 if {[info exists idtags($id)]} {
1689 $ctext insert end "Tags:"
1690 foreach tag $idtags($id) {
1691 $ctext insert end " $tag"
1693 $ctext insert end "\n"
1695 $ctext insert end "\n"
1696 $ctext insert end [lindex $info 5]
1697 $ctext insert end "\n"
1698 $ctext tag delete Comments
1699 $ctext tag remove found 1.0 end
1700 $ctext conf -state disabled
1701 set commentend [$ctext index "end - 1c"]
1703 $cflist delete 0 end
1704 $cflist insert end "Comments"
1705 if {$nparents($id) == 1} {
1706 startdiff [concat $id $parents($id)]
1707 } elseif {$nparents($id) > 1} {
1712 proc selnextline {dir} {
1714 if {![info exists selectedline]} return
1715 set l [expr $selectedline + $dir]
1720 proc mergediff {id} {
1721 global parents diffmergeid diffmergegca mergefilelist diffpindex
1725 set diffmergegca [findgca $parents($id)]
1726 if {[info exists mergefilelist($id)]} {
1733 proc findgca {ids} {
1740 set gca [exec git-merge-base $gca $id]
1749 proc contmergediff {ids} {
1750 global diffmergeid diffpindex parents nparents diffmergegca
1751 global treediffs mergefilelist diffids
1753 # diff the child against each of the parents, and diff
1754 # each of the parents against the GCA.
1756 if {[lindex $ids 0] == $diffmergeid && $diffmergegca ne {}} {
1757 set ids [list [lindex $ids 1] $diffmergegca]
1759 if {[incr diffpindex] >= $nparents($diffmergeid)} break
1760 set p [lindex $parents($diffmergeid) $diffpindex]
1761 set ids [list $diffmergeid $p]
1763 if {![info exists treediffs($ids)]} {
1765 if {![info exists treepending]} {
1772 # If a file in some parent is different from the child and also
1773 # different from the GCA, then it's interesting.
1774 # If we don't have a GCA, then a file is interesting if it is
1775 # different from the child in all the parents.
1776 if {$diffmergegca ne {}} {
1778 foreach p $parents($diffmergeid) {
1779 set gcadiffs $treediffs([list $p $diffmergegca])
1780 foreach f $treediffs([list $diffmergeid $p]) {
1781 if {[lsearch -exact $files $f] < 0
1782 && [lsearch -exact $gcadiffs $f] >= 0} {
1787 set files [lsort $files]
1789 set p [lindex $parents($diffmergeid) 0]
1790 set files $treediffs([list $diffmergeid $p])
1791 for {set i 1} {$i < $nparents($diffmergeid) && $files ne {}} {incr i} {
1792 set p [lindex $parents($diffmergeid) $i]
1793 set df $treediffs([list $diffmergeid $p])
1796 if {[lsearch -exact $df $f] >= 0} {
1804 set mergefilelist($diffmergeid) $files
1810 proc showmergediff {} {
1811 global cflist diffmergeid mergefilelist parents
1812 global diffopts diffinhunk currentfile diffblocked
1813 global groupfilelast mergefds
1815 set files $mergefilelist($diffmergeid)
1817 $cflist insert end $f
1819 set env(GIT_DIFF_OPTS) $diffopts
1821 catch {unset currentfile}
1822 catch {unset currenthunk}
1823 catch {unset filelines}
1824 set groupfilelast -1
1825 foreach p $parents($diffmergeid) {
1826 set cmd [list | git-diff-tree -p $p $diffmergeid]
1827 set cmd [concat $cmd $mergefilelist($diffmergeid)]
1828 if {[catch {set f [open $cmd r]} err]} {
1829 error_popup "Error getting diffs: $err"
1836 set ids [list $diffmergeid $p]
1837 set mergefds($ids) $f
1838 set diffinhunk($ids) 0
1839 set diffblocked($ids) 0
1840 fconfigure $f -blocking 0
1841 fileevent $f readable [list getmergediffline $f $ids $diffmergeid]
1845 proc getmergediffline {f ids id} {
1846 global diffmergeid diffinhunk diffoldlines diffnewlines
1847 global currentfile currenthunk
1848 global diffoldstart diffnewstart diffoldlno diffnewlno
1849 global diffblocked mergefilelist
1850 global noldlines nnewlines difflcounts filelines
1852 set n [gets $f line]
1854 if {![eof $f]} return
1857 if {!([info exists diffmergeid] && $diffmergeid == $id)} {
1864 if {$diffinhunk($ids) != 0} {
1865 set fi $currentfile($ids)
1866 if {$n > 0 && [regexp {^[-+ \\]} $line match]} {
1867 # continuing an existing hunk
1868 set line [string range $line 1 end]
1869 set p [lindex $ids 1]
1870 if {$match eq "-" || $match eq " "} {
1871 set filelines($p,$fi,$diffoldlno($ids)) $line
1872 incr diffoldlno($ids)
1874 if {$match eq "+" || $match eq " "} {
1875 set filelines($id,$fi,$diffnewlno($ids)) $line
1876 incr diffnewlno($ids)
1878 if {$match eq " "} {
1879 if {$diffinhunk($ids) == 2} {
1880 lappend difflcounts($ids) \
1881 [list $noldlines($ids) $nnewlines($ids)]
1882 set noldlines($ids) 0
1883 set diffinhunk($ids) 1
1885 incr noldlines($ids)
1886 } elseif {$match eq "-" || $match eq "+"} {
1887 if {$diffinhunk($ids) == 1} {
1888 lappend difflcounts($ids) [list $noldlines($ids)]
1889 set noldlines($ids) 0
1890 set nnewlines($ids) 0
1891 set diffinhunk($ids) 2
1893 if {$match eq "-"} {
1894 incr noldlines($ids)
1896 incr nnewlines($ids)
1899 # and if it's \ No newline at end of line, then what?
1903 if {$diffinhunk($ids) == 1 && $noldlines($ids) != 0} {
1904 lappend difflcounts($ids) [list $noldlines($ids)]
1905 } elseif {$diffinhunk($ids) == 2
1906 && ($noldlines($ids) != 0 || $nnewlines($ids) != 0)} {
1907 lappend difflcounts($ids) [list $noldlines($ids) $nnewlines($ids)]
1909 set currenthunk($ids) [list $currentfile($ids) \
1910 $diffoldstart($ids) $diffnewstart($ids) \
1911 $diffoldlno($ids) $diffnewlno($ids) \
1913 set diffinhunk($ids) 0
1914 # -1 = need to block, 0 = unblocked, 1 = is blocked
1915 set diffblocked($ids) -1
1917 if {$diffblocked($ids) == -1} {
1918 fileevent $f readable {}
1919 set diffblocked($ids) 1
1925 if {!$diffblocked($ids)} {
1927 set currentfile($ids) [llength $mergefilelist($diffmergeid)]
1928 set currenthunk($ids) [list $currentfile($ids) 0 0 0 0 {}]
1931 } elseif {[regexp {^diff --git a/(.*) b/} $line match fname]} {
1932 # start of a new file
1933 set currentfile($ids) \
1934 [lsearch -exact $mergefilelist($diffmergeid) $fname]
1935 } elseif {[regexp {^@@ -([0-9]+),([0-9]+) \+([0-9]+),([0-9]+) @@(.*)} \
1936 $line match f1l f1c f2l f2c rest]} {
1937 if {[info exists currentfile($ids)] && $currentfile($ids) >= 0} {
1938 # start of a new hunk
1939 if {$f1l == 0 && $f1c == 0} {
1942 if {$f2l == 0 && $f2c == 0} {
1945 set diffinhunk($ids) 1
1946 set diffoldstart($ids) $f1l
1947 set diffnewstart($ids) $f2l
1948 set diffoldlno($ids) $f1l
1949 set diffnewlno($ids) $f2l
1950 set difflcounts($ids) {}
1951 set noldlines($ids) 0
1952 set nnewlines($ids) 0
1957 proc processhunks {} {
1958 global diffmergeid parents nparents currenthunk
1959 global mergefilelist diffblocked mergefds
1960 global grouphunks grouplinestart grouplineend groupfilenum
1962 set nfiles [llength $mergefilelist($diffmergeid)]
1966 # look for the earliest hunk
1967 foreach p $parents($diffmergeid) {
1968 set ids [list $diffmergeid $p]
1969 if {![info exists currenthunk($ids)]} return
1970 set i [lindex $currenthunk($ids) 0]
1971 set l [lindex $currenthunk($ids) 2]
1972 if {$i < $fi || ($i == $fi && $l < $lno)} {
1979 if {$fi < $nfiles} {
1980 set ids [list $diffmergeid $pi]
1981 set hunk $currenthunk($ids)
1982 unset currenthunk($ids)
1983 if {$diffblocked($ids) > 0} {
1984 fileevent $mergefds($ids) readable \
1985 [list getmergediffline $mergefds($ids) $ids $diffmergeid]
1987 set diffblocked($ids) 0
1989 if {[info exists groupfilenum] && $groupfilenum == $fi
1990 && $lno <= $grouplineend} {
1991 # add this hunk to the pending group
1992 lappend grouphunks($pi) $hunk
1993 set endln [lindex $hunk 4]
1994 if {$endln > $grouplineend} {
1995 set grouplineend $endln
2001 # succeeding stuff doesn't belong in this group, so
2002 # process the group now
2003 if {[info exists groupfilenum]} {
2009 if {$fi >= $nfiles} break
2012 set groupfilenum $fi
2013 set grouphunks($pi) [list $hunk]
2014 set grouplinestart $lno
2015 set grouplineend [lindex $hunk 4]
2019 proc processgroup {} {
2020 global groupfilelast groupfilenum difffilestart
2021 global mergefilelist diffmergeid ctext filelines
2022 global parents diffmergeid diffoffset
2023 global grouphunks grouplinestart grouplineend nparents
2026 $ctext conf -state normal
2029 if {$groupfilelast != $f} {
2030 $ctext insert end "\n"
2031 set here [$ctext index "end - 1c"]
2032 set difffilestart($f) $here
2033 set mark fmark.[expr {$f + 1}]
2034 $ctext mark set $mark $here
2035 $ctext mark gravity $mark left
2036 set header [lindex $mergefilelist($id) $f]
2037 set l [expr {(78 - [string length $header]) / 2}]
2038 set pad [string range "----------------------------------------" 1 $l]
2039 $ctext insert end "$pad $header $pad\n" filesep
2040 set groupfilelast $f
2041 foreach p $parents($id) {
2042 set diffoffset($p) 0
2046 $ctext insert end "@@" msep
2047 set nlines [expr {$grouplineend - $grouplinestart}]
2050 foreach p $parents($id) {
2051 set startline [expr {$grouplinestart + $diffoffset($p)}]
2052 set offset($p) $diffoffset($p)
2054 set nl $grouplinestart
2055 if {[info exists grouphunks($p)]} {
2056 foreach h $grouphunks($p) {
2059 for {} {$nl < $l} {incr nl} {
2060 set filelines($p,$f,$ol) $filelines($id,$f,$nl)
2064 foreach chunk [lindex $h 5] {
2065 if {[llength $chunk] == 2} {
2066 set olc [lindex $chunk 0]
2067 set nlc [lindex $chunk 1]
2068 set nnl [expr {$nl + $nlc}]
2069 lappend events [list $nl $nnl $pnum $olc $nlc]
2073 incr ol [lindex $chunk 0]
2074 incr nl [lindex $chunk 0]
2079 if {$nl < $grouplineend} {
2080 for {} {$nl < $grouplineend} {incr nl} {
2081 set filelines($p,$f,$ol) $filelines($id,$f,$nl)
2085 set nlines [expr {$ol - $startline}]
2086 $ctext insert end " -$startline,$nlines" msep
2090 set nlines [expr {$grouplineend - $grouplinestart}]
2091 $ctext insert end " +$grouplinestart,$nlines @@\n" msep
2093 set events [lsort -integer -index 0 $events]
2094 set nevents [llength $events]
2095 set nmerge $nparents($diffmergeid)
2097 set l $grouplinestart
2098 while {$i < $nevents} {
2099 set nl [lindex $events $i 0]
2101 $ctext insert end " $filelines($id,$f,$l)\n"
2104 set e [lindex $events $i]
2105 set enl [lindex $e 1]
2109 set pnum [lindex $e 2]
2110 set olc [lindex $e 3]
2111 set nlc [lindex $e 4]
2112 if {![info exists delta($pnum)]} {
2113 set delta($pnum) [expr {$olc - $nlc}]
2114 lappend active $pnum
2116 incr delta($pnum) [expr {$olc - $nlc}]
2118 if {[incr j] >= $nevents} break
2119 set e [lindex $events $j]
2120 if {[lindex $e 0] >= $enl} break
2121 if {[lindex $e 1] > $enl} {
2122 set enl [lindex $e 1]
2125 set nlc [expr {$enl - $l}]
2127 if {[llength $active] == $nmerge - 1} {
2128 for {set pnum 0} {$pnum < $nmerge} {incr pnum} {
2129 if {![info exists delta($pnum)]} {
2130 if {$pnum < $mergemax} {
2140 foreach p $parents($id) {
2142 if {![info exists delta($pnum)]} continue
2143 set olc [expr {$nlc + $delta($pnum)}]
2144 set ol [expr {$l + $diffoffset($p)}]
2145 incr diffoffset($p) $delta($pnum)
2147 for {} {$olc > 0} {incr olc -1} {
2148 $ctext insert end "-$filelines($p,$f,$ol)\n" m$pnum
2152 for {} {$nlc > 0} {incr nlc -1} {
2153 $ctext insert end "+$filelines($id,$f,$l)\n" $ncol
2158 while {$l < $grouplineend} {
2159 $ctext insert end " $filelines($id,$f,$l)\n"
2162 $ctext conf -state disabled
2165 proc startdiff {ids} {
2166 global treediffs diffids treepending diffmergeid
2169 catch {unset diffmergeid}
2170 if {![info exists treediffs($ids)]} {
2171 if {![info exists treepending]} {
2179 proc addtocflist {ids} {
2180 global treediffs cflist
2181 foreach f $treediffs($ids) {
2182 $cflist insert end $f
2187 proc gettreediffs {ids} {
2188 global treediff parents treepending
2189 set treepending $ids
2191 set id [lindex $ids 0]
2192 set p [lindex $ids 1]
2193 if [catch {set gdtf [open "|git-diff-tree -r $p $id" r]}] return
2194 fconfigure $gdtf -blocking 0
2195 fileevent $gdtf readable [list gettreediffline $gdtf $ids]
2198 proc gettreediffline {gdtf ids} {
2199 global treediff treediffs treepending diffids diffmergeid
2201 set n [gets $gdtf line]
2203 if {![eof $gdtf]} return
2205 set treediffs($ids) $treediff
2207 if {$ids != $diffids} {
2208 gettreediffs $diffids
2210 if {[info exists diffmergeid]} {
2218 set file [lindex $line 5]
2219 lappend treediff $file
2222 proc getblobdiffs {ids} {
2223 global diffopts blobdifffd diffids env curdifftag curtagstart
2224 global difffilestart nextupdate diffinhdr treediffs
2226 set id [lindex $ids 0]
2227 set p [lindex $ids 1]
2228 set env(GIT_DIFF_OPTS) $diffopts
2229 set cmd [list | git-diff-tree -r -p -C $p $id]
2230 if {[catch {set bdf [open $cmd r]} err]} {
2231 puts "error getting diffs: $err"
2235 fconfigure $bdf -blocking 0
2236 set blobdifffd($ids) $bdf
2237 set curdifftag Comments
2239 catch {unset difffilestart}
2240 fileevent $bdf readable [list getblobdiffline $bdf $diffids]
2241 set nextupdate [expr {[clock clicks -milliseconds] + 100}]
2244 proc getblobdiffline {bdf ids} {
2245 global diffids blobdifffd ctext curdifftag curtagstart
2246 global diffnexthead diffnextnote difffilestart
2247 global nextupdate diffinhdr treediffs
2250 set n [gets $bdf line]
2254 if {$ids == $diffids && $bdf == $blobdifffd($ids)} {
2255 $ctext tag add $curdifftag $curtagstart end
2260 if {$ids != $diffids || $bdf != $blobdifffd($ids)} {
2263 $ctext conf -state normal
2264 if {[regexp {^diff --git a/(.*) b/(.*)} $line match fname newname]} {
2265 # start of a new file
2266 $ctext insert end "\n"
2267 $ctext tag add $curdifftag $curtagstart end
2268 set curtagstart [$ctext index "end - 1c"]
2270 set here [$ctext index "end - 1c"]
2271 set i [lsearch -exact $treediffs($diffids) $fname]
2273 set difffilestart($i) $here
2275 $ctext mark set fmark.$i $here
2276 $ctext mark gravity fmark.$i left
2278 if {$newname != $fname} {
2279 set i [lsearch -exact $treediffs($diffids) $newname]
2281 set difffilestart($i) $here
2283 $ctext mark set fmark.$i $here
2284 $ctext mark gravity fmark.$i left
2287 set curdifftag "f:$fname"
2288 $ctext tag delete $curdifftag
2289 set l [expr {(78 - [string length $header]) / 2}]
2290 set pad [string range "----------------------------------------" 1 $l]
2291 $ctext insert end "$pad $header $pad\n" filesep
2293 } elseif {[regexp {^(---|\+\+\+)} $line]} {
2295 } elseif {[regexp {^@@ -([0-9]+),([0-9]+) \+([0-9]+),([0-9]+) @@(.*)} \
2296 $line match f1l f1c f2l f2c rest]} {
2298 $ctext insert end "\t" hunksep
2299 $ctext insert end " $f1l " d0 " $f2l " d1
2300 $ctext insert end " $rest \n" hunksep
2302 $ctext insert end "$line\n" hunksep
2306 set x [string range $line 0 0]
2307 if {$x == "-" || $x == "+"} {
2308 set tag [expr {$x == "+"}]
2310 set line [string range $line 1 end]
2312 $ctext insert end "$line\n" d$tag
2313 } elseif {$x == " "} {
2315 set line [string range $line 1 end]
2317 $ctext insert end "$line\n"
2318 } elseif {$diffinhdr || $x == "\\"} {
2319 # e.g. "\ No newline at end of file"
2320 $ctext insert end "$line\n" filesep
2322 # Something else we don't recognize
2323 if {$curdifftag != "Comments"} {
2324 $ctext insert end "\n"
2325 $ctext tag add $curdifftag $curtagstart end
2326 set curtagstart [$ctext index "end - 1c"]
2327 set curdifftag Comments
2329 $ctext insert end "$line\n" filesep
2332 $ctext conf -state disabled
2333 if {[clock clicks -milliseconds] >= $nextupdate} {
2335 fileevent $bdf readable {}
2337 fileevent $bdf readable "getblobdiffline $bdf {$ids}"
2342 global difffilestart ctext
2343 set here [$ctext index @0,0]
2344 for {set i 0} {[info exists difffilestart($i)]} {incr i} {
2345 if {[$ctext compare $difffilestart($i) > $here]} {
2346 if {![info exists pos]
2347 || [$ctext compare $difffilestart($i) < $pos]} {
2348 set pos $difffilestart($i)
2352 if {[info exists pos]} {
2357 proc listboxsel {} {
2358 global ctext cflist currentid
2359 if {![info exists currentid]} return
2360 set sel [lsort [$cflist curselection]]
2361 if {$sel eq {}} return
2362 set first [lindex $sel 0]
2363 catch {$ctext yview fmark.$first}
2367 global linespc charspc canvx0 canvy0 mainfont
2368 set linespc [font metrics $mainfont -linespace]
2369 set charspc [font measure $mainfont "m"]
2370 set canvy0 [expr 3 + 0.5 * $linespc]
2371 set canvx0 [expr 3 + 0.5 * $linespc]
2375 global selectedline stopped redisplaying phase
2376 if {$stopped > 1} return
2377 if {$phase == "getcommits"} return
2379 if {$phase == "drawgraph" || $phase == "incrdraw"} {
2386 proc incrfont {inc} {
2387 global mainfont namefont textfont selectedline ctext canv phase
2388 global stopped entries
2390 set mainfont [lreplace $mainfont 1 1 [expr {[lindex $mainfont 1] + $inc}]]
2391 set namefont [lreplace $namefont 1 1 [expr {[lindex $namefont 1] + $inc}]]
2392 set textfont [lreplace $textfont 1 1 [expr {[lindex $textfont 1] + $inc}]]
2394 $ctext conf -font $textfont
2395 $ctext tag conf filesep -font [concat $textfont bold]
2396 foreach e $entries {
2397 $e conf -font $mainfont
2399 if {$phase == "getcommits"} {
2400 $canv itemconf textitems -font $mainfont
2406 global sha1entry sha1string
2407 if {[string length $sha1string] == 40} {
2408 $sha1entry delete 0 end
2412 proc sha1change {n1 n2 op} {
2413 global sha1string currentid sha1but
2414 if {$sha1string == {}
2415 || ([info exists currentid] && $sha1string == $currentid)} {
2420 if {[$sha1but cget -state] == $state} return
2421 if {$state == "normal"} {
2422 $sha1but conf -state normal -relief raised -text "Goto: "
2424 $sha1but conf -state disabled -relief flat -text "SHA1 ID: "
2428 proc gotocommit {} {
2429 global sha1string currentid idline tagids
2430 global lineid numcommits
2432 if {$sha1string == {}
2433 || ([info exists currentid] && $sha1string == $currentid)} return
2434 if {[info exists tagids($sha1string)]} {
2435 set id $tagids($sha1string)
2437 set id [string tolower $sha1string]
2438 if {[regexp {^[0-9a-f]{4,39}$} $id]} {
2440 for {set l 0} {$l < $numcommits} {incr l} {
2441 if {[string match $id* $lineid($l)]} {
2442 lappend matches $lineid($l)
2445 if {$matches ne {}} {
2446 if {[llength $matches] > 1} {
2447 error_popup "Short SHA1 id $id is ambiguous"
2450 set id [lindex $matches 0]
2454 if {[info exists idline($id)]} {
2455 selectline $idline($id)
2458 if {[regexp {^[0-9a-fA-F]{4,}$} $sha1string]} {
2463 error_popup "$type $sha1string is not known"
2466 proc lineenter {x y id} {
2467 global hoverx hovery hoverid hovertimer
2468 global commitinfo canv
2470 if {![info exists commitinfo($id)]} return
2474 if {[info exists hovertimer]} {
2475 after cancel $hovertimer
2477 set hovertimer [after 500 linehover]
2481 proc linemotion {x y id} {
2482 global hoverx hovery hoverid hovertimer
2484 if {[info exists hoverid] && $id == $hoverid} {
2487 if {[info exists hovertimer]} {
2488 after cancel $hovertimer
2490 set hovertimer [after 500 linehover]
2494 proc lineleave {id} {
2495 global hoverid hovertimer canv
2497 if {[info exists hoverid] && $id == $hoverid} {
2499 if {[info exists hovertimer]} {
2500 after cancel $hovertimer
2508 global hoverx hovery hoverid hovertimer
2509 global canv linespc lthickness
2510 global commitinfo mainfont
2512 set text [lindex $commitinfo($hoverid) 0]
2513 set ymax [lindex [$canv cget -scrollregion] 3]
2514 if {$ymax == {}} return
2515 set yfrac [lindex [$canv yview] 0]
2516 set x [expr {$hoverx + 2 * $linespc}]
2517 set y [expr {$hovery + $yfrac * $ymax - $linespc / 2}]
2518 set x0 [expr {$x - 2 * $lthickness}]
2519 set y0 [expr {$y - 2 * $lthickness}]
2520 set x1 [expr {$x + [font measure $mainfont $text] + 2 * $lthickness}]
2521 set y1 [expr {$y + $linespc + 2 * $lthickness}]
2522 set t [$canv create rectangle $x0 $y0 $x1 $y1 \
2523 -fill \#ffff80 -outline black -width 1 -tags hover]
2525 set t [$canv create text $x $y -anchor nw -text $text -tags hover]
2529 proc lineclick {x y id} {
2530 global ctext commitinfo children cflist canv
2534 # fill the details pane with info about this line
2535 $ctext conf -state normal
2536 $ctext delete 0.0 end
2537 $ctext insert end "Parent:\n "
2538 catch {destroy $ctext.$id}
2539 button $ctext.$id -text "Go:" -command "selbyid $id" \
2541 $ctext window create end -window $ctext.$id -align center
2542 set info $commitinfo($id)
2543 $ctext insert end "\t[lindex $info 0]\n"
2544 $ctext insert end "\tAuthor:\t[lindex $info 1]\n"
2545 $ctext insert end "\tDate:\t[lindex $info 2]\n"
2546 $ctext insert end "\tID:\t$id\n"
2547 if {[info exists children($id)]} {
2548 $ctext insert end "\nChildren:"
2549 foreach child $children($id) {
2550 $ctext insert end "\n "
2551 catch {destroy $ctext.$child}
2552 button $ctext.$child -text "Go:" -command "selbyid $child" \
2554 $ctext window create end -window $ctext.$child -align center
2555 set info $commitinfo($child)
2556 $ctext insert end "\t[lindex $info 0]"
2559 $ctext conf -state disabled
2561 $cflist delete 0 end
2566 if {[info exists idline($id)]} {
2567 selectline $idline($id)
2573 if {![info exists startmstime]} {
2574 set startmstime [clock clicks -milliseconds]
2576 return [format "%.3f" [expr {([clock click -milliseconds] - $startmstime) / 1000.0}]]
2579 proc rowmenu {x y id} {
2580 global rowctxmenu idline selectedline rowmenuid
2582 if {![info exists selectedline] || $idline($id) eq $selectedline} {
2587 $rowctxmenu entryconfigure 0 -state $state
2588 $rowctxmenu entryconfigure 1 -state $state
2589 $rowctxmenu entryconfigure 2 -state $state
2591 tk_popup $rowctxmenu $x $y
2594 proc diffvssel {dirn} {
2595 global rowmenuid selectedline lineid
2599 if {![info exists selectedline]} return
2601 set oldid $lineid($selectedline)
2602 set newid $rowmenuid
2604 set oldid $rowmenuid
2605 set newid $lineid($selectedline)
2607 $ctext conf -state normal
2608 $ctext delete 0.0 end
2609 $ctext mark set fmark.0 0.0
2610 $ctext mark gravity fmark.0 left
2611 $cflist delete 0 end
2612 $cflist insert end "Top"
2613 $ctext insert end "From $oldid\n "
2614 $ctext insert end [lindex $commitinfo($oldid) 0]
2615 $ctext insert end "\n\nTo $newid\n "
2616 $ctext insert end [lindex $commitinfo($newid) 0]
2617 $ctext insert end "\n"
2618 $ctext conf -state disabled
2619 $ctext tag delete Comments
2620 $ctext tag remove found 1.0 end
2621 startdiff $newid [list $oldid]
2625 global rowmenuid currentid commitinfo patchtop patchnum
2627 if {![info exists currentid]} return
2628 set oldid $currentid
2629 set oldhead [lindex $commitinfo($oldid) 0]
2630 set newid $rowmenuid
2631 set newhead [lindex $commitinfo($newid) 0]
2634 catch {destroy $top}
2636 label $top.title -text "Generate patch"
2637 grid $top.title - -pady 10
2638 label $top.from -text "From:"
2639 entry $top.fromsha1 -width 40 -relief flat
2640 $top.fromsha1 insert 0 $oldid
2641 $top.fromsha1 conf -state readonly
2642 grid $top.from $top.fromsha1 -sticky w
2643 entry $top.fromhead -width 60 -relief flat
2644 $top.fromhead insert 0 $oldhead
2645 $top.fromhead conf -state readonly
2646 grid x $top.fromhead -sticky w
2647 label $top.to -text "To:"
2648 entry $top.tosha1 -width 40 -relief flat
2649 $top.tosha1 insert 0 $newid
2650 $top.tosha1 conf -state readonly
2651 grid $top.to $top.tosha1 -sticky w
2652 entry $top.tohead -width 60 -relief flat
2653 $top.tohead insert 0 $newhead
2654 $top.tohead conf -state readonly
2655 grid x $top.tohead -sticky w
2656 button $top.rev -text "Reverse" -command mkpatchrev -padx 5
2657 grid $top.rev x -pady 10
2658 label $top.flab -text "Output file:"
2659 entry $top.fname -width 60
2660 $top.fname insert 0 [file normalize "patch$patchnum.patch"]
2662 grid $top.flab $top.fname -sticky w
2664 button $top.buts.gen -text "Generate" -command mkpatchgo
2665 button $top.buts.can -text "Cancel" -command mkpatchcan
2666 grid $top.buts.gen $top.buts.can
2667 grid columnconfigure $top.buts 0 -weight 1 -uniform a
2668 grid columnconfigure $top.buts 1 -weight 1 -uniform a
2669 grid $top.buts - -pady 10 -sticky ew
2673 proc mkpatchrev {} {
2676 set oldid [$patchtop.fromsha1 get]
2677 set oldhead [$patchtop.fromhead get]
2678 set newid [$patchtop.tosha1 get]
2679 set newhead [$patchtop.tohead get]
2680 foreach e [list fromsha1 fromhead tosha1 tohead] \
2681 v [list $newid $newhead $oldid $oldhead] {
2682 $patchtop.$e conf -state normal
2683 $patchtop.$e delete 0 end
2684 $patchtop.$e insert 0 $v
2685 $patchtop.$e conf -state readonly
2692 set oldid [$patchtop.fromsha1 get]
2693 set newid [$patchtop.tosha1 get]
2694 set fname [$patchtop.fname get]
2695 if {[catch {exec git-diff-tree -p $oldid $newid >$fname &} err]} {
2696 error_popup "Error creating patch: $err"
2698 catch {destroy $patchtop}
2702 proc mkpatchcan {} {
2705 catch {destroy $patchtop}
2710 global rowmenuid mktagtop commitinfo
2714 catch {destroy $top}
2716 label $top.title -text "Create tag"
2717 grid $top.title - -pady 10
2718 label $top.id -text "ID:"
2719 entry $top.sha1 -width 40 -relief flat
2720 $top.sha1 insert 0 $rowmenuid
2721 $top.sha1 conf -state readonly
2722 grid $top.id $top.sha1 -sticky w
2723 entry $top.head -width 60 -relief flat
2724 $top.head insert 0 [lindex $commitinfo($rowmenuid) 0]
2725 $top.head conf -state readonly
2726 grid x $top.head -sticky w
2727 label $top.tlab -text "Tag name:"
2728 entry $top.tag -width 60
2729 grid $top.tlab $top.tag -sticky w
2731 button $top.buts.gen -text "Create" -command mktaggo
2732 button $top.buts.can -text "Cancel" -command mktagcan
2733 grid $top.buts.gen $top.buts.can
2734 grid columnconfigure $top.buts 0 -weight 1 -uniform a
2735 grid columnconfigure $top.buts 1 -weight 1 -uniform a
2736 grid $top.buts - -pady 10 -sticky ew
2741 global mktagtop env tagids idtags
2742 global idpos idline linehtag canv selectedline
2744 set id [$mktagtop.sha1 get]
2745 set tag [$mktagtop.tag get]
2747 error_popup "No tag name specified"
2750 if {[info exists tagids($tag)]} {
2751 error_popup "Tag \"$tag\" already exists"
2756 if {[info exists env(GIT_DIR)]} {
2757 set dir $env(GIT_DIR)
2759 set fname [file join $dir "refs/tags" $tag]
2760 set f [open $fname w]
2764 error_popup "Error creating tag: $err"
2768 set tagids($tag) $id
2769 lappend idtags($id) $tag
2770 $canv delete tag.$id
2771 set xt [eval drawtags $id $idpos($id)]
2772 $canv coords $linehtag($idline($id)) $xt [lindex $idpos($id) 2]
2773 if {[info exists selectedline] && $selectedline == $idline($id)} {
2774 selectline $selectedline
2781 catch {destroy $mktagtop}
2790 proc writecommit {} {
2791 global rowmenuid wrcomtop commitinfo wrcomcmd
2793 set top .writecommit
2795 catch {destroy $top}
2797 label $top.title -text "Write commit to file"
2798 grid $top.title - -pady 10
2799 label $top.id -text "ID:"
2800 entry $top.sha1 -width 40 -relief flat
2801 $top.sha1 insert 0 $rowmenuid
2802 $top.sha1 conf -state readonly
2803 grid $top.id $top.sha1 -sticky w
2804 entry $top.head -width 60 -relief flat
2805 $top.head insert 0 [lindex $commitinfo($rowmenuid) 0]
2806 $top.head conf -state readonly
2807 grid x $top.head -sticky w
2808 label $top.clab -text "Command:"
2809 entry $top.cmd -width 60 -textvariable wrcomcmd
2810 grid $top.clab $top.cmd -sticky w -pady 10
2811 label $top.flab -text "Output file:"
2812 entry $top.fname -width 60
2813 $top.fname insert 0 [file normalize "commit-[string range $rowmenuid 0 6]"]
2814 grid $top.flab $top.fname -sticky w
2816 button $top.buts.gen -text "Write" -command wrcomgo
2817 button $top.buts.can -text "Cancel" -command wrcomcan
2818 grid $top.buts.gen $top.buts.can
2819 grid columnconfigure $top.buts 0 -weight 1 -uniform a
2820 grid columnconfigure $top.buts 1 -weight 1 -uniform a
2821 grid $top.buts - -pady 10 -sticky ew
2828 set id [$wrcomtop.sha1 get]
2829 set cmd "echo $id | [$wrcomtop.cmd get]"
2830 set fname [$wrcomtop.fname get]
2831 if {[catch {exec sh -c $cmd >$fname &} err]} {
2832 error_popup "Error writing commit: $err"
2834 catch {destroy $wrcomtop}
2841 catch {destroy $wrcomtop}
2854 set diffopts "-U 5 -p"
2855 set wrcomcmd "git-diff-tree --stdin -p --pretty"
2857 set mainfont {Helvetica 9}
2858 set textfont {Courier 9}
2859 set findmergefiles 0
2862 set colors {green red blue magenta darkgrey brown orange}
2864 catch {source ~/.gitk}
2866 set namefont $mainfont
2868 lappend namefont bold
2873 switch -regexp -- $arg {
2875 "^-b" { set boldnames 1 }
2876 "^-d" { set datemode 1 }
2878 lappend revtreeargs $arg
2890 getcommits $revtreeargs