2 # Tcl ignores the next line -*- tcl -*- \
5 # Copyright © 2005-2008 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 proc unmerged_files
{files
} {
96 # find the list of unmerged files
100 set fd
[open
"| git ls-files -u" r
]
102 show_error
{} .
"[mc "Couldn
't get list of unmerged files:"] $err"
105 while {[gets $fd line] >= 0} {
106 set i [string first "\t" $line]
108 set fname [string range $line [expr {$i+1}] end]
109 if {[lsearch -exact $mlist $fname] >= 0} continue
111 if {$files eq {} || [path_filter $files $fname]} {
119 proc parseviewargs {n arglist} {
120 global vdatemode vmergeonly vflags vdflags vrevs vfiltered vorigargs
128 set origargs $arglist
132 foreach arg $arglist {
139 switch -glob -- $arg {
143 # remove from origargs in case we hit an unknown option
144 set origargs [lreplace $origargs $i $i]
147 # These request or affect diff output, which we don't want.
148 # Some could be used to set our defaults for diff display.
150 "--no-renames" - "--full-index" - "--binary" - "--abbrev=*" -
151 "--find-copies-harder" - "-l*" - "--ext-diff" - "--no-ext-diff" -
152 "--src-prefix=*" - "--dst-prefix=*" - "--no-prefix" -
153 "-O*" - "--text" - "--full-diff" - "--ignore-space-at-eol" -
154 "--ignore-space-change" - "-U*" - "--unified=*" {
155 lappend diffargs
$arg
157 # These cause our parsing of git log's output to fail, or else
158 # they're options we want to set ourselves, so ignore them.
159 "--raw" - "--patch-with-raw" - "--patch-with-stat" -
160 "--name-only" - "--name-status" - "--color" - "--color-words" -
161 "--log-size" - "--pretty=*" - "--decorate" - "--abbrev-commit" -
162 "--cc" - "-z" - "--header" - "--parents" - "--boundary" -
163 "--no-color" - "-g" - "--walk-reflogs" - "--no-walk" -
164 "--timestamp" - "relative-date" - "--date=*" - "--stdin" -
165 "--objects" - "--objects-edge" - "--reverse" {
167 # These are harmless, and some are even useful
168 "--stat=*" - "--numstat" - "--shortstat" - "--summary" -
169 "--check" - "--exit-code" - "--quiet" - "--topo-order" -
170 "--full-history" - "--dense" - "--sparse" -
171 "--follow" - "--left-right" - "--encoding=*" {
174 # These mean that we get a subset of the commits
175 "--diff-filter=*" - "--no-merges" - "--unpacked" -
176 "--max-count=*" - "--skip=*" - "--since=*" - "--after=*" -
177 "--until=*" - "--before=*" - "--max-age=*" - "--min-age=*" -
178 "--author=*" - "--committer=*" - "--grep=*" - "-[iE]" -
179 "--remove-empty" - "--first-parent" - "--cherry-pick" -
180 "-S*" - "--pickaxe-all" - "--pickaxe-regex" - {
184 # This appears to be the only one that has a value as a
185 # separate word following it
192 set notflag
[expr {!$notflag}]
200 # git rev-parse doesn't understand --merge
201 lappend revargs
--gitk-symmetric-diff-marker MERGE_HEAD...HEAD
203 # Other flag arguments including -<n>
205 if {[string is digit
-strict [string range
$arg 1 end
]]} {
208 # a flag argument that we don't recognize;
209 # that means we can't optimize
214 # Non-flag arguments specify commits or ranges of commits
216 if {[string match
"*...*" $arg]} {
217 lappend revargs
--gitk-symmetric-diff-marker
223 set vdflags
($n) $diffargs
224 set vflags
($n) $glflags
225 set vrevs
($n) $revargs
226 set vfiltered
($n) $filtered
227 set vorigargs
($n) $origargs
231 proc parseviewrevs
{view revs
} {
232 global vposids vnegids
237 if {[catch
{set ids
[eval exec git rev-parse
$revs]} err
]} {
238 # we get stdout followed by stderr in $err
239 # for an unknown rev, git rev-parse echoes it and then errors out
240 set errlines
[split $err "\n"]
242 for {set l
0} {$l < [llength
$errlines]} {incr l
} {
243 set line
[lindex
$errlines $l]
244 if {!([string length
$line] == 40 && [string is xdigit
$line])} {
245 if {[string match
"fatal:*" $line]} {
246 if {[string match
"fatal: ambiguous argument*" $line]
248 if {[llength
$badrev] == 1} {
249 set err
"unknown revision $badrev"
251 set err
"unknown revisions: [join $badrev ", "]"
254 set err
[join [lrange
$errlines $l end
] "\n"]
261 error_popup
"Error parsing revisions: $err"
268 foreach id
[split $ids "\n"] {
269 if {$id eq
"--gitk-symmetric-diff-marker"} {
271 } elseif
{[string match
"^*" $id]} {
278 lappend neg
[string range
$id 1 end
]
283 lset ret end
[lindex
$ret end
]...
$id
289 set vposids
($view) $pos
290 set vnegids
($view) $neg
294 # Start off a git log process and arrange to read its output
295 proc start_rev_list
{view
} {
296 global startmsecs commitidx viewcomplete curview
297 global commfd leftover tclencoding
298 global viewargs viewargscmd viewfiles vfilelimit
299 global showlocalchanges commitinterest
300 global viewactive loginstance viewinstances vmergeonly
301 global pending_select mainheadid
302 global vcanopt vflags vrevs vorigargs
304 set startmsecs
[clock clicks
-milliseconds]
305 set commitidx
($view) 0
306 # these are set this way for the error exits
307 set viewcomplete
($view) 1
308 set viewactive
($view) 0
311 set args
$viewargs($view)
312 if {$viewargscmd($view) ne
{}} {
314 set str
[exec sh
-c $viewargscmd($view)]
316 error_popup
"Error executing --argscmd command: $err"
319 set args
[concat
$args [split $str "\n"]]
321 set vcanopt
($view) [parseviewargs
$view $args]
323 set files
$viewfiles($view)
324 if {$vmergeonly($view)} {
325 set files
[unmerged_files
$files]
328 if {$nr_unmerged == 0} {
329 error_popup
[mc
"No files selected: --merge specified but\
330 no files are unmerged."]
332 error_popup
[mc
"No files selected: --merge specified but\
333 no unmerged files are within file limit."]
338 set vfilelimit
($view) $files
340 if {$vcanopt($view)} {
341 set revs
[parseviewrevs
$view $vrevs($view)]
345 set args
[concat
$vflags($view) $revs]
347 set args
$vorigargs($view)
351 set fd
[open
[concat | git log
--no-color -z --pretty=raw
--parents \
352 --boundary $args "--" $files] r
]
354 error_popup
"[mc "Error executing git log
:"] $err"
357 set i
[incr loginstance
]
358 set viewinstances
($view) [list
$i]
361 if {$showlocalchanges && $mainheadid ne
{}} {
362 lappend commitinterest
($mainheadid) {dodiffindex
}
364 fconfigure
$fd -blocking 0 -translation lf
-eofchar {}
365 if {$tclencoding != {}} {
366 fconfigure
$fd -encoding $tclencoding
368 filerun
$fd [list getcommitlines
$fd $i $view 0]
369 nowbusy
$view [mc
"Reading"]
370 if {$view == $curview} {
371 set pending_select
$mainheadid
373 set viewcomplete
($view) 0
374 set viewactive
($view) 1
378 proc stop_rev_list
{view
} {
379 global commfd viewinstances leftover
381 foreach inst
$viewinstances($view) {
382 set fd
$commfd($inst)
390 unset leftover
($inst)
392 set viewinstances
($view) {}
396 global canv curview need_redisplay viewactive
399 if {[start_rev_list
$curview]} {
400 show_status
[mc
"Reading commits..."]
403 show_status
[mc
"No commits selected"]
407 proc updatecommits
{} {
408 global curview vcanopt vorigargs vfilelimit viewinstances
409 global viewactive viewcomplete loginstance tclencoding
410 global startmsecs commfd showneartags showlocalchanges leftover
411 global mainheadid pending_select
413 global varcid vposids vnegids vflags vrevs
415 set isworktree
[expr {[exec git rev-parse
--is-inside-work-tree] == "true"}]
416 set oldmainid
$mainheadid
418 if {$showlocalchanges} {
419 if {$mainheadid ne
$oldmainid} {
422 if {[commitinview
$mainheadid $curview]} {
427 if {$vcanopt($view)} {
428 set oldpos
$vposids($view)
429 set oldneg
$vnegids($view)
430 set revs
[parseviewrevs
$view $vrevs($view)]
434 # note: getting the delta when negative refs change is hard,
435 # and could require multiple git log invocations, so in that
436 # case we ask git log for all the commits (not just the delta)
437 if {$oldneg eq
$vnegids($view)} {
440 # take out positive refs that we asked for before or
441 # that we have already seen
443 if {[string length
$rev] == 40} {
444 if {[lsearch
-exact $oldpos $rev] < 0
445 && ![info exists varcid
($view,$rev)]} {
450 lappend
$newrevs $rev
453 if {$npos == 0} return
455 set vposids
($view) [lsort
-unique [concat
$oldpos $vposids($view)]]
457 set args
[concat
$vflags($view) $revs --not $oldpos]
459 set args
$vorigargs($view)
462 set fd
[open
[concat | git log
--no-color -z --pretty=raw
--parents \
463 --boundary $args "--" $vfilelimit($view)] r
]
465 error_popup
"Error executing git log: $err"
468 if {$viewactive($view) == 0} {
469 set startmsecs
[clock clicks
-milliseconds]
471 set i
[incr loginstance
]
472 lappend viewinstances
($view) $i
475 fconfigure
$fd -blocking 0 -translation lf
-eofchar {}
476 if {$tclencoding != {}} {
477 fconfigure
$fd -encoding $tclencoding
479 filerun
$fd [list getcommitlines
$fd $i $view 1]
480 incr viewactive
($view)
481 set viewcomplete
($view) 0
482 set pending_select
$mainheadid
483 nowbusy
$view "Reading"
489 proc reloadcommits
{} {
490 global curview viewcomplete selectedline currentid thickerline
491 global showneartags treediffs commitinterest cached_commitrow
494 if {!$viewcomplete($curview)} {
495 stop_rev_list
$curview
499 catch
{unset currentid
}
500 catch
{unset thickerline
}
501 catch
{unset treediffs
}
508 catch
{unset commitinterest
}
509 catch
{unset cached_commitrow
}
510 catch
{unset targetid
}
516 # This makes a string representation of a positive integer which
517 # sorts as a string in numerical order
520 return [format
"%x" $n]
521 } elseif
{$n < 256} {
522 return [format
"x%.2x" $n]
523 } elseif
{$n < 65536} {
524 return [format
"y%.4x" $n]
526 return [format
"z%.8x" $n]
529 # Procedures used in reordering commits from git log (without
530 # --topo-order) into the order for display.
532 proc varcinit
{view
} {
533 global varcstart vupptr vdownptr vleftptr vbackptr varctok varcrow
534 global vtokmod varcmod vrowmod varcix vlastins
536 set varcstart
($view) {{}}
537 set vupptr
($view) {0}
538 set vdownptr
($view) {0}
539 set vleftptr
($view) {0}
540 set vbackptr
($view) {0}
541 set varctok
($view) {{}}
542 set varcrow
($view) {{}}
543 set vtokmod
($view) {}
546 set varcix
($view) {{}}
547 set vlastins
($view) {0}
550 proc resetvarcs
{view
} {
551 global varcid varccommits parents children vseedcount ordertok
553 foreach vid
[array names varcid
$view,*] {
558 # some commits might have children but haven't been seen yet
559 foreach vid
[array names children
$view,*] {
562 foreach va
[array names varccommits
$view,*] {
563 unset varccommits
($va)
565 foreach vd
[array names vseedcount
$view,*] {
566 unset vseedcount
($vd)
568 catch
{unset ordertok
}
571 # returns a list of the commits with no children
573 global vdownptr vleftptr varcstart
576 set a
[lindex
$vdownptr($v) 0]
578 lappend ret
[lindex
$varcstart($v) $a]
579 set a
[lindex
$vleftptr($v) $a]
584 proc newvarc
{view id
} {
585 global varcid varctok parents children vdatemode
586 global vupptr vdownptr vleftptr vbackptr varcrow varcix varcstart
587 global commitdata commitinfo vseedcount varccommits vlastins
589 set a
[llength
$varctok($view)]
591 if {[llength
$children($vid)] == 0 ||
$vdatemode($view)} {
592 if {![info exists commitinfo
($id)]} {
593 parsecommit
$id $commitdata($id) 1
595 set cdate
[lindex
$commitinfo($id) 4]
596 if {![string is integer
-strict $cdate]} {
599 if {![info exists vseedcount
($view,$cdate)]} {
600 set vseedcount
($view,$cdate) -1
602 set c
[incr vseedcount
($view,$cdate)]
603 set cdate
[expr {$cdate ^
0xffffffff}]
604 set tok
"s[strrep $cdate][strrep $c]"
609 if {[llength
$children($vid)] > 0} {
610 set kid
[lindex
$children($vid) end
]
611 set k
$varcid($view,$kid)
612 if {[string compare
[lindex
$varctok($view) $k] $tok] > 0} {
615 set tok
[lindex
$varctok($view) $k]
619 set i
[lsearch
-exact $parents($view,$ki) $id]
620 set j
[expr {[llength
$parents($view,$ki)] - 1 - $i}]
621 append tok
[strrep
$j]
623 set c
[lindex
$vlastins($view) $ka]
624 if {$c == 0 ||
[string compare
$tok [lindex
$varctok($view) $c]] < 0} {
626 set b
[lindex
$vdownptr($view) $ka]
628 set b
[lindex
$vleftptr($view) $c]
630 while {$b != 0 && [string compare
$tok [lindex
$varctok($view) $b]] >= 0} {
632 set b
[lindex
$vleftptr($view) $c]
635 lset vdownptr
($view) $ka $a
636 lappend vbackptr
($view) 0
638 lset vleftptr
($view) $c $a
639 lappend vbackptr
($view) $c
641 lset vlastins
($view) $ka $a
642 lappend vupptr
($view) $ka
643 lappend vleftptr
($view) $b
645 lset vbackptr
($view) $b $a
647 lappend varctok
($view) $tok
648 lappend varcstart
($view) $id
649 lappend vdownptr
($view) 0
650 lappend varcrow
($view) {}
651 lappend varcix
($view) {}
652 set varccommits
($view,$a) {}
653 lappend vlastins
($view) 0
657 proc splitvarc
{p v
} {
658 global varcid varcstart varccommits varctok
659 global vupptr vdownptr vleftptr vbackptr varcix varcrow vlastins
661 set oa
$varcid($v,$p)
662 set ac
$varccommits($v,$oa)
663 set i
[lsearch
-exact $varccommits($v,$oa) $p]
665 set na
[llength
$varctok($v)]
666 # "%" sorts before "0"...
667 set tok
"[lindex $varctok($v) $oa]%[strrep $i]"
668 lappend varctok
($v) $tok
669 lappend varcrow
($v) {}
670 lappend varcix
($v) {}
671 set varccommits
($v,$oa) [lrange
$ac 0 [expr {$i - 1}]]
672 set varccommits
($v,$na) [lrange
$ac $i end
]
673 lappend varcstart
($v) $p
674 foreach id
$varccommits($v,$na) {
675 set varcid
($v,$id) $na
677 lappend vdownptr
($v) [lindex
$vdownptr($v) $oa]
678 lappend vlastins
($v) [lindex
$vlastins($v) $oa]
679 lset vdownptr
($v) $oa $na
680 lset vlastins
($v) $oa 0
681 lappend vupptr
($v) $oa
682 lappend vleftptr
($v) 0
683 lappend vbackptr
($v) 0
684 for {set b
[lindex
$vdownptr($v) $na]} {$b != 0} {set b
[lindex
$vleftptr($v) $b]} {
685 lset vupptr
($v) $b $na
689 proc renumbervarc
{a v
} {
690 global parents children varctok varcstart varccommits
691 global vupptr vdownptr vleftptr vbackptr vlastins varcid vtokmod vdatemode
693 set t1
[clock clicks
-milliseconds]
699 if {[info exists isrelated
($a)]} {
701 set id
[lindex
$varccommits($v,$a) end
]
702 foreach p
$parents($v,$id) {
703 if {[info exists varcid
($v,$p)]} {
704 set isrelated
($varcid($v,$p)) 1
709 set b
[lindex
$vdownptr($v) $a]
712 set b
[lindex
$vleftptr($v) $a]
714 set a
[lindex
$vupptr($v) $a]
720 if {![info exists kidchanged
($a)]} continue
721 set id
[lindex
$varcstart($v) $a]
722 if {[llength
$children($v,$id)] > 1} {
723 set children
($v,$id) [lsort
-command [list vtokcmp
$v] \
726 set oldtok
[lindex
$varctok($v) $a]
727 if {!$vdatemode($v)} {
733 set kid
[last_real_child
$v,$id]
735 set k
$varcid($v,$kid)
736 if {[string compare
[lindex
$varctok($v) $k] $tok] > 0} {
739 set tok
[lindex
$varctok($v) $k]
743 set i
[lsearch
-exact $parents($v,$ki) $id]
744 set j
[expr {[llength
$parents($v,$ki)] - 1 - $i}]
745 append tok
[strrep
$j]
747 if {$tok eq
$oldtok} {
750 set id
[lindex
$varccommits($v,$a) end
]
751 foreach p
$parents($v,$id) {
752 if {[info exists varcid
($v,$p)]} {
753 set kidchanged
($varcid($v,$p)) 1
758 lset varctok
($v) $a $tok
759 set b
[lindex
$vupptr($v) $a]
761 if {[string compare
[lindex
$varctok($v) $ka] $vtokmod($v)] < 0} {
764 if {[string compare
[lindex
$varctok($v) $b] $vtokmod($v)] < 0} {
767 set c
[lindex
$vbackptr($v) $a]
768 set d
[lindex
$vleftptr($v) $a]
770 lset vdownptr
($v) $b $d
772 lset vleftptr
($v) $c $d
775 lset vbackptr
($v) $d $c
777 if {[lindex
$vlastins($v) $b] == $a} {
778 lset vlastins
($v) $b $c
780 lset vupptr
($v) $a $ka
781 set c
[lindex
$vlastins($v) $ka]
783 [string compare
$tok [lindex
$varctok($v) $c]] < 0} {
785 set b
[lindex
$vdownptr($v) $ka]
787 set b
[lindex
$vleftptr($v) $c]
790 [string compare
$tok [lindex
$varctok($v) $b]] >= 0} {
792 set b
[lindex
$vleftptr($v) $c]
795 lset vdownptr
($v) $ka $a
796 lset vbackptr
($v) $a 0
798 lset vleftptr
($v) $c $a
799 lset vbackptr
($v) $a $c
801 lset vleftptr
($v) $a $b
803 lset vbackptr
($v) $b $a
805 lset vlastins
($v) $ka $a
808 foreach id
[array names sortkids
] {
809 if {[llength
$children($v,$id)] > 1} {
810 set children
($v,$id) [lsort
-command [list vtokcmp
$v] \
814 set t2
[clock clicks
-milliseconds]
815 #puts "renumbervarc did [llength $todo] of $ntot arcs in [expr {$t2-$t1}]ms"
818 # Fix up the graph after we have found out that in view $v,
819 # $p (a commit that we have already seen) is actually the parent
820 # of the last commit in arc $a.
821 proc fix_reversal
{p a v
} {
822 global varcid varcstart varctok vupptr
824 set pa
$varcid($v,$p)
825 if {$p ne
[lindex
$varcstart($v) $pa]} {
827 set pa
$varcid($v,$p)
829 # seeds always need to be renumbered
830 if {[lindex
$vupptr($v) $pa] == 0 ||
831 [string compare
[lindex
$varctok($v) $a] \
832 [lindex
$varctok($v) $pa]] > 0} {
837 proc insertrow
{id p v
} {
838 global cmitlisted children parents varcid varctok vtokmod
839 global varccommits ordertok commitidx numcommits curview
840 global targetid targetrow
844 set cmitlisted
($vid) 1
845 set children
($vid) {}
846 set parents
($vid) [list
$p]
847 set a
[newvarc
$v $id]
849 if {[string compare
[lindex
$varctok($v) $a] $vtokmod($v)] < 0} {
852 lappend varccommits
($v,$a) $id
854 if {[llength
[lappend children
($vp) $id]] > 1} {
855 set children
($vp) [lsort
-command [list vtokcmp
$v] $children($vp)]
856 catch
{unset ordertok
}
858 fix_reversal
$p $a $v
860 if {$v == $curview} {
861 set numcommits
$commitidx($v)
863 if {[info exists targetid
]} {
864 if {![comes_before
$targetid $p]} {
871 proc insertfakerow
{id p
} {
872 global varcid varccommits parents children cmitlisted
873 global commitidx varctok vtokmod targetid targetrow curview numcommits
877 set i
[lsearch
-exact $varccommits($v,$a) $p]
879 puts
"oops: insertfakerow can't find [shortids $p] on arc $a"
882 set children
($v,$id) {}
883 set parents
($v,$id) [list
$p]
884 set varcid
($v,$id) $a
885 lappend children
($v,$p) $id
886 set cmitlisted
($v,$id) 1
887 set numcommits
[incr commitidx
($v)]
888 # note we deliberately don't update varcstart($v) even if $i == 0
889 set varccommits
($v,$a) [linsert
$varccommits($v,$a) $i $id]
891 if {[info exists targetid
]} {
892 if {![comes_before
$targetid $p]} {
900 proc removefakerow
{id
} {
901 global varcid varccommits parents children commitidx
902 global varctok vtokmod cmitlisted currentid selectedline
903 global targetid curview numcommits
906 if {[llength
$parents($v,$id)] != 1} {
907 puts
"oops: removefakerow [shortids $id] has [llength $parents($v,$id)] parents"
910 set p
[lindex
$parents($v,$id) 0]
911 set a
$varcid($v,$id)
912 set i
[lsearch
-exact $varccommits($v,$a) $id]
914 puts
"oops: removefakerow can't find [shortids $id] on arc $a"
918 set varccommits
($v,$a) [lreplace
$varccommits($v,$a) $i $i]
919 unset parents
($v,$id)
920 unset children
($v,$id)
921 unset cmitlisted
($v,$id)
922 set numcommits
[incr commitidx
($v) -1]
923 set j
[lsearch
-exact $children($v,$p) $id]
925 set children
($v,$p) [lreplace
$children($v,$p) $j $j]
928 if {[info exist currentid
] && $id eq
$currentid} {
932 if {[info exists targetid
] && $targetid eq
$id} {
939 proc first_real_child
{vp
} {
940 global children nullid nullid2
942 foreach id
$children($vp) {
943 if {$id ne
$nullid && $id ne
$nullid2} {
950 proc last_real_child
{vp
} {
951 global children nullid nullid2
953 set kids
$children($vp)
954 for {set i
[llength
$kids]} {[incr i
-1] >= 0} {} {
955 set id
[lindex
$kids $i]
956 if {$id ne
$nullid && $id ne
$nullid2} {
963 proc vtokcmp
{v a b
} {
964 global varctok varcid
966 return [string compare
[lindex
$varctok($v) $varcid($v,$a)] \
967 [lindex
$varctok($v) $varcid($v,$b)]]
970 # This assumes that if lim is not given, the caller has checked that
971 # arc a's token is less than $vtokmod($v)
972 proc modify_arc
{v a
{lim
{}}} {
973 global varctok vtokmod varcmod varcrow vupptr curview vrowmod varccommits
976 set c
[string compare
[lindex
$varctok($v) $a] $vtokmod($v)]
979 set r
[lindex
$varcrow($v) $a]
980 if {$r ne
{} && $vrowmod($v) <= $r + $lim} return
983 set vtokmod
($v) [lindex
$varctok($v) $a]
985 if {$v == $curview} {
986 while {$a != 0 && [lindex
$varcrow($v) $a] eq
{}} {
987 set a
[lindex
$vupptr($v) $a]
993 set lim
[llength
$varccommits($v,$a)]
995 set r
[expr {[lindex
$varcrow($v) $a] + $lim}]
1002 proc update_arcrows
{v
} {
1003 global vtokmod varcmod vrowmod varcrow commitidx currentid selectedline
1004 global varcid vrownum varcorder varcix varccommits
1005 global vupptr vdownptr vleftptr varctok
1006 global displayorder parentlist curview cached_commitrow
1008 if {$vrowmod($v) == $commitidx($v)} return
1009 if {$v == $curview} {
1010 if {[llength
$displayorder] > $vrowmod($v)} {
1011 set displayorder
[lrange
$displayorder 0 [expr {$vrowmod($v) - 1}]]
1012 set parentlist
[lrange
$parentlist 0 [expr {$vrowmod($v) - 1}]]
1014 catch
{unset cached_commitrow
}
1016 set narctot
[expr {[llength
$varctok($v)] - 1}]
1018 while {$a != 0 && [lindex
$varcix($v) $a] eq
{}} {
1019 # go up the tree until we find something that has a row number,
1020 # or we get to a seed
1021 set a
[lindex
$vupptr($v) $a]
1024 set a
[lindex
$vdownptr($v) 0]
1027 set varcorder
($v) [list
$a]
1028 lset varcix
($v) $a 0
1029 lset varcrow
($v) $a 0
1033 set arcn
[lindex
$varcix($v) $a]
1034 if {[llength
$vrownum($v)] > $arcn + 1} {
1035 set vrownum
($v) [lrange
$vrownum($v) 0 $arcn]
1036 set varcorder
($v) [lrange
$varcorder($v) 0 $arcn]
1038 set row
[lindex
$varcrow($v) $a]
1042 incr row
[llength
$varccommits($v,$a)]
1043 # go down if possible
1044 set b
[lindex
$vdownptr($v) $a]
1046 # if not, go left, or go up until we can go left
1048 set b
[lindex
$vleftptr($v) $a]
1050 set a
[lindex
$vupptr($v) $a]
1056 lappend vrownum
($v) $row
1057 lappend varcorder
($v) $a
1058 lset varcix
($v) $a $arcn
1059 lset varcrow
($v) $a $row
1061 set vtokmod
($v) [lindex
$varctok($v) $p]
1063 set vrowmod
($v) $row
1064 if {[info exists currentid
]} {
1065 set selectedline
[rowofcommit
$currentid]
1069 # Test whether view $v contains commit $id
1070 proc commitinview
{id v
} {
1073 return [info exists varcid
($v,$id)]
1076 # Return the row number for commit $id in the current view
1077 proc rowofcommit
{id
} {
1078 global varcid varccommits varcrow curview cached_commitrow
1079 global varctok vtokmod
1082 if {![info exists varcid
($v,$id)]} {
1083 puts
"oops rowofcommit no arc for [shortids $id]"
1086 set a
$varcid($v,$id)
1087 if {[string compare
[lindex
$varctok($v) $a] $vtokmod($v)] >= 0} {
1090 if {[info exists cached_commitrow
($id)]} {
1091 return $cached_commitrow($id)
1093 set i
[lsearch
-exact $varccommits($v,$a) $id]
1095 puts
"oops didn't find commit [shortids $id] in arc $a"
1098 incr i
[lindex
$varcrow($v) $a]
1099 set cached_commitrow
($id) $i
1103 # Returns 1 if a is on an earlier row than b, otherwise 0
1104 proc comes_before
{a b
} {
1105 global varcid varctok curview
1108 if {$a eq
$b ||
![info exists varcid
($v,$a)] || \
1109 ![info exists varcid
($v,$b)]} {
1112 if {$varcid($v,$a) != $varcid($v,$b)} {
1113 return [expr {[string compare
[lindex
$varctok($v) $varcid($v,$a)] \
1114 [lindex
$varctok($v) $varcid($v,$b)]] < 0}]
1116 return [expr {[rowofcommit
$a] < [rowofcommit
$b]}]
1119 proc bsearch
{l elt
} {
1120 if {[llength
$l] == 0 ||
$elt <= [lindex
$l 0]} {
1125 while {$hi - $lo > 1} {
1126 set mid
[expr {int
(($lo + $hi) / 2)}]
1127 set t
[lindex
$l $mid]
1130 } elseif
{$elt > $t} {
1139 # Make sure rows $start..$end-1 are valid in displayorder and parentlist
1140 proc make_disporder
{start end
} {
1141 global vrownum curview commitidx displayorder parentlist
1142 global varccommits varcorder parents vrowmod varcrow
1143 global d_valid_start d_valid_end
1145 if {$end > $vrowmod($curview)} {
1146 update_arcrows
$curview
1148 set ai
[bsearch
$vrownum($curview) $start]
1149 set start
[lindex
$vrownum($curview) $ai]
1150 set narc
[llength
$vrownum($curview)]
1151 for {set r
$start} {$ai < $narc && $r < $end} {incr ai
} {
1152 set a
[lindex
$varcorder($curview) $ai]
1153 set l
[llength
$displayorder]
1154 set al
[llength
$varccommits($curview,$a)]
1155 if {$l < $r + $al} {
1157 set pad
[ntimes
[expr {$r - $l}] {}]
1158 set displayorder
[concat
$displayorder $pad]
1159 set parentlist
[concat
$parentlist $pad]
1160 } elseif
{$l > $r} {
1161 set displayorder
[lrange
$displayorder 0 [expr {$r - 1}]]
1162 set parentlist
[lrange
$parentlist 0 [expr {$r - 1}]]
1164 foreach id
$varccommits($curview,$a) {
1165 lappend displayorder
$id
1166 lappend parentlist
$parents($curview,$id)
1168 } elseif
{[lindex
$displayorder [expr {$r + $al - 1}]] eq
{}} {
1170 foreach id
$varccommits($curview,$a) {
1171 lset displayorder
$i $id
1172 lset parentlist
$i $parents($curview,$id)
1180 proc commitonrow
{row
} {
1183 set id
[lindex
$displayorder $row]
1185 make_disporder
$row [expr {$row + 1}]
1186 set id
[lindex
$displayorder $row]
1191 proc closevarcs
{v
} {
1192 global varctok varccommits varcid parents children
1193 global cmitlisted commitidx commitinterest vtokmod
1195 set missing_parents
0
1197 set narcs
[llength
$varctok($v)]
1198 for {set a
1} {$a < $narcs} {incr a
} {
1199 set id
[lindex
$varccommits($v,$a) end
]
1200 foreach p
$parents($v,$id) {
1201 if {[info exists varcid
($v,$p)]} continue
1202 # add p as a new commit
1203 incr missing_parents
1204 set cmitlisted
($v,$p) 0
1205 set parents
($v,$p) {}
1206 if {[llength
$children($v,$p)] == 1 &&
1207 [llength
$parents($v,$id)] == 1} {
1210 set b
[newvarc
$v $p]
1212 set varcid
($v,$p) $b
1213 if {[string compare
[lindex
$varctok($v) $b] $vtokmod($v)] < 0} {
1216 lappend varccommits
($v,$b) $p
1218 if {[info exists commitinterest
($p)]} {
1219 foreach
script $commitinterest($p) {
1220 lappend scripts
[string map
[list
"%I" $p] $script]
1222 unset commitinterest
($id)
1226 if {$missing_parents > 0} {
1227 foreach s
$scripts {
1233 # Use $rwid as a substitute for $id, i.e. reparent $id's children to $rwid
1234 # Assumes we already have an arc for $rwid.
1235 proc rewrite_commit
{v id rwid
} {
1236 global children parents varcid varctok vtokmod varccommits
1238 foreach ch
$children($v,$id) {
1239 # make $rwid be $ch's parent in place of $id
1240 set i
[lsearch
-exact $parents($v,$ch) $id]
1242 puts
"oops rewrite_commit didn't find $id in parent list for $ch"
1244 set parents
($v,$ch) [lreplace
$parents($v,$ch) $i $i $rwid]
1245 # add $ch to $rwid's children and sort the list if necessary
1246 if {[llength
[lappend children
($v,$rwid) $ch]] > 1} {
1247 set children
($v,$rwid) [lsort
-command [list vtokcmp
$v] \
1248 $children($v,$rwid)]
1250 # fix the graph after joining $id to $rwid
1251 set a
$varcid($v,$ch)
1252 fix_reversal
$rwid $a $v
1253 # parentlist is wrong for the last element of arc $a
1254 # even if displayorder is right, hence the 3rd arg here
1255 modify_arc
$v $a [expr {[llength
$varccommits($v,$a)] - 1}]
1259 proc getcommitlines
{fd inst view updating
} {
1260 global cmitlisted commitinterest leftover
1261 global commitidx commitdata vdatemode
1262 global parents children curview hlview
1263 global idpending ordertok
1264 global varccommits varcid varctok vtokmod vfilelimit
1266 set stuff
[read $fd 500000]
1267 # git log doesn't terminate the last commit with a null...
1268 if {$stuff == {} && $leftover($inst) ne
{} && [eof
$fd]} {
1275 global commfd viewcomplete viewactive viewname
1276 global viewinstances
1278 set i
[lsearch
-exact $viewinstances($view) $inst]
1280 set viewinstances
($view) [lreplace
$viewinstances($view) $i $i]
1282 # set it blocking so we wait for the process to terminate
1283 fconfigure
$fd -blocking 1
1284 if {[catch
{close
$fd} err
]} {
1286 if {$view != $curview} {
1287 set fv
" for the \"$viewname($view)\" view"
1289 if {[string range
$err 0 4] == "usage"} {
1290 set err
"Gitk: error reading commits$fv:\
1291 bad arguments to git log."
1292 if {$viewname($view) eq
"Command line"} {
1294 " (Note: arguments to gitk are passed to git log\
1295 to allow selection of commits to be displayed.)"
1298 set err
"Error reading commits$fv: $err"
1302 if {[incr viewactive
($view) -1] <= 0} {
1303 set viewcomplete
($view) 1
1304 # Check if we have seen any ids listed as parents that haven't
1305 # appeared in the list
1309 if {$view == $curview} {
1318 set i
[string first
"\0" $stuff $start]
1320 append leftover
($inst) [string range
$stuff $start end
]
1324 set cmit
$leftover($inst)
1325 append cmit
[string range
$stuff 0 [expr {$i - 1}]]
1326 set leftover
($inst) {}
1328 set cmit
[string range
$stuff $start [expr {$i - 1}]]
1330 set start
[expr {$i + 1}]
1331 set j
[string first
"\n" $cmit]
1334 if {$j >= 0 && [string match
"commit *" $cmit]} {
1335 set ids
[string range
$cmit 7 [expr {$j - 1}]]
1336 if {[string match
{[-^
<>]*} $ids]} {
1337 switch
-- [string index
$ids 0] {
1343 set ids
[string range
$ids 1 end
]
1347 if {[string length
$id] != 40} {
1355 if {[string length
$shortcmit] > 80} {
1356 set shortcmit
"[string range $shortcmit 0 80]..."
1358 error_popup
"[mc "Can
't parse git log output:"] {$shortcmit}"
1361 set id [lindex $ids 0]
1364 if {!$listed && $updating && ![info exists varcid($vid)] &&
1365 $vfilelimit($view) ne {}} {
1366 # git log doesn't rewrite parents
for unlisted commits
1367 # when doing path limiting, so work around that here
1368 # by working out the rewritten parent with git rev-list
1369 # and if we already know about it, using the rewritten
1370 # parent as a substitute parent for $id's children.
1372 set rwid
[exec git rev-list
--first-parent --max-count=1 \
1373 $id -- $vfilelimit($view)]
1375 if {$rwid ne
{} && [info exists varcid
($view,$rwid)]} {
1376 # use $rwid in place of $id
1377 rewrite_commit
$view $id $rwid
1384 if {[info exists varcid
($vid)]} {
1385 if {$cmitlisted($vid) ||
!$listed} continue
1389 set olds
[lrange
$ids 1 end
]
1393 set commitdata
($id) [string range
$cmit [expr {$j + 1}] end
]
1394 set cmitlisted
($vid) $listed
1395 set parents
($vid) $olds
1396 if {![info exists children
($vid)]} {
1397 set children
($vid) {}
1398 } elseif
{$a == 0 && [llength
$children($vid)] == 1} {
1399 set k
[lindex
$children($vid) 0]
1400 if {[llength
$parents($view,$k)] == 1 &&
1401 (!$vdatemode($view) ||
1402 $varcid($view,$k) == [llength
$varctok($view)] - 1)} {
1403 set a
$varcid($view,$k)
1408 set a
[newvarc
$view $id]
1410 if {[string compare
[lindex
$varctok($view) $a] $vtokmod($view)] < 0} {
1413 if {![info exists varcid
($vid)]} {
1415 lappend varccommits
($view,$a) $id
1416 incr commitidx
($view)
1421 if {$i == 0 ||
[lsearch
-exact $olds $p] >= $i} {
1423 if {[llength
[lappend children
($vp) $id]] > 1 &&
1424 [vtokcmp
$view [lindex
$children($vp) end-1
] $id] > 0} {
1425 set children
($vp) [lsort
-command [list vtokcmp
$view] \
1427 catch
{unset ordertok
}
1429 if {[info exists varcid
($view,$p)]} {
1430 fix_reversal
$p $a $view
1436 if {[info exists commitinterest
($id)]} {
1437 foreach
script $commitinterest($id) {
1438 lappend scripts
[string map
[list
"%I" $id] $script]
1440 unset commitinterest
($id)
1445 global numcommits hlview
1447 if {$view == $curview} {
1448 set numcommits
$commitidx($view)
1451 if {[info exists hlview
] && $view == $hlview} {
1452 # we never actually get here...
1455 foreach s
$scripts {
1462 proc chewcommits
{} {
1463 global curview hlview viewcomplete
1464 global pending_select
1467 if {$viewcomplete($curview)} {
1468 global commitidx varctok
1469 global numcommits startmsecs
1471 if {[info exists pending_select
]} {
1472 set row
[first_real_row
]
1475 if {$commitidx($curview) > 0} {
1476 #set ms [expr {[clock clicks -milliseconds] - $startmsecs}]
1477 #puts "overall $ms ms for $numcommits commits"
1478 #puts "[llength $varctok($view)] arcs, $commitidx($view) commits"
1480 show_status
[mc
"No commits selected"]
1487 proc readcommit
{id
} {
1488 if {[catch
{set contents
[exec git cat-file commit
$id]}]} return
1489 parsecommit
$id $contents 0
1492 proc parsecommit
{id contents listed
} {
1493 global commitinfo cdate
1502 set hdrend
[string first
"\n\n" $contents]
1504 # should never happen...
1505 set hdrend
[string length
$contents]
1507 set header
[string range
$contents 0 [expr {$hdrend - 1}]]
1508 set comment
[string range
$contents [expr {$hdrend + 2}] end
]
1509 foreach line
[split $header "\n"] {
1510 set tag
[lindex
$line 0]
1511 if {$tag == "author"} {
1512 set audate
[lindex
$line end-1
]
1513 set auname
[lrange
$line 1 end-2
]
1514 } elseif
{$tag == "committer"} {
1515 set comdate
[lindex
$line end-1
]
1516 set comname
[lrange
$line 1 end-2
]
1520 # take the first non-blank line of the comment as the headline
1521 set headline
[string trimleft
$comment]
1522 set i
[string first
"\n" $headline]
1524 set headline
[string range
$headline 0 $i]
1526 set headline
[string trimright
$headline]
1527 set i
[string first
"\r" $headline]
1529 set headline
[string trimright
[string range
$headline 0 $i]]
1532 # git log indents the comment by 4 spaces;
1533 # if we got this via git cat-file, add the indentation
1535 foreach line
[split $comment "\n"] {
1536 append newcomment
" "
1537 append newcomment
$line
1538 append newcomment
"\n"
1540 set comment
$newcomment
1542 if {$comdate != {}} {
1543 set cdate
($id) $comdate
1545 set commitinfo
($id) [list
$headline $auname $audate \
1546 $comname $comdate $comment]
1549 proc getcommit
{id
} {
1550 global commitdata commitinfo
1552 if {[info exists commitdata
($id)]} {
1553 parsecommit
$id $commitdata($id) 1
1556 if {![info exists commitinfo
($id)]} {
1557 set commitinfo
($id) [list
[mc
"No commit information available"]]
1564 global tagids idtags headids idheads tagobjid
1565 global otherrefids idotherrefs mainhead mainheadid
1567 foreach v
{tagids idtags headids idheads otherrefids idotherrefs
} {
1570 set refd
[open
[list | git show-ref
-d] r
]
1571 while {[gets
$refd line
] >= 0} {
1572 if {[string index
$line 40] ne
" "} continue
1573 set id
[string range
$line 0 39]
1574 set ref
[string range
$line 41 end
]
1575 if {![string match
"refs/*" $ref]} continue
1576 set name
[string range
$ref 5 end
]
1577 if {[string match
"remotes/*" $name]} {
1578 if {![string match
"*/HEAD" $name]} {
1579 set headids
($name) $id
1580 lappend idheads
($id) $name
1582 } elseif
{[string match
"heads/*" $name]} {
1583 set name
[string range
$name 6 end
]
1584 set headids
($name) $id
1585 lappend idheads
($id) $name
1586 } elseif
{[string match
"tags/*" $name]} {
1587 # this lets refs/tags/foo^{} overwrite refs/tags/foo,
1588 # which is what we want since the former is the commit ID
1589 set name
[string range
$name 5 end
]
1590 if {[string match
"*^{}" $name]} {
1591 set name
[string range
$name 0 end-3
]
1593 set tagobjid
($name) $id
1595 set tagids
($name) $id
1596 lappend idtags
($id) $name
1598 set otherrefids
($name) $id
1599 lappend idotherrefs
($id) $name
1606 set mainheadid
[exec git rev-parse HEAD
]
1607 set thehead
[exec git symbolic-ref HEAD
]
1608 if {[string match
"refs/heads/*" $thehead]} {
1609 set mainhead
[string range
$thehead 11 end
]
1614 # skip over fake commits
1615 proc first_real_row
{} {
1616 global nullid nullid2 numcommits
1618 for {set row
0} {$row < $numcommits} {incr row
} {
1619 set id
[commitonrow
$row]
1620 if {$id ne
$nullid && $id ne
$nullid2} {
1627 # update things for a head moved to a child of its previous location
1628 proc movehead
{id name
} {
1629 global headids idheads
1631 removehead
$headids($name) $name
1632 set headids
($name) $id
1633 lappend idheads
($id) $name
1636 # update things when a head has been removed
1637 proc removehead
{id name
} {
1638 global headids idheads
1640 if {$idheads($id) eq
$name} {
1643 set i
[lsearch
-exact $idheads($id) $name]
1645 set idheads
($id) [lreplace
$idheads($id) $i $i]
1648 unset headids
($name)
1651 proc show_error
{w top msg
} {
1652 message
$w.m
-text $msg -justify center
-aspect 400
1653 pack
$w.m
-side top
-fill x
-padx 20 -pady 20
1654 button
$w.ok
-text [mc OK
] -command "destroy $top"
1655 pack
$w.ok
-side bottom
-fill x
1656 bind $top <Visibility
> "grab $top; focus $top"
1657 bind $top <Key-Return
> "destroy $top"
1661 proc error_popup msg
{
1665 show_error
$w $w $msg
1668 proc confirm_popup msg
{
1674 message
$w.m
-text $msg -justify center
-aspect 400
1675 pack
$w.m
-side top
-fill x
-padx 20 -pady 20
1676 button
$w.ok
-text [mc OK
] -command "set confirm_ok 1; destroy $w"
1677 pack
$w.ok
-side left
-fill x
1678 button
$w.cancel
-text [mc Cancel
] -command "destroy $w"
1679 pack
$w.cancel
-side right
-fill x
1680 bind $w <Visibility
> "grab $w; focus $w"
1685 proc setoptions
{} {
1686 option add
*Panedwindow.showHandle
1 startupFile
1687 option add
*Panedwindow.sashRelief raised startupFile
1688 option add
*Button.font uifont startupFile
1689 option add
*Checkbutton.font uifont startupFile
1690 option add
*Radiobutton.font uifont startupFile
1691 option add
*Menu.font uifont startupFile
1692 option add
*Menubutton.font uifont startupFile
1693 option add
*Label.font uifont startupFile
1694 option add
*Message.font uifont startupFile
1695 option add
*Entry.font uifont startupFile
1698 proc makewindow
{} {
1699 global canv canv2 canv3 linespc charspc ctext cflist cscroll
1701 global findtype findtypemenu findloc findstring fstring geometry
1702 global entries sha1entry sha1string sha1but
1703 global diffcontextstring diffcontext
1705 global maincursor textcursor curtextcursor
1706 global rowctxmenu fakerowmenu mergemax wrapcomment
1707 global highlight_files gdttype
1708 global searchstring sstring
1709 global bgcolor fgcolor bglist fglist diffcolors selectbgcolor
1710 global headctxmenu progresscanv progressitem progresscoords statusw
1711 global fprogitem fprogcoord lastprogupdate progupdatepending
1712 global rprogitem rprogcoord rownumsel numcommits
1716 .bar add cascade
-label [mc
"File"] -menu .bar.
file
1718 .bar.
file add
command -label [mc
"Update"] -command updatecommits
1719 .bar.
file add
command -label [mc
"Reload"] -command reloadcommits
1720 .bar.
file add
command -label [mc
"Reread references"] -command rereadrefs
1721 .bar.
file add
command -label [mc
"List references"] -command showrefs
1722 .bar.
file add
command -label [mc
"Quit"] -command doquit
1724 .bar add cascade
-label [mc
"Edit"] -menu .bar.edit
1725 .bar.edit add
command -label [mc
"Preferences"] -command doprefs
1728 .bar add cascade
-label [mc
"View"] -menu .bar.view
1729 .bar.view add
command -label [mc
"New view..."] -command {newview
0}
1730 .bar.view add
command -label [mc
"Edit view..."] -command editview \
1732 .bar.view add
command -label [mc
"Delete view"] -command delview
-state disabled
1733 .bar.view add separator
1734 .bar.view add radiobutton
-label [mc
"All files"] -command {showview
0} \
1735 -variable selectedview
-value 0
1738 .bar add cascade
-label [mc
"Help"] -menu .bar.
help
1739 .bar.
help add
command -label [mc
"About gitk"] -command about
1740 .bar.
help add
command -label [mc
"Key bindings"] -command keys
1742 . configure
-menu .bar
1744 # the gui has upper and lower half, parts of a paned window.
1745 panedwindow .ctop
-orient vertical
1747 # possibly use assumed geometry
1748 if {![info exists geometry
(pwsash0
)]} {
1749 set geometry
(topheight
) [expr {15 * $linespc}]
1750 set geometry
(topwidth
) [expr {80 * $charspc}]
1751 set geometry
(botheight
) [expr {15 * $linespc}]
1752 set geometry
(botwidth
) [expr {50 * $charspc}]
1753 set geometry
(pwsash0
) "[expr {40 * $charspc}] 2"
1754 set geometry
(pwsash1
) "[expr {60 * $charspc}] 2"
1757 # the upper half will have a paned window, a scroll bar to the right, and some stuff below
1758 frame .tf
-height $geometry(topheight
) -width $geometry(topwidth
)
1760 panedwindow .tf.histframe.pwclist
-orient horizontal
-sashpad 0 -handlesize 4
1762 # create three canvases
1763 set cscroll .tf.histframe.csb
1764 set canv .tf.histframe.pwclist.canv
1766 -selectbackground $selectbgcolor \
1767 -background $bgcolor -bd 0 \
1768 -yscrollincr $linespc -yscrollcommand "scrollcanv $cscroll"
1769 .tf.histframe.pwclist add
$canv
1770 set canv2 .tf.histframe.pwclist.canv2
1772 -selectbackground $selectbgcolor \
1773 -background $bgcolor -bd 0 -yscrollincr $linespc
1774 .tf.histframe.pwclist add
$canv2
1775 set canv3 .tf.histframe.pwclist.canv3
1777 -selectbackground $selectbgcolor \
1778 -background $bgcolor -bd 0 -yscrollincr $linespc
1779 .tf.histframe.pwclist add
$canv3
1780 eval .tf.histframe.pwclist sash place
0 $geometry(pwsash0
)
1781 eval .tf.histframe.pwclist sash place
1 $geometry(pwsash1
)
1783 # a scroll bar to rule them
1784 scrollbar
$cscroll -command {allcanvs yview
} -highlightthickness 0
1785 pack
$cscroll -side right
-fill y
1786 bind .tf.histframe.pwclist
<Configure
> {resizeclistpanes
%W
%w
}
1787 lappend bglist
$canv $canv2 $canv3
1788 pack .tf.histframe.pwclist
-fill both
-expand 1 -side left
1790 # we have two button bars at bottom of top frame. Bar 1
1792 frame .tf.lbar
-height 15
1794 set sha1entry .tf.bar.sha1
1795 set entries
$sha1entry
1796 set sha1but .tf.bar.sha1label
1797 button
$sha1but -text [mc
"SHA1 ID: "] -state disabled
-relief flat \
1798 -command gotocommit
-width 8
1799 $sha1but conf
-disabledforeground [$sha1but cget
-foreground]
1800 pack .tf.bar.sha1label
-side left
1801 entry
$sha1entry -width 40 -font textfont
-textvariable sha1string
1802 trace add variable sha1string
write sha1change
1803 pack
$sha1entry -side left
-pady 2
1805 image create bitmap bm-left
-data {
1806 #define left_width 16
1807 #define left_height 16
1808 static unsigned char left_bits
[] = {
1809 0x00, 0x00, 0xc0, 0x01, 0xe0, 0x00, 0x70, 0x00, 0x38, 0x00, 0x1c, 0x00,
1810 0x0e, 0x00, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0x0e, 0x00, 0x1c, 0x00,
1811 0x38, 0x00, 0x70, 0x00, 0xe0, 0x00, 0xc0, 0x01};
1813 image create bitmap bm-right
-data {
1814 #define right_width 16
1815 #define right_height 16
1816 static unsigned char right_bits
[] = {
1817 0x00, 0x00, 0xc0, 0x01, 0x80, 0x03, 0x00, 0x07, 0x00, 0x0e, 0x00, 0x1c,
1818 0x00, 0x38, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0x00, 0x38, 0x00, 0x1c,
1819 0x00, 0x0e, 0x00, 0x07, 0x80, 0x03, 0xc0, 0x01};
1821 button .tf.bar.leftbut
-image bm-left
-command goback \
1822 -state disabled
-width 26
1823 pack .tf.bar.leftbut
-side left
-fill y
1824 button .tf.bar.rightbut
-image bm-right
-command goforw \
1825 -state disabled
-width 26
1826 pack .tf.bar.rightbut
-side left
-fill y
1828 label .tf.bar.rowlabel
-text [mc
"Row"]
1830 label .tf.bar.rownum
-width 7 -font textfont
-textvariable rownumsel \
1831 -relief sunken
-anchor e
1832 label .tf.bar.rowlabel2
-text "/"
1833 label .tf.bar.numcommits
-width 7 -font textfont
-textvariable numcommits \
1834 -relief sunken
-anchor e
1835 pack .tf.bar.rowlabel .tf.bar.rownum .tf.bar.rowlabel2 .tf.bar.numcommits \
1838 trace add variable selectedline
write selectedline_change
1840 # Status label and progress bar
1841 set statusw .tf.bar.status
1842 label
$statusw -width 15 -relief sunken
1843 pack
$statusw -side left
-padx 5
1844 set h
[expr {[font metrics uifont
-linespace] + 2}]
1845 set progresscanv .tf.bar.progress
1846 canvas
$progresscanv -relief sunken
-height $h -borderwidth 2
1847 set progressitem
[$progresscanv create rect
-1 0 0 $h -fill green
]
1848 set fprogitem
[$progresscanv create rect
-1 0 0 $h -fill yellow
]
1849 set rprogitem
[$progresscanv create rect
-1 0 0 $h -fill red
]
1850 pack
$progresscanv -side right
-expand 1 -fill x
1851 set progresscoords
{0 0}
1854 bind $progresscanv <Configure
> adjustprogress
1855 set lastprogupdate
[clock clicks
-milliseconds]
1856 set progupdatepending
0
1858 # build up the bottom bar of upper window
1859 label .tf.lbar.flabel
-text "[mc "Find
"] "
1860 button .tf.lbar.fnext
-text [mc
"next"] -command {dofind
1 1}
1861 button .tf.lbar.fprev
-text [mc
"prev"] -command {dofind
-1 1}
1862 label .tf.lbar.flab2
-text " [mc "commit
"] "
1863 pack .tf.lbar.flabel .tf.lbar.fnext .tf.lbar.fprev .tf.lbar.flab2 \
1865 set gdttype
[mc
"containing:"]
1866 set gm
[tk_optionMenu .tf.lbar.gdttype gdttype \
1867 [mc
"containing:"] \
1868 [mc
"touching paths:"] \
1869 [mc
"adding/removing string:"]]
1870 trace add variable gdttype
write gdttype_change
1871 pack .tf.lbar.gdttype
-side left
-fill y
1874 set fstring .tf.lbar.findstring
1875 lappend entries
$fstring
1876 entry
$fstring -width 30 -font textfont
-textvariable findstring
1877 trace add variable findstring
write find_change
1878 set findtype
[mc
"Exact"]
1879 set findtypemenu
[tk_optionMenu .tf.lbar.findtype \
1880 findtype
[mc
"Exact"] [mc
"IgnCase"] [mc
"Regexp"]]
1881 trace add variable findtype
write findcom_change
1882 set findloc
[mc
"All fields"]
1883 tk_optionMenu .tf.lbar.findloc findloc
[mc
"All fields"] [mc
"Headline"] \
1884 [mc
"Comments"] [mc
"Author"] [mc
"Committer"]
1885 trace add variable findloc
write find_change
1886 pack .tf.lbar.findloc
-side right
1887 pack .tf.lbar.findtype
-side right
1888 pack
$fstring -side left
-expand 1 -fill x
1890 # Finish putting the upper half of the viewer together
1891 pack .tf.lbar
-in .tf
-side bottom
-fill x
1892 pack .tf.bar
-in .tf
-side bottom
-fill x
1893 pack .tf.histframe
-fill both
-side top
-expand 1
1895 .ctop paneconfigure .tf
-height $geometry(topheight
)
1896 .ctop paneconfigure .tf
-width $geometry(topwidth
)
1898 # now build up the bottom
1899 panedwindow .pwbottom
-orient horizontal
1901 # lower left, a text box over search bar, scroll bar to the right
1902 # if we know window height, then that will set the lower text height, otherwise
1903 # we set lower text height which will drive window height
1904 if {[info exists geometry
(main
)]} {
1905 frame .bleft
-width $geometry(botwidth
)
1907 frame .bleft
-width $geometry(botwidth
) -height $geometry(botheight
)
1913 button .bleft.top.search
-text [mc
"Search"] -command dosearch
1914 pack .bleft.top.search
-side left
-padx 5
1915 set sstring .bleft.top.sstring
1916 entry
$sstring -width 20 -font textfont
-textvariable searchstring
1917 lappend entries
$sstring
1918 trace add variable searchstring
write incrsearch
1919 pack
$sstring -side left
-expand 1 -fill x
1920 radiobutton .bleft.mid.
diff -text [mc
"Diff"] \
1921 -command changediffdisp
-variable diffelide
-value {0 0}
1922 radiobutton .bleft.mid.old
-text [mc
"Old version"] \
1923 -command changediffdisp
-variable diffelide
-value {0 1}
1924 radiobutton .bleft.mid.new
-text [mc
"New version"] \
1925 -command changediffdisp
-variable diffelide
-value {1 0}
1926 label .bleft.mid.labeldiffcontext
-text " [mc "Lines of context
"]: "
1927 pack .bleft.mid.
diff .bleft.mid.old .bleft.mid.new
-side left
1928 spinbox .bleft.mid.diffcontext
-width 5 -font textfont \
1929 -from 1 -increment 1 -to 10000000 \
1930 -validate all
-validatecommand "diffcontextvalidate %P" \
1931 -textvariable diffcontextstring
1932 .bleft.mid.diffcontext
set $diffcontext
1933 trace add variable diffcontextstring
write diffcontextchange
1934 lappend entries .bleft.mid.diffcontext
1935 pack .bleft.mid.labeldiffcontext .bleft.mid.diffcontext
-side left
1936 checkbutton .bleft.mid.ignspace
-text [mc
"Ignore space change"] \
1937 -command changeignorespace
-variable ignorespace
1938 pack .bleft.mid.ignspace
-side left
-padx 5
1939 set ctext .bleft.bottom.ctext
1940 text
$ctext -background $bgcolor -foreground $fgcolor \
1941 -state disabled
-font textfont \
1942 -yscrollcommand scrolltext
-wrap none \
1943 -xscrollcommand ".bleft.bottom.sbhorizontal set"
1945 $ctext conf
-tabstyle wordprocessor
1947 scrollbar .bleft.bottom.sb
-command "$ctext yview"
1948 scrollbar .bleft.bottom.sbhorizontal
-command "$ctext xview" -orient h \
1950 pack .bleft.top
-side top
-fill x
1951 pack .bleft.mid
-side top
-fill x
1952 grid
$ctext .bleft.bottom.sb
-sticky nsew
1953 grid .bleft.bottom.sbhorizontal
-sticky ew
1954 grid columnconfigure .bleft.bottom
0 -weight 1
1955 grid rowconfigure .bleft.bottom
0 -weight 1
1956 grid rowconfigure .bleft.bottom
1 -weight 0
1957 pack .bleft.bottom
-side top
-fill both
-expand 1
1958 lappend bglist
$ctext
1959 lappend fglist
$ctext
1961 $ctext tag conf comment
-wrap $wrapcomment
1962 $ctext tag conf filesep
-font textfontbold
-back "#aaaaaa"
1963 $ctext tag conf hunksep
-fore [lindex
$diffcolors 2]
1964 $ctext tag conf d0
-fore [lindex
$diffcolors 0]
1965 $ctext tag conf d1
-fore [lindex
$diffcolors 1]
1966 $ctext tag conf m0
-fore red
1967 $ctext tag conf m1
-fore blue
1968 $ctext tag conf m2
-fore green
1969 $ctext tag conf m3
-fore purple
1970 $ctext tag conf
m4 -fore brown
1971 $ctext tag conf m5
-fore "#009090"
1972 $ctext tag conf m6
-fore magenta
1973 $ctext tag conf m7
-fore "#808000"
1974 $ctext tag conf m8
-fore "#009000"
1975 $ctext tag conf m9
-fore "#ff0080"
1976 $ctext tag conf m10
-fore cyan
1977 $ctext tag conf m11
-fore "#b07070"
1978 $ctext tag conf m12
-fore "#70b0f0"
1979 $ctext tag conf m13
-fore "#70f0b0"
1980 $ctext tag conf m14
-fore "#f0b070"
1981 $ctext tag conf m15
-fore "#ff70b0"
1982 $ctext tag conf mmax
-fore darkgrey
1984 $ctext tag conf mresult
-font textfontbold
1985 $ctext tag conf msep
-font textfontbold
1986 $ctext tag conf found
-back yellow
1988 .pwbottom add .bleft
1989 .pwbottom paneconfigure .bleft
-width $geometry(botwidth
)
1994 radiobutton .bright.mode.
patch -text [mc
"Patch"] \
1995 -command reselectline
-variable cmitmode
-value "patch"
1996 radiobutton .bright.mode.tree
-text [mc
"Tree"] \
1997 -command reselectline
-variable cmitmode
-value "tree"
1998 grid .bright.mode.
patch .bright.mode.tree
-sticky ew
1999 pack .bright.mode
-side top
-fill x
2000 set cflist .bright.cfiles
2001 set indent
[font measure mainfont
"nn"]
2003 -selectbackground $selectbgcolor \
2004 -background $bgcolor -foreground $fgcolor \
2006 -tabs [list
$indent [expr {2 * $indent}]] \
2007 -yscrollcommand ".bright.sb set" \
2008 -cursor [. cget
-cursor] \
2009 -spacing1 1 -spacing3 1
2010 lappend bglist
$cflist
2011 lappend fglist
$cflist
2012 scrollbar .bright.sb
-command "$cflist yview"
2013 pack .bright.sb
-side right
-fill y
2014 pack
$cflist -side left
-fill both
-expand 1
2015 $cflist tag configure highlight \
2016 -background [$cflist cget
-selectbackground]
2017 $cflist tag configure bold
-font mainfontbold
2019 .pwbottom add .bright
2022 # restore window width & height if known
2023 if {[info exists geometry
(main
)]} {
2024 if {[scan
$geometry(main
) "%dx%d" w h
] >= 2} {
2025 if {$w > [winfo screenwidth .
]} {
2026 set w
[winfo screenwidth .
]
2028 if {$h > [winfo screenheight .
]} {
2029 set h
[winfo screenheight .
]
2031 wm geometry .
"${w}x$h"
2035 if {[tk windowingsystem
] eq
{aqua
}} {
2041 bind .pwbottom
<Configure
> {resizecdetpanes
%W
%w
}
2042 pack .ctop
-fill both
-expand 1
2043 bindall
<1> {selcanvline
%W
%x
%y
}
2044 #bindall <B1-Motion> {selcanvline %W %x %y}
2045 if {[tk windowingsystem
] == "win32"} {
2046 bind .
<MouseWheel
> { windows_mousewheel_redirector
%W
%X
%Y
%D
}
2047 bind $ctext <MouseWheel
> { windows_mousewheel_redirector
%W
%X
%Y
%D
; break }
2049 bindall
<ButtonRelease-4
> "allcanvs yview scroll -5 units"
2050 bindall
<ButtonRelease-5
> "allcanvs yview scroll 5 units"
2051 if {[tk windowingsystem
] eq
"aqua"} {
2052 bindall
<MouseWheel
> {
2053 set delta
[expr {- (%D
)}]
2054 allcanvs yview scroll
$delta units
2058 bindall
<2> "canvscan mark %W %x %y"
2059 bindall
<B2-Motion
> "canvscan dragto %W %x %y"
2060 bindkey
<Home
> selfirstline
2061 bindkey
<End
> sellastline
2062 bind .
<Key-Up
> "selnextline -1"
2063 bind .
<Key-Down
> "selnextline 1"
2064 bind .
<Shift-Key-Up
> "dofind -1 0"
2065 bind .
<Shift-Key-Down
> "dofind 1 0"
2066 bindkey
<Key-Right
> "goforw"
2067 bindkey
<Key-Left
> "goback"
2068 bind .
<Key-Prior
> "selnextpage -1"
2069 bind .
<Key-Next
> "selnextpage 1"
2070 bind .
<$M1B-Home> "allcanvs yview moveto 0.0"
2071 bind .
<$M1B-End> "allcanvs yview moveto 1.0"
2072 bind .
<$M1B-Key-Up> "allcanvs yview scroll -1 units"
2073 bind .
<$M1B-Key-Down> "allcanvs yview scroll 1 units"
2074 bind .
<$M1B-Key-Prior> "allcanvs yview scroll -1 pages"
2075 bind .
<$M1B-Key-Next> "allcanvs yview scroll 1 pages"
2076 bindkey
<Key-Delete
> "$ctext yview scroll -1 pages"
2077 bindkey
<Key-BackSpace
> "$ctext yview scroll -1 pages"
2078 bindkey
<Key-space
> "$ctext yview scroll 1 pages"
2079 bindkey p
"selnextline -1"
2080 bindkey n
"selnextline 1"
2083 bindkey i
"selnextline -1"
2084 bindkey k
"selnextline 1"
2088 bindkey d
"$ctext yview scroll 18 units"
2089 bindkey u
"$ctext yview scroll -18 units"
2090 bindkey
/ {dofind
1 1}
2091 bindkey
<Key-Return
> {dofind
1 1}
2092 bindkey ?
{dofind
-1 1}
2094 bindkey
<F5
> updatecommits
2095 bind .
<$M1B-q> doquit
2096 bind .
<$M1B-f> {dofind
1 1}
2097 bind .
<$M1B-g> {dofind
1 0}
2098 bind .
<$M1B-r> dosearchback
2099 bind .
<$M1B-s> dosearch
2100 bind .
<$M1B-equal> {incrfont
1}
2101 bind .
<$M1B-plus> {incrfont
1}
2102 bind .
<$M1B-KP_Add> {incrfont
1}
2103 bind .
<$M1B-minus> {incrfont
-1}
2104 bind .
<$M1B-KP_Subtract> {incrfont
-1}
2105 wm protocol . WM_DELETE_WINDOW doquit
2106 bind .
<Button-1
> "click %W"
2107 bind $fstring <Key-Return
> {dofind
1 1}
2108 bind $sha1entry <Key-Return
> gotocommit
2109 bind $sha1entry <<PasteSelection>> clearsha1
2110 bind $cflist <1> {sel_flist %W %x %y; break}
2111 bind $cflist <B1-Motion> {sel_flist %W %x %y; break}
2112 bind $cflist <ButtonRelease-1> {treeclick %W %x %y}
2113 bind $cflist <Button-3> {pop_flist_menu %W %X %Y %x %y}
2115 set maincursor [. cget -cursor]
2116 set textcursor [$ctext cget -cursor]
2117 set curtextcursor $textcursor
2119 set rowctxmenu .rowctxmenu
2120 menu $rowctxmenu -tearoff 0
2121 $rowctxmenu add command -label [mc "Diff this -> selected"] \
2122 -command {diffvssel 0}
2123 $rowctxmenu add command -label [mc "Diff selected -> this"] \
2124 -command {diffvssel 1}
2125 $rowctxmenu add command -label [mc "Make patch"] -command mkpatch
2126 $rowctxmenu add command -label [mc "Create tag"] -command mktag
2127 $rowctxmenu add command -label [mc "Write commit to file"] -command writecommit
2128 $rowctxmenu add command -label [mc "Create new branch"] -command mkbranch
2129 $rowctxmenu add command -label [mc "Cherry-pick this commit"] \
2131 $rowctxmenu add command -label [mc "Reset HEAD branch to here"] \
2134 set fakerowmenu .fakerowmenu
2135 menu $fakerowmenu -tearoff 0
2136 $fakerowmenu add command -label [mc "Diff this -> selected"] \
2137 -command {diffvssel 0}
2138 $fakerowmenu add command -label [mc "Diff selected -> this"] \
2139 -command {diffvssel 1}
2140 $fakerowmenu add command -label [mc "Make patch"] -command mkpatch
2141 # $fakerowmenu add command -label [mc "Commit"] -command {mkcommit 0}
2142 # $fakerowmenu add command -label [mc "Commit all"] -command {mkcommit 1}
2143 # $fakerowmenu add command -label [mc "Revert local changes"] -command revertlocal
2145 set headctxmenu .headctxmenu
2146 menu $headctxmenu -tearoff 0
2147 $headctxmenu add command -label [mc "Check out this branch"] \
2149 $headctxmenu add command -label [mc "Remove this branch"] \
2153 set flist_menu .flistctxmenu
2154 menu $flist_menu -tearoff 0
2155 $flist_menu add command -label [mc "Highlight this too"] \
2156 -command {flist_hl 0}
2157 $flist_menu add command -label [mc "Highlight this only"] \
2158 -command {flist_hl 1}
2159 $flist_menu add command -label [mc "External diff"] \
2160 -command {external_diff}
2163 # Windows sends all mouse wheel events to the current focused window, not
2164 # the one where the mouse hovers, so bind those events here and redirect
2165 # to the correct window
2166 proc windows_mousewheel_redirector {W X Y D} {
2167 global canv canv2 canv3
2168 set w [winfo containing -displayof $W $X $Y]
2170 set u [expr {$D < 0 ? 5 : -5}]
2171 if {$w == $canv || $w == $canv2 || $w == $canv3} {
2172 allcanvs yview scroll $u units
2175 $w yview scroll $u units
2181 # Update row number label when selectedline changes
2182 proc selectedline_change {n1 n2 op} {
2183 global selectedline rownumsel
2185 if {$selectedline eq {}} {
2188 set rownumsel [expr {$selectedline + 1}]
2192 # mouse-2 makes all windows scan vertically, but only the one
2193 # the cursor is in scans horizontally
2194 proc canvscan {op w x y} {
2195 global canv canv2 canv3
2196 foreach c [list $canv $canv2 $canv3] {
2205 proc scrollcanv {cscroll f0 f1} {
2206 $cscroll set $f0 $f1
2211 # when we make a key binding for the toplevel, make sure
2212 # it doesn't get triggered when that key is pressed in the
2213 # find string entry widget.
2214 proc bindkey {ev script} {
2217 set escript [bind Entry $ev]
2218 if {$escript == {}} {
2219 set escript [bind Entry <Key>]
2221 foreach e $entries {
2222 bind $e $ev "$escript; break"
2226 # set the focus back to the toplevel for any click outside
2229 global ctext entries
2230 foreach e [concat $entries $ctext] {
2231 if {$w == $e} return
2236 # Adjust the progress bar for a change in requested extent or canvas size
2237 proc adjustprogress {} {
2238 global progresscanv progressitem progresscoords
2239 global fprogitem fprogcoord lastprogupdate progupdatepending
2240 global rprogitem rprogcoord
2242 set w [expr {[winfo width $progresscanv] - 4}]
2243 set x0 [expr {$w * [lindex $progresscoords 0]}]
2244 set x1 [expr {$w * [lindex $progresscoords 1]}]
2245 set h [winfo height $progresscanv]
2246 $progresscanv coords $progressitem $x0 0 $x1 $h
2247 $progresscanv coords $fprogitem 0 0 [expr {$w * $fprogcoord}] $h
2248 $progresscanv coords $rprogitem 0 0 [expr {$w * $rprogcoord}] $h
2249 set now [clock clicks -milliseconds]
2250 if {$now >= $lastprogupdate + 100} {
2251 set progupdatepending 0
2253 } elseif {!$progupdatepending} {
2254 set progupdatepending 1
2255 after [expr {$lastprogupdate + 100 - $now}] doprogupdate
2259 proc doprogupdate {} {
2260 global lastprogupdate progupdatepending
2262 if {$progupdatepending} {
2263 set progupdatepending 0
2264 set lastprogupdate [clock clicks -milliseconds]
2269 proc savestuff {w} {
2270 global canv canv2 canv3 mainfont textfont uifont tabstop
2271 global stuffsaved findmergefiles maxgraphpct
2272 global maxwidth showneartags showlocalchanges
2273 global viewname viewfiles viewargs viewargscmd viewperm nextviewnum
2274 global cmitmode wrapcomment datetimeformat limitdiffs
2275 global colors bgcolor fgcolor diffcolors diffcontext selectbgcolor
2276 global autoselect extdifftool
2278 if {$stuffsaved} return
2279 if {![winfo viewable .]} return
2281 set f [open "~/.gitk-new" w]
2282 puts $f [list set mainfont $mainfont]
2283 puts $f [list set textfont $textfont]
2284 puts $f [list set uifont $uifont]
2285 puts $f [list set tabstop $tabstop]
2286 puts $f [list set findmergefiles $findmergefiles]
2287 puts $f [list set maxgraphpct $maxgraphpct]
2288 puts $f [list set maxwidth $maxwidth]
2289 puts $f [list set cmitmode $cmitmode]
2290 puts $f [list set wrapcomment $wrapcomment]
2291 puts $f [list set autoselect $autoselect]
2292 puts $f [list set showneartags $showneartags]
2293 puts $f [list set showlocalchanges $showlocalchanges]
2294 puts $f [list set datetimeformat $datetimeformat]
2295 puts $f [list set limitdiffs $limitdiffs]
2296 puts $f [list set bgcolor $bgcolor]
2297 puts $f [list set fgcolor $fgcolor]
2298 puts $f [list set colors $colors]
2299 puts $f [list set diffcolors $diffcolors]
2300 puts $f [list set diffcontext $diffcontext]
2301 puts $f [list set selectbgcolor $selectbgcolor]
2302 puts $f [list set extdifftool $extdifftool]
2304 puts $f "set geometry(main) [wm geometry .]"
2305 puts $f "set geometry(topwidth) [winfo width .tf]"
2306 puts $f "set geometry(topheight) [winfo height .tf]"
2307 puts $f "set geometry(pwsash0) \"[.tf.histframe.pwclist sash coord 0]\""
2308 puts $f "set geometry(pwsash1) \"[.tf.histframe.pwclist sash coord 1]\""
2309 puts $f "set geometry(botwidth) [winfo width .bleft]"
2310 puts $f "set geometry(botheight) [winfo height .bleft]"
2312 puts -nonewline $f "set permviews {"
2313 for {set v 0} {$v < $nextviewnum} {incr v} {
2314 if {$viewperm($v)} {
2315 puts $f "{[list $viewname($v) $viewfiles($v) $viewargs($v) $viewargscmd($v)]}"
2320 catch {file delete "~/.gitk"}
2321 file rename -force "~/.gitk-new" "~/.gitk"
2326 proc resizeclistpanes {win w} {
2328 if {[info exists oldwidth($win)]} {
2329 set s0 [$win sash coord 0]
2330 set s1 [$win sash coord 1]
2332 set sash0 [expr {int($w/2 - 2)}]
2333 set sash1 [expr {int($w*5/6 - 2)}]
2335 set factor [expr {1.0 * $w / $oldwidth($win)}]
2336 set sash0 [expr {int($factor * [lindex $s0 0])}]
2337 set sash1 [expr {int($factor * [lindex $s1 0])}]
2341 if {$sash1 < $sash0 + 20} {
2342 set sash1 [expr {$sash0 + 20}]
2344 if {$sash1 > $w - 10} {
2345 set sash1 [expr {$w - 10}]
2346 if {$sash0 > $sash1 - 20} {
2347 set sash0 [expr {$sash1 - 20}]
2351 $win sash place 0 $sash0 [lindex $s0 1]
2352 $win sash place 1 $sash1 [lindex $s1 1]
2354 set oldwidth($win) $w
2357 proc resizecdetpanes {win w} {
2359 if {[info exists oldwidth($win)]} {
2360 set s0 [$win sash coord 0]
2362 set sash0 [expr {int($w*3/4 - 2)}]
2364 set factor [expr {1.0 * $w / $oldwidth($win)}]
2365 set sash0 [expr {int($factor * [lindex $s0 0])}]
2369 if {$sash0 > $w - 15} {
2370 set sash0 [expr {$w - 15}]
2373 $win sash place 0 $sash0 [lindex $s0 1]
2375 set oldwidth($win) $w
2378 proc allcanvs args {
2379 global canv canv2 canv3
2385 proc bindall {event action} {
2386 global canv canv2 canv3
2387 bind $canv $event $action
2388 bind $canv2 $event $action
2389 bind $canv3 $event $action
2395 if {[winfo exists $w]} {
2400 wm title $w [mc "About gitk"]
2401 message $w.m -text [mc "
2402 Gitk - a commit viewer for git
2404 Copyright © 2005-2008 Paul Mackerras
2406 Use and redistribute under the terms of the GNU General Public License"] \
2407 -justify center -aspect 400 -border 2 -bg white -relief groove
2408 pack $w.m -side top -fill x -padx 2 -pady 2
2409 button $w.ok -text [mc "Close"] -command "destroy $w" -default active
2410 pack $w.ok -side bottom
2411 bind $w <Visibility> "focus $w.ok"
2412 bind $w <Key-Escape> "destroy $w"
2413 bind $w <Key-Return> "destroy $w"
2418 if {[winfo exists $w]} {
2422 if {[tk windowingsystem] eq {aqua}} {
2428 wm title $w [mc "Gitk key bindings"]
2429 message $w.m -text "
2430 [mc "Gitk key bindings:"]
2432 [mc "<%s-Q> Quit" $M1T]
2433 [mc "<Home> Move to first commit"]
2434 [mc "<End> Move to last commit"]
2435 [mc "<Up>, p, i Move up one commit"]
2436 [mc "<Down>, n, k Move down one commit"]
2437 [mc "<Left>, z, j Go back in history list"]
2438 [mc "<Right>, x, l Go forward in history list"]
2439 [mc "<PageUp> Move up one page in commit list"]
2440 [mc "<PageDown> Move down one page in commit list"]
2441 [mc "<%s-Home> Scroll to top of commit list" $M1T]
2442 [mc "<%s-End> Scroll to bottom of commit list" $M1T]
2443 [mc "<%s-Up> Scroll commit list up one line" $M1T]
2444 [mc "<%s-Down> Scroll commit list down one line" $M1T]
2445 [mc "<%s-PageUp> Scroll commit list up one page" $M1T]
2446 [mc "<%s-PageDown> Scroll commit list down one page" $M1T]
2447 [mc "<Shift-Up> Find backwards (upwards, later commits)"]
2448 [mc "<Shift-Down> Find forwards (downwards, earlier commits)"]
2449 [mc "<Delete>, b Scroll diff view up one page"]
2450 [mc "<Backspace> Scroll diff view up one page"]
2451 [mc "<Space> Scroll diff view down one page"]
2452 [mc "u Scroll diff view up 18 lines"]
2453 [mc "d Scroll diff view down 18 lines"]
2454 [mc "<%s-F> Find" $M1T]
2455 [mc "<%s-G> Move to next find hit" $M1T]
2456 [mc "<Return> Move to next find hit"]
2457 [mc "/ Move to next find hit, or redo find"]
2458 [mc "? Move to previous find hit"]
2459 [mc "f Scroll diff view to next file"]
2460 [mc "<%s-S> Search for next hit in diff view" $M1T]
2461 [mc "<%s-R> Search for previous hit in diff view" $M1T]
2462 [mc "<%s-KP+> Increase font size" $M1T]
2463 [mc "<%s-plus> Increase font size" $M1T]
2464 [mc "<%s-KP-> Decrease font size" $M1T]
2465 [mc "<%s-minus> Decrease font size" $M1T]
2468 -justify left -bg white -border 2 -relief groove
2469 pack $w.m -side top -fill both -padx 2 -pady 2
2470 button $w.ok -text [mc "Close"] -command "destroy $w" -default active
2471 pack $w.ok -side bottom
2472 bind $w <Visibility> "focus $w.ok"
2473 bind $w <Key-Escape> "destroy $w"
2474 bind $w <Key-Return> "destroy $w"
2477 # Procedures for manipulating the file list window at the
2478 # bottom right of the overall window.
2480 proc treeview {w l openlevs} {
2481 global treecontents treediropen treeheight treeparent treeindex
2491 set treecontents() {}
2492 $w conf -state normal
2494 while {[string range $f 0 $prefixend] ne $prefix} {
2495 if {$lev <= $openlevs} {
2496 $w mark set e:$treeindex($prefix) "end -1c"
2497 $w mark gravity e:$treeindex($prefix) left
2499 set treeheight($prefix) $ht
2500 incr ht [lindex $htstack end]
2501 set htstack [lreplace $htstack end end]
2502 set prefixend [lindex $prefendstack end]
2503 set prefendstack [lreplace $prefendstack end end]
2504 set prefix [string range $prefix 0 $prefixend]
2507 set tail [string range $f [expr {$prefixend+1}] end]
2508 while {[set slash [string first "/" $tail]] >= 0} {
2511 lappend prefendstack $prefixend
2512 incr prefixend [expr {$slash + 1}]
2513 set d [string range $tail 0 $slash]
2514 lappend treecontents($prefix) $d
2515 set oldprefix $prefix
2517 set treecontents($prefix) {}
2518 set treeindex($prefix) [incr ix]
2519 set treeparent($prefix) $oldprefix
2520 set tail [string range $tail [expr {$slash+1}] end]
2521 if {$lev <= $openlevs} {
2523 set treediropen($prefix) [expr {$lev < $openlevs}]
2524 set bm [expr {$lev == $openlevs? "tri-rt": "tri-dn"}]
2525 $w mark set d:$ix "end -1c"
2526 $w mark gravity d:$ix left
2528 for {set i 0} {$i < $lev} {incr i} {append str "\t"}
2530 $w image create end -align center -image $bm -padx 1 \
2532 $w insert end $d [highlight_tag $prefix]
2533 $w mark set s:$ix "end -1c"
2534 $w mark gravity s:$ix left
2539 if {$lev <= $openlevs} {
2542 for {set i 0} {$i < $lev} {incr i} {append str "\t"}
2544 $w insert end $tail [highlight_tag $f]
2546 lappend treecontents($prefix) $tail
2549 while {$htstack ne {}} {
2550 set treeheight($prefix) $ht
2551 incr ht [lindex $htstack end]
2552 set htstack [lreplace $htstack end end]
2553 set prefixend [lindex $prefendstack end]
2554 set prefendstack [lreplace $prefendstack end end]
2555 set prefix [string range $prefix 0 $prefixend]
2557 $w conf -state disabled
2560 proc linetoelt {l} {
2561 global treeheight treecontents
2566 foreach e $treecontents($prefix) {
2571 if {[string index $e end] eq "/"} {
2572 set n $treeheight($prefix$e)
2584 proc highlight_tree {y prefix} {
2585 global treeheight treecontents cflist
2587 foreach e $treecontents($prefix) {
2589 if {[highlight_tag $path] ne {}} {
2590 $cflist tag add bold $y.0 "$y.0 lineend"
2593 if {[string index $e end] eq "/" && $treeheight($path) > 1} {
2594 set y [highlight_tree $y $path]
2600 proc treeclosedir {w dir} {
2601 global treediropen treeheight treeparent treeindex
2603 set ix $treeindex($dir)
2604 $w conf -state normal
2605 $w delete s:$ix e:$ix
2606 set treediropen($dir) 0
2607 $w image configure a:$ix -image tri-rt
2608 $w conf -state disabled
2609 set n [expr {1 - $treeheight($dir)}]
2610 while {$dir ne {}} {
2611 incr treeheight($dir) $n
2612 set dir $treeparent($dir)
2616 proc treeopendir {w dir} {
2617 global treediropen treeheight treeparent treecontents treeindex
2619 set ix $treeindex($dir)
2620 $w conf -state normal
2621 $w image configure a:$ix -image tri-dn
2622 $w mark set e:$ix s:$ix
2623 $w mark gravity e:$ix right
2626 set n [llength $treecontents($dir)]
2627 for {set x $dir} {$x ne {}} {set x $treeparent($x)} {
2630 incr treeheight($x) $n
2632 foreach e $treecontents($dir) {
2634 if {[string index $e end] eq "/"} {
2635 set iy $treeindex($de)
2636 $w mark set d:$iy e:$ix
2637 $w mark gravity d:$iy left
2638 $w insert e:$ix $str
2639 set treediropen($de) 0
2640 $w image create e:$ix -align center -image tri-rt -padx 1 \
2642 $w insert e:$ix $e [highlight_tag $de]
2643 $w mark set s:$iy e:$ix
2644 $w mark gravity s:$iy left
2645 set treeheight($de) 1
2647 $w insert e:$ix $str
2648 $w insert e:$ix $e [highlight_tag $de]
2651 $w mark gravity e:$ix left
2652 $w conf -state disabled
2653 set treediropen($dir) 1
2654 set top [lindex [split [$w index @0,0] .] 0]
2655 set ht [$w cget -height]
2656 set l [lindex [split [$w index s:$ix] .] 0]
2659 } elseif {$l + $n + 1 > $top + $ht} {
2660 set top [expr {$l + $n + 2 - $ht}]
2668 proc treeclick {w x y} {
2669 global treediropen cmitmode ctext cflist cflist_top
2671 if {$cmitmode ne "tree"} return
2672 if {![info exists cflist_top]} return
2673 set l [lindex [split [$w index "@$x,$y"] "."] 0]
2674 $cflist tag remove highlight $cflist_top.0 "$cflist_top.0 lineend"
2675 $cflist tag add highlight $l.0 "$l.0 lineend"
2681 set e [linetoelt $l]
2682 if {[string index $e end] ne "/"} {
2684 } elseif {$treediropen($e)} {
2691 proc setfilelist {id} {
2692 global treefilelist cflist
2694 treeview $cflist $treefilelist($id) 0
2697 image create bitmap tri-rt -background black -foreground blue -data {
2698 #define tri-rt_width 13
2699 #define tri-rt_height 13
2700 static unsigned char tri-rt_bits[] = {
2701 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x30, 0x00, 0x70, 0x00, 0xf0, 0x00,
2702 0xf0, 0x01, 0xf0, 0x00, 0x70, 0x00, 0x30, 0x00, 0x10, 0x00, 0x00, 0x00,
2705 #define tri-rt-mask_width 13
2706 #define tri-rt-mask_height 13
2707 static unsigned char tri-rt-mask_bits[] = {
2708 0x08, 0x00, 0x18, 0x00, 0x38, 0x00, 0x78, 0x00, 0xf8, 0x00, 0xf8, 0x01,
2709 0xf8, 0x03, 0xf8, 0x01, 0xf8, 0x00, 0x78, 0x00, 0x38, 0x00, 0x18, 0x00,
2712 image create bitmap tri-dn -background black -foreground blue -data {
2713 #define tri-dn_width 13
2714 #define tri-dn_height 13
2715 static unsigned char tri-dn_bits[] = {
2716 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x07, 0xf8, 0x03,
2717 0xf0, 0x01, 0xe0, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2720 #define tri-dn-mask_width 13
2721 #define tri-dn-mask_height 13
2722 static unsigned char tri-dn-mask_bits[] = {
2723 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x1f, 0xfe, 0x0f, 0xfc, 0x07,
2724 0xf8, 0x03, 0xf0, 0x01, 0xe0, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
2728 image create bitmap reficon-T -background black -foreground yellow -data {
2729 #define tagicon_width 13
2730 #define tagicon_height 9
2731 static unsigned char tagicon_bits[] = {
2732 0x00, 0x00, 0x00, 0x00, 0xf0, 0x07, 0xf8, 0x07,
2733 0xfc, 0x07, 0xf8, 0x07, 0xf0, 0x07, 0x00, 0x00, 0x00, 0x00};
2735 #define tagicon-mask_width 13
2736 #define tagicon-mask_height 9
2737 static unsigned char tagicon-mask_bits[] = {
2738 0x00, 0x00, 0xf0, 0x0f, 0xf8, 0x0f, 0xfc, 0x0f,
2739 0xfe, 0x0f, 0xfc, 0x0f, 0xf8, 0x0f, 0xf0, 0x0f, 0x00, 0x00};
2742 #define headicon_width 13
2743 #define headicon_height 9
2744 static unsigned char headicon_bits[] = {
2745 0x00, 0x00, 0x00, 0x00, 0xf8, 0x07, 0xf8, 0x07,
2746 0xf8, 0x07, 0xf8, 0x07, 0xf8, 0x07, 0x00, 0x00, 0x00, 0x00};
2749 #define headicon-mask_width 13
2750 #define headicon-mask_height 9
2751 static unsigned char headicon-mask_bits[] = {
2752 0x00, 0x00, 0xfc, 0x0f, 0xfc, 0x0f, 0xfc, 0x0f,
2753 0xfc, 0x0f, 0xfc, 0x0f, 0xfc, 0x0f, 0xfc, 0x0f, 0x00, 0x00};
2755 image create bitmap reficon-H -background black -foreground green \
2756 -data $rectdata -maskdata $rectmask
2757 image create bitmap reficon-o -background black -foreground "#ddddff" \
2758 -data $rectdata -maskdata $rectmask
2760 proc init_flist {first} {
2761 global cflist cflist_top difffilestart
2763 $cflist conf -state normal
2764 $cflist delete 0.0 end
2766 $cflist insert end $first
2768 $cflist tag add highlight 1.0 "1.0 lineend"
2770 catch {unset cflist_top}
2772 $cflist conf -state disabled
2773 set difffilestart {}
2776 proc highlight_tag {f} {
2777 global highlight_paths
2779 foreach p $highlight_paths {
2780 if {[string match $p $f]} {
2787 proc highlight_filelist {} {
2788 global cmitmode cflist
2790 $cflist conf -state normal
2791 if {$cmitmode ne "tree"} {
2792 set end [lindex [split [$cflist index end] .] 0]
2793 for {set l 2} {$l < $end} {incr l} {
2794 set line [$cflist get $l.0 "$l.0 lineend"]
2795 if {[highlight_tag $line] ne {}} {
2796 $cflist tag add bold $l.0 "$l.0 lineend"
2802 $cflist conf -state disabled
2805 proc unhighlight_filelist {} {
2808 $cflist conf -state normal
2809 $cflist tag remove bold 1.0 end
2810 $cflist conf -state disabled
2813 proc add_flist {fl} {
2816 $cflist conf -state normal
2818 $cflist insert end "\n"
2819 $cflist insert end $f [highlight_tag $f]
2821 $cflist conf -state disabled
2824 proc sel_flist {w x y} {
2825 global ctext difffilestart cflist cflist_top cmitmode
2827 if {$cmitmode eq "tree"} return
2828 if {![info exists cflist_top]} return
2829 set l [lindex [split [$w index "@$x,$y"] "."] 0]
2830 $cflist tag remove highlight $cflist_top.0 "$cflist_top.0 lineend"
2831 $cflist tag add highlight $l.0 "$l.0 lineend"
2836 catch {$ctext yview [lindex $difffilestart [expr {$l - 2}]]}
2840 proc pop_flist_menu {w X Y x y} {
2841 global ctext cflist cmitmode flist_menu flist_menu_file
2842 global treediffs diffids
2845 set l [lindex [split [$w index "@$x,$y"] "."] 0]
2847 if {$cmitmode eq "tree"} {
2848 set e [linetoelt $l]
2849 if {[string index $e end] eq "/"} return
2851 set e [lindex $treediffs($diffids) [expr {$l-2}]]
2853 set flist_menu_file $e
2854 set xdiffstate "normal"
2855 if {$cmitmode eq "tree"} {
2856 set xdiffstate "disabled"
2858 # Disable "External diff" item in tree mode
2859 $flist_menu entryconf 2 -state $xdiffstate
2860 tk_popup $flist_menu $X $Y
2863 proc flist_hl {only} {
2864 global flist_menu_file findstring gdttype
2866 set x [shellquote $flist_menu_file]
2867 if {$only || $findstring eq {} || $gdttype ne [mc "touching paths:"]} {
2870 append findstring " " $x
2872 set gdttype [mc "touching paths:"]
2875 proc save_file_from_commit {filename output what} {
2878 if {[catch {exec git show $filename -- > $output} err]} {
2879 if {[string match "fatal: bad revision *" $err]} {
2882 error_popup "Error getting \"$filename\" from $what: $err"
2888 proc external_diff_get_one_file {diffid filename diffdir} {
2889 global nullid nullid2 nullfile
2892 if {$diffid == $nullid} {
2893 set difffile [file join [file dirname $gitdir] $filename]
2894 if {[file exists $difffile]} {
2899 if {$diffid == $nullid2} {
2900 set difffile [file join $diffdir "\[index\] [file tail $filename]"]
2901 return [save_file_from_commit :$filename $difffile index]
2903 set difffile [file join $diffdir "\[$diffid\] [file tail $filename]"]
2904 return [save_file_from_commit $diffid:$filename $difffile \
2908 proc external_diff {} {
2909 global gitktmpdir nullid nullid2
2910 global flist_menu_file
2913 global gitdir extdifftool
2915 if {[llength $diffids] == 1} {
2916 # no reference commit given
2917 set diffidto [lindex $diffids 0]
2918 if {$diffidto eq $nullid} {
2919 # diffing working copy with index
2920 set diffidfrom $nullid2
2921 } elseif {$diffidto eq $nullid2} {
2922 # diffing index with HEAD
2923 set diffidfrom "HEAD"
2925 # use first parent commit
2926 global parentlist selectedline
2927 set diffidfrom [lindex $parentlist $selectedline 0]
2930 set diffidfrom [lindex $diffids 0]
2931 set diffidto [lindex $diffids 1]
2934 # make sure that several diffs wont collide
2935 if {![info exists gitktmpdir]} {
2936 set gitktmpdir [file join [file dirname $gitdir] \
2937 [format ".gitk-tmp.%s" [pid]]]
2938 if {[catch {file mkdir $gitktmpdir} err]} {
2939 error_popup "Error creating temporary directory $gitktmpdir: $err"
2946 set diffdir [file join $gitktmpdir $diffnum]
2947 if {[catch {file mkdir $diffdir} err]} {
2948 error_popup "Error creating temporary directory $diffdir: $err"
2952 # gather files to diff
2953 set difffromfile [external_diff_get_one_file $diffidfrom $flist_menu_file $diffdir]
2954 set difftofile [external_diff_get_one_file $diffidto $flist_menu_file $diffdir]
2956 if {$difffromfile ne {} && $difftofile ne {}} {
2957 set cmd [concat | [shellsplit $extdifftool] \
2958 [list $difffromfile $difftofile]]
2959 if {[catch {set fl [open $cmd r]} err]} {
2960 file delete -force $diffdir
2961 error_popup [mc "$extdifftool: command failed: $err"]
2963 fconfigure $fl -blocking 0
2964 filerun $fl [list delete_at_eof $fl $diffdir]
2969 # delete $dir when we see eof on $f (presumably because the child has exited)
2970 proc delete_at_eof {f dir} {
2971 while {[gets $f line] >= 0} {}
2973 if {[catch {close $f} err]} {
2974 error_popup "External diff viewer failed: $err"
2976 file delete -force $dir
2982 # Functions for adding and removing shell-type quoting
2984 proc shellquote {str} {
2985 if {![string match "*\['\"\\ \t]*" $str]} {
2988 if {![string match "*\['\"\\]*" $str]} {
2991 if {![string match "*'*" $str]} {
2994 return "\"[string map {\" \\\" \\ \\\\} $str]\""
2997 proc shellarglist {l} {
3003 append str [shellquote $a]
3008 proc shelldequote {str} {
3013 if {![regexp -start $used -indices "\['\"\\\\ \t]" $str first]} {
3014 append ret [string range $str $used end]
3015 set used [string length $str]
3018 set first [lindex $first 0]
3019 set ch [string index $str $first]
3020 if {$first > $used} {
3021 append ret [string range $str $used [expr {$first - 1}]]
3024 if {$ch eq " " || $ch eq "\t"} break
3027 set first [string first "'" $str $used]
3029 error "unmatched single-quote"
3031 append ret [string range $str $used [expr {$first - 1}]]
3036 if {$used >= [string length $str]} {
3037 error "trailing backslash"
3039 append ret [string index $str $used]
3044 if {![regexp -start $used -indices "\[\"\\\\]" $str first]} {
3045 error "unmatched double-quote"
3047 set first [lindex $first 0]
3048 set ch [string index $str $first]
3049 if {$first > $used} {
3050 append ret [string range $str $used [expr {$first - 1}]]
3053 if {$ch eq "\""} break
3055 append ret [string index $str $used]
3059 return [list $used $ret]
3062 proc shellsplit {str} {
3065 set str [string trimleft $str]
3066 if {$str eq {}} break
3067 set dq [shelldequote $str]
3068 set n [lindex $dq 0]
3069 set word [lindex $dq 1]
3070 set str [string range $str $n end]
3076 # Code to implement multiple views
3078 proc newview {ishighlight} {
3079 global nextviewnum newviewname newviewperm newishighlight
3080 global newviewargs revtreeargs viewargscmd newviewargscmd curview
3082 set newishighlight $ishighlight
3084 if {[winfo exists $top]} {
3088 set newviewname($nextviewnum) "[mc "View"] $nextviewnum"
3089 set newviewperm($nextviewnum) 0
3090 set newviewargs($nextviewnum) [shellarglist $revtreeargs]
3091 set newviewargscmd($nextviewnum) $viewargscmd($curview)
3092 vieweditor $top $nextviewnum [mc "Gitk view definition"]
3097 global viewname viewperm newviewname newviewperm
3098 global viewargs newviewargs viewargscmd newviewargscmd
3100 set top .gitkvedit-$curview
3101 if {[winfo exists $top]} {
3105 set newviewname($curview) $viewname($curview)
3106 set newviewperm($curview) $viewperm($curview)
3107 set newviewargs($curview) [shellarglist $viewargs($curview)]
3108 set newviewargscmd($curview) $viewargscmd($curview)
3109 vieweditor $top $curview "Gitk: edit view $viewname($curview)"
3112 proc vieweditor {top n title} {
3113 global newviewname newviewperm viewfiles bgcolor
3116 wm title $top $title
3117 label $top.nl -text [mc "Name"]
3118 entry $top.name -width 20 -textvariable newviewname($n)
3119 grid $top.nl $top.name -sticky w -pady 5
3120 checkbutton $top.perm -text [mc "Remember this view"] \
3121 -variable newviewperm($n)
3122 grid $top.perm - -pady 5 -sticky w
3123 message $top.al -aspect 1000 \
3124 -text [mc "Commits to include (arguments to git log):"]
3125 grid $top.al - -sticky w -pady 5
3126 entry $top.args -width 50 -textvariable newviewargs($n) \
3127 -background $bgcolor
3128 grid $top.args - -sticky ew -padx 5
3130 message $top.ac -aspect 1000 \
3131 -text [mc "Command to generate more commits to include:"]
3132 grid $top.ac - -sticky w -pady 5
3133 entry $top.argscmd -width 50 -textvariable newviewargscmd($n) \
3135 grid $top.argscmd - -sticky ew -padx 5
3137 message $top.l -aspect 1000 \
3138 -text [mc "Enter files and directories to include, one per line:"]
3139 grid $top.l - -sticky w
3140 text $top.t -width 40 -height 10 -background $bgcolor -font uifont
3141 if {[info exists viewfiles($n)]} {
3142 foreach f $viewfiles($n) {
3143 $top.t insert end $f
3144 $top.t insert end "\n"
3146 $top.t delete {end - 1c} end
3147 $top.t mark set insert 0.0
3149 grid $top.t - -sticky ew -padx 5
3151 button $top.buts.ok -text [mc "OK"] -command [list newviewok $top $n]
3152 button $top.buts.can -text [mc "Cancel"] -command [list destroy $top]
3153 grid $top.buts.ok $top.buts.can
3154 grid columnconfigure $top.buts 0 -weight 1 -uniform a
3155 grid columnconfigure $top.buts 1 -weight 1 -uniform a
3156 grid $top.buts - -pady 10 -sticky ew
3160 proc doviewmenu {m first cmd op argv} {
3161 set nmenu [$m index end]
3162 for {set i $first} {$i <= $nmenu} {incr i} {
3163 if {[$m entrycget $i -command] eq $cmd} {
3164 eval $m $op $i $argv
3170 proc allviewmenus {n op args} {
3173 doviewmenu .bar.view 5 [list showview $n] $op $args
3174 # doviewmenu $viewhlmenu 1 [list addvhighlight $n] $op $args
3177 proc newviewok {top n} {
3178 global nextviewnum newviewperm newviewname newishighlight
3179 global viewname viewfiles viewperm selectedview curview
3180 global viewargs newviewargs viewargscmd newviewargscmd viewhlmenu
3183 set newargs [shellsplit $newviewargs($n)]
3185 error_popup "[mc "Error in commit selection arguments:"] $err"
3191 foreach f [split [$top.t get 0.0 end] "\n"] {
3192 set ft [string trim $f]
3197 if {![info exists viewfiles($n)]} {
3198 # creating a new view
3200 set viewname($n) $newviewname($n)
3201 set viewperm($n) $newviewperm($n)
3202 set viewfiles($n) $files
3203 set viewargs($n) $newargs
3204 set viewargscmd($n) $newviewargscmd($n)
3206 if {!$newishighlight} {
3209 run addvhighlight $n
3212 # editing an existing view
3213 set viewperm($n) $newviewperm($n)
3214 if {$newviewname($n) ne $viewname($n)} {
3215 set viewname($n) $newviewname($n)
3216 doviewmenu .bar.view 5 [list showview $n] \
3217 entryconf [list -label $viewname($n)]
3218 # doviewmenu $viewhlmenu 1 [list addvhighlight $n] \
3219 # entryconf [list -label $viewname($n) -value $viewname($n)]
3221 if {$files ne $viewfiles($n) || $newargs ne $viewargs($n) || \
3222 $newviewargscmd($n) ne $viewargscmd($n)} {
3223 set viewfiles($n) $files
3224 set viewargs($n) $newargs
3225 set viewargscmd($n) $newviewargscmd($n)
3226 if {$curview == $n} {
3231 catch {destroy $top}
3235 global curview viewperm hlview selectedhlview
3237 if {$curview == 0} return
3238 if {[info exists hlview] && $hlview == $curview} {
3239 set selectedhlview [mc "None"]
3242 allviewmenus $curview delete
3243 set viewperm($curview) 0
3247 proc addviewmenu {n} {
3248 global viewname viewhlmenu
3250 .bar.view add radiobutton -label $viewname($n) \
3251 -command [list showview $n] -variable selectedview -value $n
3252 #$viewhlmenu add radiobutton -label $viewname($n) \
3253 # -command [list addvhighlight $n] -variable selectedhlview
3257 global curview cached_commitrow ordertok
3258 global displayorder parentlist rowidlist rowisopt rowfinal
3259 global colormap rowtextx nextcolor canvxmax
3260 global numcommits viewcomplete
3261 global selectedline currentid canv canvy0
3263 global pending_select mainheadid
3266 global hlview selectedhlview commitinterest
3268 if {$n == $curview} return
3270 set ymax [lindex [$canv cget -scrollregion] 3]
3271 set span [$canv yview]
3272 set ytop [expr {[lindex $span 0] * $ymax}]
3273 set ybot [expr {[lindex $span 1] * $ymax}]
3274 set yscreen [expr {($ybot - $ytop) / 2}]
3275 if {$selectedline ne {}} {
3276 set selid $currentid
3277 set y [yc $selectedline]
3278 if {$ytop < $y && $y < $ybot} {
3279 set yscreen [expr {$y - $ytop}]
3281 } elseif {[info exists pending_select]} {
3282 set selid $pending_select
3283 unset pending_select
3287 catch {unset treediffs}
3289 if {[info exists hlview] && $hlview == $n} {
3291 set selectedhlview [mc "None"]
3293 catch {unset commitinterest}
3294 catch {unset cached_commitrow}
3295 catch {unset ordertok}
3299 .bar.view entryconf [mc "Edit view..."] -state [expr {$n == 0? "disabled": "normal"}]
3300 .bar.view entryconf [mc "Delete view"] -state [expr {$n == 0? "disabled": "normal"}]
3303 if {![info exists viewcomplete($n)]} {
3305 set pending_select $selid
3316 set numcommits $commitidx($n)
3318 catch {unset colormap}
3319 catch {unset rowtextx}
3321 set canvxmax [$canv cget -width]
3327 if {$selid ne {} && [commitinview $selid $n]} {
3328 set row [rowofcommit $selid]
3329 # try to get the selected row in the same position on the screen
3330 set ymax [lindex [$canv cget -scrollregion] 3]
3331 set ytop [expr {[yc $row] - $yscreen}]
3335 set yf [expr {$ytop * 1.0 / $ymax}]
3337 allcanvs yview moveto $yf
3341 } elseif {$mainheadid ne {} && [commitinview $mainheadid $curview]} {
3342 selectline [rowofcommit $mainheadid] 1
3343 } elseif {!$viewcomplete($n)} {
3345 set pending_select $selid
3347 set pending_select $mainheadid
3350 set row [first_real_row]
3351 if {$row < $numcommits} {
3355 if {!$viewcomplete($n)} {
3356 if {$numcommits == 0} {
3357 show_status [mc "Reading commits..."]
3359 } elseif {$numcommits == 0} {
3360 show_status [mc "No commits selected"]
3364 # Stuff relating to the highlighting facility
3366 proc ishighlighted {id} {
3367 global vhighlights fhighlights nhighlights rhighlights
3369 if {[info exists nhighlights($id)] && $nhighlights($id) > 0} {
3370 return $nhighlights($id)
3372 if {[info exists vhighlights($id)] && $vhighlights($id) > 0} {
3373 return $vhighlights($id)
3375 if {[info exists fhighlights($id)] && $fhighlights($id) > 0} {
3376 return $fhighlights($id)
3378 if {[info exists rhighlights($id)] && $rhighlights($id) > 0} {
3379 return $rhighlights($id)
3384 proc bolden {row font} {
3385 global canv linehtag selectedline boldrows
3387 lappend boldrows $row
3388 $canv itemconf $linehtag($row) -font $font
3389 if {$row == $selectedline} {
3391 set t [eval $canv create rect [$canv bbox $linehtag($row)] \
3392 -outline {{}} -tags secsel \
3393 -fill [$canv cget -selectbackground]]
3398 proc bolden_name {row font} {
3399 global canv2 linentag selectedline boldnamerows
3401 lappend boldnamerows $row
3402 $canv2 itemconf $linentag($row) -font $font
3403 if {$row == $selectedline} {
3404 $canv2 delete secsel
3405 set t [eval $canv2 create rect [$canv2 bbox $linentag($row)] \
3406 -outline {{}} -tags secsel \
3407 -fill [$canv2 cget -selectbackground]]
3416 foreach row $boldrows {
3417 if {![ishighlighted [commitonrow $row]]} {
3418 bolden $row mainfont
3420 lappend stillbold $row
3423 set boldrows $stillbold
3426 proc addvhighlight {n} {
3427 global hlview viewcomplete curview vhl_done commitidx
3429 if {[info exists hlview]} {
3433 if {$n != $curview && ![info exists viewcomplete($n)]} {
3436 set vhl_done $commitidx($hlview)
3437 if {$vhl_done > 0} {
3442 proc delvhighlight {} {
3443 global hlview vhighlights
3445 if {![info exists hlview]} return
3447 catch {unset vhighlights}
3451 proc vhighlightmore {} {
3452 global hlview vhl_done commitidx vhighlights curview
3454 set max $commitidx($hlview)
3455 set vr [visiblerows]
3456 set r0 [lindex $vr 0]
3457 set r1 [lindex $vr 1]
3458 for {set i $vhl_done} {$i < $max} {incr i} {
3459 set id [commitonrow $i $hlview]
3460 if {[commitinview $id $curview]} {
3461 set row [rowofcommit $id]
3462 if {$r0 <= $row && $row <= $r1} {
3463 if {![highlighted $row]} {
3464 bolden $row mainfontbold
3466 set vhighlights($id) 1
3474 proc askvhighlight {row id} {
3475 global hlview vhighlights iddrawn
3477 if {[commitinview $id $hlview]} {
3478 if {[info exists iddrawn($id)] && ![ishighlighted $id]} {
3479 bolden $row mainfontbold
3481 set vhighlights($id) 1
3483 set vhighlights($id) 0
3487 proc hfiles_change {} {
3488 global highlight_files filehighlight fhighlights fh_serial
3489 global highlight_paths gdttype
3491 if {[info exists filehighlight]} {
3492 # delete previous highlights
3493 catch {close $filehighlight}
3495 catch {unset fhighlights}
3497 unhighlight_filelist
3499 set highlight_paths {}
3500 after cancel do_file_hl $fh_serial
3502 if {$highlight_files ne {}} {
3503 after 300 do_file_hl $fh_serial
3507 proc gdttype_change {name ix op} {
3508 global gdttype highlight_files findstring findpattern
3511 if {$findstring ne {}} {
3512 if {$gdttype eq [mc "containing:"]} {
3513 if {$highlight_files ne {}} {
3514 set highlight_files {}
3519 if {$findpattern ne {}} {
3523 set highlight_files $findstring
3528 # enable/disable findtype/findloc menus too
3531 proc find_change {name ix op} {
3532 global gdttype findstring highlight_files
3535 if {$gdttype eq [mc "containing:"]} {
3538 if {$highlight_files ne $findstring} {
3539 set highlight_files $findstring
3546 proc findcom_change args {
3547 global nhighlights boldnamerows
3548 global findpattern findtype findstring gdttype
3551 # delete previous highlights, if any
3552 foreach row $boldnamerows {
3553 bolden_name $row mainfont
3556 catch {unset nhighlights}
3559 if {$gdttype ne [mc "containing:"] || $findstring eq {}} {
3561 } elseif {$findtype eq [mc "Regexp"]} {
3562 set findpattern $findstring
3564 set e [string map {"*" "\\*" "?" "\\?" "\[" "\\\[" "\\" "\\\\"} \
3566 set findpattern "*$e*"
3570 proc makepatterns {l} {
3573 set ee [string map {"*" "\\*" "?" "\\?" "\[" "\\\[" "\\" "\\\\"} $e]
3574 if {[string index $ee end] eq "/"} {
3584 proc do_file_hl {serial} {
3585 global highlight_files filehighlight highlight_paths gdttype fhl_list
3587 if {$gdttype eq [mc "touching paths:"]} {
3588 if {[catch {set paths [shellsplit $highlight_files]}]} return
3589 set highlight_paths [makepatterns $paths]
3591 set gdtargs [concat -- $paths]
3592 } elseif {$gdttype eq [mc "adding/removing string:"]} {
3593 set gdtargs [list "-S$highlight_files"]
3595 # must be "containing:", i.e. we're searching commit info
3598 set cmd [concat | git diff-tree -r -s --stdin $gdtargs]
3599 set filehighlight [open $cmd r+]
3600 fconfigure $filehighlight -blocking 0
3601 filerun $filehighlight readfhighlight
3607 proc flushhighlights {} {
3608 global filehighlight fhl_list
3610 if {[info exists filehighlight]} {
3612 puts $filehighlight ""
3613 flush $filehighlight
3617 proc askfilehighlight {row id} {
3618 global filehighlight fhighlights fhl_list
3620 lappend fhl_list $id
3621 set fhighlights($id) -1
3622 puts $filehighlight $id
3625 proc readfhighlight {} {
3626 global filehighlight fhighlights curview iddrawn
3627 global fhl_list find_dirn
3629 if {![info exists filehighlight]} {
3633 while {[incr nr] <= 100 && [gets $filehighlight line] >= 0} {
3634 set line [string trim $line]
3635 set i [lsearch -exact $fhl_list $line]
3636 if {$i < 0} continue
3637 for {set j 0} {$j < $i} {incr j} {
3638 set id [lindex $fhl_list $j]
3639 set fhighlights($id) 0
3641 set fhl_list [lrange $fhl_list [expr {$i+1}] end]
3642 if {$line eq {}} continue
3643 if {![commitinview $line $curview]} continue
3644 set row [rowofcommit $line]
3645 if {[info exists iddrawn($line)] && ![ishighlighted $line]} {
3646 bolden $row mainfontbold
3648 set fhighlights($line) 1
3650 if {[eof $filehighlight]} {
3652 puts "oops, git diff-tree died"
3653 catch {close $filehighlight}
3657 if {[info exists find_dirn]} {
3663 proc doesmatch {f} {
3664 global findtype findpattern
3666 if {$findtype eq [mc "Regexp"]} {
3667 return [regexp $findpattern $f]
3668 } elseif {$findtype eq [mc "IgnCase"]} {
3669 return [string match -nocase $findpattern $f]
3671 return [string match $findpattern $f]
3675 proc askfindhighlight {row id} {
3676 global nhighlights commitinfo iddrawn
3678 global markingmatches
3680 if {![info exists commitinfo($id)]} {
3683 set info $commitinfo($id)
3685 set fldtypes [list [mc Headline] [mc Author] [mc Date] [mc Committer] [mc CDate] [mc Comments]]
3686 foreach f $info ty $fldtypes {
3687 if {($findloc eq [mc "All fields"] || $findloc eq $ty) &&
3689 if {$ty eq [mc "Author"]} {
3696 if {$isbold && [info exists iddrawn($id)]} {
3697 if {![ishighlighted $id]} {
3698 bolden $row mainfontbold
3700 bolden_name $row mainfontbold
3703 if {$markingmatches} {
3704 markrowmatches $row $id
3707 set nhighlights($id) $isbold
3710 proc markrowmatches {row id} {
3711 global canv canv2 linehtag linentag commitinfo findloc
3713 set headline [lindex $commitinfo($id) 0]
3714 set author [lindex $commitinfo($id) 1]
3715 $canv delete match$row
3716 $canv2 delete match$row
3717 if {$findloc eq [mc "All fields"] || $findloc eq [mc "Headline"]} {
3718 set m [findmatches $headline]
3720 markmatches $canv $row $headline $linehtag($row) $m \
3721 [$canv itemcget $linehtag($row) -font] $row
3724 if {$findloc eq [mc "All fields"] || $findloc eq [mc "Author"]} {
3725 set m [findmatches $author]
3727 markmatches $canv2 $row $author $linentag($row) $m \
3728 [$canv2 itemcget $linentag($row) -font] $row
3733 proc vrel_change {name ix op} {
3734 global highlight_related
3737 if {$highlight_related ne [mc "None"]} {
3742 # prepare for testing whether commits are descendents or ancestors of a
3743 proc rhighlight_sel {a} {
3744 global descendent desc_todo ancestor anc_todo
3745 global highlight_related
3747 catch {unset descendent}
3748 set desc_todo [list $a]
3749 catch {unset ancestor}
3750 set anc_todo [list $a]
3751 if {$highlight_related ne [mc "None"]} {
3757 proc rhighlight_none {} {
3760 catch {unset rhighlights}
3764 proc is_descendent {a} {
3765 global curview children descendent desc_todo
3768 set la [rowofcommit $a]
3772 for {set i 0} {$i < [llength $todo]} {incr i} {
3773 set do [lindex $todo $i]
3774 if {[rowofcommit $do] < $la} {
3775 lappend leftover $do
3778 foreach nk $children($v,$do) {
3779 if {![info exists descendent($nk)]} {
3780 set descendent($nk) 1
3788 set desc_todo [concat $leftover [lrange $todo [expr {$i+1}] end]]
3792 set descendent($a) 0
3793 set desc_todo $leftover
3796 proc is_ancestor {a} {
3797 global curview parents ancestor anc_todo
3800 set la [rowofcommit $a]
3804 for {set i 0} {$i < [llength $todo]} {incr i} {
3805 set do [lindex $todo $i]
3806 if {![commitinview $do $v] || [rowofcommit $do] > $la} {
3807 lappend leftover $do
3810 foreach np $parents($v,$do) {
3811 if {![info exists ancestor($np)]} {
3820 set anc_todo [concat $leftover [lrange $todo [expr {$i+1}] end]]
3825 set anc_todo $leftover
3828 proc askrelhighlight {row id} {
3829 global descendent highlight_related iddrawn rhighlights
3830 global selectedline ancestor
3832 if {$selectedline eq {}} return
3834 if {$highlight_related eq [mc "Descendant"] ||
3835 $highlight_related eq [mc "Not descendant"]} {
3836 if {![info exists descendent($id)]} {
3839 if {$descendent($id) == ($highlight_related eq [mc "Descendant"])} {
3842 } elseif {$highlight_related eq [mc "Ancestor"] ||
3843 $highlight_related eq [mc "Not ancestor"]} {
3844 if {![info exists ancestor($id)]} {
3847 if {$ancestor($id) == ($highlight_related eq [mc "Ancestor"])} {
3851 if {[info exists iddrawn($id)]} {
3852 if {$isbold && ![ishighlighted $id]} {
3853 bolden $row mainfontbold
3856 set rhighlights($id) $isbold
3859 # Graph layout functions
3861 proc shortids {ids} {
3864 if {[llength $id] > 1} {
3865 lappend res [shortids $id]
3866 } elseif {[regexp {^[0-9a-f]{40}$} $id]} {
3867 lappend res [string range $id 0 7]
3878 for {set mask 1} {$mask <= $n} {incr mask $mask} {
3879 if {($n & $mask) != 0} {
3880 set ret [concat $ret $o]
3882 set o [concat $o $o]
3887 proc ordertoken {id} {
3888 global ordertok curview varcid varcstart varctok curview parents children
3889 global nullid nullid2
3891 if {[info exists ordertok($id)]} {
3892 return $ordertok($id)
3897 if {[info exists varcid($curview,$id)]} {
3898 set a $varcid($curview,$id)
3899 set p [lindex $varcstart($curview) $a]
3901 set p [lindex $children($curview,$id) 0]
3903 if {[info exists ordertok($p)]} {
3904 set tok $ordertok($p)
3907 set id [first_real_child $curview,$p]
3910 set tok [lindex $varctok($curview) $varcid($curview,$p)]
3913 if {[llength $parents($curview,$id)] == 1} {
3914 lappend todo [list $p {}]
3916 set j [lsearch -exact $parents($curview,$id) $p]
3918 puts "oops didn't find [shortids $p] in parents of [shortids $id]"
3920 lappend todo [list $p [strrep $j]]
3923 for {set i [llength $todo]} {[incr i -1] >= 0} {} {
3924 set p [lindex $todo $i 0]
3925 append tok [lindex $todo $i 1]
3926 set ordertok($p) $tok
3928 set ordertok($origid) $tok
3932 # Work out where id should go in idlist so that order-token
3933 # values increase from left to right
3934 proc idcol {idlist id {i 0}} {
3935 set t [ordertoken $id]
3939 if {$i >= [llength $idlist] || $t < [ordertoken [lindex $idlist $i]]} {
3940 if {$i > [llength $idlist]} {
3941 set i [llength $idlist]
3943 while {[incr i -1] >= 0 && $t < [ordertoken [lindex $idlist $i]]} {}
3946 if {$t > [ordertoken [lindex $idlist $i]]} {
3947 while {[incr i] < [llength $idlist] &&
3948 $t >= [ordertoken [lindex $idlist $i]]} {}
3954 proc initlayout {} {
3955 global rowidlist rowisopt rowfinal displayorder parentlist
3956 global numcommits canvxmax canv
3958 global colormap rowtextx
3967 set canvxmax [$canv cget -width]
3968 catch {unset colormap}
3969 catch {unset rowtextx}
3973 proc setcanvscroll {} {
3974 global canv canv2 canv3 numcommits linespc canvxmax canvy0
3975 global lastscrollset lastscrollrows
3977 set ymax [expr {$canvy0 + ($numcommits - 0.5) * $linespc + 2}]
3978 $canv conf -scrollregion [list 0 0 $canvxmax $ymax]
3979 $canv2 conf -scrollregion [list 0 0 0 $ymax]
3980 $canv3 conf -scrollregion [list 0 0 0 $ymax]
3981 set lastscrollset [clock clicks -milliseconds]
3982 set lastscrollrows $numcommits
3985 proc visiblerows {} {
3986 global canv numcommits linespc
3988 set ymax [lindex [$canv cget -scrollregion] 3]
3989 if {$ymax eq {} || $ymax == 0} return
3991 set y0 [expr {int([lindex $f 0] * $ymax)}]
3992 set r0 [expr {int(($y0 - 3) / $linespc) - 1}]
3996 set y1 [expr {int([lindex $f 1] * $ymax)}]
3997 set r1 [expr {int(($y1 - 3) / $linespc) + 1}]
3998 if {$r1 >= $numcommits} {
3999 set r1 [expr {$numcommits - 1}]
4001 return [list $r0 $r1]
4004 proc layoutmore {} {
4005 global commitidx viewcomplete curview
4006 global numcommits pending_select curview
4007 global lastscrollset lastscrollrows commitinterest
4009 if {$lastscrollrows < 100 || $viewcomplete($curview) ||
4010 [clock clicks -milliseconds] - $lastscrollset > 500} {
4013 if {[info exists pending_select] &&
4014 [commitinview $pending_select $curview]} {
4015 selectline [rowofcommit $pending_select] 1
4020 proc doshowlocalchanges {} {
4021 global curview mainheadid
4023 if {$mainheadid eq {}} return
4024 if {[commitinview $mainheadid $curview]} {
4027 lappend commitinterest($mainheadid) {dodiffindex}
4031 proc dohidelocalchanges {} {
4032 global nullid nullid2 lserial curview
4034 if {[commitinview $nullid $curview]} {
4035 removefakerow $nullid
4037 if {[commitinview $nullid2 $curview]} {
4038 removefakerow $nullid2
4043 # spawn off a process to do git diff-index --cached HEAD
4044 proc dodiffindex {} {
4045 global lserial showlocalchanges
4048 if {!$showlocalchanges || !$isworktree} return
4050 set fd [open "|git diff-index --cached HEAD" r]
4051 fconfigure $fd -blocking 0
4052 filerun $fd [list readdiffindex $fd $lserial]
4055 proc readdiffindex {fd serial} {
4056 global mainheadid nullid nullid2 curview commitinfo commitdata lserial
4059 if {[gets $fd line] < 0} {
4065 # we only need to see one line and we don't really care what it says...
4068 if {$serial != $lserial} {
4072 # now see if there are any local changes not checked in to the index
4073 set fd [open "|git diff-files" r]
4074 fconfigure $fd -blocking 0
4075 filerun $fd [list readdifffiles $fd $serial]
4077 if {$isdiff && ![commitinview $nullid2 $curview]} {
4078 # add the line for the changes in the index to the graph
4079 set hl [mc "Local changes checked in to index but not committed"]
4080 set commitinfo($nullid2) [list $hl {} {} {} {} " $hl\n"]
4081 set commitdata($nullid2) "\n $hl\n"
4082 if {[commitinview $nullid $curview]} {
4083 removefakerow $nullid
4085 insertfakerow $nullid2 $mainheadid
4086 } elseif {!$isdiff && [commitinview $nullid2 $curview]} {
4087 removefakerow $nullid2
4092 proc readdifffiles {fd serial} {
4093 global mainheadid nullid nullid2 curview
4094 global commitinfo commitdata lserial
4097 if {[gets $fd line] < 0} {
4103 # we only need to see one line and we don't really care what it says...
4106 if {$serial != $lserial} {
4110 if {$isdiff && ![commitinview $nullid $curview]} {
4111 # add the line for the local diff to the graph
4112 set hl [mc "Local uncommitted changes, not checked in to index"]
4113 set commitinfo($nullid) [list $hl {} {} {} {} " $hl\n"]
4114 set commitdata($nullid) "\n $hl\n"
4115 if {[commitinview $nullid2 $curview]} {
4120 insertfakerow $nullid $p
4121 } elseif {!$isdiff && [commitinview $nullid $curview]} {
4122 removefakerow $nullid
4127 proc nextuse {id row} {
4128 global curview children
4130 if {[info exists children($curview,$id)]} {
4131 foreach kid $children($curview,$id) {
4132 if {![commitinview $kid $curview]} {
4135 if {[rowofcommit $kid] > $row} {
4136 return [rowofcommit $kid]
4140 if {[commitinview $id $curview]} {
4141 return [rowofcommit $id]
4146 proc prevuse {id row} {
4147 global curview children
4150 if {[info exists children($curview,$id)]} {
4151 foreach kid $children($curview,$id) {
4152 if {![commitinview $kid $curview]} break
4153 if {[rowofcommit $kid] < $row} {
4154 set ret [rowofcommit $kid]
4161 proc make_idlist {row} {
4162 global displayorder parentlist uparrowlen downarrowlen mingaplen
4163 global commitidx curview children
4165 set r [expr {$row - $mingaplen - $downarrowlen - 1}]
4169 set ra [expr {$row - $downarrowlen}]
4173 set rb [expr {$row + $uparrowlen}]
4174 if {$rb > $commitidx($curview)} {
4175 set rb $commitidx($curview)
4177 make_disporder $r [expr {$rb + 1}]
4179 for {} {$r < $ra} {incr r} {
4180 set nextid [lindex $displayorder [expr {$r + 1}]]
4181 foreach p [lindex $parentlist $r] {
4182 if {$p eq $nextid} continue
4183 set rn [nextuse $p $r]
4185 $rn <= $r + $downarrowlen + $mingaplen + $uparrowlen} {
4186 lappend ids [list [ordertoken $p] $p]
4190 for {} {$r < $row} {incr r} {
4191 set nextid [lindex $displayorder [expr {$r + 1}]]
4192 foreach p [lindex $parentlist $r] {
4193 if {$p eq $nextid} continue
4194 set rn [nextuse $p $r]
4195 if {$rn < 0 || $rn >= $row} {
4196 lappend ids [list [ordertoken $p] $p]
4200 set id [lindex $displayorder $row]
4201 lappend ids [list [ordertoken $id] $id]
4203 foreach p [lindex $parentlist $r] {
4204 set firstkid [lindex $children($curview,$p) 0]
4205 if {[rowofcommit $firstkid] < $row} {
4206 lappend ids [list [ordertoken $p] $p]
4210 set id [lindex $displayorder $r]
4212 set firstkid [lindex $children($curview,$id) 0]
4213 if {$firstkid ne {} && [rowofcommit $firstkid] < $row} {
4214 lappend ids [list [ordertoken $id] $id]
4219 foreach idx [lsort -unique $ids] {
4220 lappend idlist [lindex $idx 1]
4225 proc rowsequal {a b} {
4226 while {[set i [lsearch -exact $a {}]] >= 0} {
4227 set a [lreplace $a $i $i]
4229 while {[set i [lsearch -exact $b {}]] >= 0} {
4230 set b [lreplace $b $i $i]
4232 return [expr {$a eq $b}]
4235 proc makeupline {id row rend col} {
4236 global rowidlist uparrowlen downarrowlen mingaplen
4238 for {set r $rend} {1} {set r $rstart} {
4239 set rstart [prevuse $id $r]
4240 if {$rstart < 0} return
4241 if {$rstart < $row} break
4243 if {$rstart + $uparrowlen + $mingaplen + $downarrowlen < $rend} {
4244 set rstart [expr {$rend - $uparrowlen - 1}]
4246 for {set r $rstart} {[incr r] <= $row} {} {
4247 set idlist [lindex $rowidlist $r]
4248 if {$idlist ne {} && [lsearch -exact $idlist $id] < 0} {
4249 set col [idcol $idlist $id $col]
4250 lset rowidlist $r [linsert $idlist $col $id]
4256 proc layoutrows {row endrow} {
4257 global rowidlist rowisopt rowfinal displayorder
4258 global uparrowlen downarrowlen maxwidth mingaplen
4259 global children parentlist
4260 global commitidx viewcomplete curview
4262 make_disporder [expr {$row - 1}] [expr {$endrow + $uparrowlen}]
4265 set rm1 [expr {$row - 1}]
4266 foreach id [lindex $rowidlist $rm1] {
4271 set final [lindex $rowfinal $rm1]
4273 for {} {$row < $endrow} {incr row} {
4274 set rm1 [expr {$row - 1}]
4275 if {$rm1 < 0 || $idlist eq {}} {
4276 set idlist [make_idlist $row]
4279 set id [lindex $displayorder $rm1]
4280 set col [lsearch -exact $idlist $id]
4281 set idlist [lreplace $idlist $col $col]
4282 foreach p [lindex $parentlist $rm1] {
4283 if {[lsearch -exact $idlist $p] < 0} {
4284 set col [idcol $idlist $p $col]
4285 set idlist [linsert $idlist $col $p]
4286 # if not the first child, we have to insert a line going up
4287 if {$id ne [lindex $children($curview,$p) 0]} {
4288 makeupline $p $rm1 $row $col
4292 set id [lindex $displayorder $row]
4293 if {$row > $downarrowlen} {
4294 set termrow [expr {$row - $downarrowlen - 1}]
4295 foreach p [lindex $parentlist $termrow] {
4296 set i [lsearch -exact $idlist $p]
4297 if {$i < 0} continue
4298 set nr [nextuse $p $termrow]
4299 if {$nr < 0 || $nr >= $row + $mingaplen + $uparrowlen} {
4300 set idlist [lreplace $idlist $i $i]
4304 set col [lsearch -exact $idlist $id]
4306 set col [idcol $idlist $id]
4307 set idlist [linsert $idlist $col $id]
4308 if {$children($curview,$id) ne {}} {
4309 makeupline $id $rm1 $row $col
4312 set r [expr {$row + $uparrowlen - 1}]
4313 if {$r < $commitidx($curview)} {
4315 foreach p [lindex $parentlist $r] {
4316 if {[lsearch -exact $idlist $p] >= 0} continue
4317 set fk [lindex $children($curview,$p) 0]
4318 if {[rowofcommit $fk] < $row} {
4319 set x [idcol $idlist $p $x]
4320 set idlist [linsert $idlist $x $p]
4323 if {[incr r] < $commitidx($curview)} {
4324 set p [lindex $displayorder $r]
4325 if {[lsearch -exact $idlist $p] < 0} {
4326 set fk [lindex $children($curview,$p) 0]
4327 if {$fk ne {} && [rowofcommit $fk] < $row} {
4328 set x [idcol $idlist $p $x]
4329 set idlist [linsert $idlist $x $p]
4335 if {$final && !$viewcomplete($curview) &&
4336 $row + $uparrowlen + $mingaplen + $downarrowlen
4337 >= $commitidx($curview)} {
4340 set l [llength $rowidlist]
4342 lappend rowidlist $idlist
4344 lappend rowfinal $final
4345 } elseif {$row < $l} {
4346 if {![rowsequal $idlist [lindex $rowidlist $row]]} {
4347 lset rowidlist $row $idlist
4350 lset rowfinal $row $final
4352 set pad [ntimes [expr {$row - $l}] {}]
4353 set rowidlist [concat $rowidlist $pad]
4354 lappend rowidlist $idlist
4355 set rowfinal [concat $rowfinal $pad]
4356 lappend rowfinal $final
4357 set rowisopt [concat $rowisopt [ntimes [expr {$row - $l + 1}] 0]]
4363 proc changedrow {row} {
4364 global displayorder iddrawn rowisopt need_redisplay
4366 set l [llength $rowisopt]
4368 lset rowisopt $row 0
4369 if {$row + 1 < $l} {
4370 lset rowisopt [expr {$row + 1}] 0
4371 if {$row + 2 < $l} {
4372 lset rowisopt [expr {$row + 2}] 0
4376 set id [lindex $displayorder $row]
4377 if {[info exists iddrawn($id)]} {
4378 set need_redisplay 1
4382 proc insert_pad {row col npad} {
4385 set pad [ntimes $npad {}]
4386 set idlist [lindex $rowidlist $row]
4387 set bef [lrange $idlist 0 [expr {$col - 1}]]
4388 set aft [lrange $idlist $col end]
4389 set i [lsearch -exact $aft {}]
4391 set aft [lreplace $aft $i $i]
4393 lset rowidlist $row [concat $bef $pad $aft]
4397 proc optimize_rows {row col endrow} {
4398 global rowidlist rowisopt displayorder curview children
4403 for {} {$row < $endrow} {incr row; set col 0} {
4404 if {[lindex $rowisopt $row]} continue
4406 set y0 [expr {$row - 1}]
4407 set ym [expr {$row - 2}]
4408 set idlist [lindex $rowidlist $row]
4409 set previdlist [lindex $rowidlist $y0]
4410 if {$idlist eq {} || $previdlist eq {}} continue
4412 set pprevidlist [lindex $rowidlist $ym]
4413 if {$pprevidlist eq {}} continue
4419 for {} {$col < [llength $idlist]} {incr col} {
4420 set id [lindex $idlist $col]
4421 if {[lindex $previdlist $col] eq $id} continue
4426 set x0 [lsearch -exact $previdlist $id]
4427 if {$x0 < 0} continue
4428 set z [expr {$x0 - $col}]
4432 set xm [lsearch -exact $pprevidlist $id]
4434 set z0 [expr {$xm - $x0}]
4438 # if row y0 is the first child of $id then it's not an arrow
4439 if {[lindex $children($curview,$id) 0] ne
4440 [lindex $displayorder $y0]} {
4444 if {!$isarrow && $id ne [lindex $displayorder $row] &&
4445 [lsearch -exact [lindex $rowidlist [expr {$row+1}]] $id] < 0} {
4448 # Looking at lines from this row to the previous row,
4449 # make them go straight up if they end in an arrow on
4450 # the previous row; otherwise make them go straight up
4452 if {$z < -1 || ($z < 0 && $isarrow)} {
4453 # Line currently goes left too much;
4454 # insert pads in the previous row, then optimize it
4455 set npad [expr {-1 - $z + $isarrow}]
4456 insert_pad $y0 $x0 $npad
4458 optimize_rows $y0 $x0 $row
4460 set previdlist [lindex $rowidlist $y0]
4461 set x0 [lsearch -exact $previdlist $id]
4462 set z [expr {$x0 - $col}]
4464 set pprevidlist [lindex $rowidlist $ym]
4465 set xm [lsearch -exact $pprevidlist $id]
4466 set z0 [expr {$xm - $x0}]
4468 } elseif {$z > 1 || ($z > 0 && $isarrow)} {
4469 # Line currently goes right too much;
4470 # insert pads in this line
4471 set npad [expr {$z - 1 + $isarrow}]
4472 insert_pad $row $col $npad
4473 set idlist [lindex $rowidlist $row]
4475 set z [expr {$x0 - $col}]
4478 if {$z0 eq {} && !$isarrow && $ym >= 0} {
4479 # this line links to its first child on row $row-2
4480 set id [lindex $displayorder $ym]
4481 set xc [lsearch -exact $pprevidlist $id]
4483 set z0 [expr {$xc - $x0}]
4486 # avoid lines jigging left then immediately right
4487 if {$z0 ne {} && $z < 0 && $z0 > 0} {
4488 insert_pad $y0 $x0 1
4490 optimize_rows $y0 $x0 $row
4491 set previdlist [lindex $rowidlist $y0]
4495 # Find the first column that doesn't have a line going right
4496 for {set col [llength $idlist]} {[incr col -1] >= 0} {} {
4497 set id [lindex $idlist $col]
4498 if {$id eq {}} break
4499 set x0 [lsearch -exact $previdlist $id]
4501 # check if this is the link to the first child
4502 set kid [lindex $displayorder $y0]
4503 if {[lindex $children($curview,$id) 0] eq $kid} {
4504 # it is, work out offset to child
4505 set x0 [lsearch -exact $previdlist $kid]
4508 if {$x0 <= $col} break
4510 # Insert a pad at that column as long as it has a line and
4511 # isn't the last column
4512 if {$x0 >= 0 && [incr col] < [llength $idlist]} {
4513 set idlist [linsert $idlist $col {}]
4514 lset rowidlist $row $idlist
4522 global canvx0 linespc
4523 return [expr {$canvx0 + $col * $linespc}]
4527 global canvy0 linespc
4528 return [expr {$canvy0 + $row * $linespc}]
4531 proc linewidth {id} {
4532 global thickerline lthickness
4535 if {[info exists thickerline] && $id eq $thickerline} {
4536 set wid [expr {2 * $lthickness}]
4541 proc rowranges {id} {
4542 global curview children uparrowlen downarrowlen
4545 set kids $children($curview,$id)
4551 foreach child $kids {
4552 if {![commitinview $child $curview]} break
4553 set row [rowofcommit $child]
4554 if {![info exists prev]} {
4555 lappend ret [expr {$row + 1}]
4557 if {$row <= $prevrow} {
4558 puts "oops children of [shortids $id] out of order [shortids $child] $row <= [shortids $prev] $prevrow"
4560 # see if the line extends the whole way from prevrow to row
4561 if {$row > $prevrow + $uparrowlen + $downarrowlen &&
4562 [lsearch -exact [lindex $rowidlist \
4563 [expr {int(($row + $prevrow) / 2)}]] $id] < 0} {
4564 # it doesn't, see where it ends
4565 set r [expr {$prevrow + $downarrowlen}]
4566 if {[lsearch -exact [lindex $rowidlist $r] $id] < 0} {
4567 while {[incr r -1] > $prevrow &&
4568 [lsearch -exact [lindex $rowidlist $r] $id] < 0} {}
4570 while {[incr r] <= $row &&
4571 [lsearch -exact [lindex $rowidlist $r] $id] >= 0} {}
4575 # see where it starts up again
4576 set r [expr {$row - $uparrowlen}]
4577 if {[lsearch -exact [lindex $rowidlist $r] $id] < 0} {
4578 while {[incr r] < $row &&
4579 [lsearch -exact [lindex $rowidlist $r] $id] < 0} {}
4581 while {[incr r -1] >= $prevrow &&
4582 [lsearch -exact [lindex $rowidlist $r] $id] >= 0} {}
4588 if {$child eq $id} {
4597 proc drawlineseg {id row endrow arrowlow} {
4598 global rowidlist displayorder iddrawn linesegs
4599 global canv colormap linespc curview maxlinelen parentlist
4601 set cols [list [lsearch -exact [lindex $rowidlist $row] $id]]
4602 set le [expr {$row + 1}]
4605 set c [lsearch -exact [lindex $rowidlist $le] $id]
4611 set x [lindex $displayorder $le]
4616 if {[info exists iddrawn($x)] || $le == $endrow} {
4617 set c [lsearch -exact [lindex $rowidlist [expr {$le+1}]] $id]
4633 if {[info exists linesegs($id)]} {
4634 set lines $linesegs($id)
4636 set r0 [lindex $li 0]
4638 if {$r0 == $le && [lindex $li 1] - $row <= $maxlinelen} {
4648 set li [lindex $lines [expr {$i-1}]]
4649 set r1 [lindex $li 1]
4650 if {$r1 == $row && $le - [lindex $li 0] <= $maxlinelen} {
4655 set x [lindex $cols [expr {$le - $row}]]
4656 set xp [lindex $cols [expr {$le - 1 - $row}]]
4657 set dir [expr {$xp - $x}]
4659 set ith [lindex $lines $i 2]
4660 set coords [$canv coords $ith]
4661 set ah [$canv itemcget $ith -arrow]
4662 set arrowhigh [expr {$ah eq "first" || $ah eq "both"}]
4663 set x2 [lindex $cols [expr {$le + 1 - $row}]]
4664 if {$x2 ne {} && $x - $x2 == $dir} {
4665 set coords [lrange $coords 0 end-2]
4668 set coords [list [xc $le $x] [yc $le]]
4671 set itl [lindex $lines [expr {$i-1}] 2]
4672 set al [$canv itemcget $itl -arrow]
4673 set arrowlow [expr {$al eq "last" || $al eq "both"}]
4674 } elseif {$arrowlow} {
4675 if {[lsearch -exact [lindex $rowidlist [expr {$row-1}]] $id] >= 0 ||
4676 [lsearch -exact [lindex $parentlist [expr {$row-1}]] $id] >= 0} {
4680 set arrow [lindex {none first last both} [expr {$arrowhigh + 2*$arrowlow}]]
4681 for {set y $le} {[incr y -1] > $row} {} {
4683 set xp [lindex $cols [expr {$y - 1 - $row}]]
4684 set ndir [expr {$xp - $x}]
4685 if {$dir != $ndir || $xp < 0} {
4686 lappend coords [xc $y $x] [yc $y]
4692 # join parent line to first child
4693 set ch [lindex $displayorder $row]
4694 set xc [lsearch -exact [lindex $rowidlist $row] $ch]
4696 puts "oops: drawlineseg: child $ch not on row $row"
4697 } elseif {$xc != $x} {
4698 if {($arrowhigh && $le == $row + 1) || $dir == 0} {
4699 set d [expr {int(0.5 * $linespc)}]
4702 set x2 [expr {$x1 - $d}]
4704 set x2 [expr {$x1 + $d}]
4707 set y1 [expr {$y2 + $d}]
4708 lappend coords $x1 $y1 $x2 $y2
4709 } elseif {$xc < $x - 1} {
4710 lappend coords [xc $row [expr {$x-1}]] [yc $row]
4711 } elseif {$xc > $x + 1} {
4712 lappend coords [xc $row [expr {$x+1}]] [yc $row]
4716 lappend coords [xc $row $x] [yc $row]
4718 set xn [xc $row $xp]
4720 lappend coords $xn $yn
4724 set t [$canv create line $coords -width [linewidth $id] \
4725 -fill $colormap($id) -tags lines.$id -arrow $arrow]
4728 set lines [linsert $lines $i [list $row $le $t]]
4730 $canv coords $ith $coords
4731 if {$arrow ne $ah} {
4732 $canv itemconf $ith -arrow $arrow
4734 lset lines $i 0 $row
4737 set xo [lsearch -exact [lindex $rowidlist [expr {$row - 1}]] $id]
4738 set ndir [expr {$xo - $xp}]
4739 set clow [$canv coords $itl]
4740 if {$dir == $ndir} {
4741 set clow [lrange $clow 2 end]
4743 set coords [concat $coords $clow]
4745 lset lines [expr {$i-1}] 1 $le
4747 # coalesce two pieces
4749 set b [lindex $lines [expr {$i-1}] 0]
4750 set e [lindex $lines $i 1]
4751 set lines [lreplace $lines [expr {$i-1}] $i [list $b $e $itl]]
4753 $canv coords $itl $coords
4754 if {$arrow ne $al} {
4755 $canv itemconf $itl -arrow $arrow
4759 set linesegs($id) $lines
4763 proc drawparentlinks {id row} {
4764 global rowidlist canv colormap curview parentlist
4765 global idpos linespc
4767 set rowids [lindex $rowidlist $row]
4768 set col [lsearch -exact $rowids $id]
4769 if {$col < 0} return
4770 set olds [lindex $parentlist $row]
4771 set row2 [expr {$row + 1}]
4772 set x [xc $row $col]
4775 set d [expr {int(0.5 * $linespc)}]
4776 set ymid [expr {$y + $d}]
4777 set ids [lindex $rowidlist $row2]
4778 # rmx = right-most X coord used
4781 set i [lsearch -exact $ids $p]
4783 puts "oops, parent $p of $id not in list"
4786 set x2 [xc $row2 $i]
4790 set j [lsearch -exact $rowids $p]
4792 # drawlineseg will do this one for us
4796 # should handle duplicated parents here...
4797 set coords [list $x $y]
4799 # if attaching to a vertical segment, draw a smaller
4800 # slant for visual distinctness
4803 lappend coords [expr {$x2 + $d}] $y $x2 $ymid
4805 lappend coords [expr {$x2 - $d}] $y $x2 $ymid
4807 } elseif {$i < $col && $i < $j} {
4808 # segment slants towards us already
4809 lappend coords [xc $row $j] $y
4811 if {$i < $col - 1} {
4812 lappend coords [expr {$x2 + $linespc}] $y
4813 } elseif {$i > $col + 1} {
4814 lappend coords [expr {$x2 - $linespc}] $y
4816 lappend coords $x2 $y2
4819 lappend coords $x2 $y2
4821 set t [$canv create line $coords -width [linewidth $p] \
4822 -fill $colormap($p) -tags lines.$p]
4826 if {$rmx > [lindex $idpos($id) 1]} {
4827 lset idpos($id) 1 $rmx
4832 proc drawlines {id} {
4835 $canv itemconf lines.$id -width [linewidth $id]
4838 proc drawcmittext {id row col} {
4839 global linespc canv canv2 canv3 fgcolor curview
4840 global cmitlisted commitinfo rowidlist parentlist
4841 global rowtextx idpos idtags idheads idotherrefs
4842 global linehtag linentag linedtag selectedline
4843 global canvxmax boldrows boldnamerows fgcolor
4844 global mainheadid nullid nullid2 circleitem circlecolors
4846 # listed is 0 for boundary, 1 for normal, 2 for negative, 3 for left, 4 for right
4847 set listed $cmitlisted($curview,$id)
4848 if {$id eq $nullid} {
4850 } elseif {$id eq $nullid2} {
4852 } elseif {$id eq $mainheadid} {
4855 set ofill [lindex $circlecolors $listed]
4857 set x [xc $row $col]
4859 set orad [expr {$linespc / 3}]
4861 set t [$canv create oval [expr {$x - $orad}] [expr {$y - $orad}] \
4862 [expr {$x + $orad - 1}] [expr {$y + $orad - 1}] \
4863 -fill $ofill -outline $fgcolor -width 1 -tags circle]
4864 } elseif {$listed == 3} {
4865 # triangle pointing left for left-side commits
4866 set t [$canv create polygon \
4867 [expr {$x - $orad}] $y \
4868 [expr {$x + $orad - 1}] [expr {$y - $orad}] \
4869 [expr {$x + $orad - 1}] [expr {$y + $orad - 1}] \
4870 -fill $ofill -outline $fgcolor -width 1 -tags circle]
4872 # triangle pointing right for right-side commits
4873 set t [$canv create polygon \
4874 [expr {$x + $orad - 1}] $y \
4875 [expr {$x - $orad}] [expr {$y - $orad}] \
4876 [expr {$x - $orad}] [expr {$y + $orad - 1}] \
4877 -fill $ofill -outline $fgcolor -width 1 -tags circle]
4879 set circleitem($row) $t
4881 $canv bind $t <1> {selcanvline {} %x %y}
4882 set rmx [llength [lindex $rowidlist $row]]
4883 set olds [lindex $parentlist $row]
4885 set nextids [lindex $rowidlist [expr {$row + 1}]]
4887 set i [lsearch -exact $nextids $p]
4893 set xt [xc $row $rmx]
4894 set rowtextx($row) $xt
4895 set idpos($id) [list $x $xt $y]
4896 if {[info exists idtags($id)] || [info exists idheads($id)]
4897 || [info exists idotherrefs($id)]} {
4898 set xt [drawtags $id $x $xt $y]
4900 set headline [lindex $commitinfo($id) 0]
4901 set name [lindex $commitinfo($id) 1]
4902 set date [lindex $commitinfo($id) 2]
4903 set date [formatdate $date]
4906 set isbold [ishighlighted $id]
4908 lappend boldrows $row
4909 set font mainfontbold
4911 lappend boldnamerows $row
4912 set nfont mainfontbold
4915 set linehtag($row) [$canv create text $xt $y -anchor w -fill $fgcolor \
4916 -text $headline -font $font -tags text]
4917 $canv bind $linehtag($row) <Button-3> "rowmenu %X %Y $id"
4918 set linentag($row) [$canv2 create text 3 $y -anchor w -fill $fgcolor \
4919 -text $name -font $nfont -tags text]
4920 set linedtag($row) [$canv3 create text 3 $y -anchor w -fill $fgcolor \
4921 -text $date -font mainfont -tags text]
4922 if {$selectedline == $row} {
4925 set xr [expr {$xt + [font measure $font $headline]}]
4926 if {$xr > $canvxmax} {
4932 proc drawcmitrow {row} {
4933 global displayorder rowidlist nrows_drawn
4934 global iddrawn markingmatches
4935 global commitinfo numcommits
4936 global filehighlight fhighlights findpattern nhighlights
4937 global hlview vhighlights
4938 global highlight_related rhighlights
4940 if {$row >= $numcommits} return
4942 set id [lindex $displayorder $row]
4943 if {[info exists hlview] && ![info exists vhighlights($id)]} {
4944 askvhighlight $row $id
4946 if {[info exists filehighlight] && ![info exists fhighlights($id)]} {
4947 askfilehighlight $row $id
4949 if {$findpattern ne {} && ![info exists nhighlights($id)]} {
4950 askfindhighlight $row $id
4952 if {$highlight_related ne [mc "None"] && ![info exists rhighlights($id)]} {
4953 askrelhighlight $row $id
4955 if {![info exists iddrawn($id)]} {
4956 set col [lsearch -exact [lindex $rowidlist $row] $id]
4958 puts "oops, row $row id $id not in list"
4961 if {![info exists commitinfo($id)]} {
4965 drawcmittext $id $row $col
4969 if {$markingmatches} {
4970 markrowmatches $row $id
4974 proc drawcommits {row {endrow {}}} {
4975 global numcommits iddrawn displayorder curview need_redisplay
4976 global parentlist rowidlist rowfinal uparrowlen downarrowlen nrows_drawn
4981 if {$endrow eq {}} {
4984 if {$endrow >= $numcommits} {
4985 set endrow [expr {$numcommits - 1}]
4988 set rl1 [expr {$row - $downarrowlen - 3}]
4992 set ro1 [expr {$row - 3}]
4996 set r2 [expr {$endrow + $uparrowlen + 3}]
4997 if {$r2 > $numcommits} {
5000 for {set r $rl1} {$r < $r2} {incr r} {
5001 if {[lindex $rowidlist $r] ne {} && [lindex $rowfinal $r]} {
5005 set rl1 [expr {$r + 1}]
5011 optimize_rows $ro1 0 $r2
5012 if {$need_redisplay || $nrows_drawn > 2000} {
5017 # make the lines join to already-drawn rows either side
5018 set r [expr {$row - 1}]
5019 if {$r < 0 || ![info exists iddrawn([lindex $displayorder $r])]} {
5022 set er [expr {$endrow + 1}]
5023 if {$er >= $numcommits ||
5024 ![info exists iddrawn([lindex $displayorder $er])]} {
5027 for {} {$r <= $er} {incr r} {
5028 set id [lindex $displayorder $r]
5029 set wasdrawn [info exists iddrawn($id)]
5031 if {$r == $er} break
5032 set nextid [lindex $displayorder [expr {$r + 1}]]
5033 if {$wasdrawn && [info exists iddrawn($nextid)]} continue
5034 drawparentlinks $id $r
5036 set rowids [lindex $rowidlist $r]
5037 foreach lid $rowids {
5038 if {$lid eq {}} continue
5039 if {[info exists lineend($lid)] && $lineend($lid) > $r} continue
5041 # see if this is the first child of any of its parents
5042 foreach p [lindex $parentlist $r] {
5043 if {[lsearch -exact $rowids $p] < 0} {
5044 # make this line extend up to the child
5045 set lineend($p) [drawlineseg $p $r $er 0]
5049 set lineend($lid) [drawlineseg $lid $r $er 1]
5055 proc undolayout {row} {
5056 global uparrowlen mingaplen downarrowlen
5057 global rowidlist rowisopt rowfinal need_redisplay
5059 set r [expr {$row - ($uparrowlen + $mingaplen + $downarrowlen)}]
5063 if {[llength $rowidlist] > $r} {
5065 set rowidlist [lrange $rowidlist 0 $r]
5066 set rowfinal [lrange $rowfinal 0 $r]
5067 set rowisopt [lrange $rowisopt 0 $r]
5068 set need_redisplay 1
5073 proc drawvisible {} {
5074 global canv linespc curview vrowmod selectedline targetrow targetid
5075 global need_redisplay cscroll numcommits
5077 set fs [$canv yview]
5078 set ymax [lindex [$canv cget -scrollregion] 3]
5079 if {$ymax eq {} || $ymax == 0 || $numcommits == 0} return
5080 set f0 [lindex $fs 0]
5081 set f1 [lindex $fs 1]
5082 set y0 [expr {int($f0 * $ymax)}]
5083 set y1 [expr {int($f1 * $ymax)}]
5085 if {[info exists targetid]} {
5086 if {[commitinview $targetid $curview]} {
5087 set r [rowofcommit $targetid]
5088 if {$r != $targetrow} {
5089 # Fix up the scrollregion and change the scrolling position
5090 # now that our target row has moved.
5091 set diff [expr {($r - $targetrow) * $linespc}]
5094 set ymax [lindex [$canv cget -scrollregion] 3]
5097 set f0 [expr {$y0 / $ymax}]
5098 set f1 [expr {$y1 / $ymax}]
5099 allcanvs yview moveto $f0
5100 $cscroll set $f0 $f1
5101 set need_redisplay 1
5108 set row [expr {int(($y0 - 3) / $linespc) - 1}]
5109 set endrow [expr {int(($y1 - 3) / $linespc) + 1}]
5110 if {$endrow >= $vrowmod($curview)} {
5111 update_arcrows $curview
5113 if {$selectedline ne {} &&
5114 $row <= $selectedline && $selectedline <= $endrow} {
5115 set targetrow $selectedline
5116 } elseif {[info exists targetid]} {
5117 set targetrow [expr {int(($row + $endrow) / 2)}]
5119 if {[info exists targetrow]} {
5120 if {$targetrow >= $numcommits} {
5121 set targetrow [expr {$numcommits - 1}]
5123 set targetid [commitonrow $targetrow]
5125 drawcommits $row $endrow
5128 proc clear_display {} {
5129 global iddrawn linesegs need_redisplay nrows_drawn
5130 global vhighlights fhighlights nhighlights rhighlights
5131 global linehtag linentag linedtag boldrows boldnamerows
5134 catch {unset iddrawn}
5135 catch {unset linesegs}
5136 catch {unset linehtag}
5137 catch {unset linentag}
5138 catch {unset linedtag}
5141 catch {unset vhighlights}
5142 catch {unset fhighlights}
5143 catch {unset nhighlights}
5144 catch {unset rhighlights}
5145 set need_redisplay 0
5149 proc findcrossings {id} {
5150 global rowidlist parentlist numcommits displayorder
5154 foreach {s e} [rowranges $id] {
5155 if {$e >= $numcommits} {
5156 set e [expr {$numcommits - 1}]
5158 if {$e <= $s} continue
5159 for {set row $e} {[incr row -1] >= $s} {} {
5160 set x [lsearch -exact [lindex $rowidlist $row] $id]
5162 set olds [lindex $parentlist $row]
5163 set kid [lindex $displayorder $row]
5164 set kidx [lsearch -exact [lindex $rowidlist $row] $kid]
5165 if {$kidx < 0} continue
5166 set nextrow [lindex $rowidlist [expr {$row + 1}]]
5168 set px [lsearch -exact $nextrow $p]
5169 if {$px < 0} continue
5170 if {($kidx < $x && $x < $px) || ($px < $x && $x < $kidx)} {
5171 if {[lsearch -exact $ccross $p] >= 0} continue
5172 if {$x == $px + ($kidx < $px? -1: 1)} {
5174 } elseif {[lsearch -exact $cross $p] < 0} {
5181 return [concat $ccross {{}} $cross]
5184 proc assigncolor {id} {
5185 global colormap colors nextcolor
5186 global parents children children curview
5188 if {[info exists colormap($id)]} return
5189 set ncolors [llength $colors]
5190 if {[info exists children($curview,$id)]} {
5191 set kids $children($curview,$id)
5195 if {[llength $kids] == 1} {
5196 set child [lindex $kids 0]
5197 if {[info exists colormap($child)]
5198 && [llength $parents($curview,$child)] == 1} {
5199 set colormap($id) $colormap($child)
5205 foreach x [findcrossings $id] {
5207 # delimiter between corner crossings and other crossings
5208 if {[llength $badcolors] >= $ncolors - 1} break
5209 set origbad $badcolors
5211 if {[info exists colormap($x)]
5212 && [lsearch -exact $badcolors $colormap($x)] < 0} {
5213 lappend badcolors $colormap($x)
5216 if {[llength $badcolors] >= $ncolors} {
5217 set badcolors $origbad
5219 set origbad $badcolors
5220 if {[llength $badcolors] < $ncolors - 1} {
5221 foreach child $kids {
5222 if {[info exists colormap($child)]
5223 && [lsearch -exact $badcolors $colormap($child)] < 0} {
5224 lappend badcolors $colormap($child)
5226 foreach p $parents($curview,$child) {
5227 if {[info exists colormap($p)]
5228 && [lsearch -exact $badcolors $colormap($p)] < 0} {
5229 lappend badcolors $colormap($p)
5233 if {[llength $badcolors] >= $ncolors} {
5234 set badcolors $origbad
5237 for {set i 0} {$i <= $ncolors} {incr i} {
5238 set c [lindex $colors $nextcolor]
5239 if {[incr nextcolor] >= $ncolors} {
5242 if {[lsearch -exact $badcolors $c]} break
5244 set colormap($id) $c
5247 proc bindline {t id} {
5250 $canv bind $t <Enter> "lineenter %x %y $id"
5251 $canv bind $t <Motion> "linemotion %x %y $id"
5252 $canv bind $t <Leave> "lineleave $id"
5253 $canv bind $t <Button-1> "lineclick %x %y $id 1"
5256 proc drawtags {id x xt y1} {
5257 global idtags idheads idotherrefs mainhead
5258 global linespc lthickness
5259 global canv rowtextx curview fgcolor bgcolor
5264 if {[info exists idtags($id)]} {
5265 set marks $idtags($id)
5266 set ntags [llength $marks]
5268 if {[info exists idheads($id)]} {
5269 set marks [concat $marks $idheads($id)]
5270 set nheads [llength $idheads($id)]
5272 if {[info exists idotherrefs($id)]} {
5273 set marks [concat $marks $idotherrefs($id)]
5279 set delta [expr {int(0.5 * ($linespc - $lthickness))}]
5280 set yt [expr {$y1 - 0.5 * $linespc}]
5281 set yb [expr {$yt + $linespc - 1}]
5285 foreach tag $marks {
5287 if {$i >= $ntags && $i < $ntags + $nheads && $tag eq $mainhead} {
5288 set wid [font measure mainfontbold $tag]
5290 set wid [font measure mainfont $tag]
5294 set xt [expr {$xt + $delta + $wid + $lthickness + $linespc}]
5296 set t [$canv create line $x $y1 [lindex $xvals end] $y1 \
5297 -width $lthickness -fill black -tags tag.$id]
5299 foreach tag $marks x $xvals wid $wvals {
5300 set xl [expr {$x + $delta}]
5301 set xr [expr {$x + $delta + $wid + $lthickness}]
5303 if {[incr ntags -1] >= 0} {
5305 set t [$canv create polygon $x [expr {$yt + $delta}] $xl $yt \
5306 $xr $yt $xr $yb $xl $yb $x [expr {$yb - $delta}] \
5307 -width 1 -outline black -fill yellow -tags tag.$id]
5308 $canv bind $t <1> [list showtag $tag 1]
5309 set rowtextx([rowofcommit $id]) [expr {$xr + $linespc}]
5311 # draw a head or other ref
5312 if {[incr nheads -1] >= 0} {
5314 if {$tag eq $mainhead} {
5315 set font mainfontbold
5320 set xl [expr {$xl - $delta/2}]
5321 $canv create polygon $x $yt $xr $yt $xr $yb $x $yb \
5322 -width 1 -outline black -fill $col -tags tag.$id
5323 if {[regexp {^(remotes/.*/|remotes/)} $tag match remoteprefix]} {
5324 set rwid [font measure mainfont $remoteprefix]
5325 set xi [expr {$x + 1}]
5326 set yti [expr {$yt + 1}]
5327 set xri [expr {$x + $rwid}]
5328 $canv create polygon $xi $yti $xri $yti $xri $yb $xi $yb \
5329 -width 0 -fill "#ffddaa" -tags tag.$id
5332 set t [$canv create text $xl $y1 -anchor w -text $tag -fill $fgcolor \
5333 -font $font -tags [list tag.$id text]]
5335 $canv bind $t <1> [list showtag $tag 1]
5336 } elseif {$nheads >= 0} {
5337 $canv bind $t <Button-3> [list headmenu %X %Y $id $tag]
5343 proc xcoord {i level ln} {
5344 global canvx0 xspc1 xspc2
5346 set x [expr {$canvx0 + $i * $xspc1($ln)}]
5347 if {$i > 0 && $i == $level} {
5348 set x [expr {$x + 0.5 * ($xspc2 - $xspc1($ln))}]
5349 } elseif {$i > $level} {
5350 set x [expr {$x + $xspc2 - $xspc1($ln)}]
5355 proc show_status {msg} {
5359 $canv create text 3 3 -anchor nw -text $msg -font mainfont \
5360 -tags text -fill $fgcolor
5363 # Don't change the text pane cursor if it is currently the hand cursor,
5364 # showing that we are over a sha1 ID link.
5365 proc settextcursor {c} {
5366 global ctext curtextcursor
5368 if {[$ctext cget -cursor] == $curtextcursor} {
5369 $ctext config -cursor $c
5371 set curtextcursor $c
5374 proc nowbusy {what {name {}}} {
5375 global isbusy busyname statusw
5377 if {[array names isbusy] eq {}} {
5378 . config -cursor watch
5382 set busyname($what) $name
5384 $statusw conf -text $name
5388 proc notbusy {what} {
5389 global isbusy maincursor textcursor busyname statusw
5393 if {$busyname($what) ne {} &&
5394 [$statusw cget -text] eq $busyname($what)} {
5395 $statusw conf -text {}
5398 if {[array names isbusy] eq {}} {
5399 . config -cursor $maincursor
5400 settextcursor $textcursor
5404 proc findmatches {f} {
5405 global findtype findstring
5406 if {$findtype == [mc "Regexp"]} {
5407 set matches [regexp -indices -all -inline $findstring $f]
5410 if {$findtype == [mc "IgnCase"]} {
5411 set f [string tolower $f]
5412 set fs [string tolower $fs]
5416 set l [string length $fs]
5417 while {[set j [string first $fs $f $i]] >= 0} {
5418 lappend matches [list $j [expr {$j+$l-1}]]
5419 set i [expr {$j + $l}]
5425 proc dofind {{dirn 1} {wrap 1}} {
5426 global findstring findstartline findcurline selectedline numcommits
5427 global gdttype filehighlight fh_serial find_dirn findallowwrap
5429 if {[info exists find_dirn]} {
5430 if {$find_dirn == $dirn} return
5434 if {$findstring eq {} || $numcommits == 0} return
5435 if {$selectedline eq {}} {
5436 set findstartline [lindex [visiblerows] [expr {$dirn < 0}]]
5438 set findstartline $selectedline
5440 set findcurline $findstartline
5441 nowbusy finding [mc "Searching"]
5442 if {$gdttype ne [mc "containing:"] && ![info exists filehighlight]} {
5443 after cancel do_file_hl $fh_serial
5444 do_file_hl $fh_serial
5447 set findallowwrap $wrap
5451 proc stopfinding {} {
5452 global find_dirn findcurline fprogcoord
5454 if {[info exists find_dirn]} {
5464 global commitdata commitinfo numcommits findpattern findloc
5465 global findstartline findcurline findallowwrap
5466 global find_dirn gdttype fhighlights fprogcoord
5467 global curview varcorder vrownum varccommits vrowmod
5469 if {![info exists find_dirn]} {
5472 set fldtypes [list [mc "Headline"] [mc "Author"] [mc "Date"] [mc "Committer"] [mc "CDate"] [mc "Comments"]]
5475 if {$find_dirn > 0} {
5477 if {$l >= $numcommits} {
5480 if {$l <= $findstartline} {
5481 set lim [expr {$findstartline + 1}]
5484 set moretodo $findallowwrap
5491 if {$l >= $findstartline} {
5492 set lim [expr {$findstartline - 1}]
5495 set moretodo $findallowwrap
5498 set n [expr {($lim - $l) * $find_dirn}]
5503 if {$l + ($find_dirn > 0? $n: 1) > $vrowmod($curview)} {
5504 update_arcrows $curview
5508 set ai [bsearch $vrownum($curview) $l]
5509 set a [lindex $varcorder($curview) $ai]
5510 set arow [lindex $vrownum($curview) $ai]
5511 set ids [lindex $varccommits($curview,$a)]
5512 set arowend [expr {$arow + [llength $ids]}]
5513 if {$gdttype eq [mc "containing:"]} {
5514 for {} {$n > 0} {incr n -1; incr l $find_dirn} {
5515 if {$l < $arow || $l >= $arowend} {
5517 set a [lindex $varcorder($curview) $ai]
5518 set arow [lindex $vrownum($curview) $ai]
5519 set ids [lindex $varccommits($curview,$a)]
5520 set arowend [expr {$arow + [llength $ids]}]
5522 set id [lindex $ids [expr {$l - $arow}]]
5523 # shouldn't happen unless git log doesn't give all the commits...
5524 if {![info exists commitdata($id)] ||
5525 ![doesmatch $commitdata($id)]} {
5528 if {![info exists commitinfo($id)]} {
5531 set info $commitinfo($id)
5532 foreach f $info ty $fldtypes {
5533 if {($findloc eq [mc "All fields"] || $findloc eq $ty) &&
5542 for {} {$n > 0} {incr n -1; incr l $find_dirn} {
5543 if {$l < $arow || $l >= $arowend} {
5545 set a [lindex $varcorder($curview) $ai]
5546 set arow [lindex $vrownum($curview) $ai]
5547 set ids [lindex $varccommits($curview,$a)]
5548 set arowend [expr {$arow + [llength $ids]}]
5550 set id [lindex $ids [expr {$l - $arow}]]
5551 if {![info exists fhighlights($id)]} {
5552 # this sets fhighlights($id) to -1
5553 askfilehighlight $l $id
5555 if {$fhighlights($id) > 0} {
5559 if {$fhighlights($id) < 0} {
5562 set findcurline [expr {$l - $find_dirn}]
5567 if {$found || ($domore && !$moretodo)} {
5583 set findcurline [expr {$l - $find_dirn}]
5585 set n [expr {($findcurline - $findstartline) * $find_dirn - 1}]
5589 set fprogcoord [expr {$n * 1.0 / $numcommits}]
5594 proc findselectline {l} {
5595 global findloc commentend ctext findcurline markingmatches gdttype
5597 set markingmatches 1
5600 if {$findloc == [mc "All fields"] || $findloc == [mc "Comments"]} {
5601 # highlight the matches in the comments
5602 set f [$ctext get 1.0 $commentend]
5603 set matches [findmatches $f]
5604 foreach match $matches {
5605 set start [lindex $match 0]
5606 set end [expr {[lindex $match 1] + 1}]
5607 $ctext tag add found "1.0 + $start c" "1.0 + $end c"
5613 # mark the bits of a headline or author that match a find string
5614 proc markmatches {canv l str tag matches font row} {
5617 set bbox [$canv bbox $tag]
5618 set x0 [lindex $bbox 0]
5619 set y0 [lindex $bbox 1]
5620 set y1 [lindex $bbox 3]
5621 foreach match $matches {
5622 set start [lindex $match 0]
5623 set end [lindex $match 1]
5624 if {$start > $end} continue
5625 set xoff [font measure $font [string range $str 0 [expr {$start-1}]]]
5626 set xlen [font measure $font [string range $str 0 [expr {$end}]]]
5627 set t [$canv create rect [expr {$x0+$xoff}] $y0 \
5628 [expr {$x0+$xlen+2}] $y1 \
5629 -outline {} -tags [list match$l matches] -fill yellow]
5631 if {$row == $selectedline} {
5632 $canv raise $t secsel
5637 proc unmarkmatches {} {
5638 global markingmatches
5640 allcanvs delete matches
5641 set markingmatches 0
5645 proc selcanvline {w x y} {
5646 global canv canvy0 ctext linespc
5648 set ymax [lindex [$canv cget -scrollregion] 3]
5649 if {$ymax == {}} return
5650 set yfrac [lindex [$canv yview] 0]
5651 set y [expr {$y + $yfrac * $ymax}]
5652 set l [expr {int(($y - $canvy0) / $linespc + 0.5)}]
5657 set xmax [lindex [$canv cget -scrollregion] 2]
5658 set xleft [expr {[lindex [$canv xview] 0] * $xmax}]
5659 if {![info exists rowtextx($l)] || $xleft + $x < $rowtextx($l)} return
5665 proc commit_descriptor {p} {
5667 if {![info exists commitinfo($p)]} {
5671 if {[llength $commitinfo($p)] > 1} {
5672 set l [lindex $commitinfo($p) 0]
5677 # append some text to the ctext widget, and make any SHA1 ID
5678 # that we know about be a clickable link.
5679 proc appendwithlinks {text tags} {
5680 global ctext linknum curview pendinglinks
5682 set start [$ctext index "end - 1c"]
5683 $ctext insert end $text $tags
5684 set links [regexp -indices -all -inline {[0-9a-f]{40}} $text]
5688 set linkid [string range $text $s $e]
5690 $ctext tag delete link$linknum
5691 $ctext tag add link$linknum "$start + $s c" "$start + $e c"
5692 setlink $linkid link$linknum
5697 proc setlink {id lk} {
5698 global curview ctext pendinglinks commitinterest
5700 if {[commitinview $id $curview]} {
5701 $ctext tag conf $lk -foreground blue -underline 1
5702 $ctext tag bind $lk <1> [list selectline [rowofcommit $id] 1]
5703 $ctext tag bind $lk <Enter> {linkcursor %W 1}
5704 $ctext tag bind $lk <Leave> {linkcursor %W -1}
5706 lappend pendinglinks($id) $lk
5707 lappend commitinterest($id) {makelink %I}
5711 proc makelink {id} {
5714 if {![info exists pendinglinks($id)]} return
5715 foreach lk $pendinglinks($id) {
5718 unset pendinglinks($id)
5721 proc linkcursor {w inc} {
5722 global linkentercount curtextcursor
5724 if {[incr linkentercount $inc] > 0} {
5725 $w configure -cursor hand2
5727 $w configure -cursor $curtextcursor
5728 if {$linkentercount < 0} {
5729 set linkentercount 0
5734 proc viewnextline {dir} {
5738 set ymax [lindex [$canv cget -scrollregion] 3]
5739 set wnow [$canv yview]
5740 set wtop [expr {[lindex $wnow 0] * $ymax}]
5741 set newtop [expr {$wtop + $dir * $linespc}]
5744 } elseif {$newtop > $ymax} {
5747 allcanvs yview moveto [expr {$newtop * 1.0 / $ymax}]
5750 # add a list of tag or branch names at position pos
5751 # returns the number of names inserted
5752 proc appendrefs {pos ids var} {
5753 global ctext linknum curview $var maxrefs
5755 if {[catch {$ctext index $pos}]} {
5758 $ctext conf -state normal
5759 $ctext delete $pos "$pos lineend"
5762 foreach tag [set $var\($id\)] {
5763 lappend tags [list $tag $id]
5766 if {[llength $tags] > $maxrefs} {
5767 $ctext insert $pos "many ([llength $tags])"
5769 set tags [lsort -index 0 -decreasing $tags]
5772 set id [lindex $ti 1]
5775 $ctext tag delete $lk
5776 $ctext insert $pos $sep
5777 $ctext insert $pos [lindex $ti 0] $lk
5782 $ctext conf -state disabled
5783 return [llength $tags]
5786 # called when we have finished computing the nearby tags
5787 proc dispneartags {delay} {
5788 global selectedline currentid showneartags tagphase
5790 if {$selectedline eq {} || !$showneartags} return
5791 after cancel dispnexttag
5793 after 200 dispnexttag
5796 after idle dispnexttag
5801 proc dispnexttag {} {
5802 global selectedline currentid showneartags tagphase ctext
5804 if {$selectedline eq {} || !$showneartags} return
5805 switch -- $tagphase {
5807 set dtags [desctags $currentid]
5809 appendrefs precedes $dtags idtags
5813 set atags [anctags $currentid]
5815 appendrefs follows $atags idtags
5819 set dheads [descheads $currentid]
5820 if {$dheads ne {}} {
5821 if {[appendrefs branch $dheads idheads] > 1
5822 && [$ctext get "branch -3c"] eq "h"} {
5823 # turn "Branch" into "Branches"
5824 $ctext conf -state normal
5825 $ctext insert "branch -2c" "es"
5826 $ctext conf -state disabled
5831 if {[incr tagphase] <= 2} {
5832 after idle dispnexttag
5836 proc make_secsel {l} {
5837 global linehtag linentag linedtag canv canv2 canv3
5839 if {![info exists linehtag($l)]} return
5841 set t [eval $canv create rect [$canv bbox $linehtag($l)] -outline {{}} \
5842 -tags secsel -fill [$canv cget -selectbackground]]
5844 $canv2 delete secsel
5845 set t [eval $canv2 create rect [$canv2 bbox $linentag($l)] -outline {{}} \
5846 -tags secsel -fill [$canv2 cget -selectbackground]]
5848 $canv3 delete secsel
5849 set t [eval $canv3 create rect [$canv3 bbox $linedtag($l)] -outline {{}} \
5850 -tags secsel -fill [$canv3 cget -selectbackground]]
5854 proc selectline {l isnew} {
5855 global canv ctext commitinfo selectedline
5856 global canvy0 linespc parents children curview
5857 global currentid sha1entry
5858 global commentend idtags linknum
5859 global mergemax numcommits pending_select
5860 global cmitmode showneartags allcommits
5861 global targetrow targetid lastscrollrows
5864 catch {unset pending_select}
5869 if {$l < 0 || $l >= $numcommits} return
5870 set id [commitonrow $l]
5875 if {$lastscrollrows < $numcommits} {
5879 set y [expr {$canvy0 + $l * $linespc}]
5880 set ymax [lindex [$canv cget -scrollregion] 3]
5881 set ytop [expr {$y - $linespc - 1}]
5882 set ybot [expr {$y + $linespc + 1}]
5883 set wnow [$canv yview]
5884 set wtop [expr {[lindex $wnow 0] * $ymax}]
5885 set wbot [expr {[lindex $wnow 1] * $ymax}]
5886 set wh [expr {$wbot - $wtop}]
5888 if {$ytop < $wtop} {
5889 if {$ybot < $wtop} {
5890 set newtop [expr {$y - $wh / 2.0}]
5893 if {$newtop > $wtop - $linespc} {
5894 set newtop [expr {$wtop - $linespc}]
5897 } elseif {$ybot > $wbot} {
5898 if {$ytop > $wbot} {
5899 set newtop [expr {$y - $wh / 2.0}]
5901 set newtop [expr {$ybot - $wh}]
5902 if {$newtop < $wtop + $linespc} {
5903 set newtop [expr {$wtop + $linespc}]
5907 if {$newtop != $wtop} {
5911 allcanvs yview moveto [expr {$newtop * 1.0 / $ymax}]
5918 addtohistory [list selbyid $id]
5921 $sha1entry delete 0 end
5922 $sha1entry insert 0 $id
5924 $sha1entry selection from 0
5925 $sha1entry selection to end
5929 $ctext conf -state normal
5932 if {![info exists commitinfo($id)]} {
5935 set info $commitinfo($id)
5936 set date [formatdate [lindex $info 2]]
5937 $ctext insert end "[mc "Author"]: [lindex $info 1] $date\n"
5938 set date [formatdate [lindex $info 4]]
5939 $ctext insert end "[mc "Committer"]: [lindex $info 3] $date\n"
5940 if {[info exists idtags($id)]} {
5941 $ctext insert end [mc "Tags:"]
5942 foreach tag $idtags($id) {
5943 $ctext insert end " $tag"
5945 $ctext insert end "\n"
5949 set olds $parents($curview,$id)
5950 if {[llength $olds] > 1} {
5953 if {$np >= $mergemax} {
5958 $ctext insert end "[mc "Parent"]: " $tag
5959 appendwithlinks [commit_descriptor $p] {}
5964 append headers "[mc "Parent"]: [commit_descriptor $p]"
5968 foreach c $children($curview,$id) {
5969 append headers "[mc "Child"]: [commit_descriptor $c]"
5972 # make anything that looks like a SHA1 ID be a clickable link
5973 appendwithlinks $headers {}
5974 if {$showneartags} {
5975 if {![info exists allcommits]} {
5978 $ctext insert end "[mc "Branch"]: "
5979 $ctext mark set branch "end -1c"
5980 $ctext mark gravity branch left
5981 $ctext insert end "\n[mc "Follows"]: "
5982 $ctext mark set follows "end -1c"
5983 $ctext mark gravity follows left
5984 $ctext insert end "\n[mc "Precedes"]: "
5985 $ctext mark set precedes "end -1c"
5986 $ctext mark gravity precedes left
5987 $ctext insert end "\n"
5990 $ctext insert end "\n"
5991 set comment [lindex $info 5]
5992 if {[string first "\r" $comment] >= 0} {
5993 set comment [string map {"\r" "\n "} $comment]
5995 appendwithlinks $comment {comment}
5997 $ctext tag remove found 1.0 end
5998 $ctext conf -state disabled
5999 set commentend [$ctext index "end - 1c"]
6001 init_flist [mc "Comments"]
6002 if {$cmitmode eq "tree"} {
6004 } elseif {[llength $olds] <= 1} {
6011 proc selfirstline {} {
6016 proc sellastline {} {
6019 set l [expr {$numcommits - 1}]
6023 proc selnextline {dir} {
6026 if {$selectedline eq {}} return
6027 set l [expr {$selectedline + $dir}]
6032 proc selnextpage {dir} {
6033 global canv linespc selectedline numcommits
6035 set lpp [expr {([winfo height $canv] - 2) / $linespc}]
6039 allcanvs yview scroll [expr {$dir * $lpp}] units
6041 if {$selectedline eq {}} return
6042 set l [expr {$selectedline + $dir * $lpp}]
6045 } elseif {$l >= $numcommits} {
6046 set l [expr $numcommits - 1]
6052 proc unselectline {} {
6053 global selectedline currentid
6056 catch {unset currentid}
6057 allcanvs delete secsel
6061 proc reselectline {} {
6064 if {$selectedline ne {}} {
6065 selectline $selectedline 0
6069 proc addtohistory {cmd} {
6070 global history historyindex curview
6072 set elt [list $curview $cmd]
6073 if {$historyindex > 0
6074 && [lindex $history [expr {$historyindex - 1}]] == $elt} {
6078 if {$historyindex < [llength $history]} {
6079 set history [lreplace $history $historyindex end $elt]
6081 lappend history $elt
6084 if {$historyindex > 1} {
6085 .tf.bar.leftbut conf -state normal
6087 .tf.bar.leftbut conf -state disabled
6089 .tf.bar.rightbut conf -state disabled
6095 set view [lindex $elt 0]
6096 set cmd [lindex $elt 1]
6097 if {$curview != $view} {
6104 global history historyindex
6107 if {$historyindex > 1} {
6108 incr historyindex -1
6109 godo [lindex $history [expr {$historyindex - 1}]]
6110 .tf.bar.rightbut conf -state normal
6112 if {$historyindex <= 1} {
6113 .tf.bar.leftbut conf -state disabled
6118 global history historyindex
6121 if {$historyindex < [llength $history]} {
6122 set cmd [lindex $history $historyindex]
6125 .tf.bar.leftbut conf -state normal
6127 if {$historyindex >= [llength $history]} {
6128 .tf.bar.rightbut conf -state disabled
6133 global treefilelist treeidlist diffids diffmergeid treepending
6134 global nullid nullid2
6137 catch {unset diffmergeid}
6138 if {![info exists treefilelist($id)]} {
6139 if {![info exists treepending]} {
6140 if {$id eq $nullid} {
6141 set cmd [list | git ls-files]
6142 } elseif {$id eq $nullid2} {
6143 set cmd [list | git ls-files --stage -t]
6145 set cmd [list | git ls-tree -r $id]
6147 if {[catch {set gtf [open $cmd r]}]} {
6151 set treefilelist($id) {}
6152 set treeidlist($id) {}
6153 fconfigure $gtf -blocking 0
6154 filerun $gtf [list gettreeline $gtf $id]
6161 proc gettreeline {gtf id} {
6162 global treefilelist treeidlist treepending cmitmode diffids nullid nullid2
6165 while {[incr nl] <= 1000 && [gets $gtf line] >= 0} {
6166 if {$diffids eq $nullid} {
6169 set i [string first "\t" $line]
6170 if {$i < 0} continue
6171 set fname [string range $line [expr {$i+1}] end]
6172 set line [string range $line 0 [expr {$i-1}]]
6173 if {$diffids ne $nullid2 && [lindex $line 1] ne "blob"} continue
6174 set sha1 [lindex $line 2]
6175 if {[string index $fname 0] eq "\""} {
6176 set fname [lindex $fname 0]
6178 lappend treeidlist($id) $sha1
6180 lappend treefilelist($id) $fname
6183 return [expr {$nl >= 1000? 2: 1}]
6187 if {$cmitmode ne "tree"} {
6188 if {![info exists diffmergeid]} {
6189 gettreediffs $diffids
6191 } elseif {$id ne $diffids} {
6200 global treefilelist treeidlist diffids nullid nullid2
6201 global ctext commentend
6203 set i [lsearch -exact $treefilelist($diffids) $f]
6205 puts "oops, $f not in list for id $diffids"
6208 if {$diffids eq $nullid} {
6209 if {[catch {set bf [open $f r]} err]} {
6210 puts "oops, can't read $f: $err"
6214 set blob [lindex $treeidlist($diffids) $i]
6215 if {[catch {set bf [open [concat | git cat-file blob $blob] r]} err]} {
6216 puts "oops, error reading blob $blob: $err"
6220 fconfigure $bf -blocking 0
6221 filerun $bf [list getblobline $bf $diffids]
6222 $ctext config -state normal
6223 clear_ctext $commentend
6224 $ctext insert end "\n"
6225 $ctext insert end "$f\n" filesep
6226 $ctext config -state disabled
6227 $ctext yview $commentend
6231 proc getblobline {bf id} {
6232 global diffids cmitmode ctext
6234 if {$id ne $diffids || $cmitmode ne "tree"} {
6238 $ctext config -state normal
6240 while {[incr nl] <= 1000 && [gets $bf line] >= 0} {
6241 $ctext insert end "$line\n"
6244 # delete last newline
6245 $ctext delete "end - 2c" "end - 1c"
6249 $ctext config -state disabled
6250 return [expr {$nl >= 1000? 2: 1}]
6253 proc mergediff {id} {
6254 global diffmergeid mdifffd
6258 global limitdiffs vfilelimit curview
6262 # this doesn't seem to actually affect anything...
6263 set cmd [concat | git diff-tree --no-commit-id --cc -U$diffcontext $id]
6264 if {$limitdiffs && $vfilelimit($curview) ne {}} {
6265 set cmd [concat $cmd -- $vfilelimit($curview)]
6267 if {[catch {set mdf [open $cmd r]} err]} {
6268 error_popup "[mc "Error getting merge diffs:"] $err"
6271 fconfigure $mdf -blocking 0
6272 set mdifffd($id) $mdf
6273 set np [llength $parents($curview,$id)]
6275 filerun $mdf [list getmergediffline $mdf $id $np]
6278 proc getmergediffline {mdf id np} {
6279 global diffmergeid ctext cflist mergemax
6280 global difffilestart mdifffd
6282 $ctext conf -state normal
6284 while {[incr nr] <= 1000 && [gets $mdf line] >= 0} {
6285 if {![info exists diffmergeid] || $id != $diffmergeid
6286 || $mdf != $mdifffd($id)} {
6290 if {[regexp {^diff --cc (.*)} $line match fname]} {
6291 # start of a new file
6292 $ctext insert end "\n"
6293 set here [$ctext index "end - 1c"]
6294 lappend difffilestart $here
6295 add_flist [list $fname]
6296 set l [expr {(78 - [string length $fname]) / 2}]
6297 set pad [string range "----------------------------------------" 1 $l]
6298 $ctext insert end "$pad $fname $pad\n" filesep
6299 } elseif {[regexp {^@@} $line]} {
6300 $ctext insert end "$line\n" hunksep
6301 } elseif {[regexp {^[0-9a-f]{40}$} $line] || [regexp {^index} $line]} {
6304 # parse the prefix - one ' ', '-' or '+' for each parent
6309 for {set j 0} {$j < $np} {incr j} {
6310 set c [string range $line $j $j]
6313 } elseif {$c == "-"} {
6315 } elseif {$c == "+"} {
6324 if {!$isbad && $minuses ne {} && $pluses eq {}} {
6325 # line doesn't appear in result, parents in $minuses have the line
6326 set num [lindex $minuses 0]
6327 } elseif {!$isbad && $pluses ne {} && $minuses eq {}} {
6328 # line appears in result, parents in $pluses don't have the line
6329 lappend tags mresult
6330 set num [lindex $spaces 0]
6333 if {$num >= $mergemax} {
6338 $ctext insert end "$line\n" $tags
6341 $ctext conf -state disabled
6346 return [expr {$nr >= 1000? 2: 1}]
6349 proc startdiff {ids} {
6350 global treediffs diffids treepending diffmergeid nullid nullid2
6354 catch {unset diffmergeid}
6355 if {![info exists treediffs($ids)] ||
6356 [lsearch -exact $ids $nullid] >= 0 ||
6357 [lsearch -exact $ids $nullid2] >= 0} {
6358 if {![info exists treepending]} {
6366 proc path_filter {filter name} {
6368 set l [string length $p]
6369 if {[string index $p end] eq "/"} {
6370 if {[string compare -length $l $p $name] == 0} {
6374 if {[string compare -length $l $p $name] == 0 &&
6375 ([string length $name] == $l ||
6376 [string index $name $l] eq "/")} {
6384 proc addtocflist {ids} {
6387 add_flist $treediffs($ids)
6391 proc diffcmd {ids flags} {
6392 global nullid nullid2
6394 set i [lsearch -exact $ids $nullid]
6395 set j [lsearch -exact $ids $nullid2]
6397 if {[llength $ids] > 1 && $j < 0} {
6398 # comparing working directory with some specific revision
6399 set cmd [concat | git diff-index $flags]
6401 lappend cmd -R [lindex $ids 1]
6403 lappend cmd [lindex $ids 0]
6406 # comparing working directory with index
6407 set cmd [concat | git diff-files $flags]
6412 } elseif {$j >= 0} {
6413 set cmd [concat | git diff-index --cached $flags]
6414 if {[llength $ids] > 1} {
6415 # comparing index with specific revision
6417 lappend cmd -R [lindex $ids 1]
6419 lappend cmd [lindex $ids 0]
6422 # comparing index with HEAD
6426 set cmd [concat | git diff-tree -r $flags $ids]
6431 proc gettreediffs {ids} {
6432 global treediff treepending
6434 set treepending $ids
6436 if {[catch {set gdtf [open [diffcmd $ids {--no-commit-id}] r]}]} return
6437 fconfigure $gdtf -blocking 0
6438 filerun $gdtf [list gettreediffline $gdtf $ids]
6441 proc gettreediffline {gdtf ids} {
6442 global treediff treediffs treepending diffids diffmergeid
6443 global cmitmode vfilelimit curview limitdiffs
6446 while {[incr nr] <= 1000 && [gets $gdtf line] >= 0} {
6447 set i [string first "\t" $line]
6449 set file [string range $line [expr {$i+1}] end]
6450 if {[string index $file 0] eq "\""} {
6451 set file [lindex $file 0]
6453 lappend treediff $file
6457 return [expr {$nr >= 1000? 2: 1}]
6460 if {$limitdiffs && $vfilelimit($curview) ne {}} {
6462 foreach f $treediff {
6463 if {[path_filter $vfilelimit($curview) $f]} {
6467 set treediffs($ids) $flist
6469 set treediffs($ids) $treediff
6472 if {$cmitmode eq "tree"} {
6474 } elseif {$ids != $diffids} {
6475 if {![info exists diffmergeid]} {
6476 gettreediffs $diffids
6484 # empty string or positive integer
6485 proc diffcontextvalidate {v} {
6486 return [regexp {^(|[1-9][0-9]*)$} $v]
6489 proc diffcontextchange {n1 n2 op} {
6490 global diffcontextstring diffcontext
6492 if {[string is integer -strict $diffcontextstring]} {
6493 if {$diffcontextstring > 0} {
6494 set diffcontext $diffcontextstring
6500 proc changeignorespace {} {
6504 proc getblobdiffs {ids} {
6505 global blobdifffd diffids env
6506 global diffinhdr treediffs
6509 global limitdiffs vfilelimit curview
6511 set cmd [diffcmd $ids "-p -C --no-commit-id -U$diffcontext"]
6515 if {$limitdiffs && $vfilelimit($curview) ne {}} {
6516 set cmd [concat $cmd -- $vfilelimit($curview)]
6518 if {[catch {set bdf [open $cmd r]} err]} {
6519 puts "error getting diffs: $err"
6523 fconfigure $bdf -blocking 0
6524 set blobdifffd($ids) $bdf
6525 filerun $bdf [list getblobdiffline $bdf $diffids]
6528 proc setinlist {var i val} {
6531 while {[llength [set $var]] < $i} {
6534 if {[llength [set $var]] == $i} {
6541 proc makediffhdr {fname ids} {
6542 global ctext curdiffstart treediffs
6544 set i [lsearch -exact $treediffs($ids) $fname]
6546 setinlist difffilestart $i $curdiffstart
6548 set l [expr {(78 - [string length $fname]) / 2}]
6549 set pad [string range "----------------------------------------" 1 $l]
6550 $ctext insert $curdiffstart "$pad $fname $pad" filesep
6553 proc getblobdiffline {bdf ids} {
6554 global diffids blobdifffd ctext curdiffstart
6555 global diffnexthead diffnextnote difffilestart
6556 global diffinhdr treediffs
6559 $ctext conf -state normal
6560 while {[incr nr] <= 1000 && [gets $bdf line] >= 0} {
6561 if {$ids != $diffids || $bdf != $blobdifffd($ids)} {
6565 if {![string compare -length 11 "diff --git " $line]} {
6566 # trim off "diff --git "
6567 set line [string range $line 11 end]
6569 # start of a new file
6570 $ctext insert end "\n"
6571 set curdiffstart [$ctext index "end - 1c"]
6572 $ctext insert end "\n" filesep
6573 # If the name hasn't changed the length will be odd,
6574 # the middle char will be a space, and the two bits either
6575 # side will be a/name and b/name, or "a/name" and "b/name".
6576 # If the name has changed we'll get "rename from" and
6577 # "rename to" or "copy from" and "copy to" lines following this,
6578 # and we'll use them to get the filenames.
6579 # This complexity is necessary because spaces in the filename(s)
6580 # don't get escaped.
6581 set l [string length $line]
6582 set i [expr {$l / 2}]
6583 if {!(($l & 1) && [string index $line $i] eq " " &&
6584 [string range $line 2 [expr {$i - 1}]] eq \
6585 [string range $line [expr {$i + 3}] end])} {
6588 # unescape if quoted and chop off the a/ from the front
6589 if {[string index $line 0] eq "\""} {
6590 set fname [string range [lindex $line 0] 2 end]
6592 set fname [string range $line 2 [expr {$i - 1}]]
6594 makediffhdr $fname $ids
6596 } elseif {[regexp {^@@ -([0-9]+)(,[0-9]+)? \+([0-9]+)(,[0-9]+)? @@(.*)} \
6597 $line match f1l f1c f2l f2c rest]} {
6598 $ctext insert end "$line\n" hunksep
6601 } elseif {$diffinhdr} {
6602 if {![string compare -length 12 "rename from " $line]} {
6603 set fname [string range $line [expr 6 + [string first " from " $line] ] end]
6604 if {[string index $fname 0] eq "\""} {
6605 set fname [lindex $fname 0]
6607 set i [lsearch -exact $treediffs($ids) $fname]
6609 setinlist difffilestart $i $curdiffstart
6611 } elseif {![string compare -length 10 $line "rename to "] ||
6612 ![string compare -length 8 $line "copy to "]} {
6613 set fname [string range $line [expr 4 + [string first " to " $line] ] end]
6614 if {[string index $fname 0] eq "\""} {
6615 set fname [lindex $fname 0]
6617 makediffhdr $fname $ids
6618 } elseif {[string compare -length 3 $line "---"] == 0} {
6621 } elseif {[string compare -length 3 $line "+++"] == 0} {
6625 $ctext insert end "$line\n" filesep
6628 set x [string range $line 0 0]
6629 if {$x == "-" || $x == "+"} {
6630 set tag [expr {$x == "+"}]
6631 $ctext insert end "$line\n" d$tag
6632 } elseif {$x == " "} {
6633 $ctext insert end "$line\n"
6635 # "\ No newline at end of file",
6636 # or something else we don't recognize
6637 $ctext insert end "$line\n" hunksep
6641 $ctext conf -state disabled
6646 return [expr {$nr >= 1000? 2: 1}]
6649 proc changediffdisp {} {
6650 global ctext diffelide
6652 $ctext tag conf d0 -elide [lindex $diffelide 0]
6653 $ctext tag conf d1 -elide [lindex $diffelide 1]
6656 proc highlightfile {loc cline} {
6657 global ctext cflist cflist_top
6660 $cflist tag remove highlight $cflist_top.0 "$cflist_top.0 lineend"
6661 $cflist tag add highlight $cline.0 "$cline.0 lineend"
6662 $cflist see $cline.0
6663 set cflist_top $cline
6667 global difffilestart ctext cmitmode
6669 if {$cmitmode eq "tree"} return
6672 set here [$ctext index @0,0]
6673 foreach loc $difffilestart {
6674 if {[$ctext compare $loc >= $here]} {
6675 highlightfile $prev $prevline
6681 highlightfile $prev $prevline
6685 global difffilestart ctext cmitmode
6687 if {$cmitmode eq "tree"} return
6688 set here [$ctext index @0,0]
6690 foreach loc $difffilestart {
6692 if {[$ctext compare $loc > $here]} {
6693 highlightfile $loc $line
6699 proc clear_ctext {{first 1.0}} {
6700 global ctext smarktop smarkbot
6703 set l [lindex [split $first .] 0]
6704 if {![info exists smarktop] || [$ctext compare $first < $smarktop.0]} {
6707 if {![info exists smarkbot] || [$ctext compare $first < $smarkbot.0]} {
6710 $ctext delete $first end
6711 if {$first eq "1.0"} {
6712 catch {unset pendinglinks}
6716 proc settabs {{firstab {}}} {
6717 global firsttabstop tabstop ctext have_tk85
6719 if {$firstab ne {} && $have_tk85} {
6720 set firsttabstop $firstab
6722 set w [font measure textfont "0"]
6723 if {$firsttabstop != 0} {
6724 $ctext conf -tabs [list [expr {($firsttabstop + $tabstop) * $w}] \
6725 [expr {($firsttabstop + 2 * $tabstop) * $w}]]
6726 } elseif {$have_tk85 || $tabstop != 8} {
6727 $ctext conf -tabs [expr {$tabstop * $w}]
6729 $ctext conf -tabs {}
6733 proc incrsearch {name ix op} {
6734 global ctext searchstring searchdirn
6736 $ctext tag remove found 1.0 end
6737 if {[catch {$ctext index anchor}]} {
6738 # no anchor set, use start of selection, or of visible area
6739 set sel [$ctext tag ranges sel]
6741 $ctext mark set anchor [lindex $sel 0]
6742 } elseif {$searchdirn eq "-forwards"} {
6743 $ctext mark set anchor @0,0
6745 $ctext mark set anchor @0,[winfo height $ctext]
6748 if {$searchstring ne {}} {
6749 set here [$ctext search $searchdirn -- $searchstring anchor]
6758 global sstring ctext searchstring searchdirn
6761 $sstring icursor end
6762 set searchdirn -forwards
6763 if {$searchstring ne {}} {
6764 set sel [$ctext tag ranges sel]
6766 set start "[lindex $sel 0] + 1c"
6767 } elseif {[catch {set start [$ctext index anchor]}]} {
6770 set match [$ctext search -count mlen -- $searchstring $start]
6771 $ctext tag remove sel 1.0 end
6777 set mend "$match + $mlen c"
6778 $ctext tag add sel $match $mend
6779 $ctext mark unset anchor
6783 proc dosearchback {} {
6784 global sstring ctext searchstring searchdirn
6787 $sstring icursor end
6788 set searchdirn -backwards
6789 if {$searchstring ne {}} {
6790 set sel [$ctext tag ranges sel]
6792 set start [lindex $sel 0]
6793 } elseif {[catch {set start [$ctext index anchor]}]} {
6794 set start @0,[winfo height $ctext]
6796 set match [$ctext search -backwards -count ml -- $searchstring $start]
6797 $ctext tag remove sel 1.0 end
6803 set mend "$match + $ml c"
6804 $ctext tag add sel $match $mend
6805 $ctext mark unset anchor
6809 proc searchmark {first last} {
6810 global ctext searchstring
6814 set match [$ctext search -count mlen -- $searchstring $mend $last.end]
6815 if {$match eq {}} break
6816 set mend "$match + $mlen c"
6817 $ctext tag add found $match $mend
6821 proc searchmarkvisible {doall} {
6822 global ctext smarktop smarkbot
6824 set topline [lindex [split [$ctext index @0,0] .] 0]
6825 set botline [lindex [split [$ctext index @0,[winfo height $ctext]] .] 0]
6826 if {$doall || $botline < $smarktop || $topline > $smarkbot} {
6827 # no overlap with previous
6828 searchmark $topline $botline
6829 set smarktop $topline
6830 set smarkbot $botline
6832 if {$topline < $smarktop} {
6833 searchmark $topline [expr {$smarktop-1}]
6834 set smarktop $topline
6836 if {$botline > $smarkbot} {
6837 searchmark [expr {$smarkbot+1}] $botline
6838 set smarkbot $botline
6843 proc scrolltext {f0 f1} {
6846 .bleft.bottom.sb set $f0 $f1
6847 if {$searchstring ne {}} {
6853 global linespc charspc canvx0 canvy0
6854 global xspc1 xspc2 lthickness
6856 set linespc [font metrics mainfont -linespace]
6857 set charspc [font measure mainfont "m"]
6858 set canvy0 [expr {int(3 + 0.5 * $linespc)}]
6859 set canvx0 [expr {int(3 + 0.5 * $linespc)}]
6860 set lthickness [expr {int($linespc / 9) + 1}]
6861 set xspc1(0) $linespc
6869 set ymax [lindex [$canv cget -scrollregion] 3]
6870 if {$ymax eq {} || $ymax == 0} return
6871 set span [$canv yview]
6874 allcanvs yview moveto [lindex $span 0]
6876 if {$selectedline ne {}} {
6877 selectline $selectedline 0
6878 allcanvs yview moveto [lindex $span 0]
6882 proc parsefont {f n} {
6885 set fontattr($f,family) [lindex $n 0]
6887 if {$s eq {} || $s == 0} {
6890 set s [expr {int(-$s / [winfo fpixels . 1p] + 0.5)}]
6892 set fontattr($f,size) $s
6893 set fontattr($f,weight) normal
6894 set fontattr($f,slant) roman
6895 foreach style [lrange $n 2 end] {
6898 "bold" {set fontattr($f,weight) $style}
6900 "italic" {set fontattr($f,slant) $style}
6905 proc fontflags {f {isbold 0}} {
6908 return [list -family $fontattr($f,family) -size $fontattr($f,size) \
6909 -weight [expr {$isbold? "bold": $fontattr($f,weight)}] \
6910 -slant $fontattr($f,slant)]
6916 set n [list $fontattr($f,family) $fontattr($f,size)]
6917 if {$fontattr($f,weight) eq "bold"} {
6920 if {$fontattr($f,slant) eq "italic"} {
6926 proc incrfont {inc} {
6927 global mainfont textfont ctext canv cflist showrefstop
6928 global stopped entries fontattr
6931 set s $fontattr(mainfont,size)
6936 set fontattr(mainfont,size) $s
6937 font config mainfont -size $s
6938 font config mainfontbold -size $s
6939 set mainfont [fontname mainfont]
6940 set s $fontattr(textfont,size)
6945 set fontattr(textfont,size) $s
6946 font config textfont -size $s
6947 font config textfontbold -size $s
6948 set textfont [fontname textfont]
6955 global sha1entry sha1string
6956 if {[string length $sha1string] == 40} {
6957 $sha1entry delete 0 end
6961 proc sha1change {n1 n2 op} {
6962 global sha1string currentid sha1but
6963 if {$sha1string == {}
6964 || ([info exists currentid] && $sha1string == $currentid)} {
6969 if {[$sha1but cget -state] == $state} return
6970 if {$state == "normal"} {
6971 $sha1but conf -state normal -relief raised -text "[mc "Goto:"] "
6973 $sha1but conf -state disabled -relief flat -text "[mc "SHA1 ID:"] "
6977 proc gotocommit {} {
6978 global sha1string tagids headids curview varcid
6980 if {$sha1string == {}
6981 || ([info exists currentid] && $sha1string == $currentid)} return
6982 if {[info exists tagids($sha1string)]} {
6983 set id $tagids($sha1string)
6984 } elseif {[info exists headids($sha1string)]} {
6985 set id $headids($sha1string)
6987 set id [string tolower $sha1string]
6988 if {[regexp {^[0-9a-f]{4,39}$} $id]} {
6989 set matches [array names varcid "$curview,$id*"]
6990 if {$matches ne {}} {
6991 if {[llength $matches] > 1} {
6992 error_popup [mc "Short SHA1 id %s is ambiguous" $id]
6995 set id [lindex [split [lindex $matches 0] ","] 1]
6999 if {[commitinview $id $curview]} {
7000 selectline [rowofcommit $id] 1
7003 if {[regexp {^[0-9a-fA-F]{4,}$} $sha1string]} {
7004 set msg [mc "SHA1 id %s is not known" $sha1string]
7006 set msg [mc "Tag/Head %s is not known" $sha1string]
7011 proc lineenter {x y id} {
7012 global hoverx hovery hoverid hovertimer
7013 global commitinfo canv
7015 if {![info exists commitinfo($id)] && ![getcommit $id]} return
7019 if {[info exists hovertimer]} {
7020 after cancel $hovertimer
7022 set hovertimer [after 500 linehover]
7026 proc linemotion {x y id} {
7027 global hoverx hovery hoverid hovertimer
7029 if {[info exists hoverid] && $id == $hoverid} {
7032 if {[info exists hovertimer]} {
7033 after cancel $hovertimer
7035 set hovertimer [after 500 linehover]
7039 proc lineleave {id} {
7040 global hoverid hovertimer canv
7042 if {[info exists hoverid] && $id == $hoverid} {
7044 if {[info exists hovertimer]} {
7045 after cancel $hovertimer
7053 global hoverx hovery hoverid hovertimer
7054 global canv linespc lthickness
7057 set text [lindex $commitinfo($hoverid) 0]
7058 set ymax [lindex [$canv cget -scrollregion] 3]
7059 if {$ymax == {}} return
7060 set yfrac [lindex [$canv yview] 0]
7061 set x [expr {$hoverx + 2 * $linespc}]
7062 set y [expr {$hovery + $yfrac * $ymax - $linespc / 2}]
7063 set x0 [expr {$x - 2 * $lthickness}]
7064 set y0 [expr {$y - 2 * $lthickness}]
7065 set x1 [expr {$x + [font measure mainfont $text] + 2 * $lthickness}]
7066 set y1 [expr {$y + $linespc + 2 * $lthickness}]
7067 set t [$canv create rectangle $x0 $y0 $x1 $y1 \
7068 -fill \#ffff80 -outline black -width 1 -tags hover]
7070 set t [$canv create text $x $y -anchor nw -text $text -tags hover \
7075 proc clickisonarrow {id y} {
7078 set ranges [rowranges $id]
7079 set thresh [expr {2 * $lthickness + 6}]
7080 set n [expr {[llength $ranges] - 1}]
7081 for {set i 1} {$i < $n} {incr i} {
7082 set row [lindex $ranges $i]
7083 if {abs([yc $row] - $y) < $thresh} {
7090 proc arrowjump {id n y} {
7093 # 1 <-> 2, 3 <-> 4, etc...
7094 set n [expr {(($n - 1) ^ 1) + 1}]
7095 set row [lindex [rowranges $id] $n]
7097 set ymax [lindex [$canv cget -scrollregion] 3]
7098 if {$ymax eq {} || $ymax <= 0} return
7099 set view [$canv yview]
7100 set yspan [expr {[lindex $view 1] - [lindex $view 0]}]
7101 set yfrac [expr {$yt / $ymax - $yspan / 2}]
7105 allcanvs yview moveto $yfrac
7108 proc lineclick {x y id isnew} {
7109 global ctext commitinfo children canv thickerline curview
7111 if {![info exists commitinfo($id)] && ![getcommit $id]} return
7116 # draw this line thicker than normal
7120 set ymax [lindex [$canv cget -scrollregion] 3]
7121 if {$ymax eq {}} return
7122 set yfrac [lindex [$canv yview] 0]
7123 set y [expr {$y + $yfrac * $ymax}]
7125 set dirn [clickisonarrow $id $y]
7127 arrowjump $id $dirn $y
7132 addtohistory [list lineclick $x $y $id 0]
7134 # fill the details pane with info about this line
7135 $ctext conf -state normal
7138 $ctext insert end "[mc "Parent"]:\t"
7139 $ctext insert end $id link0
7141 set info $commitinfo($id)
7142 $ctext insert end "\n\t[lindex $info 0]\n"
7143 $ctext insert end "\t[mc "Author"]:\t[lindex $info 1]\n"
7144 set date [formatdate [lindex $info 2]]
7145 $ctext insert end "\t[mc "Date"]:\t$date\n"
7146 set kids $children($curview,$id)
7148 $ctext insert end "\n[mc "Children"]:"
7150 foreach child $kids {
7152 if {![info exists commitinfo($child)] && ![getcommit $child]} continue
7153 set info $commitinfo($child)
7154 $ctext insert end "\n\t"
7155 $ctext insert end $child link$i
7156 setlink $child link$i
7157 $ctext insert end "\n\t[lindex $info 0]"
7158 $ctext insert end "\n\t[mc "Author"]:\t[lindex $info 1]"
7159 set date [formatdate [lindex $info 2]]
7160 $ctext insert end "\n\t[mc "Date"]:\t$date\n"
7163 $ctext conf -state disabled
7167 proc normalline {} {
7169 if {[info exists thickerline]} {
7178 if {[commitinview $id $curview]} {
7179 selectline [rowofcommit $id] 1
7185 if {![info exists startmstime]} {
7186 set startmstime [clock clicks -milliseconds]
7188 return [format "%.3f" [expr {([clock click -milliseconds] - $startmstime) / 1000.0}]]
7191 proc rowmenu {x y id} {
7192 global rowctxmenu selectedline rowmenuid curview
7193 global nullid nullid2 fakerowmenu mainhead
7197 if {$selectedline eq {} || [rowofcommit $id] eq $selectedline} {
7202 if {$id ne $nullid && $id ne $nullid2} {
7203 set menu $rowctxmenu
7204 if {$mainhead ne {}} {
7205 $menu entryconfigure 7 -label [mc "Reset %s branch to here" $mainhead]
7207 $menu entryconfigure 7 -label [mc "Detached head: can't reset" $mainhead] -state disabled
7210 set menu $fakerowmenu
7212 $menu entryconfigure [mc "Diff this -> selected"] -state $state
7213 $menu entryconfigure [mc "Diff selected -> this"] -state $state
7214 $menu entryconfigure [mc "Make patch"] -state $state
7215 tk_popup $menu $x $y
7218 proc diffvssel {dirn} {
7219 global rowmenuid selectedline
7221 if {$selectedline eq {}} return
7223 set oldid [commitonrow $selectedline]
7224 set newid $rowmenuid
7226 set oldid $rowmenuid
7227 set newid [commitonrow $selectedline]
7229 addtohistory [list doseldiff $oldid $newid]
7230 doseldiff $oldid $newid
7233 proc doseldiff {oldid newid} {
7237 $ctext conf -state normal
7239 init_flist [mc "Top"]
7240 $ctext insert end "[mc "From"] "
7241 $ctext insert end $oldid link0
7242 setlink $oldid link0
7243 $ctext insert end "\n "
7244 $ctext insert end [lindex $commitinfo($oldid) 0]
7245 $ctext insert end "\n\n[mc "To"] "
7246 $ctext insert end $newid link1
7247 setlink $newid link1
7248 $ctext insert end "\n "
7249 $ctext insert end [lindex $commitinfo($newid) 0]
7250 $ctext insert end "\n"
7251 $ctext conf -state disabled
7252 $ctext tag remove found 1.0 end
7253 startdiff [list $oldid $newid]
7257 global rowmenuid currentid commitinfo patchtop patchnum
7259 if {![info exists currentid]} return
7260 set oldid $currentid
7261 set oldhead [lindex $commitinfo($oldid) 0]
7262 set newid $rowmenuid
7263 set newhead [lindex $commitinfo($newid) 0]
7266 catch {destroy $top}
7268 label $top.title -text [mc "Generate patch"]
7269 grid $top.title - -pady 10
7270 label $top.from -text [mc "From:"]
7271 entry $top.fromsha1 -width 40 -relief flat
7272 $top.fromsha1 insert 0 $oldid
7273 $top.fromsha1 conf -state readonly
7274 grid $top.from $top.fromsha1 -sticky w
7275 entry $top.fromhead -width 60 -relief flat
7276 $top.fromhead insert 0 $oldhead
7277 $top.fromhead conf -state readonly
7278 grid x $top.fromhead -sticky w
7279 label $top.to -text [mc "To:"]
7280 entry $top.tosha1 -width 40 -relief flat
7281 $top.tosha1 insert 0 $newid
7282 $top.tosha1 conf -state readonly
7283 grid $top.to $top.tosha1 -sticky w
7284 entry $top.tohead -width 60 -relief flat
7285 $top.tohead insert 0 $newhead
7286 $top.tohead conf -state readonly
7287 grid x $top.tohead -sticky w
7288 button $top.rev -text [mc "Reverse"] -command mkpatchrev -padx 5
7289 grid $top.rev x -pady 10
7290 label $top.flab -text [mc "Output file:"]
7291 entry $top.fname -width 60
7292 $top.fname insert 0 [file normalize "patch$patchnum.patch"]
7294 grid $top.flab $top.fname -sticky w
7296 button $top.buts.gen -text [mc "Generate"] -command mkpatchgo
7297 button $top.buts.can -text [mc "Cancel"] -command mkpatchcan
7298 grid $top.buts.gen $top.buts.can
7299 grid columnconfigure $top.buts 0 -weight 1 -uniform a
7300 grid columnconfigure $top.buts 1 -weight 1 -uniform a
7301 grid $top.buts - -pady 10 -sticky ew
7305 proc mkpatchrev {} {
7308 set oldid [$patchtop.fromsha1 get]
7309 set oldhead [$patchtop.fromhead get]
7310 set newid [$patchtop.tosha1 get]
7311 set newhead [$patchtop.tohead get]
7312 foreach e [list fromsha1 fromhead tosha1 tohead] \
7313 v [list $newid $newhead $oldid $oldhead] {
7314 $patchtop.$e conf -state normal
7315 $patchtop.$e delete 0 end
7316 $patchtop.$e insert 0 $v
7317 $patchtop.$e conf -state readonly
7322 global patchtop nullid nullid2
7324 set oldid [$patchtop.fromsha1 get]
7325 set newid [$patchtop.tosha1 get]
7326 set fname [$patchtop.fname get]
7327 set cmd [diffcmd [list $oldid $newid] -p]
7328 # trim off the initial "|"
7329 set cmd [lrange $cmd 1 end]
7330 lappend cmd >$fname &
7331 if {[catch {eval exec $cmd} err]} {
7332 error_popup "[mc "Error creating patch:"] $err"
7334 catch {destroy $patchtop}
7338 proc mkpatchcan {} {
7341 catch {destroy $patchtop}
7346 global rowmenuid mktagtop commitinfo
7350 catch {destroy $top}
7352 label $top.title -text [mc "Create tag"]
7353 grid $top.title - -pady 10
7354 label $top.id -text [mc "ID:"]
7355 entry $top.sha1 -width 40 -relief flat
7356 $top.sha1 insert 0 $rowmenuid
7357 $top.sha1 conf -state readonly
7358 grid $top.id $top.sha1 -sticky w
7359 entry $top.head -width 60 -relief flat
7360 $top.head insert 0 [lindex $commitinfo($rowmenuid) 0]
7361 $top.head conf -state readonly
7362 grid x $top.head -sticky w
7363 label $top.tlab -text [mc "Tag name:"]
7364 entry $top.tag -width 60
7365 grid $top.tlab $top.tag -sticky w
7367 button $top.buts.gen -text [mc "Create"] -command mktaggo
7368 button $top.buts.can -text [mc "Cancel"] -command mktagcan
7369 grid $top.buts.gen $top.buts.can
7370 grid columnconfigure $top.buts 0 -weight 1 -uniform a
7371 grid columnconfigure $top.buts 1 -weight 1 -uniform a
7372 grid $top.buts - -pady 10 -sticky ew
7377 global mktagtop env tagids idtags
7379 set id [$mktagtop.sha1 get]
7380 set tag [$mktagtop.tag get]
7382 error_popup [mc "No tag name specified"]
7385 if {[info exists tagids($tag)]} {
7386 error_popup [mc "Tag \"%s\" already exists" $tag]
7390 exec git tag $tag $id
7392 error_popup "[mc "Error creating tag:"] $err"
7396 set tagids($tag) $id
7397 lappend idtags($id) $tag
7404 proc redrawtags {id} {
7405 global canv linehtag idpos currentid curview cmitlisted
7406 global canvxmax iddrawn circleitem mainheadid circlecolors
7408 if {![commitinview $id $curview]} return
7409 if {![info exists iddrawn($id)]} return
7410 set row [rowofcommit $id]
7411 if {$id eq $mainheadid} {
7414 set ofill [lindex $circlecolors $cmitlisted($curview,$id)]
7416 $canv itemconf $circleitem($row) -fill $ofill
7417 $canv delete tag.$id
7418 set xt [eval drawtags $id $idpos($id)]
7419 $canv coords $linehtag($row) $xt [lindex $idpos($id) 2]
7420 set text [$canv itemcget $linehtag($row) -text]
7421 set font [$canv itemcget $linehtag($row) -font]
7422 set xr [expr {$xt + [font measure $font $text]}]
7423 if {$xr > $canvxmax} {
7427 if {[info exists currentid] && $currentid == $id} {
7435 catch {destroy $mktagtop}
7444 proc writecommit {} {
7445 global rowmenuid wrcomtop commitinfo wrcomcmd
7447 set top .writecommit
7449 catch {destroy $top}
7451 label $top.title -text [mc "Write commit to file"]
7452 grid $top.title - -pady 10
7453 label $top.id -text [mc "ID:"]
7454 entry $top.sha1 -width 40 -relief flat
7455 $top.sha1 insert 0 $rowmenuid
7456 $top.sha1 conf -state readonly
7457 grid $top.id $top.sha1 -sticky w
7458 entry $top.head -width 60 -relief flat
7459 $top.head insert 0 [lindex $commitinfo($rowmenuid) 0]
7460 $top.head conf -state readonly
7461 grid x $top.head -sticky w
7462 label $top.clab -text [mc "Command:"]
7463 entry $top.cmd -width 60 -textvariable wrcomcmd
7464 grid $top.clab $top.cmd -sticky w -pady 10
7465 label $top.flab -text [mc "Output file:"]
7466 entry $top.fname -width 60
7467 $top.fname insert 0 [file normalize "commit-[string range $rowmenuid 0 6]"]
7468 grid $top.flab $top.fname -sticky w
7470 button $top.buts.gen -text [mc "Write"] -command wrcomgo
7471 button $top.buts.can -text [mc "Cancel"] -command wrcomcan
7472 grid $top.buts.gen $top.buts.can
7473 grid columnconfigure $top.buts 0 -weight 1 -uniform a
7474 grid columnconfigure $top.buts 1 -weight 1 -uniform a
7475 grid $top.buts - -pady 10 -sticky ew
7482 set id [$wrcomtop.sha1 get]
7483 set cmd "echo $id | [$wrcomtop.cmd get]"
7484 set fname [$wrcomtop.fname get]
7485 if {[catch {exec sh -c $cmd >$fname &} err]} {
7486 error_popup "[mc "Error writing commit:"] $err"
7488 catch {destroy $wrcomtop}
7495 catch {destroy $wrcomtop}
7500 global rowmenuid mkbrtop
7503 catch {destroy $top}
7505 label $top.title -text [mc "Create new branch"]
7506 grid $top.title - -pady 10
7507 label $top.id -text [mc "ID:"]
7508 entry $top.sha1 -width 40 -relief flat
7509 $top.sha1 insert 0 $rowmenuid
7510 $top.sha1 conf -state readonly
7511 grid $top.id $top.sha1 -sticky w
7512 label $top.nlab -text [mc "Name:"]
7513 entry $top.name -width 40
7514 grid $top.nlab $top.name -sticky w
7516 button $top.buts.go -text [mc "Create"] -command [list mkbrgo $top]
7517 button $top.buts.can -text [mc "Cancel"] -command "catch {destroy $top}"
7518 grid $top.buts.go $top.buts.can
7519 grid columnconfigure $top.buts 0 -weight 1 -uniform a
7520 grid columnconfigure $top.buts 1 -weight 1 -uniform a
7521 grid $top.buts - -pady 10 -sticky ew
7526 global headids idheads
7528 set name [$top.name get]
7529 set id [$top.sha1 get]
7531 error_popup [mc "Please specify a name for the new branch"]
7534 catch {destroy $top}
7538 exec git branch $name $id
7543 set headids($name) $id
7544 lappend idheads($id) $name
7553 proc cherrypick {} {
7554 global rowmenuid curview
7555 global mainhead mainheadid
7557 set oldhead [exec git rev-parse HEAD]
7558 set dheads [descheads $rowmenuid]
7559 if {$dheads ne {} && [lsearch -exact $dheads $oldhead] >= 0} {
7560 set ok [confirm_popup [mc "Commit %s is already\
7561 included in branch %s -- really re-apply it?" \
7562 [string range $rowmenuid 0 7] $mainhead]]
7565 nowbusy cherrypick [mc "Cherry-picking"]
7567 # Unfortunately git-cherry-pick writes stuff to stderr even when
7568 # no error occurs, and exec takes that as an indication of error...
7569 if {[catch {exec sh -c "git cherry-pick -r $rowmenuid 2>&1"} err]} {
7574 set newhead [exec git rev-parse HEAD]
7575 if {$newhead eq $oldhead} {
7577 error_popup [mc "No changes committed"]
7580 addnewchild $newhead $oldhead
7581 if {[commitinview $oldhead $curview]} {
7582 insertrow $newhead $oldhead $curview
7583 if {$mainhead ne {}} {
7584 movehead $newhead $mainhead
7585 movedhead $newhead $mainhead
7587 set mainheadid $newhead
7596 global mainhead rowmenuid confirm_ok resettype
7599 set w ".confirmreset"
7602 wm title $w [mc "Confirm reset"]
7603 message $w.m -text \
7604 [mc "Reset branch %s to %s?" $mainhead [string range $rowmenuid 0 7]] \
7605 -justify center -aspect 1000
7606 pack $w.m -side top -fill x -padx 20 -pady 20
7607 frame $w.f -relief sunken -border 2
7608 message $w.f.rt -text [mc "Reset type:"] -aspect 1000
7609 grid $w.f.rt -sticky w
7611 radiobutton $w.f.soft -value soft -variable resettype -justify left \
7612 -text [mc "Soft: Leave working tree and index untouched"]
7613 grid $w.f.soft -sticky w
7614 radiobutton $w.f.mixed -value mixed -variable resettype -justify left \
7615 -text [mc "Mixed: Leave working tree untouched, reset index"]
7616 grid $w.f.mixed -sticky w
7617 radiobutton $w.f.hard -value hard -variable resettype -justify left \
7618 -text [mc "Hard: Reset working tree and index\n(discard ALL local changes)"]
7619 grid $w.f.hard -sticky w
7620 pack $w.f -side top -fill x
7621 button $w.ok -text [mc OK] -command "set confirm_ok 1; destroy $w"
7622 pack $w.ok -side left -fill x -padx 20 -pady 20
7623 button $w.cancel -text [mc Cancel] -command "destroy $w"
7624 pack $w.cancel -side right -fill x -padx 20 -pady 20
7625 bind $w <Visibility> "grab $w; focus $w"
7627 if {!$confirm_ok} return
7628 if {[catch {set fd [open \
7629 [list | git reset --$resettype $rowmenuid 2>@1] r]} err]} {
7633 filerun $fd [list readresetstat $fd]
7634 nowbusy reset [mc "Resetting"]
7639 proc readresetstat {fd} {
7640 global mainhead mainheadid showlocalchanges rprogcoord
7642 if {[gets $fd line] >= 0} {
7643 if {[regexp {([0-9]+)% \(([0-9]+)/([0-9]+)\)} $line match p m n]} {
7644 set rprogcoord [expr {1.0 * $m / $n}]
7652 if {[catch {close $fd} err]} {
7655 set oldhead $mainheadid
7656 set newhead [exec git rev-parse HEAD]
7657 if {$newhead ne $oldhead} {
7658 movehead $newhead $mainhead
7659 movedhead $newhead $mainhead
7660 set mainheadid $newhead
7664 if {$showlocalchanges} {
7670 # context menu for a head
7671 proc headmenu {x y id head} {
7672 global headmenuid headmenuhead headctxmenu mainhead
7676 set headmenuhead $head
7678 if {$head eq $mainhead} {
7681 $headctxmenu entryconfigure 0 -state $state
7682 $headctxmenu entryconfigure 1 -state $state
7683 tk_popup $headctxmenu $x $y
7687 global headmenuid headmenuhead headids
7688 global showlocalchanges mainheadid
7690 # check the tree is clean first??
7691 nowbusy checkout [mc "Checking out"]
7695 set fd [open [list | git checkout $headmenuhead 2>@1] r]
7699 if {$showlocalchanges} {
7703 filerun $fd [list readcheckoutstat $fd $headmenuhead $headmenuid]
7707 proc readcheckoutstat {fd newhead newheadid} {
7708 global mainhead mainheadid headids showlocalchanges progresscoords
7710 if {[gets $fd line] >= 0} {
7711 if {[regexp {([0-9]+)% \(([0-9]+)/([0-9]+)\)} $line match p m n]} {
7712 set progresscoords [list 0 [expr {1.0 * $m / $n}]]
7717 set progresscoords {0 0}
7720 if {[catch {close $fd} err]} {
7723 set oldmainid $mainheadid
7724 set mainhead $newhead
7725 set mainheadid $newheadid
7726 redrawtags $oldmainid
7727 redrawtags $newheadid
7729 if {$showlocalchanges} {
7735 global headmenuid headmenuhead mainhead
7738 set head $headmenuhead
7740 # this check shouldn't be needed any more...
7741 if {$head eq $mainhead} {
7742 error_popup [mc "Cannot delete the currently checked-out branch"]
7745 set dheads [descheads $id]
7746 if {[llength $dheads] == 1 && $idheads($dheads) eq $head} {
7747 # the stuff on this branch isn't on any other branch
7748 if {![confirm_popup [mc "The commits on branch %s aren't on any other\
7749 branch.\nReally delete branch %s?" $head $head]]} return
7753 if {[catch {exec git branch -D $head} err]} {
7758 removehead $id $head
7759 removedhead $id $head
7766 # Display a list of tags and heads
7768 global showrefstop bgcolor fgcolor selectbgcolor
7769 global bglist fglist reflistfilter reflist maincursor
7772 set showrefstop $top
7773 if {[winfo exists $top]} {
7779 wm title $top [mc "Tags and heads: %s" [file tail [pwd]]]
7780 text $top.list -background $bgcolor -foreground $fgcolor \
7781 -selectbackground $selectbgcolor -font mainfont \
7782 -xscrollcommand "$top.xsb set" -yscrollcommand "$top.ysb set" \
7783 -width 30 -height 20 -cursor $maincursor \
7784 -spacing1 1 -spacing3 1 -state disabled
7785 $top.list tag configure highlight -background $selectbgcolor
7786 lappend bglist $top.list
7787 lappend fglist $top.list
7788 scrollbar $top.ysb -command "$top.list yview" -orient vertical
7789 scrollbar $top.xsb -command "$top.list xview" -orient horizontal
7790 grid $top.list $top.ysb -sticky nsew
7791 grid $top.xsb x -sticky ew
7793 label $top.f.l -text "[mc "Filter"]: "
7794 entry $top.f.e -width 20 -textvariable reflistfilter
7795 set reflistfilter "*"
7796 trace add variable reflistfilter write reflistfilter_change
7797 pack $top.f.e -side right -fill x -expand 1
7798 pack $top.f.l -side left
7799 grid $top.f - -sticky ew -pady 2
7800 button $top.close -command [list destroy $top] -text [mc "Close"]
7802 grid columnconfigure $top 0 -weight 1
7803 grid rowconfigure $top 0 -weight 1
7804 bind $top.list <1> {break}
7805 bind $top.list <B1-Motion> {break}
7806 bind $top.list <ButtonRelease-1> {sel_reflist %W %x %y; break}
7811 proc sel_reflist {w x y} {
7812 global showrefstop reflist headids tagids otherrefids
7814 if {![winfo exists $showrefstop]} return
7815 set l [lindex [split [$w index "@$x,$y"] "."] 0]
7816 set ref [lindex $reflist [expr {$l-1}]]
7817 set n [lindex $ref 0]
7818 switch -- [lindex $ref 1] {
7819 "H" {selbyid $headids($n)}
7820 "T" {selbyid $tagids($n)}
7821 "o" {selbyid $otherrefids($n)}
7823 $showrefstop.list tag add highlight $l.0 "$l.0 lineend"
7826 proc unsel_reflist {} {
7829 if {![info exists showrefstop] || ![winfo exists $showrefstop]} return
7830 $showrefstop.list tag remove highlight 0.0 end
7833 proc reflistfilter_change {n1 n2 op} {
7834 global reflistfilter
7836 after cancel refill_reflist
7837 after 200 refill_reflist
7840 proc refill_reflist {} {
7841 global reflist reflistfilter showrefstop headids tagids otherrefids
7842 global curview commitinterest
7844 if {![info exists showrefstop] || ![winfo exists $showrefstop]} return
7846 foreach n [array names headids] {
7847 if {[string match $reflistfilter $n]} {
7848 if {[commitinview $headids($n) $curview]} {
7849 lappend refs [list $n H]
7851 set commitinterest($headids($n)) {run refill_reflist}
7855 foreach n [array names tagids] {
7856 if {[string match $reflistfilter $n]} {
7857 if {[commitinview $tagids($n) $curview]} {
7858 lappend refs [list $n T]
7860 set commitinterest($tagids($n)) {run refill_reflist}
7864 foreach n [array names otherrefids] {
7865 if {[string match $reflistfilter $n]} {
7866 if {[commitinview $otherrefids($n) $curview]} {
7867 lappend refs [list $n o]
7869 set commitinterest($otherrefids($n)) {run refill_reflist}
7873 set refs [lsort -index 0 $refs]
7874 if {$refs eq $reflist} return
7876 # Update the contents of $showrefstop.list according to the
7877 # differences between $reflist (old) and $refs (new)
7878 $showrefstop.list conf -state normal
7879 $showrefstop.list insert end "\n"
7882 while {$i < [llength $reflist] || $j < [llength $refs]} {
7883 if {$i < [llength $reflist]} {
7884 if {$j < [llength $refs]} {
7885 set cmp [string compare [lindex $reflist $i 0] \
7886 [lindex $refs $j 0]]
7888 set cmp [string compare [lindex $reflist $i 1] \
7889 [lindex $refs $j 1]]
7899 $showrefstop.list delete "[expr {$j+1}].0" "[expr {$j+2}].0"
7907 set l [expr {$j + 1}]
7908 $showrefstop.list image create $l.0 -align baseline \
7909 -image reficon-[lindex $refs $j 1] -padx 2
7910 $showrefstop.list insert $l.1 "[lindex $refs $j 0]\n"
7916 # delete last newline
7917 $showrefstop.list delete end-2c end-1c
7918 $showrefstop.list conf -state disabled
7921 # Stuff for finding nearby tags
7922 proc getallcommits {} {
7923 global allcommits nextarc seeds allccache allcwait cachedarcs allcupdate
7924 global idheads idtags idotherrefs allparents tagobjid
7926 if {![info exists allcommits]} {
7932 set allccache [file join [gitdir] "gitk.cache"]
7934 set f [open $allccache r]
7943 set cmd [list | git rev-list --parents]
7944 set allcupdate [expr {$seeds ne {}}]
7948 set refs [concat [array names idheads] [array names idtags] \
7949 [array names idotherrefs]]
7952 foreach name [array names tagobjid] {
7953 lappend tagobjs $tagobjid($name)
7955 foreach id [lsort -unique $refs] {
7956 if {![info exists allparents($id)] &&
7957 [lsearch -exact $tagobjs $id] < 0} {
7968 set fd [open [concat $cmd $ids] r]
7969 fconfigure $fd -blocking 0
7972 filerun $fd [list getallclines $fd]
7978 # Since most commits have 1 parent and 1 child, we group strings of
7979 # such commits into "arcs" joining branch/merge points (BMPs), which
7980 # are commits that either don't have 1 parent or don't have 1 child.
7982 # arcnos(id) - incoming arcs for BMP, arc we're on for other nodes
7983 # arcout(id) - outgoing arcs for BMP
7984 # arcids(a) - list of IDs on arc including end but not start
7985 # arcstart(a) - BMP ID at start of arc
7986 # arcend(a) - BMP ID at end of arc
7987 # growing(a) - arc a is still growing
7988 # arctags(a) - IDs out of arcids (excluding end) that have tags
7989 # archeads(a) - IDs out of arcids (excluding end) that have heads
7990 # The start of an arc is at the descendent end, so "incoming" means
7991 # coming from descendents, and "outgoing" means going towards ancestors.
7993 proc getallclines {fd} {
7994 global allparents allchildren idtags idheads nextarc
7995 global arcnos arcids arctags arcout arcend arcstart archeads growing
7996 global seeds allcommits cachedarcs allcupdate
7999 while {[incr nid] <= 1000 && [gets $fd line] >= 0} {
8000 set id [lindex $line 0]
8001 if {[info exists allparents($id)]} {
8006 set olds [lrange $line 1 end]
8007 set allparents($id) $olds
8008 if {![info exists allchildren($id)]} {
8009 set allchildren($id) {}
8014 if {[llength $olds] == 1 && [llength $a] == 1} {
8015 lappend arcids($a) $id
8016 if {[info exists idtags($id)]} {
8017 lappend arctags($a) $id
8019 if {[info exists idheads($id)]} {
8020 lappend archeads($a) $id
8022 if {[info exists allparents($olds)]} {
8023 # seen parent already
8024 if {![info exists arcout($olds)]} {
8027 lappend arcids($a) $olds
8028 set arcend($a) $olds
8031 lappend allchildren($olds) $id
8032 lappend arcnos($olds) $a
8036 foreach a $arcnos($id) {
8037 lappend arcids($a) $id
8044 lappend allchildren($p) $id
8045 set a [incr nextarc]
8046 set arcstart($a) $id
8053 if {[info exists allparents($p)]} {
8054 # seen it already, may need to make a new branch
8055 if {![info exists arcout($p)]} {
8058 lappend arcids($a) $p
8062 lappend arcnos($p) $a
8067 global cached_dheads cached_dtags cached_atags
8068 catch {unset cached_dheads}
8069 catch {unset cached_dtags}
8070 catch {unset cached_atags}
8073 return [expr {$nid >= 1000? 2: 1}]
8077 fconfigure $fd -blocking 1
8080 # got an error reading the list of commits
8081 # if we were updating, try rereading the whole thing again
8087 error_popup "[mc "Error reading commit topology information;\
8088 branch and preceding/following tag information\
8089 will be incomplete."]\n($err)"
8092 if {[incr allcommits -1] == 0} {
8102 proc recalcarc {a} {
8103 global arctags archeads arcids idtags idheads
8107 foreach id [lrange $arcids($a) 0 end-1] {
8108 if {[info exists idtags($id)]} {
8111 if {[info exists idheads($id)]} {
8116 set archeads($a) $ah
8120 global arcnos arcids nextarc arctags archeads idtags idheads
8121 global arcstart arcend arcout allparents growing
8124 if {[llength $a] != 1} {
8125 puts "oops splitarc called but [llength $a] arcs already"
8129 set i [lsearch -exact $arcids($a) $p]
8131 puts "oops splitarc $p not in arc $a"
8134 set na [incr nextarc]
8135 if {[info exists arcend($a)]} {
8136 set arcend($na) $arcend($a)
8138 set l [lindex $allparents([lindex $arcids($a) end]) 0]
8139 set j [lsearch -exact $arcnos($l) $a]
8140 set arcnos($l) [lreplace $arcnos($l) $j $j $na]
8142 set tail [lrange $arcids($a) [expr {$i+1}] end]
8143 set arcids($a) [lrange $arcids($a) 0 $i]
8145 set arcstart($na) $p
8147 set arcids($na) $tail
8148 if {[info exists growing($a)]} {
8154 if {[llength $arcnos($id)] == 1} {
8157 set j [lsearch -exact $arcnos($id) $a]
8158 set arcnos($id) [lreplace $arcnos($id) $j $j $na]
8162 # reconstruct tags and heads lists
8163 if {$arctags($a) ne {} || $archeads($a) ne {}} {
8168 set archeads($na) {}
8172 # Update things for a new commit added that is a child of one
8173 # existing commit. Used when cherry-picking.
8174 proc addnewchild {id p} {
8175 global allparents allchildren idtags nextarc
8176 global arcnos arcids arctags arcout arcend arcstart archeads growing
8177 global seeds allcommits
8179 if {![info exists allcommits] || ![info exists arcnos($p)]} return
8180 set allparents($id) [list $p]
8181 set allchildren($id) {}
8184 lappend allchildren($p) $id
8185 set a [incr nextarc]
8186 set arcstart($a) $id
8189 set arcids($a) [list $p]
8191 if {![info exists arcout($p)]} {
8194 lappend arcnos($p) $a
8195 set arcout($id) [list $a]
8198 # This implements a cache for the topology information.
8199 # The cache saves, for each arc, the start and end of the arc,
8200 # the ids on the arc, and the outgoing arcs from the end.
8201 proc readcache {f} {
8202 global arcnos arcids arcout arcstart arcend arctags archeads nextarc
8203 global idtags idheads allparents cachedarcs possible_seeds seeds growing
8208 if {$lim - $a > 500} {
8209 set lim [expr {$a + 500}]
8213 # finish reading the cache and setting up arctags, etc.
8215 if {$line ne "1"} {error "bad final version"}
8217 foreach id [array names idtags] {
8218 if {[info exists arcnos($id)] && [llength $arcnos($id)] == 1 &&
8219 [llength $allparents($id)] == 1} {
8220 set a [lindex $arcnos($id) 0]
8221 if {$arctags($a) eq {}} {
8226 foreach id [array names idheads] {
8227 if {[info exists arcnos($id)] && [llength $arcnos($id)] == 1 &&
8228 [llength $allparents($id)] == 1} {
8229 set a [lindex $arcnos($id) 0]
8230 if {$archeads($a) eq {}} {
8235 foreach id [lsort -unique $possible_seeds] {
8236 if {$arcnos($id) eq {}} {
8242 while {[incr a] <= $lim} {
8244 if {[llength $line] != 3} {error "bad line"}
8245 set s [lindex $line 0]
8247 lappend arcout($s) $a
8248 if {![info exists arcnos($s)]} {
8249 lappend possible_seeds $s
8252 set e [lindex $line 1]
8257 if {![info exists arcout($e)]} {
8261 set arcids($a) [lindex $line 2]
8262 foreach id $arcids($a) {
8263 lappend allparents($s) $id
8265 lappend arcnos($id) $a
8267 if {![info exists allparents($s)]} {
8268 set allparents($s) {}
8273 set nextarc [expr {$a - 1}]
8286 global nextarc cachedarcs possible_seeds
8290 if {[llength $line] != 2 || [lindex $line 0] ne "1"} {error "bad version"}
8291 # make sure it's an integer
8292 set cachedarcs [expr {int([lindex $line 1])}]
8293 if {$cachedarcs < 0} {error "bad number of arcs"}
8295 set possible_seeds {}
8303 proc dropcache {err} {
8304 global allcwait nextarc cachedarcs seeds
8306 #puts "dropping cache ($err)"
8307 foreach v {arcnos arcout arcids arcstart arcend growing \
8308 arctags archeads allparents allchildren} {
8319 proc writecache {f} {
8320 global cachearc cachedarcs allccache
8321 global arcstart arcend arcnos arcids arcout
8325 if {$lim - $a > 1000} {
8326 set lim [expr {$a + 1000}]
8329 while {[incr a] <= $lim} {
8330 if {[info exists arcend($a)]} {
8331 puts $f [list $arcstart($a) $arcend($a) $arcids($a)]
8333 puts $f [list $arcstart($a) {} $arcids($a)]
8338 catch {file delete $allccache}
8339 #puts "writing cache failed ($err)"
8342 set cachearc [expr {$a - 1}]
8343 if {$a > $cachedarcs} {
8352 global nextarc cachedarcs cachearc allccache
8354 if {$nextarc == $cachedarcs} return
8356 set cachedarcs $nextarc
8358 set f [open $allccache w]
8359 puts $f [list 1 $cachedarcs]
8364 # Returns 1 if a is an ancestor of b, -1 if b is an ancestor of a,
8365 # or 0 if neither is true.
8366 proc anc_or_desc {a b} {
8367 global arcout arcstart arcend arcnos cached_isanc
8369 if {$arcnos($a) eq $arcnos($b)} {
8370 # Both are on the same arc(s); either both are the same BMP,
8371 # or if one is not a BMP, the other is also not a BMP or is
8372 # the BMP at end of the arc (and it only has 1 incoming arc).
8373 # Or both can be BMPs with no incoming arcs.
8374 if {$a eq $b || $arcnos($a) eq {}} {
8377 # assert {[llength $arcnos($a)] == 1}
8378 set arc [lindex $arcnos($a) 0]
8379 set i [lsearch -exact $arcids($arc) $a]
8380 set j [lsearch -exact $arcids($arc) $b]
8381 if {$i < 0 || $i > $j} {
8388 if {![info exists arcout($a)]} {
8389 set arc [lindex $arcnos($a) 0]
8390 if {[info exists arcend($arc)]} {
8391 set aend $arcend($arc)
8395 set a $arcstart($arc)
8399 if {![info exists arcout($b)]} {
8400 set arc [lindex $arcnos($b) 0]
8401 if {[info exists arcend($arc)]} {
8402 set bend $arcend($arc)
8406 set b $arcstart($arc)
8416 if {[info exists cached_isanc($a,$bend)]} {
8417 if {$cached_isanc($a,$bend)} {
8421 if {[info exists cached_isanc($b,$aend)]} {
8422 if {$cached_isanc($b,$aend)} {
8425 if {[info exists cached_isanc($a,$bend)]} {
8430 set todo [list $a $b]
8433 for {set i 0} {$i < [llength $todo]} {incr i} {
8434 set x [lindex $todo $i]
8435 if {$anc($x) eq {}} {
8438 foreach arc $arcnos($x) {
8439 set xd $arcstart($arc)
8441 set cached_isanc($a,$bend) 1
8442 set cached_isanc($b,$aend) 0
8444 } elseif {$xd eq $aend} {
8445 set cached_isanc($b,$aend) 1
8446 set cached_isanc($a,$bend) 0
8449 if {![info exists anc($xd)]} {
8450 set anc($xd) $anc($x)
8452 } elseif {$anc($xd) ne $anc($x)} {
8457 set cached_isanc($a,$bend) 0
8458 set cached_isanc($b,$aend) 0
8462 # This identifies whether $desc has an ancestor that is
8463 # a growing tip of the graph and which is not an ancestor of $anc
8464 # and returns 0 if so and 1 if not.
8465 # If we subsequently discover a tag on such a growing tip, and that
8466 # turns out to be a descendent of $anc (which it could, since we
8467 # don't necessarily see children before parents), then $desc
8468 # isn't a good choice to display as a descendent tag of
8469 # $anc (since it is the descendent of another tag which is
8470 # a descendent of $anc). Similarly, $anc isn't a good choice to
8471 # display as a ancestor tag of $desc.
8473 proc is_certain {desc anc} {
8474 global arcnos arcout arcstart arcend growing problems
8477 if {[llength $arcnos($anc)] == 1} {
8478 # tags on the same arc are certain
8479 if {$arcnos($desc) eq $arcnos($anc)} {
8482 if {![info exists arcout($anc)]} {
8483 # if $anc is partway along an arc, use the start of the arc instead
8484 set a [lindex $arcnos($anc) 0]
8485 set anc $arcstart($a)
8488 if {[llength $arcnos($desc)] > 1 || [info exists arcout($desc)]} {
8491 set a [lindex $arcnos($desc) 0]
8497 set anclist [list $x]
8501 for {set i 0} {$i < [llength $anclist] && ($nnh > 0 || $ngrowanc > 0)} {incr i} {
8502 set x [lindex $anclist $i]
8507 foreach a $arcout($x) {
8508 if {[info exists growing($a)]} {
8509 if {![info exists growanc($x)] && $dl($x)} {
8515 if {[info exists dl($y)]} {
8519 if {![info exists done($y)]} {
8522 if {[info exists growanc($x)]} {
8526 for {set k 0} {$k < [llength $xl]} {incr k} {
8527 set z [lindex $xl $k]
8528 foreach c $arcout($z) {
8529 if {[info exists arcend($c)]} {
8531 if {[info exists dl($v)] && $dl($v)} {
8533 if {![info exists done($v)]} {
8536 if {[info exists growanc($v)]} {
8546 } elseif {$y eq $anc || !$dl($x)} {
8557 foreach x [array names growanc] {
8566 proc validate_arctags {a} {
8567 global arctags idtags
8571 foreach id $arctags($a) {
8573 if {![info exists idtags($id)]} {
8574 set na [lreplace $na $i $i]
8581 proc validate_archeads {a} {
8582 global archeads idheads
8585 set na $archeads($a)
8586 foreach id $archeads($a) {
8588 if {![info exists idheads($id)]} {
8589 set na [lreplace $na $i $i]
8593 set archeads($a) $na
8596 # Return the list of IDs that have tags that are descendents of id,
8597 # ignoring IDs that are descendents of IDs already reported.
8598 proc desctags {id} {
8599 global arcnos arcstart arcids arctags idtags allparents
8600 global growing cached_dtags
8602 if {![info exists allparents($id)]} {
8605 set t1 [clock clicks -milliseconds]
8607 if {[llength $arcnos($id)] == 1 && [llength $allparents($id)] == 1} {
8608 # part-way along an arc; check that arc first
8609 set a [lindex $arcnos($id) 0]
8610 if {$arctags($a) ne {}} {
8612 set i [lsearch -exact $arcids($a) $id]
8614 foreach t $arctags($a) {
8615 set j [lsearch -exact $arcids($a) $t]
8623 set id $arcstart($a)
8624 if {[info exists idtags($id)]} {
8628 if {[info exists cached_dtags($id)]} {
8629 return $cached_dtags($id)
8636 for {set i 0} {$i < [llength $todo] && $nc > 0} {incr i} {
8637 set id [lindex $todo $i]
8639 set ta [info exists hastaggedancestor($id)]
8643 # ignore tags on starting node
8644 if {!$ta && $i > 0} {
8645 if {[info exists idtags($id)]} {
8648 } elseif {[info exists cached_dtags($id)]} {
8649 set tagloc($id) $cached_dtags($id)
8653 foreach a $arcnos($id) {
8655 if {!$ta && $arctags($a) ne {}} {
8657 if {$arctags($a) ne {}} {
8658 lappend tagloc($id) [lindex $arctags($a) end]
8661 if {$ta || $arctags($a) ne {}} {
8662 set tomark [list $d]
8663 for {set j 0} {$j < [llength $tomark]} {incr j} {
8664 set dd [lindex $tomark $j]
8665 if {![info exists hastaggedancestor($dd)]} {
8666 if {[info exists done($dd)]} {
8667 foreach b $arcnos($dd) {
8668 lappend tomark $arcstart($b)
8670 if {[info exists tagloc($dd)]} {
8673 } elseif {[info exists queued($dd)]} {
8676 set hastaggedancestor($dd) 1
8680 if {![info exists queued($d)]} {
8683 if {![info exists hastaggedancestor($d)]} {
8690 foreach id [array names tagloc] {
8691 if {![info exists hastaggedancestor($id)]} {
8692 foreach t $tagloc($id) {
8693 if {[lsearch -exact $tags $t] < 0} {
8699 set t2 [clock clicks -milliseconds]
8702 # remove tags that are descendents of other tags
8703 for {set i 0} {$i < [llength $tags]} {incr i} {
8704 set a [lindex $tags $i]
8705 for {set j 0} {$j < $i} {incr j} {
8706 set b [lindex $tags $j]
8707 set r [anc_or_desc $a $b]
8709 set tags [lreplace $tags $j $j]
8712 } elseif {$r == -1} {
8713 set tags [lreplace $tags $i $i]
8720 if {[array names growing] ne {}} {
8721 # graph isn't finished, need to check if any tag could get
8722 # eclipsed by another tag coming later. Simply ignore any
8723 # tags that could later get eclipsed.
8726 if {[is_certain $t $origid]} {
8730 if {$tags eq $ctags} {
8731 set cached_dtags($origid) $tags
8736 set cached_dtags($origid) $tags
8738 set t3 [clock clicks -milliseconds]
8739 if {0 && $t3 - $t1 >= 100} {
8740 puts "iterating descendents ($loopix/[llength $todo] nodes) took\
8741 [expr {$t2-$t1}]+[expr {$t3-$t2}]ms, $nc candidates left"
8747 global arcnos arcids arcout arcend arctags idtags allparents
8748 global growing cached_atags
8750 if {![info exists allparents($id)]} {
8753 set t1 [clock clicks -milliseconds]
8755 if {[llength $arcnos($id)] == 1 && [llength $allparents($id)] == 1} {
8756 # part-way along an arc; check that arc first
8757 set a [lindex $arcnos($id) 0]
8758 if {$arctags($a) ne {}} {
8760 set i [lsearch -exact $arcids($a) $id]
8761 foreach t $arctags($a) {
8762 set j [lsearch -exact $arcids($a) $t]
8768 if {![info exists arcend($a)]} {
8772 if {[info exists idtags($id)]} {
8776 if {[info exists cached_atags($id)]} {
8777 return $cached_atags($id)
8785 for {set i 0} {$i < [llength $todo] && $nc > 0} {incr i} {
8786 set id [lindex $todo $i]
8788 set td [info exists hastaggeddescendent($id)]
8792 # ignore tags on starting node
8793 if {!$td && $i > 0} {
8794 if {[info exists idtags($id)]} {
8797 } elseif {[info exists cached_atags($id)]} {
8798 set tagloc($id) $cached_atags($id)
8802 foreach a $arcout($id) {
8803 if {!$td && $arctags($a) ne {}} {
8805 if {$arctags($a) ne {}} {
8806 lappend tagloc($id) [lindex $arctags($a) 0]
8809 if {![info exists arcend($a)]} continue
8811 if {$td || $arctags($a) ne {}} {
8812 set tomark [list $d]
8813 for {set j 0} {$j < [llength $tomark]} {incr j} {
8814 set dd [lindex $tomark $j]
8815 if {![info exists hastaggeddescendent($dd)]} {
8816 if {[info exists done($dd)]} {
8817 foreach b $arcout($dd) {
8818 if {[info exists arcend($b)]} {
8819 lappend tomark $arcend($b)
8822 if {[info exists tagloc($dd)]} {
8825 } elseif {[info exists queued($dd)]} {
8828 set hastaggeddescendent($dd) 1
8832 if {![info exists queued($d)]} {
8835 if {![info exists hastaggeddescendent($d)]} {
8841 set t2 [clock clicks -milliseconds]
8844 foreach id [array names tagloc] {
8845 if {![info exists hastaggeddescendent($id)]} {
8846 foreach t $tagloc($id) {
8847 if {[lsearch -exact $tags $t] < 0} {
8854 # remove tags that are ancestors of other tags
8855 for {set i 0} {$i < [llength $tags]} {incr i} {
8856 set a [lindex $tags $i]
8857 for {set j 0} {$j < $i} {incr j} {
8858 set b [lindex $tags $j]
8859 set r [anc_or_desc $a $b]
8861 set tags [lreplace $tags $j $j]
8864 } elseif {$r == 1} {
8865 set tags [lreplace $tags $i $i]
8872 if {[array names growing] ne {}} {
8873 # graph isn't finished, need to check if any tag could get
8874 # eclipsed by another tag coming later. Simply ignore any
8875 # tags that could later get eclipsed.
8878 if {[is_certain $origid $t]} {
8882 if {$tags eq $ctags} {
8883 set cached_atags($origid) $tags
8888 set cached_atags($origid) $tags
8890 set t3 [clock clicks -milliseconds]
8891 if {0 && $t3 - $t1 >= 100} {
8892 puts "iterating ancestors ($loopix/[llength $todo] nodes) took\
8893 [expr {$t2-$t1}]+[expr {$t3-$t2}]ms, $nc candidates left"
8898 # Return the list of IDs that have heads that are descendents of id,
8899 # including id itself if it has a head.
8900 proc descheads {id} {
8901 global arcnos arcstart arcids archeads idheads cached_dheads
8904 if {![info exists allparents($id)]} {
8908 if {[llength $arcnos($id)] == 1 && [llength $allparents($id)] == 1} {
8909 # part-way along an arc; check it first
8910 set a [lindex $arcnos($id) 0]
8911 if {$archeads($a) ne {}} {
8912 validate_archeads $a
8913 set i [lsearch -exact $arcids($a) $id]
8914 foreach t $archeads($a) {
8915 set j [lsearch -exact $arcids($a) $t]
8920 set id $arcstart($a)
8926 for {set i 0} {$i < [llength $todo]} {incr i} {
8927 set id [lindex $todo $i]
8928 if {[info exists cached_dheads($id)]} {
8929 set ret [concat $ret $cached_dheads($id)]
8931 if {[info exists idheads($id)]} {
8934 foreach a $arcnos($id) {
8935 if {$archeads($a) ne {}} {
8936 validate_archeads $a
8937 if {$archeads($a) ne {}} {
8938 set ret [concat $ret $archeads($a)]
8942 if {![info exists seen($d)]} {
8949 set ret [lsort -unique $ret]
8950 set cached_dheads($origid) $ret
8951 return [concat $ret $aret]
8954 proc addedtag {id} {
8955 global arcnos arcout cached_dtags cached_atags
8957 if {![info exists arcnos($id)]} return
8958 if {![info exists arcout($id)]} {
8959 recalcarc [lindex $arcnos($id) 0]
8961 catch {unset cached_dtags}
8962 catch {unset cached_atags}
8965 proc addedhead {hid head} {
8966 global arcnos arcout cached_dheads
8968 if {![info exists arcnos($hid)]} return
8969 if {![info exists arcout($hid)]} {
8970 recalcarc [lindex $arcnos($hid) 0]
8972 catch {unset cached_dheads}
8975 proc removedhead {hid head} {
8976 global cached_dheads
8978 catch {unset cached_dheads}
8981 proc movedhead {hid head} {
8982 global arcnos arcout cached_dheads
8984 if {![info exists arcnos($hid)]} return
8985 if {![info exists arcout($hid)]} {
8986 recalcarc [lindex $arcnos($hid) 0]
8988 catch {unset cached_dheads}
8991 proc changedrefs {} {
8992 global cached_dheads cached_dtags cached_atags
8993 global arctags archeads arcnos arcout idheads idtags
8995 foreach id [concat [array names idheads] [array names idtags]] {
8996 if {[info exists arcnos($id)] && ![info exists arcout($id)]} {
8997 set a [lindex $arcnos($id) 0]
8998 if {![info exists donearc($a)]} {
9004 catch {unset cached_dtags}
9005 catch {unset cached_atags}
9006 catch {unset cached_dheads}
9009 proc rereadrefs {} {
9010 global idtags idheads idotherrefs mainheadid
9012 set refids [concat [array names idtags] \
9013 [array names idheads] [array names idotherrefs]]
9014 foreach id $refids {
9015 if {![info exists ref($id)]} {
9016 set ref($id) [listrefs $id]
9019 set oldmainhead $mainheadid
9022 set refids [lsort -unique [concat $refids [array names idtags] \
9023 [array names idheads] [array names idotherrefs]]]
9024 foreach id $refids {
9025 set v [listrefs $id]
9026 if {![info exists ref($id)] || $ref($id) != $v} {
9030 if {$oldmainhead ne $mainheadid} {
9031 redrawtags $oldmainhead
9032 redrawtags $mainheadid
9037 proc listrefs {id} {
9038 global idtags idheads idotherrefs
9041 if {[info exists idtags($id)]} {
9045 if {[info exists idheads($id)]} {
9049 if {[info exists idotherrefs($id)]} {
9050 set z $idotherrefs($id)
9052 return [list $x $y $z]
9055 proc showtag {tag isnew} {
9056 global ctext tagcontents tagids linknum tagobjid
9059 addtohistory [list showtag $tag 0]
9061 $ctext conf -state normal
9065 if {![info exists tagcontents($tag)]} {
9067 set tagcontents($tag) [exec git cat-file tag $tagobjid($tag)]
9070 if {[info exists tagcontents($tag)]} {
9071 set text $tagcontents($tag)
9073 set text "[mc "Tag"]: $tag\n[mc "Id"]: $tagids($tag)"
9075 appendwithlinks $text {}
9076 $ctext conf -state disabled
9088 if {[info exists gitktmpdir]} {
9089 catch {file delete -force $gitktmpdir}
9093 proc mkfontdisp {font top which} {
9094 global fontattr fontpref $font
9096 set fontpref($font) [set $font]
9097 button $top.${font}but -text $which -font optionfont \
9098 -command [list choosefont $font $which]
9099 label $top.$font -relief flat -font $font \
9100 -text $fontattr($font,family) -justify left
9101 grid x $top.${font}but $top.$font -sticky w
9104 proc choosefont {font which} {
9105 global fontparam fontlist fonttop fontattr
9107 set fontparam(which) $which
9108 set fontparam(font) $font
9109 set fontparam(family) [font actual $font -family]
9110 set fontparam(size) $fontattr($font,size)
9111 set fontparam(weight) $fontattr($font,weight)
9112 set fontparam(slant) $fontattr($font,slant)
9115 if {![winfo exists $top]} {
9117 eval font config sample [font actual $font]
9119 wm title $top [mc "Gitk font chooser"]
9120 label $top.l -textvariable fontparam(which)
9121 pack $top.l -side top
9122 set fontlist [lsort [font families]]
9124 listbox $top.f.fam -listvariable fontlist \
9125 -yscrollcommand [list $top.f.sb set]
9126 bind $top.f.fam <<ListboxSelect>> selfontfam
9127 scrollbar $top.f.sb -command [list $top.f.fam yview]
9128 pack $top.f.sb -side right -fill y
9129 pack $top.f.fam -side left -fill both -expand 1
9130 pack $top.f -side top -fill both -expand 1
9132 spinbox $top.g.size -from 4 -to 40 -width 4 \
9133 -textvariable fontparam(size) \
9134 -validatecommand {string is integer -strict %s}
9135 checkbutton $top.g.bold -padx 5 \
9136 -font {{Times New Roman} 12 bold} -text [mc "B"] -indicatoron 0 \
9137 -variable fontparam(weight) -onvalue bold -offvalue normal
9138 checkbutton $top.g.ital -padx 5 \
9139 -font {{Times New Roman} 12 italic} -text [mc "I"] -indicatoron 0 \
9140 -variable fontparam(slant) -onvalue italic -offvalue roman
9141 pack $top.g.size $top.g.bold $top.g.ital -side left
9142 pack $top.g -side top
9143 canvas $top.c -width 150 -height 50 -border 2 -relief sunk \
9145 $top.c create text 100 25 -anchor center -text $which -font sample \
9146 -fill black -tags text
9147 bind $top.c <Configure> [list centertext $top.c]
9148 pack $top.c -side top -fill x
9150 button $top.buts.ok -text [mc "OK"] -command fontok -default active
9151 button $top.buts.can -text [mc "Cancel"] -command fontcan -default normal
9152 grid $top.buts.ok $top.buts.can
9153 grid columnconfigure $top.buts 0 -weight 1 -uniform a
9154 grid columnconfigure $top.buts 1 -weight 1 -uniform a
9155 pack $top.buts -side bottom -fill x
9156 trace add variable fontparam write chg_fontparam
9159 $top.c itemconf text -text $which
9161 set i [lsearch -exact $fontlist $fontparam(family)]
9163 $top.f.fam selection set $i
9168 proc centertext {w} {
9169 $w coords text [expr {[winfo width $w] / 2}] [expr {[winfo height $w] / 2}]
9173 global fontparam fontpref prefstop
9175 set f $fontparam(font)
9176 set fontpref($f) [list $fontparam(family) $fontparam(size)]
9177 if {$fontparam(weight) eq "bold"} {
9178 lappend fontpref($f) "bold"
9180 if {$fontparam(slant) eq "italic"} {
9181 lappend fontpref($f) "italic"
9184 $w conf -text $fontparam(family) -font $fontpref($f)
9190 global fonttop fontparam
9192 if {[info exists fonttop]} {
9193 catch {destroy $fonttop}
9194 catch {font delete sample}
9200 proc selfontfam {} {
9201 global fonttop fontparam
9203 set i [$fonttop.f.fam curselection]
9205 set fontparam(family) [$fonttop.f.fam get $i]
9209 proc chg_fontparam {v sub op} {
9212 font config sample -$sub $fontparam($sub)
9216 global maxwidth maxgraphpct
9217 global oldprefs prefstop showneartags showlocalchanges
9218 global bgcolor fgcolor ctext diffcolors selectbgcolor
9219 global tabstop limitdiffs autoselect extdifftool
9223 if {[winfo exists $top]} {
9227 foreach v {maxwidth maxgraphpct showneartags showlocalchanges \
9228 limitdiffs tabstop} {
9229 set oldprefs($v) [set $v]
9232 wm title $top [mc "Gitk preferences"]
9233 label $top.ldisp -text [mc "Commit list display options"]
9234 grid $top.ldisp - -sticky w -pady 10
9235 label $top.spacer -text " "
9236 label $top.maxwidthl -text [mc "Maximum graph width (lines)"] \
9238 spinbox $top.maxwidth -from 0 -to 100 -width 4 -textvariable maxwidth
9239 grid $top.spacer $top.maxwidthl $top.maxwidth -sticky w
9240 label $top.maxpctl -text [mc "Maximum graph width (% of pane)"] \
9242 spinbox $top.maxpct -from 1 -to 100 -width 4 -textvariable maxgraphpct
9243 grid x $top.maxpctl $top.maxpct -sticky w
9244 frame $top.showlocal
9245 label $top.showlocal.l -text [mc "Show local changes"] -font optionfont
9246 checkbutton $top.showlocal.b -variable showlocalchanges
9247 pack $top.showlocal.b $top.showlocal.l -side left
9248 grid x $top.showlocal -sticky w
9249 frame $top.autoselect
9250 label $top.autoselect.l -text [mc "Auto-select SHA1"] -font optionfont
9251 checkbutton $top.autoselect.b -variable autoselect
9252 pack $top.autoselect.b $top.autoselect.l -side left
9253 grid x $top.autoselect -sticky w
9255 label $top.ddisp -text [mc "Diff display options"]
9256 grid $top.ddisp - -sticky w -pady 10
9257 label $top.tabstopl -text [mc "Tab spacing"] -font optionfont
9258 spinbox $top.tabstop -from 1 -to 20 -width 4 -textvariable tabstop
9259 grid x $top.tabstopl $top.tabstop -sticky w
9261 label $top.ntag.l -text [mc "Display nearby tags"] -font optionfont
9262 checkbutton $top.ntag.b -variable showneartags
9263 pack $top.ntag.b $top.ntag.l -side left
9264 grid x $top.ntag -sticky w
9266 label $top.ldiff.l -text [mc "Limit diffs to listed paths"] -font optionfont
9267 checkbutton $top.ldiff.b -variable limitdiffs
9268 pack $top.ldiff.b $top.ldiff.l -side left
9269 grid x $top.ldiff -sticky w
9271 entry $top.extdifft -textvariable extdifftool
9273 label $top.extdifff.l -text [mc "External diff tool" ] -font optionfont \
9275 button $top.extdifff.b -text [mc "Choose..."] -font optionfont \
9276 -command choose_extdiff
9277 pack $top.extdifff.l $top.extdifff.b -side left
9278 grid x $top.extdifff $top.extdifft -sticky w
9280 label $top.cdisp -text [mc "Colors: press to choose"]
9281 grid $top.cdisp - -sticky w -pady 10
9282 label $top.bg -padx 40 -relief sunk -background $bgcolor
9283 button $top.bgbut -text [mc "Background"] -font optionfont \
9284 -command [list choosecolor bgcolor {} $top.bg background setbg]
9285 grid x $top.bgbut $top.bg -sticky w
9286 label $top.fg -padx 40 -relief sunk -background $fgcolor
9287 button $top.fgbut -text [mc "Foreground"] -font optionfont \
9288 -command [list choosecolor fgcolor {} $top.fg foreground setfg]
9289 grid x $top.fgbut $top.fg -sticky w
9290 label $top.diffold -padx 40 -relief sunk -background [lindex $diffcolors 0]
9291 button $top.diffoldbut -text [mc "Diff: old lines"] -font optionfont \
9292 -command [list choosecolor diffcolors 0 $top.diffold "diff old lines" \
9293 [list $ctext tag conf d0 -foreground]]
9294 grid x $top.diffoldbut $top.diffold -sticky w
9295 label $top.diffnew -padx 40 -relief sunk -background [lindex $diffcolors 1]
9296 button $top.diffnewbut -text [mc "Diff: new lines"] -font optionfont \
9297 -command [list choosecolor diffcolors 1 $top.diffnew "diff new lines" \
9298 [list $ctext tag conf d1 -foreground]]
9299 grid x $top.diffnewbut $top.diffnew -sticky w
9300 label $top.hunksep -padx 40 -relief sunk -background [lindex $diffcolors 2]
9301 button $top.hunksepbut -text [mc "Diff: hunk header"] -font optionfont \
9302 -command [list choosecolor diffcolors 2 $top.hunksep \
9303 "diff hunk header" \
9304 [list $ctext tag conf hunksep -foreground]]
9305 grid x $top.hunksepbut $top.hunksep -sticky w
9306 label $top.selbgsep -padx 40 -relief sunk -background $selectbgcolor
9307 button $top.selbgbut -text [mc "Select bg"] -font optionfont \
9308 -command [list choosecolor selectbgcolor {} $top.selbgsep background setselbg]
9309 grid x $top.selbgbut $top.selbgsep -sticky w
9311 label $top.cfont -text [mc "Fonts: press to choose"]
9312 grid $top.cfont - -sticky w -pady 10
9313 mkfontdisp mainfont $top [mc "Main font"]
9314 mkfontdisp textfont $top [mc "Diff display font"]
9315 mkfontdisp uifont $top [mc "User interface font"]
9318 button $top.buts.ok -text [mc "OK"] -command prefsok -default active
9319 button $top.buts.can -text [mc "Cancel"] -command prefscan -default normal
9320 grid $top.buts.ok $top.buts.can
9321 grid columnconfigure $top.buts 0 -weight 1 -uniform a
9322 grid columnconfigure $top.buts 1 -weight 1 -uniform a
9323 grid $top.buts - - -pady 10 -sticky ew
9324 bind $top <Visibility> "focus $top.buts.ok"
9327 proc choose_extdiff {} {
9330 set prog [tk_getOpenFile -title "External diff tool" -multiple false]
9332 set extdifftool $prog
9336 proc choosecolor {v vi w x cmd} {
9339 set c [tk_chooseColor -initialcolor [lindex [set $v] $vi] \
9340 -title [mc "Gitk: choose color for %s" $x]]
9341 if {$c eq {}} return
9342 $w conf -background $c
9348 global bglist cflist
9350 $w configure -selectbackground $c
9352 $cflist tag configure highlight \
9353 -background [$cflist cget -selectbackground]
9354 allcanvs itemconf secsel -fill $c
9361 $w conf -background $c
9369 $w conf -foreground $c
9371 allcanvs itemconf text -fill $c
9372 $canv itemconf circle -outline $c
9376 global oldprefs prefstop
9378 foreach v {maxwidth maxgraphpct showneartags showlocalchanges \
9379 limitdiffs tabstop} {
9381 set $v $oldprefs($v)
9383 catch {destroy $prefstop}
9389 global maxwidth maxgraphpct
9390 global oldprefs prefstop showneartags showlocalchanges
9391 global fontpref mainfont textfont uifont
9392 global limitdiffs treediffs
9394 catch {destroy $prefstop}
9398 if {$mainfont ne $fontpref(mainfont)} {
9399 set mainfont $fontpref(mainfont)
9400 parsefont mainfont $mainfont
9401 eval font configure mainfont [fontflags mainfont]
9402 eval font configure mainfontbold [fontflags mainfont 1]
9406 if {$textfont ne $fontpref(textfont)} {
9407 set textfont $fontpref(textfont)
9408 parsefont textfont $textfont
9409 eval font configure textfont [fontflags textfont]
9410 eval font configure textfontbold [fontflags textfont 1]
9412 if {$uifont ne $fontpref(uifont)} {
9413 set uifont $fontpref(uifont)
9414 parsefont uifont $uifont
9415 eval font configure uifont [fontflags uifont]
9418 if {$showlocalchanges != $oldprefs(showlocalchanges)} {
9419 if {$showlocalchanges} {
9425 if {$limitdiffs != $oldprefs(limitdiffs)} {
9426 # treediffs elements are limited by path
9427 catch {unset treediffs}
9429 if {$fontchanged || $maxwidth != $oldprefs(maxwidth)
9430 || $maxgraphpct != $oldprefs(maxgraphpct)} {
9432 } elseif {$showneartags != $oldprefs(showneartags) ||
9433 $limitdiffs != $oldprefs(limitdiffs)} {
9438 proc formatdate {d} {
9439 global datetimeformat
9441 set d [clock format $d -format $datetimeformat]
9446 # This list of encoding names and aliases is distilled from
9447 # http://www.iana.org/assignments/character-sets.
9448 # Not all of them are supported by Tcl.
9449 set encoding_aliases {
9450 { ANSI_X3.4-1968 iso-ir-6 ANSI_X3.4-1986 ISO_646.irv:1991 ASCII
9451 ISO646-US US-ASCII us IBM367 cp367 csASCII }
9452 { ISO-10646-UTF-1 csISO10646UTF1 }
9453 { ISO_646.basic:1983 ref csISO646basic1983 }
9454 { INVARIANT csINVARIANT }
9455 { ISO_646.irv:1983 iso-ir-2 irv csISO2IntlRefVersion }
9456 { BS_4730 iso-ir-4 ISO646-GB gb uk csISO4UnitedKingdom }
9457 { NATS-SEFI iso-ir-8-1 csNATSSEFI }
9458 { NATS-SEFI-ADD iso-ir-8-2 csNATSSEFIADD }
9459 { NATS-DANO iso-ir-9-1 csNATSDANO }
9460 { NATS-DANO-ADD iso-ir-9-2 csNATSDANOADD }
9461 { SEN_850200_B iso-ir-10 FI ISO646-FI ISO646-SE se csISO10Swedish }
9462 { SEN_850200_C iso-ir-11 ISO646-SE2 se2 csISO11SwedishForNames }
9463 { KS_C_5601-1987 iso-ir-149 KS_C_5601-1989 KSC_5601 korean csKSC56011987 }
9464 { ISO-2022-KR csISO2022KR }
9466 { ISO-2022-JP csISO2022JP }
9467 { ISO-2022-JP-2 csISO2022JP2 }
9468 { JIS_C6220-1969-jp JIS_C6220-1969 iso-ir-13 katakana x0201-7
9470 { JIS_C6220-1969-ro iso-ir-14 jp ISO646-JP csISO14JISC6220ro }
9471 { IT iso-ir-15 ISO646-IT csISO15Italian }
9472 { PT iso-ir-16 ISO646-PT csISO16Portuguese }
9473 { ES iso-ir-17 ISO646-ES csISO17Spanish }
9474 { greek7-old iso-ir-18 csISO18Greek7Old }
9475 { latin-greek iso-ir-19 csISO19LatinGreek }
9476 { DIN_66003 iso-ir-21 de ISO646-DE csISO21German }
9477 { NF_Z_62-010_(1973) iso-ir-25 ISO646-FR1 csISO25French }
9478 { Latin-greek-1 iso-ir-27 csISO27LatinGreek1 }
9479 { ISO_5427 iso-ir-37 csISO5427Cyrillic }
9480 { JIS_C6226-1978 iso-ir-42 csISO42JISC62261978 }
9481 { BS_viewdata iso-ir-47 csISO47BSViewdata }
9482 { INIS iso-ir-49 csISO49INIS }
9483 { INIS-8 iso-ir-50 csISO50INIS8 }
9484 { INIS-cyrillic iso-ir-51 csISO51INISCyrillic }
9485 { ISO_5427:1981 iso-ir-54 ISO5427Cyrillic1981 }
9486 { ISO_5428:1980 iso-ir-55 csISO5428Greek }
9487 { GB_1988-80 iso-ir-57 cn ISO646-CN csISO57GB1988 }
9488 { GB_2312-80 iso-ir-58 chinese csISO58GB231280 }
9489 { NS_4551-1 iso-ir-60 ISO646-NO no csISO60DanishNorwegian
9491 { NS_4551-2 ISO646-NO2 iso-ir-61 no2 csISO61Norwegian2 }
9492 { NF_Z_62-010 iso-ir-69 ISO646-FR fr csISO69French }
9493 { videotex-suppl iso-ir-70 csISO70VideotexSupp1 }
9494 { PT2 iso-ir-84 ISO646-PT2 csISO84Portuguese2 }
9495 { ES2 iso-ir-85 ISO646-ES2 csISO85Spanish2 }
9496 { MSZ_7795.3 iso-ir-86 ISO646-HU hu csISO86Hungarian }
9497 { JIS_C6226-1983 iso-ir-87 x0208 JIS_X0208-1983 csISO87JISX0208 }
9498 { greek7 iso-ir-88 csISO88Greek7 }
9499 { ASMO_449 ISO_9036 arabic7 iso-ir-89 csISO89ASMO449 }
9500 { iso-ir-90 csISO90 }
9501 { JIS_C6229-1984-a iso-ir-91 jp-ocr-a csISO91JISC62291984a }
9502 { JIS_C6229-1984-b iso-ir-92 ISO646-JP-OCR-B jp-ocr-b
9503 csISO92JISC62991984b }
9504 { JIS_C6229-1984-b-add iso-ir-93 jp-ocr-b-add csISO93JIS62291984badd }
9505 { JIS_C6229-1984-hand iso-ir-94 jp-ocr-hand csISO94JIS62291984hand }
9506 { JIS_C6229-1984-hand-add iso-ir-95 jp-ocr-hand-add
9507 csISO95JIS62291984handadd }
9508 { JIS_C6229-1984-kana iso-ir-96 csISO96JISC62291984kana }
9509 { ISO_2033-1983 iso-ir-98 e13b csISO2033 }
9510 { ANSI_X3.110-1983 iso-ir-99 CSA_T500-1983 NAPLPS csISO99NAPLPS }
9511 { ISO_8859-1:1987 iso-ir-100 ISO_8859-1 ISO-8859-1 latin1 l1 IBM819
9513 { ISO_8859-2:1987 iso-ir-101 ISO_8859-2 ISO-8859-2 latin2 l2 csISOLatin2 }
9514 { T.61-7bit iso-ir-102 csISO102T617bit }
9515 { T.61-8bit T.61 iso-ir-103 csISO103T618bit }
9516 { ISO_8859-3:1988 iso-ir-109 ISO_8859-3 ISO-8859-3 latin3 l3 csISOLatin3 }
9517 { ISO_8859-4:1988 iso-ir-110 ISO_8859-4 ISO-8859-4 latin4 l4 csISOLatin4 }
9518 { ECMA-cyrillic iso-ir-111 KOI8-E csISO111ECMACyrillic }
9519 { CSA_Z243.4-1985-1 iso-ir-121 ISO646-CA csa7-1 ca csISO121Canadian1 }
9520 { CSA_Z243.4-1985-2 iso-ir-122 ISO646-CA2 csa7-2 csISO122Canadian2 }
9521 { CSA_Z243.4-1985-gr iso-ir-123 csISO123CSAZ24341985gr }
9522 { ISO_8859-6:1987 iso-ir-127 ISO_8859-6 ISO-8859-6 ECMA-114 ASMO-708
9523 arabic csISOLatinArabic }
9524 { ISO_8859-6-E csISO88596E ISO-8859-6-E }
9525 { ISO_8859-6-I csISO88596I ISO-8859-6-I }
9526 { ISO_8859-7:1987 iso-ir-126 ISO_8859-7 ISO-8859-7 ELOT_928 ECMA-118
9527 greek greek8 csISOLatinGreek }
9528 { T.101-G2 iso-ir-128 csISO128T101G2 }
9529 { ISO_8859-8:1988 iso-ir-138 ISO_8859-8 ISO-8859-8 hebrew
9531 { ISO_8859-8-E csISO88598E ISO-8859-8-E }
9532 { ISO_8859-8-I csISO88598I ISO-8859-8-I }
9533 { CSN_369103 iso-ir-139 csISO139CSN369103 }
9534 { JUS_I.B1.002 iso-ir-141 ISO646-YU js yu csISO141JUSIB1002 }
9535 { ISO_6937-2-add iso-ir-142 csISOTextComm }
9536 { IEC_P27-1 iso-ir-143 csISO143IECP271 }
9537 { ISO_8859-5:1988 iso-ir-144 ISO_8859-5 ISO-8859-5 cyrillic
9538 csISOLatinCyrillic }
9539 { JUS_I.B1.003-serb iso-ir-146 serbian csISO146Serbian }
9540 { JUS_I.B1.003-mac macedonian iso-ir-147 csISO147Macedonian }
9541 { ISO_8859-9:1989 iso-ir-148 ISO_8859-9 ISO-8859-9 latin5 l5 csISOLatin5 }
9542 { greek-ccitt iso-ir-150 csISO150 csISO150GreekCCITT }
9543 { NC_NC00-10:81 cuba iso-ir-151 ISO646-CU csISO151Cuba }
9544 { ISO_6937-2-25 iso-ir-152 csISO6937Add }
9545 { GOST_19768-74 ST_SEV_358-88 iso-ir-153 csISO153GOST1976874 }
9546 { ISO_8859-supp iso-ir-154 latin1-2-5 csISO8859Supp }
9547 { ISO_10367-box iso-ir-155 csISO10367Box }
9548 { ISO-8859-10 iso-ir-157 l6 ISO_8859-10:1992 csISOLatin6 latin6 }
9549 { latin-lap lap iso-ir-158 csISO158Lap }
9550 { JIS_X0212-1990 x0212 iso-ir-159 csISO159JISX02121990 }
9551 { DS_2089 DS2089 ISO646-DK dk csISO646Danish }
9554 { JIS_X0201 X0201 csHalfWidthKatakana }
9555 { KSC5636 ISO646-KR csKSC5636 }
9556 { ISO-10646-UCS-2 csUnicode }
9557 { ISO-10646-UCS-4 csUCS4 }
9558 { DEC-MCS dec csDECMCS }
9559 { hp-roman8 roman8 r8 csHPRoman8 }
9560 { macintosh mac csMacintosh }
9561 { IBM037 cp037 ebcdic-cp-us ebcdic-cp-ca ebcdic-cp-wt ebcdic-cp-nl
9563 { IBM038 EBCDIC-INT cp038 csIBM038 }
9564 { IBM273 CP273 csIBM273 }
9565 { IBM274 EBCDIC-BE CP274 csIBM274 }
9566 { IBM275 EBCDIC-BR cp275 csIBM275 }
9567 { IBM277 EBCDIC-CP-DK EBCDIC-CP-NO csIBM277 }
9568 { IBM278 CP278 ebcdic-cp-fi ebcdic-cp-se csIBM278 }
9569 { IBM280 CP280 ebcdic-cp-it csIBM280 }
9570 { IBM281 EBCDIC-JP-E cp281 csIBM281 }
9571 { IBM284 CP284 ebcdic-cp-es csIBM284 }
9572 { IBM285 CP285 ebcdic-cp-gb csIBM285 }
9573 { IBM290 cp290 EBCDIC-JP-kana csIBM290 }
9574 { IBM297 cp297 ebcdic-cp-fr csIBM297 }
9575 { IBM420 cp420 ebcdic-cp-ar1 csIBM420 }
9576 { IBM423 cp423 ebcdic-cp-gr csIBM423 }
9577 { IBM424 cp424 ebcdic-cp-he csIBM424 }
9578 { IBM437 cp437 437 csPC8CodePage437 }
9579 { IBM500 CP500 ebcdic-cp-be ebcdic-cp-ch csIBM500 }
9580 { IBM775 cp775 csPC775Baltic }
9581 { IBM850 cp850 850 csPC850Multilingual }
9582 { IBM851 cp851 851 csIBM851 }
9583 { IBM852 cp852 852 csPCp852 }
9584 { IBM855 cp855 855 csIBM855 }
9585 { IBM857 cp857 857 csIBM857 }
9586 { IBM860 cp860 860 csIBM860 }
9587 { IBM861 cp861 861 cp-is csIBM861 }
9588 { IBM862 cp862 862 csPC862LatinHebrew }
9589 { IBM863 cp863 863 csIBM863 }
9590 { IBM864 cp864 csIBM864 }
9591 { IBM865 cp865 865 csIBM865 }
9592 { IBM866 cp866 866 csIBM866 }
9593 { IBM868 CP868 cp-ar csIBM868 }
9594 { IBM869 cp869 869 cp-gr csIBM869 }
9595 { IBM870 CP870 ebcdic-cp-roece ebcdic-cp-yu csIBM870 }
9596 { IBM871 CP871 ebcdic-cp-is csIBM871 }
9597 { IBM880 cp880 EBCDIC-Cyrillic csIBM880 }
9598 { IBM891 cp891 csIBM891 }
9599 { IBM903 cp903 csIBM903 }
9600 { IBM904 cp904 904 csIBBM904 }
9601 { IBM905 CP905 ebcdic-cp-tr csIBM905 }
9602 { IBM918 CP918 ebcdic-cp-ar2 csIBM918 }
9603 { IBM1026 CP1026 csIBM1026 }
9604 { EBCDIC-AT-DE csIBMEBCDICATDE }
9605 { EBCDIC-AT-DE-A csEBCDICATDEA }
9606 { EBCDIC-CA-FR csEBCDICCAFR }
9607 { EBCDIC-DK-NO csEBCDICDKNO }
9608 { EBCDIC-DK-NO-A csEBCDICDKNOA }
9609 { EBCDIC-FI-SE csEBCDICFISE }
9610 { EBCDIC-FI-SE-A csEBCDICFISEA }
9611 { EBCDIC-FR csEBCDICFR }
9612 { EBCDIC-IT csEBCDICIT }
9613 { EBCDIC-PT csEBCDICPT }
9614 { EBCDIC-ES csEBCDICES }
9615 { EBCDIC-ES-A csEBCDICESA }
9616 { EBCDIC-ES-S csEBCDICESS }
9617 { EBCDIC-UK csEBCDICUK }
9618 { EBCDIC-US csEBCDICUS }
9619 { UNKNOWN-8BIT csUnknown8BiT }
9620 { MNEMONIC csMnemonic }
9625 { IBM00858 CCSID00858 CP00858 PC-Multilingual-850+euro }
9626 { IBM00924 CCSID00924 CP00924 ebcdic-Latin9--euro }
9627 { IBM01140 CCSID01140 CP01140 ebcdic-us-37+euro }
9628 { IBM01141 CCSID01141 CP01141 ebcdic-de-273+euro }
9629 { IBM01142 CCSID01142 CP01142 ebcdic-dk-277+euro ebcdic-no-277+euro }
9630 { IBM01143 CCSID01143 CP01143 ebcdic-fi-278+euro ebcdic-se-278+euro }
9631 { IBM01144 CCSID01144 CP01144 ebcdic-it-280+euro }
9632 { IBM01145 CCSID01145 CP01145 ebcdic-es-284+euro }
9633 { IBM01146 CCSID01146 CP01146 ebcdic-gb-285+euro }
9634 { IBM01147 CCSID01147 CP01147 ebcdic-fr-297+euro }
9635 { IBM01148 CCSID01148 CP01148 ebcdic-international-500+euro }
9636 { IBM01149 CCSID01149 CP01149 ebcdic-is-871+euro }
9637 { IBM1047 IBM-1047 }
9638 { PTCP154 csPTCP154 PT154 CP154 Cyrillic-Asian }
9639 { Amiga-1251 Ami1251 Amiga1251 Ami-1251 }
9640 { UNICODE-1-1 csUnicode11 }
9643 { UNICODE-1-1-UTF-7 csUnicode11UTF7 }
9644 { ISO-8859-14 iso-ir-199 ISO_8859-14:1998 ISO_8859-14 latin8 iso-celtic
9646 { ISO-8859-15 ISO_8859-15 Latin-9 }
9647 { ISO-8859-16 iso-ir-226 ISO_8859-16:2001 ISO_8859-16 latin10 l10 }
9648 { GBK CP936 MS936 windows-936 }
9649 { JIS_Encoding csJISEncoding }
9650 { Shift_JIS MS_Kanji csShiftJIS }
9651 { Extended_UNIX_Code_Packed_Format_for_Japanese csEUCPkdFmtJapanese
9653 { Extended_UNIX_Code_Fixed_Width_for_Japanese csEUCFixWidJapanese }
9654 { ISO-10646-UCS-Basic csUnicodeASCII }
9655 { ISO-10646-Unicode-Latin1 csUnicodeLatin1 ISO-10646 }
9656 { ISO-Unicode-IBM-1261 csUnicodeIBM1261 }
9657 { ISO-Unicode-IBM-1268 csUnicodeIBM1268 }
9658 { ISO-Unicode-IBM-1276 csUnicodeIBM1276 }
9659 { ISO-Unicode-IBM-1264 csUnicodeIBM1264 }
9660 { ISO-Unicode-IBM-1265 csUnicodeIBM1265 }
9661 { ISO-8859-1-Windows-3.0-Latin-1 csWindows30Latin1 }
9662 { ISO-8859-1-Windows-3.1-Latin-1 csWindows31Latin1 }
9663 { ISO-8859-2-Windows-Latin-2 csWindows31Latin2 }
9664 { ISO-8859-9-Windows-Latin-5 csWindows31Latin5 }
9665 { Adobe-Standard-Encoding csAdobeStandardEncoding }
9666 { Ventura-US csVenturaUS }
9667 { Ventura-International csVenturaInternational }
9668 { PC8-Danish-Norwegian csPC8DanishNorwegian }
9669 { PC8-Turkish csPC8Turkish }
9670 { IBM-Symbols csIBMSymbols }
9671 { IBM-Thai csIBMThai }
9672 { HP-Legal csHPLegal }
9673 { HP-Pi-font csHPPiFont }
9674 { HP-Math8 csHPMath8 }
9675 { Adobe-Symbol-Encoding csHPPSMath }
9676 { HP-DeskTop csHPDesktop }
9677 { Ventura-Math csVenturaMath }
9678 { Microsoft-Publishing csMicrosoftPublishing }
9679 { Windows-31J csWindows31J }
9684 proc tcl_encoding {enc} {
9685 global encoding_aliases
9686 set names [encoding names]
9687 set lcnames [string tolower $names]
9688 set enc [string tolower $enc]
9689 set i [lsearch -exact $lcnames $enc]
9691 # look for "isonnn" instead of "iso-nnn" or "iso_nnn"
9692 if {[regsub {^iso[-_]} $enc iso encx]} {
9693 set i [lsearch -exact $lcnames $encx]
9697 foreach l $encoding_aliases {
9698 set ll [string tolower $l]
9699 if {[lsearch -exact $ll $enc] < 0} continue
9700 # look through the aliases for one that tcl knows about
9702 set i [lsearch -exact $lcnames $e]
9704 if {[regsub {^iso[-_]} $e iso ex]} {
9705 set i [lsearch -exact $lcnames $ex]
9714 return [lindex $names $i]
9719 # First check that Tcl/Tk is recent enough
9720 if {[catch {package require Tk 8.4} err]} {
9721 show_error {} . [mc "Sorry, gitk cannot run with this version of Tcl/Tk.\n\
9722 Gitk requires at least Tcl/Tk 8.4."]
9727 set wrcomcmd "git diff-tree --stdin -p --pretty"
9731 set gitencoding [exec git config --get i18n.commitencoding]
9733 if {$gitencoding == ""} {
9734 set gitencoding "utf-8"
9736 set tclencoding [tcl_encoding $gitencoding]
9737 if {$tclencoding == {}} {
9738 puts stderr "Warning: encoding $gitencoding is not supported by Tcl/Tk"
9741 set mainfont {Helvetica 9}
9742 set textfont {Courier 9}
9743 set uifont {Helvetica 9 bold}
9745 set findmergefiles 0
9753 set cmitmode "patch"
9754 set wrapcomment "none"
9758 set showlocalchanges 1
9760 set datetimeformat "%Y-%m-%d %H:%M:%S"
9763 set extdifftool "meld"
9765 set colors {green red blue magenta darkgrey brown orange}
9768 set diffcolors {red "#00a000" blue}
9771 set selectbgcolor gray85
9773 set circlecolors {white blue gray blue blue}
9775 ## For msgcat loading, first locate the installation location.
9776 if { [info exists ::env(GITK_MSGSDIR)] } {
9777 ## Msgsdir was manually set in the environment.
9778 set gitk_msgsdir $::env(GITK_MSGSDIR)
9780 ## Let's guess the prefix from argv0.
9781 set gitk_prefix [file dirname [file dirname [file normalize $argv0]]]
9782 set gitk_libdir [file join $gitk_prefix share gitk lib]
9783 set gitk_msgsdir [file join $gitk_libdir msgs]
9787 ## Internationalization (i18n) through msgcat and gettext. See
9788 ## http://www.gnu.org/software/gettext/manual/html_node/Tcl.html
9789 package require msgcat
9790 namespace import ::msgcat::mc
9791 ## And eventually load the actual message catalog
9792 ::msgcat::mcload $gitk_msgsdir
9794 catch {source ~/.gitk}
9796 font create optionfont -family sans-serif -size -12
9798 parsefont mainfont $mainfont
9799 eval font create mainfont [fontflags mainfont]
9800 eval font create mainfontbold [fontflags mainfont 1]
9802 parsefont textfont $textfont
9803 eval font create textfont [fontflags textfont]
9804 eval font create textfontbold [fontflags textfont 1]
9806 parsefont uifont $uifont
9807 eval font create uifont [fontflags uifont]
9811 # check that we can find a .git directory somewhere...
9812 if {[catch {set gitdir [gitdir]}]} {
9813 show_error {} . [mc "Cannot find a git repository here."]
9816 if {![file isdirectory $gitdir]} {
9817 show_error {} . [mc "Cannot find the git directory \"%s\"." $gitdir]
9822 set cmdline_files {}
9824 set revtreeargscmd {}
9826 switch -glob -- $arg {
9829 set cmdline_files [lrange $argv [expr {$i + 1}] end]
9833 set revtreeargscmd [string range $arg 10 end]
9836 lappend revtreeargs $arg
9842 if {$i >= [llength $argv] && $revtreeargs ne {}} {
9843 # no -- on command line, but some arguments (other than --argscmd)
9845 set f [eval exec git rev-parse --no-revs --no-flags $revtreeargs]
9846 set cmdline_files [split $f "\n"]
9847 set n [llength $cmdline_files]
9848 set revtreeargs [lrange $revtreeargs 0 end-$n]
9849 # Unfortunately git rev-parse doesn't produce an error when
9850 # something is both a revision and a filename. To be consistent
9851 # with git log and git rev-list, check revtreeargs for filenames.
9852 foreach arg $revtreeargs {
9853 if {[file exists $arg]} {
9854 show_error {} . [mc "Ambiguous argument '%s': both revision\
9860 # unfortunately we get both stdout and stderr in $err,
9861 # so look for "fatal:".
9862 set i [string first "fatal:" $err]
9864 set err [string range $err [expr {$i + 6}] end]
9866 show_error {} . "[mc "Bad arguments to gitk:"]\n$err"
9871 set nullid "0000000000000000000000000000000000000000"
9872 set nullid2 "0000000000000000000000000000000000000001"
9873 set nullfile "/dev/null"
9875 set have_tk85 [expr {[package vcompare $tk_version "8.5"] >= 0}]
9882 set highlight_paths {}
9884 set searchdirn -forwards
9888 set markingmatches 0
9889 set linkentercount 0
9890 set need_redisplay 0
9897 set selectedhlview [mc "None"]
9898 set highlight_related [mc "None"]
9899 set highlight_files {}
9903 set viewargscmd(0) {}
9913 set isworktree [expr {[exec git rev-parse --is-inside-work-tree] == "true"}]
9916 # wait for the window to become visible
9918 wm title . "[file tail $argv0]: [file tail [pwd]]"
9921 if {$cmdline_files ne {} || $revtreeargs ne {} || $revtreeargscmd ne {}} {
9922 # create a view for the files/dirs specified on the command line
9926 set viewname(1) [mc "Command line"]
9927 set viewfiles(1) $cmdline_files
9928 set viewargs(1) $revtreeargs
9929 set viewargscmd(1) $revtreeargscmd
9933 .bar.view entryconf [mc "Edit view..."] -state normal
9934 .bar.view entryconf [mc "Delete view"] -state normal
9937 if {[info exists permviews]} {
9938 foreach v $permviews {
9941 set viewname($n) [lindex $v 0]
9942 set viewfiles($n) [lindex $v 1]
9943 set viewargs($n) [lindex $v 2]
9944 set viewargscmd($n) [lindex $v 3]