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.
25 global isonrunq runq currunq
28 if {[info exists isonrunq
($script)]} return
29 if {$runq eq
{} && ![info exists currunq
]} {
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
{}
44 if {$runq eq
{} && ![info exists currunq
]} {
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]
63 global isonrunq runq currunq
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 currunq
[lindex
$runq 0]
71 set runq
[lrange
$runq 1 end
]
72 set repeat
[eval $script]
74 set t1
[clock clicks
-milliseconds]
75 set t
[expr {$t1 - $t0}]
76 if {$repeat ne
{} && $repeat} {
77 if {$fd eq
{} ||
$repeat == 2} {
78 # script returns 1 if it wants to be readded
79 # file readers return 2 if they could do more straight away
80 lappend runq
[list
$fd $script]
82 fileevent
$fd readable
[list filereadable
$fd $script]
84 } elseif
{$fd eq
{}} {
85 unset isonrunq
($script)
88 if {$t1 - $tstart >= 80} break
95 proc reg_instance
{fd
} {
96 global commfd leftover loginstance
98 set i
[incr loginstance
]
104 proc unmerged_files
{files
} {
107 # find the list of unmerged files
111 set fd
[open
"| git ls-files -u" r
]
113 show_error
{} .
"[mc "Couldn
't get list of unmerged files:"] $err"
116 while {[gets $fd line] >= 0} {
117 set i [string first "\t" $line]
119 set fname [string range $line [expr {$i+1}] end]
120 if {[lsearch -exact $mlist $fname] >= 0} continue
122 if {$files eq {} || [path_filter $files $fname]} {
130 proc parseviewargs {n arglist} {
131 global vdatemode vmergeonly vflags vdflags vrevs vfiltered vorigargs
139 set origargs $arglist
143 foreach arg $arglist {
150 switch -glob -- $arg {
154 # remove from origargs in case we hit an unknown option
155 set origargs [lreplace $origargs $i $i]
158 # These request or affect diff output, which we don't want.
159 # Some could be used to set our defaults for diff display.
161 "--no-renames" - "--full-index" - "--binary" - "--abbrev=*" -
162 "--find-copies-harder" - "-l*" - "--ext-diff" - "--no-ext-diff" -
163 "--src-prefix=*" - "--dst-prefix=*" - "--no-prefix" -
164 "-O*" - "--text" - "--full-diff" - "--ignore-space-at-eol" -
165 "--ignore-space-change" - "-U*" - "--unified=*" {
166 lappend diffargs
$arg
168 # These cause our parsing of git log's output to fail, or else
169 # they're options we want to set ourselves, so ignore them.
170 "--raw" - "--patch-with-raw" - "--patch-with-stat" -
171 "--name-only" - "--name-status" - "--color" - "--color-words" -
172 "--log-size" - "--pretty=*" - "--decorate" - "--abbrev-commit" -
173 "--cc" - "-z" - "--header" - "--parents" - "--boundary" -
174 "--no-color" - "-g" - "--walk-reflogs" - "--no-walk" -
175 "--timestamp" - "relative-date" - "--date=*" - "--stdin" -
176 "--objects" - "--objects-edge" - "--reverse" {
178 # These are harmless, and some are even useful
179 "--stat=*" - "--numstat" - "--shortstat" - "--summary" -
180 "--check" - "--exit-code" - "--quiet" - "--topo-order" -
181 "--full-history" - "--dense" - "--sparse" -
182 "--follow" - "--left-right" - "--encoding=*" {
185 # These mean that we get a subset of the commits
186 "--diff-filter=*" - "--no-merges" - "--unpacked" -
187 "--max-count=*" - "--skip=*" - "--since=*" - "--after=*" -
188 "--until=*" - "--before=*" - "--max-age=*" - "--min-age=*" -
189 "--author=*" - "--committer=*" - "--grep=*" - "-[iE]" -
190 "--remove-empty" - "--first-parent" - "--cherry-pick" -
191 "-S*" - "--pickaxe-all" - "--pickaxe-regex" - {
195 # This appears to be the only one that has a value as a
196 # separate word following it
203 set notflag
[expr {!$notflag}]
211 # git rev-parse doesn't understand --merge
212 lappend revargs
--gitk-symmetric-diff-marker MERGE_HEAD...HEAD
214 # Other flag arguments including -<n>
216 if {[string is digit
-strict [string range
$arg 1 end
]]} {
219 # a flag argument that we don't recognize;
220 # that means we can't optimize
225 # Non-flag arguments specify commits or ranges of commits
227 if {[string match
"*...*" $arg]} {
228 lappend revargs
--gitk-symmetric-diff-marker
234 set vdflags
($n) $diffargs
235 set vflags
($n) $glflags
236 set vrevs
($n) $revargs
237 set vfiltered
($n) $filtered
238 set vorigargs
($n) $origargs
242 proc parseviewrevs
{view revs
} {
243 global vposids vnegids
248 if {[catch
{set ids
[eval exec git rev-parse
$revs]} err
]} {
249 # we get stdout followed by stderr in $err
250 # for an unknown rev, git rev-parse echoes it and then errors out
251 set errlines
[split $err "\n"]
253 for {set l
0} {$l < [llength
$errlines]} {incr l
} {
254 set line
[lindex
$errlines $l]
255 if {!([string length
$line] == 40 && [string is xdigit
$line])} {
256 if {[string match
"fatal:*" $line]} {
257 if {[string match
"fatal: ambiguous argument*" $line]
259 if {[llength
$badrev] == 1} {
260 set err
"unknown revision $badrev"
262 set err
"unknown revisions: [join $badrev ", "]"
265 set err
[join [lrange
$errlines $l end
] "\n"]
272 error_popup
"Error parsing revisions: $err"
279 foreach id
[split $ids "\n"] {
280 if {$id eq
"--gitk-symmetric-diff-marker"} {
282 } elseif
{[string match
"^*" $id]} {
289 lappend neg
[string range
$id 1 end
]
294 lset ret end
[lindex
$ret end
]...
$id
300 set vposids
($view) $pos
301 set vnegids
($view) $neg
305 # Start off a git log process and arrange to read its output
306 proc start_rev_list
{view
} {
307 global startmsecs commitidx viewcomplete curview
309 global viewargs viewargscmd viewfiles vfilelimit
310 global showlocalchanges commitinterest
311 global viewactive viewinstances vmergeonly
313 global vcanopt vflags vrevs vorigargs
315 set startmsecs
[clock clicks
-milliseconds]
316 set commitidx
($view) 0
317 # these are set this way for the error exits
318 set viewcomplete
($view) 1
319 set viewactive
($view) 0
322 set args
$viewargs($view)
323 if {$viewargscmd($view) ne
{}} {
325 set str
[exec sh
-c $viewargscmd($view)]
327 error_popup
"Error executing --argscmd command: $err"
330 set args
[concat
$args [split $str "\n"]]
332 set vcanopt
($view) [parseviewargs
$view $args]
334 set files
$viewfiles($view)
335 if {$vmergeonly($view)} {
336 set files
[unmerged_files
$files]
339 if {$nr_unmerged == 0} {
340 error_popup
[mc
"No files selected: --merge specified but\
341 no files are unmerged."]
343 error_popup
[mc
"No files selected: --merge specified but\
344 no unmerged files are within file limit."]
349 set vfilelimit
($view) $files
351 if {$vcanopt($view)} {
352 set revs
[parseviewrevs
$view $vrevs($view)]
356 set args
[concat
$vflags($view) $revs]
358 set args
$vorigargs($view)
362 set fd
[open
[concat | git log
--no-color -z --pretty=raw
--parents \
363 --boundary $args "--" $files] r
]
365 error_popup
"[mc "Error executing git log
:"] $err"
368 set i
[reg_instance
$fd]
369 set viewinstances
($view) [list
$i]
370 if {$showlocalchanges && $mainheadid ne
{}} {
371 lappend commitinterest
($mainheadid) {dodiffindex
}
373 fconfigure
$fd -blocking 0 -translation lf
-eofchar {}
374 if {$tclencoding != {}} {
375 fconfigure
$fd -encoding $tclencoding
377 filerun
$fd [list getcommitlines
$fd $i $view 0]
378 nowbusy
$view [mc
"Reading"]
379 set viewcomplete
($view) 0
380 set viewactive
($view) 1
384 proc stop_instance
{inst
} {
385 global commfd leftover
387 set fd
$commfd($inst)
391 if {$
::tcl_platform
(platform
) eq
{windows
}} {
400 unset leftover
($inst)
403 proc stop_backends
{} {
406 foreach inst
[array names commfd
] {
411 proc stop_rev_list
{view
} {
414 foreach inst
$viewinstances($view) {
417 set viewinstances
($view) {}
420 proc reset_pending_select
{selid
} {
421 global pending_select mainheadid
424 set pending_select
$selid
426 set pending_select
$mainheadid
430 proc getcommits
{selid
} {
431 global canv curview need_redisplay viewactive
434 if {[start_rev_list
$curview]} {
435 reset_pending_select
$selid
436 show_status
[mc
"Reading commits..."]
439 show_status
[mc
"No commits selected"]
443 proc updatecommits
{} {
444 global curview vcanopt vorigargs vfilelimit viewinstances
445 global viewactive viewcomplete tclencoding
446 global startmsecs showneartags showlocalchanges
447 global mainheadid pending_select
449 global varcid vposids vnegids vflags vrevs
451 set isworktree
[expr {[exec git rev-parse
--is-inside-work-tree] == "true"}]
452 set oldmainid
$mainheadid
454 if {$showlocalchanges} {
455 if {$mainheadid ne
$oldmainid} {
458 if {[commitinview
$mainheadid $curview]} {
463 if {$vcanopt($view)} {
464 set oldpos
$vposids($view)
465 set oldneg
$vnegids($view)
466 set revs
[parseviewrevs
$view $vrevs($view)]
470 # note: getting the delta when negative refs change is hard,
471 # and could require multiple git log invocations, so in that
472 # case we ask git log for all the commits (not just the delta)
473 if {$oldneg eq
$vnegids($view)} {
476 # take out positive refs that we asked for before or
477 # that we have already seen
479 if {[string length
$rev] == 40} {
480 if {[lsearch
-exact $oldpos $rev] < 0
481 && ![info exists varcid
($view,$rev)]} {
486 lappend
$newrevs $rev
489 if {$npos == 0} return
491 set vposids
($view) [lsort
-unique [concat
$oldpos $vposids($view)]]
493 set args
[concat
$vflags($view) $revs --not $oldpos]
495 set args
$vorigargs($view)
498 set fd
[open
[concat | git log
--no-color -z --pretty=raw
--parents \
499 --boundary $args "--" $vfilelimit($view)] r
]
501 error_popup
"Error executing git log: $err"
504 if {$viewactive($view) == 0} {
505 set startmsecs
[clock clicks
-milliseconds]
507 set i
[reg_instance
$fd]
508 lappend viewinstances
($view) $i
509 fconfigure
$fd -blocking 0 -translation lf
-eofchar {}
510 if {$tclencoding != {}} {
511 fconfigure
$fd -encoding $tclencoding
513 filerun
$fd [list getcommitlines
$fd $i $view 1]
514 incr viewactive
($view)
515 set viewcomplete
($view) 0
516 reset_pending_select
{}
517 nowbusy
$view "Reading"
523 proc reloadcommits
{} {
524 global curview viewcomplete selectedline currentid thickerline
525 global showneartags treediffs commitinterest cached_commitrow
529 if {$selectedline ne
{}} {
533 if {!$viewcomplete($curview)} {
534 stop_rev_list
$curview
538 catch
{unset currentid
}
539 catch
{unset thickerline
}
540 catch
{unset treediffs
}
547 catch
{unset commitinterest
}
548 catch
{unset cached_commitrow
}
549 catch
{unset targetid
}
555 # This makes a string representation of a positive integer which
556 # sorts as a string in numerical order
559 return [format
"%x" $n]
560 } elseif
{$n < 256} {
561 return [format
"x%.2x" $n]
562 } elseif
{$n < 65536} {
563 return [format
"y%.4x" $n]
565 return [format
"z%.8x" $n]
568 # Procedures used in reordering commits from git log (without
569 # --topo-order) into the order for display.
571 proc varcinit
{view
} {
572 global varcstart vupptr vdownptr vleftptr vbackptr varctok varcrow
573 global vtokmod varcmod vrowmod varcix vlastins
575 set varcstart
($view) {{}}
576 set vupptr
($view) {0}
577 set vdownptr
($view) {0}
578 set vleftptr
($view) {0}
579 set vbackptr
($view) {0}
580 set varctok
($view) {{}}
581 set varcrow
($view) {{}}
582 set vtokmod
($view) {}
585 set varcix
($view) {{}}
586 set vlastins
($view) {0}
589 proc resetvarcs
{view
} {
590 global varcid varccommits parents children vseedcount ordertok
592 foreach vid
[array names varcid
$view,*] {
597 # some commits might have children but haven't been seen yet
598 foreach vid
[array names children
$view,*] {
601 foreach va
[array names varccommits
$view,*] {
602 unset varccommits
($va)
604 foreach vd
[array names vseedcount
$view,*] {
605 unset vseedcount
($vd)
607 catch
{unset ordertok
}
610 # returns a list of the commits with no children
612 global vdownptr vleftptr varcstart
615 set a
[lindex
$vdownptr($v) 0]
617 lappend ret
[lindex
$varcstart($v) $a]
618 set a
[lindex
$vleftptr($v) $a]
623 proc newvarc
{view id
} {
624 global varcid varctok parents children vdatemode
625 global vupptr vdownptr vleftptr vbackptr varcrow varcix varcstart
626 global commitdata commitinfo vseedcount varccommits vlastins
628 set a
[llength
$varctok($view)]
630 if {[llength
$children($vid)] == 0 ||
$vdatemode($view)} {
631 if {![info exists commitinfo
($id)]} {
632 parsecommit
$id $commitdata($id) 1
634 set cdate
[lindex
$commitinfo($id) 4]
635 if {![string is integer
-strict $cdate]} {
638 if {![info exists vseedcount
($view,$cdate)]} {
639 set vseedcount
($view,$cdate) -1
641 set c
[incr vseedcount
($view,$cdate)]
642 set cdate
[expr {$cdate ^
0xffffffff}]
643 set tok
"s[strrep $cdate][strrep $c]"
648 if {[llength
$children($vid)] > 0} {
649 set kid
[lindex
$children($vid) end
]
650 set k
$varcid($view,$kid)
651 if {[string compare
[lindex
$varctok($view) $k] $tok] > 0} {
654 set tok
[lindex
$varctok($view) $k]
658 set i
[lsearch
-exact $parents($view,$ki) $id]
659 set j
[expr {[llength
$parents($view,$ki)] - 1 - $i}]
660 append tok
[strrep
$j]
662 set c
[lindex
$vlastins($view) $ka]
663 if {$c == 0 ||
[string compare
$tok [lindex
$varctok($view) $c]] < 0} {
665 set b
[lindex
$vdownptr($view) $ka]
667 set b
[lindex
$vleftptr($view) $c]
669 while {$b != 0 && [string compare
$tok [lindex
$varctok($view) $b]] >= 0} {
671 set b
[lindex
$vleftptr($view) $c]
674 lset vdownptr
($view) $ka $a
675 lappend vbackptr
($view) 0
677 lset vleftptr
($view) $c $a
678 lappend vbackptr
($view) $c
680 lset vlastins
($view) $ka $a
681 lappend vupptr
($view) $ka
682 lappend vleftptr
($view) $b
684 lset vbackptr
($view) $b $a
686 lappend varctok
($view) $tok
687 lappend varcstart
($view) $id
688 lappend vdownptr
($view) 0
689 lappend varcrow
($view) {}
690 lappend varcix
($view) {}
691 set varccommits
($view,$a) {}
692 lappend vlastins
($view) 0
696 proc splitvarc
{p v
} {
697 global varcid varcstart varccommits varctok
698 global vupptr vdownptr vleftptr vbackptr varcix varcrow vlastins
700 set oa
$varcid($v,$p)
701 set ac
$varccommits($v,$oa)
702 set i
[lsearch
-exact $varccommits($v,$oa) $p]
704 set na
[llength
$varctok($v)]
705 # "%" sorts before "0"...
706 set tok
"[lindex $varctok($v) $oa]%[strrep $i]"
707 lappend varctok
($v) $tok
708 lappend varcrow
($v) {}
709 lappend varcix
($v) {}
710 set varccommits
($v,$oa) [lrange
$ac 0 [expr {$i - 1}]]
711 set varccommits
($v,$na) [lrange
$ac $i end
]
712 lappend varcstart
($v) $p
713 foreach id
$varccommits($v,$na) {
714 set varcid
($v,$id) $na
716 lappend vdownptr
($v) [lindex
$vdownptr($v) $oa]
717 lappend vlastins
($v) [lindex
$vlastins($v) $oa]
718 lset vdownptr
($v) $oa $na
719 lset vlastins
($v) $oa 0
720 lappend vupptr
($v) $oa
721 lappend vleftptr
($v) 0
722 lappend vbackptr
($v) 0
723 for {set b
[lindex
$vdownptr($v) $na]} {$b != 0} {set b
[lindex
$vleftptr($v) $b]} {
724 lset vupptr
($v) $b $na
728 proc renumbervarc
{a v
} {
729 global parents children varctok varcstart varccommits
730 global vupptr vdownptr vleftptr vbackptr vlastins varcid vtokmod vdatemode
732 set t1
[clock clicks
-milliseconds]
738 if {[info exists isrelated
($a)]} {
740 set id
[lindex
$varccommits($v,$a) end
]
741 foreach p
$parents($v,$id) {
742 if {[info exists varcid
($v,$p)]} {
743 set isrelated
($varcid($v,$p)) 1
748 set b
[lindex
$vdownptr($v) $a]
751 set b
[lindex
$vleftptr($v) $a]
753 set a
[lindex
$vupptr($v) $a]
759 if {![info exists kidchanged
($a)]} continue
760 set id
[lindex
$varcstart($v) $a]
761 if {[llength
$children($v,$id)] > 1} {
762 set children
($v,$id) [lsort
-command [list vtokcmp
$v] \
765 set oldtok
[lindex
$varctok($v) $a]
766 if {!$vdatemode($v)} {
772 set kid
[last_real_child
$v,$id]
774 set k
$varcid($v,$kid)
775 if {[string compare
[lindex
$varctok($v) $k] $tok] > 0} {
778 set tok
[lindex
$varctok($v) $k]
782 set i
[lsearch
-exact $parents($v,$ki) $id]
783 set j
[expr {[llength
$parents($v,$ki)] - 1 - $i}]
784 append tok
[strrep
$j]
786 if {$tok eq
$oldtok} {
789 set id
[lindex
$varccommits($v,$a) end
]
790 foreach p
$parents($v,$id) {
791 if {[info exists varcid
($v,$p)]} {
792 set kidchanged
($varcid($v,$p)) 1
797 lset varctok
($v) $a $tok
798 set b
[lindex
$vupptr($v) $a]
800 if {[string compare
[lindex
$varctok($v) $ka] $vtokmod($v)] < 0} {
803 if {[string compare
[lindex
$varctok($v) $b] $vtokmod($v)] < 0} {
806 set c
[lindex
$vbackptr($v) $a]
807 set d
[lindex
$vleftptr($v) $a]
809 lset vdownptr
($v) $b $d
811 lset vleftptr
($v) $c $d
814 lset vbackptr
($v) $d $c
816 if {[lindex
$vlastins($v) $b] == $a} {
817 lset vlastins
($v) $b $c
819 lset vupptr
($v) $a $ka
820 set c
[lindex
$vlastins($v) $ka]
822 [string compare
$tok [lindex
$varctok($v) $c]] < 0} {
824 set b
[lindex
$vdownptr($v) $ka]
826 set b
[lindex
$vleftptr($v) $c]
829 [string compare
$tok [lindex
$varctok($v) $b]] >= 0} {
831 set b
[lindex
$vleftptr($v) $c]
834 lset vdownptr
($v) $ka $a
835 lset vbackptr
($v) $a 0
837 lset vleftptr
($v) $c $a
838 lset vbackptr
($v) $a $c
840 lset vleftptr
($v) $a $b
842 lset vbackptr
($v) $b $a
844 lset vlastins
($v) $ka $a
847 foreach id
[array names sortkids
] {
848 if {[llength
$children($v,$id)] > 1} {
849 set children
($v,$id) [lsort
-command [list vtokcmp
$v] \
853 set t2
[clock clicks
-milliseconds]
854 #puts "renumbervarc did [llength $todo] of $ntot arcs in [expr {$t2-$t1}]ms"
857 # Fix up the graph after we have found out that in view $v,
858 # $p (a commit that we have already seen) is actually the parent
859 # of the last commit in arc $a.
860 proc fix_reversal
{p a v
} {
861 global varcid varcstart varctok vupptr
863 set pa
$varcid($v,$p)
864 if {$p ne
[lindex
$varcstart($v) $pa]} {
866 set pa
$varcid($v,$p)
868 # seeds always need to be renumbered
869 if {[lindex
$vupptr($v) $pa] == 0 ||
870 [string compare
[lindex
$varctok($v) $a] \
871 [lindex
$varctok($v) $pa]] > 0} {
876 proc insertrow
{id p v
} {
877 global cmitlisted children parents varcid varctok vtokmod
878 global varccommits ordertok commitidx numcommits curview
879 global targetid targetrow
883 set cmitlisted
($vid) 1
884 set children
($vid) {}
885 set parents
($vid) [list
$p]
886 set a
[newvarc
$v $id]
888 if {[string compare
[lindex
$varctok($v) $a] $vtokmod($v)] < 0} {
891 lappend varccommits
($v,$a) $id
893 if {[llength
[lappend children
($vp) $id]] > 1} {
894 set children
($vp) [lsort
-command [list vtokcmp
$v] $children($vp)]
895 catch
{unset ordertok
}
897 fix_reversal
$p $a $v
899 if {$v == $curview} {
900 set numcommits
$commitidx($v)
902 if {[info exists targetid
]} {
903 if {![comes_before
$targetid $p]} {
910 proc insertfakerow
{id p
} {
911 global varcid varccommits parents children cmitlisted
912 global commitidx varctok vtokmod targetid targetrow curview numcommits
916 set i
[lsearch
-exact $varccommits($v,$a) $p]
918 puts
"oops: insertfakerow can't find [shortids $p] on arc $a"
921 set children
($v,$id) {}
922 set parents
($v,$id) [list
$p]
923 set varcid
($v,$id) $a
924 lappend children
($v,$p) $id
925 set cmitlisted
($v,$id) 1
926 set numcommits
[incr commitidx
($v)]
927 # note we deliberately don't update varcstart($v) even if $i == 0
928 set varccommits
($v,$a) [linsert
$varccommits($v,$a) $i $id]
930 if {[info exists targetid
]} {
931 if {![comes_before
$targetid $p]} {
939 proc removefakerow
{id
} {
940 global varcid varccommits parents children commitidx
941 global varctok vtokmod cmitlisted currentid selectedline
942 global targetid curview numcommits
945 if {[llength
$parents($v,$id)] != 1} {
946 puts
"oops: removefakerow [shortids $id] has [llength $parents($v,$id)] parents"
949 set p
[lindex
$parents($v,$id) 0]
950 set a
$varcid($v,$id)
951 set i
[lsearch
-exact $varccommits($v,$a) $id]
953 puts
"oops: removefakerow can't find [shortids $id] on arc $a"
957 set varccommits
($v,$a) [lreplace
$varccommits($v,$a) $i $i]
958 unset parents
($v,$id)
959 unset children
($v,$id)
960 unset cmitlisted
($v,$id)
961 set numcommits
[incr commitidx
($v) -1]
962 set j
[lsearch
-exact $children($v,$p) $id]
964 set children
($v,$p) [lreplace
$children($v,$p) $j $j]
967 if {[info exist currentid
] && $id eq
$currentid} {
971 if {[info exists targetid
] && $targetid eq
$id} {
978 proc first_real_child
{vp
} {
979 global children nullid nullid2
981 foreach id
$children($vp) {
982 if {$id ne
$nullid && $id ne
$nullid2} {
989 proc last_real_child
{vp
} {
990 global children nullid nullid2
992 set kids
$children($vp)
993 for {set i
[llength
$kids]} {[incr i
-1] >= 0} {} {
994 set id
[lindex
$kids $i]
995 if {$id ne
$nullid && $id ne
$nullid2} {
1002 proc vtokcmp
{v a b
} {
1003 global varctok varcid
1005 return [string compare
[lindex
$varctok($v) $varcid($v,$a)] \
1006 [lindex
$varctok($v) $varcid($v,$b)]]
1009 # This assumes that if lim is not given, the caller has checked that
1010 # arc a's token is less than $vtokmod($v)
1011 proc modify_arc
{v a
{lim
{}}} {
1012 global varctok vtokmod varcmod varcrow vupptr curview vrowmod varccommits
1015 set c
[string compare
[lindex
$varctok($v) $a] $vtokmod($v)]
1018 set r
[lindex
$varcrow($v) $a]
1019 if {$r ne
{} && $vrowmod($v) <= $r + $lim} return
1022 set vtokmod
($v) [lindex
$varctok($v) $a]
1024 if {$v == $curview} {
1025 while {$a != 0 && [lindex
$varcrow($v) $a] eq
{}} {
1026 set a
[lindex
$vupptr($v) $a]
1032 set lim
[llength
$varccommits($v,$a)]
1034 set r
[expr {[lindex
$varcrow($v) $a] + $lim}]
1041 proc update_arcrows
{v
} {
1042 global vtokmod varcmod vrowmod varcrow commitidx currentid selectedline
1043 global varcid vrownum varcorder varcix varccommits
1044 global vupptr vdownptr vleftptr varctok
1045 global displayorder parentlist curview cached_commitrow
1047 if {$vrowmod($v) == $commitidx($v)} return
1048 if {$v == $curview} {
1049 if {[llength
$displayorder] > $vrowmod($v)} {
1050 set displayorder
[lrange
$displayorder 0 [expr {$vrowmod($v) - 1}]]
1051 set parentlist
[lrange
$parentlist 0 [expr {$vrowmod($v) - 1}]]
1053 catch
{unset cached_commitrow
}
1055 set narctot
[expr {[llength
$varctok($v)] - 1}]
1057 while {$a != 0 && [lindex
$varcix($v) $a] eq
{}} {
1058 # go up the tree until we find something that has a row number,
1059 # or we get to a seed
1060 set a
[lindex
$vupptr($v) $a]
1063 set a
[lindex
$vdownptr($v) 0]
1066 set varcorder
($v) [list
$a]
1067 lset varcix
($v) $a 0
1068 lset varcrow
($v) $a 0
1072 set arcn
[lindex
$varcix($v) $a]
1073 if {[llength
$vrownum($v)] > $arcn + 1} {
1074 set vrownum
($v) [lrange
$vrownum($v) 0 $arcn]
1075 set varcorder
($v) [lrange
$varcorder($v) 0 $arcn]
1077 set row
[lindex
$varcrow($v) $a]
1081 incr row
[llength
$varccommits($v,$a)]
1082 # go down if possible
1083 set b
[lindex
$vdownptr($v) $a]
1085 # if not, go left, or go up until we can go left
1087 set b
[lindex
$vleftptr($v) $a]
1089 set a
[lindex
$vupptr($v) $a]
1095 lappend vrownum
($v) $row
1096 lappend varcorder
($v) $a
1097 lset varcix
($v) $a $arcn
1098 lset varcrow
($v) $a $row
1100 set vtokmod
($v) [lindex
$varctok($v) $p]
1102 set vrowmod
($v) $row
1103 if {[info exists currentid
]} {
1104 set selectedline
[rowofcommit
$currentid]
1108 # Test whether view $v contains commit $id
1109 proc commitinview
{id v
} {
1112 return [info exists varcid
($v,$id)]
1115 # Return the row number for commit $id in the current view
1116 proc rowofcommit
{id
} {
1117 global varcid varccommits varcrow curview cached_commitrow
1118 global varctok vtokmod
1121 if {![info exists varcid
($v,$id)]} {
1122 puts
"oops rowofcommit no arc for [shortids $id]"
1125 set a
$varcid($v,$id)
1126 if {[string compare
[lindex
$varctok($v) $a] $vtokmod($v)] >= 0} {
1129 if {[info exists cached_commitrow
($id)]} {
1130 return $cached_commitrow($id)
1132 set i
[lsearch
-exact $varccommits($v,$a) $id]
1134 puts
"oops didn't find commit [shortids $id] in arc $a"
1137 incr i
[lindex
$varcrow($v) $a]
1138 set cached_commitrow
($id) $i
1142 # Returns 1 if a is on an earlier row than b, otherwise 0
1143 proc comes_before
{a b
} {
1144 global varcid varctok curview
1147 if {$a eq
$b ||
![info exists varcid
($v,$a)] || \
1148 ![info exists varcid
($v,$b)]} {
1151 if {$varcid($v,$a) != $varcid($v,$b)} {
1152 return [expr {[string compare
[lindex
$varctok($v) $varcid($v,$a)] \
1153 [lindex
$varctok($v) $varcid($v,$b)]] < 0}]
1155 return [expr {[rowofcommit
$a] < [rowofcommit
$b]}]
1158 proc bsearch
{l elt
} {
1159 if {[llength
$l] == 0 ||
$elt <= [lindex
$l 0]} {
1164 while {$hi - $lo > 1} {
1165 set mid
[expr {int
(($lo + $hi) / 2)}]
1166 set t
[lindex
$l $mid]
1169 } elseif
{$elt > $t} {
1178 # Make sure rows $start..$end-1 are valid in displayorder and parentlist
1179 proc make_disporder
{start end
} {
1180 global vrownum curview commitidx displayorder parentlist
1181 global varccommits varcorder parents vrowmod varcrow
1182 global d_valid_start d_valid_end
1184 if {$end > $vrowmod($curview)} {
1185 update_arcrows
$curview
1187 set ai
[bsearch
$vrownum($curview) $start]
1188 set start
[lindex
$vrownum($curview) $ai]
1189 set narc
[llength
$vrownum($curview)]
1190 for {set r
$start} {$ai < $narc && $r < $end} {incr ai
} {
1191 set a
[lindex
$varcorder($curview) $ai]
1192 set l
[llength
$displayorder]
1193 set al
[llength
$varccommits($curview,$a)]
1194 if {$l < $r + $al} {
1196 set pad
[ntimes
[expr {$r - $l}] {}]
1197 set displayorder
[concat
$displayorder $pad]
1198 set parentlist
[concat
$parentlist $pad]
1199 } elseif
{$l > $r} {
1200 set displayorder
[lrange
$displayorder 0 [expr {$r - 1}]]
1201 set parentlist
[lrange
$parentlist 0 [expr {$r - 1}]]
1203 foreach id
$varccommits($curview,$a) {
1204 lappend displayorder
$id
1205 lappend parentlist
$parents($curview,$id)
1207 } elseif
{[lindex
$displayorder [expr {$r + $al - 1}]] eq
{}} {
1209 foreach id
$varccommits($curview,$a) {
1210 lset displayorder
$i $id
1211 lset parentlist
$i $parents($curview,$id)
1219 proc commitonrow
{row
} {
1222 set id
[lindex
$displayorder $row]
1224 make_disporder
$row [expr {$row + 1}]
1225 set id
[lindex
$displayorder $row]
1230 proc closevarcs
{v
} {
1231 global varctok varccommits varcid parents children
1232 global cmitlisted commitidx commitinterest vtokmod
1234 set missing_parents
0
1236 set narcs
[llength
$varctok($v)]
1237 for {set a
1} {$a < $narcs} {incr a
} {
1238 set id
[lindex
$varccommits($v,$a) end
]
1239 foreach p
$parents($v,$id) {
1240 if {[info exists varcid
($v,$p)]} continue
1241 # add p as a new commit
1242 incr missing_parents
1243 set cmitlisted
($v,$p) 0
1244 set parents
($v,$p) {}
1245 if {[llength
$children($v,$p)] == 1 &&
1246 [llength
$parents($v,$id)] == 1} {
1249 set b
[newvarc
$v $p]
1251 set varcid
($v,$p) $b
1252 if {[string compare
[lindex
$varctok($v) $b] $vtokmod($v)] < 0} {
1255 lappend varccommits
($v,$b) $p
1257 if {[info exists commitinterest
($p)]} {
1258 foreach
script $commitinterest($p) {
1259 lappend scripts
[string map
[list
"%I" $p] $script]
1261 unset commitinterest
($id)
1265 if {$missing_parents > 0} {
1266 foreach s
$scripts {
1272 # Use $rwid as a substitute for $id, i.e. reparent $id's children to $rwid
1273 # Assumes we already have an arc for $rwid.
1274 proc rewrite_commit
{v id rwid
} {
1275 global children parents varcid varctok vtokmod varccommits
1277 foreach ch
$children($v,$id) {
1278 # make $rwid be $ch's parent in place of $id
1279 set i
[lsearch
-exact $parents($v,$ch) $id]
1281 puts
"oops rewrite_commit didn't find $id in parent list for $ch"
1283 set parents
($v,$ch) [lreplace
$parents($v,$ch) $i $i $rwid]
1284 # add $ch to $rwid's children and sort the list if necessary
1285 if {[llength
[lappend children
($v,$rwid) $ch]] > 1} {
1286 set children
($v,$rwid) [lsort
-command [list vtokcmp
$v] \
1287 $children($v,$rwid)]
1289 # fix the graph after joining $id to $rwid
1290 set a
$varcid($v,$ch)
1291 fix_reversal
$rwid $a $v
1292 # parentlist is wrong for the last element of arc $a
1293 # even if displayorder is right, hence the 3rd arg here
1294 modify_arc
$v $a [expr {[llength
$varccommits($v,$a)] - 1}]
1298 proc getcommitlines
{fd inst view updating
} {
1299 global cmitlisted commitinterest leftover
1300 global commitidx commitdata vdatemode
1301 global parents children curview hlview
1302 global idpending ordertok
1303 global varccommits varcid varctok vtokmod vfilelimit
1305 set stuff
[read $fd 500000]
1306 # git log doesn't terminate the last commit with a null...
1307 if {$stuff == {} && $leftover($inst) ne
{} && [eof
$fd]} {
1314 global commfd viewcomplete viewactive viewname
1315 global viewinstances
1317 set i
[lsearch
-exact $viewinstances($view) $inst]
1319 set viewinstances
($view) [lreplace
$viewinstances($view) $i $i]
1321 # set it blocking so we wait for the process to terminate
1322 fconfigure
$fd -blocking 1
1323 if {[catch
{close
$fd} err
]} {
1325 if {$view != $curview} {
1326 set fv
" for the \"$viewname($view)\" view"
1328 if {[string range
$err 0 4] == "usage"} {
1329 set err
"Gitk: error reading commits$fv:\
1330 bad arguments to git log."
1331 if {$viewname($view) eq
"Command line"} {
1333 " (Note: arguments to gitk are passed to git log\
1334 to allow selection of commits to be displayed.)"
1337 set err
"Error reading commits$fv: $err"
1341 if {[incr viewactive
($view) -1] <= 0} {
1342 set viewcomplete
($view) 1
1343 # Check if we have seen any ids listed as parents that haven't
1344 # appeared in the list
1348 if {$view == $curview} {
1357 set i
[string first
"\0" $stuff $start]
1359 append leftover
($inst) [string range
$stuff $start end
]
1363 set cmit
$leftover($inst)
1364 append cmit
[string range
$stuff 0 [expr {$i - 1}]]
1365 set leftover
($inst) {}
1367 set cmit
[string range
$stuff $start [expr {$i - 1}]]
1369 set start
[expr {$i + 1}]
1370 set j
[string first
"\n" $cmit]
1373 if {$j >= 0 && [string match
"commit *" $cmit]} {
1374 set ids
[string range
$cmit 7 [expr {$j - 1}]]
1375 if {[string match
{[-^
<>]*} $ids]} {
1376 switch
-- [string index
$ids 0] {
1382 set ids
[string range
$ids 1 end
]
1386 if {[string length
$id] != 40} {
1394 if {[string length
$shortcmit] > 80} {
1395 set shortcmit
"[string range $shortcmit 0 80]..."
1397 error_popup
"[mc "Can
't parse git log output:"] {$shortcmit}"
1400 set id [lindex $ids 0]
1403 if {!$listed && $updating && ![info exists varcid($vid)] &&
1404 $vfilelimit($view) ne {}} {
1405 # git log doesn't rewrite parents
for unlisted commits
1406 # when doing path limiting, so work around that here
1407 # by working out the rewritten parent with git rev-list
1408 # and if we already know about it, using the rewritten
1409 # parent as a substitute parent for $id's children.
1411 set rwid
[exec git rev-list
--first-parent --max-count=1 \
1412 $id -- $vfilelimit($view)]
1414 if {$rwid ne
{} && [info exists varcid
($view,$rwid)]} {
1415 # use $rwid in place of $id
1416 rewrite_commit
$view $id $rwid
1423 if {[info exists varcid
($vid)]} {
1424 if {$cmitlisted($vid) ||
!$listed} continue
1428 set olds
[lrange
$ids 1 end
]
1432 set commitdata
($id) [string range
$cmit [expr {$j + 1}] end
]
1433 set cmitlisted
($vid) $listed
1434 set parents
($vid) $olds
1435 if {![info exists children
($vid)]} {
1436 set children
($vid) {}
1437 } elseif
{$a == 0 && [llength
$children($vid)] == 1} {
1438 set k
[lindex
$children($vid) 0]
1439 if {[llength
$parents($view,$k)] == 1 &&
1440 (!$vdatemode($view) ||
1441 $varcid($view,$k) == [llength
$varctok($view)] - 1)} {
1442 set a
$varcid($view,$k)
1447 set a
[newvarc
$view $id]
1449 if {[string compare
[lindex
$varctok($view) $a] $vtokmod($view)] < 0} {
1452 if {![info exists varcid
($vid)]} {
1454 lappend varccommits
($view,$a) $id
1455 incr commitidx
($view)
1460 if {$i == 0 ||
[lsearch
-exact $olds $p] >= $i} {
1462 if {[llength
[lappend children
($vp) $id]] > 1 &&
1463 [vtokcmp
$view [lindex
$children($vp) end-1
] $id] > 0} {
1464 set children
($vp) [lsort
-command [list vtokcmp
$view] \
1466 catch
{unset ordertok
}
1468 if {[info exists varcid
($view,$p)]} {
1469 fix_reversal
$p $a $view
1475 if {[info exists commitinterest
($id)]} {
1476 foreach
script $commitinterest($id) {
1477 lappend scripts
[string map
[list
"%I" $id] $script]
1479 unset commitinterest
($id)
1484 global numcommits hlview
1486 if {$view == $curview} {
1487 set numcommits
$commitidx($view)
1490 if {[info exists hlview
] && $view == $hlview} {
1491 # we never actually get here...
1494 foreach s
$scripts {
1501 proc chewcommits
{} {
1502 global curview hlview viewcomplete
1503 global pending_select
1506 if {$viewcomplete($curview)} {
1507 global commitidx varctok
1508 global numcommits startmsecs
1510 if {[info exists pending_select
]} {
1512 reset_pending_select
{}
1514 if {[commitinview
$pending_select $curview]} {
1515 selectline
[rowofcommit
$pending_select] 1
1517 set row
[first_real_row
]
1521 if {$commitidx($curview) > 0} {
1522 #set ms [expr {[clock clicks -milliseconds] - $startmsecs}]
1523 #puts "overall $ms ms for $numcommits commits"
1524 #puts "[llength $varctok($view)] arcs, $commitidx($view) commits"
1526 show_status
[mc
"No commits selected"]
1533 proc readcommit
{id
} {
1534 if {[catch
{set contents
[exec git cat-file commit
$id]}]} return
1535 parsecommit
$id $contents 0
1538 proc parsecommit
{id contents listed
} {
1539 global commitinfo cdate
1548 set hdrend
[string first
"\n\n" $contents]
1550 # should never happen...
1551 set hdrend
[string length
$contents]
1553 set header
[string range
$contents 0 [expr {$hdrend - 1}]]
1554 set comment
[string range
$contents [expr {$hdrend + 2}] end
]
1555 foreach line
[split $header "\n"] {
1556 set tag
[lindex
$line 0]
1557 if {$tag == "author"} {
1558 set audate
[lindex
$line end-1
]
1559 set auname
[lrange
$line 1 end-2
]
1560 } elseif
{$tag == "committer"} {
1561 set comdate
[lindex
$line end-1
]
1562 set comname
[lrange
$line 1 end-2
]
1566 # take the first non-blank line of the comment as the headline
1567 set headline
[string trimleft
$comment]
1568 set i
[string first
"\n" $headline]
1570 set headline
[string range
$headline 0 $i]
1572 set headline
[string trimright
$headline]
1573 set i
[string first
"\r" $headline]
1575 set headline
[string trimright
[string range
$headline 0 $i]]
1578 # git log indents the comment by 4 spaces;
1579 # if we got this via git cat-file, add the indentation
1581 foreach line
[split $comment "\n"] {
1582 append newcomment
" "
1583 append newcomment
$line
1584 append newcomment
"\n"
1586 set comment
$newcomment
1588 if {$comdate != {}} {
1589 set cdate
($id) $comdate
1591 set commitinfo
($id) [list
$headline $auname $audate \
1592 $comname $comdate $comment]
1595 proc getcommit
{id
} {
1596 global commitdata commitinfo
1598 if {[info exists commitdata
($id)]} {
1599 parsecommit
$id $commitdata($id) 1
1602 if {![info exists commitinfo
($id)]} {
1603 set commitinfo
($id) [list
[mc
"No commit information available"]]
1610 global tagids idtags headids idheads tagobjid
1611 global otherrefids idotherrefs mainhead mainheadid
1613 foreach v
{tagids idtags headids idheads otherrefids idotherrefs
} {
1616 set refd
[open
[list | git show-ref
-d] r
]
1617 while {[gets
$refd line
] >= 0} {
1618 if {[string index
$line 40] ne
" "} continue
1619 set id
[string range
$line 0 39]
1620 set ref
[string range
$line 41 end
]
1621 if {![string match
"refs/*" $ref]} continue
1622 set name
[string range
$ref 5 end
]
1623 if {[string match
"remotes/*" $name]} {
1624 if {![string match
"*/HEAD" $name]} {
1625 set headids
($name) $id
1626 lappend idheads
($id) $name
1628 } elseif
{[string match
"heads/*" $name]} {
1629 set name
[string range
$name 6 end
]
1630 set headids
($name) $id
1631 lappend idheads
($id) $name
1632 } elseif
{[string match
"tags/*" $name]} {
1633 # this lets refs/tags/foo^{} overwrite refs/tags/foo,
1634 # which is what we want since the former is the commit ID
1635 set name
[string range
$name 5 end
]
1636 if {[string match
"*^{}" $name]} {
1637 set name
[string range
$name 0 end-3
]
1639 set tagobjid
($name) $id
1641 set tagids
($name) $id
1642 lappend idtags
($id) $name
1644 set otherrefids
($name) $id
1645 lappend idotherrefs
($id) $name
1652 set mainheadid
[exec git rev-parse HEAD
]
1653 set thehead
[exec git symbolic-ref HEAD
]
1654 if {[string match
"refs/heads/*" $thehead]} {
1655 set mainhead
[string range
$thehead 11 end
]
1660 # skip over fake commits
1661 proc first_real_row
{} {
1662 global nullid nullid2 numcommits
1664 for {set row
0} {$row < $numcommits} {incr row
} {
1665 set id
[commitonrow
$row]
1666 if {$id ne
$nullid && $id ne
$nullid2} {
1673 # update things for a head moved to a child of its previous location
1674 proc movehead
{id name
} {
1675 global headids idheads
1677 removehead
$headids($name) $name
1678 set headids
($name) $id
1679 lappend idheads
($id) $name
1682 # update things when a head has been removed
1683 proc removehead
{id name
} {
1684 global headids idheads
1686 if {$idheads($id) eq
$name} {
1689 set i
[lsearch
-exact $idheads($id) $name]
1691 set idheads
($id) [lreplace
$idheads($id) $i $i]
1694 unset headids
($name)
1697 proc show_error
{w top msg
} {
1698 message
$w.m
-text $msg -justify center
-aspect 400
1699 pack
$w.m
-side top
-fill x
-padx 20 -pady 20
1700 button
$w.ok
-text [mc OK
] -command "destroy $top"
1701 pack
$w.ok
-side bottom
-fill x
1702 bind $top <Visibility
> "grab $top; focus $top"
1703 bind $top <Key-Return
> "destroy $top"
1707 proc error_popup msg
{
1711 show_error
$w $w $msg
1714 proc confirm_popup msg
{
1720 message
$w.m
-text $msg -justify center
-aspect 400
1721 pack
$w.m
-side top
-fill x
-padx 20 -pady 20
1722 button
$w.ok
-text [mc OK
] -command "set confirm_ok 1; destroy $w"
1723 pack
$w.ok
-side left
-fill x
1724 button
$w.cancel
-text [mc Cancel
] -command "destroy $w"
1725 pack
$w.cancel
-side right
-fill x
1726 bind $w <Visibility
> "grab $w; focus $w"
1731 proc setoptions
{} {
1732 option add
*Panedwindow.showHandle
1 startupFile
1733 option add
*Panedwindow.sashRelief raised startupFile
1734 option add
*Button.font uifont startupFile
1735 option add
*Checkbutton.font uifont startupFile
1736 option add
*Radiobutton.font uifont startupFile
1737 option add
*Menu.font uifont startupFile
1738 option add
*Menubutton.font uifont startupFile
1739 option add
*Label.font uifont startupFile
1740 option add
*Message.font uifont startupFile
1741 option add
*Entry.font uifont startupFile
1744 proc makewindow
{} {
1745 global canv canv2 canv3 linespc charspc ctext cflist cscroll
1747 global findtype findtypemenu findloc findstring fstring geometry
1748 global entries sha1entry sha1string sha1but
1749 global diffcontextstring diffcontext
1751 global maincursor textcursor curtextcursor
1752 global rowctxmenu fakerowmenu mergemax wrapcomment
1753 global highlight_files gdttype
1754 global searchstring sstring
1755 global bgcolor fgcolor bglist fglist diffcolors selectbgcolor
1756 global headctxmenu progresscanv progressitem progresscoords statusw
1757 global fprogitem fprogcoord lastprogupdate progupdatepending
1758 global rprogitem rprogcoord rownumsel numcommits
1762 .bar add cascade
-label [mc
"File"] -menu .bar.
file
1764 .bar.
file add
command -label [mc
"Update"] -command updatecommits
1765 .bar.
file add
command -label [mc
"Reload"] -command reloadcommits
1766 .bar.
file add
command -label [mc
"Reread references"] -command rereadrefs
1767 .bar.
file add
command -label [mc
"List references"] -command showrefs
1768 .bar.
file add
command -label [mc
"Quit"] -command doquit
1770 .bar add cascade
-label [mc
"Edit"] -menu .bar.edit
1771 .bar.edit add
command -label [mc
"Preferences"] -command doprefs
1774 .bar add cascade
-label [mc
"View"] -menu .bar.view
1775 .bar.view add
command -label [mc
"New view..."] -command {newview
0}
1776 .bar.view add
command -label [mc
"Edit view..."] -command editview \
1778 .bar.view add
command -label [mc
"Delete view"] -command delview
-state disabled
1779 .bar.view add separator
1780 .bar.view add radiobutton
-label [mc
"All files"] -command {showview
0} \
1781 -variable selectedview
-value 0
1784 .bar add cascade
-label [mc
"Help"] -menu .bar.
help
1785 .bar.
help add
command -label [mc
"About gitk"] -command about
1786 .bar.
help add
command -label [mc
"Key bindings"] -command keys
1788 . configure
-menu .bar
1790 # the gui has upper and lower half, parts of a paned window.
1791 panedwindow .ctop
-orient vertical
1793 # possibly use assumed geometry
1794 if {![info exists geometry
(pwsash0
)]} {
1795 set geometry
(topheight
) [expr {15 * $linespc}]
1796 set geometry
(topwidth
) [expr {80 * $charspc}]
1797 set geometry
(botheight
) [expr {15 * $linespc}]
1798 set geometry
(botwidth
) [expr {50 * $charspc}]
1799 set geometry
(pwsash0
) "[expr {40 * $charspc}] 2"
1800 set geometry
(pwsash1
) "[expr {60 * $charspc}] 2"
1803 # the upper half will have a paned window, a scroll bar to the right, and some stuff below
1804 frame .tf
-height $geometry(topheight
) -width $geometry(topwidth
)
1806 panedwindow .tf.histframe.pwclist
-orient horizontal
-sashpad 0 -handlesize 4
1808 # create three canvases
1809 set cscroll .tf.histframe.csb
1810 set canv .tf.histframe.pwclist.canv
1812 -selectbackground $selectbgcolor \
1813 -background $bgcolor -bd 0 \
1814 -yscrollincr $linespc -yscrollcommand "scrollcanv $cscroll"
1815 .tf.histframe.pwclist add
$canv
1816 set canv2 .tf.histframe.pwclist.canv2
1818 -selectbackground $selectbgcolor \
1819 -background $bgcolor -bd 0 -yscrollincr $linespc
1820 .tf.histframe.pwclist add
$canv2
1821 set canv3 .tf.histframe.pwclist.canv3
1823 -selectbackground $selectbgcolor \
1824 -background $bgcolor -bd 0 -yscrollincr $linespc
1825 .tf.histframe.pwclist add
$canv3
1826 eval .tf.histframe.pwclist sash place
0 $geometry(pwsash0
)
1827 eval .tf.histframe.pwclist sash place
1 $geometry(pwsash1
)
1829 # a scroll bar to rule them
1830 scrollbar
$cscroll -command {allcanvs yview
} -highlightthickness 0
1831 pack
$cscroll -side right
-fill y
1832 bind .tf.histframe.pwclist
<Configure
> {resizeclistpanes
%W
%w
}
1833 lappend bglist
$canv $canv2 $canv3
1834 pack .tf.histframe.pwclist
-fill both
-expand 1 -side left
1836 # we have two button bars at bottom of top frame. Bar 1
1838 frame .tf.lbar
-height 15
1840 set sha1entry .tf.bar.sha1
1841 set entries
$sha1entry
1842 set sha1but .tf.bar.sha1label
1843 button
$sha1but -text [mc
"SHA1 ID: "] -state disabled
-relief flat \
1844 -command gotocommit
-width 8
1845 $sha1but conf
-disabledforeground [$sha1but cget
-foreground]
1846 pack .tf.bar.sha1label
-side left
1847 entry
$sha1entry -width 40 -font textfont
-textvariable sha1string
1848 trace add variable sha1string
write sha1change
1849 pack
$sha1entry -side left
-pady 2
1851 image create bitmap bm-left
-data {
1852 #define left_width 16
1853 #define left_height 16
1854 static unsigned char left_bits
[] = {
1855 0x00, 0x00, 0xc0, 0x01, 0xe0, 0x00, 0x70, 0x00, 0x38, 0x00, 0x1c, 0x00,
1856 0x0e, 0x00, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0x0e, 0x00, 0x1c, 0x00,
1857 0x38, 0x00, 0x70, 0x00, 0xe0, 0x00, 0xc0, 0x01};
1859 image create bitmap bm-right
-data {
1860 #define right_width 16
1861 #define right_height 16
1862 static unsigned char right_bits
[] = {
1863 0x00, 0x00, 0xc0, 0x01, 0x80, 0x03, 0x00, 0x07, 0x00, 0x0e, 0x00, 0x1c,
1864 0x00, 0x38, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0x00, 0x38, 0x00, 0x1c,
1865 0x00, 0x0e, 0x00, 0x07, 0x80, 0x03, 0xc0, 0x01};
1867 button .tf.bar.leftbut
-image bm-left
-command goback \
1868 -state disabled
-width 26
1869 pack .tf.bar.leftbut
-side left
-fill y
1870 button .tf.bar.rightbut
-image bm-right
-command goforw \
1871 -state disabled
-width 26
1872 pack .tf.bar.rightbut
-side left
-fill y
1874 label .tf.bar.rowlabel
-text [mc
"Row"]
1876 label .tf.bar.rownum
-width 7 -font textfont
-textvariable rownumsel \
1877 -relief sunken
-anchor e
1878 label .tf.bar.rowlabel2
-text "/"
1879 label .tf.bar.numcommits
-width 7 -font textfont
-textvariable numcommits \
1880 -relief sunken
-anchor e
1881 pack .tf.bar.rowlabel .tf.bar.rownum .tf.bar.rowlabel2 .tf.bar.numcommits \
1884 trace add variable selectedline
write selectedline_change
1886 # Status label and progress bar
1887 set statusw .tf.bar.status
1888 label
$statusw -width 15 -relief sunken
1889 pack
$statusw -side left
-padx 5
1890 set h
[expr {[font metrics uifont
-linespace] + 2}]
1891 set progresscanv .tf.bar.progress
1892 canvas
$progresscanv -relief sunken
-height $h -borderwidth 2
1893 set progressitem
[$progresscanv create rect
-1 0 0 $h -fill green
]
1894 set fprogitem
[$progresscanv create rect
-1 0 0 $h -fill yellow
]
1895 set rprogitem
[$progresscanv create rect
-1 0 0 $h -fill red
]
1896 pack
$progresscanv -side right
-expand 1 -fill x
1897 set progresscoords
{0 0}
1900 bind $progresscanv <Configure
> adjustprogress
1901 set lastprogupdate
[clock clicks
-milliseconds]
1902 set progupdatepending
0
1904 # build up the bottom bar of upper window
1905 label .tf.lbar.flabel
-text "[mc "Find
"] "
1906 button .tf.lbar.fnext
-text [mc
"next"] -command {dofind
1 1}
1907 button .tf.lbar.fprev
-text [mc
"prev"] -command {dofind
-1 1}
1908 label .tf.lbar.flab2
-text " [mc "commit
"] "
1909 pack .tf.lbar.flabel .tf.lbar.fnext .tf.lbar.fprev .tf.lbar.flab2 \
1911 set gdttype
[mc
"containing:"]
1912 set gm
[tk_optionMenu .tf.lbar.gdttype gdttype \
1913 [mc
"containing:"] \
1914 [mc
"touching paths:"] \
1915 [mc
"adding/removing string:"]]
1916 trace add variable gdttype
write gdttype_change
1917 pack .tf.lbar.gdttype
-side left
-fill y
1920 set fstring .tf.lbar.findstring
1921 lappend entries
$fstring
1922 entry
$fstring -width 30 -font textfont
-textvariable findstring
1923 trace add variable findstring
write find_change
1924 set findtype
[mc
"Exact"]
1925 set findtypemenu
[tk_optionMenu .tf.lbar.findtype \
1926 findtype
[mc
"Exact"] [mc
"IgnCase"] [mc
"Regexp"]]
1927 trace add variable findtype
write findcom_change
1928 set findloc
[mc
"All fields"]
1929 tk_optionMenu .tf.lbar.findloc findloc
[mc
"All fields"] [mc
"Headline"] \
1930 [mc
"Comments"] [mc
"Author"] [mc
"Committer"]
1931 trace add variable findloc
write find_change
1932 pack .tf.lbar.findloc
-side right
1933 pack .tf.lbar.findtype
-side right
1934 pack
$fstring -side left
-expand 1 -fill x
1936 # Finish putting the upper half of the viewer together
1937 pack .tf.lbar
-in .tf
-side bottom
-fill x
1938 pack .tf.bar
-in .tf
-side bottom
-fill x
1939 pack .tf.histframe
-fill both
-side top
-expand 1
1941 .ctop paneconfigure .tf
-height $geometry(topheight
)
1942 .ctop paneconfigure .tf
-width $geometry(topwidth
)
1944 # now build up the bottom
1945 panedwindow .pwbottom
-orient horizontal
1947 # lower left, a text box over search bar, scroll bar to the right
1948 # if we know window height, then that will set the lower text height, otherwise
1949 # we set lower text height which will drive window height
1950 if {[info exists geometry
(main
)]} {
1951 frame .bleft
-width $geometry(botwidth
)
1953 frame .bleft
-width $geometry(botwidth
) -height $geometry(botheight
)
1959 button .bleft.top.search
-text [mc
"Search"] -command dosearch
1960 pack .bleft.top.search
-side left
-padx 5
1961 set sstring .bleft.top.sstring
1962 entry
$sstring -width 20 -font textfont
-textvariable searchstring
1963 lappend entries
$sstring
1964 trace add variable searchstring
write incrsearch
1965 pack
$sstring -side left
-expand 1 -fill x
1966 radiobutton .bleft.mid.
diff -text [mc
"Diff"] \
1967 -command changediffdisp
-variable diffelide
-value {0 0}
1968 radiobutton .bleft.mid.old
-text [mc
"Old version"] \
1969 -command changediffdisp
-variable diffelide
-value {0 1}
1970 radiobutton .bleft.mid.new
-text [mc
"New version"] \
1971 -command changediffdisp
-variable diffelide
-value {1 0}
1972 label .bleft.mid.labeldiffcontext
-text " [mc "Lines of context
"]: "
1973 pack .bleft.mid.
diff .bleft.mid.old .bleft.mid.new
-side left
1974 spinbox .bleft.mid.diffcontext
-width 5 -font textfont \
1975 -from 1 -increment 1 -to 10000000 \
1976 -validate all
-validatecommand "diffcontextvalidate %P" \
1977 -textvariable diffcontextstring
1978 .bleft.mid.diffcontext
set $diffcontext
1979 trace add variable diffcontextstring
write diffcontextchange
1980 lappend entries .bleft.mid.diffcontext
1981 pack .bleft.mid.labeldiffcontext .bleft.mid.diffcontext
-side left
1982 checkbutton .bleft.mid.ignspace
-text [mc
"Ignore space change"] \
1983 -command changeignorespace
-variable ignorespace
1984 pack .bleft.mid.ignspace
-side left
-padx 5
1985 set ctext .bleft.bottom.ctext
1986 text
$ctext -background $bgcolor -foreground $fgcolor \
1987 -state disabled
-font textfont \
1988 -yscrollcommand scrolltext
-wrap none \
1989 -xscrollcommand ".bleft.bottom.sbhorizontal set"
1991 $ctext conf
-tabstyle wordprocessor
1993 scrollbar .bleft.bottom.sb
-command "$ctext yview"
1994 scrollbar .bleft.bottom.sbhorizontal
-command "$ctext xview" -orient h \
1996 pack .bleft.top
-side top
-fill x
1997 pack .bleft.mid
-side top
-fill x
1998 grid
$ctext .bleft.bottom.sb
-sticky nsew
1999 grid .bleft.bottom.sbhorizontal
-sticky ew
2000 grid columnconfigure .bleft.bottom
0 -weight 1
2001 grid rowconfigure .bleft.bottom
0 -weight 1
2002 grid rowconfigure .bleft.bottom
1 -weight 0
2003 pack .bleft.bottom
-side top
-fill both
-expand 1
2004 lappend bglist
$ctext
2005 lappend fglist
$ctext
2007 $ctext tag conf comment
-wrap $wrapcomment
2008 $ctext tag conf filesep
-font textfontbold
-back "#aaaaaa"
2009 $ctext tag conf hunksep
-fore [lindex
$diffcolors 2]
2010 $ctext tag conf d0
-fore [lindex
$diffcolors 0]
2011 $ctext tag conf d1
-fore [lindex
$diffcolors 1]
2012 $ctext tag conf m0
-fore red
2013 $ctext tag conf m1
-fore blue
2014 $ctext tag conf m2
-fore green
2015 $ctext tag conf m3
-fore purple
2016 $ctext tag conf
m4 -fore brown
2017 $ctext tag conf m5
-fore "#009090"
2018 $ctext tag conf m6
-fore magenta
2019 $ctext tag conf m7
-fore "#808000"
2020 $ctext tag conf m8
-fore "#009000"
2021 $ctext tag conf m9
-fore "#ff0080"
2022 $ctext tag conf m10
-fore cyan
2023 $ctext tag conf m11
-fore "#b07070"
2024 $ctext tag conf m12
-fore "#70b0f0"
2025 $ctext tag conf m13
-fore "#70f0b0"
2026 $ctext tag conf m14
-fore "#f0b070"
2027 $ctext tag conf m15
-fore "#ff70b0"
2028 $ctext tag conf mmax
-fore darkgrey
2030 $ctext tag conf mresult
-font textfontbold
2031 $ctext tag conf msep
-font textfontbold
2032 $ctext tag conf found
-back yellow
2034 .pwbottom add .bleft
2035 .pwbottom paneconfigure .bleft
-width $geometry(botwidth
)
2040 radiobutton .bright.mode.
patch -text [mc
"Patch"] \
2041 -command reselectline
-variable cmitmode
-value "patch"
2042 radiobutton .bright.mode.tree
-text [mc
"Tree"] \
2043 -command reselectline
-variable cmitmode
-value "tree"
2044 grid .bright.mode.
patch .bright.mode.tree
-sticky ew
2045 pack .bright.mode
-side top
-fill x
2046 set cflist .bright.cfiles
2047 set indent
[font measure mainfont
"nn"]
2049 -selectbackground $selectbgcolor \
2050 -background $bgcolor -foreground $fgcolor \
2052 -tabs [list
$indent [expr {2 * $indent}]] \
2053 -yscrollcommand ".bright.sb set" \
2054 -cursor [. cget
-cursor] \
2055 -spacing1 1 -spacing3 1
2056 lappend bglist
$cflist
2057 lappend fglist
$cflist
2058 scrollbar .bright.sb
-command "$cflist yview"
2059 pack .bright.sb
-side right
-fill y
2060 pack
$cflist -side left
-fill both
-expand 1
2061 $cflist tag configure highlight \
2062 -background [$cflist cget
-selectbackground]
2063 $cflist tag configure bold
-font mainfontbold
2065 .pwbottom add .bright
2068 # restore window width & height if known
2069 if {[info exists geometry
(main
)]} {
2070 if {[scan
$geometry(main
) "%dx%d" w h
] >= 2} {
2071 if {$w > [winfo screenwidth .
]} {
2072 set w
[winfo screenwidth .
]
2074 if {$h > [winfo screenheight .
]} {
2075 set h
[winfo screenheight .
]
2077 wm geometry .
"${w}x$h"
2081 if {[tk windowingsystem
] eq
{aqua
}} {
2087 bind .pwbottom
<Configure
> {resizecdetpanes
%W
%w
}
2088 pack .ctop
-fill both
-expand 1
2089 bindall
<1> {selcanvline
%W
%x
%y
}
2090 #bindall <B1-Motion> {selcanvline %W %x %y}
2091 if {[tk windowingsystem
] == "win32"} {
2092 bind .
<MouseWheel
> { windows_mousewheel_redirector
%W
%X
%Y
%D
}
2093 bind $ctext <MouseWheel
> { windows_mousewheel_redirector
%W
%X
%Y
%D
; break }
2095 bindall
<ButtonRelease-4
> "allcanvs yview scroll -5 units"
2096 bindall
<ButtonRelease-5
> "allcanvs yview scroll 5 units"
2097 if {[tk windowingsystem
] eq
"aqua"} {
2098 bindall
<MouseWheel
> {
2099 set delta
[expr {- (%D
)}]
2100 allcanvs yview scroll
$delta units
2104 bindall
<2> "canvscan mark %W %x %y"
2105 bindall
<B2-Motion
> "canvscan dragto %W %x %y"
2106 bindkey
<Home
> selfirstline
2107 bindkey
<End
> sellastline
2108 bind .
<Key-Up
> "selnextline -1"
2109 bind .
<Key-Down
> "selnextline 1"
2110 bind .
<Shift-Key-Up
> "dofind -1 0"
2111 bind .
<Shift-Key-Down
> "dofind 1 0"
2112 bindkey
<Key-Right
> "goforw"
2113 bindkey
<Key-Left
> "goback"
2114 bind .
<Key-Prior
> "selnextpage -1"
2115 bind .
<Key-Next
> "selnextpage 1"
2116 bind .
<$M1B-Home> "allcanvs yview moveto 0.0"
2117 bind .
<$M1B-End> "allcanvs yview moveto 1.0"
2118 bind .
<$M1B-Key-Up> "allcanvs yview scroll -1 units"
2119 bind .
<$M1B-Key-Down> "allcanvs yview scroll 1 units"
2120 bind .
<$M1B-Key-Prior> "allcanvs yview scroll -1 pages"
2121 bind .
<$M1B-Key-Next> "allcanvs yview scroll 1 pages"
2122 bindkey
<Key-Delete
> "$ctext yview scroll -1 pages"
2123 bindkey
<Key-BackSpace
> "$ctext yview scroll -1 pages"
2124 bindkey
<Key-space
> "$ctext yview scroll 1 pages"
2125 bindkey p
"selnextline -1"
2126 bindkey n
"selnextline 1"
2129 bindkey i
"selnextline -1"
2130 bindkey k
"selnextline 1"
2134 bindkey d
"$ctext yview scroll 18 units"
2135 bindkey u
"$ctext yview scroll -18 units"
2136 bindkey
/ {dofind
1 1}
2137 bindkey
<Key-Return
> {dofind
1 1}
2138 bindkey ?
{dofind
-1 1}
2140 bindkey
<F5
> updatecommits
2141 bind .
<$M1B-q> doquit
2142 bind .
<$M1B-f> {dofind
1 1}
2143 bind .
<$M1B-g> {dofind
1 0}
2144 bind .
<$M1B-r> dosearchback
2145 bind .
<$M1B-s> dosearch
2146 bind .
<$M1B-equal> {incrfont
1}
2147 bind .
<$M1B-plus> {incrfont
1}
2148 bind .
<$M1B-KP_Add> {incrfont
1}
2149 bind .
<$M1B-minus> {incrfont
-1}
2150 bind .
<$M1B-KP_Subtract> {incrfont
-1}
2151 wm protocol . WM_DELETE_WINDOW doquit
2152 bind .
<Destroy
> {stop_backends
}
2153 bind .
<Button-1
> "click %W"
2154 bind $fstring <Key-Return
> {dofind
1 1}
2155 bind $sha1entry <Key-Return
> gotocommit
2156 bind $sha1entry <<PasteSelection>> clearsha1
2157 bind $cflist <1> {sel_flist %W %x %y; break}
2158 bind $cflist <B1-Motion> {sel_flist %W %x %y; break}
2159 bind $cflist <ButtonRelease-1> {treeclick %W %x %y}
2160 bind $cflist <Button-3> {pop_flist_menu %W %X %Y %x %y}
2162 set maincursor [. cget -cursor]
2163 set textcursor [$ctext cget -cursor]
2164 set curtextcursor $textcursor
2166 set rowctxmenu .rowctxmenu
2167 menu $rowctxmenu -tearoff 0
2168 $rowctxmenu add command -label [mc "Diff this -> selected"] \
2169 -command {diffvssel 0}
2170 $rowctxmenu add command -label [mc "Diff selected -> this"] \
2171 -command {diffvssel 1}
2172 $rowctxmenu add command -label [mc "Make patch"] -command mkpatch
2173 $rowctxmenu add command -label [mc "Create tag"] -command mktag
2174 $rowctxmenu add command -label [mc "Write commit to file"] -command writecommit
2175 $rowctxmenu add command -label [mc "Create new branch"] -command mkbranch
2176 $rowctxmenu add command -label [mc "Cherry-pick this commit"] \
2178 $rowctxmenu add command -label [mc "Reset HEAD branch to here"] \
2181 set fakerowmenu .fakerowmenu
2182 menu $fakerowmenu -tearoff 0
2183 $fakerowmenu add command -label [mc "Diff this -> selected"] \
2184 -command {diffvssel 0}
2185 $fakerowmenu add command -label [mc "Diff selected -> this"] \
2186 -command {diffvssel 1}
2187 $fakerowmenu add command -label [mc "Make patch"] -command mkpatch
2188 # $fakerowmenu add command -label [mc "Commit"] -command {mkcommit 0}
2189 # $fakerowmenu add command -label [mc "Commit all"] -command {mkcommit 1}
2190 # $fakerowmenu add command -label [mc "Revert local changes"] -command revertlocal
2192 set headctxmenu .headctxmenu
2193 menu $headctxmenu -tearoff 0
2194 $headctxmenu add command -label [mc "Check out this branch"] \
2196 $headctxmenu add command -label [mc "Remove this branch"] \
2200 set flist_menu .flistctxmenu
2201 menu $flist_menu -tearoff 0
2202 $flist_menu add command -label [mc "Highlight this too"] \
2203 -command {flist_hl 0}
2204 $flist_menu add command -label [mc "Highlight this only"] \
2205 -command {flist_hl 1}
2206 $flist_menu add command -label [mc "External diff"] \
2207 -command {external_diff}
2210 # Windows sends all mouse wheel events to the current focused window, not
2211 # the one where the mouse hovers, so bind those events here and redirect
2212 # to the correct window
2213 proc windows_mousewheel_redirector {W X Y D} {
2214 global canv canv2 canv3
2215 set w [winfo containing -displayof $W $X $Y]
2217 set u [expr {$D < 0 ? 5 : -5}]
2218 if {$w == $canv || $w == $canv2 || $w == $canv3} {
2219 allcanvs yview scroll $u units
2222 $w yview scroll $u units
2228 # Update row number label when selectedline changes
2229 proc selectedline_change {n1 n2 op} {
2230 global selectedline rownumsel
2232 if {$selectedline eq {}} {
2235 set rownumsel [expr {$selectedline + 1}]
2239 # mouse-2 makes all windows scan vertically, but only the one
2240 # the cursor is in scans horizontally
2241 proc canvscan {op w x y} {
2242 global canv canv2 canv3
2243 foreach c [list $canv $canv2 $canv3] {
2252 proc scrollcanv {cscroll f0 f1} {
2253 $cscroll set $f0 $f1
2258 # when we make a key binding for the toplevel, make sure
2259 # it doesn't get triggered when that key is pressed in the
2260 # find string entry widget.
2261 proc bindkey {ev script} {
2264 set escript [bind Entry $ev]
2265 if {$escript == {}} {
2266 set escript [bind Entry <Key>]
2268 foreach e $entries {
2269 bind $e $ev "$escript; break"
2273 # set the focus back to the toplevel for any click outside
2276 global ctext entries
2277 foreach e [concat $entries $ctext] {
2278 if {$w == $e} return
2283 # Adjust the progress bar for a change in requested extent or canvas size
2284 proc adjustprogress {} {
2285 global progresscanv progressitem progresscoords
2286 global fprogitem fprogcoord lastprogupdate progupdatepending
2287 global rprogitem rprogcoord
2289 set w [expr {[winfo width $progresscanv] - 4}]
2290 set x0 [expr {$w * [lindex $progresscoords 0]}]
2291 set x1 [expr {$w * [lindex $progresscoords 1]}]
2292 set h [winfo height $progresscanv]
2293 $progresscanv coords $progressitem $x0 0 $x1 $h
2294 $progresscanv coords $fprogitem 0 0 [expr {$w * $fprogcoord}] $h
2295 $progresscanv coords $rprogitem 0 0 [expr {$w * $rprogcoord}] $h
2296 set now [clock clicks -milliseconds]
2297 if {$now >= $lastprogupdate + 100} {
2298 set progupdatepending 0
2300 } elseif {!$progupdatepending} {
2301 set progupdatepending 1
2302 after [expr {$lastprogupdate + 100 - $now}] doprogupdate
2306 proc doprogupdate {} {
2307 global lastprogupdate progupdatepending
2309 if {$progupdatepending} {
2310 set progupdatepending 0
2311 set lastprogupdate [clock clicks -milliseconds]
2316 proc savestuff {w} {
2317 global canv canv2 canv3 mainfont textfont uifont tabstop
2318 global stuffsaved findmergefiles maxgraphpct
2319 global maxwidth showneartags showlocalchanges
2320 global viewname viewfiles viewargs viewargscmd viewperm nextviewnum
2321 global cmitmode wrapcomment datetimeformat limitdiffs
2322 global colors bgcolor fgcolor diffcolors diffcontext selectbgcolor
2323 global autoselect extdifftool
2325 if {$stuffsaved} return
2326 if {![winfo viewable .]} return
2328 set f [open "~/.gitk-new" w]
2329 puts $f [list set mainfont $mainfont]
2330 puts $f [list set textfont $textfont]
2331 puts $f [list set uifont $uifont]
2332 puts $f [list set tabstop $tabstop]
2333 puts $f [list set findmergefiles $findmergefiles]
2334 puts $f [list set maxgraphpct $maxgraphpct]
2335 puts $f [list set maxwidth $maxwidth]
2336 puts $f [list set cmitmode $cmitmode]
2337 puts $f [list set wrapcomment $wrapcomment]
2338 puts $f [list set autoselect $autoselect]
2339 puts $f [list set showneartags $showneartags]
2340 puts $f [list set showlocalchanges $showlocalchanges]
2341 puts $f [list set datetimeformat $datetimeformat]
2342 puts $f [list set limitdiffs $limitdiffs]
2343 puts $f [list set bgcolor $bgcolor]
2344 puts $f [list set fgcolor $fgcolor]
2345 puts $f [list set colors $colors]
2346 puts $f [list set diffcolors $diffcolors]
2347 puts $f [list set diffcontext $diffcontext]
2348 puts $f [list set selectbgcolor $selectbgcolor]
2349 puts $f [list set extdifftool $extdifftool]
2351 puts $f "set geometry(main) [wm geometry .]"
2352 puts $f "set geometry(topwidth) [winfo width .tf]"
2353 puts $f "set geometry(topheight) [winfo height .tf]"
2354 puts $f "set geometry(pwsash0) \"[.tf.histframe.pwclist sash coord 0]\""
2355 puts $f "set geometry(pwsash1) \"[.tf.histframe.pwclist sash coord 1]\""
2356 puts $f "set geometry(botwidth) [winfo width .bleft]"
2357 puts $f "set geometry(botheight) [winfo height .bleft]"
2359 puts -nonewline $f "set permviews {"
2360 for {set v 0} {$v < $nextviewnum} {incr v} {
2361 if {$viewperm($v)} {
2362 puts $f "{[list $viewname($v) $viewfiles($v) $viewargs($v) $viewargscmd($v)]}"
2367 catch {file delete "~/.gitk"}
2368 file rename -force "~/.gitk-new" "~/.gitk"
2373 proc resizeclistpanes {win w} {
2375 if {[info exists oldwidth($win)]} {
2376 set s0 [$win sash coord 0]
2377 set s1 [$win sash coord 1]
2379 set sash0 [expr {int($w/2 - 2)}]
2380 set sash1 [expr {int($w*5/6 - 2)}]
2382 set factor [expr {1.0 * $w / $oldwidth($win)}]
2383 set sash0 [expr {int($factor * [lindex $s0 0])}]
2384 set sash1 [expr {int($factor * [lindex $s1 0])}]
2388 if {$sash1 < $sash0 + 20} {
2389 set sash1 [expr {$sash0 + 20}]
2391 if {$sash1 > $w - 10} {
2392 set sash1 [expr {$w - 10}]
2393 if {$sash0 > $sash1 - 20} {
2394 set sash0 [expr {$sash1 - 20}]
2398 $win sash place 0 $sash0 [lindex $s0 1]
2399 $win sash place 1 $sash1 [lindex $s1 1]
2401 set oldwidth($win) $w
2404 proc resizecdetpanes {win w} {
2406 if {[info exists oldwidth($win)]} {
2407 set s0 [$win sash coord 0]
2409 set sash0 [expr {int($w*3/4 - 2)}]
2411 set factor [expr {1.0 * $w / $oldwidth($win)}]
2412 set sash0 [expr {int($factor * [lindex $s0 0])}]
2416 if {$sash0 > $w - 15} {
2417 set sash0 [expr {$w - 15}]
2420 $win sash place 0 $sash0 [lindex $s0 1]
2422 set oldwidth($win) $w
2425 proc allcanvs args {
2426 global canv canv2 canv3
2432 proc bindall {event action} {
2433 global canv canv2 canv3
2434 bind $canv $event $action
2435 bind $canv2 $event $action
2436 bind $canv3 $event $action
2442 if {[winfo exists $w]} {
2447 wm title $w [mc "About gitk"]
2448 message $w.m -text [mc "
2449 Gitk - a commit viewer for git
2451 Copyright © 2005-2008 Paul Mackerras
2453 Use and redistribute under the terms of the GNU General Public License"] \
2454 -justify center -aspect 400 -border 2 -bg white -relief groove
2455 pack $w.m -side top -fill x -padx 2 -pady 2
2456 button $w.ok -text [mc "Close"] -command "destroy $w" -default active
2457 pack $w.ok -side bottom
2458 bind $w <Visibility> "focus $w.ok"
2459 bind $w <Key-Escape> "destroy $w"
2460 bind $w <Key-Return> "destroy $w"
2465 if {[winfo exists $w]} {
2469 if {[tk windowingsystem] eq {aqua}} {
2475 wm title $w [mc "Gitk key bindings"]
2476 message $w.m -text "
2477 [mc "Gitk key bindings:"]
2479 [mc "<%s-Q> Quit" $M1T]
2480 [mc "<Home> Move to first commit"]
2481 [mc "<End> Move to last commit"]
2482 [mc "<Up>, p, i Move up one commit"]
2483 [mc "<Down>, n, k Move down one commit"]
2484 [mc "<Left>, z, j Go back in history list"]
2485 [mc "<Right>, x, l Go forward in history list"]
2486 [mc "<PageUp> Move up one page in commit list"]
2487 [mc "<PageDown> Move down one page in commit list"]
2488 [mc "<%s-Home> Scroll to top of commit list" $M1T]
2489 [mc "<%s-End> Scroll to bottom of commit list" $M1T]
2490 [mc "<%s-Up> Scroll commit list up one line" $M1T]
2491 [mc "<%s-Down> Scroll commit list down one line" $M1T]
2492 [mc "<%s-PageUp> Scroll commit list up one page" $M1T]
2493 [mc "<%s-PageDown> Scroll commit list down one page" $M1T]
2494 [mc "<Shift-Up> Find backwards (upwards, later commits)"]
2495 [mc "<Shift-Down> Find forwards (downwards, earlier commits)"]
2496 [mc "<Delete>, b Scroll diff view up one page"]
2497 [mc "<Backspace> Scroll diff view up one page"]
2498 [mc "<Space> Scroll diff view down one page"]
2499 [mc "u Scroll diff view up 18 lines"]
2500 [mc "d Scroll diff view down 18 lines"]
2501 [mc "<%s-F> Find" $M1T]
2502 [mc "<%s-G> Move to next find hit" $M1T]
2503 [mc "<Return> Move to next find hit"]
2504 [mc "/ Move to next find hit, or redo find"]
2505 [mc "? Move to previous find hit"]
2506 [mc "f Scroll diff view to next file"]
2507 [mc "<%s-S> Search for next hit in diff view" $M1T]
2508 [mc "<%s-R> Search for previous hit in diff view" $M1T]
2509 [mc "<%s-KP+> Increase font size" $M1T]
2510 [mc "<%s-plus> Increase font size" $M1T]
2511 [mc "<%s-KP-> Decrease font size" $M1T]
2512 [mc "<%s-minus> Decrease font size" $M1T]
2515 -justify left -bg white -border 2 -relief groove
2516 pack $w.m -side top -fill both -padx 2 -pady 2
2517 button $w.ok -text [mc "Close"] -command "destroy $w" -default active
2518 pack $w.ok -side bottom
2519 bind $w <Visibility> "focus $w.ok"
2520 bind $w <Key-Escape> "destroy $w"
2521 bind $w <Key-Return> "destroy $w"
2524 # Procedures for manipulating the file list window at the
2525 # bottom right of the overall window.
2527 proc treeview {w l openlevs} {
2528 global treecontents treediropen treeheight treeparent treeindex
2538 set treecontents() {}
2539 $w conf -state normal
2541 while {[string range $f 0 $prefixend] ne $prefix} {
2542 if {$lev <= $openlevs} {
2543 $w mark set e:$treeindex($prefix) "end -1c"
2544 $w mark gravity e:$treeindex($prefix) left
2546 set treeheight($prefix) $ht
2547 incr ht [lindex $htstack end]
2548 set htstack [lreplace $htstack end end]
2549 set prefixend [lindex $prefendstack end]
2550 set prefendstack [lreplace $prefendstack end end]
2551 set prefix [string range $prefix 0 $prefixend]
2554 set tail [string range $f [expr {$prefixend+1}] end]
2555 while {[set slash [string first "/" $tail]] >= 0} {
2558 lappend prefendstack $prefixend
2559 incr prefixend [expr {$slash + 1}]
2560 set d [string range $tail 0 $slash]
2561 lappend treecontents($prefix) $d
2562 set oldprefix $prefix
2564 set treecontents($prefix) {}
2565 set treeindex($prefix) [incr ix]
2566 set treeparent($prefix) $oldprefix
2567 set tail [string range $tail [expr {$slash+1}] end]
2568 if {$lev <= $openlevs} {
2570 set treediropen($prefix) [expr {$lev < $openlevs}]
2571 set bm [expr {$lev == $openlevs? "tri-rt": "tri-dn"}]
2572 $w mark set d:$ix "end -1c"
2573 $w mark gravity d:$ix left
2575 for {set i 0} {$i < $lev} {incr i} {append str "\t"}
2577 $w image create end -align center -image $bm -padx 1 \
2579 $w insert end $d [highlight_tag $prefix]
2580 $w mark set s:$ix "end -1c"
2581 $w mark gravity s:$ix left
2586 if {$lev <= $openlevs} {
2589 for {set i 0} {$i < $lev} {incr i} {append str "\t"}
2591 $w insert end $tail [highlight_tag $f]
2593 lappend treecontents($prefix) $tail
2596 while {$htstack ne {}} {
2597 set treeheight($prefix) $ht
2598 incr ht [lindex $htstack end]
2599 set htstack [lreplace $htstack end end]
2600 set prefixend [lindex $prefendstack end]
2601 set prefendstack [lreplace $prefendstack end end]
2602 set prefix [string range $prefix 0 $prefixend]
2604 $w conf -state disabled
2607 proc linetoelt {l} {
2608 global treeheight treecontents
2613 foreach e $treecontents($prefix) {
2618 if {[string index $e end] eq "/"} {
2619 set n $treeheight($prefix$e)
2631 proc highlight_tree {y prefix} {
2632 global treeheight treecontents cflist
2634 foreach e $treecontents($prefix) {
2636 if {[highlight_tag $path] ne {}} {
2637 $cflist tag add bold $y.0 "$y.0 lineend"
2640 if {[string index $e end] eq "/" && $treeheight($path) > 1} {
2641 set y [highlight_tree $y $path]
2647 proc treeclosedir {w dir} {
2648 global treediropen treeheight treeparent treeindex
2650 set ix $treeindex($dir)
2651 $w conf -state normal
2652 $w delete s:$ix e:$ix
2653 set treediropen($dir) 0
2654 $w image configure a:$ix -image tri-rt
2655 $w conf -state disabled
2656 set n [expr {1 - $treeheight($dir)}]
2657 while {$dir ne {}} {
2658 incr treeheight($dir) $n
2659 set dir $treeparent($dir)
2663 proc treeopendir {w dir} {
2664 global treediropen treeheight treeparent treecontents treeindex
2666 set ix $treeindex($dir)
2667 $w conf -state normal
2668 $w image configure a:$ix -image tri-dn
2669 $w mark set e:$ix s:$ix
2670 $w mark gravity e:$ix right
2673 set n [llength $treecontents($dir)]
2674 for {set x $dir} {$x ne {}} {set x $treeparent($x)} {
2677 incr treeheight($x) $n
2679 foreach e $treecontents($dir) {
2681 if {[string index $e end] eq "/"} {
2682 set iy $treeindex($de)
2683 $w mark set d:$iy e:$ix
2684 $w mark gravity d:$iy left
2685 $w insert e:$ix $str
2686 set treediropen($de) 0
2687 $w image create e:$ix -align center -image tri-rt -padx 1 \
2689 $w insert e:$ix $e [highlight_tag $de]
2690 $w mark set s:$iy e:$ix
2691 $w mark gravity s:$iy left
2692 set treeheight($de) 1
2694 $w insert e:$ix $str
2695 $w insert e:$ix $e [highlight_tag $de]
2698 $w mark gravity e:$ix left
2699 $w conf -state disabled
2700 set treediropen($dir) 1
2701 set top [lindex [split [$w index @0,0] .] 0]
2702 set ht [$w cget -height]
2703 set l [lindex [split [$w index s:$ix] .] 0]
2706 } elseif {$l + $n + 1 > $top + $ht} {
2707 set top [expr {$l + $n + 2 - $ht}]
2715 proc treeclick {w x y} {
2716 global treediropen cmitmode ctext cflist cflist_top
2718 if {$cmitmode ne "tree"} return
2719 if {![info exists cflist_top]} return
2720 set l [lindex [split [$w index "@$x,$y"] "."] 0]
2721 $cflist tag remove highlight $cflist_top.0 "$cflist_top.0 lineend"
2722 $cflist tag add highlight $l.0 "$l.0 lineend"
2728 set e [linetoelt $l]
2729 if {[string index $e end] ne "/"} {
2731 } elseif {$treediropen($e)} {
2738 proc setfilelist {id} {
2739 global treefilelist cflist
2741 treeview $cflist $treefilelist($id) 0
2744 image create bitmap tri-rt -background black -foreground blue -data {
2745 #define tri-rt_width 13
2746 #define tri-rt_height 13
2747 static unsigned char tri-rt_bits[] = {
2748 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x30, 0x00, 0x70, 0x00, 0xf0, 0x00,
2749 0xf0, 0x01, 0xf0, 0x00, 0x70, 0x00, 0x30, 0x00, 0x10, 0x00, 0x00, 0x00,
2752 #define tri-rt-mask_width 13
2753 #define tri-rt-mask_height 13
2754 static unsigned char tri-rt-mask_bits[] = {
2755 0x08, 0x00, 0x18, 0x00, 0x38, 0x00, 0x78, 0x00, 0xf8, 0x00, 0xf8, 0x01,
2756 0xf8, 0x03, 0xf8, 0x01, 0xf8, 0x00, 0x78, 0x00, 0x38, 0x00, 0x18, 0x00,
2759 image create bitmap tri-dn -background black -foreground blue -data {
2760 #define tri-dn_width 13
2761 #define tri-dn_height 13
2762 static unsigned char tri-dn_bits[] = {
2763 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x07, 0xf8, 0x03,
2764 0xf0, 0x01, 0xe0, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2767 #define tri-dn-mask_width 13
2768 #define tri-dn-mask_height 13
2769 static unsigned char tri-dn-mask_bits[] = {
2770 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x1f, 0xfe, 0x0f, 0xfc, 0x07,
2771 0xf8, 0x03, 0xf0, 0x01, 0xe0, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
2775 image create bitmap reficon-T -background black -foreground yellow -data {
2776 #define tagicon_width 13
2777 #define tagicon_height 9
2778 static unsigned char tagicon_bits[] = {
2779 0x00, 0x00, 0x00, 0x00, 0xf0, 0x07, 0xf8, 0x07,
2780 0xfc, 0x07, 0xf8, 0x07, 0xf0, 0x07, 0x00, 0x00, 0x00, 0x00};
2782 #define tagicon-mask_width 13
2783 #define tagicon-mask_height 9
2784 static unsigned char tagicon-mask_bits[] = {
2785 0x00, 0x00, 0xf0, 0x0f, 0xf8, 0x0f, 0xfc, 0x0f,
2786 0xfe, 0x0f, 0xfc, 0x0f, 0xf8, 0x0f, 0xf0, 0x0f, 0x00, 0x00};
2789 #define headicon_width 13
2790 #define headicon_height 9
2791 static unsigned char headicon_bits[] = {
2792 0x00, 0x00, 0x00, 0x00, 0xf8, 0x07, 0xf8, 0x07,
2793 0xf8, 0x07, 0xf8, 0x07, 0xf8, 0x07, 0x00, 0x00, 0x00, 0x00};
2796 #define headicon-mask_width 13
2797 #define headicon-mask_height 9
2798 static unsigned char headicon-mask_bits[] = {
2799 0x00, 0x00, 0xfc, 0x0f, 0xfc, 0x0f, 0xfc, 0x0f,
2800 0xfc, 0x0f, 0xfc, 0x0f, 0xfc, 0x0f, 0xfc, 0x0f, 0x00, 0x00};
2802 image create bitmap reficon-H -background black -foreground green \
2803 -data $rectdata -maskdata $rectmask
2804 image create bitmap reficon-o -background black -foreground "#ddddff" \
2805 -data $rectdata -maskdata $rectmask
2807 proc init_flist {first} {
2808 global cflist cflist_top difffilestart
2810 $cflist conf -state normal
2811 $cflist delete 0.0 end
2813 $cflist insert end $first
2815 $cflist tag add highlight 1.0 "1.0 lineend"
2817 catch {unset cflist_top}
2819 $cflist conf -state disabled
2820 set difffilestart {}
2823 proc highlight_tag {f} {
2824 global highlight_paths
2826 foreach p $highlight_paths {
2827 if {[string match $p $f]} {
2834 proc highlight_filelist {} {
2835 global cmitmode cflist
2837 $cflist conf -state normal
2838 if {$cmitmode ne "tree"} {
2839 set end [lindex [split [$cflist index end] .] 0]
2840 for {set l 2} {$l < $end} {incr l} {
2841 set line [$cflist get $l.0 "$l.0 lineend"]
2842 if {[highlight_tag $line] ne {}} {
2843 $cflist tag add bold $l.0 "$l.0 lineend"
2849 $cflist conf -state disabled
2852 proc unhighlight_filelist {} {
2855 $cflist conf -state normal
2856 $cflist tag remove bold 1.0 end
2857 $cflist conf -state disabled
2860 proc add_flist {fl} {
2863 $cflist conf -state normal
2865 $cflist insert end "\n"
2866 $cflist insert end $f [highlight_tag $f]
2868 $cflist conf -state disabled
2871 proc sel_flist {w x y} {
2872 global ctext difffilestart cflist cflist_top cmitmode
2874 if {$cmitmode eq "tree"} return
2875 if {![info exists cflist_top]} return
2876 set l [lindex [split [$w index "@$x,$y"] "."] 0]
2877 $cflist tag remove highlight $cflist_top.0 "$cflist_top.0 lineend"
2878 $cflist tag add highlight $l.0 "$l.0 lineend"
2883 catch {$ctext yview [lindex $difffilestart [expr {$l - 2}]]}
2887 proc pop_flist_menu {w X Y x y} {
2888 global ctext cflist cmitmode flist_menu flist_menu_file
2889 global treediffs diffids
2892 set l [lindex [split [$w index "@$x,$y"] "."] 0]
2894 if {$cmitmode eq "tree"} {
2895 set e [linetoelt $l]
2896 if {[string index $e end] eq "/"} return
2898 set e [lindex $treediffs($diffids) [expr {$l-2}]]
2900 set flist_menu_file $e
2901 set xdiffstate "normal"
2902 if {$cmitmode eq "tree"} {
2903 set xdiffstate "disabled"
2905 # Disable "External diff" item in tree mode
2906 $flist_menu entryconf 2 -state $xdiffstate
2907 tk_popup $flist_menu $X $Y
2910 proc flist_hl {only} {
2911 global flist_menu_file findstring gdttype
2913 set x [shellquote $flist_menu_file]
2914 if {$only || $findstring eq {} || $gdttype ne [mc "touching paths:"]} {
2917 append findstring " " $x
2919 set gdttype [mc "touching paths:"]
2922 proc save_file_from_commit {filename output what} {
2925 if {[catch {exec git show $filename -- > $output} err]} {
2926 if {[string match "fatal: bad revision *" $err]} {
2929 error_popup "Error getting \"$filename\" from $what: $err"
2935 proc external_diff_get_one_file {diffid filename diffdir} {
2936 global nullid nullid2 nullfile
2939 if {$diffid == $nullid} {
2940 set difffile [file join [file dirname $gitdir] $filename]
2941 if {[file exists $difffile]} {
2946 if {$diffid == $nullid2} {
2947 set difffile [file join $diffdir "\[index\] [file tail $filename]"]
2948 return [save_file_from_commit :$filename $difffile index]
2950 set difffile [file join $diffdir "\[$diffid\] [file tail $filename]"]
2951 return [save_file_from_commit $diffid:$filename $difffile \
2955 proc external_diff {} {
2956 global gitktmpdir nullid nullid2
2957 global flist_menu_file
2960 global gitdir extdifftool
2962 if {[llength $diffids] == 1} {
2963 # no reference commit given
2964 set diffidto [lindex $diffids 0]
2965 if {$diffidto eq $nullid} {
2966 # diffing working copy with index
2967 set diffidfrom $nullid2
2968 } elseif {$diffidto eq $nullid2} {
2969 # diffing index with HEAD
2970 set diffidfrom "HEAD"
2972 # use first parent commit
2973 global parentlist selectedline
2974 set diffidfrom [lindex $parentlist $selectedline 0]
2977 set diffidfrom [lindex $diffids 0]
2978 set diffidto [lindex $diffids 1]
2981 # make sure that several diffs wont collide
2982 if {![info exists gitktmpdir]} {
2983 set gitktmpdir [file join [file dirname $gitdir] \
2984 [format ".gitk-tmp.%s" [pid]]]
2985 if {[catch {file mkdir $gitktmpdir} err]} {
2986 error_popup "Error creating temporary directory $gitktmpdir: $err"
2993 set diffdir [file join $gitktmpdir $diffnum]
2994 if {[catch {file mkdir $diffdir} err]} {
2995 error_popup "Error creating temporary directory $diffdir: $err"
2999 # gather files to diff
3000 set difffromfile [external_diff_get_one_file $diffidfrom $flist_menu_file $diffdir]
3001 set difftofile [external_diff_get_one_file $diffidto $flist_menu_file $diffdir]
3003 if {$difffromfile ne {} && $difftofile ne {}} {
3004 set cmd [concat | [shellsplit $extdifftool] \
3005 [list $difffromfile $difftofile]]
3006 if {[catch {set fl [open $cmd r]} err]} {
3007 file delete -force $diffdir
3008 error_popup [mc "$extdifftool: command failed: $err"]
3010 fconfigure $fl -blocking 0
3011 filerun $fl [list delete_at_eof $fl $diffdir]
3016 # delete $dir when we see eof on $f (presumably because the child has exited)
3017 proc delete_at_eof {f dir} {
3018 while {[gets $f line] >= 0} {}
3020 if {[catch {close $f} err]} {
3021 error_popup "External diff viewer failed: $err"
3023 file delete -force $dir
3029 # Functions for adding and removing shell-type quoting
3031 proc shellquote {str} {
3032 if {![string match "*\['\"\\ \t]*" $str]} {
3035 if {![string match "*\['\"\\]*" $str]} {
3038 if {![string match "*'*" $str]} {
3041 return "\"[string map {\" \\\" \\ \\\\} $str]\""
3044 proc shellarglist {l} {
3050 append str [shellquote $a]
3055 proc shelldequote {str} {
3060 if {![regexp -start $used -indices "\['\"\\\\ \t]" $str first]} {
3061 append ret [string range $str $used end]
3062 set used [string length $str]
3065 set first [lindex $first 0]
3066 set ch [string index $str $first]
3067 if {$first > $used} {
3068 append ret [string range $str $used [expr {$first - 1}]]
3071 if {$ch eq " " || $ch eq "\t"} break
3074 set first [string first "'" $str $used]
3076 error "unmatched single-quote"
3078 append ret [string range $str $used [expr {$first - 1}]]
3083 if {$used >= [string length $str]} {
3084 error "trailing backslash"
3086 append ret [string index $str $used]
3091 if {![regexp -start $used -indices "\[\"\\\\]" $str first]} {
3092 error "unmatched double-quote"
3094 set first [lindex $first 0]
3095 set ch [string index $str $first]
3096 if {$first > $used} {
3097 append ret [string range $str $used [expr {$first - 1}]]
3100 if {$ch eq "\""} break
3102 append ret [string index $str $used]
3106 return [list $used $ret]
3109 proc shellsplit {str} {
3112 set str [string trimleft $str]
3113 if {$str eq {}} break
3114 set dq [shelldequote $str]
3115 set n [lindex $dq 0]
3116 set word [lindex $dq 1]
3117 set str [string range $str $n end]
3123 # Code to implement multiple views
3125 proc newview {ishighlight} {
3126 global nextviewnum newviewname newviewperm newishighlight
3127 global newviewargs revtreeargs viewargscmd newviewargscmd curview
3129 set newishighlight $ishighlight
3131 if {[winfo exists $top]} {
3135 set newviewname($nextviewnum) "[mc "View"] $nextviewnum"
3136 set newviewperm($nextviewnum) 0
3137 set newviewargs($nextviewnum) [shellarglist $revtreeargs]
3138 set newviewargscmd($nextviewnum) $viewargscmd($curview)
3139 vieweditor $top $nextviewnum [mc "Gitk view definition"]
3144 global viewname viewperm newviewname newviewperm
3145 global viewargs newviewargs viewargscmd newviewargscmd
3147 set top .gitkvedit-$curview
3148 if {[winfo exists $top]} {
3152 set newviewname($curview) $viewname($curview)
3153 set newviewperm($curview) $viewperm($curview)
3154 set newviewargs($curview) [shellarglist $viewargs($curview)]
3155 set newviewargscmd($curview) $viewargscmd($curview)
3156 vieweditor $top $curview "Gitk: edit view $viewname($curview)"
3159 proc vieweditor {top n title} {
3160 global newviewname newviewperm viewfiles bgcolor
3163 wm title $top $title
3164 label $top.nl -text [mc "Name"]
3165 entry $top.name -width 20 -textvariable newviewname($n)
3166 grid $top.nl $top.name -sticky w -pady 5
3167 checkbutton $top.perm -text [mc "Remember this view"] \
3168 -variable newviewperm($n)
3169 grid $top.perm - -pady 5 -sticky w
3170 message $top.al -aspect 1000 \
3171 -text [mc "Commits to include (arguments to git log):"]
3172 grid $top.al - -sticky w -pady 5
3173 entry $top.args -width 50 -textvariable newviewargs($n) \
3174 -background $bgcolor
3175 grid $top.args - -sticky ew -padx 5
3177 message $top.ac -aspect 1000 \
3178 -text [mc "Command to generate more commits to include:"]
3179 grid $top.ac - -sticky w -pady 5
3180 entry $top.argscmd -width 50 -textvariable newviewargscmd($n) \
3182 grid $top.argscmd - -sticky ew -padx 5
3184 message $top.l -aspect 1000 \
3185 -text [mc "Enter files and directories to include, one per line:"]
3186 grid $top.l - -sticky w
3187 text $top.t -width 40 -height 10 -background $bgcolor -font uifont
3188 if {[info exists viewfiles($n)]} {
3189 foreach f $viewfiles($n) {
3190 $top.t insert end $f
3191 $top.t insert end "\n"
3193 $top.t delete {end - 1c} end
3194 $top.t mark set insert 0.0
3196 grid $top.t - -sticky ew -padx 5
3198 button $top.buts.ok -text [mc "OK"] -command [list newviewok $top $n]
3199 button $top.buts.can -text [mc "Cancel"] -command [list destroy $top]
3200 grid $top.buts.ok $top.buts.can
3201 grid columnconfigure $top.buts 0 -weight 1 -uniform a
3202 grid columnconfigure $top.buts 1 -weight 1 -uniform a
3203 grid $top.buts - -pady 10 -sticky ew
3207 proc doviewmenu {m first cmd op argv} {
3208 set nmenu [$m index end]
3209 for {set i $first} {$i <= $nmenu} {incr i} {
3210 if {[$m entrycget $i -command] eq $cmd} {
3211 eval $m $op $i $argv
3217 proc allviewmenus {n op args} {
3220 doviewmenu .bar.view 5 [list showview $n] $op $args
3221 # doviewmenu $viewhlmenu 1 [list addvhighlight $n] $op $args
3224 proc newviewok {top n} {
3225 global nextviewnum newviewperm newviewname newishighlight
3226 global viewname viewfiles viewperm selectedview curview
3227 global viewargs newviewargs viewargscmd newviewargscmd viewhlmenu
3230 set newargs [shellsplit $newviewargs($n)]
3232 error_popup "[mc "Error in commit selection arguments:"] $err"
3238 foreach f [split [$top.t get 0.0 end] "\n"] {
3239 set ft [string trim $f]
3244 if {![info exists viewfiles($n)]} {
3245 # creating a new view
3247 set viewname($n) $newviewname($n)
3248 set viewperm($n) $newviewperm($n)
3249 set viewfiles($n) $files
3250 set viewargs($n) $newargs
3251 set viewargscmd($n) $newviewargscmd($n)
3253 if {!$newishighlight} {
3256 run addvhighlight $n
3259 # editing an existing view
3260 set viewperm($n) $newviewperm($n)
3261 if {$newviewname($n) ne $viewname($n)} {
3262 set viewname($n) $newviewname($n)
3263 doviewmenu .bar.view 5 [list showview $n] \
3264 entryconf [list -label $viewname($n)]
3265 # doviewmenu $viewhlmenu 1 [list addvhighlight $n] \
3266 # entryconf [list -label $viewname($n) -value $viewname($n)]
3268 if {$files ne $viewfiles($n) || $newargs ne $viewargs($n) || \
3269 $newviewargscmd($n) ne $viewargscmd($n)} {
3270 set viewfiles($n) $files
3271 set viewargs($n) $newargs
3272 set viewargscmd($n) $newviewargscmd($n)
3273 if {$curview == $n} {
3278 catch {destroy $top}
3282 global curview viewperm hlview selectedhlview
3284 if {$curview == 0} return
3285 if {[info exists hlview] && $hlview == $curview} {
3286 set selectedhlview [mc "None"]
3289 allviewmenus $curview delete
3290 set viewperm($curview) 0
3294 proc addviewmenu {n} {
3295 global viewname viewhlmenu
3297 .bar.view add radiobutton -label $viewname($n) \
3298 -command [list showview $n] -variable selectedview -value $n
3299 #$viewhlmenu add radiobutton -label $viewname($n) \
3300 # -command [list addvhighlight $n] -variable selectedhlview
3304 global curview cached_commitrow ordertok
3305 global displayorder parentlist rowidlist rowisopt rowfinal
3306 global colormap rowtextx nextcolor canvxmax
3307 global numcommits viewcomplete
3308 global selectedline currentid canv canvy0
3310 global pending_select mainheadid
3313 global hlview selectedhlview commitinterest
3315 if {$n == $curview} return
3317 set ymax [lindex [$canv cget -scrollregion] 3]
3318 set span [$canv yview]
3319 set ytop [expr {[lindex $span 0] * $ymax}]
3320 set ybot [expr {[lindex $span 1] * $ymax}]
3321 set yscreen [expr {($ybot - $ytop) / 2}]
3322 if {$selectedline ne {}} {
3323 set selid $currentid
3324 set y [yc $selectedline]
3325 if {$ytop < $y && $y < $ybot} {
3326 set yscreen [expr {$y - $ytop}]
3328 } elseif {[info exists pending_select]} {
3329 set selid $pending_select
3330 unset pending_select
3334 catch {unset treediffs}
3336 if {[info exists hlview] && $hlview == $n} {
3338 set selectedhlview [mc "None"]
3340 catch {unset commitinterest}
3341 catch {unset cached_commitrow}
3342 catch {unset ordertok}
3346 .bar.view entryconf [mc "Edit view..."] -state [expr {$n == 0? "disabled": "normal"}]
3347 .bar.view entryconf [mc "Delete view"] -state [expr {$n == 0? "disabled": "normal"}]
3350 if {![info exists viewcomplete($n)]} {
3360 set numcommits $commitidx($n)
3362 catch {unset colormap}
3363 catch {unset rowtextx}
3365 set canvxmax [$canv cget -width]
3371 if {$selid ne {} && [commitinview $selid $n]} {
3372 set row [rowofcommit $selid]
3373 # try to get the selected row in the same position on the screen
3374 set ymax [lindex [$canv cget -scrollregion] 3]
3375 set ytop [expr {[yc $row] - $yscreen}]
3379 set yf [expr {$ytop * 1.0 / $ymax}]
3381 allcanvs yview moveto $yf
3385 } elseif {!$viewcomplete($n)} {
3386 reset_pending_select $selid
3388 reset_pending_select {}
3390 if {[commitinview $pending_select $curview]} {
3391 selectline [rowofcommit $pending_select] 1
3393 set row [first_real_row]
3394 if {$row < $numcommits} {
3399 if {!$viewcomplete($n)} {
3400 if {$numcommits == 0} {
3401 show_status [mc "Reading commits..."]
3403 } elseif {$numcommits == 0} {
3404 show_status [mc "No commits selected"]
3408 # Stuff relating to the highlighting facility
3410 proc ishighlighted {id} {
3411 global vhighlights fhighlights nhighlights rhighlights
3413 if {[info exists nhighlights($id)] && $nhighlights($id) > 0} {
3414 return $nhighlights($id)
3416 if {[info exists vhighlights($id)] && $vhighlights($id) > 0} {
3417 return $vhighlights($id)
3419 if {[info exists fhighlights($id)] && $fhighlights($id) > 0} {
3420 return $fhighlights($id)
3422 if {[info exists rhighlights($id)] && $rhighlights($id) > 0} {
3423 return $rhighlights($id)
3428 proc bolden {row font} {
3429 global canv linehtag selectedline boldrows
3431 lappend boldrows $row
3432 $canv itemconf $linehtag($row) -font $font
3433 if {$row == $selectedline} {
3435 set t [eval $canv create rect [$canv bbox $linehtag($row)] \
3436 -outline {{}} -tags secsel \
3437 -fill [$canv cget -selectbackground]]
3442 proc bolden_name {row font} {
3443 global canv2 linentag selectedline boldnamerows
3445 lappend boldnamerows $row
3446 $canv2 itemconf $linentag($row) -font $font
3447 if {$row == $selectedline} {
3448 $canv2 delete secsel
3449 set t [eval $canv2 create rect [$canv2 bbox $linentag($row)] \
3450 -outline {{}} -tags secsel \
3451 -fill [$canv2 cget -selectbackground]]
3460 foreach row $boldrows {
3461 if {![ishighlighted [commitonrow $row]]} {
3462 bolden $row mainfont
3464 lappend stillbold $row
3467 set boldrows $stillbold
3470 proc addvhighlight {n} {
3471 global hlview viewcomplete curview vhl_done commitidx
3473 if {[info exists hlview]} {
3477 if {$n != $curview && ![info exists viewcomplete($n)]} {
3480 set vhl_done $commitidx($hlview)
3481 if {$vhl_done > 0} {
3486 proc delvhighlight {} {
3487 global hlview vhighlights
3489 if {![info exists hlview]} return
3491 catch {unset vhighlights}
3495 proc vhighlightmore {} {
3496 global hlview vhl_done commitidx vhighlights curview
3498 set max $commitidx($hlview)
3499 set vr [visiblerows]
3500 set r0 [lindex $vr 0]
3501 set r1 [lindex $vr 1]
3502 for {set i $vhl_done} {$i < $max} {incr i} {
3503 set id [commitonrow $i $hlview]
3504 if {[commitinview $id $curview]} {
3505 set row [rowofcommit $id]
3506 if {$r0 <= $row && $row <= $r1} {
3507 if {![highlighted $row]} {
3508 bolden $row mainfontbold
3510 set vhighlights($id) 1
3518 proc askvhighlight {row id} {
3519 global hlview vhighlights iddrawn
3521 if {[commitinview $id $hlview]} {
3522 if {[info exists iddrawn($id)] && ![ishighlighted $id]} {
3523 bolden $row mainfontbold
3525 set vhighlights($id) 1
3527 set vhighlights($id) 0
3531 proc hfiles_change {} {
3532 global highlight_files filehighlight fhighlights fh_serial
3533 global highlight_paths gdttype
3535 if {[info exists filehighlight]} {
3536 # delete previous highlights
3537 catch {close $filehighlight}
3539 catch {unset fhighlights}
3541 unhighlight_filelist
3543 set highlight_paths {}
3544 after cancel do_file_hl $fh_serial
3546 if {$highlight_files ne {}} {
3547 after 300 do_file_hl $fh_serial
3551 proc gdttype_change {name ix op} {
3552 global gdttype highlight_files findstring findpattern
3555 if {$findstring ne {}} {
3556 if {$gdttype eq [mc "containing:"]} {
3557 if {$highlight_files ne {}} {
3558 set highlight_files {}
3563 if {$findpattern ne {}} {
3567 set highlight_files $findstring
3572 # enable/disable findtype/findloc menus too
3575 proc find_change {name ix op} {
3576 global gdttype findstring highlight_files
3579 if {$gdttype eq [mc "containing:"]} {
3582 if {$highlight_files ne $findstring} {
3583 set highlight_files $findstring
3590 proc findcom_change args {
3591 global nhighlights boldnamerows
3592 global findpattern findtype findstring gdttype
3595 # delete previous highlights, if any
3596 foreach row $boldnamerows {
3597 bolden_name $row mainfont
3600 catch {unset nhighlights}
3603 if {$gdttype ne [mc "containing:"] || $findstring eq {}} {
3605 } elseif {$findtype eq [mc "Regexp"]} {
3606 set findpattern $findstring
3608 set e [string map {"*" "\\*" "?" "\\?" "\[" "\\\[" "\\" "\\\\"} \
3610 set findpattern "*$e*"
3614 proc makepatterns {l} {
3617 set ee [string map {"*" "\\*" "?" "\\?" "\[" "\\\[" "\\" "\\\\"} $e]
3618 if {[string index $ee end] eq "/"} {
3628 proc do_file_hl {serial} {
3629 global highlight_files filehighlight highlight_paths gdttype fhl_list
3631 if {$gdttype eq [mc "touching paths:"]} {
3632 if {[catch {set paths [shellsplit $highlight_files]}]} return
3633 set highlight_paths [makepatterns $paths]
3635 set gdtargs [concat -- $paths]
3636 } elseif {$gdttype eq [mc "adding/removing string:"]} {
3637 set gdtargs [list "-S$highlight_files"]
3639 # must be "containing:", i.e. we're searching commit info
3642 set cmd [concat | git diff-tree -r -s --stdin $gdtargs]
3643 set filehighlight [open $cmd r+]
3644 fconfigure $filehighlight -blocking 0
3645 filerun $filehighlight readfhighlight
3651 proc flushhighlights {} {
3652 global filehighlight fhl_list
3654 if {[info exists filehighlight]} {
3656 puts $filehighlight ""
3657 flush $filehighlight
3661 proc askfilehighlight {row id} {
3662 global filehighlight fhighlights fhl_list
3664 lappend fhl_list $id
3665 set fhighlights($id) -1
3666 puts $filehighlight $id
3669 proc readfhighlight {} {
3670 global filehighlight fhighlights curview iddrawn
3671 global fhl_list find_dirn
3673 if {![info exists filehighlight]} {
3677 while {[incr nr] <= 100 && [gets $filehighlight line] >= 0} {
3678 set line [string trim $line]
3679 set i [lsearch -exact $fhl_list $line]
3680 if {$i < 0} continue
3681 for {set j 0} {$j < $i} {incr j} {
3682 set id [lindex $fhl_list $j]
3683 set fhighlights($id) 0
3685 set fhl_list [lrange $fhl_list [expr {$i+1}] end]
3686 if {$line eq {}} continue
3687 if {![commitinview $line $curview]} continue
3688 set row [rowofcommit $line]
3689 if {[info exists iddrawn($line)] && ![ishighlighted $line]} {
3690 bolden $row mainfontbold
3692 set fhighlights($line) 1
3694 if {[eof $filehighlight]} {
3696 puts "oops, git diff-tree died"
3697 catch {close $filehighlight}
3701 if {[info exists find_dirn]} {
3707 proc doesmatch {f} {
3708 global findtype findpattern
3710 if {$findtype eq [mc "Regexp"]} {
3711 return [regexp $findpattern $f]
3712 } elseif {$findtype eq [mc "IgnCase"]} {
3713 return [string match -nocase $findpattern $f]
3715 return [string match $findpattern $f]
3719 proc askfindhighlight {row id} {
3720 global nhighlights commitinfo iddrawn
3722 global markingmatches
3724 if {![info exists commitinfo($id)]} {
3727 set info $commitinfo($id)
3729 set fldtypes [list [mc Headline] [mc Author] [mc Date] [mc Committer] [mc CDate] [mc Comments]]
3730 foreach f $info ty $fldtypes {
3731 if {($findloc eq [mc "All fields"] || $findloc eq $ty) &&
3733 if {$ty eq [mc "Author"]} {
3740 if {$isbold && [info exists iddrawn($id)]} {
3741 if {![ishighlighted $id]} {
3742 bolden $row mainfontbold
3744 bolden_name $row mainfontbold
3747 if {$markingmatches} {
3748 markrowmatches $row $id
3751 set nhighlights($id) $isbold
3754 proc markrowmatches {row id} {
3755 global canv canv2 linehtag linentag commitinfo findloc
3757 set headline [lindex $commitinfo($id) 0]
3758 set author [lindex $commitinfo($id) 1]
3759 $canv delete match$row
3760 $canv2 delete match$row
3761 if {$findloc eq [mc "All fields"] || $findloc eq [mc "Headline"]} {
3762 set m [findmatches $headline]
3764 markmatches $canv $row $headline $linehtag($row) $m \
3765 [$canv itemcget $linehtag($row) -font] $row
3768 if {$findloc eq [mc "All fields"] || $findloc eq [mc "Author"]} {
3769 set m [findmatches $author]
3771 markmatches $canv2 $row $author $linentag($row) $m \
3772 [$canv2 itemcget $linentag($row) -font] $row
3777 proc vrel_change {name ix op} {
3778 global highlight_related
3781 if {$highlight_related ne [mc "None"]} {
3786 # prepare for testing whether commits are descendents or ancestors of a
3787 proc rhighlight_sel {a} {
3788 global descendent desc_todo ancestor anc_todo
3789 global highlight_related
3791 catch {unset descendent}
3792 set desc_todo [list $a]
3793 catch {unset ancestor}
3794 set anc_todo [list $a]
3795 if {$highlight_related ne [mc "None"]} {
3801 proc rhighlight_none {} {
3804 catch {unset rhighlights}
3808 proc is_descendent {a} {
3809 global curview children descendent desc_todo
3812 set la [rowofcommit $a]
3816 for {set i 0} {$i < [llength $todo]} {incr i} {
3817 set do [lindex $todo $i]
3818 if {[rowofcommit $do] < $la} {
3819 lappend leftover $do
3822 foreach nk $children($v,$do) {
3823 if {![info exists descendent($nk)]} {
3824 set descendent($nk) 1
3832 set desc_todo [concat $leftover [lrange $todo [expr {$i+1}] end]]
3836 set descendent($a) 0
3837 set desc_todo $leftover
3840 proc is_ancestor {a} {
3841 global curview parents ancestor anc_todo
3844 set la [rowofcommit $a]
3848 for {set i 0} {$i < [llength $todo]} {incr i} {
3849 set do [lindex $todo $i]
3850 if {![commitinview $do $v] || [rowofcommit $do] > $la} {
3851 lappend leftover $do
3854 foreach np $parents($v,$do) {
3855 if {![info exists ancestor($np)]} {
3864 set anc_todo [concat $leftover [lrange $todo [expr {$i+1}] end]]
3869 set anc_todo $leftover
3872 proc askrelhighlight {row id} {
3873 global descendent highlight_related iddrawn rhighlights
3874 global selectedline ancestor
3876 if {$selectedline eq {}} return
3878 if {$highlight_related eq [mc "Descendant"] ||
3879 $highlight_related eq [mc "Not descendant"]} {
3880 if {![info exists descendent($id)]} {
3883 if {$descendent($id) == ($highlight_related eq [mc "Descendant"])} {
3886 } elseif {$highlight_related eq [mc "Ancestor"] ||
3887 $highlight_related eq [mc "Not ancestor"]} {
3888 if {![info exists ancestor($id)]} {
3891 if {$ancestor($id) == ($highlight_related eq [mc "Ancestor"])} {
3895 if {[info exists iddrawn($id)]} {
3896 if {$isbold && ![ishighlighted $id]} {
3897 bolden $row mainfontbold
3900 set rhighlights($id) $isbold
3903 # Graph layout functions
3905 proc shortids {ids} {
3908 if {[llength $id] > 1} {
3909 lappend res [shortids $id]
3910 } elseif {[regexp {^[0-9a-f]{40}$} $id]} {
3911 lappend res [string range $id 0 7]
3922 for {set mask 1} {$mask <= $n} {incr mask $mask} {
3923 if {($n & $mask) != 0} {
3924 set ret [concat $ret $o]
3926 set o [concat $o $o]
3931 proc ordertoken {id} {
3932 global ordertok curview varcid varcstart varctok curview parents children
3933 global nullid nullid2
3935 if {[info exists ordertok($id)]} {
3936 return $ordertok($id)
3941 if {[info exists varcid($curview,$id)]} {
3942 set a $varcid($curview,$id)
3943 set p [lindex $varcstart($curview) $a]
3945 set p [lindex $children($curview,$id) 0]
3947 if {[info exists ordertok($p)]} {
3948 set tok $ordertok($p)
3951 set id [first_real_child $curview,$p]
3954 set tok [lindex $varctok($curview) $varcid($curview,$p)]
3957 if {[llength $parents($curview,$id)] == 1} {
3958 lappend todo [list $p {}]
3960 set j [lsearch -exact $parents($curview,$id) $p]
3962 puts "oops didn't find [shortids $p] in parents of [shortids $id]"
3964 lappend todo [list $p [strrep $j]]
3967 for {set i [llength $todo]} {[incr i -1] >= 0} {} {
3968 set p [lindex $todo $i 0]
3969 append tok [lindex $todo $i 1]
3970 set ordertok($p) $tok
3972 set ordertok($origid) $tok
3976 # Work out where id should go in idlist so that order-token
3977 # values increase from left to right
3978 proc idcol {idlist id {i 0}} {
3979 set t [ordertoken $id]
3983 if {$i >= [llength $idlist] || $t < [ordertoken [lindex $idlist $i]]} {
3984 if {$i > [llength $idlist]} {
3985 set i [llength $idlist]
3987 while {[incr i -1] >= 0 && $t < [ordertoken [lindex $idlist $i]]} {}
3990 if {$t > [ordertoken [lindex $idlist $i]]} {
3991 while {[incr i] < [llength $idlist] &&
3992 $t >= [ordertoken [lindex $idlist $i]]} {}
3998 proc initlayout {} {
3999 global rowidlist rowisopt rowfinal displayorder parentlist
4000 global numcommits canvxmax canv
4002 global colormap rowtextx
4011 set canvxmax [$canv cget -width]
4012 catch {unset colormap}
4013 catch {unset rowtextx}
4017 proc setcanvscroll {} {
4018 global canv canv2 canv3 numcommits linespc canvxmax canvy0
4019 global lastscrollset lastscrollrows
4021 set ymax [expr {$canvy0 + ($numcommits - 0.5) * $linespc + 2}]
4022 $canv conf -scrollregion [list 0 0 $canvxmax $ymax]
4023 $canv2 conf -scrollregion [list 0 0 0 $ymax]
4024 $canv3 conf -scrollregion [list 0 0 0 $ymax]
4025 set lastscrollset [clock clicks -milliseconds]
4026 set lastscrollrows $numcommits
4029 proc visiblerows {} {
4030 global canv numcommits linespc
4032 set ymax [lindex [$canv cget -scrollregion] 3]
4033 if {$ymax eq {} || $ymax == 0} return
4035 set y0 [expr {int([lindex $f 0] * $ymax)}]
4036 set r0 [expr {int(($y0 - 3) / $linespc) - 1}]
4040 set y1 [expr {int([lindex $f 1] * $ymax)}]
4041 set r1 [expr {int(($y1 - 3) / $linespc) + 1}]
4042 if {$r1 >= $numcommits} {
4043 set r1 [expr {$numcommits - 1}]
4045 return [list $r0 $r1]
4048 proc layoutmore {} {
4049 global commitidx viewcomplete curview
4050 global numcommits pending_select curview
4051 global lastscrollset lastscrollrows commitinterest
4053 if {$lastscrollrows < 100 || $viewcomplete($curview) ||
4054 [clock clicks -milliseconds] - $lastscrollset > 500} {
4057 if {[info exists pending_select] &&
4058 [commitinview $pending_select $curview]} {
4060 selectline [rowofcommit $pending_select] 1
4065 proc doshowlocalchanges {} {
4066 global curview mainheadid
4068 if {$mainheadid eq {}} return
4069 if {[commitinview $mainheadid $curview]} {
4072 lappend commitinterest($mainheadid) {dodiffindex}
4076 proc dohidelocalchanges {} {
4077 global nullid nullid2 lserial curview
4079 if {[commitinview $nullid $curview]} {
4080 removefakerow $nullid
4082 if {[commitinview $nullid2 $curview]} {
4083 removefakerow $nullid2
4088 # spawn off a process to do git diff-index --cached HEAD
4089 proc dodiffindex {} {
4090 global lserial showlocalchanges
4093 if {!$showlocalchanges || !$isworktree} return
4095 set fd [open "|git diff-index --cached HEAD" r]
4096 fconfigure $fd -blocking 0
4097 set i [reg_instance $fd]
4098 filerun $fd [list readdiffindex $fd $lserial $i]
4101 proc readdiffindex {fd serial inst} {
4102 global mainheadid nullid nullid2 curview commitinfo commitdata lserial
4105 if {[gets $fd line] < 0} {
4111 # we only need to see one line and we don't really care what it says...
4114 if {$serial != $lserial} {
4118 # now see if there are any local changes not checked in to the index
4119 set fd [open "|git diff-files" r]
4120 fconfigure $fd -blocking 0
4121 set i [reg_instance $fd]
4122 filerun $fd [list readdifffiles $fd $serial $i]
4124 if {$isdiff && ![commitinview $nullid2 $curview]} {
4125 # add the line for the changes in the index to the graph
4126 set hl [mc "Local changes checked in to index but not committed"]
4127 set commitinfo($nullid2) [list $hl {} {} {} {} " $hl\n"]
4128 set commitdata($nullid2) "\n $hl\n"
4129 if {[commitinview $nullid $curview]} {
4130 removefakerow $nullid
4132 insertfakerow $nullid2 $mainheadid
4133 } elseif {!$isdiff && [commitinview $nullid2 $curview]} {
4134 removefakerow $nullid2
4139 proc readdifffiles {fd serial inst} {
4140 global mainheadid nullid nullid2 curview
4141 global commitinfo commitdata lserial
4144 if {[gets $fd line] < 0} {
4150 # we only need to see one line and we don't really care what it says...
4153 if {$serial != $lserial} {
4157 if {$isdiff && ![commitinview $nullid $curview]} {
4158 # add the line for the local diff to the graph
4159 set hl [mc "Local uncommitted changes, not checked in to index"]
4160 set commitinfo($nullid) [list $hl {} {} {} {} " $hl\n"]
4161 set commitdata($nullid) "\n $hl\n"
4162 if {[commitinview $nullid2 $curview]} {
4167 insertfakerow $nullid $p
4168 } elseif {!$isdiff && [commitinview $nullid $curview]} {
4169 removefakerow $nullid
4174 proc nextuse {id row} {
4175 global curview children
4177 if {[info exists children($curview,$id)]} {
4178 foreach kid $children($curview,$id) {
4179 if {![commitinview $kid $curview]} {
4182 if {[rowofcommit $kid] > $row} {
4183 return [rowofcommit $kid]
4187 if {[commitinview $id $curview]} {
4188 return [rowofcommit $id]
4193 proc prevuse {id row} {
4194 global curview children
4197 if {[info exists children($curview,$id)]} {
4198 foreach kid $children($curview,$id) {
4199 if {![commitinview $kid $curview]} break
4200 if {[rowofcommit $kid] < $row} {
4201 set ret [rowofcommit $kid]
4208 proc make_idlist {row} {
4209 global displayorder parentlist uparrowlen downarrowlen mingaplen
4210 global commitidx curview children
4212 set r [expr {$row - $mingaplen - $downarrowlen - 1}]
4216 set ra [expr {$row - $downarrowlen}]
4220 set rb [expr {$row + $uparrowlen}]
4221 if {$rb > $commitidx($curview)} {
4222 set rb $commitidx($curview)
4224 make_disporder $r [expr {$rb + 1}]
4226 for {} {$r < $ra} {incr r} {
4227 set nextid [lindex $displayorder [expr {$r + 1}]]
4228 foreach p [lindex $parentlist $r] {
4229 if {$p eq $nextid} continue
4230 set rn [nextuse $p $r]
4232 $rn <= $r + $downarrowlen + $mingaplen + $uparrowlen} {
4233 lappend ids [list [ordertoken $p] $p]
4237 for {} {$r < $row} {incr r} {
4238 set nextid [lindex $displayorder [expr {$r + 1}]]
4239 foreach p [lindex $parentlist $r] {
4240 if {$p eq $nextid} continue
4241 set rn [nextuse $p $r]
4242 if {$rn < 0 || $rn >= $row} {
4243 lappend ids [list [ordertoken $p] $p]
4247 set id [lindex $displayorder $row]
4248 lappend ids [list [ordertoken $id] $id]
4250 foreach p [lindex $parentlist $r] {
4251 set firstkid [lindex $children($curview,$p) 0]
4252 if {[rowofcommit $firstkid] < $row} {
4253 lappend ids [list [ordertoken $p] $p]
4257 set id [lindex $displayorder $r]
4259 set firstkid [lindex $children($curview,$id) 0]
4260 if {$firstkid ne {} && [rowofcommit $firstkid] < $row} {
4261 lappend ids [list [ordertoken $id] $id]
4266 foreach idx [lsort -unique $ids] {
4267 lappend idlist [lindex $idx 1]
4272 proc rowsequal {a b} {
4273 while {[set i [lsearch -exact $a {}]] >= 0} {
4274 set a [lreplace $a $i $i]
4276 while {[set i [lsearch -exact $b {}]] >= 0} {
4277 set b [lreplace $b $i $i]
4279 return [expr {$a eq $b}]
4282 proc makeupline {id row rend col} {
4283 global rowidlist uparrowlen downarrowlen mingaplen
4285 for {set r $rend} {1} {set r $rstart} {
4286 set rstart [prevuse $id $r]
4287 if {$rstart < 0} return
4288 if {$rstart < $row} break
4290 if {$rstart + $uparrowlen + $mingaplen + $downarrowlen < $rend} {
4291 set rstart [expr {$rend - $uparrowlen - 1}]
4293 for {set r $rstart} {[incr r] <= $row} {} {
4294 set idlist [lindex $rowidlist $r]
4295 if {$idlist ne {} && [lsearch -exact $idlist $id] < 0} {
4296 set col [idcol $idlist $id $col]
4297 lset rowidlist $r [linsert $idlist $col $id]
4303 proc layoutrows {row endrow} {
4304 global rowidlist rowisopt rowfinal displayorder
4305 global uparrowlen downarrowlen maxwidth mingaplen
4306 global children parentlist
4307 global commitidx viewcomplete curview
4309 make_disporder [expr {$row - 1}] [expr {$endrow + $uparrowlen}]
4312 set rm1 [expr {$row - 1}]
4313 foreach id [lindex $rowidlist $rm1] {
4318 set final [lindex $rowfinal $rm1]
4320 for {} {$row < $endrow} {incr row} {
4321 set rm1 [expr {$row - 1}]
4322 if {$rm1 < 0 || $idlist eq {}} {
4323 set idlist [make_idlist $row]
4326 set id [lindex $displayorder $rm1]
4327 set col [lsearch -exact $idlist $id]
4328 set idlist [lreplace $idlist $col $col]
4329 foreach p [lindex $parentlist $rm1] {
4330 if {[lsearch -exact $idlist $p] < 0} {
4331 set col [idcol $idlist $p $col]
4332 set idlist [linsert $idlist $col $p]
4333 # if not the first child, we have to insert a line going up
4334 if {$id ne [lindex $children($curview,$p) 0]} {
4335 makeupline $p $rm1 $row $col
4339 set id [lindex $displayorder $row]
4340 if {$row > $downarrowlen} {
4341 set termrow [expr {$row - $downarrowlen - 1}]
4342 foreach p [lindex $parentlist $termrow] {
4343 set i [lsearch -exact $idlist $p]
4344 if {$i < 0} continue
4345 set nr [nextuse $p $termrow]
4346 if {$nr < 0 || $nr >= $row + $mingaplen + $uparrowlen} {
4347 set idlist [lreplace $idlist $i $i]
4351 set col [lsearch -exact $idlist $id]
4353 set col [idcol $idlist $id]
4354 set idlist [linsert $idlist $col $id]
4355 if {$children($curview,$id) ne {}} {
4356 makeupline $id $rm1 $row $col
4359 set r [expr {$row + $uparrowlen - 1}]
4360 if {$r < $commitidx($curview)} {
4362 foreach p [lindex $parentlist $r] {
4363 if {[lsearch -exact $idlist $p] >= 0} continue
4364 set fk [lindex $children($curview,$p) 0]
4365 if {[rowofcommit $fk] < $row} {
4366 set x [idcol $idlist $p $x]
4367 set idlist [linsert $idlist $x $p]
4370 if {[incr r] < $commitidx($curview)} {
4371 set p [lindex $displayorder $r]
4372 if {[lsearch -exact $idlist $p] < 0} {
4373 set fk [lindex $children($curview,$p) 0]
4374 if {$fk ne {} && [rowofcommit $fk] < $row} {
4375 set x [idcol $idlist $p $x]
4376 set idlist [linsert $idlist $x $p]
4382 if {$final && !$viewcomplete($curview) &&
4383 $row + $uparrowlen + $mingaplen + $downarrowlen
4384 >= $commitidx($curview)} {
4387 set l [llength $rowidlist]
4389 lappend rowidlist $idlist
4391 lappend rowfinal $final
4392 } elseif {$row < $l} {
4393 if {![rowsequal $idlist [lindex $rowidlist $row]]} {
4394 lset rowidlist $row $idlist
4397 lset rowfinal $row $final
4399 set pad [ntimes [expr {$row - $l}] {}]
4400 set rowidlist [concat $rowidlist $pad]
4401 lappend rowidlist $idlist
4402 set rowfinal [concat $rowfinal $pad]
4403 lappend rowfinal $final
4404 set rowisopt [concat $rowisopt [ntimes [expr {$row - $l + 1}] 0]]
4410 proc changedrow {row} {
4411 global displayorder iddrawn rowisopt need_redisplay
4413 set l [llength $rowisopt]
4415 lset rowisopt $row 0
4416 if {$row + 1 < $l} {
4417 lset rowisopt [expr {$row + 1}] 0
4418 if {$row + 2 < $l} {
4419 lset rowisopt [expr {$row + 2}] 0
4423 set id [lindex $displayorder $row]
4424 if {[info exists iddrawn($id)]} {
4425 set need_redisplay 1
4429 proc insert_pad {row col npad} {
4432 set pad [ntimes $npad {}]
4433 set idlist [lindex $rowidlist $row]
4434 set bef [lrange $idlist 0 [expr {$col - 1}]]
4435 set aft [lrange $idlist $col end]
4436 set i [lsearch -exact $aft {}]
4438 set aft [lreplace $aft $i $i]
4440 lset rowidlist $row [concat $bef $pad $aft]
4444 proc optimize_rows {row col endrow} {
4445 global rowidlist rowisopt displayorder curview children
4450 for {} {$row < $endrow} {incr row; set col 0} {
4451 if {[lindex $rowisopt $row]} continue
4453 set y0 [expr {$row - 1}]
4454 set ym [expr {$row - 2}]
4455 set idlist [lindex $rowidlist $row]
4456 set previdlist [lindex $rowidlist $y0]
4457 if {$idlist eq {} || $previdlist eq {}} continue
4459 set pprevidlist [lindex $rowidlist $ym]
4460 if {$pprevidlist eq {}} continue
4466 for {} {$col < [llength $idlist]} {incr col} {
4467 set id [lindex $idlist $col]
4468 if {[lindex $previdlist $col] eq $id} continue
4473 set x0 [lsearch -exact $previdlist $id]
4474 if {$x0 < 0} continue
4475 set z [expr {$x0 - $col}]
4479 set xm [lsearch -exact $pprevidlist $id]
4481 set z0 [expr {$xm - $x0}]
4485 # if row y0 is the first child of $id then it's not an arrow
4486 if {[lindex $children($curview,$id) 0] ne
4487 [lindex $displayorder $y0]} {
4491 if {!$isarrow && $id ne [lindex $displayorder $row] &&
4492 [lsearch -exact [lindex $rowidlist [expr {$row+1}]] $id] < 0} {
4495 # Looking at lines from this row to the previous row,
4496 # make them go straight up if they end in an arrow on
4497 # the previous row; otherwise make them go straight up
4499 if {$z < -1 || ($z < 0 && $isarrow)} {
4500 # Line currently goes left too much;
4501 # insert pads in the previous row, then optimize it
4502 set npad [expr {-1 - $z + $isarrow}]
4503 insert_pad $y0 $x0 $npad
4505 optimize_rows $y0 $x0 $row
4507 set previdlist [lindex $rowidlist $y0]
4508 set x0 [lsearch -exact $previdlist $id]
4509 set z [expr {$x0 - $col}]
4511 set pprevidlist [lindex $rowidlist $ym]
4512 set xm [lsearch -exact $pprevidlist $id]
4513 set z0 [expr {$xm - $x0}]
4515 } elseif {$z > 1 || ($z > 0 && $isarrow)} {
4516 # Line currently goes right too much;
4517 # insert pads in this line
4518 set npad [expr {$z - 1 + $isarrow}]
4519 insert_pad $row $col $npad
4520 set idlist [lindex $rowidlist $row]
4522 set z [expr {$x0 - $col}]
4525 if {$z0 eq {} && !$isarrow && $ym >= 0} {
4526 # this line links to its first child on row $row-2
4527 set id [lindex $displayorder $ym]
4528 set xc [lsearch -exact $pprevidlist $id]
4530 set z0 [expr {$xc - $x0}]
4533 # avoid lines jigging left then immediately right
4534 if {$z0 ne {} && $z < 0 && $z0 > 0} {
4535 insert_pad $y0 $x0 1
4537 optimize_rows $y0 $x0 $row
4538 set previdlist [lindex $rowidlist $y0]
4542 # Find the first column that doesn't have a line going right
4543 for {set col [llength $idlist]} {[incr col -1] >= 0} {} {
4544 set id [lindex $idlist $col]
4545 if {$id eq {}} break
4546 set x0 [lsearch -exact $previdlist $id]
4548 # check if this is the link to the first child
4549 set kid [lindex $displayorder $y0]
4550 if {[lindex $children($curview,$id) 0] eq $kid} {
4551 # it is, work out offset to child
4552 set x0 [lsearch -exact $previdlist $kid]
4555 if {$x0 <= $col} break
4557 # Insert a pad at that column as long as it has a line and
4558 # isn't the last column
4559 if {$x0 >= 0 && [incr col] < [llength $idlist]} {
4560 set idlist [linsert $idlist $col {}]
4561 lset rowidlist $row $idlist
4569 global canvx0 linespc
4570 return [expr {$canvx0 + $col * $linespc}]
4574 global canvy0 linespc
4575 return [expr {$canvy0 + $row * $linespc}]
4578 proc linewidth {id} {
4579 global thickerline lthickness
4582 if {[info exists thickerline] && $id eq $thickerline} {
4583 set wid [expr {2 * $lthickness}]
4588 proc rowranges {id} {
4589 global curview children uparrowlen downarrowlen
4592 set kids $children($curview,$id)
4598 foreach child $kids {
4599 if {![commitinview $child $curview]} break
4600 set row [rowofcommit $child]
4601 if {![info exists prev]} {
4602 lappend ret [expr {$row + 1}]
4604 if {$row <= $prevrow} {
4605 puts "oops children of [shortids $id] out of order [shortids $child] $row <= [shortids $prev] $prevrow"
4607 # see if the line extends the whole way from prevrow to row
4608 if {$row > $prevrow + $uparrowlen + $downarrowlen &&
4609 [lsearch -exact [lindex $rowidlist \
4610 [expr {int(($row + $prevrow) / 2)}]] $id] < 0} {
4611 # it doesn't, see where it ends
4612 set r [expr {$prevrow + $downarrowlen}]
4613 if {[lsearch -exact [lindex $rowidlist $r] $id] < 0} {
4614 while {[incr r -1] > $prevrow &&
4615 [lsearch -exact [lindex $rowidlist $r] $id] < 0} {}
4617 while {[incr r] <= $row &&
4618 [lsearch -exact [lindex $rowidlist $r] $id] >= 0} {}
4622 # see where it starts up again
4623 set r [expr {$row - $uparrowlen}]
4624 if {[lsearch -exact [lindex $rowidlist $r] $id] < 0} {
4625 while {[incr r] < $row &&
4626 [lsearch -exact [lindex $rowidlist $r] $id] < 0} {}
4628 while {[incr r -1] >= $prevrow &&
4629 [lsearch -exact [lindex $rowidlist $r] $id] >= 0} {}
4635 if {$child eq $id} {
4644 proc drawlineseg {id row endrow arrowlow} {
4645 global rowidlist displayorder iddrawn linesegs
4646 global canv colormap linespc curview maxlinelen parentlist
4648 set cols [list [lsearch -exact [lindex $rowidlist $row] $id]]
4649 set le [expr {$row + 1}]
4652 set c [lsearch -exact [lindex $rowidlist $le] $id]
4658 set x [lindex $displayorder $le]
4663 if {[info exists iddrawn($x)] || $le == $endrow} {
4664 set c [lsearch -exact [lindex $rowidlist [expr {$le+1}]] $id]
4680 if {[info exists linesegs($id)]} {
4681 set lines $linesegs($id)
4683 set r0 [lindex $li 0]
4685 if {$r0 == $le && [lindex $li 1] - $row <= $maxlinelen} {
4695 set li [lindex $lines [expr {$i-1}]]
4696 set r1 [lindex $li 1]
4697 if {$r1 == $row && $le - [lindex $li 0] <= $maxlinelen} {
4702 set x [lindex $cols [expr {$le - $row}]]
4703 set xp [lindex $cols [expr {$le - 1 - $row}]]
4704 set dir [expr {$xp - $x}]
4706 set ith [lindex $lines $i 2]
4707 set coords [$canv coords $ith]
4708 set ah [$canv itemcget $ith -arrow]
4709 set arrowhigh [expr {$ah eq "first" || $ah eq "both"}]
4710 set x2 [lindex $cols [expr {$le + 1 - $row}]]
4711 if {$x2 ne {} && $x - $x2 == $dir} {
4712 set coords [lrange $coords 0 end-2]
4715 set coords [list [xc $le $x] [yc $le]]
4718 set itl [lindex $lines [expr {$i-1}] 2]
4719 set al [$canv itemcget $itl -arrow]
4720 set arrowlow [expr {$al eq "last" || $al eq "both"}]
4721 } elseif {$arrowlow} {
4722 if {[lsearch -exact [lindex $rowidlist [expr {$row-1}]] $id] >= 0 ||
4723 [lsearch -exact [lindex $parentlist [expr {$row-1}]] $id] >= 0} {
4727 set arrow [lindex {none first last both} [expr {$arrowhigh + 2*$arrowlow}]]
4728 for {set y $le} {[incr y -1] > $row} {} {
4730 set xp [lindex $cols [expr {$y - 1 - $row}]]
4731 set ndir [expr {$xp - $x}]
4732 if {$dir != $ndir || $xp < 0} {
4733 lappend coords [xc $y $x] [yc $y]
4739 # join parent line to first child
4740 set ch [lindex $displayorder $row]
4741 set xc [lsearch -exact [lindex $rowidlist $row] $ch]
4743 puts "oops: drawlineseg: child $ch not on row $row"
4744 } elseif {$xc != $x} {
4745 if {($arrowhigh && $le == $row + 1) || $dir == 0} {
4746 set d [expr {int(0.5 * $linespc)}]
4749 set x2 [expr {$x1 - $d}]
4751 set x2 [expr {$x1 + $d}]
4754 set y1 [expr {$y2 + $d}]
4755 lappend coords $x1 $y1 $x2 $y2
4756 } elseif {$xc < $x - 1} {
4757 lappend coords [xc $row [expr {$x-1}]] [yc $row]
4758 } elseif {$xc > $x + 1} {
4759 lappend coords [xc $row [expr {$x+1}]] [yc $row]
4763 lappend coords [xc $row $x] [yc $row]
4765 set xn [xc $row $xp]
4767 lappend coords $xn $yn
4771 set t [$canv create line $coords -width [linewidth $id] \
4772 -fill $colormap($id) -tags lines.$id -arrow $arrow]
4775 set lines [linsert $lines $i [list $row $le $t]]
4777 $canv coords $ith $coords
4778 if {$arrow ne $ah} {
4779 $canv itemconf $ith -arrow $arrow
4781 lset lines $i 0 $row
4784 set xo [lsearch -exact [lindex $rowidlist [expr {$row - 1}]] $id]
4785 set ndir [expr {$xo - $xp}]
4786 set clow [$canv coords $itl]
4787 if {$dir == $ndir} {
4788 set clow [lrange $clow 2 end]
4790 set coords [concat $coords $clow]
4792 lset lines [expr {$i-1}] 1 $le
4794 # coalesce two pieces
4796 set b [lindex $lines [expr {$i-1}] 0]
4797 set e [lindex $lines $i 1]
4798 set lines [lreplace $lines [expr {$i-1}] $i [list $b $e $itl]]
4800 $canv coords $itl $coords
4801 if {$arrow ne $al} {
4802 $canv itemconf $itl -arrow $arrow
4806 set linesegs($id) $lines
4810 proc drawparentlinks {id row} {
4811 global rowidlist canv colormap curview parentlist
4812 global idpos linespc
4814 set rowids [lindex $rowidlist $row]
4815 set col [lsearch -exact $rowids $id]
4816 if {$col < 0} return
4817 set olds [lindex $parentlist $row]
4818 set row2 [expr {$row + 1}]
4819 set x [xc $row $col]
4822 set d [expr {int(0.5 * $linespc)}]
4823 set ymid [expr {$y + $d}]
4824 set ids [lindex $rowidlist $row2]
4825 # rmx = right-most X coord used
4828 set i [lsearch -exact $ids $p]
4830 puts "oops, parent $p of $id not in list"
4833 set x2 [xc $row2 $i]
4837 set j [lsearch -exact $rowids $p]
4839 # drawlineseg will do this one for us
4843 # should handle duplicated parents here...
4844 set coords [list $x $y]
4846 # if attaching to a vertical segment, draw a smaller
4847 # slant for visual distinctness
4850 lappend coords [expr {$x2 + $d}] $y $x2 $ymid
4852 lappend coords [expr {$x2 - $d}] $y $x2 $ymid
4854 } elseif {$i < $col && $i < $j} {
4855 # segment slants towards us already
4856 lappend coords [xc $row $j] $y
4858 if {$i < $col - 1} {
4859 lappend coords [expr {$x2 + $linespc}] $y
4860 } elseif {$i > $col + 1} {
4861 lappend coords [expr {$x2 - $linespc}] $y
4863 lappend coords $x2 $y2
4866 lappend coords $x2 $y2
4868 set t [$canv create line $coords -width [linewidth $p] \
4869 -fill $colormap($p) -tags lines.$p]
4873 if {$rmx > [lindex $idpos($id) 1]} {
4874 lset idpos($id) 1 $rmx
4879 proc drawlines {id} {
4882 $canv itemconf lines.$id -width [linewidth $id]
4885 proc drawcmittext {id row col} {
4886 global linespc canv canv2 canv3 fgcolor curview
4887 global cmitlisted commitinfo rowidlist parentlist
4888 global rowtextx idpos idtags idheads idotherrefs
4889 global linehtag linentag linedtag selectedline
4890 global canvxmax boldrows boldnamerows fgcolor
4891 global mainheadid nullid nullid2 circleitem circlecolors
4893 # listed is 0 for boundary, 1 for normal, 2 for negative, 3 for left, 4 for right
4894 set listed $cmitlisted($curview,$id)
4895 if {$id eq $nullid} {
4897 } elseif {$id eq $nullid2} {
4899 } elseif {$id eq $mainheadid} {
4902 set ofill [lindex $circlecolors $listed]
4904 set x [xc $row $col]
4906 set orad [expr {$linespc / 3}]
4908 set t [$canv create oval [expr {$x - $orad}] [expr {$y - $orad}] \
4909 [expr {$x + $orad - 1}] [expr {$y + $orad - 1}] \
4910 -fill $ofill -outline $fgcolor -width 1 -tags circle]
4911 } elseif {$listed == 3} {
4912 # triangle pointing left for left-side commits
4913 set t [$canv create polygon \
4914 [expr {$x - $orad}] $y \
4915 [expr {$x + $orad - 1}] [expr {$y - $orad}] \
4916 [expr {$x + $orad - 1}] [expr {$y + $orad - 1}] \
4917 -fill $ofill -outline $fgcolor -width 1 -tags circle]
4919 # triangle pointing right for right-side commits
4920 set t [$canv create polygon \
4921 [expr {$x + $orad - 1}] $y \
4922 [expr {$x - $orad}] [expr {$y - $orad}] \
4923 [expr {$x - $orad}] [expr {$y + $orad - 1}] \
4924 -fill $ofill -outline $fgcolor -width 1 -tags circle]
4926 set circleitem($row) $t
4928 $canv bind $t <1> {selcanvline {} %x %y}
4929 set rmx [llength [lindex $rowidlist $row]]
4930 set olds [lindex $parentlist $row]
4932 set nextids [lindex $rowidlist [expr {$row + 1}]]
4934 set i [lsearch -exact $nextids $p]
4940 set xt [xc $row $rmx]
4941 set rowtextx($row) $xt
4942 set idpos($id) [list $x $xt $y]
4943 if {[info exists idtags($id)] || [info exists idheads($id)]
4944 || [info exists idotherrefs($id)]} {
4945 set xt [drawtags $id $x $xt $y]
4947 set headline [lindex $commitinfo($id) 0]
4948 set name [lindex $commitinfo($id) 1]
4949 set date [lindex $commitinfo($id) 2]
4950 set date [formatdate $date]
4953 set isbold [ishighlighted $id]
4955 lappend boldrows $row
4956 set font mainfontbold
4958 lappend boldnamerows $row
4959 set nfont mainfontbold
4962 set linehtag($row) [$canv create text $xt $y -anchor w -fill $fgcolor \
4963 -text $headline -font $font -tags text]
4964 $canv bind $linehtag($row) <Button-3> "rowmenu %X %Y $id"
4965 set linentag($row) [$canv2 create text 3 $y -anchor w -fill $fgcolor \
4966 -text $name -font $nfont -tags text]
4967 set linedtag($row) [$canv3 create text 3 $y -anchor w -fill $fgcolor \
4968 -text $date -font mainfont -tags text]
4969 if {$selectedline == $row} {
4972 set xr [expr {$xt + [font measure $font $headline]}]
4973 if {$xr > $canvxmax} {
4979 proc drawcmitrow {row} {
4980 global displayorder rowidlist nrows_drawn
4981 global iddrawn markingmatches
4982 global commitinfo numcommits
4983 global filehighlight fhighlights findpattern nhighlights
4984 global hlview vhighlights
4985 global highlight_related rhighlights
4987 if {$row >= $numcommits} return
4989 set id [lindex $displayorder $row]
4990 if {[info exists hlview] && ![info exists vhighlights($id)]} {
4991 askvhighlight $row $id
4993 if {[info exists filehighlight] && ![info exists fhighlights($id)]} {
4994 askfilehighlight $row $id
4996 if {$findpattern ne {} && ![info exists nhighlights($id)]} {
4997 askfindhighlight $row $id
4999 if {$highlight_related ne [mc "None"] && ![info exists rhighlights($id)]} {
5000 askrelhighlight $row $id
5002 if {![info exists iddrawn($id)]} {
5003 set col [lsearch -exact [lindex $rowidlist $row] $id]
5005 puts "oops, row $row id $id not in list"
5008 if {![info exists commitinfo($id)]} {
5012 drawcmittext $id $row $col
5016 if {$markingmatches} {
5017 markrowmatches $row $id
5021 proc drawcommits {row {endrow {}}} {
5022 global numcommits iddrawn displayorder curview need_redisplay
5023 global parentlist rowidlist rowfinal uparrowlen downarrowlen nrows_drawn
5028 if {$endrow eq {}} {
5031 if {$endrow >= $numcommits} {
5032 set endrow [expr {$numcommits - 1}]
5035 set rl1 [expr {$row - $downarrowlen - 3}]
5039 set ro1 [expr {$row - 3}]
5043 set r2 [expr {$endrow + $uparrowlen + 3}]
5044 if {$r2 > $numcommits} {
5047 for {set r $rl1} {$r < $r2} {incr r} {
5048 if {[lindex $rowidlist $r] ne {} && [lindex $rowfinal $r]} {
5052 set rl1 [expr {$r + 1}]
5058 optimize_rows $ro1 0 $r2
5059 if {$need_redisplay || $nrows_drawn > 2000} {
5064 # make the lines join to already-drawn rows either side
5065 set r [expr {$row - 1}]
5066 if {$r < 0 || ![info exists iddrawn([lindex $displayorder $r])]} {
5069 set er [expr {$endrow + 1}]
5070 if {$er >= $numcommits ||
5071 ![info exists iddrawn([lindex $displayorder $er])]} {
5074 for {} {$r <= $er} {incr r} {
5075 set id [lindex $displayorder $r]
5076 set wasdrawn [info exists iddrawn($id)]
5078 if {$r == $er} break
5079 set nextid [lindex $displayorder [expr {$r + 1}]]
5080 if {$wasdrawn && [info exists iddrawn($nextid)]} continue
5081 drawparentlinks $id $r
5083 set rowids [lindex $rowidlist $r]
5084 foreach lid $rowids {
5085 if {$lid eq {}} continue
5086 if {[info exists lineend($lid)] && $lineend($lid) > $r} continue
5088 # see if this is the first child of any of its parents
5089 foreach p [lindex $parentlist $r] {
5090 if {[lsearch -exact $rowids $p] < 0} {
5091 # make this line extend up to the child
5092 set lineend($p) [drawlineseg $p $r $er 0]
5096 set lineend($lid) [drawlineseg $lid $r $er 1]
5102 proc undolayout {row} {
5103 global uparrowlen mingaplen downarrowlen
5104 global rowidlist rowisopt rowfinal need_redisplay
5106 set r [expr {$row - ($uparrowlen + $mingaplen + $downarrowlen)}]
5110 if {[llength $rowidlist] > $r} {
5112 set rowidlist [lrange $rowidlist 0 $r]
5113 set rowfinal [lrange $rowfinal 0 $r]
5114 set rowisopt [lrange $rowisopt 0 $r]
5115 set need_redisplay 1
5120 proc drawvisible {} {
5121 global canv linespc curview vrowmod selectedline targetrow targetid
5122 global need_redisplay cscroll numcommits
5124 set fs [$canv yview]
5125 set ymax [lindex [$canv cget -scrollregion] 3]
5126 if {$ymax eq {} || $ymax == 0 || $numcommits == 0} return
5127 set f0 [lindex $fs 0]
5128 set f1 [lindex $fs 1]
5129 set y0 [expr {int($f0 * $ymax)}]
5130 set y1 [expr {int($f1 * $ymax)}]
5132 if {[info exists targetid]} {
5133 if {[commitinview $targetid $curview]} {
5134 set r [rowofcommit $targetid]
5135 if {$r != $targetrow} {
5136 # Fix up the scrollregion and change the scrolling position
5137 # now that our target row has moved.
5138 set diff [expr {($r - $targetrow) * $linespc}]
5141 set ymax [lindex [$canv cget -scrollregion] 3]
5144 set f0 [expr {$y0 / $ymax}]
5145 set f1 [expr {$y1 / $ymax}]
5146 allcanvs yview moveto $f0
5147 $cscroll set $f0 $f1
5148 set need_redisplay 1
5155 set row [expr {int(($y0 - 3) / $linespc) - 1}]
5156 set endrow [expr {int(($y1 - 3) / $linespc) + 1}]
5157 if {$endrow >= $vrowmod($curview)} {
5158 update_arcrows $curview
5160 if {$selectedline ne {} &&
5161 $row <= $selectedline && $selectedline <= $endrow} {
5162 set targetrow $selectedline
5163 } elseif {[info exists targetid]} {
5164 set targetrow [expr {int(($row + $endrow) / 2)}]
5166 if {[info exists targetrow]} {
5167 if {$targetrow >= $numcommits} {
5168 set targetrow [expr {$numcommits - 1}]
5170 set targetid [commitonrow $targetrow]
5172 drawcommits $row $endrow
5175 proc clear_display {} {
5176 global iddrawn linesegs need_redisplay nrows_drawn
5177 global vhighlights fhighlights nhighlights rhighlights
5178 global linehtag linentag linedtag boldrows boldnamerows
5181 catch {unset iddrawn}
5182 catch {unset linesegs}
5183 catch {unset linehtag}
5184 catch {unset linentag}
5185 catch {unset linedtag}
5188 catch {unset vhighlights}
5189 catch {unset fhighlights}
5190 catch {unset nhighlights}
5191 catch {unset rhighlights}
5192 set need_redisplay 0
5196 proc findcrossings {id} {
5197 global rowidlist parentlist numcommits displayorder
5201 foreach {s e} [rowranges $id] {
5202 if {$e >= $numcommits} {
5203 set e [expr {$numcommits - 1}]
5205 if {$e <= $s} continue
5206 for {set row $e} {[incr row -1] >= $s} {} {
5207 set x [lsearch -exact [lindex $rowidlist $row] $id]
5209 set olds [lindex $parentlist $row]
5210 set kid [lindex $displayorder $row]
5211 set kidx [lsearch -exact [lindex $rowidlist $row] $kid]
5212 if {$kidx < 0} continue
5213 set nextrow [lindex $rowidlist [expr {$row + 1}]]
5215 set px [lsearch -exact $nextrow $p]
5216 if {$px < 0} continue
5217 if {($kidx < $x && $x < $px) || ($px < $x && $x < $kidx)} {
5218 if {[lsearch -exact $ccross $p] >= 0} continue
5219 if {$x == $px + ($kidx < $px? -1: 1)} {
5221 } elseif {[lsearch -exact $cross $p] < 0} {
5228 return [concat $ccross {{}} $cross]
5231 proc assigncolor {id} {
5232 global colormap colors nextcolor
5233 global parents children children curview
5235 if {[info exists colormap($id)]} return
5236 set ncolors [llength $colors]
5237 if {[info exists children($curview,$id)]} {
5238 set kids $children($curview,$id)
5242 if {[llength $kids] == 1} {
5243 set child [lindex $kids 0]
5244 if {[info exists colormap($child)]
5245 && [llength $parents($curview,$child)] == 1} {
5246 set colormap($id) $colormap($child)
5252 foreach x [findcrossings $id] {
5254 # delimiter between corner crossings and other crossings
5255 if {[llength $badcolors] >= $ncolors - 1} break
5256 set origbad $badcolors
5258 if {[info exists colormap($x)]
5259 && [lsearch -exact $badcolors $colormap($x)] < 0} {
5260 lappend badcolors $colormap($x)
5263 if {[llength $badcolors] >= $ncolors} {
5264 set badcolors $origbad
5266 set origbad $badcolors
5267 if {[llength $badcolors] < $ncolors - 1} {
5268 foreach child $kids {
5269 if {[info exists colormap($child)]
5270 && [lsearch -exact $badcolors $colormap($child)] < 0} {
5271 lappend badcolors $colormap($child)
5273 foreach p $parents($curview,$child) {
5274 if {[info exists colormap($p)]
5275 && [lsearch -exact $badcolors $colormap($p)] < 0} {
5276 lappend badcolors $colormap($p)
5280 if {[llength $badcolors] >= $ncolors} {
5281 set badcolors $origbad
5284 for {set i 0} {$i <= $ncolors} {incr i} {
5285 set c [lindex $colors $nextcolor]
5286 if {[incr nextcolor] >= $ncolors} {
5289 if {[lsearch -exact $badcolors $c]} break
5291 set colormap($id) $c
5294 proc bindline {t id} {
5297 $canv bind $t <Enter> "lineenter %x %y $id"
5298 $canv bind $t <Motion> "linemotion %x %y $id"
5299 $canv bind $t <Leave> "lineleave $id"
5300 $canv bind $t <Button-1> "lineclick %x %y $id 1"
5303 proc drawtags {id x xt y1} {
5304 global idtags idheads idotherrefs mainhead
5305 global linespc lthickness
5306 global canv rowtextx curview fgcolor bgcolor
5311 if {[info exists idtags($id)]} {
5312 set marks $idtags($id)
5313 set ntags [llength $marks]
5315 if {[info exists idheads($id)]} {
5316 set marks [concat $marks $idheads($id)]
5317 set nheads [llength $idheads($id)]
5319 if {[info exists idotherrefs($id)]} {
5320 set marks [concat $marks $idotherrefs($id)]
5326 set delta [expr {int(0.5 * ($linespc - $lthickness))}]
5327 set yt [expr {$y1 - 0.5 * $linespc}]
5328 set yb [expr {$yt + $linespc - 1}]
5332 foreach tag $marks {
5334 if {$i >= $ntags && $i < $ntags + $nheads && $tag eq $mainhead} {
5335 set wid [font measure mainfontbold $tag]
5337 set wid [font measure mainfont $tag]
5341 set xt [expr {$xt + $delta + $wid + $lthickness + $linespc}]
5343 set t [$canv create line $x $y1 [lindex $xvals end] $y1 \
5344 -width $lthickness -fill black -tags tag.$id]
5346 foreach tag $marks x $xvals wid $wvals {
5347 set xl [expr {$x + $delta}]
5348 set xr [expr {$x + $delta + $wid + $lthickness}]
5350 if {[incr ntags -1] >= 0} {
5352 set t [$canv create polygon $x [expr {$yt + $delta}] $xl $yt \
5353 $xr $yt $xr $yb $xl $yb $x [expr {$yb - $delta}] \
5354 -width 1 -outline black -fill yellow -tags tag.$id]
5355 $canv bind $t <1> [list showtag $tag 1]
5356 set rowtextx([rowofcommit $id]) [expr {$xr + $linespc}]
5358 # draw a head or other ref
5359 if {[incr nheads -1] >= 0} {
5361 if {$tag eq $mainhead} {
5362 set font mainfontbold
5367 set xl [expr {$xl - $delta/2}]
5368 $canv create polygon $x $yt $xr $yt $xr $yb $x $yb \
5369 -width 1 -outline black -fill $col -tags tag.$id
5370 if {[regexp {^(remotes/.*/|remotes/)} $tag match remoteprefix]} {
5371 set rwid [font measure mainfont $remoteprefix]
5372 set xi [expr {$x + 1}]
5373 set yti [expr {$yt + 1}]
5374 set xri [expr {$x + $rwid}]
5375 $canv create polygon $xi $yti $xri $yti $xri $yb $xi $yb \
5376 -width 0 -fill "#ffddaa" -tags tag.$id
5379 set t [$canv create text $xl $y1 -anchor w -text $tag -fill $fgcolor \
5380 -font $font -tags [list tag.$id text]]
5382 $canv bind $t <1> [list showtag $tag 1]
5383 } elseif {$nheads >= 0} {
5384 $canv bind $t <Button-3> [list headmenu %X %Y $id $tag]
5390 proc xcoord {i level ln} {
5391 global canvx0 xspc1 xspc2
5393 set x [expr {$canvx0 + $i * $xspc1($ln)}]
5394 if {$i > 0 && $i == $level} {
5395 set x [expr {$x + 0.5 * ($xspc2 - $xspc1($ln))}]
5396 } elseif {$i > $level} {
5397 set x [expr {$x + $xspc2 - $xspc1($ln)}]
5402 proc show_status {msg} {
5406 $canv create text 3 3 -anchor nw -text $msg -font mainfont \
5407 -tags text -fill $fgcolor
5410 # Don't change the text pane cursor if it is currently the hand cursor,
5411 # showing that we are over a sha1 ID link.
5412 proc settextcursor {c} {
5413 global ctext curtextcursor
5415 if {[$ctext cget -cursor] == $curtextcursor} {
5416 $ctext config -cursor $c
5418 set curtextcursor $c
5421 proc nowbusy {what {name {}}} {
5422 global isbusy busyname statusw
5424 if {[array names isbusy] eq {}} {
5425 . config -cursor watch
5429 set busyname($what) $name
5431 $statusw conf -text $name
5435 proc notbusy {what} {
5436 global isbusy maincursor textcursor busyname statusw
5440 if {$busyname($what) ne {} &&
5441 [$statusw cget -text] eq $busyname($what)} {
5442 $statusw conf -text {}
5445 if {[array names isbusy] eq {}} {
5446 . config -cursor $maincursor
5447 settextcursor $textcursor
5451 proc findmatches {f} {
5452 global findtype findstring
5453 if {$findtype == [mc "Regexp"]} {
5454 set matches [regexp -indices -all -inline $findstring $f]
5457 if {$findtype == [mc "IgnCase"]} {
5458 set f [string tolower $f]
5459 set fs [string tolower $fs]
5463 set l [string length $fs]
5464 while {[set j [string first $fs $f $i]] >= 0} {
5465 lappend matches [list $j [expr {$j+$l-1}]]
5466 set i [expr {$j + $l}]
5472 proc dofind {{dirn 1} {wrap 1}} {
5473 global findstring findstartline findcurline selectedline numcommits
5474 global gdttype filehighlight fh_serial find_dirn findallowwrap
5476 if {[info exists find_dirn]} {
5477 if {$find_dirn == $dirn} return
5481 if {$findstring eq {} || $numcommits == 0} return
5482 if {$selectedline eq {}} {
5483 set findstartline [lindex [visiblerows] [expr {$dirn < 0}]]
5485 set findstartline $selectedline
5487 set findcurline $findstartline
5488 nowbusy finding [mc "Searching"]
5489 if {$gdttype ne [mc "containing:"] && ![info exists filehighlight]} {
5490 after cancel do_file_hl $fh_serial
5491 do_file_hl $fh_serial
5494 set findallowwrap $wrap
5498 proc stopfinding {} {
5499 global find_dirn findcurline fprogcoord
5501 if {[info exists find_dirn]} {
5511 global commitdata commitinfo numcommits findpattern findloc
5512 global findstartline findcurline findallowwrap
5513 global find_dirn gdttype fhighlights fprogcoord
5514 global curview varcorder vrownum varccommits vrowmod
5516 if {![info exists find_dirn]} {
5519 set fldtypes [list [mc "Headline"] [mc "Author"] [mc "Date"] [mc "Committer"] [mc "CDate"] [mc "Comments"]]
5522 if {$find_dirn > 0} {
5524 if {$l >= $numcommits} {
5527 if {$l <= $findstartline} {
5528 set lim [expr {$findstartline + 1}]
5531 set moretodo $findallowwrap
5538 if {$l >= $findstartline} {
5539 set lim [expr {$findstartline - 1}]
5542 set moretodo $findallowwrap
5545 set n [expr {($lim - $l) * $find_dirn}]
5550 if {$l + ($find_dirn > 0? $n: 1) > $vrowmod($curview)} {
5551 update_arcrows $curview
5555 set ai [bsearch $vrownum($curview) $l]
5556 set a [lindex $varcorder($curview) $ai]
5557 set arow [lindex $vrownum($curview) $ai]
5558 set ids [lindex $varccommits($curview,$a)]
5559 set arowend [expr {$arow + [llength $ids]}]
5560 if {$gdttype eq [mc "containing:"]} {
5561 for {} {$n > 0} {incr n -1; incr l $find_dirn} {
5562 if {$l < $arow || $l >= $arowend} {
5564 set a [lindex $varcorder($curview) $ai]
5565 set arow [lindex $vrownum($curview) $ai]
5566 set ids [lindex $varccommits($curview,$a)]
5567 set arowend [expr {$arow + [llength $ids]}]
5569 set id [lindex $ids [expr {$l - $arow}]]
5570 # shouldn't happen unless git log doesn't give all the commits...
5571 if {![info exists commitdata($id)] ||
5572 ![doesmatch $commitdata($id)]} {
5575 if {![info exists commitinfo($id)]} {
5578 set info $commitinfo($id)
5579 foreach f $info ty $fldtypes {
5580 if {($findloc eq [mc "All fields"] || $findloc eq $ty) &&
5589 for {} {$n > 0} {incr n -1; incr l $find_dirn} {
5590 if {$l < $arow || $l >= $arowend} {
5592 set a [lindex $varcorder($curview) $ai]
5593 set arow [lindex $vrownum($curview) $ai]
5594 set ids [lindex $varccommits($curview,$a)]
5595 set arowend [expr {$arow + [llength $ids]}]
5597 set id [lindex $ids [expr {$l - $arow}]]
5598 if {![info exists fhighlights($id)]} {
5599 # this sets fhighlights($id) to -1
5600 askfilehighlight $l $id
5602 if {$fhighlights($id) > 0} {
5606 if {$fhighlights($id) < 0} {
5609 set findcurline [expr {$l - $find_dirn}]
5614 if {$found || ($domore && !$moretodo)} {
5630 set findcurline [expr {$l - $find_dirn}]
5632 set n [expr {($findcurline - $findstartline) * $find_dirn - 1}]
5636 set fprogcoord [expr {$n * 1.0 / $numcommits}]
5641 proc findselectline {l} {
5642 global findloc commentend ctext findcurline markingmatches gdttype
5644 set markingmatches 1
5647 if {$findloc == [mc "All fields"] || $findloc == [mc "Comments"]} {
5648 # highlight the matches in the comments
5649 set f [$ctext get 1.0 $commentend]
5650 set matches [findmatches $f]
5651 foreach match $matches {
5652 set start [lindex $match 0]
5653 set end [expr {[lindex $match 1] + 1}]
5654 $ctext tag add found "1.0 + $start c" "1.0 + $end c"
5660 # mark the bits of a headline or author that match a find string
5661 proc markmatches {canv l str tag matches font row} {
5664 set bbox [$canv bbox $tag]
5665 set x0 [lindex $bbox 0]
5666 set y0 [lindex $bbox 1]
5667 set y1 [lindex $bbox 3]
5668 foreach match $matches {
5669 set start [lindex $match 0]
5670 set end [lindex $match 1]
5671 if {$start > $end} continue
5672 set xoff [font measure $font [string range $str 0 [expr {$start-1}]]]
5673 set xlen [font measure $font [string range $str 0 [expr {$end}]]]
5674 set t [$canv create rect [expr {$x0+$xoff}] $y0 \
5675 [expr {$x0+$xlen+2}] $y1 \
5676 -outline {} -tags [list match$l matches] -fill yellow]
5678 if {$row == $selectedline} {
5679 $canv raise $t secsel
5684 proc unmarkmatches {} {
5685 global markingmatches
5687 allcanvs delete matches
5688 set markingmatches 0
5692 proc selcanvline {w x y} {
5693 global canv canvy0 ctext linespc
5695 set ymax [lindex [$canv cget -scrollregion] 3]
5696 if {$ymax == {}} return
5697 set yfrac [lindex [$canv yview] 0]
5698 set y [expr {$y + $yfrac * $ymax}]
5699 set l [expr {int(($y - $canvy0) / $linespc + 0.5)}]
5704 set xmax [lindex [$canv cget -scrollregion] 2]
5705 set xleft [expr {[lindex [$canv xview] 0] * $xmax}]
5706 if {![info exists rowtextx($l)] || $xleft + $x < $rowtextx($l)} return
5712 proc commit_descriptor {p} {
5714 if {![info exists commitinfo($p)]} {
5718 if {[llength $commitinfo($p)] > 1} {
5719 set l [lindex $commitinfo($p) 0]
5724 # append some text to the ctext widget, and make any SHA1 ID
5725 # that we know about be a clickable link.
5726 proc appendwithlinks {text tags} {
5727 global ctext linknum curview pendinglinks
5729 set start [$ctext index "end - 1c"]
5730 $ctext insert end $text $tags
5731 set links [regexp -indices -all -inline {[0-9a-f]{40}} $text]
5735 set linkid [string range $text $s $e]
5737 $ctext tag delete link$linknum
5738 $ctext tag add link$linknum "$start + $s c" "$start + $e c"
5739 setlink $linkid link$linknum
5744 proc setlink {id lk} {
5745 global curview ctext pendinglinks commitinterest
5747 if {[commitinview $id $curview]} {
5748 $ctext tag conf $lk -foreground blue -underline 1
5749 $ctext tag bind $lk <1> [list selectline [rowofcommit $id] 1]
5750 $ctext tag bind $lk <Enter> {linkcursor %W 1}
5751 $ctext tag bind $lk <Leave> {linkcursor %W -1}
5753 lappend pendinglinks($id) $lk
5754 lappend commitinterest($id) {makelink %I}
5758 proc makelink {id} {
5761 if {![info exists pendinglinks($id)]} return
5762 foreach lk $pendinglinks($id) {
5765 unset pendinglinks($id)
5768 proc linkcursor {w inc} {
5769 global linkentercount curtextcursor
5771 if {[incr linkentercount $inc] > 0} {
5772 $w configure -cursor hand2
5774 $w configure -cursor $curtextcursor
5775 if {$linkentercount < 0} {
5776 set linkentercount 0
5781 proc viewnextline {dir} {
5785 set ymax [lindex [$canv cget -scrollregion] 3]
5786 set wnow [$canv yview]
5787 set wtop [expr {[lindex $wnow 0] * $ymax}]
5788 set newtop [expr {$wtop + $dir * $linespc}]
5791 } elseif {$newtop > $ymax} {
5794 allcanvs yview moveto [expr {$newtop * 1.0 / $ymax}]
5797 # add a list of tag or branch names at position pos
5798 # returns the number of names inserted
5799 proc appendrefs {pos ids var} {
5800 global ctext linknum curview $var maxrefs
5802 if {[catch {$ctext index $pos}]} {
5805 $ctext conf -state normal
5806 $ctext delete $pos "$pos lineend"
5809 foreach tag [set $var\($id\)] {
5810 lappend tags [list $tag $id]
5813 if {[llength $tags] > $maxrefs} {
5814 $ctext insert $pos "many ([llength $tags])"
5816 set tags [lsort -index 0 -decreasing $tags]
5819 set id [lindex $ti 1]
5822 $ctext tag delete $lk
5823 $ctext insert $pos $sep
5824 $ctext insert $pos [lindex $ti 0] $lk
5829 $ctext conf -state disabled
5830 return [llength $tags]
5833 # called when we have finished computing the nearby tags
5834 proc dispneartags {delay} {
5835 global selectedline currentid showneartags tagphase
5837 if {$selectedline eq {} || !$showneartags} return
5838 after cancel dispnexttag
5840 after 200 dispnexttag
5843 after idle dispnexttag
5848 proc dispnexttag {} {
5849 global selectedline currentid showneartags tagphase ctext
5851 if {$selectedline eq {} || !$showneartags} return
5852 switch -- $tagphase {
5854 set dtags [desctags $currentid]
5856 appendrefs precedes $dtags idtags
5860 set atags [anctags $currentid]
5862 appendrefs follows $atags idtags
5866 set dheads [descheads $currentid]
5867 if {$dheads ne {}} {
5868 if {[appendrefs branch $dheads idheads] > 1
5869 && [$ctext get "branch -3c"] eq "h"} {
5870 # turn "Branch" into "Branches"
5871 $ctext conf -state normal
5872 $ctext insert "branch -2c" "es"
5873 $ctext conf -state disabled
5878 if {[incr tagphase] <= 2} {
5879 after idle dispnexttag
5883 proc make_secsel {l} {
5884 global linehtag linentag linedtag canv canv2 canv3
5886 if {![info exists linehtag($l)]} return
5888 set t [eval $canv create rect [$canv bbox $linehtag($l)] -outline {{}} \
5889 -tags secsel -fill [$canv cget -selectbackground]]
5891 $canv2 delete secsel
5892 set t [eval $canv2 create rect [$canv2 bbox $linentag($l)] -outline {{}} \
5893 -tags secsel -fill [$canv2 cget -selectbackground]]
5895 $canv3 delete secsel
5896 set t [eval $canv3 create rect [$canv3 bbox $linedtag($l)] -outline {{}} \
5897 -tags secsel -fill [$canv3 cget -selectbackground]]
5901 proc selectline {l isnew} {
5902 global canv ctext commitinfo selectedline
5903 global canvy0 linespc parents children curview
5904 global currentid sha1entry
5905 global commentend idtags linknum
5906 global mergemax numcommits pending_select
5907 global cmitmode showneartags allcommits
5908 global targetrow targetid lastscrollrows
5911 catch {unset pending_select}
5916 if {$l < 0 || $l >= $numcommits} return
5917 set id [commitonrow $l]
5922 if {$lastscrollrows < $numcommits} {
5926 set y [expr {$canvy0 + $l * $linespc}]
5927 set ymax [lindex [$canv cget -scrollregion] 3]
5928 set ytop [expr {$y - $linespc - 1}]
5929 set ybot [expr {$y + $linespc + 1}]
5930 set wnow [$canv yview]
5931 set wtop [expr {[lindex $wnow 0] * $ymax}]
5932 set wbot [expr {[lindex $wnow 1] * $ymax}]
5933 set wh [expr {$wbot - $wtop}]
5935 if {$ytop < $wtop} {
5936 if {$ybot < $wtop} {
5937 set newtop [expr {$y - $wh / 2.0}]
5940 if {$newtop > $wtop - $linespc} {
5941 set newtop [expr {$wtop - $linespc}]
5944 } elseif {$ybot > $wbot} {
5945 if {$ytop > $wbot} {
5946 set newtop [expr {$y - $wh / 2.0}]
5948 set newtop [expr {$ybot - $wh}]
5949 if {$newtop < $wtop + $linespc} {
5950 set newtop [expr {$wtop + $linespc}]
5954 if {$newtop != $wtop} {
5958 allcanvs yview moveto [expr {$newtop * 1.0 / $ymax}]
5965 addtohistory [list selbyid $id]
5968 $sha1entry delete 0 end
5969 $sha1entry insert 0 $id
5971 $sha1entry selection from 0
5972 $sha1entry selection to end
5976 $ctext conf -state normal
5979 if {![info exists commitinfo($id)]} {
5982 set info $commitinfo($id)
5983 set date [formatdate [lindex $info 2]]
5984 $ctext insert end "[mc "Author"]: [lindex $info 1] $date\n"
5985 set date [formatdate [lindex $info 4]]
5986 $ctext insert end "[mc "Committer"]: [lindex $info 3] $date\n"
5987 if {[info exists idtags($id)]} {
5988 $ctext insert end [mc "Tags:"]
5989 foreach tag $idtags($id) {
5990 $ctext insert end " $tag"
5992 $ctext insert end "\n"
5996 set olds $parents($curview,$id)
5997 if {[llength $olds] > 1} {
6000 if {$np >= $mergemax} {
6005 $ctext insert end "[mc "Parent"]: " $tag
6006 appendwithlinks [commit_descriptor $p] {}
6011 append headers "[mc "Parent"]: [commit_descriptor $p]"
6015 foreach c $children($curview,$id) {
6016 append headers "[mc "Child"]: [commit_descriptor $c]"
6019 # make anything that looks like a SHA1 ID be a clickable link
6020 appendwithlinks $headers {}
6021 if {$showneartags} {
6022 if {![info exists allcommits]} {
6025 $ctext insert end "[mc "Branch"]: "
6026 $ctext mark set branch "end -1c"
6027 $ctext mark gravity branch left
6028 $ctext insert end "\n[mc "Follows"]: "
6029 $ctext mark set follows "end -1c"
6030 $ctext mark gravity follows left
6031 $ctext insert end "\n[mc "Precedes"]: "
6032 $ctext mark set precedes "end -1c"
6033 $ctext mark gravity precedes left
6034 $ctext insert end "\n"
6037 $ctext insert end "\n"
6038 set comment [lindex $info 5]
6039 if {[string first "\r" $comment] >= 0} {
6040 set comment [string map {"\r" "\n "} $comment]
6042 appendwithlinks $comment {comment}
6044 $ctext tag remove found 1.0 end
6045 $ctext conf -state disabled
6046 set commentend [$ctext index "end - 1c"]
6048 init_flist [mc "Comments"]
6049 if {$cmitmode eq "tree"} {
6051 } elseif {[llength $olds] <= 1} {
6058 proc selfirstline {} {
6063 proc sellastline {} {
6066 set l [expr {$numcommits - 1}]
6070 proc selnextline {dir} {
6073 if {$selectedline eq {}} return
6074 set l [expr {$selectedline + $dir}]
6079 proc selnextpage {dir} {
6080 global canv linespc selectedline numcommits
6082 set lpp [expr {([winfo height $canv] - 2) / $linespc}]
6086 allcanvs yview scroll [expr {$dir * $lpp}] units
6088 if {$selectedline eq {}} return
6089 set l [expr {$selectedline + $dir * $lpp}]
6092 } elseif {$l >= $numcommits} {
6093 set l [expr $numcommits - 1]
6099 proc unselectline {} {
6100 global selectedline currentid
6103 catch {unset currentid}
6104 allcanvs delete secsel
6108 proc reselectline {} {
6111 if {$selectedline ne {}} {
6112 selectline $selectedline 0
6116 proc addtohistory {cmd} {
6117 global history historyindex curview
6119 set elt [list $curview $cmd]
6120 if {$historyindex > 0
6121 && [lindex $history [expr {$historyindex - 1}]] == $elt} {
6125 if {$historyindex < [llength $history]} {
6126 set history [lreplace $history $historyindex end $elt]
6128 lappend history $elt
6131 if {$historyindex > 1} {
6132 .tf.bar.leftbut conf -state normal
6134 .tf.bar.leftbut conf -state disabled
6136 .tf.bar.rightbut conf -state disabled
6142 set view [lindex $elt 0]
6143 set cmd [lindex $elt 1]
6144 if {$curview != $view} {
6151 global history historyindex
6154 if {$historyindex > 1} {
6155 incr historyindex -1
6156 godo [lindex $history [expr {$historyindex - 1}]]
6157 .tf.bar.rightbut conf -state normal
6159 if {$historyindex <= 1} {
6160 .tf.bar.leftbut conf -state disabled
6165 global history historyindex
6168 if {$historyindex < [llength $history]} {
6169 set cmd [lindex $history $historyindex]
6172 .tf.bar.leftbut conf -state normal
6174 if {$historyindex >= [llength $history]} {
6175 .tf.bar.rightbut conf -state disabled
6180 global treefilelist treeidlist diffids diffmergeid treepending
6181 global nullid nullid2
6184 catch {unset diffmergeid}
6185 if {![info exists treefilelist($id)]} {
6186 if {![info exists treepending]} {
6187 if {$id eq $nullid} {
6188 set cmd [list | git ls-files]
6189 } elseif {$id eq $nullid2} {
6190 set cmd [list | git ls-files --stage -t]
6192 set cmd [list | git ls-tree -r $id]
6194 if {[catch {set gtf [open $cmd r]}]} {
6198 set treefilelist($id) {}
6199 set treeidlist($id) {}
6200 fconfigure $gtf -blocking 0
6201 filerun $gtf [list gettreeline $gtf $id]
6208 proc gettreeline {gtf id} {
6209 global treefilelist treeidlist treepending cmitmode diffids nullid nullid2
6212 while {[incr nl] <= 1000 && [gets $gtf line] >= 0} {
6213 if {$diffids eq $nullid} {
6216 set i [string first "\t" $line]
6217 if {$i < 0} continue
6218 set fname [string range $line [expr {$i+1}] end]
6219 set line [string range $line 0 [expr {$i-1}]]
6220 if {$diffids ne $nullid2 && [lindex $line 1] ne "blob"} continue
6221 set sha1 [lindex $line 2]
6222 if {[string index $fname 0] eq "\""} {
6223 set fname [lindex $fname 0]
6225 lappend treeidlist($id) $sha1
6227 lappend treefilelist($id) $fname
6230 return [expr {$nl >= 1000? 2: 1}]
6234 if {$cmitmode ne "tree"} {
6235 if {![info exists diffmergeid]} {
6236 gettreediffs $diffids
6238 } elseif {$id ne $diffids} {
6247 global treefilelist treeidlist diffids nullid nullid2
6248 global ctext commentend
6250 set i [lsearch -exact $treefilelist($diffids) $f]
6252 puts "oops, $f not in list for id $diffids"
6255 if {$diffids eq $nullid} {
6256 if {[catch {set bf [open $f r]} err]} {
6257 puts "oops, can't read $f: $err"
6261 set blob [lindex $treeidlist($diffids) $i]
6262 if {[catch {set bf [open [concat | git cat-file blob $blob] r]} err]} {
6263 puts "oops, error reading blob $blob: $err"
6267 fconfigure $bf -blocking 0
6268 filerun $bf [list getblobline $bf $diffids]
6269 $ctext config -state normal
6270 clear_ctext $commentend
6271 $ctext insert end "\n"
6272 $ctext insert end "$f\n" filesep
6273 $ctext config -state disabled
6274 $ctext yview $commentend
6278 proc getblobline {bf id} {
6279 global diffids cmitmode ctext
6281 if {$id ne $diffids || $cmitmode ne "tree"} {
6285 $ctext config -state normal
6287 while {[incr nl] <= 1000 && [gets $bf line] >= 0} {
6288 $ctext insert end "$line\n"
6291 # delete last newline
6292 $ctext delete "end - 2c" "end - 1c"
6296 $ctext config -state disabled
6297 return [expr {$nl >= 1000? 2: 1}]
6300 proc mergediff {id} {
6301 global diffmergeid mdifffd
6305 global limitdiffs vfilelimit curview
6309 # this doesn't seem to actually affect anything...
6310 set cmd [concat | git diff-tree --no-commit-id --cc -U$diffcontext $id]
6311 if {$limitdiffs && $vfilelimit($curview) ne {}} {
6312 set cmd [concat $cmd -- $vfilelimit($curview)]
6314 if {[catch {set mdf [open $cmd r]} err]} {
6315 error_popup "[mc "Error getting merge diffs:"] $err"
6318 fconfigure $mdf -blocking 0
6319 set mdifffd($id) $mdf
6320 set np [llength $parents($curview,$id)]
6322 filerun $mdf [list getmergediffline $mdf $id $np]
6325 proc getmergediffline {mdf id np} {
6326 global diffmergeid ctext cflist mergemax
6327 global difffilestart mdifffd
6329 $ctext conf -state normal
6331 while {[incr nr] <= 1000 && [gets $mdf line] >= 0} {
6332 if {![info exists diffmergeid] || $id != $diffmergeid
6333 || $mdf != $mdifffd($id)} {
6337 if {[regexp {^diff --cc (.*)} $line match fname]} {
6338 # start of a new file
6339 $ctext insert end "\n"
6340 set here [$ctext index "end - 1c"]
6341 lappend difffilestart $here
6342 add_flist [list $fname]
6343 set l [expr {(78 - [string length $fname]) / 2}]
6344 set pad [string range "----------------------------------------" 1 $l]
6345 $ctext insert end "$pad $fname $pad\n" filesep
6346 } elseif {[regexp {^@@} $line]} {
6347 $ctext insert end "$line\n" hunksep
6348 } elseif {[regexp {^[0-9a-f]{40}$} $line] || [regexp {^index} $line]} {
6351 # parse the prefix - one ' ', '-' or '+' for each parent
6356 for {set j 0} {$j < $np} {incr j} {
6357 set c [string range $line $j $j]
6360 } elseif {$c == "-"} {
6362 } elseif {$c == "+"} {
6371 if {!$isbad && $minuses ne {} && $pluses eq {}} {
6372 # line doesn't appear in result, parents in $minuses have the line
6373 set num [lindex $minuses 0]
6374 } elseif {!$isbad && $pluses ne {} && $minuses eq {}} {
6375 # line appears in result, parents in $pluses don't have the line
6376 lappend tags mresult
6377 set num [lindex $spaces 0]
6380 if {$num >= $mergemax} {
6385 $ctext insert end "$line\n" $tags
6388 $ctext conf -state disabled
6393 return [expr {$nr >= 1000? 2: 1}]
6396 proc startdiff {ids} {
6397 global treediffs diffids treepending diffmergeid nullid nullid2
6401 catch {unset diffmergeid}
6402 if {![info exists treediffs($ids)] ||
6403 [lsearch -exact $ids $nullid] >= 0 ||
6404 [lsearch -exact $ids $nullid2] >= 0} {
6405 if {![info exists treepending]} {
6413 proc path_filter {filter name} {
6415 set l [string length $p]
6416 if {[string index $p end] eq "/"} {
6417 if {[string compare -length $l $p $name] == 0} {
6421 if {[string compare -length $l $p $name] == 0 &&
6422 ([string length $name] == $l ||
6423 [string index $name $l] eq "/")} {
6431 proc addtocflist {ids} {
6434 add_flist $treediffs($ids)
6438 proc diffcmd {ids flags} {
6439 global nullid nullid2
6441 set i [lsearch -exact $ids $nullid]
6442 set j [lsearch -exact $ids $nullid2]
6444 if {[llength $ids] > 1 && $j < 0} {
6445 # comparing working directory with some specific revision
6446 set cmd [concat | git diff-index $flags]
6448 lappend cmd -R [lindex $ids 1]
6450 lappend cmd [lindex $ids 0]
6453 # comparing working directory with index
6454 set cmd [concat | git diff-files $flags]
6459 } elseif {$j >= 0} {
6460 set cmd [concat | git diff-index --cached $flags]
6461 if {[llength $ids] > 1} {
6462 # comparing index with specific revision
6464 lappend cmd -R [lindex $ids 1]
6466 lappend cmd [lindex $ids 0]
6469 # comparing index with HEAD
6473 set cmd [concat | git diff-tree -r $flags $ids]
6478 proc gettreediffs {ids} {
6479 global treediff treepending
6481 if {[catch {set gdtf [open [diffcmd $ids {--no-commit-id}] r]}]} return
6483 set treepending $ids
6485 fconfigure $gdtf -blocking 0
6486 filerun $gdtf [list gettreediffline $gdtf $ids]
6489 proc gettreediffline {gdtf ids} {
6490 global treediff treediffs treepending diffids diffmergeid
6491 global cmitmode vfilelimit curview limitdiffs
6494 while {[incr nr] <= 1000 && [gets $gdtf line] >= 0} {
6495 set i [string first "\t" $line]
6497 set file [string range $line [expr {$i+1}] end]
6498 if {[string index $file 0] eq "\""} {
6499 set file [lindex $file 0]
6501 lappend treediff $file
6505 return [expr {$nr >= 1000? 2: 1}]
6508 if {$limitdiffs && $vfilelimit($curview) ne {}} {
6510 foreach f $treediff {
6511 if {[path_filter $vfilelimit($curview) $f]} {
6515 set treediffs($ids) $flist
6517 set treediffs($ids) $treediff
6520 if {$cmitmode eq "tree"} {
6522 } elseif {$ids != $diffids} {
6523 if {![info exists diffmergeid]} {
6524 gettreediffs $diffids
6532 # empty string or positive integer
6533 proc diffcontextvalidate {v} {
6534 return [regexp {^(|[1-9][0-9]*)$} $v]
6537 proc diffcontextchange {n1 n2 op} {
6538 global diffcontextstring diffcontext
6540 if {[string is integer -strict $diffcontextstring]} {
6541 if {$diffcontextstring > 0} {
6542 set diffcontext $diffcontextstring
6548 proc changeignorespace {} {
6552 proc getblobdiffs {ids} {
6553 global blobdifffd diffids env
6554 global diffinhdr treediffs
6557 global limitdiffs vfilelimit curview
6559 set cmd [diffcmd $ids "-p -C --no-commit-id -U$diffcontext"]
6563 if {$limitdiffs && $vfilelimit($curview) ne {}} {
6564 set cmd [concat $cmd -- $vfilelimit($curview)]
6566 if {[catch {set bdf [open $cmd r]} err]} {
6567 puts "error getting diffs: $err"
6571 fconfigure $bdf -blocking 0
6572 set blobdifffd($ids) $bdf
6573 filerun $bdf [list getblobdiffline $bdf $diffids]
6576 proc setinlist {var i val} {
6579 while {[llength [set $var]] < $i} {
6582 if {[llength [set $var]] == $i} {
6589 proc makediffhdr {fname ids} {
6590 global ctext curdiffstart treediffs
6592 set i [lsearch -exact $treediffs($ids) $fname]
6594 setinlist difffilestart $i $curdiffstart
6596 set l [expr {(78 - [string length $fname]) / 2}]
6597 set pad [string range "----------------------------------------" 1 $l]
6598 $ctext insert $curdiffstart "$pad $fname $pad" filesep
6601 proc getblobdiffline {bdf ids} {
6602 global diffids blobdifffd ctext curdiffstart
6603 global diffnexthead diffnextnote difffilestart
6604 global diffinhdr treediffs
6607 $ctext conf -state normal
6608 while {[incr nr] <= 1000 && [gets $bdf line] >= 0} {
6609 if {$ids != $diffids || $bdf != $blobdifffd($ids)} {
6613 if {![string compare -length 11 "diff --git " $line]} {
6614 # trim off "diff --git "
6615 set line [string range $line 11 end]
6617 # start of a new file
6618 $ctext insert end "\n"
6619 set curdiffstart [$ctext index "end - 1c"]
6620 $ctext insert end "\n" filesep
6621 # If the name hasn't changed the length will be odd,
6622 # the middle char will be a space, and the two bits either
6623 # side will be a/name and b/name, or "a/name" and "b/name".
6624 # If the name has changed we'll get "rename from" and
6625 # "rename to" or "copy from" and "copy to" lines following this,
6626 # and we'll use them to get the filenames.
6627 # This complexity is necessary because spaces in the filename(s)
6628 # don't get escaped.
6629 set l [string length $line]
6630 set i [expr {$l / 2}]
6631 if {!(($l & 1) && [string index $line $i] eq " " &&
6632 [string range $line 2 [expr {$i - 1}]] eq \
6633 [string range $line [expr {$i + 3}] end])} {
6636 # unescape if quoted and chop off the a/ from the front
6637 if {[string index $line 0] eq "\""} {
6638 set fname [string range [lindex $line 0] 2 end]
6640 set fname [string range $line 2 [expr {$i - 1}]]
6642 makediffhdr $fname $ids
6644 } elseif {[regexp {^@@ -([0-9]+)(,[0-9]+)? \+([0-9]+)(,[0-9]+)? @@(.*)} \
6645 $line match f1l f1c f2l f2c rest]} {
6646 $ctext insert end "$line\n" hunksep
6649 } elseif {$diffinhdr} {
6650 if {![string compare -length 12 "rename from " $line]} {
6651 set fname [string range $line [expr 6 + [string first " from " $line] ] end]
6652 if {[string index $fname 0] eq "\""} {
6653 set fname [lindex $fname 0]
6655 set i [lsearch -exact $treediffs($ids) $fname]
6657 setinlist difffilestart $i $curdiffstart
6659 } elseif {![string compare -length 10 $line "rename to "] ||
6660 ![string compare -length 8 $line "copy to "]} {
6661 set fname [string range $line [expr 4 + [string first " to " $line] ] end]
6662 if {[string index $fname 0] eq "\""} {
6663 set fname [lindex $fname 0]
6665 makediffhdr $fname $ids
6666 } elseif {[string compare -length 3 $line "---"] == 0} {
6669 } elseif {[string compare -length 3 $line "+++"] == 0} {
6673 $ctext insert end "$line\n" filesep
6676 set x [string range $line 0 0]
6677 if {$x == "-" || $x == "+"} {
6678 set tag [expr {$x == "+"}]
6679 $ctext insert end "$line\n" d$tag
6680 } elseif {$x == " "} {
6681 $ctext insert end "$line\n"
6683 # "\ No newline at end of file",
6684 # or something else we don't recognize
6685 $ctext insert end "$line\n" hunksep
6689 $ctext conf -state disabled
6694 return [expr {$nr >= 1000? 2: 1}]
6697 proc changediffdisp {} {
6698 global ctext diffelide
6700 $ctext tag conf d0 -elide [lindex $diffelide 0]
6701 $ctext tag conf d1 -elide [lindex $diffelide 1]
6704 proc highlightfile {loc cline} {
6705 global ctext cflist cflist_top
6708 $cflist tag remove highlight $cflist_top.0 "$cflist_top.0 lineend"
6709 $cflist tag add highlight $cline.0 "$cline.0 lineend"
6710 $cflist see $cline.0
6711 set cflist_top $cline
6715 global difffilestart ctext cmitmode
6717 if {$cmitmode eq "tree"} return
6720 set here [$ctext index @0,0]
6721 foreach loc $difffilestart {
6722 if {[$ctext compare $loc >= $here]} {
6723 highlightfile $prev $prevline
6729 highlightfile $prev $prevline
6733 global difffilestart ctext cmitmode
6735 if {$cmitmode eq "tree"} return
6736 set here [$ctext index @0,0]
6738 foreach loc $difffilestart {
6740 if {[$ctext compare $loc > $here]} {
6741 highlightfile $loc $line
6747 proc clear_ctext {{first 1.0}} {
6748 global ctext smarktop smarkbot
6751 set l [lindex [split $first .] 0]
6752 if {![info exists smarktop] || [$ctext compare $first < $smarktop.0]} {
6755 if {![info exists smarkbot] || [$ctext compare $first < $smarkbot.0]} {
6758 $ctext delete $first end
6759 if {$first eq "1.0"} {
6760 catch {unset pendinglinks}
6764 proc settabs {{firstab {}}} {
6765 global firsttabstop tabstop ctext have_tk85
6767 if {$firstab ne {} && $have_tk85} {
6768 set firsttabstop $firstab
6770 set w [font measure textfont "0"]
6771 if {$firsttabstop != 0} {
6772 $ctext conf -tabs [list [expr {($firsttabstop + $tabstop) * $w}] \
6773 [expr {($firsttabstop + 2 * $tabstop) * $w}]]
6774 } elseif {$have_tk85 || $tabstop != 8} {
6775 $ctext conf -tabs [expr {$tabstop * $w}]
6777 $ctext conf -tabs {}
6781 proc incrsearch {name ix op} {
6782 global ctext searchstring searchdirn
6784 $ctext tag remove found 1.0 end
6785 if {[catch {$ctext index anchor}]} {
6786 # no anchor set, use start of selection, or of visible area
6787 set sel [$ctext tag ranges sel]
6789 $ctext mark set anchor [lindex $sel 0]
6790 } elseif {$searchdirn eq "-forwards"} {
6791 $ctext mark set anchor @0,0
6793 $ctext mark set anchor @0,[winfo height $ctext]
6796 if {$searchstring ne {}} {
6797 set here [$ctext search $searchdirn -- $searchstring anchor]
6806 global sstring ctext searchstring searchdirn
6809 $sstring icursor end
6810 set searchdirn -forwards
6811 if {$searchstring ne {}} {
6812 set sel [$ctext tag ranges sel]
6814 set start "[lindex $sel 0] + 1c"
6815 } elseif {[catch {set start [$ctext index anchor]}]} {
6818 set match [$ctext search -count mlen -- $searchstring $start]
6819 $ctext tag remove sel 1.0 end
6825 set mend "$match + $mlen c"
6826 $ctext tag add sel $match $mend
6827 $ctext mark unset anchor
6831 proc dosearchback {} {
6832 global sstring ctext searchstring searchdirn
6835 $sstring icursor end
6836 set searchdirn -backwards
6837 if {$searchstring ne {}} {
6838 set sel [$ctext tag ranges sel]
6840 set start [lindex $sel 0]
6841 } elseif {[catch {set start [$ctext index anchor]}]} {
6842 set start @0,[winfo height $ctext]
6844 set match [$ctext search -backwards -count ml -- $searchstring $start]
6845 $ctext tag remove sel 1.0 end
6851 set mend "$match + $ml c"
6852 $ctext tag add sel $match $mend
6853 $ctext mark unset anchor
6857 proc searchmark {first last} {
6858 global ctext searchstring
6862 set match [$ctext search -count mlen -- $searchstring $mend $last.end]
6863 if {$match eq {}} break
6864 set mend "$match + $mlen c"
6865 $ctext tag add found $match $mend
6869 proc searchmarkvisible {doall} {
6870 global ctext smarktop smarkbot
6872 set topline [lindex [split [$ctext index @0,0] .] 0]
6873 set botline [lindex [split [$ctext index @0,[winfo height $ctext]] .] 0]
6874 if {$doall || $botline < $smarktop || $topline > $smarkbot} {
6875 # no overlap with previous
6876 searchmark $topline $botline
6877 set smarktop $topline
6878 set smarkbot $botline
6880 if {$topline < $smarktop} {
6881 searchmark $topline [expr {$smarktop-1}]
6882 set smarktop $topline
6884 if {$botline > $smarkbot} {
6885 searchmark [expr {$smarkbot+1}] $botline
6886 set smarkbot $botline
6891 proc scrolltext {f0 f1} {
6894 .bleft.bottom.sb set $f0 $f1
6895 if {$searchstring ne {}} {
6901 global linespc charspc canvx0 canvy0
6902 global xspc1 xspc2 lthickness
6904 set linespc [font metrics mainfont -linespace]
6905 set charspc [font measure mainfont "m"]
6906 set canvy0 [expr {int(3 + 0.5 * $linespc)}]
6907 set canvx0 [expr {int(3 + 0.5 * $linespc)}]
6908 set lthickness [expr {int($linespc / 9) + 1}]
6909 set xspc1(0) $linespc
6917 set ymax [lindex [$canv cget -scrollregion] 3]
6918 if {$ymax eq {} || $ymax == 0} return
6919 set span [$canv yview]
6922 allcanvs yview moveto [lindex $span 0]
6924 if {$selectedline ne {}} {
6925 selectline $selectedline 0
6926 allcanvs yview moveto [lindex $span 0]
6930 proc parsefont {f n} {
6933 set fontattr($f,family) [lindex $n 0]
6935 if {$s eq {} || $s == 0} {
6938 set s [expr {int(-$s / [winfo fpixels . 1p] + 0.5)}]
6940 set fontattr($f,size) $s
6941 set fontattr($f,weight) normal
6942 set fontattr($f,slant) roman
6943 foreach style [lrange $n 2 end] {
6946 "bold" {set fontattr($f,weight) $style}
6948 "italic" {set fontattr($f,slant) $style}
6953 proc fontflags {f {isbold 0}} {
6956 return [list -family $fontattr($f,family) -size $fontattr($f,size) \
6957 -weight [expr {$isbold? "bold": $fontattr($f,weight)}] \
6958 -slant $fontattr($f,slant)]
6964 set n [list $fontattr($f,family) $fontattr($f,size)]
6965 if {$fontattr($f,weight) eq "bold"} {
6968 if {$fontattr($f,slant) eq "italic"} {
6974 proc incrfont {inc} {
6975 global mainfont textfont ctext canv cflist showrefstop
6976 global stopped entries fontattr
6979 set s $fontattr(mainfont,size)
6984 set fontattr(mainfont,size) $s
6985 font config mainfont -size $s
6986 font config mainfontbold -size $s
6987 set mainfont [fontname mainfont]
6988 set s $fontattr(textfont,size)
6993 set fontattr(textfont,size) $s
6994 font config textfont -size $s
6995 font config textfontbold -size $s
6996 set textfont [fontname textfont]
7003 global sha1entry sha1string
7004 if {[string length $sha1string] == 40} {
7005 $sha1entry delete 0 end
7009 proc sha1change {n1 n2 op} {
7010 global sha1string currentid sha1but
7011 if {$sha1string == {}
7012 || ([info exists currentid] && $sha1string == $currentid)} {
7017 if {[$sha1but cget -state] == $state} return
7018 if {$state == "normal"} {
7019 $sha1but conf -state normal -relief raised -text "[mc "Goto:"] "
7021 $sha1but conf -state disabled -relief flat -text "[mc "SHA1 ID:"] "
7025 proc gotocommit {} {
7026 global sha1string tagids headids curview varcid
7028 if {$sha1string == {}
7029 || ([info exists currentid] && $sha1string == $currentid)} return
7030 if {[info exists tagids($sha1string)]} {
7031 set id $tagids($sha1string)
7032 } elseif {[info exists headids($sha1string)]} {
7033 set id $headids($sha1string)
7035 set id [string tolower $sha1string]
7036 if {[regexp {^[0-9a-f]{4,39}$} $id]} {
7037 set matches [array names varcid "$curview,$id*"]
7038 if {$matches ne {}} {
7039 if {[llength $matches] > 1} {
7040 error_popup [mc "Short SHA1 id %s is ambiguous" $id]
7043 set id [lindex [split [lindex $matches 0] ","] 1]
7047 if {[commitinview $id $curview]} {
7048 selectline [rowofcommit $id] 1
7051 if {[regexp {^[0-9a-fA-F]{4,}$} $sha1string]} {
7052 set msg [mc "SHA1 id %s is not known" $sha1string]
7054 set msg [mc "Tag/Head %s is not known" $sha1string]
7059 proc lineenter {x y id} {
7060 global hoverx hovery hoverid hovertimer
7061 global commitinfo canv
7063 if {![info exists commitinfo($id)] && ![getcommit $id]} return
7067 if {[info exists hovertimer]} {
7068 after cancel $hovertimer
7070 set hovertimer [after 500 linehover]
7074 proc linemotion {x y id} {
7075 global hoverx hovery hoverid hovertimer
7077 if {[info exists hoverid] && $id == $hoverid} {
7080 if {[info exists hovertimer]} {
7081 after cancel $hovertimer
7083 set hovertimer [after 500 linehover]
7087 proc lineleave {id} {
7088 global hoverid hovertimer canv
7090 if {[info exists hoverid] && $id == $hoverid} {
7092 if {[info exists hovertimer]} {
7093 after cancel $hovertimer
7101 global hoverx hovery hoverid hovertimer
7102 global canv linespc lthickness
7105 set text [lindex $commitinfo($hoverid) 0]
7106 set ymax [lindex [$canv cget -scrollregion] 3]
7107 if {$ymax == {}} return
7108 set yfrac [lindex [$canv yview] 0]
7109 set x [expr {$hoverx + 2 * $linespc}]
7110 set y [expr {$hovery + $yfrac * $ymax - $linespc / 2}]
7111 set x0 [expr {$x - 2 * $lthickness}]
7112 set y0 [expr {$y - 2 * $lthickness}]
7113 set x1 [expr {$x + [font measure mainfont $text] + 2 * $lthickness}]
7114 set y1 [expr {$y + $linespc + 2 * $lthickness}]
7115 set t [$canv create rectangle $x0 $y0 $x1 $y1 \
7116 -fill \#ffff80 -outline black -width 1 -tags hover]
7118 set t [$canv create text $x $y -anchor nw -text $text -tags hover \
7123 proc clickisonarrow {id y} {
7126 set ranges [rowranges $id]
7127 set thresh [expr {2 * $lthickness + 6}]
7128 set n [expr {[llength $ranges] - 1}]
7129 for {set i 1} {$i < $n} {incr i} {
7130 set row [lindex $ranges $i]
7131 if {abs([yc $row] - $y) < $thresh} {
7138 proc arrowjump {id n y} {
7141 # 1 <-> 2, 3 <-> 4, etc...
7142 set n [expr {(($n - 1) ^ 1) + 1}]
7143 set row [lindex [rowranges $id] $n]
7145 set ymax [lindex [$canv cget -scrollregion] 3]
7146 if {$ymax eq {} || $ymax <= 0} return
7147 set view [$canv yview]
7148 set yspan [expr {[lindex $view 1] - [lindex $view 0]}]
7149 set yfrac [expr {$yt / $ymax - $yspan / 2}]
7153 allcanvs yview moveto $yfrac
7156 proc lineclick {x y id isnew} {
7157 global ctext commitinfo children canv thickerline curview
7159 if {![info exists commitinfo($id)] && ![getcommit $id]} return
7164 # draw this line thicker than normal
7168 set ymax [lindex [$canv cget -scrollregion] 3]
7169 if {$ymax eq {}} return
7170 set yfrac [lindex [$canv yview] 0]
7171 set y [expr {$y + $yfrac * $ymax}]
7173 set dirn [clickisonarrow $id $y]
7175 arrowjump $id $dirn $y
7180 addtohistory [list lineclick $x $y $id 0]
7182 # fill the details pane with info about this line
7183 $ctext conf -state normal
7186 $ctext insert end "[mc "Parent"]:\t"
7187 $ctext insert end $id link0
7189 set info $commitinfo($id)
7190 $ctext insert end "\n\t[lindex $info 0]\n"
7191 $ctext insert end "\t[mc "Author"]:\t[lindex $info 1]\n"
7192 set date [formatdate [lindex $info 2]]
7193 $ctext insert end "\t[mc "Date"]:\t$date\n"
7194 set kids $children($curview,$id)
7196 $ctext insert end "\n[mc "Children"]:"
7198 foreach child $kids {
7200 if {![info exists commitinfo($child)] && ![getcommit $child]} continue
7201 set info $commitinfo($child)
7202 $ctext insert end "\n\t"
7203 $ctext insert end $child link$i
7204 setlink $child link$i
7205 $ctext insert end "\n\t[lindex $info 0]"
7206 $ctext insert end "\n\t[mc "Author"]:\t[lindex $info 1]"
7207 set date [formatdate [lindex $info 2]]
7208 $ctext insert end "\n\t[mc "Date"]:\t$date\n"
7211 $ctext conf -state disabled
7215 proc normalline {} {
7217 if {[info exists thickerline]} {
7226 if {[commitinview $id $curview]} {
7227 selectline [rowofcommit $id] 1
7233 if {![info exists startmstime]} {
7234 set startmstime [clock clicks -milliseconds]
7236 return [format "%.3f" [expr {([clock click -milliseconds] - $startmstime) / 1000.0}]]
7239 proc rowmenu {x y id} {
7240 global rowctxmenu selectedline rowmenuid curview
7241 global nullid nullid2 fakerowmenu mainhead
7245 if {$selectedline eq {} || [rowofcommit $id] eq $selectedline} {
7250 if {$id ne $nullid && $id ne $nullid2} {
7251 set menu $rowctxmenu
7252 if {$mainhead ne {}} {
7253 $menu entryconfigure 7 -label [mc "Reset %s branch to here" $mainhead]
7255 $menu entryconfigure 7 -label [mc "Detached head: can't reset" $mainhead] -state disabled
7258 set menu $fakerowmenu
7260 $menu entryconfigure [mc "Diff this -> selected"] -state $state
7261 $menu entryconfigure [mc "Diff selected -> this"] -state $state
7262 $menu entryconfigure [mc "Make patch"] -state $state
7263 tk_popup $menu $x $y
7266 proc diffvssel {dirn} {
7267 global rowmenuid selectedline
7269 if {$selectedline eq {}} return
7271 set oldid [commitonrow $selectedline]
7272 set newid $rowmenuid
7274 set oldid $rowmenuid
7275 set newid [commitonrow $selectedline]
7277 addtohistory [list doseldiff $oldid $newid]
7278 doseldiff $oldid $newid
7281 proc doseldiff {oldid newid} {
7285 $ctext conf -state normal
7287 init_flist [mc "Top"]
7288 $ctext insert end "[mc "From"] "
7289 $ctext insert end $oldid link0
7290 setlink $oldid link0
7291 $ctext insert end "\n "
7292 $ctext insert end [lindex $commitinfo($oldid) 0]
7293 $ctext insert end "\n\n[mc "To"] "
7294 $ctext insert end $newid link1
7295 setlink $newid link1
7296 $ctext insert end "\n "
7297 $ctext insert end [lindex $commitinfo($newid) 0]
7298 $ctext insert end "\n"
7299 $ctext conf -state disabled
7300 $ctext tag remove found 1.0 end
7301 startdiff [list $oldid $newid]
7305 global rowmenuid currentid commitinfo patchtop patchnum
7307 if {![info exists currentid]} return
7308 set oldid $currentid
7309 set oldhead [lindex $commitinfo($oldid) 0]
7310 set newid $rowmenuid
7311 set newhead [lindex $commitinfo($newid) 0]
7314 catch {destroy $top}
7316 label $top.title -text [mc "Generate patch"]
7317 grid $top.title - -pady 10
7318 label $top.from -text [mc "From:"]
7319 entry $top.fromsha1 -width 40 -relief flat
7320 $top.fromsha1 insert 0 $oldid
7321 $top.fromsha1 conf -state readonly
7322 grid $top.from $top.fromsha1 -sticky w
7323 entry $top.fromhead -width 60 -relief flat
7324 $top.fromhead insert 0 $oldhead
7325 $top.fromhead conf -state readonly
7326 grid x $top.fromhead -sticky w
7327 label $top.to -text [mc "To:"]
7328 entry $top.tosha1 -width 40 -relief flat
7329 $top.tosha1 insert 0 $newid
7330 $top.tosha1 conf -state readonly
7331 grid $top.to $top.tosha1 -sticky w
7332 entry $top.tohead -width 60 -relief flat
7333 $top.tohead insert 0 $newhead
7334 $top.tohead conf -state readonly
7335 grid x $top.tohead -sticky w
7336 button $top.rev -text [mc "Reverse"] -command mkpatchrev -padx 5
7337 grid $top.rev x -pady 10
7338 label $top.flab -text [mc "Output file:"]
7339 entry $top.fname -width 60
7340 $top.fname insert 0 [file normalize "patch$patchnum.patch"]
7342 grid $top.flab $top.fname -sticky w
7344 button $top.buts.gen -text [mc "Generate"] -command mkpatchgo
7345 button $top.buts.can -text [mc "Cancel"] -command mkpatchcan
7346 grid $top.buts.gen $top.buts.can
7347 grid columnconfigure $top.buts 0 -weight 1 -uniform a
7348 grid columnconfigure $top.buts 1 -weight 1 -uniform a
7349 grid $top.buts - -pady 10 -sticky ew
7353 proc mkpatchrev {} {
7356 set oldid [$patchtop.fromsha1 get]
7357 set oldhead [$patchtop.fromhead get]
7358 set newid [$patchtop.tosha1 get]
7359 set newhead [$patchtop.tohead get]
7360 foreach e [list fromsha1 fromhead tosha1 tohead] \
7361 v [list $newid $newhead $oldid $oldhead] {
7362 $patchtop.$e conf -state normal
7363 $patchtop.$e delete 0 end
7364 $patchtop.$e insert 0 $v
7365 $patchtop.$e conf -state readonly
7370 global patchtop nullid nullid2
7372 set oldid [$patchtop.fromsha1 get]
7373 set newid [$patchtop.tosha1 get]
7374 set fname [$patchtop.fname get]
7375 set cmd [diffcmd [list $oldid $newid] -p]
7376 # trim off the initial "|"
7377 set cmd [lrange $cmd 1 end]
7378 lappend cmd >$fname &
7379 if {[catch {eval exec $cmd} err]} {
7380 error_popup "[mc "Error creating patch:"] $err"
7382 catch {destroy $patchtop}
7386 proc mkpatchcan {} {
7389 catch {destroy $patchtop}
7394 global rowmenuid mktagtop commitinfo
7398 catch {destroy $top}
7400 label $top.title -text [mc "Create tag"]
7401 grid $top.title - -pady 10
7402 label $top.id -text [mc "ID:"]
7403 entry $top.sha1 -width 40 -relief flat
7404 $top.sha1 insert 0 $rowmenuid
7405 $top.sha1 conf -state readonly
7406 grid $top.id $top.sha1 -sticky w
7407 entry $top.head -width 60 -relief flat
7408 $top.head insert 0 [lindex $commitinfo($rowmenuid) 0]
7409 $top.head conf -state readonly
7410 grid x $top.head -sticky w
7411 label $top.tlab -text [mc "Tag name:"]
7412 entry $top.tag -width 60
7413 grid $top.tlab $top.tag -sticky w
7415 button $top.buts.gen -text [mc "Create"] -command mktaggo
7416 button $top.buts.can -text [mc "Cancel"] -command mktagcan
7417 grid $top.buts.gen $top.buts.can
7418 grid columnconfigure $top.buts 0 -weight 1 -uniform a
7419 grid columnconfigure $top.buts 1 -weight 1 -uniform a
7420 grid $top.buts - -pady 10 -sticky ew
7425 global mktagtop env tagids idtags
7427 set id [$mktagtop.sha1 get]
7428 set tag [$mktagtop.tag get]
7430 error_popup [mc "No tag name specified"]
7433 if {[info exists tagids($tag)]} {
7434 error_popup [mc "Tag \"%s\" already exists" $tag]
7438 exec git tag $tag $id
7440 error_popup "[mc "Error creating tag:"] $err"
7444 set tagids($tag) $id
7445 lappend idtags($id) $tag
7452 proc redrawtags {id} {
7453 global canv linehtag idpos currentid curview cmitlisted
7454 global canvxmax iddrawn circleitem mainheadid circlecolors
7456 if {![commitinview $id $curview]} return
7457 if {![info exists iddrawn($id)]} return
7458 set row [rowofcommit $id]
7459 if {$id eq $mainheadid} {
7462 set ofill [lindex $circlecolors $cmitlisted($curview,$id)]
7464 $canv itemconf $circleitem($row) -fill $ofill
7465 $canv delete tag.$id
7466 set xt [eval drawtags $id $idpos($id)]
7467 $canv coords $linehtag($row) $xt [lindex $idpos($id) 2]
7468 set text [$canv itemcget $linehtag($row) -text]
7469 set font [$canv itemcget $linehtag($row) -font]
7470 set xr [expr {$xt + [font measure $font $text]}]
7471 if {$xr > $canvxmax} {
7475 if {[info exists currentid] && $currentid == $id} {
7483 catch {destroy $mktagtop}
7492 proc writecommit {} {
7493 global rowmenuid wrcomtop commitinfo wrcomcmd
7495 set top .writecommit
7497 catch {destroy $top}
7499 label $top.title -text [mc "Write commit to file"]
7500 grid $top.title - -pady 10
7501 label $top.id -text [mc "ID:"]
7502 entry $top.sha1 -width 40 -relief flat
7503 $top.sha1 insert 0 $rowmenuid
7504 $top.sha1 conf -state readonly
7505 grid $top.id $top.sha1 -sticky w
7506 entry $top.head -width 60 -relief flat
7507 $top.head insert 0 [lindex $commitinfo($rowmenuid) 0]
7508 $top.head conf -state readonly
7509 grid x $top.head -sticky w
7510 label $top.clab -text [mc "Command:"]
7511 entry $top.cmd -width 60 -textvariable wrcomcmd
7512 grid $top.clab $top.cmd -sticky w -pady 10
7513 label $top.flab -text [mc "Output file:"]
7514 entry $top.fname -width 60
7515 $top.fname insert 0 [file normalize "commit-[string range $rowmenuid 0 6]"]
7516 grid $top.flab $top.fname -sticky w
7518 button $top.buts.gen -text [mc "Write"] -command wrcomgo
7519 button $top.buts.can -text [mc "Cancel"] -command wrcomcan
7520 grid $top.buts.gen $top.buts.can
7521 grid columnconfigure $top.buts 0 -weight 1 -uniform a
7522 grid columnconfigure $top.buts 1 -weight 1 -uniform a
7523 grid $top.buts - -pady 10 -sticky ew
7530 set id [$wrcomtop.sha1 get]
7531 set cmd "echo $id | [$wrcomtop.cmd get]"
7532 set fname [$wrcomtop.fname get]
7533 if {[catch {exec sh -c $cmd >$fname &} err]} {
7534 error_popup "[mc "Error writing commit:"] $err"
7536 catch {destroy $wrcomtop}
7543 catch {destroy $wrcomtop}
7548 global rowmenuid mkbrtop
7551 catch {destroy $top}
7553 label $top.title -text [mc "Create new branch"]
7554 grid $top.title - -pady 10
7555 label $top.id -text [mc "ID:"]
7556 entry $top.sha1 -width 40 -relief flat
7557 $top.sha1 insert 0 $rowmenuid
7558 $top.sha1 conf -state readonly
7559 grid $top.id $top.sha1 -sticky w
7560 label $top.nlab -text [mc "Name:"]
7561 entry $top.name -width 40
7562 grid $top.nlab $top.name -sticky w
7564 button $top.buts.go -text [mc "Create"] -command [list mkbrgo $top]
7565 button $top.buts.can -text [mc "Cancel"] -command "catch {destroy $top}"
7566 grid $top.buts.go $top.buts.can
7567 grid columnconfigure $top.buts 0 -weight 1 -uniform a
7568 grid columnconfigure $top.buts 1 -weight 1 -uniform a
7569 grid $top.buts - -pady 10 -sticky ew
7574 global headids idheads
7576 set name [$top.name get]
7577 set id [$top.sha1 get]
7579 error_popup [mc "Please specify a name for the new branch"]
7582 catch {destroy $top}
7586 exec git branch $name $id
7591 set headids($name) $id
7592 lappend idheads($id) $name
7601 proc cherrypick {} {
7602 global rowmenuid curview
7603 global mainhead mainheadid
7605 set oldhead [exec git rev-parse HEAD]
7606 set dheads [descheads $rowmenuid]
7607 if {$dheads ne {} && [lsearch -exact $dheads $oldhead] >= 0} {
7608 set ok [confirm_popup [mc "Commit %s is already\
7609 included in branch %s -- really re-apply it?" \
7610 [string range $rowmenuid 0 7] $mainhead]]
7613 nowbusy cherrypick [mc "Cherry-picking"]
7615 # Unfortunately git-cherry-pick writes stuff to stderr even when
7616 # no error occurs, and exec takes that as an indication of error...
7617 if {[catch {exec sh -c "git cherry-pick -r $rowmenuid 2>&1"} err]} {
7622 set newhead [exec git rev-parse HEAD]
7623 if {$newhead eq $oldhead} {
7625 error_popup [mc "No changes committed"]
7628 addnewchild $newhead $oldhead
7629 if {[commitinview $oldhead $curview]} {
7630 insertrow $newhead $oldhead $curview
7631 if {$mainhead ne {}} {
7632 movehead $newhead $mainhead
7633 movedhead $newhead $mainhead
7635 set mainheadid $newhead
7644 global mainhead rowmenuid confirm_ok resettype
7647 set w ".confirmreset"
7650 wm title $w [mc "Confirm reset"]
7651 message $w.m -text \
7652 [mc "Reset branch %s to %s?" $mainhead [string range $rowmenuid 0 7]] \
7653 -justify center -aspect 1000
7654 pack $w.m -side top -fill x -padx 20 -pady 20
7655 frame $w.f -relief sunken -border 2
7656 message $w.f.rt -text [mc "Reset type:"] -aspect 1000
7657 grid $w.f.rt -sticky w
7659 radiobutton $w.f.soft -value soft -variable resettype -justify left \
7660 -text [mc "Soft: Leave working tree and index untouched"]
7661 grid $w.f.soft -sticky w
7662 radiobutton $w.f.mixed -value mixed -variable resettype -justify left \
7663 -text [mc "Mixed: Leave working tree untouched, reset index"]
7664 grid $w.f.mixed -sticky w
7665 radiobutton $w.f.hard -value hard -variable resettype -justify left \
7666 -text [mc "Hard: Reset working tree and index\n(discard ALL local changes)"]
7667 grid $w.f.hard -sticky w
7668 pack $w.f -side top -fill x
7669 button $w.ok -text [mc OK] -command "set confirm_ok 1; destroy $w"
7670 pack $w.ok -side left -fill x -padx 20 -pady 20
7671 button $w.cancel -text [mc Cancel] -command "destroy $w"
7672 pack $w.cancel -side right -fill x -padx 20 -pady 20
7673 bind $w <Visibility> "grab $w; focus $w"
7675 if {!$confirm_ok} return
7676 if {[catch {set fd [open \
7677 [list | git reset --$resettype $rowmenuid 2>@1] r]} err]} {
7681 filerun $fd [list readresetstat $fd]
7682 nowbusy reset [mc "Resetting"]
7687 proc readresetstat {fd} {
7688 global mainhead mainheadid showlocalchanges rprogcoord
7690 if {[gets $fd line] >= 0} {
7691 if {[regexp {([0-9]+)% \(([0-9]+)/([0-9]+)\)} $line match p m n]} {
7692 set rprogcoord [expr {1.0 * $m / $n}]
7700 if {[catch {close $fd} err]} {
7703 set oldhead $mainheadid
7704 set newhead [exec git rev-parse HEAD]
7705 if {$newhead ne $oldhead} {
7706 movehead $newhead $mainhead
7707 movedhead $newhead $mainhead
7708 set mainheadid $newhead
7712 if {$showlocalchanges} {
7718 # context menu for a head
7719 proc headmenu {x y id head} {
7720 global headmenuid headmenuhead headctxmenu mainhead
7724 set headmenuhead $head
7726 if {$head eq $mainhead} {
7729 $headctxmenu entryconfigure 0 -state $state
7730 $headctxmenu entryconfigure 1 -state $state
7731 tk_popup $headctxmenu $x $y
7735 global headmenuid headmenuhead headids
7736 global showlocalchanges mainheadid
7738 # check the tree is clean first??
7739 nowbusy checkout [mc "Checking out"]
7743 set fd [open [list | git checkout $headmenuhead 2>@1] r]
7747 if {$showlocalchanges} {
7751 filerun $fd [list readcheckoutstat $fd $headmenuhead $headmenuid]
7755 proc readcheckoutstat {fd newhead newheadid} {
7756 global mainhead mainheadid headids showlocalchanges progresscoords
7758 if {[gets $fd line] >= 0} {
7759 if {[regexp {([0-9]+)% \(([0-9]+)/([0-9]+)\)} $line match p m n]} {
7760 set progresscoords [list 0 [expr {1.0 * $m / $n}]]
7765 set progresscoords {0 0}
7768 if {[catch {close $fd} err]} {
7771 set oldmainid $mainheadid
7772 set mainhead $newhead
7773 set mainheadid $newheadid
7774 redrawtags $oldmainid
7775 redrawtags $newheadid
7777 if {$showlocalchanges} {
7783 global headmenuid headmenuhead mainhead
7786 set head $headmenuhead
7788 # this check shouldn't be needed any more...
7789 if {$head eq $mainhead} {
7790 error_popup [mc "Cannot delete the currently checked-out branch"]
7793 set dheads [descheads $id]
7794 if {[llength $dheads] == 1 && $idheads($dheads) eq $head} {
7795 # the stuff on this branch isn't on any other branch
7796 if {![confirm_popup [mc "The commits on branch %s aren't on any other\
7797 branch.\nReally delete branch %s?" $head $head]]} return
7801 if {[catch {exec git branch -D $head} err]} {
7806 removehead $id $head
7807 removedhead $id $head
7814 # Display a list of tags and heads
7816 global showrefstop bgcolor fgcolor selectbgcolor
7817 global bglist fglist reflistfilter reflist maincursor
7820 set showrefstop $top
7821 if {[winfo exists $top]} {
7827 wm title $top [mc "Tags and heads: %s" [file tail [pwd]]]
7828 text $top.list -background $bgcolor -foreground $fgcolor \
7829 -selectbackground $selectbgcolor -font mainfont \
7830 -xscrollcommand "$top.xsb set" -yscrollcommand "$top.ysb set" \
7831 -width 30 -height 20 -cursor $maincursor \
7832 -spacing1 1 -spacing3 1 -state disabled
7833 $top.list tag configure highlight -background $selectbgcolor
7834 lappend bglist $top.list
7835 lappend fglist $top.list
7836 scrollbar $top.ysb -command "$top.list yview" -orient vertical
7837 scrollbar $top.xsb -command "$top.list xview" -orient horizontal
7838 grid $top.list $top.ysb -sticky nsew
7839 grid $top.xsb x -sticky ew
7841 label $top.f.l -text "[mc "Filter"]: "
7842 entry $top.f.e -width 20 -textvariable reflistfilter
7843 set reflistfilter "*"
7844 trace add variable reflistfilter write reflistfilter_change
7845 pack $top.f.e -side right -fill x -expand 1
7846 pack $top.f.l -side left
7847 grid $top.f - -sticky ew -pady 2
7848 button $top.close -command [list destroy $top] -text [mc "Close"]
7850 grid columnconfigure $top 0 -weight 1
7851 grid rowconfigure $top 0 -weight 1
7852 bind $top.list <1> {break}
7853 bind $top.list <B1-Motion> {break}
7854 bind $top.list <ButtonRelease-1> {sel_reflist %W %x %y; break}
7859 proc sel_reflist {w x y} {
7860 global showrefstop reflist headids tagids otherrefids
7862 if {![winfo exists $showrefstop]} return
7863 set l [lindex [split [$w index "@$x,$y"] "."] 0]
7864 set ref [lindex $reflist [expr {$l-1}]]
7865 set n [lindex $ref 0]
7866 switch -- [lindex $ref 1] {
7867 "H" {selbyid $headids($n)}
7868 "T" {selbyid $tagids($n)}
7869 "o" {selbyid $otherrefids($n)}
7871 $showrefstop.list tag add highlight $l.0 "$l.0 lineend"
7874 proc unsel_reflist {} {
7877 if {![info exists showrefstop] || ![winfo exists $showrefstop]} return
7878 $showrefstop.list tag remove highlight 0.0 end
7881 proc reflistfilter_change {n1 n2 op} {
7882 global reflistfilter
7884 after cancel refill_reflist
7885 after 200 refill_reflist
7888 proc refill_reflist {} {
7889 global reflist reflistfilter showrefstop headids tagids otherrefids
7890 global curview commitinterest
7892 if {![info exists showrefstop] || ![winfo exists $showrefstop]} return
7894 foreach n [array names headids] {
7895 if {[string match $reflistfilter $n]} {
7896 if {[commitinview $headids($n) $curview]} {
7897 lappend refs [list $n H]
7899 set commitinterest($headids($n)) {run refill_reflist}
7903 foreach n [array names tagids] {
7904 if {[string match $reflistfilter $n]} {
7905 if {[commitinview $tagids($n) $curview]} {
7906 lappend refs [list $n T]
7908 set commitinterest($tagids($n)) {run refill_reflist}
7912 foreach n [array names otherrefids] {
7913 if {[string match $reflistfilter $n]} {
7914 if {[commitinview $otherrefids($n) $curview]} {
7915 lappend refs [list $n o]
7917 set commitinterest($otherrefids($n)) {run refill_reflist}
7921 set refs [lsort -index 0 $refs]
7922 if {$refs eq $reflist} return
7924 # Update the contents of $showrefstop.list according to the
7925 # differences between $reflist (old) and $refs (new)
7926 $showrefstop.list conf -state normal
7927 $showrefstop.list insert end "\n"
7930 while {$i < [llength $reflist] || $j < [llength $refs]} {
7931 if {$i < [llength $reflist]} {
7932 if {$j < [llength $refs]} {
7933 set cmp [string compare [lindex $reflist $i 0] \
7934 [lindex $refs $j 0]]
7936 set cmp [string compare [lindex $reflist $i 1] \
7937 [lindex $refs $j 1]]
7947 $showrefstop.list delete "[expr {$j+1}].0" "[expr {$j+2}].0"
7955 set l [expr {$j + 1}]
7956 $showrefstop.list image create $l.0 -align baseline \
7957 -image reficon-[lindex $refs $j 1] -padx 2
7958 $showrefstop.list insert $l.1 "[lindex $refs $j 0]\n"
7964 # delete last newline
7965 $showrefstop.list delete end-2c end-1c
7966 $showrefstop.list conf -state disabled
7969 # Stuff for finding nearby tags
7970 proc getallcommits {} {
7971 global allcommits nextarc seeds allccache allcwait cachedarcs allcupdate
7972 global idheads idtags idotherrefs allparents tagobjid
7974 if {![info exists allcommits]} {
7980 set allccache [file join [gitdir] "gitk.cache"]
7982 set f [open $allccache r]
7991 set cmd [list | git rev-list --parents]
7992 set allcupdate [expr {$seeds ne {}}]
7996 set refs [concat [array names idheads] [array names idtags] \
7997 [array names idotherrefs]]
8000 foreach name [array names tagobjid] {
8001 lappend tagobjs $tagobjid($name)
8003 foreach id [lsort -unique $refs] {
8004 if {![info exists allparents($id)] &&
8005 [lsearch -exact $tagobjs $id] < 0} {
8016 set fd [open [concat $cmd $ids] r]
8017 fconfigure $fd -blocking 0
8020 filerun $fd [list getallclines $fd]
8026 # Since most commits have 1 parent and 1 child, we group strings of
8027 # such commits into "arcs" joining branch/merge points (BMPs), which
8028 # are commits that either don't have 1 parent or don't have 1 child.
8030 # arcnos(id) - incoming arcs for BMP, arc we're on for other nodes
8031 # arcout(id) - outgoing arcs for BMP
8032 # arcids(a) - list of IDs on arc including end but not start
8033 # arcstart(a) - BMP ID at start of arc
8034 # arcend(a) - BMP ID at end of arc
8035 # growing(a) - arc a is still growing
8036 # arctags(a) - IDs out of arcids (excluding end) that have tags
8037 # archeads(a) - IDs out of arcids (excluding end) that have heads
8038 # The start of an arc is at the descendent end, so "incoming" means
8039 # coming from descendents, and "outgoing" means going towards ancestors.
8041 proc getallclines {fd} {
8042 global allparents allchildren idtags idheads nextarc
8043 global arcnos arcids arctags arcout arcend arcstart archeads growing
8044 global seeds allcommits cachedarcs allcupdate
8047 while {[incr nid] <= 1000 && [gets $fd line] >= 0} {
8048 set id [lindex $line 0]
8049 if {[info exists allparents($id)]} {
8054 set olds [lrange $line 1 end]
8055 set allparents($id) $olds
8056 if {![info exists allchildren($id)]} {
8057 set allchildren($id) {}
8062 if {[llength $olds] == 1 && [llength $a] == 1} {
8063 lappend arcids($a) $id
8064 if {[info exists idtags($id)]} {
8065 lappend arctags($a) $id
8067 if {[info exists idheads($id)]} {
8068 lappend archeads($a) $id
8070 if {[info exists allparents($olds)]} {
8071 # seen parent already
8072 if {![info exists arcout($olds)]} {
8075 lappend arcids($a) $olds
8076 set arcend($a) $olds
8079 lappend allchildren($olds) $id
8080 lappend arcnos($olds) $a
8084 foreach a $arcnos($id) {
8085 lappend arcids($a) $id
8092 lappend allchildren($p) $id
8093 set a [incr nextarc]
8094 set arcstart($a) $id
8101 if {[info exists allparents($p)]} {
8102 # seen it already, may need to make a new branch
8103 if {![info exists arcout($p)]} {
8106 lappend arcids($a) $p
8110 lappend arcnos($p) $a
8115 global cached_dheads cached_dtags cached_atags
8116 catch {unset cached_dheads}
8117 catch {unset cached_dtags}
8118 catch {unset cached_atags}
8121 return [expr {$nid >= 1000? 2: 1}]
8125 fconfigure $fd -blocking 1
8128 # got an error reading the list of commits
8129 # if we were updating, try rereading the whole thing again
8135 error_popup "[mc "Error reading commit topology information;\
8136 branch and preceding/following tag information\
8137 will be incomplete."]\n($err)"
8140 if {[incr allcommits -1] == 0} {
8150 proc recalcarc {a} {
8151 global arctags archeads arcids idtags idheads
8155 foreach id [lrange $arcids($a) 0 end-1] {
8156 if {[info exists idtags($id)]} {
8159 if {[info exists idheads($id)]} {
8164 set archeads($a) $ah
8168 global arcnos arcids nextarc arctags archeads idtags idheads
8169 global arcstart arcend arcout allparents growing
8172 if {[llength $a] != 1} {
8173 puts "oops splitarc called but [llength $a] arcs already"
8177 set i [lsearch -exact $arcids($a) $p]
8179 puts "oops splitarc $p not in arc $a"
8182 set na [incr nextarc]
8183 if {[info exists arcend($a)]} {
8184 set arcend($na) $arcend($a)
8186 set l [lindex $allparents([lindex $arcids($a) end]) 0]
8187 set j [lsearch -exact $arcnos($l) $a]
8188 set arcnos($l) [lreplace $arcnos($l) $j $j $na]
8190 set tail [lrange $arcids($a) [expr {$i+1}] end]
8191 set arcids($a) [lrange $arcids($a) 0 $i]
8193 set arcstart($na) $p
8195 set arcids($na) $tail
8196 if {[info exists growing($a)]} {
8202 if {[llength $arcnos($id)] == 1} {
8205 set j [lsearch -exact $arcnos($id) $a]
8206 set arcnos($id) [lreplace $arcnos($id) $j $j $na]
8210 # reconstruct tags and heads lists
8211 if {$arctags($a) ne {} || $archeads($a) ne {}} {
8216 set archeads($na) {}
8220 # Update things for a new commit added that is a child of one
8221 # existing commit. Used when cherry-picking.
8222 proc addnewchild {id p} {
8223 global allparents allchildren idtags nextarc
8224 global arcnos arcids arctags arcout arcend arcstart archeads growing
8225 global seeds allcommits
8227 if {![info exists allcommits] || ![info exists arcnos($p)]} return
8228 set allparents($id) [list $p]
8229 set allchildren($id) {}
8232 lappend allchildren($p) $id
8233 set a [incr nextarc]
8234 set arcstart($a) $id
8237 set arcids($a) [list $p]
8239 if {![info exists arcout($p)]} {
8242 lappend arcnos($p) $a
8243 set arcout($id) [list $a]
8246 # This implements a cache for the topology information.
8247 # The cache saves, for each arc, the start and end of the arc,
8248 # the ids on the arc, and the outgoing arcs from the end.
8249 proc readcache {f} {
8250 global arcnos arcids arcout arcstart arcend arctags archeads nextarc
8251 global idtags idheads allparents cachedarcs possible_seeds seeds growing
8256 if {$lim - $a > 500} {
8257 set lim [expr {$a + 500}]
8261 # finish reading the cache and setting up arctags, etc.
8263 if {$line ne "1"} {error "bad final version"}
8265 foreach id [array names idtags] {
8266 if {[info exists arcnos($id)] && [llength $arcnos($id)] == 1 &&
8267 [llength $allparents($id)] == 1} {
8268 set a [lindex $arcnos($id) 0]
8269 if {$arctags($a) eq {}} {
8274 foreach id [array names idheads] {
8275 if {[info exists arcnos($id)] && [llength $arcnos($id)] == 1 &&
8276 [llength $allparents($id)] == 1} {
8277 set a [lindex $arcnos($id) 0]
8278 if {$archeads($a) eq {}} {
8283 foreach id [lsort -unique $possible_seeds] {
8284 if {$arcnos($id) eq {}} {
8290 while {[incr a] <= $lim} {
8292 if {[llength $line] != 3} {error "bad line"}
8293 set s [lindex $line 0]
8295 lappend arcout($s) $a
8296 if {![info exists arcnos($s)]} {
8297 lappend possible_seeds $s
8300 set e [lindex $line 1]
8305 if {![info exists arcout($e)]} {
8309 set arcids($a) [lindex $line 2]
8310 foreach id $arcids($a) {
8311 lappend allparents($s) $id
8313 lappend arcnos($id) $a
8315 if {![info exists allparents($s)]} {
8316 set allparents($s) {}
8321 set nextarc [expr {$a - 1}]
8334 global nextarc cachedarcs possible_seeds
8338 if {[llength $line] != 2 || [lindex $line 0] ne "1"} {error "bad version"}
8339 # make sure it's an integer
8340 set cachedarcs [expr {int([lindex $line 1])}]
8341 if {$cachedarcs < 0} {error "bad number of arcs"}
8343 set possible_seeds {}
8351 proc dropcache {err} {
8352 global allcwait nextarc cachedarcs seeds
8354 #puts "dropping cache ($err)"
8355 foreach v {arcnos arcout arcids arcstart arcend growing \
8356 arctags archeads allparents allchildren} {
8367 proc writecache {f} {
8368 global cachearc cachedarcs allccache
8369 global arcstart arcend arcnos arcids arcout
8373 if {$lim - $a > 1000} {
8374 set lim [expr {$a + 1000}]
8377 while {[incr a] <= $lim} {
8378 if {[info exists arcend($a)]} {
8379 puts $f [list $arcstart($a) $arcend($a) $arcids($a)]
8381 puts $f [list $arcstart($a) {} $arcids($a)]
8386 catch {file delete $allccache}
8387 #puts "writing cache failed ($err)"
8390 set cachearc [expr {$a - 1}]
8391 if {$a > $cachedarcs} {
8400 global nextarc cachedarcs cachearc allccache
8402 if {$nextarc == $cachedarcs} return
8404 set cachedarcs $nextarc
8406 set f [open $allccache w]
8407 puts $f [list 1 $cachedarcs]
8412 # Returns 1 if a is an ancestor of b, -1 if b is an ancestor of a,
8413 # or 0 if neither is true.
8414 proc anc_or_desc {a b} {
8415 global arcout arcstart arcend arcnos cached_isanc
8417 if {$arcnos($a) eq $arcnos($b)} {
8418 # Both are on the same arc(s); either both are the same BMP,
8419 # or if one is not a BMP, the other is also not a BMP or is
8420 # the BMP at end of the arc (and it only has 1 incoming arc).
8421 # Or both can be BMPs with no incoming arcs.
8422 if {$a eq $b || $arcnos($a) eq {}} {
8425 # assert {[llength $arcnos($a)] == 1}
8426 set arc [lindex $arcnos($a) 0]
8427 set i [lsearch -exact $arcids($arc) $a]
8428 set j [lsearch -exact $arcids($arc) $b]
8429 if {$i < 0 || $i > $j} {
8436 if {![info exists arcout($a)]} {
8437 set arc [lindex $arcnos($a) 0]
8438 if {[info exists arcend($arc)]} {
8439 set aend $arcend($arc)
8443 set a $arcstart($arc)
8447 if {![info exists arcout($b)]} {
8448 set arc [lindex $arcnos($b) 0]
8449 if {[info exists arcend($arc)]} {
8450 set bend $arcend($arc)
8454 set b $arcstart($arc)
8464 if {[info exists cached_isanc($a,$bend)]} {
8465 if {$cached_isanc($a,$bend)} {
8469 if {[info exists cached_isanc($b,$aend)]} {
8470 if {$cached_isanc($b,$aend)} {
8473 if {[info exists cached_isanc($a,$bend)]} {
8478 set todo [list $a $b]
8481 for {set i 0} {$i < [llength $todo]} {incr i} {
8482 set x [lindex $todo $i]
8483 if {$anc($x) eq {}} {
8486 foreach arc $arcnos($x) {
8487 set xd $arcstart($arc)
8489 set cached_isanc($a,$bend) 1
8490 set cached_isanc($b,$aend) 0
8492 } elseif {$xd eq $aend} {
8493 set cached_isanc($b,$aend) 1
8494 set cached_isanc($a,$bend) 0
8497 if {![info exists anc($xd)]} {
8498 set anc($xd) $anc($x)
8500 } elseif {$anc($xd) ne $anc($x)} {
8505 set cached_isanc($a,$bend) 0
8506 set cached_isanc($b,$aend) 0
8510 # This identifies whether $desc has an ancestor that is
8511 # a growing tip of the graph and which is not an ancestor of $anc
8512 # and returns 0 if so and 1 if not.
8513 # If we subsequently discover a tag on such a growing tip, and that
8514 # turns out to be a descendent of $anc (which it could, since we
8515 # don't necessarily see children before parents), then $desc
8516 # isn't a good choice to display as a descendent tag of
8517 # $anc (since it is the descendent of another tag which is
8518 # a descendent of $anc). Similarly, $anc isn't a good choice to
8519 # display as a ancestor tag of $desc.
8521 proc is_certain {desc anc} {
8522 global arcnos arcout arcstart arcend growing problems
8525 if {[llength $arcnos($anc)] == 1} {
8526 # tags on the same arc are certain
8527 if {$arcnos($desc) eq $arcnos($anc)} {
8530 if {![info exists arcout($anc)]} {
8531 # if $anc is partway along an arc, use the start of the arc instead
8532 set a [lindex $arcnos($anc) 0]
8533 set anc $arcstart($a)
8536 if {[llength $arcnos($desc)] > 1 || [info exists arcout($desc)]} {
8539 set a [lindex $arcnos($desc) 0]
8545 set anclist [list $x]
8549 for {set i 0} {$i < [llength $anclist] && ($nnh > 0 || $ngrowanc > 0)} {incr i} {
8550 set x [lindex $anclist $i]
8555 foreach a $arcout($x) {
8556 if {[info exists growing($a)]} {
8557 if {![info exists growanc($x)] && $dl($x)} {
8563 if {[info exists dl($y)]} {
8567 if {![info exists done($y)]} {
8570 if {[info exists growanc($x)]} {
8574 for {set k 0} {$k < [llength $xl]} {incr k} {
8575 set z [lindex $xl $k]
8576 foreach c $arcout($z) {
8577 if {[info exists arcend($c)]} {
8579 if {[info exists dl($v)] && $dl($v)} {
8581 if {![info exists done($v)]} {
8584 if {[info exists growanc($v)]} {
8594 } elseif {$y eq $anc || !$dl($x)} {
8605 foreach x [array names growanc] {
8614 proc validate_arctags {a} {
8615 global arctags idtags
8619 foreach id $arctags($a) {
8621 if {![info exists idtags($id)]} {
8622 set na [lreplace $na $i $i]
8629 proc validate_archeads {a} {
8630 global archeads idheads
8633 set na $archeads($a)
8634 foreach id $archeads($a) {
8636 if {![info exists idheads($id)]} {
8637 set na [lreplace $na $i $i]
8641 set archeads($a) $na
8644 # Return the list of IDs that have tags that are descendents of id,
8645 # ignoring IDs that are descendents of IDs already reported.
8646 proc desctags {id} {
8647 global arcnos arcstart arcids arctags idtags allparents
8648 global growing cached_dtags
8650 if {![info exists allparents($id)]} {
8653 set t1 [clock clicks -milliseconds]
8655 if {[llength $arcnos($id)] == 1 && [llength $allparents($id)] == 1} {
8656 # part-way along an arc; check that arc first
8657 set a [lindex $arcnos($id) 0]
8658 if {$arctags($a) ne {}} {
8660 set i [lsearch -exact $arcids($a) $id]
8662 foreach t $arctags($a) {
8663 set j [lsearch -exact $arcids($a) $t]
8671 set id $arcstart($a)
8672 if {[info exists idtags($id)]} {
8676 if {[info exists cached_dtags($id)]} {
8677 return $cached_dtags($id)
8684 for {set i 0} {$i < [llength $todo] && $nc > 0} {incr i} {
8685 set id [lindex $todo $i]
8687 set ta [info exists hastaggedancestor($id)]
8691 # ignore tags on starting node
8692 if {!$ta && $i > 0} {
8693 if {[info exists idtags($id)]} {
8696 } elseif {[info exists cached_dtags($id)]} {
8697 set tagloc($id) $cached_dtags($id)
8701 foreach a $arcnos($id) {
8703 if {!$ta && $arctags($a) ne {}} {
8705 if {$arctags($a) ne {}} {
8706 lappend tagloc($id) [lindex $arctags($a) end]
8709 if {$ta || $arctags($a) ne {}} {
8710 set tomark [list $d]
8711 for {set j 0} {$j < [llength $tomark]} {incr j} {
8712 set dd [lindex $tomark $j]
8713 if {![info exists hastaggedancestor($dd)]} {
8714 if {[info exists done($dd)]} {
8715 foreach b $arcnos($dd) {
8716 lappend tomark $arcstart($b)
8718 if {[info exists tagloc($dd)]} {
8721 } elseif {[info exists queued($dd)]} {
8724 set hastaggedancestor($dd) 1
8728 if {![info exists queued($d)]} {
8731 if {![info exists hastaggedancestor($d)]} {
8738 foreach id [array names tagloc] {
8739 if {![info exists hastaggedancestor($id)]} {
8740 foreach t $tagloc($id) {
8741 if {[lsearch -exact $tags $t] < 0} {
8747 set t2 [clock clicks -milliseconds]
8750 # remove tags that are descendents of other tags
8751 for {set i 0} {$i < [llength $tags]} {incr i} {
8752 set a [lindex $tags $i]
8753 for {set j 0} {$j < $i} {incr j} {
8754 set b [lindex $tags $j]
8755 set r [anc_or_desc $a $b]
8757 set tags [lreplace $tags $j $j]
8760 } elseif {$r == -1} {
8761 set tags [lreplace $tags $i $i]
8768 if {[array names growing] ne {}} {
8769 # graph isn't finished, need to check if any tag could get
8770 # eclipsed by another tag coming later. Simply ignore any
8771 # tags that could later get eclipsed.
8774 if {[is_certain $t $origid]} {
8778 if {$tags eq $ctags} {
8779 set cached_dtags($origid) $tags
8784 set cached_dtags($origid) $tags
8786 set t3 [clock clicks -milliseconds]
8787 if {0 && $t3 - $t1 >= 100} {
8788 puts "iterating descendents ($loopix/[llength $todo] nodes) took\
8789 [expr {$t2-$t1}]+[expr {$t3-$t2}]ms, $nc candidates left"
8795 global arcnos arcids arcout arcend arctags idtags allparents
8796 global growing cached_atags
8798 if {![info exists allparents($id)]} {
8801 set t1 [clock clicks -milliseconds]
8803 if {[llength $arcnos($id)] == 1 && [llength $allparents($id)] == 1} {
8804 # part-way along an arc; check that arc first
8805 set a [lindex $arcnos($id) 0]
8806 if {$arctags($a) ne {}} {
8808 set i [lsearch -exact $arcids($a) $id]
8809 foreach t $arctags($a) {
8810 set j [lsearch -exact $arcids($a) $t]
8816 if {![info exists arcend($a)]} {
8820 if {[info exists idtags($id)]} {
8824 if {[info exists cached_atags($id)]} {
8825 return $cached_atags($id)
8833 for {set i 0} {$i < [llength $todo] && $nc > 0} {incr i} {
8834 set id [lindex $todo $i]
8836 set td [info exists hastaggeddescendent($id)]
8840 # ignore tags on starting node
8841 if {!$td && $i > 0} {
8842 if {[info exists idtags($id)]} {
8845 } elseif {[info exists cached_atags($id)]} {
8846 set tagloc($id) $cached_atags($id)
8850 foreach a $arcout($id) {
8851 if {!$td && $arctags($a) ne {}} {
8853 if {$arctags($a) ne {}} {
8854 lappend tagloc($id) [lindex $arctags($a) 0]
8857 if {![info exists arcend($a)]} continue
8859 if {$td || $arctags($a) ne {}} {
8860 set tomark [list $d]
8861 for {set j 0} {$j < [llength $tomark]} {incr j} {
8862 set dd [lindex $tomark $j]
8863 if {![info exists hastaggeddescendent($dd)]} {
8864 if {[info exists done($dd)]} {
8865 foreach b $arcout($dd) {
8866 if {[info exists arcend($b)]} {
8867 lappend tomark $arcend($b)
8870 if {[info exists tagloc($dd)]} {
8873 } elseif {[info exists queued($dd)]} {
8876 set hastaggeddescendent($dd) 1
8880 if {![info exists queued($d)]} {
8883 if {![info exists hastaggeddescendent($d)]} {
8889 set t2 [clock clicks -milliseconds]
8892 foreach id [array names tagloc] {
8893 if {![info exists hastaggeddescendent($id)]} {
8894 foreach t $tagloc($id) {
8895 if {[lsearch -exact $tags $t] < 0} {
8902 # remove tags that are ancestors of other tags
8903 for {set i 0} {$i < [llength $tags]} {incr i} {
8904 set a [lindex $tags $i]
8905 for {set j 0} {$j < $i} {incr j} {
8906 set b [lindex $tags $j]
8907 set r [anc_or_desc $a $b]
8909 set tags [lreplace $tags $j $j]
8912 } elseif {$r == 1} {
8913 set tags [lreplace $tags $i $i]
8920 if {[array names growing] ne {}} {
8921 # graph isn't finished, need to check if any tag could get
8922 # eclipsed by another tag coming later. Simply ignore any
8923 # tags that could later get eclipsed.
8926 if {[is_certain $origid $t]} {
8930 if {$tags eq $ctags} {
8931 set cached_atags($origid) $tags
8936 set cached_atags($origid) $tags
8938 set t3 [clock clicks -milliseconds]
8939 if {0 && $t3 - $t1 >= 100} {
8940 puts "iterating ancestors ($loopix/[llength $todo] nodes) took\
8941 [expr {$t2-$t1}]+[expr {$t3-$t2}]ms, $nc candidates left"
8946 # Return the list of IDs that have heads that are descendents of id,
8947 # including id itself if it has a head.
8948 proc descheads {id} {
8949 global arcnos arcstart arcids archeads idheads cached_dheads
8952 if {![info exists allparents($id)]} {
8956 if {[llength $arcnos($id)] == 1 && [llength $allparents($id)] == 1} {
8957 # part-way along an arc; check it first
8958 set a [lindex $arcnos($id) 0]
8959 if {$archeads($a) ne {}} {
8960 validate_archeads $a
8961 set i [lsearch -exact $arcids($a) $id]
8962 foreach t $archeads($a) {
8963 set j [lsearch -exact $arcids($a) $t]
8968 set id $arcstart($a)
8974 for {set i 0} {$i < [llength $todo]} {incr i} {
8975 set id [lindex $todo $i]
8976 if {[info exists cached_dheads($id)]} {
8977 set ret [concat $ret $cached_dheads($id)]
8979 if {[info exists idheads($id)]} {
8982 foreach a $arcnos($id) {
8983 if {$archeads($a) ne {}} {
8984 validate_archeads $a
8985 if {$archeads($a) ne {}} {
8986 set ret [concat $ret $archeads($a)]
8990 if {![info exists seen($d)]} {
8997 set ret [lsort -unique $ret]
8998 set cached_dheads($origid) $ret
8999 return [concat $ret $aret]
9002 proc addedtag {id} {
9003 global arcnos arcout cached_dtags cached_atags
9005 if {![info exists arcnos($id)]} return
9006 if {![info exists arcout($id)]} {
9007 recalcarc [lindex $arcnos($id) 0]
9009 catch {unset cached_dtags}
9010 catch {unset cached_atags}
9013 proc addedhead {hid head} {
9014 global arcnos arcout cached_dheads
9016 if {![info exists arcnos($hid)]} return
9017 if {![info exists arcout($hid)]} {
9018 recalcarc [lindex $arcnos($hid) 0]
9020 catch {unset cached_dheads}
9023 proc removedhead {hid head} {
9024 global cached_dheads
9026 catch {unset cached_dheads}
9029 proc movedhead {hid head} {
9030 global arcnos arcout cached_dheads
9032 if {![info exists arcnos($hid)]} return
9033 if {![info exists arcout($hid)]} {
9034 recalcarc [lindex $arcnos($hid) 0]
9036 catch {unset cached_dheads}
9039 proc changedrefs {} {
9040 global cached_dheads cached_dtags cached_atags
9041 global arctags archeads arcnos arcout idheads idtags
9043 foreach id [concat [array names idheads] [array names idtags]] {
9044 if {[info exists arcnos($id)] && ![info exists arcout($id)]} {
9045 set a [lindex $arcnos($id) 0]
9046 if {![info exists donearc($a)]} {
9052 catch {unset cached_dtags}
9053 catch {unset cached_atags}
9054 catch {unset cached_dheads}
9057 proc rereadrefs {} {
9058 global idtags idheads idotherrefs mainheadid
9060 set refids [concat [array names idtags] \
9061 [array names idheads] [array names idotherrefs]]
9062 foreach id $refids {
9063 if {![info exists ref($id)]} {
9064 set ref($id) [listrefs $id]
9067 set oldmainhead $mainheadid
9070 set refids [lsort -unique [concat $refids [array names idtags] \
9071 [array names idheads] [array names idotherrefs]]]
9072 foreach id $refids {
9073 set v [listrefs $id]
9074 if {![info exists ref($id)] || $ref($id) != $v} {
9078 if {$oldmainhead ne $mainheadid} {
9079 redrawtags $oldmainhead
9080 redrawtags $mainheadid
9085 proc listrefs {id} {
9086 global idtags idheads idotherrefs
9089 if {[info exists idtags($id)]} {
9093 if {[info exists idheads($id)]} {
9097 if {[info exists idotherrefs($id)]} {
9098 set z $idotherrefs($id)
9100 return [list $x $y $z]
9103 proc showtag {tag isnew} {
9104 global ctext tagcontents tagids linknum tagobjid
9107 addtohistory [list showtag $tag 0]
9109 $ctext conf -state normal
9113 if {![info exists tagcontents($tag)]} {
9115 set tagcontents($tag) [exec git cat-file tag $tagobjid($tag)]
9118 if {[info exists tagcontents($tag)]} {
9119 set text $tagcontents($tag)
9121 set text "[mc "Tag"]: $tag\n[mc "Id"]: $tagids($tag)"
9123 appendwithlinks $text {}
9124 $ctext conf -state disabled
9136 if {[info exists gitktmpdir]} {
9137 catch {file delete -force $gitktmpdir}
9141 proc mkfontdisp {font top which} {
9142 global fontattr fontpref $font
9144 set fontpref($font) [set $font]
9145 button $top.${font}but -text $which -font optionfont \
9146 -command [list choosefont $font $which]
9147 label $top.$font -relief flat -font $font \
9148 -text $fontattr($font,family) -justify left
9149 grid x $top.${font}but $top.$font -sticky w
9152 proc choosefont {font which} {
9153 global fontparam fontlist fonttop fontattr
9155 set fontparam(which) $which
9156 set fontparam(font) $font
9157 set fontparam(family) [font actual $font -family]
9158 set fontparam(size) $fontattr($font,size)
9159 set fontparam(weight) $fontattr($font,weight)
9160 set fontparam(slant) $fontattr($font,slant)
9163 if {![winfo exists $top]} {
9165 eval font config sample [font actual $font]
9167 wm title $top [mc "Gitk font chooser"]
9168 label $top.l -textvariable fontparam(which)
9169 pack $top.l -side top
9170 set fontlist [lsort [font families]]
9172 listbox $top.f.fam -listvariable fontlist \
9173 -yscrollcommand [list $top.f.sb set]
9174 bind $top.f.fam <<ListboxSelect>> selfontfam
9175 scrollbar $top.f.sb -command [list $top.f.fam yview]
9176 pack $top.f.sb -side right -fill y
9177 pack $top.f.fam -side left -fill both -expand 1
9178 pack $top.f -side top -fill both -expand 1
9180 spinbox $top.g.size -from 4 -to 40 -width 4 \
9181 -textvariable fontparam(size) \
9182 -validatecommand {string is integer -strict %s}
9183 checkbutton $top.g.bold -padx 5 \
9184 -font {{Times New Roman} 12 bold} -text [mc "B"] -indicatoron 0 \
9185 -variable fontparam(weight) -onvalue bold -offvalue normal
9186 checkbutton $top.g.ital -padx 5 \
9187 -font {{Times New Roman} 12 italic} -text [mc "I"] -indicatoron 0 \
9188 -variable fontparam(slant) -onvalue italic -offvalue roman
9189 pack $top.g.size $top.g.bold $top.g.ital -side left
9190 pack $top.g -side top
9191 canvas $top.c -width 150 -height 50 -border 2 -relief sunk \
9193 $top.c create text 100 25 -anchor center -text $which -font sample \
9194 -fill black -tags text
9195 bind $top.c <Configure> [list centertext $top.c]
9196 pack $top.c -side top -fill x
9198 button $top.buts.ok -text [mc "OK"] -command fontok -default active
9199 button $top.buts.can -text [mc "Cancel"] -command fontcan -default normal
9200 grid $top.buts.ok $top.buts.can
9201 grid columnconfigure $top.buts 0 -weight 1 -uniform a
9202 grid columnconfigure $top.buts 1 -weight 1 -uniform a
9203 pack $top.buts -side bottom -fill x
9204 trace add variable fontparam write chg_fontparam
9207 $top.c itemconf text -text $which
9209 set i [lsearch -exact $fontlist $fontparam(family)]
9211 $top.f.fam selection set $i
9216 proc centertext {w} {
9217 $w coords text [expr {[winfo width $w] / 2}] [expr {[winfo height $w] / 2}]
9221 global fontparam fontpref prefstop
9223 set f $fontparam(font)
9224 set fontpref($f) [list $fontparam(family) $fontparam(size)]
9225 if {$fontparam(weight) eq "bold"} {
9226 lappend fontpref($f) "bold"
9228 if {$fontparam(slant) eq "italic"} {
9229 lappend fontpref($f) "italic"
9232 $w conf -text $fontparam(family) -font $fontpref($f)
9238 global fonttop fontparam
9240 if {[info exists fonttop]} {
9241 catch {destroy $fonttop}
9242 catch {font delete sample}
9248 proc selfontfam {} {
9249 global fonttop fontparam
9251 set i [$fonttop.f.fam curselection]
9253 set fontparam(family) [$fonttop.f.fam get $i]
9257 proc chg_fontparam {v sub op} {
9260 font config sample -$sub $fontparam($sub)
9264 global maxwidth maxgraphpct
9265 global oldprefs prefstop showneartags showlocalchanges
9266 global bgcolor fgcolor ctext diffcolors selectbgcolor
9267 global tabstop limitdiffs autoselect extdifftool
9271 if {[winfo exists $top]} {
9275 foreach v {maxwidth maxgraphpct showneartags showlocalchanges \
9276 limitdiffs tabstop} {
9277 set oldprefs($v) [set $v]
9280 wm title $top [mc "Gitk preferences"]
9281 label $top.ldisp -text [mc "Commit list display options"]
9282 grid $top.ldisp - -sticky w -pady 10
9283 label $top.spacer -text " "
9284 label $top.maxwidthl -text [mc "Maximum graph width (lines)"] \
9286 spinbox $top.maxwidth -from 0 -to 100 -width 4 -textvariable maxwidth
9287 grid $top.spacer $top.maxwidthl $top.maxwidth -sticky w
9288 label $top.maxpctl -text [mc "Maximum graph width (% of pane)"] \
9290 spinbox $top.maxpct -from 1 -to 100 -width 4 -textvariable maxgraphpct
9291 grid x $top.maxpctl $top.maxpct -sticky w
9292 frame $top.showlocal
9293 label $top.showlocal.l -text [mc "Show local changes"] -font optionfont
9294 checkbutton $top.showlocal.b -variable showlocalchanges
9295 pack $top.showlocal.b $top.showlocal.l -side left
9296 grid x $top.showlocal -sticky w
9297 frame $top.autoselect
9298 label $top.autoselect.l -text [mc "Auto-select SHA1"] -font optionfont
9299 checkbutton $top.autoselect.b -variable autoselect
9300 pack $top.autoselect.b $top.autoselect.l -side left
9301 grid x $top.autoselect -sticky w
9303 label $top.ddisp -text [mc "Diff display options"]
9304 grid $top.ddisp - -sticky w -pady 10
9305 label $top.tabstopl -text [mc "Tab spacing"] -font optionfont
9306 spinbox $top.tabstop -from 1 -to 20 -width 4 -textvariable tabstop
9307 grid x $top.tabstopl $top.tabstop -sticky w
9309 label $top.ntag.l -text [mc "Display nearby tags"] -font optionfont
9310 checkbutton $top.ntag.b -variable showneartags
9311 pack $top.ntag.b $top.ntag.l -side left
9312 grid x $top.ntag -sticky w
9314 label $top.ldiff.l -text [mc "Limit diffs to listed paths"] -font optionfont
9315 checkbutton $top.ldiff.b -variable limitdiffs
9316 pack $top.ldiff.b $top.ldiff.l -side left
9317 grid x $top.ldiff -sticky w
9319 entry $top.extdifft -textvariable extdifftool
9321 label $top.extdifff.l -text [mc "External diff tool" ] -font optionfont \
9323 button $top.extdifff.b -text [mc "Choose..."] -font optionfont \
9324 -command choose_extdiff
9325 pack $top.extdifff.l $top.extdifff.b -side left
9326 grid x $top.extdifff $top.extdifft -sticky w
9328 label $top.cdisp -text [mc "Colors: press to choose"]
9329 grid $top.cdisp - -sticky w -pady 10
9330 label $top.bg -padx 40 -relief sunk -background $bgcolor
9331 button $top.bgbut -text [mc "Background"] -font optionfont \
9332 -command [list choosecolor bgcolor {} $top.bg background setbg]
9333 grid x $top.bgbut $top.bg -sticky w
9334 label $top.fg -padx 40 -relief sunk -background $fgcolor
9335 button $top.fgbut -text [mc "Foreground"] -font optionfont \
9336 -command [list choosecolor fgcolor {} $top.fg foreground setfg]
9337 grid x $top.fgbut $top.fg -sticky w
9338 label $top.diffold -padx 40 -relief sunk -background [lindex $diffcolors 0]
9339 button $top.diffoldbut -text [mc "Diff: old lines"] -font optionfont \
9340 -command [list choosecolor diffcolors 0 $top.diffold "diff old lines" \
9341 [list $ctext tag conf d0 -foreground]]
9342 grid x $top.diffoldbut $top.diffold -sticky w
9343 label $top.diffnew -padx 40 -relief sunk -background [lindex $diffcolors 1]
9344 button $top.diffnewbut -text [mc "Diff: new lines"] -font optionfont \
9345 -command [list choosecolor diffcolors 1 $top.diffnew "diff new lines" \
9346 [list $ctext tag conf d1 -foreground]]
9347 grid x $top.diffnewbut $top.diffnew -sticky w
9348 label $top.hunksep -padx 40 -relief sunk -background [lindex $diffcolors 2]
9349 button $top.hunksepbut -text [mc "Diff: hunk header"] -font optionfont \
9350 -command [list choosecolor diffcolors 2 $top.hunksep \
9351 "diff hunk header" \
9352 [list $ctext tag conf hunksep -foreground]]
9353 grid x $top.hunksepbut $top.hunksep -sticky w
9354 label $top.selbgsep -padx 40 -relief sunk -background $selectbgcolor
9355 button $top.selbgbut -text [mc "Select bg"] -font optionfont \
9356 -command [list choosecolor selectbgcolor {} $top.selbgsep background setselbg]
9357 grid x $top.selbgbut $top.selbgsep -sticky w
9359 label $top.cfont -text [mc "Fonts: press to choose"]
9360 grid $top.cfont - -sticky w -pady 10
9361 mkfontdisp mainfont $top [mc "Main font"]
9362 mkfontdisp textfont $top [mc "Diff display font"]
9363 mkfontdisp uifont $top [mc "User interface font"]
9366 button $top.buts.ok -text [mc "OK"] -command prefsok -default active
9367 button $top.buts.can -text [mc "Cancel"] -command prefscan -default normal
9368 grid $top.buts.ok $top.buts.can
9369 grid columnconfigure $top.buts 0 -weight 1 -uniform a
9370 grid columnconfigure $top.buts 1 -weight 1 -uniform a
9371 grid $top.buts - - -pady 10 -sticky ew
9372 bind $top <Visibility> "focus $top.buts.ok"
9375 proc choose_extdiff {} {
9378 set prog [tk_getOpenFile -title "External diff tool" -multiple false]
9380 set extdifftool $prog
9384 proc choosecolor {v vi w x cmd} {
9387 set c [tk_chooseColor -initialcolor [lindex [set $v] $vi] \
9388 -title [mc "Gitk: choose color for %s" $x]]
9389 if {$c eq {}} return
9390 $w conf -background $c
9396 global bglist cflist
9398 $w configure -selectbackground $c
9400 $cflist tag configure highlight \
9401 -background [$cflist cget -selectbackground]
9402 allcanvs itemconf secsel -fill $c
9409 $w conf -background $c
9417 $w conf -foreground $c
9419 allcanvs itemconf text -fill $c
9420 $canv itemconf circle -outline $c
9424 global oldprefs prefstop
9426 foreach v {maxwidth maxgraphpct showneartags showlocalchanges \
9427 limitdiffs tabstop} {
9429 set $v $oldprefs($v)
9431 catch {destroy $prefstop}
9437 global maxwidth maxgraphpct
9438 global oldprefs prefstop showneartags showlocalchanges
9439 global fontpref mainfont textfont uifont
9440 global limitdiffs treediffs
9442 catch {destroy $prefstop}
9446 if {$mainfont ne $fontpref(mainfont)} {
9447 set mainfont $fontpref(mainfont)
9448 parsefont mainfont $mainfont
9449 eval font configure mainfont [fontflags mainfont]
9450 eval font configure mainfontbold [fontflags mainfont 1]
9454 if {$textfont ne $fontpref(textfont)} {
9455 set textfont $fontpref(textfont)
9456 parsefont textfont $textfont
9457 eval font configure textfont [fontflags textfont]
9458 eval font configure textfontbold [fontflags textfont 1]
9460 if {$uifont ne $fontpref(uifont)} {
9461 set uifont $fontpref(uifont)
9462 parsefont uifont $uifont
9463 eval font configure uifont [fontflags uifont]
9466 if {$showlocalchanges != $oldprefs(showlocalchanges)} {
9467 if {$showlocalchanges} {
9473 if {$limitdiffs != $oldprefs(limitdiffs)} {
9474 # treediffs elements are limited by path
9475 catch {unset treediffs}
9477 if {$fontchanged || $maxwidth != $oldprefs(maxwidth)
9478 || $maxgraphpct != $oldprefs(maxgraphpct)} {
9480 } elseif {$showneartags != $oldprefs(showneartags) ||
9481 $limitdiffs != $oldprefs(limitdiffs)} {
9486 proc formatdate {d} {
9487 global datetimeformat
9489 set d [clock format $d -format $datetimeformat]
9494 # This list of encoding names and aliases is distilled from
9495 # http://www.iana.org/assignments/character-sets.
9496 # Not all of them are supported by Tcl.
9497 set encoding_aliases {
9498 { ANSI_X3.4-1968 iso-ir-6 ANSI_X3.4-1986 ISO_646.irv:1991 ASCII
9499 ISO646-US US-ASCII us IBM367 cp367 csASCII }
9500 { ISO-10646-UTF-1 csISO10646UTF1 }
9501 { ISO_646.basic:1983 ref csISO646basic1983 }
9502 { INVARIANT csINVARIANT }
9503 { ISO_646.irv:1983 iso-ir-2 irv csISO2IntlRefVersion }
9504 { BS_4730 iso-ir-4 ISO646-GB gb uk csISO4UnitedKingdom }
9505 { NATS-SEFI iso-ir-8-1 csNATSSEFI }
9506 { NATS-SEFI-ADD iso-ir-8-2 csNATSSEFIADD }
9507 { NATS-DANO iso-ir-9-1 csNATSDANO }
9508 { NATS-DANO-ADD iso-ir-9-2 csNATSDANOADD }
9509 { SEN_850200_B iso-ir-10 FI ISO646-FI ISO646-SE se csISO10Swedish }
9510 { SEN_850200_C iso-ir-11 ISO646-SE2 se2 csISO11SwedishForNames }
9511 { KS_C_5601-1987 iso-ir-149 KS_C_5601-1989 KSC_5601 korean csKSC56011987 }
9512 { ISO-2022-KR csISO2022KR }
9514 { ISO-2022-JP csISO2022JP }
9515 { ISO-2022-JP-2 csISO2022JP2 }
9516 { JIS_C6220-1969-jp JIS_C6220-1969 iso-ir-13 katakana x0201-7
9518 { JIS_C6220-1969-ro iso-ir-14 jp ISO646-JP csISO14JISC6220ro }
9519 { IT iso-ir-15 ISO646-IT csISO15Italian }
9520 { PT iso-ir-16 ISO646-PT csISO16Portuguese }
9521 { ES iso-ir-17 ISO646-ES csISO17Spanish }
9522 { greek7-old iso-ir-18 csISO18Greek7Old }
9523 { latin-greek iso-ir-19 csISO19LatinGreek }
9524 { DIN_66003 iso-ir-21 de ISO646-DE csISO21German }
9525 { NF_Z_62-010_(1973) iso-ir-25 ISO646-FR1 csISO25French }
9526 { Latin-greek-1 iso-ir-27 csISO27LatinGreek1 }
9527 { ISO_5427 iso-ir-37 csISO5427Cyrillic }
9528 { JIS_C6226-1978 iso-ir-42 csISO42JISC62261978 }
9529 { BS_viewdata iso-ir-47 csISO47BSViewdata }
9530 { INIS iso-ir-49 csISO49INIS }
9531 { INIS-8 iso-ir-50 csISO50INIS8 }
9532 { INIS-cyrillic iso-ir-51 csISO51INISCyrillic }
9533 { ISO_5427:1981 iso-ir-54 ISO5427Cyrillic1981 }
9534 { ISO_5428:1980 iso-ir-55 csISO5428Greek }
9535 { GB_1988-80 iso-ir-57 cn ISO646-CN csISO57GB1988 }
9536 { GB_2312-80 iso-ir-58 chinese csISO58GB231280 }
9537 { NS_4551-1 iso-ir-60 ISO646-NO no csISO60DanishNorwegian
9539 { NS_4551-2 ISO646-NO2 iso-ir-61 no2 csISO61Norwegian2 }
9540 { NF_Z_62-010 iso-ir-69 ISO646-FR fr csISO69French }
9541 { videotex-suppl iso-ir-70 csISO70VideotexSupp1 }
9542 { PT2 iso-ir-84 ISO646-PT2 csISO84Portuguese2 }
9543 { ES2 iso-ir-85 ISO646-ES2 csISO85Spanish2 }
9544 { MSZ_7795.3 iso-ir-86 ISO646-HU hu csISO86Hungarian }
9545 { JIS_C6226-1983 iso-ir-87 x0208 JIS_X0208-1983 csISO87JISX0208 }
9546 { greek7 iso-ir-88 csISO88Greek7 }
9547 { ASMO_449 ISO_9036 arabic7 iso-ir-89 csISO89ASMO449 }
9548 { iso-ir-90 csISO90 }
9549 { JIS_C6229-1984-a iso-ir-91 jp-ocr-a csISO91JISC62291984a }
9550 { JIS_C6229-1984-b iso-ir-92 ISO646-JP-OCR-B jp-ocr-b
9551 csISO92JISC62991984b }
9552 { JIS_C6229-1984-b-add iso-ir-93 jp-ocr-b-add csISO93JIS62291984badd }
9553 { JIS_C6229-1984-hand iso-ir-94 jp-ocr-hand csISO94JIS62291984hand }
9554 { JIS_C6229-1984-hand-add iso-ir-95 jp-ocr-hand-add
9555 csISO95JIS62291984handadd }
9556 { JIS_C6229-1984-kana iso-ir-96 csISO96JISC62291984kana }
9557 { ISO_2033-1983 iso-ir-98 e13b csISO2033 }
9558 { ANSI_X3.110-1983 iso-ir-99 CSA_T500-1983 NAPLPS csISO99NAPLPS }
9559 { ISO_8859-1:1987 iso-ir-100 ISO_8859-1 ISO-8859-1 latin1 l1 IBM819
9561 { ISO_8859-2:1987 iso-ir-101 ISO_8859-2 ISO-8859-2 latin2 l2 csISOLatin2 }
9562 { T.61-7bit iso-ir-102 csISO102T617bit }
9563 { T.61-8bit T.61 iso-ir-103 csISO103T618bit }
9564 { ISO_8859-3:1988 iso-ir-109 ISO_8859-3 ISO-8859-3 latin3 l3 csISOLatin3 }
9565 { ISO_8859-4:1988 iso-ir-110 ISO_8859-4 ISO-8859-4 latin4 l4 csISOLatin4 }
9566 { ECMA-cyrillic iso-ir-111 KOI8-E csISO111ECMACyrillic }
9567 { CSA_Z243.4-1985-1 iso-ir-121 ISO646-CA csa7-1 ca csISO121Canadian1 }
9568 { CSA_Z243.4-1985-2 iso-ir-122 ISO646-CA2 csa7-2 csISO122Canadian2 }
9569 { CSA_Z243.4-1985-gr iso-ir-123 csISO123CSAZ24341985gr }
9570 { ISO_8859-6:1987 iso-ir-127 ISO_8859-6 ISO-8859-6 ECMA-114 ASMO-708
9571 arabic csISOLatinArabic }
9572 { ISO_8859-6-E csISO88596E ISO-8859-6-E }
9573 { ISO_8859-6-I csISO88596I ISO-8859-6-I }
9574 { ISO_8859-7:1987 iso-ir-126 ISO_8859-7 ISO-8859-7 ELOT_928 ECMA-118
9575 greek greek8 csISOLatinGreek }
9576 { T.101-G2 iso-ir-128 csISO128T101G2 }
9577 { ISO_8859-8:1988 iso-ir-138 ISO_8859-8 ISO-8859-8 hebrew
9579 { ISO_8859-8-E csISO88598E ISO-8859-8-E }
9580 { ISO_8859-8-I csISO88598I ISO-8859-8-I }
9581 { CSN_369103 iso-ir-139 csISO139CSN369103 }
9582 { JUS_I.B1.002 iso-ir-141 ISO646-YU js yu csISO141JUSIB1002 }
9583 { ISO_6937-2-add iso-ir-142 csISOTextComm }
9584 { IEC_P27-1 iso-ir-143 csISO143IECP271 }
9585 { ISO_8859-5:1988 iso-ir-144 ISO_8859-5 ISO-8859-5 cyrillic
9586 csISOLatinCyrillic }
9587 { JUS_I.B1.003-serb iso-ir-146 serbian csISO146Serbian }
9588 { JUS_I.B1.003-mac macedonian iso-ir-147 csISO147Macedonian }
9589 { ISO_8859-9:1989 iso-ir-148 ISO_8859-9 ISO-8859-9 latin5 l5 csISOLatin5 }
9590 { greek-ccitt iso-ir-150 csISO150 csISO150GreekCCITT }
9591 { NC_NC00-10:81 cuba iso-ir-151 ISO646-CU csISO151Cuba }
9592 { ISO_6937-2-25 iso-ir-152 csISO6937Add }
9593 { GOST_19768-74 ST_SEV_358-88 iso-ir-153 csISO153GOST1976874 }
9594 { ISO_8859-supp iso-ir-154 latin1-2-5 csISO8859Supp }
9595 { ISO_10367-box iso-ir-155 csISO10367Box }
9596 { ISO-8859-10 iso-ir-157 l6 ISO_8859-10:1992 csISOLatin6 latin6 }
9597 { latin-lap lap iso-ir-158 csISO158Lap }
9598 { JIS_X0212-1990 x0212 iso-ir-159 csISO159JISX02121990 }
9599 { DS_2089 DS2089 ISO646-DK dk csISO646Danish }
9602 { JIS_X0201 X0201 csHalfWidthKatakana }
9603 { KSC5636 ISO646-KR csKSC5636 }
9604 { ISO-10646-UCS-2 csUnicode }
9605 { ISO-10646-UCS-4 csUCS4 }
9606 { DEC-MCS dec csDECMCS }
9607 { hp-roman8 roman8 r8 csHPRoman8 }
9608 { macintosh mac csMacintosh }
9609 { IBM037 cp037 ebcdic-cp-us ebcdic-cp-ca ebcdic-cp-wt ebcdic-cp-nl
9611 { IBM038 EBCDIC-INT cp038 csIBM038 }
9612 { IBM273 CP273 csIBM273 }
9613 { IBM274 EBCDIC-BE CP274 csIBM274 }
9614 { IBM275 EBCDIC-BR cp275 csIBM275 }
9615 { IBM277 EBCDIC-CP-DK EBCDIC-CP-NO csIBM277 }
9616 { IBM278 CP278 ebcdic-cp-fi ebcdic-cp-se csIBM278 }
9617 { IBM280 CP280 ebcdic-cp-it csIBM280 }
9618 { IBM281 EBCDIC-JP-E cp281 csIBM281 }
9619 { IBM284 CP284 ebcdic-cp-es csIBM284 }
9620 { IBM285 CP285 ebcdic-cp-gb csIBM285 }
9621 { IBM290 cp290 EBCDIC-JP-kana csIBM290 }
9622 { IBM297 cp297 ebcdic-cp-fr csIBM297 }
9623 { IBM420 cp420 ebcdic-cp-ar1 csIBM420 }
9624 { IBM423 cp423 ebcdic-cp-gr csIBM423 }
9625 { IBM424 cp424 ebcdic-cp-he csIBM424 }
9626 { IBM437 cp437 437 csPC8CodePage437 }
9627 { IBM500 CP500 ebcdic-cp-be ebcdic-cp-ch csIBM500 }
9628 { IBM775 cp775 csPC775Baltic }
9629 { IBM850 cp850 850 csPC850Multilingual }
9630 { IBM851 cp851 851 csIBM851 }
9631 { IBM852 cp852 852 csPCp852 }
9632 { IBM855 cp855 855 csIBM855 }
9633 { IBM857 cp857 857 csIBM857 }
9634 { IBM860 cp860 860 csIBM860 }
9635 { IBM861 cp861 861 cp-is csIBM861 }
9636 { IBM862 cp862 862 csPC862LatinHebrew }
9637 { IBM863 cp863 863 csIBM863 }
9638 { IBM864 cp864 csIBM864 }
9639 { IBM865 cp865 865 csIBM865 }
9640 { IBM866 cp866 866 csIBM866 }
9641 { IBM868 CP868 cp-ar csIBM868 }
9642 { IBM869 cp869 869 cp-gr csIBM869 }
9643 { IBM870 CP870 ebcdic-cp-roece ebcdic-cp-yu csIBM870 }
9644 { IBM871 CP871 ebcdic-cp-is csIBM871 }
9645 { IBM880 cp880 EBCDIC-Cyrillic csIBM880 }
9646 { IBM891 cp891 csIBM891 }
9647 { IBM903 cp903 csIBM903 }
9648 { IBM904 cp904 904 csIBBM904 }
9649 { IBM905 CP905 ebcdic-cp-tr csIBM905 }
9650 { IBM918 CP918 ebcdic-cp-ar2 csIBM918 }
9651 { IBM1026 CP1026 csIBM1026 }
9652 { EBCDIC-AT-DE csIBMEBCDICATDE }
9653 { EBCDIC-AT-DE-A csEBCDICATDEA }
9654 { EBCDIC-CA-FR csEBCDICCAFR }
9655 { EBCDIC-DK-NO csEBCDICDKNO }
9656 { EBCDIC-DK-NO-A csEBCDICDKNOA }
9657 { EBCDIC-FI-SE csEBCDICFISE }
9658 { EBCDIC-FI-SE-A csEBCDICFISEA }
9659 { EBCDIC-FR csEBCDICFR }
9660 { EBCDIC-IT csEBCDICIT }
9661 { EBCDIC-PT csEBCDICPT }
9662 { EBCDIC-ES csEBCDICES }
9663 { EBCDIC-ES-A csEBCDICESA }
9664 { EBCDIC-ES-S csEBCDICESS }
9665 { EBCDIC-UK csEBCDICUK }
9666 { EBCDIC-US csEBCDICUS }
9667 { UNKNOWN-8BIT csUnknown8BiT }
9668 { MNEMONIC csMnemonic }
9673 { IBM00858 CCSID00858 CP00858 PC-Multilingual-850+euro }
9674 { IBM00924 CCSID00924 CP00924 ebcdic-Latin9--euro }
9675 { IBM01140 CCSID01140 CP01140 ebcdic-us-37+euro }
9676 { IBM01141 CCSID01141 CP01141 ebcdic-de-273+euro }
9677 { IBM01142 CCSID01142 CP01142 ebcdic-dk-277+euro ebcdic-no-277+euro }
9678 { IBM01143 CCSID01143 CP01143 ebcdic-fi-278+euro ebcdic-se-278+euro }
9679 { IBM01144 CCSID01144 CP01144 ebcdic-it-280+euro }
9680 { IBM01145 CCSID01145 CP01145 ebcdic-es-284+euro }
9681 { IBM01146 CCSID01146 CP01146 ebcdic-gb-285+euro }
9682 { IBM01147 CCSID01147 CP01147 ebcdic-fr-297+euro }
9683 { IBM01148 CCSID01148 CP01148 ebcdic-international-500+euro }
9684 { IBM01149 CCSID01149 CP01149 ebcdic-is-871+euro }
9685 { IBM1047 IBM-1047 }
9686 { PTCP154 csPTCP154 PT154 CP154 Cyrillic-Asian }
9687 { Amiga-1251 Ami1251 Amiga1251 Ami-1251 }
9688 { UNICODE-1-1 csUnicode11 }
9691 { UNICODE-1-1-UTF-7 csUnicode11UTF7 }
9692 { ISO-8859-14 iso-ir-199 ISO_8859-14:1998 ISO_8859-14 latin8 iso-celtic
9694 { ISO-8859-15 ISO_8859-15 Latin-9 }
9695 { ISO-8859-16 iso-ir-226 ISO_8859-16:2001 ISO_8859-16 latin10 l10 }
9696 { GBK CP936 MS936 windows-936 }
9697 { JIS_Encoding csJISEncoding }
9698 { Shift_JIS MS_Kanji csShiftJIS }
9699 { Extended_UNIX_Code_Packed_Format_for_Japanese csEUCPkdFmtJapanese
9701 { Extended_UNIX_Code_Fixed_Width_for_Japanese csEUCFixWidJapanese }
9702 { ISO-10646-UCS-Basic csUnicodeASCII }
9703 { ISO-10646-Unicode-Latin1 csUnicodeLatin1 ISO-10646 }
9704 { ISO-Unicode-IBM-1261 csUnicodeIBM1261 }
9705 { ISO-Unicode-IBM-1268 csUnicodeIBM1268 }
9706 { ISO-Unicode-IBM-1276 csUnicodeIBM1276 }
9707 { ISO-Unicode-IBM-1264 csUnicodeIBM1264 }
9708 { ISO-Unicode-IBM-1265 csUnicodeIBM1265 }
9709 { ISO-8859-1-Windows-3.0-Latin-1 csWindows30Latin1 }
9710 { ISO-8859-1-Windows-3.1-Latin-1 csWindows31Latin1 }
9711 { ISO-8859-2-Windows-Latin-2 csWindows31Latin2 }
9712 { ISO-8859-9-Windows-Latin-5 csWindows31Latin5 }
9713 { Adobe-Standard-Encoding csAdobeStandardEncoding }
9714 { Ventura-US csVenturaUS }
9715 { Ventura-International csVenturaInternational }
9716 { PC8-Danish-Norwegian csPC8DanishNorwegian }
9717 { PC8-Turkish csPC8Turkish }
9718 { IBM-Symbols csIBMSymbols }
9719 { IBM-Thai csIBMThai }
9720 { HP-Legal csHPLegal }
9721 { HP-Pi-font csHPPiFont }
9722 { HP-Math8 csHPMath8 }
9723 { Adobe-Symbol-Encoding csHPPSMath }
9724 { HP-DeskTop csHPDesktop }
9725 { Ventura-Math csVenturaMath }
9726 { Microsoft-Publishing csMicrosoftPublishing }
9727 { Windows-31J csWindows31J }
9732 proc tcl_encoding {enc} {
9733 global encoding_aliases
9734 set names [encoding names]
9735 set lcnames [string tolower $names]
9736 set enc [string tolower $enc]
9737 set i [lsearch -exact $lcnames $enc]
9739 # look for "isonnn" instead of "iso-nnn" or "iso_nnn"
9740 if {[regsub {^iso[-_]} $enc iso encx]} {
9741 set i [lsearch -exact $lcnames $encx]
9745 foreach l $encoding_aliases {
9746 set ll [string tolower $l]
9747 if {[lsearch -exact $ll $enc] < 0} continue
9748 # look through the aliases for one that tcl knows about
9750 set i [lsearch -exact $lcnames $e]
9752 if {[regsub {^iso[-_]} $e iso ex]} {
9753 set i [lsearch -exact $lcnames $ex]
9762 return [lindex $names $i]
9767 # First check that Tcl/Tk is recent enough
9768 if {[catch {package require Tk 8.4} err]} {
9769 show_error {} . [mc "Sorry, gitk cannot run with this version of Tcl/Tk.\n\
9770 Gitk requires at least Tcl/Tk 8.4."]
9775 set wrcomcmd "git diff-tree --stdin -p --pretty"
9779 set gitencoding [exec git config --get i18n.commitencoding]
9781 if {$gitencoding == ""} {
9782 set gitencoding "utf-8"
9784 set tclencoding [tcl_encoding $gitencoding]
9785 if {$tclencoding == {}} {
9786 puts stderr "Warning: encoding $gitencoding is not supported by Tcl/Tk"
9789 set mainfont {Helvetica 9}
9790 set textfont {Courier 9}
9791 set uifont {Helvetica 9 bold}
9793 set findmergefiles 0
9801 set cmitmode "patch"
9802 set wrapcomment "none"
9806 set showlocalchanges 1
9808 set datetimeformat "%Y-%m-%d %H:%M:%S"
9811 set extdifftool "meld"
9813 set colors {green red blue magenta darkgrey brown orange}
9816 set diffcolors {red "#00a000" blue}
9819 set selectbgcolor gray85
9821 set circlecolors {white blue gray blue blue}
9823 ## For msgcat loading, first locate the installation location.
9824 if { [info exists ::env(GITK_MSGSDIR)] } {
9825 ## Msgsdir was manually set in the environment.
9826 set gitk_msgsdir $::env(GITK_MSGSDIR)
9828 ## Let's guess the prefix from argv0.
9829 set gitk_prefix [file dirname [file dirname [file normalize $argv0]]]
9830 set gitk_libdir [file join $gitk_prefix share gitk lib]
9831 set gitk_msgsdir [file join $gitk_libdir msgs]
9835 ## Internationalization (i18n) through msgcat and gettext. See
9836 ## http://www.gnu.org/software/gettext/manual/html_node/Tcl.html
9837 package require msgcat
9838 namespace import ::msgcat::mc
9839 ## And eventually load the actual message catalog
9840 ::msgcat::mcload $gitk_msgsdir
9842 catch {source ~/.gitk}
9844 font create optionfont -family sans-serif -size -12
9846 parsefont mainfont $mainfont
9847 eval font create mainfont [fontflags mainfont]
9848 eval font create mainfontbold [fontflags mainfont 1]
9850 parsefont textfont $textfont
9851 eval font create textfont [fontflags textfont]
9852 eval font create textfontbold [fontflags textfont 1]
9854 parsefont uifont $uifont
9855 eval font create uifont [fontflags uifont]
9859 # check that we can find a .git directory somewhere...
9860 if {[catch {set gitdir [gitdir]}]} {
9861 show_error {} . [mc "Cannot find a git repository here."]
9864 if {![file isdirectory $gitdir]} {
9865 show_error {} . [mc "Cannot find the git directory \"%s\"." $gitdir]
9870 set cmdline_files {}
9872 set revtreeargscmd {}
9874 switch -glob -- $arg {
9877 set cmdline_files [lrange $argv [expr {$i + 1}] end]
9881 set revtreeargscmd [string range $arg 10 end]
9884 lappend revtreeargs $arg
9890 if {$i >= [llength $argv] && $revtreeargs ne {}} {
9891 # no -- on command line, but some arguments (other than --argscmd)
9893 set f [eval exec git rev-parse --no-revs --no-flags $revtreeargs]
9894 set cmdline_files [split $f "\n"]
9895 set n [llength $cmdline_files]
9896 set revtreeargs [lrange $revtreeargs 0 end-$n]
9897 # Unfortunately git rev-parse doesn't produce an error when
9898 # something is both a revision and a filename. To be consistent
9899 # with git log and git rev-list, check revtreeargs for filenames.
9900 foreach arg $revtreeargs {
9901 if {[file exists $arg]} {
9902 show_error {} . [mc "Ambiguous argument '%s': both revision\
9908 # unfortunately we get both stdout and stderr in $err,
9909 # so look for "fatal:".
9910 set i [string first "fatal:" $err]
9912 set err [string range $err [expr {$i + 6}] end]
9914 show_error {} . "[mc "Bad arguments to gitk:"]\n$err"
9919 set nullid "0000000000000000000000000000000000000000"
9920 set nullid2 "0000000000000000000000000000000000000001"
9921 set nullfile "/dev/null"
9923 set have_tk85 [expr {[package vcompare $tk_version "8.5"] >= 0}]
9930 set highlight_paths {}
9932 set searchdirn -forwards
9936 set markingmatches 0
9937 set linkentercount 0
9938 set need_redisplay 0
9945 set selectedhlview [mc "None"]
9946 set highlight_related [mc "None"]
9947 set highlight_files {}
9951 set viewargscmd(0) {}
9961 set isworktree [expr {[exec git rev-parse --is-inside-work-tree] == "true"}]
9964 # wait for the window to become visible
9966 wm title . "[file tail $argv0]: [file tail [pwd]]"
9969 if {$cmdline_files ne {} || $revtreeargs ne {} || $revtreeargscmd ne {}} {
9970 # create a view for the files/dirs specified on the command line
9974 set viewname(1) [mc "Command line"]
9975 set viewfiles(1) $cmdline_files
9976 set viewargs(1) $revtreeargs
9977 set viewargscmd(1) $revtreeargscmd
9981 .bar.view entryconf [mc "Edit view..."] -state normal
9982 .bar.view entryconf [mc "Delete view"] -state normal
9985 if {[info exists permviews]} {
9986 foreach v $permviews {
9989 set viewname($n) [lindex $v 0]
9990 set viewfiles($n) [lindex $v 1]
9991 set viewargs($n) [lindex $v 2]
9992 set viewargscmd($n) [lindex $v 3]