2 # Tcl ignores the next line -*- tcl -*- \
5 # Copyright (C) 2005-2006 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.
12 if {[info exists env
(GIT_DIR
)]} {
15 return [exec git rev-parse
--git-dir]
19 # A simple scheduler for compute-intensive stuff.
20 # The aim is to make sure that event handlers for GUI actions can
21 # run at least every 50-100 ms. Unfortunately fileevent handlers are
22 # run before X event handlers, so reading from a fast source can
23 # make the GUI completely unresponsive.
28 if {[info exists isonrunq
($script)]} return
32 lappend runq
[list
{} $script]
33 set isonrunq
($script) 1
36 proc filerun
{fd
script} {
37 fileevent
$fd readable
[list filereadable
$fd $script]
40 proc filereadable
{fd
script} {
43 fileevent
$fd readable
{}
47 lappend runq
[list
$fd $script]
53 for {set i
0} {$i < [llength
$runq]} {} {
54 if {[lindex
$runq $i 0] eq
$fd} {
55 set runq
[lreplace
$runq $i $i]
65 set tstart
[clock clicks
-milliseconds]
67 while {[llength
$runq] > 0} {
68 set fd
[lindex
$runq 0 0]
69 set script [lindex
$runq 0 1]
70 set repeat
[eval $script]
71 set t1
[clock clicks
-milliseconds]
72 set t
[expr {$t1 - $t0}]
73 set runq
[lrange
$runq 1 end
]
74 if {$repeat ne
{} && $repeat} {
75 if {$fd eq
{} ||
$repeat == 2} {
76 # script returns 1 if it wants to be readded
77 # file readers return 2 if they could do more straight away
78 lappend runq
[list
$fd $script]
80 fileevent
$fd readable
[list filereadable
$fd $script]
82 } elseif
{$fd eq
{}} {
83 unset isonrunq
($script)
86 if {$t1 - $tstart >= 80} break
93 # Start off a git rev-list process and arrange to read its output
94 proc start_rev_list
{view
} {
96 global commfd leftover tclencoding datemode
97 global viewargs viewfiles commitidx viewcomplete vnextroot
98 global showlocalchanges commitinterest mainheadid
99 global progressdirn progresscoords proglastnc curview
100 global viewincl viewactive loginstance viewinstances
102 set startmsecs
[clock clicks
-milliseconds]
103 set commitidx
($view) 0
104 set viewcomplete
($view) 0
105 set viewactive
($view) 1
106 set vnextroot
($view) 0
109 set commits
[eval exec git rev-parse
--default HEAD
--revs-only \
111 set viewincl
($view) {}
113 if {![string match
"^*" $c]} {
114 lappend viewincl
($view) $c
118 set fd
[open
[concat | git log
--no-color -z --pretty=raw
--parents \
119 --boundary $commits "--" $viewfiles($view)] r
]
121 error_popup
"Error executing git log: $err"
124 set i
[incr loginstance
]
125 set viewinstances
($view) [list
$i]
128 if {$showlocalchanges} {
129 lappend commitinterest
($mainheadid) {dodiffindex
}
131 fconfigure
$fd -blocking 0 -translation lf
-eofchar {}
132 if {$tclencoding != {}} {
133 fconfigure
$fd -encoding $tclencoding
135 filerun
$fd [list getcommitlines
$fd $i $view]
136 nowbusy
$view "Reading"
137 if {$view == $curview} {
139 set progresscoords
{0 0}
144 proc stop_rev_list
{view
} {
145 global commfd viewinstances leftover
147 foreach inst
$viewinstances($view) {
148 set fd
$commfd($inst)
156 unset leftover
($inst)
158 set viewinstances
($view) {}
165 start_rev_list
$curview
166 show_status
"Reading commits..."
169 proc updatecommits
{} {
170 global curview viewargs viewfiles viewincl viewinstances
171 global viewactive viewcomplete loginstance tclencoding mainheadid
172 global varcid startmsecs commfd showneartags showlocalchanges leftover
174 if {$showlocalchanges && [commitinview
$mainheadid $curview]} {
178 set commits
[exec git rev-parse
--default HEAD
--revs-only \
183 if {[string match
"^*" $c]} {
186 if {!([info exists varcid
($view,$c)] ||
187 [lsearch
-exact $viewincl($view) $c] >= 0)} {
195 foreach id
$viewincl($view) {
198 set viewincl
($view) [concat
$viewincl($view) $pos]
200 set fd
[open
[concat | git log
--no-color -z --pretty=raw
--parents \
201 --boundary $pos $neg "--" $viewfiles($view)] r
]
203 error_popup
"Error executing git log: $err"
206 if {$viewactive($view) == 0} {
207 set startmsecs
[clock clicks
-milliseconds]
209 set i
[incr loginstance
]
210 lappend viewinstances
($view) $i
213 fconfigure
$fd -blocking 0 -translation lf
-eofchar {}
214 if {$tclencoding != {}} {
215 fconfigure
$fd -encoding $tclencoding
217 filerun
$fd [list getcommitlines
$fd $i $view]
218 incr viewactive
($view)
219 set viewcomplete
($view) 0
220 nowbusy
$view "Reading"
228 proc reloadcommits
{} {
229 global curview viewcomplete selectedline currentid thickerline
230 global showneartags treediffs commitinterest cached_commitrow
231 global progresscoords
233 if {!$viewcomplete($curview)} {
234 stop_rev_list
$curview
235 set progresscoords
{0 0}
239 catch
{unset selectedline
}
240 catch
{unset currentid
}
241 catch
{unset thickerline
}
242 catch
{unset treediffs
}
249 catch
{unset commitinterest
}
250 catch
{unset cached_commitrow
}
255 # This makes a string representation of a positive integer which
256 # sorts as a string in numerical order
259 return [format
"%x" $n]
260 } elseif
{$n < 256} {
261 return [format
"x%.2x" $n]
262 } elseif
{$n < 65536} {
263 return [format
"y%.4x" $n]
265 return [format
"z%.8x" $n]
268 # Procedures used in reordering commits from git log (without
269 # --topo-order) into the order for display.
271 proc varcinit
{view
} {
272 global varcstart vupptr vdownptr vleftptr vbackptr varctok varcrow
273 global vtokmod varcmod vrowmod varcix vlastins
275 set varcstart
($view) {{}}
276 set vupptr
($view) {0}
277 set vdownptr
($view) {0}
278 set vleftptr
($view) {0}
279 set vbackptr
($view) {0}
280 set varctok
($view) {{}}
281 set varcrow
($view) {{}}
282 set vtokmod
($view) {}
285 set varcix
($view) {{}}
286 set vlastins
($view) {0}
289 proc resetvarcs
{view
} {
290 global varcid varccommits parents children vseedcount ordertok
292 foreach vid
[array names varcid
$view,*] {
297 # some commits might have children but haven't been seen yet
298 foreach vid
[array names children
$view,*] {
301 foreach va
[array names varccommits
$view,*] {
302 unset varccommits
($va)
304 foreach vd
[array names vseedcount
$view,*] {
305 unset vseedcount
($vd)
307 catch
{unset ordertok
}
310 proc newvarc
{view id
} {
311 global varcid varctok parents children datemode
312 global vupptr vdownptr vleftptr vbackptr varcrow varcix varcstart
313 global commitdata commitinfo vseedcount varccommits vlastins
315 set a
[llength
$varctok($view)]
317 if {[llength
$children($vid)] == 0 ||
$datemode} {
318 if {![info exists commitinfo
($id)]} {
319 parsecommit
$id $commitdata($id) 1
321 set cdate
[lindex
$commitinfo($id) 4]
322 if {![string is integer
-strict $cdate]} {
325 if {![info exists vseedcount
($view,$cdate)]} {
326 set vseedcount
($view,$cdate) -1
328 set c
[incr vseedcount
($view,$cdate)]
329 set cdate
[expr {$cdate ^
0xffffffff}]
330 set tok
"s[strrep $cdate][strrep $c]"
335 if {[llength
$children($vid)] > 0} {
336 set kid
[lindex
$children($vid) end
]
337 set k
$varcid($view,$kid)
338 if {[string compare
[lindex
$varctok($view) $k] $tok] > 0} {
341 set tok
[lindex
$varctok($view) $k]
345 set i
[lsearch
-exact $parents($view,$ki) $id]
346 set j
[expr {[llength
$parents($view,$ki)] - 1 - $i}]
347 append tok
[strrep
$j]
349 set c
[lindex
$vlastins($view) $ka]
350 if {$c == 0 ||
[string compare
$tok [lindex
$varctok($view) $c]] < 0} {
352 set b
[lindex
$vdownptr($view) $ka]
354 set b
[lindex
$vleftptr($view) $c]
356 while {$b != 0 && [string compare
$tok [lindex
$varctok($view) $b]] >= 0} {
358 set b
[lindex
$vleftptr($view) $c]
361 lset vdownptr
($view) $ka $a
362 lappend vbackptr
($view) 0
364 lset vleftptr
($view) $c $a
365 lappend vbackptr
($view) $c
367 lset vlastins
($view) $ka $a
368 lappend vupptr
($view) $ka
369 lappend vleftptr
($view) $b
371 lset vbackptr
($view) $b $a
373 lappend varctok
($view) $tok
374 lappend varcstart
($view) $id
375 lappend vdownptr
($view) 0
376 lappend varcrow
($view) {}
377 lappend varcix
($view) {}
378 set varccommits
($view,$a) {}
379 lappend vlastins
($view) 0
383 proc splitvarc
{p v
} {
384 global varcid varcstart varccommits varctok
385 global vupptr vdownptr vleftptr vbackptr varcix varcrow vlastins
387 set oa
$varcid($v,$p)
388 set ac
$varccommits($v,$oa)
389 set i
[lsearch
-exact $varccommits($v,$oa) $p]
391 set na
[llength
$varctok($v)]
392 # "%" sorts before "0"...
393 set tok
"[lindex $varctok($v) $oa]%[strrep $i]"
394 lappend varctok
($v) $tok
395 lappend varcrow
($v) {}
396 lappend varcix
($v) {}
397 set varccommits
($v,$oa) [lrange
$ac 0 [expr {$i - 1}]]
398 set varccommits
($v,$na) [lrange
$ac $i end
]
399 lappend varcstart
($v) $p
400 foreach id
$varccommits($v,$na) {
401 set varcid
($v,$id) $na
403 lappend vdownptr
($v) [lindex
$vdownptr($v) $oa]
404 lset vdownptr
($v) $oa $na
405 lappend vupptr
($v) $oa
406 lappend vleftptr
($v) 0
407 lappend vbackptr
($v) 0
408 lappend vlastins
($v) 0
409 for {set b
[lindex
$vdownptr($v) $na]} {$b != 0} {set b
[lindex
$vleftptr($v) $b]} {
410 lset vupptr
($v) $b $na
414 proc renumbervarc
{a v
} {
415 global parents children varctok varcstart varccommits
416 global vupptr vdownptr vleftptr vbackptr vlastins varcid vtokmod datemode
418 set t1
[clock clicks
-milliseconds]
424 if {[info exists isrelated
($a)]} {
426 set id
[lindex
$varccommits($v,$a) end
]
427 foreach p
$parents($v,$id) {
428 if {[info exists varcid
($v,$p)]} {
429 set isrelated
($varcid($v,$p)) 1
434 set b
[lindex
$vdownptr($v) $a]
437 set b
[lindex
$vleftptr($v) $a]
439 set a
[lindex
$vupptr($v) $a]
445 if {![info exists kidchanged
($a)]} continue
446 set id
[lindex
$varcstart($v) $a]
447 if {[llength
$children($v,$id)] > 1} {
448 set children
($v,$id) [lsort
-command [list vtokcmp
$v] \
451 set oldtok
[lindex
$varctok($v) $a]
458 if {[llength
$children($v,$id)] > 0} {
459 set kid
[lindex
$children($v,$id) end
]
460 set k
$varcid($v,$kid)
461 if {[string compare
[lindex
$varctok($v) $k] $tok] > 0} {
464 set tok
[lindex
$varctok($v) $k]
468 set i
[lsearch
-exact $parents($v,$ki) $id]
469 set j
[expr {[llength
$parents($v,$ki)] - 1 - $i}]
470 append tok
[strrep
$j]
472 if {$tok eq
$oldtok} {
475 set id
[lindex
$varccommits($v,$a) end
]
476 foreach p
$parents($v,$id) {
477 if {[info exists varcid
($v,$p)]} {
478 set kidchanged
($varcid($v,$p)) 1
483 lset varctok
($v) $a $tok
484 set b
[lindex
$vupptr($v) $a]
486 if {[string compare
[lindex
$varctok($v) $ka] $vtokmod($v)] < 0} {
489 if {[string compare
[lindex
$varctok($v) $b] $vtokmod($v)] < 0} {
492 set c
[lindex
$vbackptr($v) $a]
493 set d
[lindex
$vleftptr($v) $a]
495 lset vdownptr
($v) $b $d
497 lset vleftptr
($v) $c $d
500 lset vbackptr
($v) $d $c
502 lset vupptr
($v) $a $ka
503 set c
[lindex
$vlastins($v) $ka]
505 [string compare
$tok [lindex
$varctok($v) $c]] < 0} {
507 set b
[lindex
$vdownptr($v) $ka]
509 set b
[lindex
$vleftptr($v) $c]
512 [string compare
$tok [lindex
$varctok($v) $b]] >= 0} {
514 set b
[lindex
$vleftptr($v) $c]
517 lset vdownptr
($v) $ka $a
518 lset vbackptr
($v) $a 0
520 lset vleftptr
($v) $c $a
521 lset vbackptr
($v) $a $c
523 lset vleftptr
($v) $a $b
525 lset vbackptr
($v) $b $a
527 lset vlastins
($v) $ka $a
530 foreach id
[array names sortkids
] {
531 if {[llength
$children($v,$id)] > 1} {
532 set children
($v,$id) [lsort
-command [list vtokcmp
$v] \
536 set t2
[clock clicks
-milliseconds]
537 #puts "renumbervarc did [llength $todo] of $ntot arcs in [expr {$t2-$t1}]ms"
540 proc fix_reversal
{p a v
} {
541 global varcid varcstart varctok vupptr
543 set pa
$varcid($v,$p)
544 if {$p ne
[lindex
$varcstart($v) $pa]} {
546 set pa
$varcid($v,$p)
548 # seeds always need to be renumbered
549 if {[lindex
$vupptr($v) $pa] == 0 ||
550 [string compare
[lindex
$varctok($v) $a] \
551 [lindex
$varctok($v) $pa]] > 0} {
556 proc insertrow
{id p v
} {
557 global varcid varccommits parents children cmitlisted
558 global commitidx varctok vtokmod
561 set i
[lsearch
-exact $varccommits($v,$a) $p]
563 puts
"oops: insertrow can't find [shortids $p] on arc $a"
566 set children
($v,$id) {}
567 set parents
($v,$id) [list
$p]
568 set varcid
($v,$id) $a
569 lappend children
($v,$p) $id
570 set cmitlisted
($v,$id) 1
572 # note we deliberately don't update varcstart($v) even if $i == 0
573 set varccommits
($v,$a) [linsert
$varccommits($v,$a) $i $id]
574 if {[string compare
[lindex
$varctok($v) $a] $vtokmod($v)] < 0} {
580 proc removerow
{id v
} {
581 global varcid varccommits parents children commitidx
582 global varctok vtokmod cmitlisted
584 if {[llength
$parents($v,$id)] != 1} {
585 puts
"oops: removerow [shortids $id] has [llength $parents($v,$id)] parents"
588 set p
[lindex
$parents($v,$id) 0]
589 set a
$varcid($v,$id)
590 set i
[lsearch
-exact $varccommits($v,$a) $id]
592 puts
"oops: removerow can't find [shortids $id] on arc $a"
596 set varccommits
($v,$a) [lreplace
$varccommits($v,$a) $i $i]
597 unset parents
($v,$id)
598 unset children
($v,$id)
599 unset cmitlisted
($v,$id)
600 incr commitidx
($v) -1
601 set j
[lsearch
-exact $children($v,$p) $id]
603 set children
($v,$p) [lreplace
$children($v,$p) $j $j]
605 if {[string compare
[lindex
$varctok($v) $a] $vtokmod($v)] < 0} {
611 proc vtokcmp
{v a b
} {
612 global varctok varcid
614 return [string compare
[lindex
$varctok($v) $varcid($v,$a)] \
615 [lindex
$varctok($v) $varcid($v,$b)]]
618 proc modify_arc
{v a
{lim
{}}} {
619 global varctok vtokmod varcmod varcrow vupptr curview vrowmod varccommits
620 global vhighlights nhighlights fhighlights rhighlights
622 set vtokmod
($v) [lindex
$varctok($v) $a]
624 if {$v == $curview} {
625 while {$a != 0 && [lindex
$varcrow($v) $a] eq
{}} {
626 set a
[lindex
$vupptr($v) $a]
632 set lim
[llength
$varccommits($v,$a)]
634 set r
[expr {[lindex
$varcrow($v) $a] + $lim}]
639 catch
{unset nhighlights
}
640 catch
{unset fhighlights
}
641 catch
{unset vhighlights
}
642 catch
{unset rhighlights
}
645 proc update_arcrows
{v
} {
646 global vtokmod varcmod vrowmod varcrow commitidx currentid selectedline
647 global varcid vrownum varcorder varcix varccommits
648 global vupptr vdownptr vleftptr varctok
649 global displayorder parentlist curview cached_commitrow
651 set narctot
[expr {[llength
$varctok($v)] - 1}]
653 while {$a != 0 && [lindex
$varcix($v) $a] eq
{}} {
654 # go up the tree until we find something that has a row number,
655 # or we get to a seed
656 set a
[lindex
$vupptr($v) $a]
659 set a
[lindex
$vdownptr($v) 0]
662 set varcorder
($v) [list
$a]
664 lset varcrow
($v) $a 0
668 set arcn
[lindex
$varcix($v) $a]
669 # see if a is the last arc; if so, nothing to do
670 if {$arcn == $narctot - 1} {
673 if {[llength
$vrownum($v)] > $arcn + 1} {
674 set vrownum
($v) [lrange
$vrownum($v) 0 $arcn]
675 set varcorder
($v) [lrange
$varcorder($v) 0 $arcn]
677 set row
[lindex
$varcrow($v) $a]
679 if {$v == $curview} {
680 if {[llength
$displayorder] > $vrowmod($v)} {
681 set displayorder
[lrange
$displayorder 0 [expr {$vrowmod($v) - 1}]]
682 set parentlist
[lrange
$parentlist 0 [expr {$vrowmod($v) - 1}]]
684 catch
{unset cached_commitrow
}
688 incr row
[llength
$varccommits($v,$a)]
689 # go down if possible
690 set b
[lindex
$vdownptr($v) $a]
692 # if not, go left, or go up until we can go left
694 set b
[lindex
$vleftptr($v) $a]
696 set a
[lindex
$vupptr($v) $a]
702 lappend vrownum
($v) $row
703 lappend varcorder
($v) $a
704 lset varcix
($v) $a $arcn
705 lset varcrow
($v) $a $row
707 set vtokmod
($v) [lindex
$varctok($v) $p]
710 if {[info exists currentid
]} {
711 set selectedline
[rowofcommit
$currentid]
715 # Test whether view $v contains commit $id
716 proc commitinview
{id v
} {
719 return [info exists varcid
($v,$id)]
722 # Return the row number for commit $id in the current view
723 proc rowofcommit
{id
} {
724 global varcid varccommits varcrow curview cached_commitrow
725 global varctok vtokmod
727 if {[info exists cached_commitrow
($id)]} {
728 return $cached_commitrow($id)
731 if {![info exists varcid
($v,$id)]} {
732 puts
"oops rowofcommit no arc for [shortids $id]"
735 set a
$varcid($v,$id)
736 if {[string compare
[lindex
$varctok($v) $a] $vtokmod($v)] > 0} {
739 set i
[lsearch
-exact $varccommits($v,$a) $id]
741 puts
"oops didn't find commit [shortids $id] in arc $a"
744 incr i
[lindex
$varcrow($v) $a]
745 set cached_commitrow
($id) $i
749 proc bsearch
{l elt
} {
750 if {[llength
$l] == 0 ||
$elt <= [lindex
$l 0]} {
755 while {$hi - $lo > 1} {
756 set mid
[expr {int
(($lo + $hi) / 2)}]
757 set t
[lindex
$l $mid]
760 } elseif
{$elt > $t} {
769 # Make sure rows $start..$end-1 are valid in displayorder and parentlist
770 proc make_disporder
{start end
} {
771 global vrownum curview commitidx displayorder parentlist
772 global varccommits varcorder parents vrowmod varcrow
773 global d_valid_start d_valid_end
775 if {$end > $vrowmod($curview)} {
776 update_arcrows
$curview
778 set ai
[bsearch
$vrownum($curview) $start]
779 set start
[lindex
$vrownum($curview) $ai]
780 set narc
[llength
$vrownum($curview)]
781 for {set r
$start} {$ai < $narc && $r < $end} {incr ai
} {
782 set a
[lindex
$varcorder($curview) $ai]
783 set l
[llength
$displayorder]
784 set al
[llength
$varccommits($curview,$a)]
787 set pad
[ntimes
[expr {$r - $l}] {}]
788 set displayorder
[concat
$displayorder $pad]
789 set parentlist
[concat
$parentlist $pad]
791 set displayorder
[lrange
$displayorder 0 [expr {$r - 1}]]
792 set parentlist
[lrange
$parentlist 0 [expr {$r - 1}]]
794 foreach id
$varccommits($curview,$a) {
795 lappend displayorder
$id
796 lappend parentlist
$parents($curview,$id)
798 } elseif
{[lindex
$displayorder $r] eq
{}} {
800 foreach id
$varccommits($curview,$a) {
801 lset displayorder
$i $id
802 lset parentlist
$i $parents($curview,$id)
810 proc commitonrow
{row
} {
813 set id
[lindex
$displayorder $row]
815 make_disporder
$row [expr {$row + 1}]
816 set id
[lindex
$displayorder $row]
821 proc closevarcs
{v
} {
822 global varctok varccommits varcid parents children
823 global cmitlisted commitidx commitinterest vtokmod
825 set missing_parents
0
827 set narcs
[llength
$varctok($v)]
828 for {set a
1} {$a < $narcs} {incr a
} {
829 set id
[lindex
$varccommits($v,$a) end
]
830 foreach p
$parents($v,$id) {
831 if {[info exists varcid
($v,$p)]} continue
832 # add p as a new commit
834 set cmitlisted
($v,$p) 0
835 set parents
($v,$p) {}
836 if {[llength
$children($v,$p)] == 1 &&
837 [llength
$parents($v,$id)] == 1} {
840 set b
[newvarc
$v $p]
843 if {[string compare
[lindex
$varctok($v) $b] $vtokmod($v)] < 0} {
846 lappend varccommits
($v,$b) $p
848 if {[info exists commitinterest
($p)]} {
849 foreach
script $commitinterest($p) {
850 lappend scripts
[string map
[list
"%I" $p] $script]
852 unset commitinterest
($id)
856 if {$missing_parents > 0} {
863 proc getcommitlines
{fd inst view
} {
864 global cmitlisted commitinterest leftover
865 global commitidx commitdata datemode
866 global parents children curview hlview
867 global vnextroot idpending ordertok
868 global varccommits varcid varctok vtokmod
870 set stuff
[read $fd 500000]
871 # git log doesn't terminate the last commit with a null...
872 if {$stuff == {} && $leftover($inst) ne
{} && [eof
$fd]} {
879 global commfd viewcomplete viewactive viewname progresscoords
882 set i
[lsearch
-exact $viewinstances($view) $inst]
884 set viewinstances
($view) [lreplace
$viewinstances($view) $i $i]
886 # set it blocking so we wait for the process to terminate
887 fconfigure
$fd -blocking 1
888 if {[catch
{close
$fd} err
]} {
890 if {$view != $curview} {
891 set fv
" for the \"$viewname($view)\" view"
893 if {[string range
$err 0 4] == "usage"} {
894 set err
"Gitk: error reading commits$fv:\
895 bad arguments to git rev-list."
896 if {$viewname($view) eq
"Command line"} {
898 " (Note: arguments to gitk are passed to git rev-list\
899 to allow selection of commits to be displayed.)"
902 set err
"Error reading commits$fv: $err"
906 if {[incr viewactive
($view) -1] <= 0} {
907 set viewcomplete
($view) 1
908 # Check if we have seen any ids listed as parents that haven't
909 # appeared in the list
912 set progresscoords
{0 0}
915 if {$view == $curview} {
916 run chewcommits
$view
924 set i
[string first
"\0" $stuff $start]
926 append leftover
($inst) [string range
$stuff $start end
]
930 set cmit
$leftover($inst)
931 append cmit
[string range
$stuff 0 [expr {$i - 1}]]
932 set leftover
($inst) {}
934 set cmit
[string range
$stuff $start [expr {$i - 1}]]
936 set start
[expr {$i + 1}]
937 set j
[string first
"\n" $cmit]
940 if {$j >= 0 && [string match
"commit *" $cmit]} {
941 set ids
[string range
$cmit 7 [expr {$j - 1}]]
942 if {[string match
{[-<>]*} $ids]} {
943 switch
-- [string index
$ids 0] {
948 set ids
[string range
$ids 1 end
]
952 if {[string length
$id] != 40} {
960 if {[string length
$shortcmit] > 80} {
961 set shortcmit
"[string range $shortcmit 0 80]..."
963 error_popup
"Can't parse git log output: {$shortcmit}"
966 set id
[lindex
$ids 0]
968 if {!$listed && [info exists parents
($vid)]} continue
970 set olds
[lrange
$ids 1 end
]
974 set commitdata
($id) [string range
$cmit [expr {$j + 1}] end
]
975 set cmitlisted
($vid) $listed
976 set parents
($vid) $olds
978 if {![info exists children
($vid)]} {
979 set children
($vid) {}
980 } elseif
{[llength
$children($vid)] == 1} {
981 set k
[lindex
$children($vid) 0]
982 if {[llength
$parents($view,$k)] == 1 &&
984 $varcid($view,$k) == [llength
$varctok($view)] - 1)} {
985 set a
$varcid($view,$k)
990 set a
[newvarc
$view $id]
993 if {[string compare
[lindex
$varctok($view) $a] $vtokmod($view)] < 0} {
996 lappend varccommits
($view,$a) $id
1000 if {$i == 0 ||
[lsearch
-exact $olds $p] >= $i} {
1002 if {[llength
[lappend children
($vp) $id]] > 1 &&
1003 [vtokcmp
$view [lindex
$children($vp) end-1
] $id] > 0} {
1004 set children
($vp) [lsort
-command [list vtokcmp
$view] \
1006 catch
{unset ordertok
}
1008 if {[info exists varcid
($view,$p)]} {
1009 fix_reversal
$p $a $view
1015 incr commitidx
($view)
1016 if {[info exists commitinterest
($id)]} {
1017 foreach
script $commitinterest($id) {
1018 lappend scripts
[string map
[list
"%I" $id] $script]
1020 unset commitinterest
($id)
1025 run chewcommits
$view
1026 foreach s
$scripts {
1029 if {$view == $curview} {
1030 # update progress bar
1031 global progressdirn progresscoords proglastnc
1032 set inc
[expr {($commitidx($view) - $proglastnc) * 0.0002}]
1033 set proglastnc
$commitidx($view)
1034 set l
[lindex
$progresscoords 0]
1035 set r
[lindex
$progresscoords 1]
1036 if {$progressdirn} {
1037 set r
[expr {$r + $inc}]
1043 set l
[expr {$r - 0.2}]
1046 set l
[expr {$l - $inc}]
1051 set r
[expr {$l + 0.2}]
1053 set progresscoords
[list
$l $r]
1060 proc chewcommits
{view
} {
1061 global curview hlview viewcomplete
1062 global pending_select
1064 if {$view == $curview} {
1066 if {$viewcomplete($view)} {
1067 global commitidx varctok
1068 global numcommits startmsecs
1069 global mainheadid commitinfo nullid
1071 if {[info exists pending_select
]} {
1072 set row
[first_real_row
]
1075 if {$commitidx($curview) > 0} {
1076 #set ms [expr {[clock clicks -milliseconds] - $startmsecs}]
1077 #puts "overall $ms ms for $numcommits commits"
1078 #puts "[llength $varctok($view)] arcs, $commitidx($view) commits"
1080 show_status
"No commits selected"
1085 if {[info exists hlview
] && $view == $hlview} {
1091 proc readcommit
{id
} {
1092 if {[catch
{set contents
[exec git cat-file commit
$id]}]} return
1093 parsecommit
$id $contents 0
1096 proc parsecommit
{id contents listed
} {
1097 global commitinfo cdate
1106 set hdrend
[string first
"\n\n" $contents]
1108 # should never happen...
1109 set hdrend
[string length
$contents]
1111 set header
[string range
$contents 0 [expr {$hdrend - 1}]]
1112 set comment
[string range
$contents [expr {$hdrend + 2}] end
]
1113 foreach line
[split $header "\n"] {
1114 set tag
[lindex
$line 0]
1115 if {$tag == "author"} {
1116 set audate
[lindex
$line end-1
]
1117 set auname
[lrange
$line 1 end-2
]
1118 } elseif
{$tag == "committer"} {
1119 set comdate
[lindex
$line end-1
]
1120 set comname
[lrange
$line 1 end-2
]
1124 # take the first non-blank line of the comment as the headline
1125 set headline
[string trimleft
$comment]
1126 set i
[string first
"\n" $headline]
1128 set headline
[string range
$headline 0 $i]
1130 set headline
[string trimright
$headline]
1131 set i
[string first
"\r" $headline]
1133 set headline
[string trimright
[string range
$headline 0 $i]]
1136 # git rev-list indents the comment by 4 spaces;
1137 # if we got this via git cat-file, add the indentation
1139 foreach line
[split $comment "\n"] {
1140 append newcomment
" "
1141 append newcomment
$line
1142 append newcomment
"\n"
1144 set comment
$newcomment
1146 if {$comdate != {}} {
1147 set cdate
($id) $comdate
1149 set commitinfo
($id) [list
$headline $auname $audate \
1150 $comname $comdate $comment]
1153 proc getcommit
{id
} {
1154 global commitdata commitinfo
1156 if {[info exists commitdata
($id)]} {
1157 parsecommit
$id $commitdata($id) 1
1160 if {![info exists commitinfo
($id)]} {
1161 set commitinfo
($id) {"No commit information available"}
1168 global tagids idtags headids idheads tagobjid
1169 global otherrefids idotherrefs mainhead mainheadid
1171 foreach v
{tagids idtags headids idheads otherrefids idotherrefs
} {
1174 set refd
[open
[list | git show-ref
-d] r
]
1175 while {[gets
$refd line
] >= 0} {
1176 if {[string index
$line 40] ne
" "} continue
1177 set id
[string range
$line 0 39]
1178 set ref
[string range
$line 41 end
]
1179 if {![string match
"refs/*" $ref]} continue
1180 set name
[string range
$ref 5 end
]
1181 if {[string match
"remotes/*" $name]} {
1182 if {![string match
"*/HEAD" $name]} {
1183 set headids
($name) $id
1184 lappend idheads
($id) $name
1186 } elseif
{[string match
"heads/*" $name]} {
1187 set name
[string range
$name 6 end
]
1188 set headids
($name) $id
1189 lappend idheads
($id) $name
1190 } elseif
{[string match
"tags/*" $name]} {
1191 # this lets refs/tags/foo^{} overwrite refs/tags/foo,
1192 # which is what we want since the former is the commit ID
1193 set name
[string range
$name 5 end
]
1194 if {[string match
"*^{}" $name]} {
1195 set name
[string range
$name 0 end-3
]
1197 set tagobjid
($name) $id
1199 set tagids
($name) $id
1200 lappend idtags
($id) $name
1202 set otherrefids
($name) $id
1203 lappend idotherrefs
($id) $name
1210 set thehead
[exec git symbolic-ref HEAD
]
1211 if {[string match
"refs/heads/*" $thehead]} {
1212 set mainhead
[string range
$thehead 11 end
]
1213 if {[info exists headids
($mainhead)]} {
1214 set mainheadid
$headids($mainhead)
1220 # skip over fake commits
1221 proc first_real_row
{} {
1222 global nullid nullid2 numcommits
1224 for {set row
0} {$row < $numcommits} {incr row
} {
1225 set id
[commitonrow
$row]
1226 if {$id ne
$nullid && $id ne
$nullid2} {
1233 # update things for a head moved to a child of its previous location
1234 proc movehead
{id name
} {
1235 global headids idheads
1237 removehead
$headids($name) $name
1238 set headids
($name) $id
1239 lappend idheads
($id) $name
1242 # update things when a head has been removed
1243 proc removehead
{id name
} {
1244 global headids idheads
1246 if {$idheads($id) eq
$name} {
1249 set i
[lsearch
-exact $idheads($id) $name]
1251 set idheads
($id) [lreplace
$idheads($id) $i $i]
1254 unset headids
($name)
1257 proc show_error
{w top msg
} {
1258 message
$w.m
-text $msg -justify center
-aspect 400
1259 pack
$w.m
-side top
-fill x
-padx 20 -pady 20
1260 button
$w.ok
-text OK
-command "destroy $top"
1261 pack
$w.ok
-side bottom
-fill x
1262 bind $top <Visibility
> "grab $top; focus $top"
1263 bind $top <Key-Return
> "destroy $top"
1267 proc error_popup msg
{
1271 show_error
$w $w $msg
1274 proc confirm_popup msg
{
1280 message
$w.m
-text $msg -justify center
-aspect 400
1281 pack
$w.m
-side top
-fill x
-padx 20 -pady 20
1282 button
$w.ok
-text OK
-command "set confirm_ok 1; destroy $w"
1283 pack
$w.ok
-side left
-fill x
1284 button
$w.cancel
-text Cancel
-command "destroy $w"
1285 pack
$w.cancel
-side right
-fill x
1286 bind $w <Visibility
> "grab $w; focus $w"
1291 proc makewindow
{} {
1292 global canv canv2 canv3 linespc charspc ctext cflist
1294 global findtype findtypemenu findloc findstring fstring geometry
1295 global entries sha1entry sha1string sha1but
1296 global diffcontextstring diffcontext
1297 global maincursor textcursor curtextcursor
1298 global rowctxmenu fakerowmenu mergemax wrapcomment
1299 global highlight_files gdttype
1300 global searchstring sstring
1301 global bgcolor fgcolor bglist fglist diffcolors selectbgcolor
1302 global headctxmenu progresscanv progressitem progresscoords statusw
1303 global fprogitem fprogcoord lastprogupdate progupdatepending
1304 global rprogitem rprogcoord
1308 .bar add cascade
-label "File" -menu .bar.
file
1309 .bar configure
-font uifont
1311 .bar.
file add
command -label "Update" -command updatecommits
1312 .bar.
file add
command -label "Reload" -command reloadcommits
1313 .bar.
file add
command -label "Reread references" -command rereadrefs
1314 .bar.
file add
command -label "List references" -command showrefs
1315 .bar.
file add
command -label "Quit" -command doquit
1316 .bar.
file configure
-font uifont
1318 .bar add cascade
-label "Edit" -menu .bar.edit
1319 .bar.edit add
command -label "Preferences" -command doprefs
1320 .bar.edit configure
-font uifont
1322 menu .bar.view
-font uifont
1323 .bar add cascade
-label "View" -menu .bar.view
1324 .bar.view add
command -label "New view..." -command {newview
0}
1325 .bar.view add
command -label "Edit view..." -command editview \
1327 .bar.view add
command -label "Delete view" -command delview
-state disabled
1328 .bar.view add separator
1329 .bar.view add radiobutton
-label "All files" -command {showview
0} \
1330 -variable selectedview
-value 0
1333 .bar add cascade
-label "Help" -menu .bar.
help
1334 .bar.
help add
command -label "About gitk" -command about
1335 .bar.
help add
command -label "Key bindings" -command keys
1336 .bar.
help configure
-font uifont
1337 . configure
-menu .bar
1339 # the gui has upper and lower half, parts of a paned window.
1340 panedwindow .ctop
-orient vertical
1342 # possibly use assumed geometry
1343 if {![info exists geometry
(pwsash0
)]} {
1344 set geometry
(topheight
) [expr {15 * $linespc}]
1345 set geometry
(topwidth
) [expr {80 * $charspc}]
1346 set geometry
(botheight
) [expr {15 * $linespc}]
1347 set geometry
(botwidth
) [expr {50 * $charspc}]
1348 set geometry
(pwsash0
) "[expr {40 * $charspc}] 2"
1349 set geometry
(pwsash1
) "[expr {60 * $charspc}] 2"
1352 # the upper half will have a paned window, a scroll bar to the right, and some stuff below
1353 frame .tf
-height $geometry(topheight
) -width $geometry(topwidth
)
1355 panedwindow .tf.histframe.pwclist
-orient horizontal
-sashpad 0 -handlesize 4
1357 # create three canvases
1358 set cscroll .tf.histframe.csb
1359 set canv .tf.histframe.pwclist.canv
1361 -selectbackground $selectbgcolor \
1362 -background $bgcolor -bd 0 \
1363 -yscrollincr $linespc -yscrollcommand "scrollcanv $cscroll"
1364 .tf.histframe.pwclist add
$canv
1365 set canv2 .tf.histframe.pwclist.canv2
1367 -selectbackground $selectbgcolor \
1368 -background $bgcolor -bd 0 -yscrollincr $linespc
1369 .tf.histframe.pwclist add
$canv2
1370 set canv3 .tf.histframe.pwclist.canv3
1372 -selectbackground $selectbgcolor \
1373 -background $bgcolor -bd 0 -yscrollincr $linespc
1374 .tf.histframe.pwclist add
$canv3
1375 eval .tf.histframe.pwclist sash place
0 $geometry(pwsash0
)
1376 eval .tf.histframe.pwclist sash place
1 $geometry(pwsash1
)
1378 # a scroll bar to rule them
1379 scrollbar
$cscroll -command {allcanvs yview
} -highlightthickness 0
1380 pack
$cscroll -side right
-fill y
1381 bind .tf.histframe.pwclist
<Configure
> {resizeclistpanes
%W
%w
}
1382 lappend bglist
$canv $canv2 $canv3
1383 pack .tf.histframe.pwclist
-fill both
-expand 1 -side left
1385 # we have two button bars at bottom of top frame. Bar 1
1387 frame .tf.lbar
-height 15
1389 set sha1entry .tf.bar.sha1
1390 set entries
$sha1entry
1391 set sha1but .tf.bar.sha1label
1392 button
$sha1but -text "SHA1 ID: " -state disabled
-relief flat \
1393 -command gotocommit
-width 8 -font uifont
1394 $sha1but conf
-disabledforeground [$sha1but cget
-foreground]
1395 pack .tf.bar.sha1label
-side left
1396 entry
$sha1entry -width 40 -font textfont
-textvariable sha1string
1397 trace add variable sha1string
write sha1change
1398 pack
$sha1entry -side left
-pady 2
1400 image create bitmap bm-left
-data {
1401 #define left_width 16
1402 #define left_height 16
1403 static unsigned char left_bits
[] = {
1404 0x00, 0x00, 0xc0, 0x01, 0xe0, 0x00, 0x70, 0x00, 0x38, 0x00, 0x1c, 0x00,
1405 0x0e, 0x00, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0x0e, 0x00, 0x1c, 0x00,
1406 0x38, 0x00, 0x70, 0x00, 0xe0, 0x00, 0xc0, 0x01};
1408 image create bitmap bm-right
-data {
1409 #define right_width 16
1410 #define right_height 16
1411 static unsigned char right_bits
[] = {
1412 0x00, 0x00, 0xc0, 0x01, 0x80, 0x03, 0x00, 0x07, 0x00, 0x0e, 0x00, 0x1c,
1413 0x00, 0x38, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0x00, 0x38, 0x00, 0x1c,
1414 0x00, 0x0e, 0x00, 0x07, 0x80, 0x03, 0xc0, 0x01};
1416 button .tf.bar.leftbut
-image bm-left
-command goback \
1417 -state disabled
-width 26
1418 pack .tf.bar.leftbut
-side left
-fill y
1419 button .tf.bar.rightbut
-image bm-right
-command goforw \
1420 -state disabled
-width 26
1421 pack .tf.bar.rightbut
-side left
-fill y
1423 # Status label and progress bar
1424 set statusw .tf.bar.status
1425 label
$statusw -width 15 -relief sunken
-font uifont
1426 pack
$statusw -side left
-padx 5
1427 set h
[expr {[font metrics uifont
-linespace] + 2}]
1428 set progresscanv .tf.bar.progress
1429 canvas
$progresscanv -relief sunken
-height $h -borderwidth 2
1430 set progressitem
[$progresscanv create rect
-1 0 0 $h -fill green
]
1431 set fprogitem
[$progresscanv create rect
-1 0 0 $h -fill yellow
]
1432 set rprogitem
[$progresscanv create rect
-1 0 0 $h -fill red
]
1433 pack
$progresscanv -side right
-expand 1 -fill x
1434 set progresscoords
{0 0}
1437 bind $progresscanv <Configure
> adjustprogress
1438 set lastprogupdate
[clock clicks
-milliseconds]
1439 set progupdatepending
0
1441 # build up the bottom bar of upper window
1442 label .tf.lbar.flabel
-text "Find " -font uifont
1443 button .tf.lbar.fnext
-text "next" -command {dofind
1 1} -font uifont
1444 button .tf.lbar.fprev
-text "prev" -command {dofind
-1 1} -font uifont
1445 label .tf.lbar.flab2
-text " commit " -font uifont
1446 pack .tf.lbar.flabel .tf.lbar.fnext .tf.lbar.fprev .tf.lbar.flab2 \
1448 set gdttype
"containing:"
1449 set gm
[tk_optionMenu .tf.lbar.gdttype gdttype \
1452 "adding/removing string:"]
1453 trace add variable gdttype
write gdttype_change
1454 $gm conf
-font uifont
1455 .tf.lbar.gdttype conf
-font uifont
1456 pack .tf.lbar.gdttype
-side left
-fill y
1459 set fstring .tf.lbar.findstring
1460 lappend entries
$fstring
1461 entry
$fstring -width 30 -font textfont
-textvariable findstring
1462 trace add variable findstring
write find_change
1464 set findtypemenu
[tk_optionMenu .tf.lbar.findtype \
1465 findtype Exact IgnCase Regexp
]
1466 trace add variable findtype
write findcom_change
1467 .tf.lbar.findtype configure
-font uifont
1468 .tf.lbar.findtype.menu configure
-font uifont
1469 set findloc
"All fields"
1470 tk_optionMenu .tf.lbar.findloc findloc
"All fields" Headline \
1471 Comments Author Committer
1472 trace add variable findloc
write find_change
1473 .tf.lbar.findloc configure
-font uifont
1474 .tf.lbar.findloc.menu configure
-font uifont
1475 pack .tf.lbar.findloc
-side right
1476 pack .tf.lbar.findtype
-side right
1477 pack
$fstring -side left
-expand 1 -fill x
1479 # Finish putting the upper half of the viewer together
1480 pack .tf.lbar
-in .tf
-side bottom
-fill x
1481 pack .tf.bar
-in .tf
-side bottom
-fill x
1482 pack .tf.histframe
-fill both
-side top
-expand 1
1484 .ctop paneconfigure .tf
-height $geometry(topheight
)
1485 .ctop paneconfigure .tf
-width $geometry(topwidth
)
1487 # now build up the bottom
1488 panedwindow .pwbottom
-orient horizontal
1490 # lower left, a text box over search bar, scroll bar to the right
1491 # if we know window height, then that will set the lower text height, otherwise
1492 # we set lower text height which will drive window height
1493 if {[info exists geometry
(main
)]} {
1494 frame .bleft
-width $geometry(botwidth
)
1496 frame .bleft
-width $geometry(botwidth
) -height $geometry(botheight
)
1501 button .bleft.top.search
-text "Search" -command dosearch \
1503 pack .bleft.top.search
-side left
-padx 5
1504 set sstring .bleft.top.sstring
1505 entry
$sstring -width 20 -font textfont
-textvariable searchstring
1506 lappend entries
$sstring
1507 trace add variable searchstring
write incrsearch
1508 pack
$sstring -side left
-expand 1 -fill x
1509 radiobutton .bleft.mid.
diff -text "Diff" -font uifont \
1510 -command changediffdisp
-variable diffelide
-value {0 0}
1511 radiobutton .bleft.mid.old
-text "Old version" -font uifont \
1512 -command changediffdisp
-variable diffelide
-value {0 1}
1513 radiobutton .bleft.mid.new
-text "New version" -font uifont \
1514 -command changediffdisp
-variable diffelide
-value {1 0}
1515 label .bleft.mid.labeldiffcontext
-text " Lines of context: " \
1517 pack .bleft.mid.
diff .bleft.mid.old .bleft.mid.new
-side left
1518 spinbox .bleft.mid.diffcontext
-width 5 -font textfont \
1519 -from 1 -increment 1 -to 10000000 \
1520 -validate all
-validatecommand "diffcontextvalidate %P" \
1521 -textvariable diffcontextstring
1522 .bleft.mid.diffcontext
set $diffcontext
1523 trace add variable diffcontextstring
write diffcontextchange
1524 lappend entries .bleft.mid.diffcontext
1525 pack .bleft.mid.labeldiffcontext .bleft.mid.diffcontext
-side left
1526 set ctext .bleft.ctext
1527 text
$ctext -background $bgcolor -foreground $fgcolor \
1528 -state disabled
-font textfont \
1529 -yscrollcommand scrolltext
-wrap none
1531 $ctext conf
-tabstyle wordprocessor
1533 scrollbar .bleft.sb
-command "$ctext yview"
1534 pack .bleft.top
-side top
-fill x
1535 pack .bleft.mid
-side top
-fill x
1536 pack .bleft.sb
-side right
-fill y
1537 pack
$ctext -side left
-fill both
-expand 1
1538 lappend bglist
$ctext
1539 lappend fglist
$ctext
1541 $ctext tag conf comment
-wrap $wrapcomment
1542 $ctext tag conf filesep
-font textfontbold
-back "#aaaaaa"
1543 $ctext tag conf hunksep
-fore [lindex
$diffcolors 2]
1544 $ctext tag conf d0
-fore [lindex
$diffcolors 0]
1545 $ctext tag conf d1
-fore [lindex
$diffcolors 1]
1546 $ctext tag conf m0
-fore red
1547 $ctext tag conf m1
-fore blue
1548 $ctext tag conf m2
-fore green
1549 $ctext tag conf m3
-fore purple
1550 $ctext tag conf
m4 -fore brown
1551 $ctext tag conf m5
-fore "#009090"
1552 $ctext tag conf m6
-fore magenta
1553 $ctext tag conf m7
-fore "#808000"
1554 $ctext tag conf m8
-fore "#009000"
1555 $ctext tag conf m9
-fore "#ff0080"
1556 $ctext tag conf m10
-fore cyan
1557 $ctext tag conf m11
-fore "#b07070"
1558 $ctext tag conf m12
-fore "#70b0f0"
1559 $ctext tag conf m13
-fore "#70f0b0"
1560 $ctext tag conf m14
-fore "#f0b070"
1561 $ctext tag conf m15
-fore "#ff70b0"
1562 $ctext tag conf mmax
-fore darkgrey
1564 $ctext tag conf mresult
-font textfontbold
1565 $ctext tag conf msep
-font textfontbold
1566 $ctext tag conf found
-back yellow
1568 .pwbottom add .bleft
1569 .pwbottom paneconfigure .bleft
-width $geometry(botwidth
)
1574 radiobutton .bright.mode.
patch -text "Patch" \
1575 -command reselectline
-variable cmitmode
-value "patch"
1576 .bright.mode.
patch configure
-font uifont
1577 radiobutton .bright.mode.tree
-text "Tree" \
1578 -command reselectline
-variable cmitmode
-value "tree"
1579 .bright.mode.tree configure
-font uifont
1580 grid .bright.mode.
patch .bright.mode.tree
-sticky ew
1581 pack .bright.mode
-side top
-fill x
1582 set cflist .bright.cfiles
1583 set indent
[font measure mainfont
"nn"]
1585 -selectbackground $selectbgcolor \
1586 -background $bgcolor -foreground $fgcolor \
1588 -tabs [list
$indent [expr {2 * $indent}]] \
1589 -yscrollcommand ".bright.sb set" \
1590 -cursor [. cget
-cursor] \
1591 -spacing1 1 -spacing3 1
1592 lappend bglist
$cflist
1593 lappend fglist
$cflist
1594 scrollbar .bright.sb
-command "$cflist yview"
1595 pack .bright.sb
-side right
-fill y
1596 pack
$cflist -side left
-fill both
-expand 1
1597 $cflist tag configure highlight \
1598 -background [$cflist cget
-selectbackground]
1599 $cflist tag configure bold
-font mainfontbold
1601 .pwbottom add .bright
1604 # restore window position if known
1605 if {[info exists geometry
(main
)]} {
1606 wm geometry .
"$geometry(main)"
1609 if {[tk windowingsystem
] eq
{aqua
}} {
1615 bind .pwbottom
<Configure
> {resizecdetpanes
%W
%w
}
1616 pack .ctop
-fill both
-expand 1
1617 bindall
<1> {selcanvline
%W
%x
%y
}
1618 #bindall <B1-Motion> {selcanvline %W %x %y}
1619 if {[tk windowingsystem
] == "win32"} {
1620 bind .
<MouseWheel
> { windows_mousewheel_redirector
%W
%X
%Y
%D
}
1621 bind $ctext <MouseWheel
> { windows_mousewheel_redirector
%W
%X
%Y
%D
; break }
1623 bindall
<ButtonRelease-4
> "allcanvs yview scroll -5 units"
1624 bindall
<ButtonRelease-5
> "allcanvs yview scroll 5 units"
1625 if {[tk windowingsystem
] eq
"aqua"} {
1626 bindall
<MouseWheel
> {
1627 set delta
[expr {- (%D
)}]
1628 allcanvs yview scroll
$delta units
1632 bindall
<2> "canvscan mark %W %x %y"
1633 bindall
<B2-Motion
> "canvscan dragto %W %x %y"
1634 bindkey
<Home
> selfirstline
1635 bindkey
<End
> sellastline
1636 bind .
<Key-Up
> "selnextline -1"
1637 bind .
<Key-Down
> "selnextline 1"
1638 bind .
<Shift-Key-Up
> "dofind -1 0"
1639 bind .
<Shift-Key-Down
> "dofind 1 0"
1640 bindkey
<Key-Right
> "goforw"
1641 bindkey
<Key-Left
> "goback"
1642 bind .
<Key-Prior
> "selnextpage -1"
1643 bind .
<Key-Next
> "selnextpage 1"
1644 bind .
<$M1B-Home> "allcanvs yview moveto 0.0"
1645 bind .
<$M1B-End> "allcanvs yview moveto 1.0"
1646 bind .
<$M1B-Key-Up> "allcanvs yview scroll -1 units"
1647 bind .
<$M1B-Key-Down> "allcanvs yview scroll 1 units"
1648 bind .
<$M1B-Key-Prior> "allcanvs yview scroll -1 pages"
1649 bind .
<$M1B-Key-Next> "allcanvs yview scroll 1 pages"
1650 bindkey
<Key-Delete
> "$ctext yview scroll -1 pages"
1651 bindkey
<Key-BackSpace
> "$ctext yview scroll -1 pages"
1652 bindkey
<Key-space
> "$ctext yview scroll 1 pages"
1653 bindkey p
"selnextline -1"
1654 bindkey n
"selnextline 1"
1657 bindkey i
"selnextline -1"
1658 bindkey k
"selnextline 1"
1661 bindkey b
"$ctext yview scroll -1 pages"
1662 bindkey d
"$ctext yview scroll 18 units"
1663 bindkey u
"$ctext yview scroll -18 units"
1664 bindkey
/ {dofind
1 1}
1665 bindkey
<Key-Return
> {dofind
1 1}
1666 bindkey ?
{dofind
-1 1}
1668 bindkey
<F5
> updatecommits
1669 bind .
<$M1B-q> doquit
1670 bind .
<$M1B-f> {dofind
1 1}
1671 bind .
<$M1B-g> {dofind
1 0}
1672 bind .
<$M1B-r> dosearchback
1673 bind .
<$M1B-s> dosearch
1674 bind .
<$M1B-equal> {incrfont
1}
1675 bind .
<$M1B-KP_Add> {incrfont
1}
1676 bind .
<$M1B-minus> {incrfont
-1}
1677 bind .
<$M1B-KP_Subtract> {incrfont
-1}
1678 wm protocol . WM_DELETE_WINDOW doquit
1679 bind .
<Button-1
> "click %W"
1680 bind $fstring <Key-Return
> {dofind
1 1}
1681 bind $sha1entry <Key-Return
> gotocommit
1682 bind $sha1entry <<PasteSelection>> clearsha1
1683 bind $cflist <1> {sel_flist %W %x %y; break}
1684 bind $cflist <B1-Motion> {sel_flist %W %x %y; break}
1685 bind $cflist <ButtonRelease-1> {treeclick %W %x %y}
1686 bind $cflist <Button-3> {pop_flist_menu %W %X %Y %x %y}
1688 set maincursor [. cget -cursor]
1689 set textcursor [$ctext cget -cursor]
1690 set curtextcursor $textcursor
1692 set rowctxmenu .rowctxmenu
1693 menu $rowctxmenu -tearoff 0
1694 $rowctxmenu add command -label "Diff this -> selected" \
1695 -command {diffvssel 0}
1696 $rowctxmenu add command -label "Diff selected -> this" \
1697 -command {diffvssel 1}
1698 $rowctxmenu add command -label "Make patch" -command mkpatch
1699 $rowctxmenu add command -label "Create tag" -command mktag
1700 $rowctxmenu add command -label "Write commit to file" -command writecommit
1701 $rowctxmenu add command -label "Create new branch" -command mkbranch
1702 $rowctxmenu add command -label "Cherry-pick this commit" \
1704 $rowctxmenu add command -label "Reset HEAD branch to here" \
1707 set fakerowmenu .fakerowmenu
1708 menu $fakerowmenu -tearoff 0
1709 $fakerowmenu add command -label "Diff this -> selected" \
1710 -command {diffvssel 0}
1711 $fakerowmenu add command -label "Diff selected -> this" \
1712 -command {diffvssel 1}
1713 $fakerowmenu add command -label "Make patch" -command mkpatch
1714 # $fakerowmenu add command -label "Commit" -command {mkcommit 0}
1715 # $fakerowmenu add command -label "Commit all" -command {mkcommit 1}
1716 # $fakerowmenu add command -label "Revert local changes" -command revertlocal
1718 set headctxmenu .headctxmenu
1719 menu $headctxmenu -tearoff 0
1720 $headctxmenu add command -label "Check out this branch" \
1722 $headctxmenu add command -label "Remove this branch" \
1726 set flist_menu .flistctxmenu
1727 menu $flist_menu -tearoff 0
1728 $flist_menu add command -label "Highlight this too" \
1729 -command {flist_hl 0}
1730 $flist_menu add command -label "Highlight this only" \
1731 -command {flist_hl 1}
1734 # Windows sends all mouse wheel events to the current focused window, not
1735 # the one where the mouse hovers, so bind those events here and redirect
1736 # to the correct window
1737 proc windows_mousewheel_redirector {W X Y D} {
1738 global canv canv2 canv3
1739 set w [winfo containing -displayof $W $X $Y]
1741 set u [expr {$D < 0 ? 5 : -5}]
1742 if {$w == $canv || $w == $canv2 || $w == $canv3} {
1743 allcanvs yview scroll $u units
1746 $w yview scroll $u units
1752 # mouse-2 makes all windows scan vertically, but only the one
1753 # the cursor is in scans horizontally
1754 proc canvscan {op w x y} {
1755 global canv canv2 canv3
1756 foreach c [list $canv $canv2 $canv3] {
1765 proc scrollcanv {cscroll f0 f1} {
1766 $cscroll set $f0 $f1
1771 # when we make a key binding for the toplevel, make sure
1772 # it doesn't get triggered when that key is pressed in the
1773 # find string entry widget.
1774 proc bindkey {ev script} {
1777 set escript [bind Entry $ev]
1778 if {$escript == {}} {
1779 set escript [bind Entry <Key>]
1781 foreach e $entries {
1782 bind $e $ev "$escript; break"
1786 # set the focus back to the toplevel for any click outside
1789 global ctext entries
1790 foreach e [concat $entries $ctext] {
1791 if {$w == $e} return
1796 # Adjust the progress bar for a change in requested extent or canvas size
1797 proc adjustprogress {} {
1798 global progresscanv progressitem progresscoords
1799 global fprogitem fprogcoord lastprogupdate progupdatepending
1800 global rprogitem rprogcoord
1802 set w [expr {[winfo width $progresscanv] - 4}]
1803 set x0 [expr {$w * [lindex $progresscoords 0]}]
1804 set x1 [expr {$w * [lindex $progresscoords 1]}]
1805 set h [winfo height $progresscanv]
1806 $progresscanv coords $progressitem $x0 0 $x1 $h
1807 $progresscanv coords $fprogitem 0 0 [expr {$w * $fprogcoord}] $h
1808 $progresscanv coords $rprogitem 0 0 [expr {$w * $rprogcoord}] $h
1809 set now [clock clicks -milliseconds]
1810 if {$now >= $lastprogupdate + 100} {
1811 set progupdatepending 0
1813 } elseif {!$progupdatepending} {
1814 set progupdatepending 1
1815 after [expr {$lastprogupdate + 100 - $now}] doprogupdate
1819 proc doprogupdate {} {
1820 global lastprogupdate progupdatepending
1822 if {$progupdatepending} {
1823 set progupdatepending 0
1824 set lastprogupdate [clock clicks -milliseconds]
1829 proc savestuff {w} {
1830 global canv canv2 canv3 mainfont textfont uifont tabstop
1831 global stuffsaved findmergefiles maxgraphpct
1832 global maxwidth showneartags showlocalchanges
1833 global viewname viewfiles viewargs viewperm nextviewnum
1834 global cmitmode wrapcomment datetimeformat limitdiffs
1835 global colors bgcolor fgcolor diffcolors diffcontext selectbgcolor
1837 if {$stuffsaved} return
1838 if {![winfo viewable .]} return
1840 set f [open "~/.gitk-new" w]
1841 puts $f [list set mainfont $mainfont]
1842 puts $f [list set textfont $textfont]
1843 puts $f [list set uifont $uifont]
1844 puts $f [list set tabstop $tabstop]
1845 puts $f [list set findmergefiles $findmergefiles]
1846 puts $f [list set maxgraphpct $maxgraphpct]
1847 puts $f [list set maxwidth $maxwidth]
1848 puts $f [list set cmitmode $cmitmode]
1849 puts $f [list set wrapcomment $wrapcomment]
1850 puts $f [list set showneartags $showneartags]
1851 puts $f [list set showlocalchanges $showlocalchanges]
1852 puts $f [list set datetimeformat $datetimeformat]
1853 puts $f [list set limitdiffs $limitdiffs]
1854 puts $f [list set bgcolor $bgcolor]
1855 puts $f [list set fgcolor $fgcolor]
1856 puts $f [list set colors $colors]
1857 puts $f [list set diffcolors $diffcolors]
1858 puts $f [list set diffcontext $diffcontext]
1859 puts $f [list set selectbgcolor $selectbgcolor]
1861 puts $f "set geometry(main) [wm geometry .]"
1862 puts $f "set geometry(topwidth) [winfo width .tf]"
1863 puts $f "set geometry(topheight) [winfo height .tf]"
1864 puts $f "set geometry(pwsash0) \"[.tf.histframe.pwclist sash coord 0]\""
1865 puts $f "set geometry(pwsash1) \"[.tf.histframe.pwclist sash coord 1]\""
1866 puts $f "set geometry(botwidth) [winfo width .bleft]"
1867 puts $f "set geometry(botheight) [winfo height .bleft]"
1869 puts -nonewline $f "set permviews {"
1870 for {set v 0} {$v < $nextviewnum} {incr v} {
1871 if {$viewperm($v)} {
1872 puts $f "{[list $viewname($v) $viewfiles($v) $viewargs($v)]}"
1877 file rename -force "~/.gitk-new" "~/.gitk"
1882 proc resizeclistpanes {win w} {
1884 if {[info exists oldwidth($win)]} {
1885 set s0 [$win sash coord 0]
1886 set s1 [$win sash coord 1]
1888 set sash0 [expr {int($w/2 - 2)}]
1889 set sash1 [expr {int($w*5/6 - 2)}]
1891 set factor [expr {1.0 * $w / $oldwidth($win)}]
1892 set sash0 [expr {int($factor * [lindex $s0 0])}]
1893 set sash1 [expr {int($factor * [lindex $s1 0])}]
1897 if {$sash1 < $sash0 + 20} {
1898 set sash1 [expr {$sash0 + 20}]
1900 if {$sash1 > $w - 10} {
1901 set sash1 [expr {$w - 10}]
1902 if {$sash0 > $sash1 - 20} {
1903 set sash0 [expr {$sash1 - 20}]
1907 $win sash place 0 $sash0 [lindex $s0 1]
1908 $win sash place 1 $sash1 [lindex $s1 1]
1910 set oldwidth($win) $w
1913 proc resizecdetpanes {win w} {
1915 if {[info exists oldwidth($win)]} {
1916 set s0 [$win sash coord 0]
1918 set sash0 [expr {int($w*3/4 - 2)}]
1920 set factor [expr {1.0 * $w / $oldwidth($win)}]
1921 set sash0 [expr {int($factor * [lindex $s0 0])}]
1925 if {$sash0 > $w - 15} {
1926 set sash0 [expr {$w - 15}]
1929 $win sash place 0 $sash0 [lindex $s0 1]
1931 set oldwidth($win) $w
1934 proc allcanvs args {
1935 global canv canv2 canv3
1941 proc bindall {event action} {
1942 global canv canv2 canv3
1943 bind $canv $event $action
1944 bind $canv2 $event $action
1945 bind $canv3 $event $action
1951 if {[winfo exists $w]} {
1956 wm title $w "About gitk"
1957 message $w.m -text {
1958 Gitk - a commit viewer for git
1960 Copyright © 2005-2007 Paul Mackerras
1962 Use and redistribute under the terms of the GNU General Public License} \
1963 -justify center -aspect 400 -border 2 -bg white -relief groove
1964 pack $w.m -side top -fill x -padx 2 -pady 2
1965 $w.m configure -font uifont
1966 button $w.ok -text Close -command "destroy $w" -default active
1967 pack $w.ok -side bottom
1968 $w.ok configure -font uifont
1969 bind $w <Visibility> "focus $w.ok"
1970 bind $w <Key-Escape> "destroy $w"
1971 bind $w <Key-Return> "destroy $w"
1977 if {[winfo exists $w]} {
1981 if {[tk windowingsystem] eq {aqua}} {
1987 wm title $w "Gitk key bindings"
1988 message $w.m -text "
1992 <Home> Move to first commit
1993 <End> Move to last commit
1994 <Up>, p, i Move up one commit
1995 <Down>, n, k Move down one commit
1996 <Left>, z, j Go back in history list
1997 <Right>, x, l Go forward in history list
1998 <PageUp> Move up one page in commit list
1999 <PageDown> Move down one page in commit list
2000 <$M1T-Home> Scroll to top of commit list
2001 <$M1T-End> Scroll to bottom of commit list
2002 <$M1T-Up> Scroll commit list up one line
2003 <$M1T-Down> Scroll commit list down one line
2004 <$M1T-PageUp> Scroll commit list up one page
2005 <$M1T-PageDown> Scroll commit list down one page
2006 <Shift-Up> Find backwards (upwards, later commits)
2007 <Shift-Down> Find forwards (downwards, earlier commits)
2008 <Delete>, b Scroll diff view up one page
2009 <Backspace> Scroll diff view up one page
2010 <Space> Scroll diff view down one page
2011 u Scroll diff view up 18 lines
2012 d Scroll diff view down 18 lines
2014 <$M1T-G> Move to next find hit
2015 <Return> Move to next find hit
2016 / Move to next find hit, or redo find
2017 ? Move to previous find hit
2018 f Scroll diff view to next file
2019 <$M1T-S> Search for next hit in diff view
2020 <$M1T-R> Search for previous hit in diff view
2021 <$M1T-KP+> Increase font size
2022 <$M1T-plus> Increase font size
2023 <$M1T-KP-> Decrease font size
2024 <$M1T-minus> Decrease font size
2027 -justify left -bg white -border 2 -relief groove
2028 pack $w.m -side top -fill both -padx 2 -pady 2
2029 $w.m configure -font uifont
2030 button $w.ok -text Close -command "destroy $w" -default active
2031 pack $w.ok -side bottom
2032 $w.ok configure -font uifont
2033 bind $w <Visibility> "focus $w.ok"
2034 bind $w <Key-Escape> "destroy $w"
2035 bind $w <Key-Return> "destroy $w"
2038 # Procedures for manipulating the file list window at the
2039 # bottom right of the overall window.
2041 proc treeview {w l openlevs} {
2042 global treecontents treediropen treeheight treeparent treeindex
2052 set treecontents() {}
2053 $w conf -state normal
2055 while {[string range $f 0 $prefixend] ne $prefix} {
2056 if {$lev <= $openlevs} {
2057 $w mark set e:$treeindex($prefix) "end -1c"
2058 $w mark gravity e:$treeindex($prefix) left
2060 set treeheight($prefix) $ht
2061 incr ht [lindex $htstack end]
2062 set htstack [lreplace $htstack end end]
2063 set prefixend [lindex $prefendstack end]
2064 set prefendstack [lreplace $prefendstack end end]
2065 set prefix [string range $prefix 0 $prefixend]
2068 set tail [string range $f [expr {$prefixend+1}] end]
2069 while {[set slash [string first "/" $tail]] >= 0} {
2072 lappend prefendstack $prefixend
2073 incr prefixend [expr {$slash + 1}]
2074 set d [string range $tail 0 $slash]
2075 lappend treecontents($prefix) $d
2076 set oldprefix $prefix
2078 set treecontents($prefix) {}
2079 set treeindex($prefix) [incr ix]
2080 set treeparent($prefix) $oldprefix
2081 set tail [string range $tail [expr {$slash+1}] end]
2082 if {$lev <= $openlevs} {
2084 set treediropen($prefix) [expr {$lev < $openlevs}]
2085 set bm [expr {$lev == $openlevs? "tri-rt": "tri-dn"}]
2086 $w mark set d:$ix "end -1c"
2087 $w mark gravity d:$ix left
2089 for {set i 0} {$i < $lev} {incr i} {append str "\t"}
2091 $w image create end -align center -image $bm -padx 1 \
2093 $w insert end $d [highlight_tag $prefix]
2094 $w mark set s:$ix "end -1c"
2095 $w mark gravity s:$ix left
2100 if {$lev <= $openlevs} {
2103 for {set i 0} {$i < $lev} {incr i} {append str "\t"}
2105 $w insert end $tail [highlight_tag $f]
2107 lappend treecontents($prefix) $tail
2110 while {$htstack ne {}} {
2111 set treeheight($prefix) $ht
2112 incr ht [lindex $htstack end]
2113 set htstack [lreplace $htstack end end]
2114 set prefixend [lindex $prefendstack end]
2115 set prefendstack [lreplace $prefendstack end end]
2116 set prefix [string range $prefix 0 $prefixend]
2118 $w conf -state disabled
2121 proc linetoelt {l} {
2122 global treeheight treecontents
2127 foreach e $treecontents($prefix) {
2132 if {[string index $e end] eq "/"} {
2133 set n $treeheight($prefix$e)
2145 proc highlight_tree {y prefix} {
2146 global treeheight treecontents cflist
2148 foreach e $treecontents($prefix) {
2150 if {[highlight_tag $path] ne {}} {
2151 $cflist tag add bold $y.0 "$y.0 lineend"
2154 if {[string index $e end] eq "/" && $treeheight($path) > 1} {
2155 set y [highlight_tree $y $path]
2161 proc treeclosedir {w dir} {
2162 global treediropen treeheight treeparent treeindex
2164 set ix $treeindex($dir)
2165 $w conf -state normal
2166 $w delete s:$ix e:$ix
2167 set treediropen($dir) 0
2168 $w image configure a:$ix -image tri-rt
2169 $w conf -state disabled
2170 set n [expr {1 - $treeheight($dir)}]
2171 while {$dir ne {}} {
2172 incr treeheight($dir) $n
2173 set dir $treeparent($dir)
2177 proc treeopendir {w dir} {
2178 global treediropen treeheight treeparent treecontents treeindex
2180 set ix $treeindex($dir)
2181 $w conf -state normal
2182 $w image configure a:$ix -image tri-dn
2183 $w mark set e:$ix s:$ix
2184 $w mark gravity e:$ix right
2187 set n [llength $treecontents($dir)]
2188 for {set x $dir} {$x ne {}} {set x $treeparent($x)} {
2191 incr treeheight($x) $n
2193 foreach e $treecontents($dir) {
2195 if {[string index $e end] eq "/"} {
2196 set iy $treeindex($de)
2197 $w mark set d:$iy e:$ix
2198 $w mark gravity d:$iy left
2199 $w insert e:$ix $str
2200 set treediropen($de) 0
2201 $w image create e:$ix -align center -image tri-rt -padx 1 \
2203 $w insert e:$ix $e [highlight_tag $de]
2204 $w mark set s:$iy e:$ix
2205 $w mark gravity s:$iy left
2206 set treeheight($de) 1
2208 $w insert e:$ix $str
2209 $w insert e:$ix $e [highlight_tag $de]
2212 $w mark gravity e:$ix left
2213 $w conf -state disabled
2214 set treediropen($dir) 1
2215 set top [lindex [split [$w index @0,0] .] 0]
2216 set ht [$w cget -height]
2217 set l [lindex [split [$w index s:$ix] .] 0]
2220 } elseif {$l + $n + 1 > $top + $ht} {
2221 set top [expr {$l + $n + 2 - $ht}]
2229 proc treeclick {w x y} {
2230 global treediropen cmitmode ctext cflist cflist_top
2232 if {$cmitmode ne "tree"} return
2233 if {![info exists cflist_top]} return
2234 set l [lindex [split [$w index "@$x,$y"] "."] 0]
2235 $cflist tag remove highlight $cflist_top.0 "$cflist_top.0 lineend"
2236 $cflist tag add highlight $l.0 "$l.0 lineend"
2242 set e [linetoelt $l]
2243 if {[string index $e end] ne "/"} {
2245 } elseif {$treediropen($e)} {
2252 proc setfilelist {id} {
2253 global treefilelist cflist
2255 treeview $cflist $treefilelist($id) 0
2258 image create bitmap tri-rt -background black -foreground blue -data {
2259 #define tri-rt_width 13
2260 #define tri-rt_height 13
2261 static unsigned char tri-rt_bits[] = {
2262 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x30, 0x00, 0x70, 0x00, 0xf0, 0x00,
2263 0xf0, 0x01, 0xf0, 0x00, 0x70, 0x00, 0x30, 0x00, 0x10, 0x00, 0x00, 0x00,
2266 #define tri-rt-mask_width 13
2267 #define tri-rt-mask_height 13
2268 static unsigned char tri-rt-mask_bits[] = {
2269 0x08, 0x00, 0x18, 0x00, 0x38, 0x00, 0x78, 0x00, 0xf8, 0x00, 0xf8, 0x01,
2270 0xf8, 0x03, 0xf8, 0x01, 0xf8, 0x00, 0x78, 0x00, 0x38, 0x00, 0x18, 0x00,
2273 image create bitmap tri-dn -background black -foreground blue -data {
2274 #define tri-dn_width 13
2275 #define tri-dn_height 13
2276 static unsigned char tri-dn_bits[] = {
2277 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x07, 0xf8, 0x03,
2278 0xf0, 0x01, 0xe0, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2281 #define tri-dn-mask_width 13
2282 #define tri-dn-mask_height 13
2283 static unsigned char tri-dn-mask_bits[] = {
2284 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x1f, 0xfe, 0x0f, 0xfc, 0x07,
2285 0xf8, 0x03, 0xf0, 0x01, 0xe0, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
2289 image create bitmap reficon-T -background black -foreground yellow -data {
2290 #define tagicon_width 13
2291 #define tagicon_height 9
2292 static unsigned char tagicon_bits[] = {
2293 0x00, 0x00, 0x00, 0x00, 0xf0, 0x07, 0xf8, 0x07,
2294 0xfc, 0x07, 0xf8, 0x07, 0xf0, 0x07, 0x00, 0x00, 0x00, 0x00};
2296 #define tagicon-mask_width 13
2297 #define tagicon-mask_height 9
2298 static unsigned char tagicon-mask_bits[] = {
2299 0x00, 0x00, 0xf0, 0x0f, 0xf8, 0x0f, 0xfc, 0x0f,
2300 0xfe, 0x0f, 0xfc, 0x0f, 0xf8, 0x0f, 0xf0, 0x0f, 0x00, 0x00};
2303 #define headicon_width 13
2304 #define headicon_height 9
2305 static unsigned char headicon_bits[] = {
2306 0x00, 0x00, 0x00, 0x00, 0xf8, 0x07, 0xf8, 0x07,
2307 0xf8, 0x07, 0xf8, 0x07, 0xf8, 0x07, 0x00, 0x00, 0x00, 0x00};
2310 #define headicon-mask_width 13
2311 #define headicon-mask_height 9
2312 static unsigned char headicon-mask_bits[] = {
2313 0x00, 0x00, 0xfc, 0x0f, 0xfc, 0x0f, 0xfc, 0x0f,
2314 0xfc, 0x0f, 0xfc, 0x0f, 0xfc, 0x0f, 0xfc, 0x0f, 0x00, 0x00};
2316 image create bitmap reficon-H -background black -foreground green \
2317 -data $rectdata -maskdata $rectmask
2318 image create bitmap reficon-o -background black -foreground "#ddddff" \
2319 -data $rectdata -maskdata $rectmask
2321 proc init_flist {first} {
2322 global cflist cflist_top difffilestart
2324 $cflist conf -state normal
2325 $cflist delete 0.0 end
2327 $cflist insert end $first
2329 $cflist tag add highlight 1.0 "1.0 lineend"
2331 catch {unset cflist_top}
2333 $cflist conf -state disabled
2334 set difffilestart {}
2337 proc highlight_tag {f} {
2338 global highlight_paths
2340 foreach p $highlight_paths {
2341 if {[string match $p $f]} {
2348 proc highlight_filelist {} {
2349 global cmitmode cflist
2351 $cflist conf -state normal
2352 if {$cmitmode ne "tree"} {
2353 set end [lindex [split [$cflist index end] .] 0]
2354 for {set l 2} {$l < $end} {incr l} {
2355 set line [$cflist get $l.0 "$l.0 lineend"]
2356 if {[highlight_tag $line] ne {}} {
2357 $cflist tag add bold $l.0 "$l.0 lineend"
2363 $cflist conf -state disabled
2366 proc unhighlight_filelist {} {
2369 $cflist conf -state normal
2370 $cflist tag remove bold 1.0 end
2371 $cflist conf -state disabled
2374 proc add_flist {fl} {
2377 $cflist conf -state normal
2379 $cflist insert end "\n"
2380 $cflist insert end $f [highlight_tag $f]
2382 $cflist conf -state disabled
2385 proc sel_flist {w x y} {
2386 global ctext difffilestart cflist cflist_top cmitmode
2388 if {$cmitmode eq "tree"} return
2389 if {![info exists cflist_top]} return
2390 set l [lindex [split [$w index "@$x,$y"] "."] 0]
2391 $cflist tag remove highlight $cflist_top.0 "$cflist_top.0 lineend"
2392 $cflist tag add highlight $l.0 "$l.0 lineend"
2397 catch {$ctext yview [lindex $difffilestart [expr {$l - 2}]]}
2401 proc pop_flist_menu {w X Y x y} {
2402 global ctext cflist cmitmode flist_menu flist_menu_file
2403 global treediffs diffids
2406 set l [lindex [split [$w index "@$x,$y"] "."] 0]
2408 if {$cmitmode eq "tree"} {
2409 set e [linetoelt $l]
2410 if {[string index $e end] eq "/"} return
2412 set e [lindex $treediffs($diffids) [expr {$l-2}]]
2414 set flist_menu_file $e
2415 tk_popup $flist_menu $X $Y
2418 proc flist_hl {only} {
2419 global flist_menu_file findstring gdttype
2421 set x [shellquote $flist_menu_file]
2422 if {$only || $findstring eq {} || $gdttype ne "touching paths:"} {
2425 append findstring " " $x
2427 set gdttype "touching paths:"
2430 # Functions for adding and removing shell-type quoting
2432 proc shellquote {str} {
2433 if {![string match "*\['\"\\ \t]*" $str]} {
2436 if {![string match "*\['\"\\]*" $str]} {
2439 if {![string match "*'*" $str]} {
2442 return "\"[string map {\" \\\" \\ \\\\} $str]\""
2445 proc shellarglist {l} {
2451 append str [shellquote $a]
2456 proc shelldequote {str} {
2461 if {![regexp -start $used -indices "\['\"\\\\ \t]" $str first]} {
2462 append ret [string range $str $used end]
2463 set used [string length $str]
2466 set first [lindex $first 0]
2467 set ch [string index $str $first]
2468 if {$first > $used} {
2469 append ret [string range $str $used [expr {$first - 1}]]
2472 if {$ch eq " " || $ch eq "\t"} break
2475 set first [string first "'" $str $used]
2477 error "unmatched single-quote"
2479 append ret [string range $str $used [expr {$first - 1}]]
2484 if {$used >= [string length $str]} {
2485 error "trailing backslash"
2487 append ret [string index $str $used]
2492 if {![regexp -start $used -indices "\[\"\\\\]" $str first]} {
2493 error "unmatched double-quote"
2495 set first [lindex $first 0]
2496 set ch [string index $str $first]
2497 if {$first > $used} {
2498 append ret [string range $str $used [expr {$first - 1}]]
2501 if {$ch eq "\""} break
2503 append ret [string index $str $used]
2507 return [list $used $ret]
2510 proc shellsplit {str} {
2513 set str [string trimleft $str]
2514 if {$str eq {}} break
2515 set dq [shelldequote $str]
2516 set n [lindex $dq 0]
2517 set word [lindex $dq 1]
2518 set str [string range $str $n end]
2524 # Code to implement multiple views
2526 proc newview {ishighlight} {
2527 global nextviewnum newviewname newviewperm uifont newishighlight
2528 global newviewargs revtreeargs
2530 set newishighlight $ishighlight
2532 if {[winfo exists $top]} {
2536 set newviewname($nextviewnum) "View $nextviewnum"
2537 set newviewperm($nextviewnum) 0
2538 set newviewargs($nextviewnum) [shellarglist $revtreeargs]
2539 vieweditor $top $nextviewnum "Gitk view definition"
2544 global viewname viewperm newviewname newviewperm
2545 global viewargs newviewargs
2547 set top .gitkvedit-$curview
2548 if {[winfo exists $top]} {
2552 set newviewname($curview) $viewname($curview)
2553 set newviewperm($curview) $viewperm($curview)
2554 set newviewargs($curview) [shellarglist $viewargs($curview)]
2555 vieweditor $top $curview "Gitk: edit view $viewname($curview)"
2558 proc vieweditor {top n title} {
2559 global newviewname newviewperm viewfiles
2563 wm title $top $title
2564 label $top.nl -text "Name" -font uifont
2565 entry $top.name -width 20 -textvariable newviewname($n) -font uifont
2566 grid $top.nl $top.name -sticky w -pady 5
2567 checkbutton $top.perm -text "Remember this view" -variable newviewperm($n) \
2569 grid $top.perm - -pady 5 -sticky w
2570 message $top.al -aspect 1000 -font uifont \
2571 -text "Commits to include (arguments to git rev-list):"
2572 grid $top.al - -sticky w -pady 5
2573 entry $top.args -width 50 -textvariable newviewargs($n) \
2574 -background white -font uifont
2575 grid $top.args - -sticky ew -padx 5
2576 message $top.l -aspect 1000 -font uifont \
2577 -text "Enter files and directories to include, one per line:"
2578 grid $top.l - -sticky w
2579 text $top.t -width 40 -height 10 -background white -font uifont
2580 if {[info exists viewfiles($n)]} {
2581 foreach f $viewfiles($n) {
2582 $top.t insert end $f
2583 $top.t insert end "\n"
2585 $top.t delete {end - 1c} end
2586 $top.t mark set insert 0.0
2588 grid $top.t - -sticky ew -padx 5
2590 button $top.buts.ok -text "OK" -command [list newviewok $top $n] \
2592 button $top.buts.can -text "Cancel" -command [list destroy $top] \
2594 grid $top.buts.ok $top.buts.can
2595 grid columnconfigure $top.buts 0 -weight 1 -uniform a
2596 grid columnconfigure $top.buts 1 -weight 1 -uniform a
2597 grid $top.buts - -pady 10 -sticky ew
2601 proc doviewmenu {m first cmd op argv} {
2602 set nmenu [$m index end]
2603 for {set i $first} {$i <= $nmenu} {incr i} {
2604 if {[$m entrycget $i -command] eq $cmd} {
2605 eval $m $op $i $argv
2611 proc allviewmenus {n op args} {
2614 doviewmenu .bar.view 5 [list showview $n] $op $args
2615 # doviewmenu $viewhlmenu 1 [list addvhighlight $n] $op $args
2618 proc newviewok {top n} {
2619 global nextviewnum newviewperm newviewname newishighlight
2620 global viewname viewfiles viewperm selectedview curview
2621 global viewargs newviewargs viewhlmenu
2624 set newargs [shellsplit $newviewargs($n)]
2626 error_popup "Error in commit selection arguments: $err"
2632 foreach f [split [$top.t get 0.0 end] "\n"] {
2633 set ft [string trim $f]
2638 if {![info exists viewfiles($n)]} {
2639 # creating a new view
2641 set viewname($n) $newviewname($n)
2642 set viewperm($n) $newviewperm($n)
2643 set viewfiles($n) $files
2644 set viewargs($n) $newargs
2646 if {!$newishighlight} {
2649 run addvhighlight $n
2652 # editing an existing view
2653 set viewperm($n) $newviewperm($n)
2654 if {$newviewname($n) ne $viewname($n)} {
2655 set viewname($n) $newviewname($n)
2656 doviewmenu .bar.view 5 [list showview $n] \
2657 entryconf [list -label $viewname($n)]
2658 # doviewmenu $viewhlmenu 1 [list addvhighlight $n] \
2659 # entryconf [list -label $viewname($n) -value $viewname($n)]
2661 if {$files ne $viewfiles($n) || $newargs ne $viewargs($n)} {
2662 set viewfiles($n) $files
2663 set viewargs($n) $newargs
2664 if {$curview == $n} {
2669 catch {destroy $top}
2673 global curview viewperm hlview selectedhlview
2675 if {$curview == 0} return
2676 if {[info exists hlview] && $hlview == $curview} {
2677 set selectedhlview None
2680 allviewmenus $curview delete
2681 set viewperm($curview) 0
2685 proc addviewmenu {n} {
2686 global viewname viewhlmenu
2688 .bar.view add radiobutton -label $viewname($n) \
2689 -command [list showview $n] -variable selectedview -value $n
2690 #$viewhlmenu add radiobutton -label $viewname($n) \
2691 # -command [list addvhighlight $n] -variable selectedhlview
2695 global curview viewfiles cached_commitrow ordertok
2696 global displayorder parentlist rowidlist rowisopt rowfinal
2697 global colormap rowtextx nextcolor canvxmax
2698 global numcommits viewcomplete
2699 global selectedline currentid canv canvy0
2701 global pending_select
2703 global selectedview selectfirst
2704 global hlview selectedhlview commitinterest
2706 if {$n == $curview} return
2708 set ymax [lindex [$canv cget -scrollregion] 3]
2709 set span [$canv yview]
2710 set ytop [expr {[lindex $span 0] * $ymax}]
2711 set ybot [expr {[lindex $span 1] * $ymax}]
2712 set yscreen [expr {($ybot - $ytop) / 2}]
2713 if {[info exists selectedline]} {
2714 set selid $currentid
2715 set y [yc $selectedline]
2716 if {$ytop < $y && $y < $ybot} {
2717 set yscreen [expr {$y - $ytop}]
2719 } elseif {[info exists pending_select]} {
2720 set selid $pending_select
2721 unset pending_select
2725 catch {unset treediffs}
2727 if {[info exists hlview] && $hlview == $n} {
2729 set selectedhlview None
2731 catch {unset commitinterest}
2732 catch {unset cached_commitrow}
2733 catch {unset ordertok}
2737 .bar.view entryconf Edit* -state [expr {$n == 0? "disabled": "normal"}]
2738 .bar.view entryconf Delete* -state [expr {$n == 0? "disabled": "normal"}]
2741 if {![info exists viewcomplete($n)]} {
2743 set pending_select $selid
2754 set numcommits $commitidx($n)
2756 catch {unset colormap}
2757 catch {unset rowtextx}
2759 set canvxmax [$canv cget -width]
2766 if {$selid ne {} && [commitinview $selid $n]} {
2767 set row [rowofcommit $selid]
2768 # try to get the selected row in the same position on the screen
2769 set ymax [lindex [$canv cget -scrollregion] 3]
2770 set ytop [expr {[yc $row] - $yscreen}]
2774 set yf [expr {$ytop * 1.0 / $ymax}]
2776 allcanvs yview moveto $yf
2780 } elseif {$selid ne {}} {
2781 set pending_select $selid
2783 set row [first_real_row]
2784 if {$row < $numcommits} {
2790 if {!$viewcomplete($n)} {
2791 if {$numcommits == 0} {
2792 show_status "Reading commits..."
2796 } elseif {$numcommits == 0} {
2797 show_status "No commits selected"
2801 # Stuff relating to the highlighting facility
2803 proc ishighlighted {row} {
2804 global vhighlights fhighlights nhighlights rhighlights
2806 if {[info exists nhighlights($row)] && $nhighlights($row) > 0} {
2807 return $nhighlights($row)
2809 if {[info exists vhighlights($row)] && $vhighlights($row) > 0} {
2810 return $vhighlights($row)
2812 if {[info exists fhighlights($row)] && $fhighlights($row) > 0} {
2813 return $fhighlights($row)
2815 if {[info exists rhighlights($row)] && $rhighlights($row) > 0} {
2816 return $rhighlights($row)
2821 proc bolden {row font} {
2822 global canv linehtag selectedline boldrows
2824 lappend boldrows $row
2825 $canv itemconf $linehtag($row) -font $font
2826 if {[info exists selectedline] && $row == $selectedline} {
2828 set t [eval $canv create rect [$canv bbox $linehtag($row)] \
2829 -outline {{}} -tags secsel \
2830 -fill [$canv cget -selectbackground]]
2835 proc bolden_name {row font} {
2836 global canv2 linentag selectedline boldnamerows
2838 lappend boldnamerows $row
2839 $canv2 itemconf $linentag($row) -font $font
2840 if {[info exists selectedline] && $row == $selectedline} {
2841 $canv2 delete secsel
2842 set t [eval $canv2 create rect [$canv2 bbox $linentag($row)] \
2843 -outline {{}} -tags secsel \
2844 -fill [$canv2 cget -selectbackground]]
2853 foreach row $boldrows {
2854 if {![ishighlighted $row]} {
2855 bolden $row mainfont
2857 lappend stillbold $row
2860 set boldrows $stillbold
2863 proc addvhighlight {n} {
2864 global hlview viewcomplete curview vhl_done vhighlights commitidx
2866 if {[info exists hlview]} {
2870 if {$n != $curview && ![info exists viewcomplete($n)]} {
2873 set vhl_done $commitidx($hlview)
2874 if {$vhl_done > 0} {
2879 proc delvhighlight {} {
2880 global hlview vhighlights
2882 if {![info exists hlview]} return
2884 catch {unset vhighlights}
2888 proc vhighlightmore {} {
2889 global hlview vhl_done commitidx vhighlights curview
2891 set max $commitidx($hlview)
2892 set vr [visiblerows]
2893 set r0 [lindex $vr 0]
2894 set r1 [lindex $vr 1]
2895 for {set i $vhl_done} {$i < $max} {incr i} {
2896 set id [commitonrow $i $hlview]
2897 if {[commitinview $id $curview]} {
2898 set row [rowofcommit $id]
2899 if {$r0 <= $row && $row <= $r1} {
2900 if {![highlighted $row]} {
2901 bolden $row mainfontbold
2903 set vhighlights($row) 1
2910 proc askvhighlight {row id} {
2911 global hlview vhighlights iddrawn
2913 if {[commitinview $id $hlview]} {
2914 if {[info exists iddrawn($id)] && ![ishighlighted $row]} {
2915 bolden $row mainfontbold
2917 set vhighlights($row) 1
2919 set vhighlights($row) 0
2923 proc hfiles_change {} {
2924 global highlight_files filehighlight fhighlights fh_serial
2925 global highlight_paths gdttype
2927 if {[info exists filehighlight]} {
2928 # delete previous highlights
2929 catch {close $filehighlight}
2931 catch {unset fhighlights}
2933 unhighlight_filelist
2935 set highlight_paths {}
2936 after cancel do_file_hl $fh_serial
2938 if {$highlight_files ne {}} {
2939 after 300 do_file_hl $fh_serial
2943 proc gdttype_change {name ix op} {
2944 global gdttype highlight_files findstring findpattern
2947 if {$findstring ne {}} {
2948 if {$gdttype eq "containing:"} {
2949 if {$highlight_files ne {}} {
2950 set highlight_files {}
2955 if {$findpattern ne {}} {
2959 set highlight_files $findstring
2964 # enable/disable findtype/findloc menus too
2967 proc find_change {name ix op} {
2968 global gdttype findstring highlight_files
2971 if {$gdttype eq "containing:"} {
2974 if {$highlight_files ne $findstring} {
2975 set highlight_files $findstring
2982 proc findcom_change args {
2983 global nhighlights boldnamerows
2984 global findpattern findtype findstring gdttype
2987 # delete previous highlights, if any
2988 foreach row $boldnamerows {
2989 bolden_name $row mainfont
2992 catch {unset nhighlights}
2995 if {$gdttype ne "containing:" || $findstring eq {}} {
2997 } elseif {$findtype eq "Regexp"} {
2998 set findpattern $findstring
3000 set e [string map {"*" "\\*" "?" "\\?" "\[" "\\\[" "\\" "\\\\"} \
3002 set findpattern "*$e*"
3006 proc makepatterns {l} {
3009 set ee [string map {"*" "\\*" "?" "\\?" "\[" "\\\[" "\\" "\\\\"} $e]
3010 if {[string index $ee end] eq "/"} {
3020 proc do_file_hl {serial} {
3021 global highlight_files filehighlight highlight_paths gdttype fhl_list
3023 if {$gdttype eq "touching paths:"} {
3024 if {[catch {set paths [shellsplit $highlight_files]}]} return
3025 set highlight_paths [makepatterns $paths]
3027 set gdtargs [concat -- $paths]
3028 } elseif {$gdttype eq "adding/removing string:"} {
3029 set gdtargs [list "-S$highlight_files"]
3031 # must be "containing:", i.e. we're searching commit info
3034 set cmd [concat | git diff-tree -r -s --stdin $gdtargs]
3035 set filehighlight [open $cmd r+]
3036 fconfigure $filehighlight -blocking 0
3037 filerun $filehighlight readfhighlight
3043 proc flushhighlights {} {
3044 global filehighlight fhl_list
3046 if {[info exists filehighlight]} {
3048 puts $filehighlight ""
3049 flush $filehighlight
3053 proc askfilehighlight {row id} {
3054 global filehighlight fhighlights fhl_list
3056 lappend fhl_list $id
3057 set fhighlights($row) -1
3058 puts $filehighlight $id
3061 proc readfhighlight {} {
3062 global filehighlight fhighlights curview iddrawn
3063 global fhl_list find_dirn
3065 if {![info exists filehighlight]} {
3069 while {[incr nr] <= 100 && [gets $filehighlight line] >= 0} {
3070 set line [string trim $line]
3071 set i [lsearch -exact $fhl_list $line]
3072 if {$i < 0} continue
3073 for {set j 0} {$j < $i} {incr j} {
3074 set id [lindex $fhl_list $j]
3075 if {[commitinview $id $curview]} {
3076 set fhighlights([rowofcommit $id]) 0
3079 set fhl_list [lrange $fhl_list [expr {$i+1}] end]
3080 if {$line eq {}} continue
3081 if {![commitinview $line $curview]} continue
3082 set row [rowofcommit $line]
3083 if {[info exists iddrawn($line)] && ![ishighlighted $row]} {
3084 bolden $row mainfontbold
3086 set fhighlights($row) 1
3088 if {[eof $filehighlight]} {
3090 puts "oops, git diff-tree died"
3091 catch {close $filehighlight}
3095 if {[info exists find_dirn]} {
3101 proc doesmatch {f} {
3102 global findtype findpattern
3104 if {$findtype eq "Regexp"} {
3105 return [regexp $findpattern $f]
3106 } elseif {$findtype eq "IgnCase"} {
3107 return [string match -nocase $findpattern $f]
3109 return [string match $findpattern $f]
3113 proc askfindhighlight {row id} {
3114 global nhighlights commitinfo iddrawn
3116 global markingmatches
3118 if {![info exists commitinfo($id)]} {
3121 set info $commitinfo($id)
3123 set fldtypes {Headline Author Date Committer CDate Comments}
3124 foreach f $info ty $fldtypes {
3125 if {($findloc eq "All fields" || $findloc eq $ty) &&
3127 if {$ty eq "Author"} {
3134 if {$isbold && [info exists iddrawn($id)]} {
3135 if {![ishighlighted $row]} {
3136 bolden $row mainfontbold
3138 bolden_name $row mainfontbold
3141 if {$markingmatches} {
3142 markrowmatches $row $id
3145 set nhighlights($row) $isbold
3148 proc markrowmatches {row id} {
3149 global canv canv2 linehtag linentag commitinfo findloc
3151 set headline [lindex $commitinfo($id) 0]
3152 set author [lindex $commitinfo($id) 1]
3153 $canv delete match$row
3154 $canv2 delete match$row
3155 if {$findloc eq "All fields" || $findloc eq "Headline"} {
3156 set m [findmatches $headline]
3158 markmatches $canv $row $headline $linehtag($row) $m \
3159 [$canv itemcget $linehtag($row) -font] $row
3162 if {$findloc eq "All fields" || $findloc eq "Author"} {
3163 set m [findmatches $author]
3165 markmatches $canv2 $row $author $linentag($row) $m \
3166 [$canv2 itemcget $linentag($row) -font] $row
3171 proc vrel_change {name ix op} {
3172 global highlight_related
3175 if {$highlight_related ne "None"} {
3180 # prepare for testing whether commits are descendents or ancestors of a
3181 proc rhighlight_sel {a} {
3182 global descendent desc_todo ancestor anc_todo
3183 global highlight_related rhighlights
3185 catch {unset descendent}
3186 set desc_todo [list $a]
3187 catch {unset ancestor}
3188 set anc_todo [list $a]
3189 if {$highlight_related ne "None"} {
3195 proc rhighlight_none {} {
3198 catch {unset rhighlights}
3202 proc is_descendent {a} {
3203 global curview children descendent desc_todo
3206 set la [rowofcommit $a]
3210 for {set i 0} {$i < [llength $todo]} {incr i} {
3211 set do [lindex $todo $i]
3212 if {[rowofcommit $do] < $la} {
3213 lappend leftover $do
3216 foreach nk $children($v,$do) {
3217 if {![info exists descendent($nk)]} {
3218 set descendent($nk) 1
3226 set desc_todo [concat $leftover [lrange $todo [expr {$i+1}] end]]
3230 set descendent($a) 0
3231 set desc_todo $leftover
3234 proc is_ancestor {a} {
3235 global curview parents ancestor anc_todo
3238 set la [rowofcommit $a]
3242 for {set i 0} {$i < [llength $todo]} {incr i} {
3243 set do [lindex $todo $i]
3244 if {![commitinview $do $v] || [rowofcommit $do] > $la} {
3245 lappend leftover $do
3248 foreach np $parents($v,$do) {
3249 if {![info exists ancestor($np)]} {
3258 set anc_todo [concat $leftover [lrange $todo [expr {$i+1}] end]]
3263 set anc_todo $leftover
3266 proc askrelhighlight {row id} {
3267 global descendent highlight_related iddrawn rhighlights
3268 global selectedline ancestor
3270 if {![info exists selectedline]} return
3272 if {$highlight_related eq "Descendent" ||
3273 $highlight_related eq "Not descendent"} {
3274 if {![info exists descendent($id)]} {
3277 if {$descendent($id) == ($highlight_related eq "Descendent")} {
3280 } elseif {$highlight_related eq "Ancestor" ||
3281 $highlight_related eq "Not ancestor"} {
3282 if {![info exists ancestor($id)]} {
3285 if {$ancestor($id) == ($highlight_related eq "Ancestor")} {
3289 if {[info exists iddrawn($id)]} {
3290 if {$isbold && ![ishighlighted $row]} {
3291 bolden $row mainfontbold
3294 set rhighlights($row) $isbold
3297 # Graph layout functions
3299 proc shortids {ids} {
3302 if {[llength $id] > 1} {
3303 lappend res [shortids $id]
3304 } elseif {[regexp {^[0-9a-f]{40}$} $id]} {
3305 lappend res [string range $id 0 7]
3316 for {set mask 1} {$mask <= $n} {incr mask $mask} {
3317 if {($n & $mask) != 0} {
3318 set ret [concat $ret $o]
3320 set o [concat $o $o]
3325 proc ordertoken {id} {
3326 global ordertok curview varcid varcstart varctok curview parents children
3327 global nullid nullid2
3329 if {[info exists ordertok($id)]} {
3330 return $ordertok($id)
3335 if {[info exists varcid($curview,$id)]} {
3336 set a $varcid($curview,$id)
3337 set p [lindex $varcstart($curview) $a]
3339 set p [lindex $children($curview,$id) 0]
3341 if {[info exists ordertok($p)]} {
3342 set tok $ordertok($p)
3345 if {[llength $children($curview,$p)] == 0} {
3347 set tok [lindex $varctok($curview) $a]
3350 set id [lindex $children($curview,$p) 0]
3351 if {$id eq $nullid || $id eq $nullid2} {
3352 # XXX treat it as a root
3353 set tok [lindex $varctok($curview) $a]
3356 if {[llength $parents($curview,$id)] == 1} {
3357 lappend todo [list $p {}]
3359 set j [lsearch -exact $parents($curview,$id) $p]
3361 puts "oops didn't find [shortids $p] in parents of [shortids $id]"
3363 lappend todo [list $p [strrep $j]]
3366 for {set i [llength $todo]} {[incr i -1] >= 0} {} {
3367 set p [lindex $todo $i 0]
3368 append tok [lindex $todo $i 1]
3369 set ordertok($p) $tok
3371 set ordertok($origid) $tok
3375 # Work out where id should go in idlist so that order-token
3376 # values increase from left to right
3377 proc idcol {idlist id {i 0}} {
3378 set t [ordertoken $id]
3382 if {$i >= [llength $idlist] || $t < [ordertoken [lindex $idlist $i]]} {
3383 if {$i > [llength $idlist]} {
3384 set i [llength $idlist]
3386 while {[incr i -1] >= 0 && $t < [ordertoken [lindex $idlist $i]]} {}
3389 if {$t > [ordertoken [lindex $idlist $i]]} {
3390 while {[incr i] < [llength $idlist] &&
3391 $t >= [ordertoken [lindex $idlist $i]]} {}
3397 proc initlayout {} {
3398 global rowidlist rowisopt rowfinal displayorder parentlist
3399 global numcommits canvxmax canv
3401 global colormap rowtextx
3411 set canvxmax [$canv cget -width]
3412 catch {unset colormap}
3413 catch {unset rowtextx}
3417 proc setcanvscroll {} {
3418 global canv canv2 canv3 numcommits linespc canvxmax canvy0
3420 set ymax [expr {$canvy0 + ($numcommits - 0.5) * $linespc + 2}]
3421 $canv conf -scrollregion [list 0 0 $canvxmax $ymax]
3422 $canv2 conf -scrollregion [list 0 0 0 $ymax]
3423 $canv3 conf -scrollregion [list 0 0 0 $ymax]
3426 proc visiblerows {} {
3427 global canv numcommits linespc
3429 set ymax [lindex [$canv cget -scrollregion] 3]
3430 if {$ymax eq {} || $ymax == 0} return
3432 set y0 [expr {int([lindex $f 0] * $ymax)}]
3433 set r0 [expr {int(($y0 - 3) / $linespc) - 1}]
3437 set y1 [expr {int([lindex $f 1] * $ymax)}]
3438 set r1 [expr {int(($y1 - 3) / $linespc) + 1}]
3439 if {$r1 >= $numcommits} {
3440 set r1 [expr {$numcommits - 1}]
3442 return [list $r0 $r1]
3445 proc layoutmore {} {
3446 global commitidx viewcomplete curview
3447 global numcommits pending_select selectedline curview
3448 global selectfirst lastscrollset commitinterest
3450 set canshow $commitidx($curview)
3451 if {$canshow <= $numcommits && !$viewcomplete($curview)} return
3452 if {$numcommits == 0} {
3456 set prev $numcommits
3457 set numcommits $canshow
3458 set t [clock clicks -milliseconds]
3459 if {$prev < 100 || $viewcomplete($curview) || $t - $lastscrollset > 500} {
3460 set lastscrollset $t
3463 set rows [visiblerows]
3464 set r1 [lindex $rows 1]
3465 if {$r1 >= $canshow} {
3466 set r1 [expr {$canshow - 1}]
3471 if {[info exists pending_select] &&
3472 [commitinview $pending_select $curview]} {
3473 selectline [rowofcommit $pending_select] 1
3476 if {[info exists selectedline] || [info exists pending_select]} {
3479 set l [first_real_row]
3486 proc doshowlocalchanges {} {
3487 global curview mainheadid
3489 if {[commitinview $mainheadid $curview]} {
3492 lappend commitinterest($mainheadid) {dodiffindex}
3496 proc dohidelocalchanges {} {
3497 global nullid nullid2 lserial curview
3499 if {[commitinview $nullid $curview]} {
3500 removerow $nullid $curview
3502 if {[commitinview $nullid2 $curview]} {
3503 removerow $nullid2 $curview
3508 # spawn off a process to do git diff-index --cached HEAD
3509 proc dodiffindex {} {
3510 global lserial showlocalchanges
3512 if {!$showlocalchanges} return
3514 set fd [open "|git diff-index --cached HEAD" r]
3515 fconfigure $fd -blocking 0
3516 filerun $fd [list readdiffindex $fd $lserial]
3519 proc readdiffindex {fd serial} {
3520 global mainheadid nullid2 curview commitinfo commitdata lserial
3523 if {[gets $fd line] < 0} {
3529 # we only need to see one line and we don't really care what it says...
3532 if {$serial != $lserial} {
3536 # now see if there are any local changes not checked in to the index
3537 set fd [open "|git diff-files" r]
3538 fconfigure $fd -blocking 0
3539 filerun $fd [list readdifffiles $fd $serial]
3541 if {$isdiff && ![commitinview $nullid2 $curview]} {
3542 # add the line for the changes in the index to the graph
3543 set hl "Local changes checked in to index but not committed"
3544 set commitinfo($nullid2) [list $hl {} {} {} {} " $hl\n"]
3545 set commitdata($nullid2) "\n $hl\n"
3546 insertrow $nullid2 $mainheadid $curview
3547 } elseif {!$isdiff && [commitinview $nullid2 $curview]} {
3548 removerow $nullid2 $curview
3553 proc readdifffiles {fd serial} {
3554 global mainheadid nullid nullid2 curview
3555 global commitinfo commitdata lserial
3558 if {[gets $fd line] < 0} {
3564 # we only need to see one line and we don't really care what it says...
3567 if {$serial != $lserial} {
3571 if {$isdiff && ![commitinview $nullid $curview]} {
3572 # add the line for the local diff to the graph
3573 set hl "Local uncommitted changes, not checked in to index"
3574 set commitinfo($nullid) [list $hl {} {} {} {} " $hl\n"]
3575 set commitdata($nullid) "\n $hl\n"
3576 if {[commitinview $nullid2 $curview]} {
3581 insertrow $nullid $p $curview
3582 } elseif {!$isdiff && [commitinview $nullid $curview]} {
3583 removerow $nullid $curview
3588 proc nextuse {id row} {
3589 global curview children
3591 if {[info exists children($curview,$id)]} {
3592 foreach kid $children($curview,$id) {
3593 if {![commitinview $kid $curview]} {
3596 if {[rowofcommit $kid] > $row} {
3597 return [rowofcommit $kid]
3601 if {[commitinview $id $curview]} {
3602 return [rowofcommit $id]
3607 proc prevuse {id row} {
3608 global curview children
3611 if {[info exists children($curview,$id)]} {
3612 foreach kid $children($curview,$id) {
3613 if {![commitinview $kid $curview]} break
3614 if {[rowofcommit $kid] < $row} {
3615 set ret [rowofcommit $kid]
3622 proc make_idlist {row} {
3623 global displayorder parentlist uparrowlen downarrowlen mingaplen
3624 global commitidx curview children
3626 set r [expr {$row - $mingaplen - $downarrowlen - 1}]
3630 set ra [expr {$row - $downarrowlen}]
3634 set rb [expr {$row + $uparrowlen}]
3635 if {$rb > $commitidx($curview)} {
3636 set rb $commitidx($curview)
3638 make_disporder $r [expr {$rb + 1}]
3640 for {} {$r < $ra} {incr r} {
3641 set nextid [lindex $displayorder [expr {$r + 1}]]
3642 foreach p [lindex $parentlist $r] {
3643 if {$p eq $nextid} continue
3644 set rn [nextuse $p $r]
3646 $rn <= $r + $downarrowlen + $mingaplen + $uparrowlen} {
3647 lappend ids [list [ordertoken $p] $p]
3651 for {} {$r < $row} {incr r} {
3652 set nextid [lindex $displayorder [expr {$r + 1}]]
3653 foreach p [lindex $parentlist $r] {
3654 if {$p eq $nextid} continue
3655 set rn [nextuse $p $r]
3656 if {$rn < 0 || $rn >= $row} {
3657 lappend ids [list [ordertoken $p] $p]
3661 set id [lindex $displayorder $row]
3662 lappend ids [list [ordertoken $id] $id]
3664 foreach p [lindex $parentlist $r] {
3665 set firstkid [lindex $children($curview,$p) 0]
3666 if {[rowofcommit $firstkid] < $row} {
3667 lappend ids [list [ordertoken $p] $p]
3671 set id [lindex $displayorder $r]
3673 set firstkid [lindex $children($curview,$id) 0]
3674 if {$firstkid ne {} && [rowofcommit $firstkid] < $row} {
3675 lappend ids [list [ordertoken $id] $id]
3680 foreach idx [lsort -unique $ids] {
3681 lappend idlist [lindex $idx 1]
3686 proc rowsequal {a b} {
3687 while {[set i [lsearch -exact $a {}]] >= 0} {
3688 set a [lreplace $a $i $i]
3690 while {[set i [lsearch -exact $b {}]] >= 0} {
3691 set b [lreplace $b $i $i]
3693 return [expr {$a eq $b}]
3696 proc makeupline {id row rend col} {
3697 global rowidlist uparrowlen downarrowlen mingaplen
3699 for {set r $rend} {1} {set r $rstart} {
3700 set rstart [prevuse $id $r]
3701 if {$rstart < 0} return
3702 if {$rstart < $row} break
3704 if {$rstart + $uparrowlen + $mingaplen + $downarrowlen < $rend} {
3705 set rstart [expr {$rend - $uparrowlen - 1}]
3707 for {set r $rstart} {[incr r] <= $row} {} {
3708 set idlist [lindex $rowidlist $r]
3709 if {$idlist ne {} && [lsearch -exact $idlist $id] < 0} {
3710 set col [idcol $idlist $id $col]
3711 lset rowidlist $r [linsert $idlist $col $id]
3717 proc layoutrows {row endrow} {
3718 global rowidlist rowisopt rowfinal displayorder
3719 global uparrowlen downarrowlen maxwidth mingaplen
3720 global children parentlist
3721 global commitidx viewcomplete curview
3723 make_disporder [expr {$row - 1}] [expr {$endrow + $uparrowlen}]
3726 set rm1 [expr {$row - 1}]
3727 foreach id [lindex $rowidlist $rm1] {
3732 set final [lindex $rowfinal $rm1]
3734 for {} {$row < $endrow} {incr row} {
3735 set rm1 [expr {$row - 1}]
3736 if {$rm1 < 0 || $idlist eq {}} {
3737 set idlist [make_idlist $row]
3740 set id [lindex $displayorder $rm1]
3741 set col [lsearch -exact $idlist $id]
3742 set idlist [lreplace $idlist $col $col]
3743 foreach p [lindex $parentlist $rm1] {
3744 if {[lsearch -exact $idlist $p] < 0} {
3745 set col [idcol $idlist $p $col]
3746 set idlist [linsert $idlist $col $p]
3747 # if not the first child, we have to insert a line going up
3748 if {$id ne [lindex $children($curview,$p) 0]} {
3749 makeupline $p $rm1 $row $col
3753 set id [lindex $displayorder $row]
3754 if {$row > $downarrowlen} {
3755 set termrow [expr {$row - $downarrowlen - 1}]
3756 foreach p [lindex $parentlist $termrow] {
3757 set i [lsearch -exact $idlist $p]
3758 if {$i < 0} continue
3759 set nr [nextuse $p $termrow]
3760 if {$nr < 0 || $nr >= $row + $mingaplen + $uparrowlen} {
3761 set idlist [lreplace $idlist $i $i]
3765 set col [lsearch -exact $idlist $id]
3767 set col [idcol $idlist $id]
3768 set idlist [linsert $idlist $col $id]
3769 if {$children($curview,$id) ne {}} {
3770 makeupline $id $rm1 $row $col
3773 set r [expr {$row + $uparrowlen - 1}]
3774 if {$r < $commitidx($curview)} {
3776 foreach p [lindex $parentlist $r] {
3777 if {[lsearch -exact $idlist $p] >= 0} continue
3778 set fk [lindex $children($curview,$p) 0]
3779 if {[rowofcommit $fk] < $row} {
3780 set x [idcol $idlist $p $x]
3781 set idlist [linsert $idlist $x $p]
3784 if {[incr r] < $commitidx($curview)} {
3785 set p [lindex $displayorder $r]
3786 if {[lsearch -exact $idlist $p] < 0} {
3787 set fk [lindex $children($curview,$p) 0]
3788 if {$fk ne {} && [rowofcommit $fk] < $row} {
3789 set x [idcol $idlist $p $x]
3790 set idlist [linsert $idlist $x $p]
3796 if {$final && !$viewcomplete($curview) &&
3797 $row + $uparrowlen + $mingaplen + $downarrowlen
3798 >= $commitidx($curview)} {
3801 set l [llength $rowidlist]
3803 lappend rowidlist $idlist
3805 lappend rowfinal $final
3806 } elseif {$row < $l} {
3807 if {![rowsequal $idlist [lindex $rowidlist $row]]} {
3808 lset rowidlist $row $idlist
3811 lset rowfinal $row $final
3813 set pad [ntimes [expr {$row - $l}] {}]
3814 set rowidlist [concat $rowidlist $pad]
3815 lappend rowidlist $idlist
3816 set rowfinal [concat $rowfinal $pad]
3817 lappend rowfinal $final
3818 set rowisopt [concat $rowisopt [ntimes [expr {$row - $l + 1}] 0]]
3824 proc changedrow {row} {
3825 global displayorder iddrawn rowisopt need_redisplay
3827 set l [llength $rowisopt]
3829 lset rowisopt $row 0
3830 if {$row + 1 < $l} {
3831 lset rowisopt [expr {$row + 1}] 0
3832 if {$row + 2 < $l} {
3833 lset rowisopt [expr {$row + 2}] 0
3837 set id [lindex $displayorder $row]
3838 if {[info exists iddrawn($id)]} {
3839 set need_redisplay 1
3843 proc insert_pad {row col npad} {
3846 set pad [ntimes $npad {}]
3847 set idlist [lindex $rowidlist $row]
3848 set bef [lrange $idlist 0 [expr {$col - 1}]]
3849 set aft [lrange $idlist $col end]
3850 set i [lsearch -exact $aft {}]
3852 set aft [lreplace $aft $i $i]
3854 lset rowidlist $row [concat $bef $pad $aft]
3858 proc optimize_rows {row col endrow} {
3859 global rowidlist rowisopt displayorder curview children
3864 for {} {$row < $endrow} {incr row; set col 0} {
3865 if {[lindex $rowisopt $row]} continue
3867 set y0 [expr {$row - 1}]
3868 set ym [expr {$row - 2}]
3869 set idlist [lindex $rowidlist $row]
3870 set previdlist [lindex $rowidlist $y0]
3871 if {$idlist eq {} || $previdlist eq {}} continue
3873 set pprevidlist [lindex $rowidlist $ym]
3874 if {$pprevidlist eq {}} continue
3880 for {} {$col < [llength $idlist]} {incr col} {
3881 set id [lindex $idlist $col]
3882 if {[lindex $previdlist $col] eq $id} continue
3887 set x0 [lsearch -exact $previdlist $id]
3888 if {$x0 < 0} continue
3889 set z [expr {$x0 - $col}]
3893 set xm [lsearch -exact $pprevidlist $id]
3895 set z0 [expr {$xm - $x0}]
3899 # if row y0 is the first child of $id then it's not an arrow
3900 if {[lindex $children($curview,$id) 0] ne
3901 [lindex $displayorder $y0]} {
3905 if {!$isarrow && $id ne [lindex $displayorder $row] &&
3906 [lsearch -exact [lindex $rowidlist [expr {$row+1}]] $id] < 0} {
3909 # Looking at lines from this row to the previous row,
3910 # make them go straight up if they end in an arrow on
3911 # the previous row; otherwise make them go straight up
3913 if {$z < -1 || ($z < 0 && $isarrow)} {
3914 # Line currently goes left too much;
3915 # insert pads in the previous row, then optimize it
3916 set npad [expr {-1 - $z + $isarrow}]
3917 insert_pad $y0 $x0 $npad
3919 optimize_rows $y0 $x0 $row
3921 set previdlist [lindex $rowidlist $y0]
3922 set x0 [lsearch -exact $previdlist $id]
3923 set z [expr {$x0 - $col}]
3925 set pprevidlist [lindex $rowidlist $ym]
3926 set xm [lsearch -exact $pprevidlist $id]
3927 set z0 [expr {$xm - $x0}]
3929 } elseif {$z > 1 || ($z > 0 && $isarrow)} {
3930 # Line currently goes right too much;
3931 # insert pads in this line
3932 set npad [expr {$z - 1 + $isarrow}]
3933 insert_pad $row $col $npad
3934 set idlist [lindex $rowidlist $row]
3936 set z [expr {$x0 - $col}]
3939 if {$z0 eq {} && !$isarrow && $ym >= 0} {
3940 # this line links to its first child on row $row-2
3941 set id [lindex $displayorder $ym]
3942 set xc [lsearch -exact $pprevidlist $id]
3944 set z0 [expr {$xc - $x0}]
3947 # avoid lines jigging left then immediately right
3948 if {$z0 ne {} && $z < 0 && $z0 > 0} {
3949 insert_pad $y0 $x0 1
3951 optimize_rows $y0 $x0 $row
3952 set previdlist [lindex $rowidlist $y0]
3956 # Find the first column that doesn't have a line going right
3957 for {set col [llength $idlist]} {[incr col -1] >= 0} {} {
3958 set id [lindex $idlist $col]
3959 if {$id eq {}} break
3960 set x0 [lsearch -exact $previdlist $id]
3962 # check if this is the link to the first child
3963 set kid [lindex $displayorder $y0]
3964 if {[lindex $children($curview,$id) 0] eq $kid} {
3965 # it is, work out offset to child
3966 set x0 [lsearch -exact $previdlist $kid]
3969 if {$x0 <= $col} break
3971 # Insert a pad at that column as long as it has a line and
3972 # isn't the last column
3973 if {$x0 >= 0 && [incr col] < [llength $idlist]} {
3974 set idlist [linsert $idlist $col {}]
3975 lset rowidlist $row $idlist
3983 global canvx0 linespc
3984 return [expr {$canvx0 + $col * $linespc}]
3988 global canvy0 linespc
3989 return [expr {$canvy0 + $row * $linespc}]
3992 proc linewidth {id} {
3993 global thickerline lthickness
3996 if {[info exists thickerline] && $id eq $thickerline} {
3997 set wid [expr {2 * $lthickness}]
4002 proc rowranges {id} {
4003 global curview children uparrowlen downarrowlen
4006 set kids $children($curview,$id)
4012 foreach child $kids {
4013 if {![commitinview $child $curview]} break
4014 set row [rowofcommit $child]
4015 if {![info exists prev]} {
4016 lappend ret [expr {$row + 1}]
4018 if {$row <= $prevrow} {
4019 puts "oops children of [shortids $id] out of order [shortids $child] $row <= [shortids $prev] $prevrow"
4021 # see if the line extends the whole way from prevrow to row
4022 if {$row > $prevrow + $uparrowlen + $downarrowlen &&
4023 [lsearch -exact [lindex $rowidlist \
4024 [expr {int(($row + $prevrow) / 2)}]] $id] < 0} {
4025 # it doesn't, see where it ends
4026 set r [expr {$prevrow + $downarrowlen}]
4027 if {[lsearch -exact [lindex $rowidlist $r] $id] < 0} {
4028 while {[incr r -1] > $prevrow &&
4029 [lsearch -exact [lindex $rowidlist $r] $id] < 0} {}
4031 while {[incr r] <= $row &&
4032 [lsearch -exact [lindex $rowidlist $r] $id] >= 0} {}
4036 # see where it starts up again
4037 set r [expr {$row - $uparrowlen}]
4038 if {[lsearch -exact [lindex $rowidlist $r] $id] < 0} {
4039 while {[incr r] < $row &&
4040 [lsearch -exact [lindex $rowidlist $r] $id] < 0} {}
4042 while {[incr r -1] >= $prevrow &&
4043 [lsearch -exact [lindex $rowidlist $r] $id] >= 0} {}
4049 if {$child eq $id} {
4058 proc drawlineseg {id row endrow arrowlow} {
4059 global rowidlist displayorder iddrawn linesegs
4060 global canv colormap linespc curview maxlinelen parentlist
4062 set cols [list [lsearch -exact [lindex $rowidlist $row] $id]]
4063 set le [expr {$row + 1}]
4066 set c [lsearch -exact [lindex $rowidlist $le] $id]
4072 set x [lindex $displayorder $le]
4077 if {[info exists iddrawn($x)] || $le == $endrow} {
4078 set c [lsearch -exact [lindex $rowidlist [expr {$le+1}]] $id]
4094 if {[info exists linesegs($id)]} {
4095 set lines $linesegs($id)
4097 set r0 [lindex $li 0]
4099 if {$r0 == $le && [lindex $li 1] - $row <= $maxlinelen} {
4109 set li [lindex $lines [expr {$i-1}]]
4110 set r1 [lindex $li 1]
4111 if {$r1 == $row && $le - [lindex $li 0] <= $maxlinelen} {
4116 set x [lindex $cols [expr {$le - $row}]]
4117 set xp [lindex $cols [expr {$le - 1 - $row}]]
4118 set dir [expr {$xp - $x}]
4120 set ith [lindex $lines $i 2]
4121 set coords [$canv coords $ith]
4122 set ah [$canv itemcget $ith -arrow]
4123 set arrowhigh [expr {$ah eq "first" || $ah eq "both"}]
4124 set x2 [lindex $cols [expr {$le + 1 - $row}]]
4125 if {$x2 ne {} && $x - $x2 == $dir} {
4126 set coords [lrange $coords 0 end-2]
4129 set coords [list [xc $le $x] [yc $le]]
4132 set itl [lindex $lines [expr {$i-1}] 2]
4133 set al [$canv itemcget $itl -arrow]
4134 set arrowlow [expr {$al eq "last" || $al eq "both"}]
4135 } elseif {$arrowlow} {
4136 if {[lsearch -exact [lindex $rowidlist [expr {$row-1}]] $id] >= 0 ||
4137 [lsearch -exact [lindex $parentlist [expr {$row-1}]] $id] >= 0} {
4141 set arrow [lindex {none first last both} [expr {$arrowhigh + 2*$arrowlow}]]
4142 for {set y $le} {[incr y -1] > $row} {} {
4144 set xp [lindex $cols [expr {$y - 1 - $row}]]
4145 set ndir [expr {$xp - $x}]
4146 if {$dir != $ndir || $xp < 0} {
4147 lappend coords [xc $y $x] [yc $y]
4153 # join parent line to first child
4154 set ch [lindex $displayorder $row]
4155 set xc [lsearch -exact [lindex $rowidlist $row] $ch]
4157 puts "oops: drawlineseg: child $ch not on row $row"
4158 } elseif {$xc != $x} {
4159 if {($arrowhigh && $le == $row + 1) || $dir == 0} {
4160 set d [expr {int(0.5 * $linespc)}]
4163 set x2 [expr {$x1 - $d}]
4165 set x2 [expr {$x1 + $d}]
4168 set y1 [expr {$y2 + $d}]
4169 lappend coords $x1 $y1 $x2 $y2
4170 } elseif {$xc < $x - 1} {
4171 lappend coords [xc $row [expr {$x-1}]] [yc $row]
4172 } elseif {$xc > $x + 1} {
4173 lappend coords [xc $row [expr {$x+1}]] [yc $row]
4177 lappend coords [xc $row $x] [yc $row]
4179 set xn [xc $row $xp]
4181 lappend coords $xn $yn
4185 set t [$canv create line $coords -width [linewidth $id] \
4186 -fill $colormap($id) -tags lines.$id -arrow $arrow]
4189 set lines [linsert $lines $i [list $row $le $t]]
4191 $canv coords $ith $coords
4192 if {$arrow ne $ah} {
4193 $canv itemconf $ith -arrow $arrow
4195 lset lines $i 0 $row
4198 set xo [lsearch -exact [lindex $rowidlist [expr {$row - 1}]] $id]
4199 set ndir [expr {$xo - $xp}]
4200 set clow [$canv coords $itl]
4201 if {$dir == $ndir} {
4202 set clow [lrange $clow 2 end]
4204 set coords [concat $coords $clow]
4206 lset lines [expr {$i-1}] 1 $le
4208 # coalesce two pieces
4210 set b [lindex $lines [expr {$i-1}] 0]
4211 set e [lindex $lines $i 1]
4212 set lines [lreplace $lines [expr {$i-1}] $i [list $b $e $itl]]
4214 $canv coords $itl $coords
4215 if {$arrow ne $al} {
4216 $canv itemconf $itl -arrow $arrow
4220 set linesegs($id) $lines
4224 proc drawparentlinks {id row} {
4225 global rowidlist canv colormap curview parentlist
4226 global idpos linespc
4228 set rowids [lindex $rowidlist $row]
4229 set col [lsearch -exact $rowids $id]
4230 if {$col < 0} return
4231 set olds [lindex $parentlist $row]
4232 set row2 [expr {$row + 1}]
4233 set x [xc $row $col]
4236 set d [expr {int(0.5 * $linespc)}]
4237 set ymid [expr {$y + $d}]
4238 set ids [lindex $rowidlist $row2]
4239 # rmx = right-most X coord used
4242 set i [lsearch -exact $ids $p]
4244 puts "oops, parent $p of $id not in list"
4247 set x2 [xc $row2 $i]
4251 set j [lsearch -exact $rowids $p]
4253 # drawlineseg will do this one for us
4257 # should handle duplicated parents here...
4258 set coords [list $x $y]
4260 # if attaching to a vertical segment, draw a smaller
4261 # slant for visual distinctness
4264 lappend coords [expr {$x2 + $d}] $y $x2 $ymid
4266 lappend coords [expr {$x2 - $d}] $y $x2 $ymid
4268 } elseif {$i < $col && $i < $j} {
4269 # segment slants towards us already
4270 lappend coords [xc $row $j] $y
4272 if {$i < $col - 1} {
4273 lappend coords [expr {$x2 + $linespc}] $y
4274 } elseif {$i > $col + 1} {
4275 lappend coords [expr {$x2 - $linespc}] $y
4277 lappend coords $x2 $y2
4280 lappend coords $x2 $y2
4282 set t [$canv create line $coords -width [linewidth $p] \
4283 -fill $colormap($p) -tags lines.$p]
4287 if {$rmx > [lindex $idpos($id) 1]} {
4288 lset idpos($id) 1 $rmx
4293 proc drawlines {id} {
4296 $canv itemconf lines.$id -width [linewidth $id]
4299 proc drawcmittext {id row col} {
4300 global linespc canv canv2 canv3 fgcolor curview
4301 global cmitlisted commitinfo rowidlist parentlist
4302 global rowtextx idpos idtags idheads idotherrefs
4303 global linehtag linentag linedtag selectedline
4304 global canvxmax boldrows boldnamerows fgcolor nullid nullid2
4306 # listed is 0 for boundary, 1 for normal, 2 for left, 3 for right
4307 set listed $cmitlisted($curview,$id)
4308 if {$id eq $nullid} {
4310 } elseif {$id eq $nullid2} {
4313 set ofill [expr {$listed != 0? "blue": "white"}]
4315 set x [xc $row $col]
4317 set orad [expr {$linespc / 3}]
4319 set t [$canv create oval [expr {$x - $orad}] [expr {$y - $orad}] \
4320 [expr {$x + $orad - 1}] [expr {$y + $orad - 1}] \
4321 -fill $ofill -outline $fgcolor -width 1 -tags circle]
4322 } elseif {$listed == 2} {
4323 # triangle pointing left for left-side commits
4324 set t [$canv create polygon \
4325 [expr {$x - $orad}] $y \
4326 [expr {$x + $orad - 1}] [expr {$y - $orad}] \
4327 [expr {$x + $orad - 1}] [expr {$y + $orad - 1}] \
4328 -fill $ofill -outline $fgcolor -width 1 -tags circle]
4330 # triangle pointing right for right-side commits
4331 set t [$canv create polygon \
4332 [expr {$x + $orad - 1}] $y \
4333 [expr {$x - $orad}] [expr {$y - $orad}] \
4334 [expr {$x - $orad}] [expr {$y + $orad - 1}] \
4335 -fill $ofill -outline $fgcolor -width 1 -tags circle]
4338 $canv bind $t <1> {selcanvline {} %x %y}
4339 set rmx [llength [lindex $rowidlist $row]]
4340 set olds [lindex $parentlist $row]
4342 set nextids [lindex $rowidlist [expr {$row + 1}]]
4344 set i [lsearch -exact $nextids $p]
4350 set xt [xc $row $rmx]
4351 set rowtextx($row) $xt
4352 set idpos($id) [list $x $xt $y]
4353 if {[info exists idtags($id)] || [info exists idheads($id)]
4354 || [info exists idotherrefs($id)]} {
4355 set xt [drawtags $id $x $xt $y]
4357 set headline [lindex $commitinfo($id) 0]
4358 set name [lindex $commitinfo($id) 1]
4359 set date [lindex $commitinfo($id) 2]
4360 set date [formatdate $date]
4363 set isbold [ishighlighted $row]
4365 lappend boldrows $row
4366 set font mainfontbold
4368 lappend boldnamerows $row
4369 set nfont mainfontbold
4372 set linehtag($row) [$canv create text $xt $y -anchor w -fill $fgcolor \
4373 -text $headline -font $font -tags text]
4374 $canv bind $linehtag($row) <Button-3> "rowmenu %X %Y $id"
4375 set linentag($row) [$canv2 create text 3 $y -anchor w -fill $fgcolor \
4376 -text $name -font $nfont -tags text]
4377 set linedtag($row) [$canv3 create text 3 $y -anchor w -fill $fgcolor \
4378 -text $date -font mainfont -tags text]
4379 if {[info exists selectedline] && $selectedline == $row} {
4382 set xr [expr {$xt + [font measure $font $headline]}]
4383 if {$xr > $canvxmax} {
4389 proc drawcmitrow {row} {
4390 global displayorder rowidlist nrows_drawn
4391 global iddrawn markingmatches
4392 global commitinfo numcommits
4393 global filehighlight fhighlights findpattern nhighlights
4394 global hlview vhighlights
4395 global highlight_related rhighlights
4397 if {$row >= $numcommits} return
4399 set id [lindex $displayorder $row]
4400 if {[info exists hlview] && ![info exists vhighlights($row)]} {
4401 askvhighlight $row $id
4403 if {[info exists filehighlight] && ![info exists fhighlights($row)]} {
4404 askfilehighlight $row $id
4406 if {$findpattern ne {} && ![info exists nhighlights($row)]} {
4407 askfindhighlight $row $id
4409 if {$highlight_related ne "None" && ![info exists rhighlights($row)]} {
4410 askrelhighlight $row $id
4412 if {![info exists iddrawn($id)]} {
4413 set col [lsearch -exact [lindex $rowidlist $row] $id]
4415 puts "oops, row $row id $id not in list"
4418 if {![info exists commitinfo($id)]} {
4422 drawcmittext $id $row $col
4426 if {$markingmatches} {
4427 markrowmatches $row $id
4431 proc drawcommits {row {endrow {}}} {
4432 global numcommits iddrawn displayorder curview need_redisplay
4433 global parentlist rowidlist rowfinal uparrowlen downarrowlen nrows_drawn
4438 if {$endrow eq {}} {
4441 if {$endrow >= $numcommits} {
4442 set endrow [expr {$numcommits - 1}]
4445 set rl1 [expr {$row - $downarrowlen - 3}]
4449 set ro1 [expr {$row - 3}]
4453 set r2 [expr {$endrow + $uparrowlen + 3}]
4454 if {$r2 > $numcommits} {
4457 for {set r $rl1} {$r < $r2} {incr r} {
4458 if {[lindex $rowidlist $r] ne {} && [lindex $rowfinal $r]} {
4462 set rl1 [expr {$r + 1}]
4468 optimize_rows $ro1 0 $r2
4469 if {$need_redisplay || $nrows_drawn > 2000} {
4474 # make the lines join to already-drawn rows either side
4475 set r [expr {$row - 1}]
4476 if {$r < 0 || ![info exists iddrawn([lindex $displayorder $r])]} {
4479 set er [expr {$endrow + 1}]
4480 if {$er >= $numcommits ||
4481 ![info exists iddrawn([lindex $displayorder $er])]} {
4484 for {} {$r <= $er} {incr r} {
4485 set id [lindex $displayorder $r]
4486 set wasdrawn [info exists iddrawn($id)]
4488 if {$r == $er} break
4489 set nextid [lindex $displayorder [expr {$r + 1}]]
4490 if {$wasdrawn && [info exists iddrawn($nextid)]} continue
4491 drawparentlinks $id $r
4493 set rowids [lindex $rowidlist $r]
4494 foreach lid $rowids {
4495 if {$lid eq {}} continue
4496 if {[info exists lineend($lid)] && $lineend($lid) > $r} continue
4498 # see if this is the first child of any of its parents
4499 foreach p [lindex $parentlist $r] {
4500 if {[lsearch -exact $rowids $p] < 0} {
4501 # make this line extend up to the child
4502 set lineend($p) [drawlineseg $p $r $er 0]
4506 set lineend($lid) [drawlineseg $lid $r $er 1]
4512 proc undolayout {row} {
4513 global uparrowlen mingaplen downarrowlen
4514 global rowidlist rowisopt rowfinal need_redisplay
4516 set r [expr {$row - ($uparrowlen + $mingaplen + $downarrowlen)}]
4520 if {[llength $rowidlist] > $r} {
4522 set rowidlist [lrange $rowidlist 0 $r]
4523 set rowfinal [lrange $rowfinal 0 $r]
4524 set rowisopt [lrange $rowisopt 0 $r]
4525 set need_redisplay 1
4530 proc drawfrac {f0 f1} {
4533 set ymax [lindex [$canv cget -scrollregion] 3]
4534 if {$ymax eq {} || $ymax == 0} return
4535 set y0 [expr {int($f0 * $ymax)}]
4536 set row [expr {int(($y0 - 3) / $linespc) - 1}]
4537 set y1 [expr {int($f1 * $ymax)}]
4538 set endrow [expr {int(($y1 - 3) / $linespc) + 1}]
4539 drawcommits $row $endrow
4542 proc drawvisible {} {
4544 eval drawfrac [$canv yview]
4547 proc clear_display {} {
4548 global iddrawn linesegs need_redisplay nrows_drawn
4549 global vhighlights fhighlights nhighlights rhighlights
4552 catch {unset iddrawn}
4553 catch {unset linesegs}
4554 catch {unset vhighlights}
4555 catch {unset fhighlights}
4556 catch {unset nhighlights}
4557 catch {unset rhighlights}
4558 set need_redisplay 0
4562 proc findcrossings {id} {
4563 global rowidlist parentlist numcommits displayorder
4567 foreach {s e} [rowranges $id] {
4568 if {$e >= $numcommits} {
4569 set e [expr {$numcommits - 1}]
4571 if {$e <= $s} continue
4572 for {set row $e} {[incr row -1] >= $s} {} {
4573 set x [lsearch -exact [lindex $rowidlist $row] $id]
4575 set olds [lindex $parentlist $row]
4576 set kid [lindex $displayorder $row]
4577 set kidx [lsearch -exact [lindex $rowidlist $row] $kid]
4578 if {$kidx < 0} continue
4579 set nextrow [lindex $rowidlist [expr {$row + 1}]]
4581 set px [lsearch -exact $nextrow $p]
4582 if {$px < 0} continue
4583 if {($kidx < $x && $x < $px) || ($px < $x && $x < $kidx)} {
4584 if {[lsearch -exact $ccross $p] >= 0} continue
4585 if {$x == $px + ($kidx < $px? -1: 1)} {
4587 } elseif {[lsearch -exact $cross $p] < 0} {
4594 return [concat $ccross {{}} $cross]
4597 proc assigncolor {id} {
4598 global colormap colors nextcolor
4599 global parents children children curview
4601 if {[info exists colormap($id)]} return
4602 set ncolors [llength $colors]
4603 if {[info exists children($curview,$id)]} {
4604 set kids $children($curview,$id)
4608 if {[llength $kids] == 1} {
4609 set child [lindex $kids 0]
4610 if {[info exists colormap($child)]
4611 && [llength $parents($curview,$child)] == 1} {
4612 set colormap($id) $colormap($child)
4618 foreach x [findcrossings $id] {
4620 # delimiter between corner crossings and other crossings
4621 if {[llength $badcolors] >= $ncolors - 1} break
4622 set origbad $badcolors
4624 if {[info exists colormap($x)]
4625 && [lsearch -exact $badcolors $colormap($x)] < 0} {
4626 lappend badcolors $colormap($x)
4629 if {[llength $badcolors] >= $ncolors} {
4630 set badcolors $origbad
4632 set origbad $badcolors
4633 if {[llength $badcolors] < $ncolors - 1} {
4634 foreach child $kids {
4635 if {[info exists colormap($child)]
4636 && [lsearch -exact $badcolors $colormap($child)] < 0} {
4637 lappend badcolors $colormap($child)
4639 foreach p $parents($curview,$child) {
4640 if {[info exists colormap($p)]
4641 && [lsearch -exact $badcolors $colormap($p)] < 0} {
4642 lappend badcolors $colormap($p)
4646 if {[llength $badcolors] >= $ncolors} {
4647 set badcolors $origbad
4650 for {set i 0} {$i <= $ncolors} {incr i} {
4651 set c [lindex $colors $nextcolor]
4652 if {[incr nextcolor] >= $ncolors} {
4655 if {[lsearch -exact $badcolors $c]} break
4657 set colormap($id) $c
4660 proc bindline {t id} {
4663 $canv bind $t <Enter> "lineenter %x %y $id"
4664 $canv bind $t <Motion> "linemotion %x %y $id"
4665 $canv bind $t <Leave> "lineleave $id"
4666 $canv bind $t <Button-1> "lineclick %x %y $id 1"
4669 proc drawtags {id x xt y1} {
4670 global idtags idheads idotherrefs mainhead
4671 global linespc lthickness
4672 global canv rowtextx curview fgcolor bgcolor
4677 if {[info exists idtags($id)]} {
4678 set marks $idtags($id)
4679 set ntags [llength $marks]
4681 if {[info exists idheads($id)]} {
4682 set marks [concat $marks $idheads($id)]
4683 set nheads [llength $idheads($id)]
4685 if {[info exists idotherrefs($id)]} {
4686 set marks [concat $marks $idotherrefs($id)]
4692 set delta [expr {int(0.5 * ($linespc - $lthickness))}]
4693 set yt [expr {$y1 - 0.5 * $linespc}]
4694 set yb [expr {$yt + $linespc - 1}]
4698 foreach tag $marks {
4700 if {$i >= $ntags && $i < $ntags + $nheads && $tag eq $mainhead} {
4701 set wid [font measure mainfontbold $tag]
4703 set wid [font measure mainfont $tag]
4707 set xt [expr {$xt + $delta + $wid + $lthickness + $linespc}]
4709 set t [$canv create line $x $y1 [lindex $xvals end] $y1 \
4710 -width $lthickness -fill black -tags tag.$id]
4712 foreach tag $marks x $xvals wid $wvals {
4713 set xl [expr {$x + $delta}]
4714 set xr [expr {$x + $delta + $wid + $lthickness}]
4716 if {[incr ntags -1] >= 0} {
4718 set t [$canv create polygon $x [expr {$yt + $delta}] $xl $yt \
4719 $xr $yt $xr $yb $xl $yb $x [expr {$yb - $delta}] \
4720 -width 1 -outline black -fill yellow -tags tag.$id]
4721 $canv bind $t <1> [list showtag $tag 1]
4722 set rowtextx([rowofcommit $id]) [expr {$xr + $linespc}]
4724 # draw a head or other ref
4725 if {[incr nheads -1] >= 0} {
4727 if {$tag eq $mainhead} {
4728 set font mainfontbold
4733 set xl [expr {$xl - $delta/2}]
4734 $canv create polygon $x $yt $xr $yt $xr $yb $x $yb \
4735 -width 1 -outline black -fill $col -tags tag.$id
4736 if {[regexp {^(remotes/.*/|remotes/)} $tag match remoteprefix]} {
4737 set rwid [font measure mainfont $remoteprefix]
4738 set xi [expr {$x + 1}]
4739 set yti [expr {$yt + 1}]
4740 set xri [expr {$x + $rwid}]
4741 $canv create polygon $xi $yti $xri $yti $xri $yb $xi $yb \
4742 -width 0 -fill "#ffddaa" -tags tag.$id
4745 set t [$canv create text $xl $y1 -anchor w -text $tag -fill $fgcolor \
4746 -font $font -tags [list tag.$id text]]
4748 $canv bind $t <1> [list showtag $tag 1]
4749 } elseif {$nheads >= 0} {
4750 $canv bind $t <Button-3> [list headmenu %X %Y $id $tag]
4756 proc xcoord {i level ln} {
4757 global canvx0 xspc1 xspc2
4759 set x [expr {$canvx0 + $i * $xspc1($ln)}]
4760 if {$i > 0 && $i == $level} {
4761 set x [expr {$x + 0.5 * ($xspc2 - $xspc1($ln))}]
4762 } elseif {$i > $level} {
4763 set x [expr {$x + $xspc2 - $xspc1($ln)}]
4768 proc show_status {msg} {
4772 $canv create text 3 3 -anchor nw -text $msg -font mainfont \
4773 -tags text -fill $fgcolor
4776 # Don't change the text pane cursor if it is currently the hand cursor,
4777 # showing that we are over a sha1 ID link.
4778 proc settextcursor {c} {
4779 global ctext curtextcursor
4781 if {[$ctext cget -cursor] == $curtextcursor} {
4782 $ctext config -cursor $c
4784 set curtextcursor $c
4787 proc nowbusy {what {name {}}} {
4788 global isbusy busyname statusw
4790 if {[array names isbusy] eq {}} {
4791 . config -cursor watch
4795 set busyname($what) $name
4797 $statusw conf -text $name
4801 proc notbusy {what} {
4802 global isbusy maincursor textcursor busyname statusw
4806 if {$busyname($what) ne {} &&
4807 [$statusw cget -text] eq $busyname($what)} {
4808 $statusw conf -text {}
4811 if {[array names isbusy] eq {}} {
4812 . config -cursor $maincursor
4813 settextcursor $textcursor
4817 proc findmatches {f} {
4818 global findtype findstring
4819 if {$findtype == "Regexp"} {
4820 set matches [regexp -indices -all -inline $findstring $f]
4823 if {$findtype == "IgnCase"} {
4824 set f [string tolower $f]
4825 set fs [string tolower $fs]
4829 set l [string length $fs]
4830 while {[set j [string first $fs $f $i]] >= 0} {
4831 lappend matches [list $j [expr {$j+$l-1}]]
4832 set i [expr {$j + $l}]
4838 proc dofind {{dirn 1} {wrap 1}} {
4839 global findstring findstartline findcurline selectedline numcommits
4840 global gdttype filehighlight fh_serial find_dirn findallowwrap
4842 if {[info exists find_dirn]} {
4843 if {$find_dirn == $dirn} return
4847 if {$findstring eq {} || $numcommits == 0} return
4848 if {![info exists selectedline]} {
4849 set findstartline [lindex [visiblerows] [expr {$dirn < 0}]]
4851 set findstartline $selectedline
4853 set findcurline $findstartline
4854 nowbusy finding "Searching"
4855 if {$gdttype ne "containing:" && ![info exists filehighlight]} {
4856 after cancel do_file_hl $fh_serial
4857 do_file_hl $fh_serial
4860 set findallowwrap $wrap
4864 proc stopfinding {} {
4865 global find_dirn findcurline fprogcoord
4867 if {[info exists find_dirn]} {
4877 global commitdata commitinfo numcommits findpattern findloc
4878 global findstartline findcurline findallowwrap
4879 global find_dirn gdttype fhighlights fprogcoord
4880 global curview varcorder vrownum varccommits
4882 if {![info exists find_dirn]} {
4885 set fldtypes {Headline Author Date Committer CDate Comments}
4888 if {$find_dirn > 0} {
4890 if {$l >= $numcommits} {
4893 if {$l <= $findstartline} {
4894 set lim [expr {$findstartline + 1}]
4897 set moretodo $findallowwrap
4904 if {$l >= $findstartline} {
4905 set lim [expr {$findstartline - 1}]
4908 set moretodo $findallowwrap
4911 set n [expr {($lim - $l) * $find_dirn}]
4918 set ai [bsearch $vrownum($curview) $l]
4919 set a [lindex $varcorder($curview) $ai]
4920 set arow [lindex $vrownum($curview) $ai]
4921 set ids [lindex $varccommits($curview,$a)]
4922 set arowend [expr {$arow + [llength $ids]}]
4923 if {$gdttype eq "containing:"} {
4924 for {} {$n > 0} {incr n -1; incr l $find_dirn} {
4925 if {$l < $arow || $l >= $arowend} {
4927 set a [lindex $varcorder($curview) $ai]
4928 set arow [lindex $vrownum($curview) $ai]
4929 set ids [lindex $varccommits($curview,$a)]
4930 set arowend [expr {$arow + [llength $ids]}]
4932 set id [lindex $ids [expr {$l - $arow}]]
4933 # shouldn't happen unless git log doesn't give all the commits...
4934 if {![info exists commitdata($id)] ||
4935 ![doesmatch $commitdata($id)]} {
4938 if {![info exists commitinfo($id)]} {
4941 set info $commitinfo($id)
4942 foreach f $info ty $fldtypes {
4943 if {($findloc eq "All fields" || $findloc eq $ty) &&
4952 for {} {$n > 0} {incr n -1; incr l $find_dirn} {
4953 if {$l < $arow || $l >= $arowend} {
4955 set a [lindex $varcorder($curview) $ai]
4956 set arow [lindex $vrownum($curview) $ai]
4957 set ids [lindex $varccommits($curview,$a)]
4958 set arowend [expr {$arow + [llength $ids]}]
4960 set id [lindex $ids [expr {$l - $arow}]]
4961 if {![info exists fhighlights($l)]} {
4962 askfilehighlight $l $id
4965 set findcurline [expr {$l - $find_dirn}]
4967 } elseif {$fhighlights($l)} {
4973 if {$found || ($domore && !$moretodo)} {
4989 set findcurline [expr {$l - $find_dirn}]
4991 set n [expr {($findcurline - $findstartline) * $find_dirn - 1}]
4995 set fprogcoord [expr {$n * 1.0 / $numcommits}]
5000 proc findselectline {l} {
5001 global findloc commentend ctext findcurline markingmatches gdttype
5003 set markingmatches 1
5006 if {$findloc == "All fields" || $findloc == "Comments"} {
5007 # highlight the matches in the comments
5008 set f [$ctext get 1.0 $commentend]
5009 set matches [findmatches $f]
5010 foreach match $matches {
5011 set start [lindex $match 0]
5012 set end [expr {[lindex $match 1] + 1}]
5013 $ctext tag add found "1.0 + $start c" "1.0 + $end c"
5019 # mark the bits of a headline or author that match a find string
5020 proc markmatches {canv l str tag matches font row} {
5023 set bbox [$canv bbox $tag]
5024 set x0 [lindex $bbox 0]
5025 set y0 [lindex $bbox 1]
5026 set y1 [lindex $bbox 3]
5027 foreach match $matches {
5028 set start [lindex $match 0]
5029 set end [lindex $match 1]
5030 if {$start > $end} continue
5031 set xoff [font measure $font [string range $str 0 [expr {$start-1}]]]
5032 set xlen [font measure $font [string range $str 0 [expr {$end}]]]
5033 set t [$canv create rect [expr {$x0+$xoff}] $y0 \
5034 [expr {$x0+$xlen+2}] $y1 \
5035 -outline {} -tags [list match$l matches] -fill yellow]
5037 if {[info exists selectedline] && $row == $selectedline} {
5038 $canv raise $t secsel
5043 proc unmarkmatches {} {
5044 global markingmatches
5046 allcanvs delete matches
5047 set markingmatches 0
5051 proc selcanvline {w x y} {
5052 global canv canvy0 ctext linespc
5054 set ymax [lindex [$canv cget -scrollregion] 3]
5055 if {$ymax == {}} return
5056 set yfrac [lindex [$canv yview] 0]
5057 set y [expr {$y + $yfrac * $ymax}]
5058 set l [expr {int(($y - $canvy0) / $linespc + 0.5)}]
5063 if {![info exists rowtextx($l)] || $x < $rowtextx($l)} return
5069 proc commit_descriptor {p} {
5071 if {![info exists commitinfo($p)]} {
5075 if {[llength $commitinfo($p)] > 1} {
5076 set l [lindex $commitinfo($p) 0]
5081 # append some text to the ctext widget, and make any SHA1 ID
5082 # that we know about be a clickable link.
5083 proc appendwithlinks {text tags} {
5084 global ctext linknum curview pendinglinks
5086 set start [$ctext index "end - 1c"]
5087 $ctext insert end $text $tags
5088 set links [regexp -indices -all -inline {[0-9a-f]{40}} $text]
5092 set linkid [string range $text $s $e]
5094 $ctext tag delete link$linknum
5095 $ctext tag add link$linknum "$start + $s c" "$start + $e c"
5096 setlink $linkid link$linknum
5101 proc setlink {id lk} {
5102 global curview ctext pendinglinks commitinterest
5104 if {[commitinview $id $curview]} {
5105 $ctext tag conf $lk -foreground blue -underline 1
5106 $ctext tag bind $lk <1> [list selectline [rowofcommit $id] 1]
5107 $ctext tag bind $lk <Enter> {linkcursor %W 1}
5108 $ctext tag bind $lk <Leave> {linkcursor %W -1}
5110 lappend pendinglinks($id) $lk
5111 lappend commitinterest($id) {makelink %I}
5115 proc makelink {id} {
5118 if {![info exists pendinglinks($id)]} return
5119 foreach lk $pendinglinks($id) {
5122 unset pendinglinks($id)
5125 proc linkcursor {w inc} {
5126 global linkentercount curtextcursor
5128 if {[incr linkentercount $inc] > 0} {
5129 $w configure -cursor hand2
5131 $w configure -cursor $curtextcursor
5132 if {$linkentercount < 0} {
5133 set linkentercount 0
5138 proc viewnextline {dir} {
5142 set ymax [lindex [$canv cget -scrollregion] 3]
5143 set wnow [$canv yview]
5144 set wtop [expr {[lindex $wnow 0] * $ymax}]
5145 set newtop [expr {$wtop + $dir * $linespc}]
5148 } elseif {$newtop > $ymax} {
5151 allcanvs yview moveto [expr {$newtop * 1.0 / $ymax}]
5154 # add a list of tag or branch names at position pos
5155 # returns the number of names inserted
5156 proc appendrefs {pos ids var} {
5157 global ctext linknum curview $var maxrefs
5159 if {[catch {$ctext index $pos}]} {
5162 $ctext conf -state normal
5163 $ctext delete $pos "$pos lineend"
5166 foreach tag [set $var\($id\)] {
5167 lappend tags [list $tag $id]
5170 if {[llength $tags] > $maxrefs} {
5171 $ctext insert $pos "many ([llength $tags])"
5173 set tags [lsort -index 0 -decreasing $tags]
5176 set id [lindex $ti 1]
5179 $ctext tag delete $lk
5180 $ctext insert $pos $sep
5181 $ctext insert $pos [lindex $ti 0] $lk
5186 $ctext conf -state disabled
5187 return [llength $tags]
5190 # called when we have finished computing the nearby tags
5191 proc dispneartags {delay} {
5192 global selectedline currentid showneartags tagphase
5194 if {![info exists selectedline] || !$showneartags} return
5195 after cancel dispnexttag
5197 after 200 dispnexttag
5200 after idle dispnexttag
5205 proc dispnexttag {} {
5206 global selectedline currentid showneartags tagphase ctext
5208 if {![info exists selectedline] || !$showneartags} return
5209 switch -- $tagphase {
5211 set dtags [desctags $currentid]
5213 appendrefs precedes $dtags idtags
5217 set atags [anctags $currentid]
5219 appendrefs follows $atags idtags
5223 set dheads [descheads $currentid]
5224 if {$dheads ne {}} {
5225 if {[appendrefs branch $dheads idheads] > 1
5226 && [$ctext get "branch -3c"] eq "h"} {
5227 # turn "Branch" into "Branches"
5228 $ctext conf -state normal
5229 $ctext insert "branch -2c" "es"
5230 $ctext conf -state disabled
5235 if {[incr tagphase] <= 2} {
5236 after idle dispnexttag
5240 proc make_secsel {l} {
5241 global linehtag linentag linedtag canv canv2 canv3
5243 if {![info exists linehtag($l)]} return
5245 set t [eval $canv create rect [$canv bbox $linehtag($l)] -outline {{}} \
5246 -tags secsel -fill [$canv cget -selectbackground]]
5248 $canv2 delete secsel
5249 set t [eval $canv2 create rect [$canv2 bbox $linentag($l)] -outline {{}} \
5250 -tags secsel -fill [$canv2 cget -selectbackground]]
5252 $canv3 delete secsel
5253 set t [eval $canv3 create rect [$canv3 bbox $linedtag($l)] -outline {{}} \
5254 -tags secsel -fill [$canv3 cget -selectbackground]]
5258 proc selectline {l isnew} {
5259 global canv ctext commitinfo selectedline
5260 global canvy0 linespc parents children curview
5261 global currentid sha1entry
5262 global commentend idtags linknum
5263 global mergemax numcommits pending_select
5264 global cmitmode showneartags allcommits
5266 catch {unset pending_select}
5271 if {$l < 0 || $l >= $numcommits} return
5272 set y [expr {$canvy0 + $l * $linespc}]
5273 set ymax [lindex [$canv cget -scrollregion] 3]
5274 set ytop [expr {$y - $linespc - 1}]
5275 set ybot [expr {$y + $linespc + 1}]
5276 set wnow [$canv yview]
5277 set wtop [expr {[lindex $wnow 0] * $ymax}]
5278 set wbot [expr {[lindex $wnow 1] * $ymax}]
5279 set wh [expr {$wbot - $wtop}]
5281 if {$ytop < $wtop} {
5282 if {$ybot < $wtop} {
5283 set newtop [expr {$y - $wh / 2.0}]
5286 if {$newtop > $wtop - $linespc} {
5287 set newtop [expr {$wtop - $linespc}]
5290 } elseif {$ybot > $wbot} {
5291 if {$ytop > $wbot} {
5292 set newtop [expr {$y - $wh / 2.0}]
5294 set newtop [expr {$ybot - $wh}]
5295 if {$newtop < $wtop + $linespc} {
5296 set newtop [expr {$wtop + $linespc}]
5300 if {$newtop != $wtop} {
5304 allcanvs yview moveto [expr {$newtop * 1.0 / $ymax}]
5311 addtohistory [list selectline $l 0]
5316 set id [commitonrow $l]
5318 $sha1entry delete 0 end
5319 $sha1entry insert 0 $id
5320 $sha1entry selection from 0
5321 $sha1entry selection to end
5324 $ctext conf -state normal
5327 set info $commitinfo($id)
5328 set date [formatdate [lindex $info 2]]
5329 $ctext insert end "Author: [lindex $info 1] $date\n"
5330 set date [formatdate [lindex $info 4]]
5331 $ctext insert end "Committer: [lindex $info 3] $date\n"
5332 if {[info exists idtags($id)]} {
5333 $ctext insert end "Tags:"
5334 foreach tag $idtags($id) {
5335 $ctext insert end " $tag"
5337 $ctext insert end "\n"
5341 set olds $parents($curview,$id)
5342 if {[llength $olds] > 1} {
5345 if {$np >= $mergemax} {
5350 $ctext insert end "Parent: " $tag
5351 appendwithlinks [commit_descriptor $p] {}
5356 append headers "Parent: [commit_descriptor $p]"
5360 foreach c $children($curview,$id) {
5361 append headers "Child: [commit_descriptor $c]"
5364 # make anything that looks like a SHA1 ID be a clickable link
5365 appendwithlinks $headers {}
5366 if {$showneartags} {
5367 if {![info exists allcommits]} {
5370 $ctext insert end "Branch: "
5371 $ctext mark set branch "end -1c"
5372 $ctext mark gravity branch left
5373 $ctext insert end "\nFollows: "
5374 $ctext mark set follows "end -1c"
5375 $ctext mark gravity follows left
5376 $ctext insert end "\nPrecedes: "
5377 $ctext mark set precedes "end -1c"
5378 $ctext mark gravity precedes left
5379 $ctext insert end "\n"
5382 $ctext insert end "\n"
5383 set comment [lindex $info 5]
5384 if {[string first "\r" $comment] >= 0} {
5385 set comment [string map {"\r" "\n "} $comment]
5387 appendwithlinks $comment {comment}
5389 $ctext tag remove found 1.0 end
5390 $ctext conf -state disabled
5391 set commentend [$ctext index "end - 1c"]
5393 init_flist "Comments"
5394 if {$cmitmode eq "tree"} {
5396 } elseif {[llength $olds] <= 1} {
5403 proc selfirstline {} {
5408 proc sellastline {} {
5411 set l [expr {$numcommits - 1}]
5415 proc selnextline {dir} {
5418 if {![info exists selectedline]} return
5419 set l [expr {$selectedline + $dir}]
5424 proc selnextpage {dir} {
5425 global canv linespc selectedline numcommits
5427 set lpp [expr {([winfo height $canv] - 2) / $linespc}]
5431 allcanvs yview scroll [expr {$dir * $lpp}] units
5433 if {![info exists selectedline]} return
5434 set l [expr {$selectedline + $dir * $lpp}]
5437 } elseif {$l >= $numcommits} {
5438 set l [expr $numcommits - 1]
5444 proc unselectline {} {
5445 global selectedline currentid
5447 catch {unset selectedline}
5448 catch {unset currentid}
5449 allcanvs delete secsel
5453 proc reselectline {} {
5456 if {[info exists selectedline]} {
5457 selectline $selectedline 0
5461 proc addtohistory {cmd} {
5462 global history historyindex curview
5464 set elt [list $curview $cmd]
5465 if {$historyindex > 0
5466 && [lindex $history [expr {$historyindex - 1}]] == $elt} {
5470 if {$historyindex < [llength $history]} {
5471 set history [lreplace $history $historyindex end $elt]
5473 lappend history $elt
5476 if {$historyindex > 1} {
5477 .tf.bar.leftbut conf -state normal
5479 .tf.bar.leftbut conf -state disabled
5481 .tf.bar.rightbut conf -state disabled
5487 set view [lindex $elt 0]
5488 set cmd [lindex $elt 1]
5489 if {$curview != $view} {
5496 global history historyindex
5499 if {$historyindex > 1} {
5500 incr historyindex -1
5501 godo [lindex $history [expr {$historyindex - 1}]]
5502 .tf.bar.rightbut conf -state normal
5504 if {$historyindex <= 1} {
5505 .tf.bar.leftbut conf -state disabled
5510 global history historyindex
5513 if {$historyindex < [llength $history]} {
5514 set cmd [lindex $history $historyindex]
5517 .tf.bar.leftbut conf -state normal
5519 if {$historyindex >= [llength $history]} {
5520 .tf.bar.rightbut conf -state disabled
5525 global treefilelist treeidlist diffids diffmergeid treepending
5526 global nullid nullid2
5529 catch {unset diffmergeid}
5530 if {![info exists treefilelist($id)]} {
5531 if {![info exists treepending]} {
5532 if {$id eq $nullid} {
5533 set cmd [list | git ls-files]
5534 } elseif {$id eq $nullid2} {
5535 set cmd [list | git ls-files --stage -t]
5537 set cmd [list | git ls-tree -r $id]
5539 if {[catch {set gtf [open $cmd r]}]} {
5543 set treefilelist($id) {}
5544 set treeidlist($id) {}
5545 fconfigure $gtf -blocking 0
5546 filerun $gtf [list gettreeline $gtf $id]
5553 proc gettreeline {gtf id} {
5554 global treefilelist treeidlist treepending cmitmode diffids nullid nullid2
5557 while {[incr nl] <= 1000 && [gets $gtf line] >= 0} {
5558 if {$diffids eq $nullid} {
5561 if {$diffids ne $nullid2 && [lindex $line 1] ne "blob"} continue
5562 set i [string first "\t" $line]
5563 if {$i < 0} continue
5564 set sha1 [lindex $line 2]
5565 set fname [string range $line [expr {$i+1}] end]
5566 if {[string index $fname 0] eq "\""} {
5567 set fname [lindex $fname 0]
5569 lappend treeidlist($id) $sha1
5571 lappend treefilelist($id) $fname
5574 return [expr {$nl >= 1000? 2: 1}]
5578 if {$cmitmode ne "tree"} {
5579 if {![info exists diffmergeid]} {
5580 gettreediffs $diffids
5582 } elseif {$id ne $diffids} {
5591 global treefilelist treeidlist diffids nullid nullid2
5592 global ctext commentend
5594 set i [lsearch -exact $treefilelist($diffids) $f]
5596 puts "oops, $f not in list for id $diffids"
5599 if {$diffids eq $nullid} {
5600 if {[catch {set bf [open $f r]} err]} {
5601 puts "oops, can't read $f: $err"
5605 set blob [lindex $treeidlist($diffids) $i]
5606 if {[catch {set bf [open [concat | git cat-file blob $blob] r]} err]} {
5607 puts "oops, error reading blob $blob: $err"
5611 fconfigure $bf -blocking 0
5612 filerun $bf [list getblobline $bf $diffids]
5613 $ctext config -state normal
5614 clear_ctext $commentend
5615 $ctext insert end "\n"
5616 $ctext insert end "$f\n" filesep
5617 $ctext config -state disabled
5618 $ctext yview $commentend
5622 proc getblobline {bf id} {
5623 global diffids cmitmode ctext
5625 if {$id ne $diffids || $cmitmode ne "tree"} {
5629 $ctext config -state normal
5631 while {[incr nl] <= 1000 && [gets $bf line] >= 0} {
5632 $ctext insert end "$line\n"
5635 # delete last newline
5636 $ctext delete "end - 2c" "end - 1c"
5640 $ctext config -state disabled
5641 return [expr {$nl >= 1000? 2: 1}]
5644 proc mergediff {id} {
5645 global diffmergeid mdifffd
5648 global limitdiffs viewfiles curview
5652 # this doesn't seem to actually affect anything...
5653 set cmd [concat | git diff-tree --no-commit-id --cc $id]
5654 if {$limitdiffs && $viewfiles($curview) ne {}} {
5655 set cmd [concat $cmd -- $viewfiles($curview)]
5657 if {[catch {set mdf [open $cmd r]} err]} {
5658 error_popup "Error getting merge diffs: $err"
5661 fconfigure $mdf -blocking 0
5662 set mdifffd($id) $mdf
5663 set np [llength $parents($curview,$id)]
5665 filerun $mdf [list getmergediffline $mdf $id $np]
5668 proc getmergediffline {mdf id np} {
5669 global diffmergeid ctext cflist mergemax
5670 global difffilestart mdifffd
5672 $ctext conf -state normal
5674 while {[incr nr] <= 1000 && [gets $mdf line] >= 0} {
5675 if {![info exists diffmergeid] || $id != $diffmergeid
5676 || $mdf != $mdifffd($id)} {
5680 if {[regexp {^diff --cc (.*)} $line match fname]} {
5681 # start of a new file
5682 $ctext insert end "\n"
5683 set here [$ctext index "end - 1c"]
5684 lappend difffilestart $here
5685 add_flist [list $fname]
5686 set l [expr {(78 - [string length $fname]) / 2}]
5687 set pad [string range "----------------------------------------" 1 $l]
5688 $ctext insert end "$pad $fname $pad\n" filesep
5689 } elseif {[regexp {^@@} $line]} {
5690 $ctext insert end "$line\n" hunksep
5691 } elseif {[regexp {^[0-9a-f]{40}$} $line] || [regexp {^index} $line]} {
5694 # parse the prefix - one ' ', '-' or '+' for each parent
5699 for {set j 0} {$j < $np} {incr j} {
5700 set c [string range $line $j $j]
5703 } elseif {$c == "-"} {
5705 } elseif {$c == "+"} {
5714 if {!$isbad && $minuses ne {} && $pluses eq {}} {
5715 # line doesn't appear in result, parents in $minuses have the line
5716 set num [lindex $minuses 0]
5717 } elseif {!$isbad && $pluses ne {} && $minuses eq {}} {
5718 # line appears in result, parents in $pluses don't have the line
5719 lappend tags mresult
5720 set num [lindex $spaces 0]
5723 if {$num >= $mergemax} {
5728 $ctext insert end "$line\n" $tags
5731 $ctext conf -state disabled
5736 return [expr {$nr >= 1000? 2: 1}]
5739 proc startdiff {ids} {
5740 global treediffs diffids treepending diffmergeid nullid nullid2
5744 catch {unset diffmergeid}
5745 if {![info exists treediffs($ids)] ||
5746 [lsearch -exact $ids $nullid] >= 0 ||
5747 [lsearch -exact $ids $nullid2] >= 0} {
5748 if {![info exists treepending]} {
5756 proc path_filter {filter name} {
5758 set l [string length $p]
5759 if {[string index $p end] eq "/"} {
5760 if {[string compare -length $l $p $name] == 0} {
5764 if {[string compare -length $l $p $name] == 0 &&
5765 ([string length $name] == $l ||
5766 [string index $name $l] eq "/")} {
5774 proc addtocflist {ids} {
5777 add_flist $treediffs($ids)
5781 proc diffcmd {ids flags} {
5782 global nullid nullid2
5784 set i [lsearch -exact $ids $nullid]
5785 set j [lsearch -exact $ids $nullid2]
5787 if {[llength $ids] > 1 && $j < 0} {
5788 # comparing working directory with some specific revision
5789 set cmd [concat | git diff-index $flags]
5791 lappend cmd -R [lindex $ids 1]
5793 lappend cmd [lindex $ids 0]
5796 # comparing working directory with index
5797 set cmd [concat | git diff-files $flags]
5802 } elseif {$j >= 0} {
5803 set cmd [concat | git diff-index --cached $flags]
5804 if {[llength $ids] > 1} {
5805 # comparing index with specific revision
5807 lappend cmd -R [lindex $ids 1]
5809 lappend cmd [lindex $ids 0]
5812 # comparing index with HEAD
5816 set cmd [concat | git diff-tree -r $flags $ids]
5821 proc gettreediffs {ids} {
5822 global treediff treepending
5824 set treepending $ids
5826 if {[catch {set gdtf [open [diffcmd $ids {--no-commit-id}] r]}]} return
5827 fconfigure $gdtf -blocking 0
5828 filerun $gdtf [list gettreediffline $gdtf $ids]
5831 proc gettreediffline {gdtf ids} {
5832 global treediff treediffs treepending diffids diffmergeid
5833 global cmitmode viewfiles curview limitdiffs
5836 while {[incr nr] <= 1000 && [gets $gdtf line] >= 0} {
5837 set i [string first "\t" $line]
5839 set file [string range $line [expr {$i+1}] end]
5840 if {[string index $file 0] eq "\""} {
5841 set file [lindex $file 0]
5843 lappend treediff $file
5847 return [expr {$nr >= 1000? 2: 1}]
5850 if {$limitdiffs && $viewfiles($curview) ne {}} {
5852 foreach f $treediff {
5853 if {[path_filter $viewfiles($curview) $f]} {
5857 set treediffs($ids) $flist
5859 set treediffs($ids) $treediff
5862 if {$cmitmode eq "tree"} {
5864 } elseif {$ids != $diffids} {
5865 if {![info exists diffmergeid]} {
5866 gettreediffs $diffids
5874 # empty string or positive integer
5875 proc diffcontextvalidate {v} {
5876 return [regexp {^(|[1-9][0-9]*)$} $v]
5879 proc diffcontextchange {n1 n2 op} {
5880 global diffcontextstring diffcontext
5882 if {[string is integer -strict $diffcontextstring]} {
5883 if {$diffcontextstring > 0} {
5884 set diffcontext $diffcontextstring
5890 proc getblobdiffs {ids} {
5891 global blobdifffd diffids env
5892 global diffinhdr treediffs
5894 global limitdiffs viewfiles curview
5896 set cmd [diffcmd $ids "-p -C --no-commit-id -U$diffcontext"]
5897 if {$limitdiffs && $viewfiles($curview) ne {}} {
5898 set cmd [concat $cmd -- $viewfiles($curview)]
5900 if {[catch {set bdf [open $cmd r]} err]} {
5901 puts "error getting diffs: $err"
5905 fconfigure $bdf -blocking 0
5906 set blobdifffd($ids) $bdf
5907 filerun $bdf [list getblobdiffline $bdf $diffids]
5910 proc setinlist {var i val} {
5913 while {[llength [set $var]] < $i} {
5916 if {[llength [set $var]] == $i} {
5923 proc makediffhdr {fname ids} {
5924 global ctext curdiffstart treediffs
5926 set i [lsearch -exact $treediffs($ids) $fname]
5928 setinlist difffilestart $i $curdiffstart
5930 set l [expr {(78 - [string length $fname]) / 2}]
5931 set pad [string range "----------------------------------------" 1 $l]
5932 $ctext insert $curdiffstart "$pad $fname $pad" filesep
5935 proc getblobdiffline {bdf ids} {
5936 global diffids blobdifffd ctext curdiffstart
5937 global diffnexthead diffnextnote difffilestart
5938 global diffinhdr treediffs
5941 $ctext conf -state normal
5942 while {[incr nr] <= 1000 && [gets $bdf line] >= 0} {
5943 if {$ids != $diffids || $bdf != $blobdifffd($ids)} {
5947 if {![string compare -length 11 "diff --git " $line]} {
5948 # trim off "diff --git "
5949 set line [string range $line 11 end]
5951 # start of a new file
5952 $ctext insert end "\n"
5953 set curdiffstart [$ctext index "end - 1c"]
5954 $ctext insert end "\n" filesep
5955 # If the name hasn't changed the length will be odd,
5956 # the middle char will be a space, and the two bits either
5957 # side will be a/name and b/name, or "a/name" and "b/name".
5958 # If the name has changed we'll get "rename from" and
5959 # "rename to" or "copy from" and "copy to" lines following this,
5960 # and we'll use them to get the filenames.
5961 # This complexity is necessary because spaces in the filename(s)
5962 # don't get escaped.
5963 set l [string length $line]
5964 set i [expr {$l / 2}]
5965 if {!(($l & 1) && [string index $line $i] eq " " &&
5966 [string range $line 2 [expr {$i - 1}]] eq \
5967 [string range $line [expr {$i + 3}] end])} {
5970 # unescape if quoted and chop off the a/ from the front
5971 if {[string index $line 0] eq "\""} {
5972 set fname [string range [lindex $line 0] 2 end]
5974 set fname [string range $line 2 [expr {$i - 1}]]
5976 makediffhdr $fname $ids
5978 } elseif {[regexp {^@@ -([0-9]+)(,[0-9]+)? \+([0-9]+)(,[0-9]+)? @@(.*)} \
5979 $line match f1l f1c f2l f2c rest]} {
5980 $ctext insert end "$line\n" hunksep
5983 } elseif {$diffinhdr} {
5984 if {![string compare -length 12 "rename from " $line]} {
5985 set fname [string range $line [expr 6 + [string first " from " $line] ] end]
5986 if {[string index $fname 0] eq "\""} {
5987 set fname [lindex $fname 0]
5989 set i [lsearch -exact $treediffs($ids) $fname]
5991 setinlist difffilestart $i $curdiffstart
5993 } elseif {![string compare -length 10 $line "rename to "] ||
5994 ![string compare -length 8 $line "copy to "]} {
5995 set fname [string range $line [expr 4 + [string first " to " $line] ] end]
5996 if {[string index $fname 0] eq "\""} {
5997 set fname [lindex $fname 0]
5999 makediffhdr $fname $ids
6000 } elseif {[string compare -length 3 $line "---"] == 0} {
6003 } elseif {[string compare -length 3 $line "+++"] == 0} {
6007 $ctext insert end "$line\n" filesep
6010 set x [string range $line 0 0]
6011 if {$x == "-" || $x == "+"} {
6012 set tag [expr {$x == "+"}]
6013 $ctext insert end "$line\n" d$tag
6014 } elseif {$x == " "} {
6015 $ctext insert end "$line\n"
6017 # "\ No newline at end of file",
6018 # or something else we don't recognize
6019 $ctext insert end "$line\n" hunksep
6023 $ctext conf -state disabled
6028 return [expr {$nr >= 1000? 2: 1}]
6031 proc changediffdisp {} {
6032 global ctext diffelide
6034 $ctext tag conf d0 -elide [lindex $diffelide 0]
6035 $ctext tag conf d1 -elide [lindex $diffelide 1]
6039 global difffilestart ctext
6040 set prev [lindex $difffilestart 0]
6041 set here [$ctext index @0,0]
6042 foreach loc $difffilestart {
6043 if {[$ctext compare $loc >= $here]} {
6053 global difffilestart ctext
6054 set here [$ctext index @0,0]
6055 foreach loc $difffilestart {
6056 if {[$ctext compare $loc > $here]} {
6063 proc clear_ctext {{first 1.0}} {
6064 global ctext smarktop smarkbot
6067 set l [lindex [split $first .] 0]
6068 if {![info exists smarktop] || [$ctext compare $first < $smarktop.0]} {
6071 if {![info exists smarkbot] || [$ctext compare $first < $smarkbot.0]} {
6074 $ctext delete $first end
6075 if {$first eq "1.0"} {
6076 catch {unset pendinglinks}
6080 proc settabs {{firstab {}}} {
6081 global firsttabstop tabstop ctext have_tk85
6083 if {$firstab ne {} && $have_tk85} {
6084 set firsttabstop $firstab
6086 set w [font measure textfont "0"]
6087 if {$firsttabstop != 0} {
6088 $ctext conf -tabs [list [expr {($firsttabstop + $tabstop) * $w}] \
6089 [expr {($firsttabstop + 2 * $tabstop) * $w}]]
6090 } elseif {$have_tk85 || $tabstop != 8} {
6091 $ctext conf -tabs [expr {$tabstop * $w}]
6093 $ctext conf -tabs {}
6097 proc incrsearch {name ix op} {
6098 global ctext searchstring searchdirn
6100 $ctext tag remove found 1.0 end
6101 if {[catch {$ctext index anchor}]} {
6102 # no anchor set, use start of selection, or of visible area
6103 set sel [$ctext tag ranges sel]
6105 $ctext mark set anchor [lindex $sel 0]
6106 } elseif {$searchdirn eq "-forwards"} {
6107 $ctext mark set anchor @0,0
6109 $ctext mark set anchor @0,[winfo height $ctext]
6112 if {$searchstring ne {}} {
6113 set here [$ctext search $searchdirn -- $searchstring anchor]
6122 global sstring ctext searchstring searchdirn
6125 $sstring icursor end
6126 set searchdirn -forwards
6127 if {$searchstring ne {}} {
6128 set sel [$ctext tag ranges sel]
6130 set start "[lindex $sel 0] + 1c"
6131 } elseif {[catch {set start [$ctext index anchor]}]} {
6134 set match [$ctext search -count mlen -- $searchstring $start]
6135 $ctext tag remove sel 1.0 end
6141 set mend "$match + $mlen c"
6142 $ctext tag add sel $match $mend
6143 $ctext mark unset anchor
6147 proc dosearchback {} {
6148 global sstring ctext searchstring searchdirn
6151 $sstring icursor end
6152 set searchdirn -backwards
6153 if {$searchstring ne {}} {
6154 set sel [$ctext tag ranges sel]
6156 set start [lindex $sel 0]
6157 } elseif {[catch {set start [$ctext index anchor]}]} {
6158 set start @0,[winfo height $ctext]
6160 set match [$ctext search -backwards -count ml -- $searchstring $start]
6161 $ctext tag remove sel 1.0 end
6167 set mend "$match + $ml c"
6168 $ctext tag add sel $match $mend
6169 $ctext mark unset anchor
6173 proc searchmark {first last} {
6174 global ctext searchstring
6178 set match [$ctext search -count mlen -- $searchstring $mend $last.end]
6179 if {$match eq {}} break
6180 set mend "$match + $mlen c"
6181 $ctext tag add found $match $mend
6185 proc searchmarkvisible {doall} {
6186 global ctext smarktop smarkbot
6188 set topline [lindex [split [$ctext index @0,0] .] 0]
6189 set botline [lindex [split [$ctext index @0,[winfo height $ctext]] .] 0]
6190 if {$doall || $botline < $smarktop || $topline > $smarkbot} {
6191 # no overlap with previous
6192 searchmark $topline $botline
6193 set smarktop $topline
6194 set smarkbot $botline
6196 if {$topline < $smarktop} {
6197 searchmark $topline [expr {$smarktop-1}]
6198 set smarktop $topline
6200 if {$botline > $smarkbot} {
6201 searchmark [expr {$smarkbot+1}] $botline
6202 set smarkbot $botline
6207 proc scrolltext {f0 f1} {
6210 .bleft.sb set $f0 $f1
6211 if {$searchstring ne {}} {
6217 global linespc charspc canvx0 canvy0
6218 global xspc1 xspc2 lthickness
6220 set linespc [font metrics mainfont -linespace]
6221 set charspc [font measure mainfont "m"]
6222 set canvy0 [expr {int(3 + 0.5 * $linespc)}]
6223 set canvx0 [expr {int(3 + 0.5 * $linespc)}]
6224 set lthickness [expr {int($linespc / 9) + 1}]
6225 set xspc1(0) $linespc
6233 set ymax [lindex [$canv cget -scrollregion] 3]
6234 if {$ymax eq {} || $ymax == 0} return
6235 set span [$canv yview]
6238 allcanvs yview moveto [lindex $span 0]
6240 if {[info exists selectedline]} {
6241 selectline $selectedline 0
6242 allcanvs yview moveto [lindex $span 0]
6246 proc parsefont {f n} {
6249 set fontattr($f,family) [lindex $n 0]
6251 if {$s eq {} || $s == 0} {
6254 set s [expr {int(-$s / [winfo fpixels . 1p] + 0.5)}]
6256 set fontattr($f,size) $s
6257 set fontattr($f,weight) normal
6258 set fontattr($f,slant) roman
6259 foreach style [lrange $n 2 end] {
6262 "bold" {set fontattr($f,weight) $style}
6264 "italic" {set fontattr($f,slant) $style}
6269 proc fontflags {f {isbold 0}} {
6272 return [list -family $fontattr($f,family) -size $fontattr($f,size) \
6273 -weight [expr {$isbold? "bold": $fontattr($f,weight)}] \
6274 -slant $fontattr($f,slant)]
6280 set n [list $fontattr($f,family) $fontattr($f,size)]
6281 if {$fontattr($f,weight) eq "bold"} {
6284 if {$fontattr($f,slant) eq "italic"} {
6290 proc incrfont {inc} {
6291 global mainfont textfont ctext canv cflist showrefstop
6292 global stopped entries fontattr
6295 set s $fontattr(mainfont,size)
6300 set fontattr(mainfont,size) $s
6301 font config mainfont -size $s
6302 font config mainfontbold -size $s
6303 set mainfont [fontname mainfont]
6304 set s $fontattr(textfont,size)
6309 set fontattr(textfont,size) $s
6310 font config textfont -size $s
6311 font config textfontbold -size $s
6312 set textfont [fontname textfont]
6319 global sha1entry sha1string
6320 if {[string length $sha1string] == 40} {
6321 $sha1entry delete 0 end
6325 proc sha1change {n1 n2 op} {
6326 global sha1string currentid sha1but
6327 if {$sha1string == {}
6328 || ([info exists currentid] && $sha1string == $currentid)} {
6333 if {[$sha1but cget -state] == $state} return
6334 if {$state == "normal"} {
6335 $sha1but conf -state normal -relief raised -text "Goto: "
6337 $sha1but conf -state disabled -relief flat -text "SHA1 ID: "
6341 proc gotocommit {} {
6342 global sha1string tagids headids curview varcid
6344 if {$sha1string == {}
6345 || ([info exists currentid] && $sha1string == $currentid)} return
6346 if {[info exists tagids($sha1string)]} {
6347 set id $tagids($sha1string)
6348 } elseif {[info exists headids($sha1string)]} {
6349 set id $headids($sha1string)
6351 set id [string tolower $sha1string]
6352 if {[regexp {^[0-9a-f]{4,39}$} $id]} {
6353 set matches [array names varcid "$curview,$id*"]
6354 if {$matches ne {}} {
6355 if {[llength $matches] > 1} {
6356 error_popup "Short SHA1 id $id is ambiguous"
6359 set id [lindex [split [lindex $matches 0] ","] 1]
6363 if {[commitinview $id $curview]} {
6364 selectline [rowofcommit $id] 1
6367 if {[regexp {^[0-9a-fA-F]{4,}$} $sha1string]} {
6372 error_popup "$type $sha1string is not known"
6375 proc lineenter {x y id} {
6376 global hoverx hovery hoverid hovertimer
6377 global commitinfo canv
6379 if {![info exists commitinfo($id)] && ![getcommit $id]} return
6383 if {[info exists hovertimer]} {
6384 after cancel $hovertimer
6386 set hovertimer [after 500 linehover]
6390 proc linemotion {x y id} {
6391 global hoverx hovery hoverid hovertimer
6393 if {[info exists hoverid] && $id == $hoverid} {
6396 if {[info exists hovertimer]} {
6397 after cancel $hovertimer
6399 set hovertimer [after 500 linehover]
6403 proc lineleave {id} {
6404 global hoverid hovertimer canv
6406 if {[info exists hoverid] && $id == $hoverid} {
6408 if {[info exists hovertimer]} {
6409 after cancel $hovertimer
6417 global hoverx hovery hoverid hovertimer
6418 global canv linespc lthickness
6421 set text [lindex $commitinfo($hoverid) 0]
6422 set ymax [lindex [$canv cget -scrollregion] 3]
6423 if {$ymax == {}} return
6424 set yfrac [lindex [$canv yview] 0]
6425 set x [expr {$hoverx + 2 * $linespc}]
6426 set y [expr {$hovery + $yfrac * $ymax - $linespc / 2}]
6427 set x0 [expr {$x - 2 * $lthickness}]
6428 set y0 [expr {$y - 2 * $lthickness}]
6429 set x1 [expr {$x + [font measure mainfont $text] + 2 * $lthickness}]
6430 set y1 [expr {$y + $linespc + 2 * $lthickness}]
6431 set t [$canv create rectangle $x0 $y0 $x1 $y1 \
6432 -fill \#ffff80 -outline black -width 1 -tags hover]
6434 set t [$canv create text $x $y -anchor nw -text $text -tags hover \
6439 proc clickisonarrow {id y} {
6442 set ranges [rowranges $id]
6443 set thresh [expr {2 * $lthickness + 6}]
6444 set n [expr {[llength $ranges] - 1}]
6445 for {set i 1} {$i < $n} {incr i} {
6446 set row [lindex $ranges $i]
6447 if {abs([yc $row] - $y) < $thresh} {
6454 proc arrowjump {id n y} {
6457 # 1 <-> 2, 3 <-> 4, etc...
6458 set n [expr {(($n - 1) ^ 1) + 1}]
6459 set row [lindex [rowranges $id] $n]
6461 set ymax [lindex [$canv cget -scrollregion] 3]
6462 if {$ymax eq {} || $ymax <= 0} return
6463 set view [$canv yview]
6464 set yspan [expr {[lindex $view 1] - [lindex $view 0]}]
6465 set yfrac [expr {$yt / $ymax - $yspan / 2}]
6469 allcanvs yview moveto $yfrac
6472 proc lineclick {x y id isnew} {
6473 global ctext commitinfo children canv thickerline curview
6475 if {![info exists commitinfo($id)] && ![getcommit $id]} return
6480 # draw this line thicker than normal
6484 set ymax [lindex [$canv cget -scrollregion] 3]
6485 if {$ymax eq {}} return
6486 set yfrac [lindex [$canv yview] 0]
6487 set y [expr {$y + $yfrac * $ymax}]
6489 set dirn [clickisonarrow $id $y]
6491 arrowjump $id $dirn $y
6496 addtohistory [list lineclick $x $y $id 0]
6498 # fill the details pane with info about this line
6499 $ctext conf -state normal
6502 $ctext insert end "Parent:\t"
6503 $ctext insert end $id link0
6505 set info $commitinfo($id)
6506 $ctext insert end "\n\t[lindex $info 0]\n"
6507 $ctext insert end "\tAuthor:\t[lindex $info 1]\n"
6508 set date [formatdate [lindex $info 2]]
6509 $ctext insert end "\tDate:\t$date\n"
6510 set kids $children($curview,$id)
6512 $ctext insert end "\nChildren:"
6514 foreach child $kids {
6516 if {![info exists commitinfo($child)] && ![getcommit $child]} continue
6517 set info $commitinfo($child)
6518 $ctext insert end "\n\t"
6519 $ctext insert end $child link$i
6520 setlink $child link$i
6521 $ctext insert end "\n\t[lindex $info 0]"
6522 $ctext insert end "\n\tAuthor:\t[lindex $info 1]"
6523 set date [formatdate [lindex $info 2]]
6524 $ctext insert end "\n\tDate:\t$date\n"
6527 $ctext conf -state disabled
6531 proc normalline {} {
6533 if {[info exists thickerline]} {
6542 if {[commitinview $id $curview]} {
6543 selectline [rowofcommit $id] 1
6549 if {![info exists startmstime]} {
6550 set startmstime [clock clicks -milliseconds]
6552 return [format "%.3f" [expr {([clock click -milliseconds] - $startmstime) / 1000.0}]]
6555 proc rowmenu {x y id} {
6556 global rowctxmenu selectedline rowmenuid curview
6557 global nullid nullid2 fakerowmenu mainhead
6561 if {![info exists selectedline]
6562 || [rowofcommit $id] eq $selectedline} {
6567 if {$id ne $nullid && $id ne $nullid2} {
6568 set menu $rowctxmenu
6569 $menu entryconfigure 7 -label "Reset $mainhead branch to here"
6571 set menu $fakerowmenu
6573 $menu entryconfigure "Diff this*" -state $state
6574 $menu entryconfigure "Diff selected*" -state $state
6575 $menu entryconfigure "Make patch" -state $state
6576 tk_popup $menu $x $y
6579 proc diffvssel {dirn} {
6580 global rowmenuid selectedline
6582 if {![info exists selectedline]} return
6584 set oldid [commitonrow $selectedline]
6585 set newid $rowmenuid
6587 set oldid $rowmenuid
6588 set newid [commitonrow $selectedline]
6590 addtohistory [list doseldiff $oldid $newid]
6591 doseldiff $oldid $newid
6594 proc doseldiff {oldid newid} {
6598 $ctext conf -state normal
6601 $ctext insert end "From "
6602 $ctext insert end $oldid link0
6603 setlink $oldid link0
6604 $ctext insert end "\n "
6605 $ctext insert end [lindex $commitinfo($oldid) 0]
6606 $ctext insert end "\n\nTo "
6607 $ctext insert end $newid link1
6608 setlink $newid link1
6609 $ctext insert end "\n "
6610 $ctext insert end [lindex $commitinfo($newid) 0]
6611 $ctext insert end "\n"
6612 $ctext conf -state disabled
6613 $ctext tag remove found 1.0 end
6614 startdiff [list $oldid $newid]
6618 global rowmenuid currentid commitinfo patchtop patchnum
6620 if {![info exists currentid]} return
6621 set oldid $currentid
6622 set oldhead [lindex $commitinfo($oldid) 0]
6623 set newid $rowmenuid
6624 set newhead [lindex $commitinfo($newid) 0]
6627 catch {destroy $top}
6629 label $top.title -text "Generate patch"
6630 grid $top.title - -pady 10
6631 label $top.from -text "From:"
6632 entry $top.fromsha1 -width 40 -relief flat
6633 $top.fromsha1 insert 0 $oldid
6634 $top.fromsha1 conf -state readonly
6635 grid $top.from $top.fromsha1 -sticky w
6636 entry $top.fromhead -width 60 -relief flat
6637 $top.fromhead insert 0 $oldhead
6638 $top.fromhead conf -state readonly
6639 grid x $top.fromhead -sticky w
6640 label $top.to -text "To:"
6641 entry $top.tosha1 -width 40 -relief flat
6642 $top.tosha1 insert 0 $newid
6643 $top.tosha1 conf -state readonly
6644 grid $top.to $top.tosha1 -sticky w
6645 entry $top.tohead -width 60 -relief flat
6646 $top.tohead insert 0 $newhead
6647 $top.tohead conf -state readonly
6648 grid x $top.tohead -sticky w
6649 button $top.rev -text "Reverse" -command mkpatchrev -padx 5
6650 grid $top.rev x -pady 10
6651 label $top.flab -text "Output file:"
6652 entry $top.fname -width 60
6653 $top.fname insert 0 [file normalize "patch$patchnum.patch"]
6655 grid $top.flab $top.fname -sticky w
6657 button $top.buts.gen -text "Generate" -command mkpatchgo
6658 button $top.buts.can -text "Cancel" -command mkpatchcan
6659 grid $top.buts.gen $top.buts.can
6660 grid columnconfigure $top.buts 0 -weight 1 -uniform a
6661 grid columnconfigure $top.buts 1 -weight 1 -uniform a
6662 grid $top.buts - -pady 10 -sticky ew
6666 proc mkpatchrev {} {
6669 set oldid [$patchtop.fromsha1 get]
6670 set oldhead [$patchtop.fromhead get]
6671 set newid [$patchtop.tosha1 get]
6672 set newhead [$patchtop.tohead get]
6673 foreach e [list fromsha1 fromhead tosha1 tohead] \
6674 v [list $newid $newhead $oldid $oldhead] {
6675 $patchtop.$e conf -state normal
6676 $patchtop.$e delete 0 end
6677 $patchtop.$e insert 0 $v
6678 $patchtop.$e conf -state readonly
6683 global patchtop nullid nullid2
6685 set oldid [$patchtop.fromsha1 get]
6686 set newid [$patchtop.tosha1 get]
6687 set fname [$patchtop.fname get]
6688 set cmd [diffcmd [list $oldid $newid] -p]
6689 # trim off the initial "|"
6690 set cmd [lrange $cmd 1 end]
6691 lappend cmd >$fname &
6692 if {[catch {eval exec $cmd} err]} {
6693 error_popup "Error creating patch: $err"
6695 catch {destroy $patchtop}
6699 proc mkpatchcan {} {
6702 catch {destroy $patchtop}
6707 global rowmenuid mktagtop commitinfo
6711 catch {destroy $top}
6713 label $top.title -text "Create tag"
6714 grid $top.title - -pady 10
6715 label $top.id -text "ID:"
6716 entry $top.sha1 -width 40 -relief flat
6717 $top.sha1 insert 0 $rowmenuid
6718 $top.sha1 conf -state readonly
6719 grid $top.id $top.sha1 -sticky w
6720 entry $top.head -width 60 -relief flat
6721 $top.head insert 0 [lindex $commitinfo($rowmenuid) 0]
6722 $top.head conf -state readonly
6723 grid x $top.head -sticky w
6724 label $top.tlab -text "Tag name:"
6725 entry $top.tag -width 60
6726 grid $top.tlab $top.tag -sticky w
6728 button $top.buts.gen -text "Create" -command mktaggo
6729 button $top.buts.can -text "Cancel" -command mktagcan
6730 grid $top.buts.gen $top.buts.can
6731 grid columnconfigure $top.buts 0 -weight 1 -uniform a
6732 grid columnconfigure $top.buts 1 -weight 1 -uniform a
6733 grid $top.buts - -pady 10 -sticky ew
6738 global mktagtop env tagids idtags
6740 set id [$mktagtop.sha1 get]
6741 set tag [$mktagtop.tag get]
6743 error_popup "No tag name specified"
6746 if {[info exists tagids($tag)]} {
6747 error_popup "Tag \"$tag\" already exists"
6752 set fname [file join $dir "refs/tags" $tag]
6753 set f [open $fname w]
6757 error_popup "Error creating tag: $err"
6761 set tagids($tag) $id
6762 lappend idtags($id) $tag
6769 proc redrawtags {id} {
6770 global canv linehtag idpos selectedline curview
6771 global canvxmax iddrawn
6773 if {![commitinview $id $curview]} return
6774 if {![info exists iddrawn($id)]} return
6775 drawcommits [rowofcommit $id]
6776 $canv delete tag.$id
6777 set xt [eval drawtags $id $idpos($id)]
6778 $canv coords $linehtag([rowofcommit $id]) $xt [lindex $idpos($id) 2]
6779 set text [$canv itemcget $linehtag([rowofcommit $id]) -text]
6780 set xr [expr {$xt + [font measure mainfont $text]}]
6781 if {$xr > $canvxmax} {
6785 if {[info exists selectedline]
6786 && $selectedline == [rowofcommit $id]} {
6787 selectline $selectedline 0
6794 catch {destroy $mktagtop}
6803 proc writecommit {} {
6804 global rowmenuid wrcomtop commitinfo wrcomcmd
6806 set top .writecommit
6808 catch {destroy $top}
6810 label $top.title -text "Write commit to file"
6811 grid $top.title - -pady 10
6812 label $top.id -text "ID:"
6813 entry $top.sha1 -width 40 -relief flat
6814 $top.sha1 insert 0 $rowmenuid
6815 $top.sha1 conf -state readonly
6816 grid $top.id $top.sha1 -sticky w
6817 entry $top.head -width 60 -relief flat
6818 $top.head insert 0 [lindex $commitinfo($rowmenuid) 0]
6819 $top.head conf -state readonly
6820 grid x $top.head -sticky w
6821 label $top.clab -text "Command:"
6822 entry $top.cmd -width 60 -textvariable wrcomcmd
6823 grid $top.clab $top.cmd -sticky w -pady 10
6824 label $top.flab -text "Output file:"
6825 entry $top.fname -width 60
6826 $top.fname insert 0 [file normalize "commit-[string range $rowmenuid 0 6]"]
6827 grid $top.flab $top.fname -sticky w
6829 button $top.buts.gen -text "Write" -command wrcomgo
6830 button $top.buts.can -text "Cancel" -command wrcomcan
6831 grid $top.buts.gen $top.buts.can
6832 grid columnconfigure $top.buts 0 -weight 1 -uniform a
6833 grid columnconfigure $top.buts 1 -weight 1 -uniform a
6834 grid $top.buts - -pady 10 -sticky ew
6841 set id [$wrcomtop.sha1 get]
6842 set cmd "echo $id | [$wrcomtop.cmd get]"
6843 set fname [$wrcomtop.fname get]
6844 if {[catch {exec sh -c $cmd >$fname &} err]} {
6845 error_popup "Error writing commit: $err"
6847 catch {destroy $wrcomtop}
6854 catch {destroy $wrcomtop}
6859 global rowmenuid mkbrtop
6862 catch {destroy $top}
6864 label $top.title -text "Create new branch"
6865 grid $top.title - -pady 10
6866 label $top.id -text "ID:"
6867 entry $top.sha1 -width 40 -relief flat
6868 $top.sha1 insert 0 $rowmenuid
6869 $top.sha1 conf -state readonly
6870 grid $top.id $top.sha1 -sticky w
6871 label $top.nlab -text "Name:"
6872 entry $top.name -width 40
6873 grid $top.nlab $top.name -sticky w
6875 button $top.buts.go -text "Create" -command [list mkbrgo $top]
6876 button $top.buts.can -text "Cancel" -command "catch {destroy $top}"
6877 grid $top.buts.go $top.buts.can
6878 grid columnconfigure $top.buts 0 -weight 1 -uniform a
6879 grid columnconfigure $top.buts 1 -weight 1 -uniform a
6880 grid $top.buts - -pady 10 -sticky ew
6885 global headids idheads
6887 set name [$top.name get]
6888 set id [$top.sha1 get]
6890 error_popup "Please specify a name for the new branch"
6893 catch {destroy $top}
6897 exec git branch $name $id
6902 set headids($name) $id
6903 lappend idheads($id) $name
6912 proc cherrypick {} {
6913 global rowmenuid curview
6916 set oldhead [exec git rev-parse HEAD]
6917 set dheads [descheads $rowmenuid]
6918 if {$dheads ne {} && [lsearch -exact $dheads $oldhead] >= 0} {
6919 set ok [confirm_popup "Commit [string range $rowmenuid 0 7] is already\
6920 included in branch $mainhead -- really re-apply it?"]
6923 nowbusy cherrypick "Cherry-picking"
6925 # Unfortunately git-cherry-pick writes stuff to stderr even when
6926 # no error occurs, and exec takes that as an indication of error...
6927 if {[catch {exec sh -c "git cherry-pick -r $rowmenuid 2>&1"} err]} {
6932 set newhead [exec git rev-parse HEAD]
6933 if {$newhead eq $oldhead} {
6935 error_popup "No changes committed"
6938 addnewchild $newhead $oldhead
6939 if {[commitinview $oldhead $curview]} {
6940 insertrow $newhead $oldhead $curview
6941 if {$mainhead ne {}} {
6942 movehead $newhead $mainhead
6943 movedhead $newhead $mainhead
6952 global mainheadid mainhead rowmenuid confirm_ok resettype
6955 set w ".confirmreset"
6958 wm title $w "Confirm reset"
6959 message $w.m -text \
6960 "Reset branch $mainhead to [string range $rowmenuid 0 7]?" \
6961 -justify center -aspect 1000
6962 pack $w.m -side top -fill x -padx 20 -pady 20
6963 frame $w.f -relief sunken -border 2
6964 message $w.f.rt -text "Reset type:" -aspect 1000
6965 grid $w.f.rt -sticky w
6967 radiobutton $w.f.soft -value soft -variable resettype -justify left \
6968 -text "Soft: Leave working tree and index untouched"
6969 grid $w.f.soft -sticky w
6970 radiobutton $w.f.mixed -value mixed -variable resettype -justify left \
6971 -text "Mixed: Leave working tree untouched, reset index"
6972 grid $w.f.mixed -sticky w
6973 radiobutton $w.f.hard -value hard -variable resettype -justify left \
6974 -text "Hard: Reset working tree and index\n(discard ALL local changes)"
6975 grid $w.f.hard -sticky w
6976 pack $w.f -side top -fill x
6977 button $w.ok -text OK -command "set confirm_ok 1; destroy $w"
6978 pack $w.ok -side left -fill x -padx 20 -pady 20
6979 button $w.cancel -text Cancel -command "destroy $w"
6980 pack $w.cancel -side right -fill x -padx 20 -pady 20
6981 bind $w <Visibility> "grab $w; focus $w"
6983 if {!$confirm_ok} return
6984 if {[catch {set fd [open \
6985 [list | sh -c "git reset --$resettype $rowmenuid 2>&1"] r]} err]} {
6989 filerun $fd [list readresetstat $fd]
6990 nowbusy reset "Resetting"
6994 proc readresetstat {fd} {
6995 global mainhead mainheadid showlocalchanges rprogcoord
6997 if {[gets $fd line] >= 0} {
6998 if {[regexp {([0-9]+)% \(([0-9]+)/([0-9]+)\)} $line match p m n]} {
6999 set rprogcoord [expr {1.0 * $m / $n}]
7007 if {[catch {close $fd} err]} {
7010 set oldhead $mainheadid
7011 set newhead [exec git rev-parse HEAD]
7012 if {$newhead ne $oldhead} {
7013 movehead $newhead $mainhead
7014 movedhead $newhead $mainhead
7015 set mainheadid $newhead
7019 if {$showlocalchanges} {
7025 # context menu for a head
7026 proc headmenu {x y id head} {
7027 global headmenuid headmenuhead headctxmenu mainhead
7031 set headmenuhead $head
7033 if {$head eq $mainhead} {
7036 $headctxmenu entryconfigure 0 -state $state
7037 $headctxmenu entryconfigure 1 -state $state
7038 tk_popup $headctxmenu $x $y
7042 global headmenuid headmenuhead mainhead headids
7043 global showlocalchanges mainheadid
7045 # check the tree is clean first??
7046 set oldmainhead $mainhead
7047 nowbusy checkout "Checking out"
7051 exec git checkout -q $headmenuhead
7057 set mainhead $headmenuhead
7058 set mainheadid $headmenuid
7059 if {[info exists headids($oldmainhead)]} {
7060 redrawtags $headids($oldmainhead)
7062 redrawtags $headmenuid
7064 if {$showlocalchanges} {
7070 global headmenuid headmenuhead mainhead
7073 set head $headmenuhead
7075 # this check shouldn't be needed any more...
7076 if {$head eq $mainhead} {
7077 error_popup "Cannot delete the currently checked-out branch"
7080 set dheads [descheads $id]
7081 if {[llength $dheads] == 1 && $idheads($dheads) eq $head} {
7082 # the stuff on this branch isn't on any other branch
7083 if {![confirm_popup "The commits on branch $head aren't on any other\
7084 branch.\nReally delete branch $head?"]} return
7088 if {[catch {exec git branch -D $head} err]} {
7093 removehead $id $head
7094 removedhead $id $head
7101 # Display a list of tags and heads
7103 global showrefstop bgcolor fgcolor selectbgcolor
7104 global bglist fglist reflistfilter reflist maincursor
7107 set showrefstop $top
7108 if {[winfo exists $top]} {
7114 wm title $top "Tags and heads: [file tail [pwd]]"
7115 text $top.list -background $bgcolor -foreground $fgcolor \
7116 -selectbackground $selectbgcolor -font mainfont \
7117 -xscrollcommand "$top.xsb set" -yscrollcommand "$top.ysb set" \
7118 -width 30 -height 20 -cursor $maincursor \
7119 -spacing1 1 -spacing3 1 -state disabled
7120 $top.list tag configure highlight -background $selectbgcolor
7121 lappend bglist $top.list
7122 lappend fglist $top.list
7123 scrollbar $top.ysb -command "$top.list yview" -orient vertical
7124 scrollbar $top.xsb -command "$top.list xview" -orient horizontal
7125 grid $top.list $top.ysb -sticky nsew
7126 grid $top.xsb x -sticky ew
7128 label $top.f.l -text "Filter: " -font uifont
7129 entry $top.f.e -width 20 -textvariable reflistfilter -font uifont
7130 set reflistfilter "*"
7131 trace add variable reflistfilter write reflistfilter_change
7132 pack $top.f.e -side right -fill x -expand 1
7133 pack $top.f.l -side left
7134 grid $top.f - -sticky ew -pady 2
7135 button $top.close -command [list destroy $top] -text "Close" \
7138 grid columnconfigure $top 0 -weight 1
7139 grid rowconfigure $top 0 -weight 1
7140 bind $top.list <1> {break}
7141 bind $top.list <B1-Motion> {break}
7142 bind $top.list <ButtonRelease-1> {sel_reflist %W %x %y; break}
7147 proc sel_reflist {w x y} {
7148 global showrefstop reflist headids tagids otherrefids
7150 if {![winfo exists $showrefstop]} return
7151 set l [lindex [split [$w index "@$x,$y"] "."] 0]
7152 set ref [lindex $reflist [expr {$l-1}]]
7153 set n [lindex $ref 0]
7154 switch -- [lindex $ref 1] {
7155 "H" {selbyid $headids($n)}
7156 "T" {selbyid $tagids($n)}
7157 "o" {selbyid $otherrefids($n)}
7159 $showrefstop.list tag add highlight $l.0 "$l.0 lineend"
7162 proc unsel_reflist {} {
7165 if {![info exists showrefstop] || ![winfo exists $showrefstop]} return
7166 $showrefstop.list tag remove highlight 0.0 end
7169 proc reflistfilter_change {n1 n2 op} {
7170 global reflistfilter
7172 after cancel refill_reflist
7173 after 200 refill_reflist
7176 proc refill_reflist {} {
7177 global reflist reflistfilter showrefstop headids tagids otherrefids
7178 global curview commitinterest
7180 if {![info exists showrefstop] || ![winfo exists $showrefstop]} return
7182 foreach n [array names headids] {
7183 if {[string match $reflistfilter $n]} {
7184 if {[commitinview $headids($n) $curview]} {
7185 lappend refs [list $n H]
7187 set commitinterest($headids($n)) {run refill_reflist}
7191 foreach n [array names tagids] {
7192 if {[string match $reflistfilter $n]} {
7193 if {[commitinview $tagids($n) $curview]} {
7194 lappend refs [list $n T]
7196 set commitinterest($tagids($n)) {run refill_reflist}
7200 foreach n [array names otherrefids] {
7201 if {[string match $reflistfilter $n]} {
7202 if {[commitinview $otherrefids($n) $curview]} {
7203 lappend refs [list $n o]
7205 set commitinterest($otherrefids($n)) {run refill_reflist}
7209 set refs [lsort -index 0 $refs]
7210 if {$refs eq $reflist} return
7212 # Update the contents of $showrefstop.list according to the
7213 # differences between $reflist (old) and $refs (new)
7214 $showrefstop.list conf -state normal
7215 $showrefstop.list insert end "\n"
7218 while {$i < [llength $reflist] || $j < [llength $refs]} {
7219 if {$i < [llength $reflist]} {
7220 if {$j < [llength $refs]} {
7221 set cmp [string compare [lindex $reflist $i 0] \
7222 [lindex $refs $j 0]]
7224 set cmp [string compare [lindex $reflist $i 1] \
7225 [lindex $refs $j 1]]
7235 $showrefstop.list delete "[expr {$j+1}].0" "[expr {$j+2}].0"
7243 set l [expr {$j + 1}]
7244 $showrefstop.list image create $l.0 -align baseline \
7245 -image reficon-[lindex $refs $j 1] -padx 2
7246 $showrefstop.list insert $l.1 "[lindex $refs $j 0]\n"
7252 # delete last newline
7253 $showrefstop.list delete end-2c end-1c
7254 $showrefstop.list conf -state disabled
7257 # Stuff for finding nearby tags
7258 proc getallcommits {} {
7259 global allcommits nextarc seeds allccache allcwait cachedarcs allcupdate
7260 global idheads idtags idotherrefs allparents tagobjid
7262 if {![info exists allcommits]} {
7268 set allccache [file join [gitdir] "gitk.cache"]
7270 set f [open $allccache r]
7279 set cmd [list | git rev-list --parents]
7280 set allcupdate [expr {$seeds ne {}}]
7284 set refs [concat [array names idheads] [array names idtags] \
7285 [array names idotherrefs]]
7288 foreach name [array names tagobjid] {
7289 lappend tagobjs $tagobjid($name)
7291 foreach id [lsort -unique $refs] {
7292 if {![info exists allparents($id)] &&
7293 [lsearch -exact $tagobjs $id] < 0} {
7304 set fd [open [concat $cmd $ids] r]
7305 fconfigure $fd -blocking 0
7308 filerun $fd [list getallclines $fd]
7314 # Since most commits have 1 parent and 1 child, we group strings of
7315 # such commits into "arcs" joining branch/merge points (BMPs), which
7316 # are commits that either don't have 1 parent or don't have 1 child.
7318 # arcnos(id) - incoming arcs for BMP, arc we're on for other nodes
7319 # arcout(id) - outgoing arcs for BMP
7320 # arcids(a) - list of IDs on arc including end but not start
7321 # arcstart(a) - BMP ID at start of arc
7322 # arcend(a) - BMP ID at end of arc
7323 # growing(a) - arc a is still growing
7324 # arctags(a) - IDs out of arcids (excluding end) that have tags
7325 # archeads(a) - IDs out of arcids (excluding end) that have heads
7326 # The start of an arc is at the descendent end, so "incoming" means
7327 # coming from descendents, and "outgoing" means going towards ancestors.
7329 proc getallclines {fd} {
7330 global allparents allchildren idtags idheads nextarc
7331 global arcnos arcids arctags arcout arcend arcstart archeads growing
7332 global seeds allcommits cachedarcs allcupdate
7335 while {[incr nid] <= 1000 && [gets $fd line] >= 0} {
7336 set id [lindex $line 0]
7337 if {[info exists allparents($id)]} {
7342 set olds [lrange $line 1 end]
7343 set allparents($id) $olds
7344 if {![info exists allchildren($id)]} {
7345 set allchildren($id) {}
7350 if {[llength $olds] == 1 && [llength $a] == 1} {
7351 lappend arcids($a) $id
7352 if {[info exists idtags($id)]} {
7353 lappend arctags($a) $id
7355 if {[info exists idheads($id)]} {
7356 lappend archeads($a) $id
7358 if {[info exists allparents($olds)]} {
7359 # seen parent already
7360 if {![info exists arcout($olds)]} {
7363 lappend arcids($a) $olds
7364 set arcend($a) $olds
7367 lappend allchildren($olds) $id
7368 lappend arcnos($olds) $a
7372 foreach a $arcnos($id) {
7373 lappend arcids($a) $id
7380 lappend allchildren($p) $id
7381 set a [incr nextarc]
7382 set arcstart($a) $id
7389 if {[info exists allparents($p)]} {
7390 # seen it already, may need to make a new branch
7391 if {![info exists arcout($p)]} {
7394 lappend arcids($a) $p
7398 lappend arcnos($p) $a
7403 global cached_dheads cached_dtags cached_atags
7404 catch {unset cached_dheads}
7405 catch {unset cached_dtags}
7406 catch {unset cached_atags}
7409 return [expr {$nid >= 1000? 2: 1}]
7413 fconfigure $fd -blocking 1
7416 # got an error reading the list of commits
7417 # if we were updating, try rereading the whole thing again
7423 error_popup "Error reading commit topology information;\
7424 branch and preceding/following tag information\
7425 will be incomplete.\n($err)"
7428 if {[incr allcommits -1] == 0} {
7438 proc recalcarc {a} {
7439 global arctags archeads arcids idtags idheads
7443 foreach id [lrange $arcids($a) 0 end-1] {
7444 if {[info exists idtags($id)]} {
7447 if {[info exists idheads($id)]} {
7452 set archeads($a) $ah
7456 global arcnos arcids nextarc arctags archeads idtags idheads
7457 global arcstart arcend arcout allparents growing
7460 if {[llength $a] != 1} {
7461 puts "oops splitarc called but [llength $a] arcs already"
7465 set i [lsearch -exact $arcids($a) $p]
7467 puts "oops splitarc $p not in arc $a"
7470 set na [incr nextarc]
7471 if {[info exists arcend($a)]} {
7472 set arcend($na) $arcend($a)
7474 set l [lindex $allparents([lindex $arcids($a) end]) 0]
7475 set j [lsearch -exact $arcnos($l) $a]
7476 set arcnos($l) [lreplace $arcnos($l) $j $j $na]
7478 set tail [lrange $arcids($a) [expr {$i+1}] end]
7479 set arcids($a) [lrange $arcids($a) 0 $i]
7481 set arcstart($na) $p
7483 set arcids($na) $tail
7484 if {[info exists growing($a)]} {
7490 if {[llength $arcnos($id)] == 1} {
7493 set j [lsearch -exact $arcnos($id) $a]
7494 set arcnos($id) [lreplace $arcnos($id) $j $j $na]
7498 # reconstruct tags and heads lists
7499 if {$arctags($a) ne {} || $archeads($a) ne {}} {
7504 set archeads($na) {}
7508 # Update things for a new commit added that is a child of one
7509 # existing commit. Used when cherry-picking.
7510 proc addnewchild {id p} {
7511 global allparents allchildren idtags nextarc
7512 global arcnos arcids arctags arcout arcend arcstart archeads growing
7513 global seeds allcommits
7515 if {![info exists allcommits] || ![info exists arcnos($p)]} return
7516 set allparents($id) [list $p]
7517 set allchildren($id) {}
7520 lappend allchildren($p) $id
7521 set a [incr nextarc]
7522 set arcstart($a) $id
7525 set arcids($a) [list $p]
7527 if {![info exists arcout($p)]} {
7530 lappend arcnos($p) $a
7531 set arcout($id) [list $a]
7534 # This implements a cache for the topology information.
7535 # The cache saves, for each arc, the start and end of the arc,
7536 # the ids on the arc, and the outgoing arcs from the end.
7537 proc readcache {f} {
7538 global arcnos arcids arcout arcstart arcend arctags archeads nextarc
7539 global idtags idheads allparents cachedarcs possible_seeds seeds growing
7544 if {$lim - $a > 500} {
7545 set lim [expr {$a + 500}]
7549 # finish reading the cache and setting up arctags, etc.
7551 if {$line ne "1"} {error "bad final version"}
7553 foreach id [array names idtags] {
7554 if {[info exists arcnos($id)] && [llength $arcnos($id)] == 1 &&
7555 [llength $allparents($id)] == 1} {
7556 set a [lindex $arcnos($id) 0]
7557 if {$arctags($a) eq {}} {
7562 foreach id [array names idheads] {
7563 if {[info exists arcnos($id)] && [llength $arcnos($id)] == 1 &&
7564 [llength $allparents($id)] == 1} {
7565 set a [lindex $arcnos($id) 0]
7566 if {$archeads($a) eq {}} {
7571 foreach id [lsort -unique $possible_seeds] {
7572 if {$arcnos($id) eq {}} {
7578 while {[incr a] <= $lim} {
7580 if {[llength $line] != 3} {error "bad line"}
7581 set s [lindex $line 0]
7583 lappend arcout($s) $a
7584 if {![info exists arcnos($s)]} {
7585 lappend possible_seeds $s
7588 set e [lindex $line 1]
7593 if {![info exists arcout($e)]} {
7597 set arcids($a) [lindex $line 2]
7598 foreach id $arcids($a) {
7599 lappend allparents($s) $id
7601 lappend arcnos($id) $a
7603 if {![info exists allparents($s)]} {
7604 set allparents($s) {}
7609 set nextarc [expr {$a - 1}]
7622 global nextarc cachedarcs possible_seeds
7626 if {[llength $line] != 2 || [lindex $line 0] ne "1"} {error "bad version"}
7627 # make sure it's an integer
7628 set cachedarcs [expr {int([lindex $line 1])}]
7629 if {$cachedarcs < 0} {error "bad number of arcs"}
7631 set possible_seeds {}
7639 proc dropcache {err} {
7640 global allcwait nextarc cachedarcs seeds
7642 #puts "dropping cache ($err)"
7643 foreach v {arcnos arcout arcids arcstart arcend growing \
7644 arctags archeads allparents allchildren} {
7655 proc writecache {f} {
7656 global cachearc cachedarcs allccache
7657 global arcstart arcend arcnos arcids arcout
7661 if {$lim - $a > 1000} {
7662 set lim [expr {$a + 1000}]
7665 while {[incr a] <= $lim} {
7666 if {[info exists arcend($a)]} {
7667 puts $f [list $arcstart($a) $arcend($a) $arcids($a)]
7669 puts $f [list $arcstart($a) {} $arcids($a)]
7674 catch {file delete $allccache}
7675 #puts "writing cache failed ($err)"
7678 set cachearc [expr {$a - 1}]
7679 if {$a > $cachedarcs} {
7688 global nextarc cachedarcs cachearc allccache
7690 if {$nextarc == $cachedarcs} return
7692 set cachedarcs $nextarc
7694 set f [open $allccache w]
7695 puts $f [list 1 $cachedarcs]
7700 # Returns 1 if a is an ancestor of b, -1 if b is an ancestor of a,
7701 # or 0 if neither is true.
7702 proc anc_or_desc {a b} {
7703 global arcout arcstart arcend arcnos cached_isanc
7705 if {$arcnos($a) eq $arcnos($b)} {
7706 # Both are on the same arc(s); either both are the same BMP,
7707 # or if one is not a BMP, the other is also not a BMP or is
7708 # the BMP at end of the arc (and it only has 1 incoming arc).
7709 # Or both can be BMPs with no incoming arcs.
7710 if {$a eq $b || $arcnos($a) eq {}} {
7713 # assert {[llength $arcnos($a)] == 1}
7714 set arc [lindex $arcnos($a) 0]
7715 set i [lsearch -exact $arcids($arc) $a]
7716 set j [lsearch -exact $arcids($arc) $b]
7717 if {$i < 0 || $i > $j} {
7724 if {![info exists arcout($a)]} {
7725 set arc [lindex $arcnos($a) 0]
7726 if {[info exists arcend($arc)]} {
7727 set aend $arcend($arc)
7731 set a $arcstart($arc)
7735 if {![info exists arcout($b)]} {
7736 set arc [lindex $arcnos($b) 0]
7737 if {[info exists arcend($arc)]} {
7738 set bend $arcend($arc)
7742 set b $arcstart($arc)
7752 if {[info exists cached_isanc($a,$bend)]} {
7753 if {$cached_isanc($a,$bend)} {
7757 if {[info exists cached_isanc($b,$aend)]} {
7758 if {$cached_isanc($b,$aend)} {
7761 if {[info exists cached_isanc($a,$bend)]} {
7766 set todo [list $a $b]
7769 for {set i 0} {$i < [llength $todo]} {incr i} {
7770 set x [lindex $todo $i]
7771 if {$anc($x) eq {}} {
7774 foreach arc $arcnos($x) {
7775 set xd $arcstart($arc)
7777 set cached_isanc($a,$bend) 1
7778 set cached_isanc($b,$aend) 0
7780 } elseif {$xd eq $aend} {
7781 set cached_isanc($b,$aend) 1
7782 set cached_isanc($a,$bend) 0
7785 if {![info exists anc($xd)]} {
7786 set anc($xd) $anc($x)
7788 } elseif {$anc($xd) ne $anc($x)} {
7793 set cached_isanc($a,$bend) 0
7794 set cached_isanc($b,$aend) 0
7798 # This identifies whether $desc has an ancestor that is
7799 # a growing tip of the graph and which is not an ancestor of $anc
7800 # and returns 0 if so and 1 if not.
7801 # If we subsequently discover a tag on such a growing tip, and that
7802 # turns out to be a descendent of $anc (which it could, since we
7803 # don't necessarily see children before parents), then $desc
7804 # isn't a good choice to display as a descendent tag of
7805 # $anc (since it is the descendent of another tag which is
7806 # a descendent of $anc). Similarly, $anc isn't a good choice to
7807 # display as a ancestor tag of $desc.
7809 proc is_certain {desc anc} {
7810 global arcnos arcout arcstart arcend growing problems
7813 if {[llength $arcnos($anc)] == 1} {
7814 # tags on the same arc are certain
7815 if {$arcnos($desc) eq $arcnos($anc)} {
7818 if {![info exists arcout($anc)]} {
7819 # if $anc is partway along an arc, use the start of the arc instead
7820 set a [lindex $arcnos($anc) 0]
7821 set anc $arcstart($a)
7824 if {[llength $arcnos($desc)] > 1 || [info exists arcout($desc)]} {
7827 set a [lindex $arcnos($desc) 0]
7833 set anclist [list $x]
7837 for {set i 0} {$i < [llength $anclist] && ($nnh > 0 || $ngrowanc > 0)} {incr i} {
7838 set x [lindex $anclist $i]
7843 foreach a $arcout($x) {
7844 if {[info exists growing($a)]} {
7845 if {![info exists growanc($x)] && $dl($x)} {
7851 if {[info exists dl($y)]} {
7855 if {![info exists done($y)]} {
7858 if {[info exists growanc($x)]} {
7862 for {set k 0} {$k < [llength $xl]} {incr k} {
7863 set z [lindex $xl $k]
7864 foreach c $arcout($z) {
7865 if {[info exists arcend($c)]} {
7867 if {[info exists dl($v)] && $dl($v)} {
7869 if {![info exists done($v)]} {
7872 if {[info exists growanc($v)]} {
7882 } elseif {$y eq $anc || !$dl($x)} {
7893 foreach x [array names growanc] {
7902 proc validate_arctags {a} {
7903 global arctags idtags
7907 foreach id $arctags($a) {
7909 if {![info exists idtags($id)]} {
7910 set na [lreplace $na $i $i]
7917 proc validate_archeads {a} {
7918 global archeads idheads
7921 set na $archeads($a)
7922 foreach id $archeads($a) {
7924 if {![info exists idheads($id)]} {
7925 set na [lreplace $na $i $i]
7929 set archeads($a) $na
7932 # Return the list of IDs that have tags that are descendents of id,
7933 # ignoring IDs that are descendents of IDs already reported.
7934 proc desctags {id} {
7935 global arcnos arcstart arcids arctags idtags allparents
7936 global growing cached_dtags
7938 if {![info exists allparents($id)]} {
7941 set t1 [clock clicks -milliseconds]
7943 if {[llength $arcnos($id)] == 1 && [llength $allparents($id)] == 1} {
7944 # part-way along an arc; check that arc first
7945 set a [lindex $arcnos($id) 0]
7946 if {$arctags($a) ne {}} {
7948 set i [lsearch -exact $arcids($a) $id]
7950 foreach t $arctags($a) {
7951 set j [lsearch -exact $arcids($a) $t]
7959 set id $arcstart($a)
7960 if {[info exists idtags($id)]} {
7964 if {[info exists cached_dtags($id)]} {
7965 return $cached_dtags($id)
7972 for {set i 0} {$i < [llength $todo] && $nc > 0} {incr i} {
7973 set id [lindex $todo $i]
7975 set ta [info exists hastaggedancestor($id)]
7979 # ignore tags on starting node
7980 if {!$ta && $i > 0} {
7981 if {[info exists idtags($id)]} {
7984 } elseif {[info exists cached_dtags($id)]} {
7985 set tagloc($id) $cached_dtags($id)
7989 foreach a $arcnos($id) {
7991 if {!$ta && $arctags($a) ne {}} {
7993 if {$arctags($a) ne {}} {
7994 lappend tagloc($id) [lindex $arctags($a) end]
7997 if {$ta || $arctags($a) ne {}} {
7998 set tomark [list $d]
7999 for {set j 0} {$j < [llength $tomark]} {incr j} {
8000 set dd [lindex $tomark $j]
8001 if {![info exists hastaggedancestor($dd)]} {
8002 if {[info exists done($dd)]} {
8003 foreach b $arcnos($dd) {
8004 lappend tomark $arcstart($b)
8006 if {[info exists tagloc($dd)]} {
8009 } elseif {[info exists queued($dd)]} {
8012 set hastaggedancestor($dd) 1
8016 if {![info exists queued($d)]} {
8019 if {![info exists hastaggedancestor($d)]} {
8026 foreach id [array names tagloc] {
8027 if {![info exists hastaggedancestor($id)]} {
8028 foreach t $tagloc($id) {
8029 if {[lsearch -exact $tags $t] < 0} {
8035 set t2 [clock clicks -milliseconds]
8038 # remove tags that are descendents of other tags
8039 for {set i 0} {$i < [llength $tags]} {incr i} {
8040 set a [lindex $tags $i]
8041 for {set j 0} {$j < $i} {incr j} {
8042 set b [lindex $tags $j]
8043 set r [anc_or_desc $a $b]
8045 set tags [lreplace $tags $j $j]
8048 } elseif {$r == -1} {
8049 set tags [lreplace $tags $i $i]
8056 if {[array names growing] ne {}} {
8057 # graph isn't finished, need to check if any tag could get
8058 # eclipsed by another tag coming later. Simply ignore any
8059 # tags that could later get eclipsed.
8062 if {[is_certain $t $origid]} {
8066 if {$tags eq $ctags} {
8067 set cached_dtags($origid) $tags
8072 set cached_dtags($origid) $tags
8074 set t3 [clock clicks -milliseconds]
8075 if {0 && $t3 - $t1 >= 100} {
8076 puts "iterating descendents ($loopix/[llength $todo] nodes) took\
8077 [expr {$t2-$t1}]+[expr {$t3-$t2}]ms, $nc candidates left"
8083 global arcnos arcids arcout arcend arctags idtags allparents
8084 global growing cached_atags
8086 if {![info exists allparents($id)]} {
8089 set t1 [clock clicks -milliseconds]
8091 if {[llength $arcnos($id)] == 1 && [llength $allparents($id)] == 1} {
8092 # part-way along an arc; check that arc first
8093 set a [lindex $arcnos($id) 0]
8094 if {$arctags($a) ne {}} {
8096 set i [lsearch -exact $arcids($a) $id]
8097 foreach t $arctags($a) {
8098 set j [lsearch -exact $arcids($a) $t]
8104 if {![info exists arcend($a)]} {
8108 if {[info exists idtags($id)]} {
8112 if {[info exists cached_atags($id)]} {
8113 return $cached_atags($id)
8121 for {set i 0} {$i < [llength $todo] && $nc > 0} {incr i} {
8122 set id [lindex $todo $i]
8124 set td [info exists hastaggeddescendent($id)]
8128 # ignore tags on starting node
8129 if {!$td && $i > 0} {
8130 if {[info exists idtags($id)]} {
8133 } elseif {[info exists cached_atags($id)]} {
8134 set tagloc($id) $cached_atags($id)
8138 foreach a $arcout($id) {
8139 if {!$td && $arctags($a) ne {}} {
8141 if {$arctags($a) ne {}} {
8142 lappend tagloc($id) [lindex $arctags($a) 0]
8145 if {![info exists arcend($a)]} continue
8147 if {$td || $arctags($a) ne {}} {
8148 set tomark [list $d]
8149 for {set j 0} {$j < [llength $tomark]} {incr j} {
8150 set dd [lindex $tomark $j]
8151 if {![info exists hastaggeddescendent($dd)]} {
8152 if {[info exists done($dd)]} {
8153 foreach b $arcout($dd) {
8154 if {[info exists arcend($b)]} {
8155 lappend tomark $arcend($b)
8158 if {[info exists tagloc($dd)]} {
8161 } elseif {[info exists queued($dd)]} {
8164 set hastaggeddescendent($dd) 1
8168 if {![info exists queued($d)]} {
8171 if {![info exists hastaggeddescendent($d)]} {
8177 set t2 [clock clicks -milliseconds]
8180 foreach id [array names tagloc] {
8181 if {![info exists hastaggeddescendent($id)]} {
8182 foreach t $tagloc($id) {
8183 if {[lsearch -exact $tags $t] < 0} {
8190 # remove tags that are ancestors of other tags
8191 for {set i 0} {$i < [llength $tags]} {incr i} {
8192 set a [lindex $tags $i]
8193 for {set j 0} {$j < $i} {incr j} {
8194 set b [lindex $tags $j]
8195 set r [anc_or_desc $a $b]
8197 set tags [lreplace $tags $j $j]
8200 } elseif {$r == 1} {
8201 set tags [lreplace $tags $i $i]
8208 if {[array names growing] ne {}} {
8209 # graph isn't finished, need to check if any tag could get
8210 # eclipsed by another tag coming later. Simply ignore any
8211 # tags that could later get eclipsed.
8214 if {[is_certain $origid $t]} {
8218 if {$tags eq $ctags} {
8219 set cached_atags($origid) $tags
8224 set cached_atags($origid) $tags
8226 set t3 [clock clicks -milliseconds]
8227 if {0 && $t3 - $t1 >= 100} {
8228 puts "iterating ancestors ($loopix/[llength $todo] nodes) took\
8229 [expr {$t2-$t1}]+[expr {$t3-$t2}]ms, $nc candidates left"
8234 # Return the list of IDs that have heads that are descendents of id,
8235 # including id itself if it has a head.
8236 proc descheads {id} {
8237 global arcnos arcstart arcids archeads idheads cached_dheads
8240 if {![info exists allparents($id)]} {
8244 if {[llength $arcnos($id)] == 1 && [llength $allparents($id)] == 1} {
8245 # part-way along an arc; check it first
8246 set a [lindex $arcnos($id) 0]
8247 if {$archeads($a) ne {}} {
8248 validate_archeads $a
8249 set i [lsearch -exact $arcids($a) $id]
8250 foreach t $archeads($a) {
8251 set j [lsearch -exact $arcids($a) $t]
8256 set id $arcstart($a)
8262 for {set i 0} {$i < [llength $todo]} {incr i} {
8263 set id [lindex $todo $i]
8264 if {[info exists cached_dheads($id)]} {
8265 set ret [concat $ret $cached_dheads($id)]
8267 if {[info exists idheads($id)]} {
8270 foreach a $arcnos($id) {
8271 if {$archeads($a) ne {}} {
8272 validate_archeads $a
8273 if {$archeads($a) ne {}} {
8274 set ret [concat $ret $archeads($a)]
8278 if {![info exists seen($d)]} {
8285 set ret [lsort -unique $ret]
8286 set cached_dheads($origid) $ret
8287 return [concat $ret $aret]
8290 proc addedtag {id} {
8291 global arcnos arcout cached_dtags cached_atags
8293 if {![info exists arcnos($id)]} return
8294 if {![info exists arcout($id)]} {
8295 recalcarc [lindex $arcnos($id) 0]
8297 catch {unset cached_dtags}
8298 catch {unset cached_atags}
8301 proc addedhead {hid head} {
8302 global arcnos arcout cached_dheads
8304 if {![info exists arcnos($hid)]} return
8305 if {![info exists arcout($hid)]} {
8306 recalcarc [lindex $arcnos($hid) 0]
8308 catch {unset cached_dheads}
8311 proc removedhead {hid head} {
8312 global cached_dheads
8314 catch {unset cached_dheads}
8317 proc movedhead {hid head} {
8318 global arcnos arcout cached_dheads
8320 if {![info exists arcnos($hid)]} return
8321 if {![info exists arcout($hid)]} {
8322 recalcarc [lindex $arcnos($hid) 0]
8324 catch {unset cached_dheads}
8327 proc changedrefs {} {
8328 global cached_dheads cached_dtags cached_atags
8329 global arctags archeads arcnos arcout idheads idtags
8331 foreach id [concat [array names idheads] [array names idtags]] {
8332 if {[info exists arcnos($id)] && ![info exists arcout($id)]} {
8333 set a [lindex $arcnos($id) 0]
8334 if {![info exists donearc($a)]} {
8340 catch {unset cached_dtags}
8341 catch {unset cached_atags}
8342 catch {unset cached_dheads}
8345 proc rereadrefs {} {
8346 global idtags idheads idotherrefs mainhead
8348 set refids [concat [array names idtags] \
8349 [array names idheads] [array names idotherrefs]]
8350 foreach id $refids {
8351 if {![info exists ref($id)]} {
8352 set ref($id) [listrefs $id]
8355 set oldmainhead $mainhead
8358 set refids [lsort -unique [concat $refids [array names idtags] \
8359 [array names idheads] [array names idotherrefs]]]
8360 foreach id $refids {
8361 set v [listrefs $id]
8362 if {![info exists ref($id)] || $ref($id) != $v ||
8363 ($id eq $oldmainhead && $id ne $mainhead) ||
8364 ($id eq $mainhead && $id ne $oldmainhead)} {
8371 proc listrefs {id} {
8372 global idtags idheads idotherrefs
8375 if {[info exists idtags($id)]} {
8379 if {[info exists idheads($id)]} {
8383 if {[info exists idotherrefs($id)]} {
8384 set z $idotherrefs($id)
8386 return [list $x $y $z]
8389 proc showtag {tag isnew} {
8390 global ctext tagcontents tagids linknum tagobjid
8393 addtohistory [list showtag $tag 0]
8395 $ctext conf -state normal
8399 if {![info exists tagcontents($tag)]} {
8401 set tagcontents($tag) [exec git cat-file tag $tagobjid($tag)]
8404 if {[info exists tagcontents($tag)]} {
8405 set text $tagcontents($tag)
8407 set text "Tag: $tag\nId: $tagids($tag)"
8409 appendwithlinks $text {}
8410 $ctext conf -state disabled
8421 proc mkfontdisp {font top which} {
8422 global fontattr fontpref $font
8424 set fontpref($font) [set $font]
8425 button $top.${font}but -text $which -font optionfont \
8426 -command [list choosefont $font $which]
8427 label $top.$font -relief flat -font $font \
8428 -text $fontattr($font,family) -justify left
8429 grid x $top.${font}but $top.$font -sticky w
8432 proc choosefont {font which} {
8433 global fontparam fontlist fonttop fontattr
8435 set fontparam(which) $which
8436 set fontparam(font) $font
8437 set fontparam(family) [font actual $font -family]
8438 set fontparam(size) $fontattr($font,size)
8439 set fontparam(weight) $fontattr($font,weight)
8440 set fontparam(slant) $fontattr($font,slant)
8443 if {![winfo exists $top]} {
8445 eval font config sample [font actual $font]
8447 wm title $top "Gitk font chooser"
8448 label $top.l -textvariable fontparam(which) -font uifont
8449 pack $top.l -side top
8450 set fontlist [lsort [font families]]
8452 listbox $top.f.fam -listvariable fontlist \
8453 -yscrollcommand [list $top.f.sb set]
8454 bind $top.f.fam <<ListboxSelect>> selfontfam
8455 scrollbar $top.f.sb -command [list $top.f.fam yview]
8456 pack $top.f.sb -side right -fill y
8457 pack $top.f.fam -side left -fill both -expand 1
8458 pack $top.f -side top -fill both -expand 1
8460 spinbox $top.g.size -from 4 -to 40 -width 4 \
8461 -textvariable fontparam(size) \
8462 -validatecommand {string is integer -strict %s}
8463 checkbutton $top.g.bold -padx 5 \
8464 -font {{Times New Roman} 12 bold} -text "B" -indicatoron 0 \
8465 -variable fontparam(weight) -onvalue bold -offvalue normal
8466 checkbutton $top.g.ital -padx 5 \
8467 -font {{Times New Roman} 12 italic} -text "I" -indicatoron 0 \
8468 -variable fontparam(slant) -onvalue italic -offvalue roman
8469 pack $top.g.size $top.g.bold $top.g.ital -side left
8470 pack $top.g -side top
8471 canvas $top.c -width 150 -height 50 -border 2 -relief sunk \
8473 $top.c create text 100 25 -anchor center -text $which -font sample \
8474 -fill black -tags text
8475 bind $top.c <Configure> [list centertext $top.c]
8476 pack $top.c -side top -fill x
8478 button $top.buts.ok -text "OK" -command fontok -default active \
8480 button $top.buts.can -text "Cancel" -command fontcan -default normal \
8482 grid $top.buts.ok $top.buts.can
8483 grid columnconfigure $top.buts 0 -weight 1 -uniform a
8484 grid columnconfigure $top.buts 1 -weight 1 -uniform a
8485 pack $top.buts -side bottom -fill x
8486 trace add variable fontparam write chg_fontparam
8489 $top.c itemconf text -text $which
8491 set i [lsearch -exact $fontlist $fontparam(family)]
8493 $top.f.fam selection set $i
8498 proc centertext {w} {
8499 $w coords text [expr {[winfo width $w] / 2}] [expr {[winfo height $w] / 2}]
8503 global fontparam fontpref prefstop
8505 set f $fontparam(font)
8506 set fontpref($f) [list $fontparam(family) $fontparam(size)]
8507 if {$fontparam(weight) eq "bold"} {
8508 lappend fontpref($f) "bold"
8510 if {$fontparam(slant) eq "italic"} {
8511 lappend fontpref($f) "italic"
8514 $w conf -text $fontparam(family) -font $fontpref($f)
8520 global fonttop fontparam
8522 if {[info exists fonttop]} {
8523 catch {destroy $fonttop}
8524 catch {font delete sample}
8530 proc selfontfam {} {
8531 global fonttop fontparam
8533 set i [$fonttop.f.fam curselection]
8535 set fontparam(family) [$fonttop.f.fam get $i]
8539 proc chg_fontparam {v sub op} {
8542 font config sample -$sub $fontparam($sub)
8546 global maxwidth maxgraphpct
8547 global oldprefs prefstop showneartags showlocalchanges
8548 global bgcolor fgcolor ctext diffcolors selectbgcolor
8549 global uifont tabstop limitdiffs
8553 if {[winfo exists $top]} {
8557 foreach v {maxwidth maxgraphpct showneartags showlocalchanges \
8558 limitdiffs tabstop} {
8559 set oldprefs($v) [set $v]
8562 wm title $top "Gitk preferences"
8563 label $top.ldisp -text "Commit list display options"
8564 $top.ldisp configure -font uifont
8565 grid $top.ldisp - -sticky w -pady 10
8566 label $top.spacer -text " "
8567 label $top.maxwidthl -text "Maximum graph width (lines)" \
8569 spinbox $top.maxwidth -from 0 -to 100 -width 4 -textvariable maxwidth
8570 grid $top.spacer $top.maxwidthl $top.maxwidth -sticky w
8571 label $top.maxpctl -text "Maximum graph width (% of pane)" \
8573 spinbox $top.maxpct -from 1 -to 100 -width 4 -textvariable maxgraphpct
8574 grid x $top.maxpctl $top.maxpct -sticky w
8575 frame $top.showlocal
8576 label $top.showlocal.l -text "Show local changes" -font optionfont
8577 checkbutton $top.showlocal.b -variable showlocalchanges
8578 pack $top.showlocal.b $top.showlocal.l -side left
8579 grid x $top.showlocal -sticky w
8581 label $top.ddisp -text "Diff display options"
8582 $top.ddisp configure -font uifont
8583 grid $top.ddisp - -sticky w -pady 10
8584 label $top.tabstopl -text "Tab spacing" -font optionfont
8585 spinbox $top.tabstop -from 1 -to 20 -width 4 -textvariable tabstop
8586 grid x $top.tabstopl $top.tabstop -sticky w
8588 label $top.ntag.l -text "Display nearby tags" -font optionfont
8589 checkbutton $top.ntag.b -variable showneartags
8590 pack $top.ntag.b $top.ntag.l -side left
8591 grid x $top.ntag -sticky w
8593 label $top.ldiff.l -text "Limit diffs to listed paths" -font optionfont
8594 checkbutton $top.ldiff.b -variable limitdiffs
8595 pack $top.ldiff.b $top.ldiff.l -side left
8596 grid x $top.ldiff -sticky w
8598 label $top.cdisp -text "Colors: press to choose"
8599 $top.cdisp configure -font uifont
8600 grid $top.cdisp - -sticky w -pady 10
8601 label $top.bg -padx 40 -relief sunk -background $bgcolor
8602 button $top.bgbut -text "Background" -font optionfont \
8603 -command [list choosecolor bgcolor 0 $top.bg background setbg]
8604 grid x $top.bgbut $top.bg -sticky w
8605 label $top.fg -padx 40 -relief sunk -background $fgcolor
8606 button $top.fgbut -text "Foreground" -font optionfont \
8607 -command [list choosecolor fgcolor 0 $top.fg foreground setfg]
8608 grid x $top.fgbut $top.fg -sticky w
8609 label $top.diffold -padx 40 -relief sunk -background [lindex $diffcolors 0]
8610 button $top.diffoldbut -text "Diff: old lines" -font optionfont \
8611 -command [list choosecolor diffcolors 0 $top.diffold "diff old lines" \
8612 [list $ctext tag conf d0 -foreground]]
8613 grid x $top.diffoldbut $top.diffold -sticky w
8614 label $top.diffnew -padx 40 -relief sunk -background [lindex $diffcolors 1]
8615 button $top.diffnewbut -text "Diff: new lines" -font optionfont \
8616 -command [list choosecolor diffcolors 1 $top.diffnew "diff new lines" \
8617 [list $ctext tag conf d1 -foreground]]
8618 grid x $top.diffnewbut $top.diffnew -sticky w
8619 label $top.hunksep -padx 40 -relief sunk -background [lindex $diffcolors 2]
8620 button $top.hunksepbut -text "Diff: hunk header" -font optionfont \
8621 -command [list choosecolor diffcolors 2 $top.hunksep \
8622 "diff hunk header" \
8623 [list $ctext tag conf hunksep -foreground]]
8624 grid x $top.hunksepbut $top.hunksep -sticky w
8625 label $top.selbgsep -padx 40 -relief sunk -background $selectbgcolor
8626 button $top.selbgbut -text "Select bg" -font optionfont \
8627 -command [list choosecolor selectbgcolor 0 $top.selbgsep background setselbg]
8628 grid x $top.selbgbut $top.selbgsep -sticky w
8630 label $top.cfont -text "Fonts: press to choose"
8631 $top.cfont configure -font uifont
8632 grid $top.cfont - -sticky w -pady 10
8633 mkfontdisp mainfont $top "Main font"
8634 mkfontdisp textfont $top "Diff display font"
8635 mkfontdisp uifont $top "User interface font"
8638 button $top.buts.ok -text "OK" -command prefsok -default active
8639 $top.buts.ok configure -font uifont
8640 button $top.buts.can -text "Cancel" -command prefscan -default normal
8641 $top.buts.can configure -font uifont
8642 grid $top.buts.ok $top.buts.can
8643 grid columnconfigure $top.buts 0 -weight 1 -uniform a
8644 grid columnconfigure $top.buts 1 -weight 1 -uniform a
8645 grid $top.buts - - -pady 10 -sticky ew
8646 bind $top <Visibility> "focus $top.buts.ok"
8649 proc choosecolor {v vi w x cmd} {
8652 set c [tk_chooseColor -initialcolor [lindex [set $v] $vi] \
8653 -title "Gitk: choose color for $x"]
8654 if {$c eq {}} return
8655 $w conf -background $c
8661 global bglist cflist
8663 $w configure -selectbackground $c
8665 $cflist tag configure highlight \
8666 -background [$cflist cget -selectbackground]
8667 allcanvs itemconf secsel -fill $c
8674 $w conf -background $c
8682 $w conf -foreground $c
8684 allcanvs itemconf text -fill $c
8685 $canv itemconf circle -outline $c
8689 global oldprefs prefstop
8691 foreach v {maxwidth maxgraphpct showneartags showlocalchanges \
8692 limitdiffs tabstop} {
8694 set $v $oldprefs($v)
8696 catch {destroy $prefstop}
8702 global maxwidth maxgraphpct
8703 global oldprefs prefstop showneartags showlocalchanges
8704 global fontpref mainfont textfont uifont
8705 global limitdiffs treediffs
8707 catch {destroy $prefstop}
8711 if {$mainfont ne $fontpref(mainfont)} {
8712 set mainfont $fontpref(mainfont)
8713 parsefont mainfont $mainfont
8714 eval font configure mainfont [fontflags mainfont]
8715 eval font configure mainfontbold [fontflags mainfont 1]
8719 if {$textfont ne $fontpref(textfont)} {
8720 set textfont $fontpref(textfont)
8721 parsefont textfont $textfont
8722 eval font configure textfont [fontflags textfont]
8723 eval font configure textfontbold [fontflags textfont 1]
8725 if {$uifont ne $fontpref(uifont)} {
8726 set uifont $fontpref(uifont)
8727 parsefont uifont $uifont
8728 eval font configure uifont [fontflags uifont]
8731 if {$showlocalchanges != $oldprefs(showlocalchanges)} {
8732 if {$showlocalchanges} {
8738 if {$limitdiffs != $oldprefs(limitdiffs)} {
8739 # treediffs elements are limited by path
8740 catch {unset treediffs}
8742 if {$fontchanged || $maxwidth != $oldprefs(maxwidth)
8743 || $maxgraphpct != $oldprefs(maxgraphpct)} {
8745 } elseif {$showneartags != $oldprefs(showneartags) ||
8746 $limitdiffs != $oldprefs(limitdiffs)} {
8751 proc formatdate {d} {
8752 global datetimeformat
8754 set d [clock format $d -format $datetimeformat]
8759 # This list of encoding names and aliases is distilled from
8760 # http://www.iana.org/assignments/character-sets.
8761 # Not all of them are supported by Tcl.
8762 set encoding_aliases {
8763 { ANSI_X3.4-1968 iso-ir-6 ANSI_X3.4-1986 ISO_646.irv:1991 ASCII
8764 ISO646-US US-ASCII us IBM367 cp367 csASCII }
8765 { ISO-10646-UTF-1 csISO10646UTF1 }
8766 { ISO_646.basic:1983 ref csISO646basic1983 }
8767 { INVARIANT csINVARIANT }
8768 { ISO_646.irv:1983 iso-ir-2 irv csISO2IntlRefVersion }
8769 { BS_4730 iso-ir-4 ISO646-GB gb uk csISO4UnitedKingdom }
8770 { NATS-SEFI iso-ir-8-1 csNATSSEFI }
8771 { NATS-SEFI-ADD iso-ir-8-2 csNATSSEFIADD }
8772 { NATS-DANO iso-ir-9-1 csNATSDANO }
8773 { NATS-DANO-ADD iso-ir-9-2 csNATSDANOADD }
8774 { SEN_850200_B iso-ir-10 FI ISO646-FI ISO646-SE se csISO10Swedish }
8775 { SEN_850200_C iso-ir-11 ISO646-SE2 se2 csISO11SwedishForNames }
8776 { KS_C_5601-1987 iso-ir-149 KS_C_5601-1989 KSC_5601 korean csKSC56011987 }
8777 { ISO-2022-KR csISO2022KR }
8779 { ISO-2022-JP csISO2022JP }
8780 { ISO-2022-JP-2 csISO2022JP2 }
8781 { JIS_C6220-1969-jp JIS_C6220-1969 iso-ir-13 katakana x0201-7
8783 { JIS_C6220-1969-ro iso-ir-14 jp ISO646-JP csISO14JISC6220ro }
8784 { IT iso-ir-15 ISO646-IT csISO15Italian }
8785 { PT iso-ir-16 ISO646-PT csISO16Portuguese }
8786 { ES iso-ir-17 ISO646-ES csISO17Spanish }
8787 { greek7-old iso-ir-18 csISO18Greek7Old }
8788 { latin-greek iso-ir-19 csISO19LatinGreek }
8789 { DIN_66003 iso-ir-21 de ISO646-DE csISO21German }
8790 { NF_Z_62-010_(1973) iso-ir-25 ISO646-FR1 csISO25French }
8791 { Latin-greek-1 iso-ir-27 csISO27LatinGreek1 }
8792 { ISO_5427 iso-ir-37 csISO5427Cyrillic }
8793 { JIS_C6226-1978 iso-ir-42 csISO42JISC62261978 }
8794 { BS_viewdata iso-ir-47 csISO47BSViewdata }
8795 { INIS iso-ir-49 csISO49INIS }
8796 { INIS-8 iso-ir-50 csISO50INIS8 }
8797 { INIS-cyrillic iso-ir-51 csISO51INISCyrillic }
8798 { ISO_5427:1981 iso-ir-54 ISO5427Cyrillic1981 }
8799 { ISO_5428:1980 iso-ir-55 csISO5428Greek }
8800 { GB_1988-80 iso-ir-57 cn ISO646-CN csISO57GB1988 }
8801 { GB_2312-80 iso-ir-58 chinese csISO58GB231280 }
8802 { NS_4551-1 iso-ir-60 ISO646-NO no csISO60DanishNorwegian
8804 { NS_4551-2 ISO646-NO2 iso-ir-61 no2 csISO61Norwegian2 }
8805 { NF_Z_62-010 iso-ir-69 ISO646-FR fr csISO69French }
8806 { videotex-suppl iso-ir-70 csISO70VideotexSupp1 }
8807 { PT2 iso-ir-84 ISO646-PT2 csISO84Portuguese2 }
8808 { ES2 iso-ir-85 ISO646-ES2 csISO85Spanish2 }
8809 { MSZ_7795.3 iso-ir-86 ISO646-HU hu csISO86Hungarian }
8810 { JIS_C6226-1983 iso-ir-87 x0208 JIS_X0208-1983 csISO87JISX0208 }
8811 { greek7 iso-ir-88 csISO88Greek7 }
8812 { ASMO_449 ISO_9036 arabic7 iso-ir-89 csISO89ASMO449 }
8813 { iso-ir-90 csISO90 }
8814 { JIS_C6229-1984-a iso-ir-91 jp-ocr-a csISO91JISC62291984a }
8815 { JIS_C6229-1984-b iso-ir-92 ISO646-JP-OCR-B jp-ocr-b
8816 csISO92JISC62991984b }
8817 { JIS_C6229-1984-b-add iso-ir-93 jp-ocr-b-add csISO93JIS62291984badd }
8818 { JIS_C6229-1984-hand iso-ir-94 jp-ocr-hand csISO94JIS62291984hand }
8819 { JIS_C6229-1984-hand-add iso-ir-95 jp-ocr-hand-add
8820 csISO95JIS62291984handadd }
8821 { JIS_C6229-1984-kana iso-ir-96 csISO96JISC62291984kana }
8822 { ISO_2033-1983 iso-ir-98 e13b csISO2033 }
8823 { ANSI_X3.110-1983 iso-ir-99 CSA_T500-1983 NAPLPS csISO99NAPLPS }
8824 { ISO_8859-1:1987 iso-ir-100 ISO_8859-1 ISO-8859-1 latin1 l1 IBM819
8826 { ISO_8859-2:1987 iso-ir-101 ISO_8859-2 ISO-8859-2 latin2 l2 csISOLatin2 }
8827 { T.61-7bit iso-ir-102 csISO102T617bit }
8828 { T.61-8bit T.61 iso-ir-103 csISO103T618bit }
8829 { ISO_8859-3:1988 iso-ir-109 ISO_8859-3 ISO-8859-3 latin3 l3 csISOLatin3 }
8830 { ISO_8859-4:1988 iso-ir-110 ISO_8859-4 ISO-8859-4 latin4 l4 csISOLatin4 }
8831 { ECMA-cyrillic iso-ir-111 KOI8-E csISO111ECMACyrillic }
8832 { CSA_Z243.4-1985-1 iso-ir-121 ISO646-CA csa7-1 ca csISO121Canadian1 }
8833 { CSA_Z243.4-1985-2 iso-ir-122 ISO646-CA2 csa7-2 csISO122Canadian2 }
8834 { CSA_Z243.4-1985-gr iso-ir-123 csISO123CSAZ24341985gr }
8835 { ISO_8859-6:1987 iso-ir-127 ISO_8859-6 ISO-8859-6 ECMA-114 ASMO-708
8836 arabic csISOLatinArabic }
8837 { ISO_8859-6-E csISO88596E ISO-8859-6-E }
8838 { ISO_8859-6-I csISO88596I ISO-8859-6-I }
8839 { ISO_8859-7:1987 iso-ir-126 ISO_8859-7 ISO-8859-7 ELOT_928 ECMA-118
8840 greek greek8 csISOLatinGreek }
8841 { T.101-G2 iso-ir-128 csISO128T101G2 }
8842 { ISO_8859-8:1988 iso-ir-138 ISO_8859-8 ISO-8859-8 hebrew
8844 { ISO_8859-8-E csISO88598E ISO-8859-8-E }
8845 { ISO_8859-8-I csISO88598I ISO-8859-8-I }
8846 { CSN_369103 iso-ir-139 csISO139CSN369103 }
8847 { JUS_I.B1.002 iso-ir-141 ISO646-YU js yu csISO141JUSIB1002 }
8848 { ISO_6937-2-add iso-ir-142 csISOTextComm }
8849 { IEC_P27-1 iso-ir-143 csISO143IECP271 }
8850 { ISO_8859-5:1988 iso-ir-144 ISO_8859-5 ISO-8859-5 cyrillic
8851 csISOLatinCyrillic }
8852 { JUS_I.B1.003-serb iso-ir-146 serbian csISO146Serbian }
8853 { JUS_I.B1.003-mac macedonian iso-ir-147 csISO147Macedonian }
8854 { ISO_8859-9:1989 iso-ir-148 ISO_8859-9 ISO-8859-9 latin5 l5 csISOLatin5 }
8855 { greek-ccitt iso-ir-150 csISO150 csISO150GreekCCITT }
8856 { NC_NC00-10:81 cuba iso-ir-151 ISO646-CU csISO151Cuba }
8857 { ISO_6937-2-25 iso-ir-152 csISO6937Add }
8858 { GOST_19768-74 ST_SEV_358-88 iso-ir-153 csISO153GOST1976874 }
8859 { ISO_8859-supp iso-ir-154 latin1-2-5 csISO8859Supp }
8860 { ISO_10367-box iso-ir-155 csISO10367Box }
8861 { ISO-8859-10 iso-ir-157 l6 ISO_8859-10:1992 csISOLatin6 latin6 }
8862 { latin-lap lap iso-ir-158 csISO158Lap }
8863 { JIS_X0212-1990 x0212 iso-ir-159 csISO159JISX02121990 }
8864 { DS_2089 DS2089 ISO646-DK dk csISO646Danish }
8867 { JIS_X0201 X0201 csHalfWidthKatakana }
8868 { KSC5636 ISO646-KR csKSC5636 }
8869 { ISO-10646-UCS-2 csUnicode }
8870 { ISO-10646-UCS-4 csUCS4 }
8871 { DEC-MCS dec csDECMCS }
8872 { hp-roman8 roman8 r8 csHPRoman8 }
8873 { macintosh mac csMacintosh }
8874 { IBM037 cp037 ebcdic-cp-us ebcdic-cp-ca ebcdic-cp-wt ebcdic-cp-nl
8876 { IBM038 EBCDIC-INT cp038 csIBM038 }
8877 { IBM273 CP273 csIBM273 }
8878 { IBM274 EBCDIC-BE CP274 csIBM274 }
8879 { IBM275 EBCDIC-BR cp275 csIBM275 }
8880 { IBM277 EBCDIC-CP-DK EBCDIC-CP-NO csIBM277 }
8881 { IBM278 CP278 ebcdic-cp-fi ebcdic-cp-se csIBM278 }
8882 { IBM280 CP280 ebcdic-cp-it csIBM280 }
8883 { IBM281 EBCDIC-JP-E cp281 csIBM281 }
8884 { IBM284 CP284 ebcdic-cp-es csIBM284 }
8885 { IBM285 CP285 ebcdic-cp-gb csIBM285 }
8886 { IBM290 cp290 EBCDIC-JP-kana csIBM290 }
8887 { IBM297 cp297 ebcdic-cp-fr csIBM297 }
8888 { IBM420 cp420 ebcdic-cp-ar1 csIBM420 }
8889 { IBM423 cp423 ebcdic-cp-gr csIBM423 }
8890 { IBM424 cp424 ebcdic-cp-he csIBM424 }
8891 { IBM437 cp437 437 csPC8CodePage437 }
8892 { IBM500 CP500 ebcdic-cp-be ebcdic-cp-ch csIBM500 }
8893 { IBM775 cp775 csPC775Baltic }
8894 { IBM850 cp850 850 csPC850Multilingual }
8895 { IBM851 cp851 851 csIBM851 }
8896 { IBM852 cp852 852 csPCp852 }
8897 { IBM855 cp855 855 csIBM855 }
8898 { IBM857 cp857 857 csIBM857 }
8899 { IBM860 cp860 860 csIBM860 }
8900 { IBM861 cp861 861 cp-is csIBM861 }
8901 { IBM862 cp862 862 csPC862LatinHebrew }
8902 { IBM863 cp863 863 csIBM863 }
8903 { IBM864 cp864 csIBM864 }
8904 { IBM865 cp865 865 csIBM865 }
8905 { IBM866 cp866 866 csIBM866 }
8906 { IBM868 CP868 cp-ar csIBM868 }
8907 { IBM869 cp869 869 cp-gr csIBM869 }
8908 { IBM870 CP870 ebcdic-cp-roece ebcdic-cp-yu csIBM870 }
8909 { IBM871 CP871 ebcdic-cp-is csIBM871 }
8910 { IBM880 cp880 EBCDIC-Cyrillic csIBM880 }
8911 { IBM891 cp891 csIBM891 }
8912 { IBM903 cp903 csIBM903 }
8913 { IBM904 cp904 904 csIBBM904 }
8914 { IBM905 CP905 ebcdic-cp-tr csIBM905 }
8915 { IBM918 CP918 ebcdic-cp-ar2 csIBM918 }
8916 { IBM1026 CP1026 csIBM1026 }
8917 { EBCDIC-AT-DE csIBMEBCDICATDE }
8918 { EBCDIC-AT-DE-A csEBCDICATDEA }
8919 { EBCDIC-CA-FR csEBCDICCAFR }
8920 { EBCDIC-DK-NO csEBCDICDKNO }
8921 { EBCDIC-DK-NO-A csEBCDICDKNOA }
8922 { EBCDIC-FI-SE csEBCDICFISE }
8923 { EBCDIC-FI-SE-A csEBCDICFISEA }
8924 { EBCDIC-FR csEBCDICFR }
8925 { EBCDIC-IT csEBCDICIT }
8926 { EBCDIC-PT csEBCDICPT }
8927 { EBCDIC-ES csEBCDICES }
8928 { EBCDIC-ES-A csEBCDICESA }
8929 { EBCDIC-ES-S csEBCDICESS }
8930 { EBCDIC-UK csEBCDICUK }
8931 { EBCDIC-US csEBCDICUS }
8932 { UNKNOWN-8BIT csUnknown8BiT }
8933 { MNEMONIC csMnemonic }
8938 { IBM00858 CCSID00858 CP00858 PC-Multilingual-850+euro }
8939 { IBM00924 CCSID00924 CP00924 ebcdic-Latin9--euro }
8940 { IBM01140 CCSID01140 CP01140 ebcdic-us-37+euro }
8941 { IBM01141 CCSID01141 CP01141 ebcdic-de-273+euro }
8942 { IBM01142 CCSID01142 CP01142 ebcdic-dk-277+euro ebcdic-no-277+euro }
8943 { IBM01143 CCSID01143 CP01143 ebcdic-fi-278+euro ebcdic-se-278+euro }
8944 { IBM01144 CCSID01144 CP01144 ebcdic-it-280+euro }
8945 { IBM01145 CCSID01145 CP01145 ebcdic-es-284+euro }
8946 { IBM01146 CCSID01146 CP01146 ebcdic-gb-285+euro }
8947 { IBM01147 CCSID01147 CP01147 ebcdic-fr-297+euro }
8948 { IBM01148 CCSID01148 CP01148 ebcdic-international-500+euro }
8949 { IBM01149 CCSID01149 CP01149 ebcdic-is-871+euro }
8950 { IBM1047 IBM-1047 }
8951 { PTCP154 csPTCP154 PT154 CP154 Cyrillic-Asian }
8952 { Amiga-1251 Ami1251 Amiga1251 Ami-1251 }
8953 { UNICODE-1-1 csUnicode11 }
8956 { UNICODE-1-1-UTF-7 csUnicode11UTF7 }
8957 { ISO-8859-14 iso-ir-199 ISO_8859-14:1998 ISO_8859-14 latin8 iso-celtic
8959 { ISO-8859-15 ISO_8859-15 Latin-9 }
8960 { ISO-8859-16 iso-ir-226 ISO_8859-16:2001 ISO_8859-16 latin10 l10 }
8961 { GBK CP936 MS936 windows-936 }
8962 { JIS_Encoding csJISEncoding }
8963 { Shift_JIS MS_Kanji csShiftJIS }
8964 { Extended_UNIX_Code_Packed_Format_for_Japanese csEUCPkdFmtJapanese
8966 { Extended_UNIX_Code_Fixed_Width_for_Japanese csEUCFixWidJapanese }
8967 { ISO-10646-UCS-Basic csUnicodeASCII }
8968 { ISO-10646-Unicode-Latin1 csUnicodeLatin1 ISO-10646 }
8969 { ISO-Unicode-IBM-1261 csUnicodeIBM1261 }
8970 { ISO-Unicode-IBM-1268 csUnicodeIBM1268 }
8971 { ISO-Unicode-IBM-1276 csUnicodeIBM1276 }
8972 { ISO-Unicode-IBM-1264 csUnicodeIBM1264 }
8973 { ISO-Unicode-IBM-1265 csUnicodeIBM1265 }
8974 { ISO-8859-1-Windows-3.0-Latin-1 csWindows30Latin1 }
8975 { ISO-8859-1-Windows-3.1-Latin-1 csWindows31Latin1 }
8976 { ISO-8859-2-Windows-Latin-2 csWindows31Latin2 }
8977 { ISO-8859-9-Windows-Latin-5 csWindows31Latin5 }
8978 { Adobe-Standard-Encoding csAdobeStandardEncoding }
8979 { Ventura-US csVenturaUS }
8980 { Ventura-International csVenturaInternational }
8981 { PC8-Danish-Norwegian csPC8DanishNorwegian }
8982 { PC8-Turkish csPC8Turkish }
8983 { IBM-Symbols csIBMSymbols }
8984 { IBM-Thai csIBMThai }
8985 { HP-Legal csHPLegal }
8986 { HP-Pi-font csHPPiFont }
8987 { HP-Math8 csHPMath8 }
8988 { Adobe-Symbol-Encoding csHPPSMath }
8989 { HP-DeskTop csHPDesktop }
8990 { Ventura-Math csVenturaMath }
8991 { Microsoft-Publishing csMicrosoftPublishing }
8992 { Windows-31J csWindows31J }
8997 proc tcl_encoding {enc} {
8998 global encoding_aliases
8999 set names [encoding names]
9000 set lcnames [string tolower $names]
9001 set enc [string tolower $enc]
9002 set i [lsearch -exact $lcnames $enc]
9004 # look for "isonnn" instead of "iso-nnn" or "iso_nnn"
9005 if {[regsub {^iso[-_]} $enc iso encx]} {
9006 set i [lsearch -exact $lcnames $encx]
9010 foreach l $encoding_aliases {
9011 set ll [string tolower $l]
9012 if {[lsearch -exact $ll $enc] < 0} continue
9013 # look through the aliases for one that tcl knows about
9015 set i [lsearch -exact $lcnames $e]
9017 if {[regsub {^iso[-_]} $e iso ex]} {
9018 set i [lsearch -exact $lcnames $ex]
9027 return [lindex $names $i]
9032 # First check that Tcl/Tk is recent enough
9033 if {[catch {package require Tk 8.4} err]} {
9034 show_error {} . "Sorry, gitk cannot run with this version of Tcl/Tk.\n\
9035 Gitk requires at least Tcl/Tk 8.4."
9041 set wrcomcmd "git diff-tree --stdin -p --pretty"
9045 set gitencoding [exec git config --get i18n.commitencoding]
9047 if {$gitencoding == ""} {
9048 set gitencoding "utf-8"
9050 set tclencoding [tcl_encoding $gitencoding]
9051 if {$tclencoding == {}} {
9052 puts stderr "Warning: encoding $gitencoding is not supported by Tcl/Tk"
9055 set mainfont {Helvetica 9}
9056 set textfont {Courier 9}
9057 set uifont {Helvetica 9 bold}
9059 set findmergefiles 0
9067 set cmitmode "patch"
9068 set wrapcomment "none"
9072 set showlocalchanges 1
9074 set datetimeformat "%Y-%m-%d %H:%M:%S"
9076 set colors {green red blue magenta darkgrey brown orange}
9079 set diffcolors {red "#00a000" blue}
9081 set selectbgcolor gray85
9083 catch {source ~/.gitk}
9085 font create optionfont -family sans-serif -size -12
9087 parsefont mainfont $mainfont
9088 eval font create mainfont [fontflags mainfont]
9089 eval font create mainfontbold [fontflags mainfont 1]
9091 parsefont textfont $textfont
9092 eval font create textfont [fontflags textfont]
9093 eval font create textfontbold [fontflags textfont 1]
9095 parsefont uifont $uifont
9096 eval font create uifont [fontflags uifont]
9098 # check that we can find a .git directory somewhere...
9099 if {[catch {set gitdir [gitdir]}]} {
9100 show_error {} . "Cannot find a git repository here."
9103 if {![file isdirectory $gitdir]} {
9104 show_error {} . "Cannot find the git directory \"$gitdir\"."
9110 set cmdline_files {}
9115 "-d" { set datemode 1 }
9118 lappend revtreeargs $arg
9121 set cmdline_files [lrange $argv [expr {$i + 1}] end]
9125 lappend revtreeargs $arg
9131 if {$i >= [llength $argv] && $revtreeargs ne {}} {
9132 # no -- on command line, but some arguments (other than -d)
9134 set f [eval exec git rev-parse --no-revs --no-flags $revtreeargs]
9135 set cmdline_files [split $f "\n"]
9136 set n [llength $cmdline_files]
9137 set revtreeargs [lrange $revtreeargs 0 end-$n]
9138 # Unfortunately git rev-parse doesn't produce an error when
9139 # something is both a revision and a filename. To be consistent
9140 # with git log and git rev-list, check revtreeargs for filenames.
9141 foreach arg $revtreeargs {
9142 if {[file exists $arg]} {
9143 show_error {} . "Ambiguous argument '$arg': both revision\
9149 # unfortunately we get both stdout and stderr in $err,
9150 # so look for "fatal:".
9151 set i [string first "fatal:" $err]
9153 set err [string range $err [expr {$i + 6}] end]
9155 show_error {} . "Bad arguments to gitk:\n$err"
9161 # find the list of unmerged files
9165 set fd [open "| git ls-files -u" r]
9167 show_error {} . "Couldn't get list of unmerged files: $err"
9170 while {[gets $fd line] >= 0} {
9171 set i [string first "\t" $line]
9172 if {$i < 0} continue
9173 set fname [string range $line [expr {$i+1}] end]
9174 if {[lsearch -exact $mlist $fname] >= 0} continue
9176 if {$cmdline_files eq {} || [path_filter $cmdline_files $fname]} {
9177 lappend mlist $fname
9182 if {$nr_unmerged == 0} {
9183 show_error {} . "No files selected: --merge specified but\
9184 no files are unmerged."
9186 show_error {} . "No files selected: --merge specified but\
9187 no unmerged files are within file limit."
9191 set cmdline_files $mlist
9194 set nullid "0000000000000000000000000000000000000000"
9195 set nullid2 "0000000000000000000000000000000000000001"
9197 set have_tk85 [expr {[package vcompare $tk_version "8.5"] >= 0}]
9204 set highlight_paths {}
9206 set searchdirn -forwards
9210 set markingmatches 0
9211 set linkentercount 0
9212 set need_redisplay 0
9219 set selectedhlview None
9220 set highlight_related None
9221 set highlight_files {}
9234 # wait for the window to become visible
9236 wm title . "[file tail $argv0]: [file tail [pwd]]"
9239 if {$cmdline_files ne {} || $revtreeargs ne {}} {
9240 # create a view for the files/dirs specified on the command line
9244 set viewname(1) "Command line"
9245 set viewfiles(1) $cmdline_files
9246 set viewargs(1) $revtreeargs
9249 .bar.view entryconf Edit* -state normal
9250 .bar.view entryconf Delete* -state normal
9253 if {[info exists permviews]} {
9254 foreach v $permviews {
9257 set viewname($n) [lindex $v 0]
9258 set viewfiles($n) [lindex $v 1]
9259 set viewargs($n) [lindex $v 2]