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 selectheadid
424 set pending_select
$selid
425 } elseif
{$selectheadid ne
{}} {
426 set pending_select
$selectheadid
428 set pending_select
$mainheadid
432 proc getcommits
{selid
} {
433 global canv curview need_redisplay viewactive
436 if {[start_rev_list
$curview]} {
437 reset_pending_select
$selid
438 show_status
[mc
"Reading commits..."]
441 show_status
[mc
"No commits selected"]
445 proc updatecommits
{} {
446 global curview vcanopt vorigargs vfilelimit viewinstances
447 global viewactive viewcomplete tclencoding
448 global startmsecs showneartags showlocalchanges
449 global mainheadid pending_select
451 global varcid vposids vnegids vflags vrevs
453 set isworktree
[expr {[exec git rev-parse
--is-inside-work-tree] == "true"}]
454 set oldmainid
$mainheadid
456 if {$showlocalchanges} {
457 if {$mainheadid ne
$oldmainid} {
460 if {[commitinview
$mainheadid $curview]} {
465 if {$vcanopt($view)} {
466 set oldpos
$vposids($view)
467 set oldneg
$vnegids($view)
468 set revs
[parseviewrevs
$view $vrevs($view)]
472 # note: getting the delta when negative refs change is hard,
473 # and could require multiple git log invocations, so in that
474 # case we ask git log for all the commits (not just the delta)
475 if {$oldneg eq
$vnegids($view)} {
478 # take out positive refs that we asked for before or
479 # that we have already seen
481 if {[string length
$rev] == 40} {
482 if {[lsearch
-exact $oldpos $rev] < 0
483 && ![info exists varcid
($view,$rev)]} {
488 lappend
$newrevs $rev
491 if {$npos == 0} return
493 set vposids
($view) [lsort
-unique [concat
$oldpos $vposids($view)]]
495 set args
[concat
$vflags($view) $revs --not $oldpos]
497 set args
$vorigargs($view)
500 set fd
[open
[concat | git log
--no-color -z --pretty=raw
--parents \
501 --boundary $args "--" $vfilelimit($view)] r
]
503 error_popup
"Error executing git log: $err"
506 if {$viewactive($view) == 0} {
507 set startmsecs
[clock clicks
-milliseconds]
509 set i
[reg_instance
$fd]
510 lappend viewinstances
($view) $i
511 fconfigure
$fd -blocking 0 -translation lf
-eofchar {}
512 if {$tclencoding != {}} {
513 fconfigure
$fd -encoding $tclencoding
515 filerun
$fd [list getcommitlines
$fd $i $view 1]
516 incr viewactive
($view)
517 set viewcomplete
($view) 0
518 reset_pending_select
{}
519 nowbusy
$view "Reading"
525 proc reloadcommits
{} {
526 global curview viewcomplete selectedline currentid thickerline
527 global showneartags treediffs commitinterest cached_commitrow
531 if {$selectedline ne
{}} {
535 if {!$viewcomplete($curview)} {
536 stop_rev_list
$curview
540 catch
{unset currentid
}
541 catch
{unset thickerline
}
542 catch
{unset treediffs
}
549 catch
{unset commitinterest
}
550 catch
{unset cached_commitrow
}
551 catch
{unset targetid
}
557 # This makes a string representation of a positive integer which
558 # sorts as a string in numerical order
561 return [format
"%x" $n]
562 } elseif
{$n < 256} {
563 return [format
"x%.2x" $n]
564 } elseif
{$n < 65536} {
565 return [format
"y%.4x" $n]
567 return [format
"z%.8x" $n]
570 # Procedures used in reordering commits from git log (without
571 # --topo-order) into the order for display.
573 proc varcinit
{view
} {
574 global varcstart vupptr vdownptr vleftptr vbackptr varctok varcrow
575 global vtokmod varcmod vrowmod varcix vlastins
577 set varcstart
($view) {{}}
578 set vupptr
($view) {0}
579 set vdownptr
($view) {0}
580 set vleftptr
($view) {0}
581 set vbackptr
($view) {0}
582 set varctok
($view) {{}}
583 set varcrow
($view) {{}}
584 set vtokmod
($view) {}
587 set varcix
($view) {{}}
588 set vlastins
($view) {0}
591 proc resetvarcs
{view
} {
592 global varcid varccommits parents children vseedcount ordertok
594 foreach vid
[array names varcid
$view,*] {
599 # some commits might have children but haven't been seen yet
600 foreach vid
[array names children
$view,*] {
603 foreach va
[array names varccommits
$view,*] {
604 unset varccommits
($va)
606 foreach vd
[array names vseedcount
$view,*] {
607 unset vseedcount
($vd)
609 catch
{unset ordertok
}
612 # returns a list of the commits with no children
614 global vdownptr vleftptr varcstart
617 set a
[lindex
$vdownptr($v) 0]
619 lappend ret
[lindex
$varcstart($v) $a]
620 set a
[lindex
$vleftptr($v) $a]
625 proc newvarc
{view id
} {
626 global varcid varctok parents children vdatemode
627 global vupptr vdownptr vleftptr vbackptr varcrow varcix varcstart
628 global commitdata commitinfo vseedcount varccommits vlastins
630 set a
[llength
$varctok($view)]
632 if {[llength
$children($vid)] == 0 ||
$vdatemode($view)} {
633 if {![info exists commitinfo
($id)]} {
634 parsecommit
$id $commitdata($id) 1
636 set cdate
[lindex
$commitinfo($id) 4]
637 if {![string is integer
-strict $cdate]} {
640 if {![info exists vseedcount
($view,$cdate)]} {
641 set vseedcount
($view,$cdate) -1
643 set c
[incr vseedcount
($view,$cdate)]
644 set cdate
[expr {$cdate ^
0xffffffff}]
645 set tok
"s[strrep $cdate][strrep $c]"
650 if {[llength
$children($vid)] > 0} {
651 set kid
[lindex
$children($vid) end
]
652 set k
$varcid($view,$kid)
653 if {[string compare
[lindex
$varctok($view) $k] $tok] > 0} {
656 set tok
[lindex
$varctok($view) $k]
660 set i
[lsearch
-exact $parents($view,$ki) $id]
661 set j
[expr {[llength
$parents($view,$ki)] - 1 - $i}]
662 append tok
[strrep
$j]
664 set c
[lindex
$vlastins($view) $ka]
665 if {$c == 0 ||
[string compare
$tok [lindex
$varctok($view) $c]] < 0} {
667 set b
[lindex
$vdownptr($view) $ka]
669 set b
[lindex
$vleftptr($view) $c]
671 while {$b != 0 && [string compare
$tok [lindex
$varctok($view) $b]] >= 0} {
673 set b
[lindex
$vleftptr($view) $c]
676 lset vdownptr
($view) $ka $a
677 lappend vbackptr
($view) 0
679 lset vleftptr
($view) $c $a
680 lappend vbackptr
($view) $c
682 lset vlastins
($view) $ka $a
683 lappend vupptr
($view) $ka
684 lappend vleftptr
($view) $b
686 lset vbackptr
($view) $b $a
688 lappend varctok
($view) $tok
689 lappend varcstart
($view) $id
690 lappend vdownptr
($view) 0
691 lappend varcrow
($view) {}
692 lappend varcix
($view) {}
693 set varccommits
($view,$a) {}
694 lappend vlastins
($view) 0
698 proc splitvarc
{p v
} {
699 global varcid varcstart varccommits varctok
700 global vupptr vdownptr vleftptr vbackptr varcix varcrow vlastins
702 set oa
$varcid($v,$p)
703 set ac
$varccommits($v,$oa)
704 set i
[lsearch
-exact $varccommits($v,$oa) $p]
706 set na
[llength
$varctok($v)]
707 # "%" sorts before "0"...
708 set tok
"[lindex $varctok($v) $oa]%[strrep $i]"
709 lappend varctok
($v) $tok
710 lappend varcrow
($v) {}
711 lappend varcix
($v) {}
712 set varccommits
($v,$oa) [lrange
$ac 0 [expr {$i - 1}]]
713 set varccommits
($v,$na) [lrange
$ac $i end
]
714 lappend varcstart
($v) $p
715 foreach id
$varccommits($v,$na) {
716 set varcid
($v,$id) $na
718 lappend vdownptr
($v) [lindex
$vdownptr($v) $oa]
719 lappend vlastins
($v) [lindex
$vlastins($v) $oa]
720 lset vdownptr
($v) $oa $na
721 lset vlastins
($v) $oa 0
722 lappend vupptr
($v) $oa
723 lappend vleftptr
($v) 0
724 lappend vbackptr
($v) 0
725 for {set b
[lindex
$vdownptr($v) $na]} {$b != 0} {set b
[lindex
$vleftptr($v) $b]} {
726 lset vupptr
($v) $b $na
730 proc renumbervarc
{a v
} {
731 global parents children varctok varcstart varccommits
732 global vupptr vdownptr vleftptr vbackptr vlastins varcid vtokmod vdatemode
734 set t1
[clock clicks
-milliseconds]
740 if {[info exists isrelated
($a)]} {
742 set id
[lindex
$varccommits($v,$a) end
]
743 foreach p
$parents($v,$id) {
744 if {[info exists varcid
($v,$p)]} {
745 set isrelated
($varcid($v,$p)) 1
750 set b
[lindex
$vdownptr($v) $a]
753 set b
[lindex
$vleftptr($v) $a]
755 set a
[lindex
$vupptr($v) $a]
761 if {![info exists kidchanged
($a)]} continue
762 set id
[lindex
$varcstart($v) $a]
763 if {[llength
$children($v,$id)] > 1} {
764 set children
($v,$id) [lsort
-command [list vtokcmp
$v] \
767 set oldtok
[lindex
$varctok($v) $a]
768 if {!$vdatemode($v)} {
774 set kid
[last_real_child
$v,$id]
776 set k
$varcid($v,$kid)
777 if {[string compare
[lindex
$varctok($v) $k] $tok] > 0} {
780 set tok
[lindex
$varctok($v) $k]
784 set i
[lsearch
-exact $parents($v,$ki) $id]
785 set j
[expr {[llength
$parents($v,$ki)] - 1 - $i}]
786 append tok
[strrep
$j]
788 if {$tok eq
$oldtok} {
791 set id
[lindex
$varccommits($v,$a) end
]
792 foreach p
$parents($v,$id) {
793 if {[info exists varcid
($v,$p)]} {
794 set kidchanged
($varcid($v,$p)) 1
799 lset varctok
($v) $a $tok
800 set b
[lindex
$vupptr($v) $a]
802 if {[string compare
[lindex
$varctok($v) $ka] $vtokmod($v)] < 0} {
805 if {[string compare
[lindex
$varctok($v) $b] $vtokmod($v)] < 0} {
808 set c
[lindex
$vbackptr($v) $a]
809 set d
[lindex
$vleftptr($v) $a]
811 lset vdownptr
($v) $b $d
813 lset vleftptr
($v) $c $d
816 lset vbackptr
($v) $d $c
818 if {[lindex
$vlastins($v) $b] == $a} {
819 lset vlastins
($v) $b $c
821 lset vupptr
($v) $a $ka
822 set c
[lindex
$vlastins($v) $ka]
824 [string compare
$tok [lindex
$varctok($v) $c]] < 0} {
826 set b
[lindex
$vdownptr($v) $ka]
828 set b
[lindex
$vleftptr($v) $c]
831 [string compare
$tok [lindex
$varctok($v) $b]] >= 0} {
833 set b
[lindex
$vleftptr($v) $c]
836 lset vdownptr
($v) $ka $a
837 lset vbackptr
($v) $a 0
839 lset vleftptr
($v) $c $a
840 lset vbackptr
($v) $a $c
842 lset vleftptr
($v) $a $b
844 lset vbackptr
($v) $b $a
846 lset vlastins
($v) $ka $a
849 foreach id
[array names sortkids
] {
850 if {[llength
$children($v,$id)] > 1} {
851 set children
($v,$id) [lsort
-command [list vtokcmp
$v] \
855 set t2
[clock clicks
-milliseconds]
856 #puts "renumbervarc did [llength $todo] of $ntot arcs in [expr {$t2-$t1}]ms"
859 # Fix up the graph after we have found out that in view $v,
860 # $p (a commit that we have already seen) is actually the parent
861 # of the last commit in arc $a.
862 proc fix_reversal
{p a v
} {
863 global varcid varcstart varctok vupptr
865 set pa
$varcid($v,$p)
866 if {$p ne
[lindex
$varcstart($v) $pa]} {
868 set pa
$varcid($v,$p)
870 # seeds always need to be renumbered
871 if {[lindex
$vupptr($v) $pa] == 0 ||
872 [string compare
[lindex
$varctok($v) $a] \
873 [lindex
$varctok($v) $pa]] > 0} {
878 proc insertrow
{id p v
} {
879 global cmitlisted children parents varcid varctok vtokmod
880 global varccommits ordertok commitidx numcommits curview
881 global targetid targetrow
885 set cmitlisted
($vid) 1
886 set children
($vid) {}
887 set parents
($vid) [list
$p]
888 set a
[newvarc
$v $id]
890 if {[string compare
[lindex
$varctok($v) $a] $vtokmod($v)] < 0} {
893 lappend varccommits
($v,$a) $id
895 if {[llength
[lappend children
($vp) $id]] > 1} {
896 set children
($vp) [lsort
-command [list vtokcmp
$v] $children($vp)]
897 catch
{unset ordertok
}
899 fix_reversal
$p $a $v
901 if {$v == $curview} {
902 set numcommits
$commitidx($v)
904 if {[info exists targetid
]} {
905 if {![comes_before
$targetid $p]} {
912 proc insertfakerow
{id p
} {
913 global varcid varccommits parents children cmitlisted
914 global commitidx varctok vtokmod targetid targetrow curview numcommits
918 set i
[lsearch
-exact $varccommits($v,$a) $p]
920 puts
"oops: insertfakerow can't find [shortids $p] on arc $a"
923 set children
($v,$id) {}
924 set parents
($v,$id) [list
$p]
925 set varcid
($v,$id) $a
926 lappend children
($v,$p) $id
927 set cmitlisted
($v,$id) 1
928 set numcommits
[incr commitidx
($v)]
929 # note we deliberately don't update varcstart($v) even if $i == 0
930 set varccommits
($v,$a) [linsert
$varccommits($v,$a) $i $id]
932 if {[info exists targetid
]} {
933 if {![comes_before
$targetid $p]} {
941 proc removefakerow
{id
} {
942 global varcid varccommits parents children commitidx
943 global varctok vtokmod cmitlisted currentid selectedline
944 global targetid curview numcommits
947 if {[llength
$parents($v,$id)] != 1} {
948 puts
"oops: removefakerow [shortids $id] has [llength $parents($v,$id)] parents"
951 set p
[lindex
$parents($v,$id) 0]
952 set a
$varcid($v,$id)
953 set i
[lsearch
-exact $varccommits($v,$a) $id]
955 puts
"oops: removefakerow can't find [shortids $id] on arc $a"
959 set varccommits
($v,$a) [lreplace
$varccommits($v,$a) $i $i]
960 unset parents
($v,$id)
961 unset children
($v,$id)
962 unset cmitlisted
($v,$id)
963 set numcommits
[incr commitidx
($v) -1]
964 set j
[lsearch
-exact $children($v,$p) $id]
966 set children
($v,$p) [lreplace
$children($v,$p) $j $j]
969 if {[info exist currentid
] && $id eq
$currentid} {
973 if {[info exists targetid
] && $targetid eq
$id} {
980 proc first_real_child
{vp
} {
981 global children nullid nullid2
983 foreach id
$children($vp) {
984 if {$id ne
$nullid && $id ne
$nullid2} {
991 proc last_real_child
{vp
} {
992 global children nullid nullid2
994 set kids
$children($vp)
995 for {set i
[llength
$kids]} {[incr i
-1] >= 0} {} {
996 set id
[lindex
$kids $i]
997 if {$id ne
$nullid && $id ne
$nullid2} {
1004 proc vtokcmp
{v a b
} {
1005 global varctok varcid
1007 return [string compare
[lindex
$varctok($v) $varcid($v,$a)] \
1008 [lindex
$varctok($v) $varcid($v,$b)]]
1011 # This assumes that if lim is not given, the caller has checked that
1012 # arc a's token is less than $vtokmod($v)
1013 proc modify_arc
{v a
{lim
{}}} {
1014 global varctok vtokmod varcmod varcrow vupptr curview vrowmod varccommits
1017 set c
[string compare
[lindex
$varctok($v) $a] $vtokmod($v)]
1020 set r
[lindex
$varcrow($v) $a]
1021 if {$r ne
{} && $vrowmod($v) <= $r + $lim} return
1024 set vtokmod
($v) [lindex
$varctok($v) $a]
1026 if {$v == $curview} {
1027 while {$a != 0 && [lindex
$varcrow($v) $a] eq
{}} {
1028 set a
[lindex
$vupptr($v) $a]
1034 set lim
[llength
$varccommits($v,$a)]
1036 set r
[expr {[lindex
$varcrow($v) $a] + $lim}]
1043 proc update_arcrows
{v
} {
1044 global vtokmod varcmod vrowmod varcrow commitidx currentid selectedline
1045 global varcid vrownum varcorder varcix varccommits
1046 global vupptr vdownptr vleftptr varctok
1047 global displayorder parentlist curview cached_commitrow
1049 if {$vrowmod($v) == $commitidx($v)} return
1050 if {$v == $curview} {
1051 if {[llength
$displayorder] > $vrowmod($v)} {
1052 set displayorder
[lrange
$displayorder 0 [expr {$vrowmod($v) - 1}]]
1053 set parentlist
[lrange
$parentlist 0 [expr {$vrowmod($v) - 1}]]
1055 catch
{unset cached_commitrow
}
1057 set narctot
[expr {[llength
$varctok($v)] - 1}]
1059 while {$a != 0 && [lindex
$varcix($v) $a] eq
{}} {
1060 # go up the tree until we find something that has a row number,
1061 # or we get to a seed
1062 set a
[lindex
$vupptr($v) $a]
1065 set a
[lindex
$vdownptr($v) 0]
1068 set varcorder
($v) [list
$a]
1069 lset varcix
($v) $a 0
1070 lset varcrow
($v) $a 0
1074 set arcn
[lindex
$varcix($v) $a]
1075 if {[llength
$vrownum($v)] > $arcn + 1} {
1076 set vrownum
($v) [lrange
$vrownum($v) 0 $arcn]
1077 set varcorder
($v) [lrange
$varcorder($v) 0 $arcn]
1079 set row
[lindex
$varcrow($v) $a]
1083 incr row
[llength
$varccommits($v,$a)]
1084 # go down if possible
1085 set b
[lindex
$vdownptr($v) $a]
1087 # if not, go left, or go up until we can go left
1089 set b
[lindex
$vleftptr($v) $a]
1091 set a
[lindex
$vupptr($v) $a]
1097 lappend vrownum
($v) $row
1098 lappend varcorder
($v) $a
1099 lset varcix
($v) $a $arcn
1100 lset varcrow
($v) $a $row
1102 set vtokmod
($v) [lindex
$varctok($v) $p]
1104 set vrowmod
($v) $row
1105 if {[info exists currentid
]} {
1106 set selectedline
[rowofcommit
$currentid]
1110 # Test whether view $v contains commit $id
1111 proc commitinview
{id v
} {
1114 return [info exists varcid
($v,$id)]
1117 # Return the row number for commit $id in the current view
1118 proc rowofcommit
{id
} {
1119 global varcid varccommits varcrow curview cached_commitrow
1120 global varctok vtokmod
1123 if {![info exists varcid
($v,$id)]} {
1124 puts
"oops rowofcommit no arc for [shortids $id]"
1127 set a
$varcid($v,$id)
1128 if {[string compare
[lindex
$varctok($v) $a] $vtokmod($v)] >= 0} {
1131 if {[info exists cached_commitrow
($id)]} {
1132 return $cached_commitrow($id)
1134 set i
[lsearch
-exact $varccommits($v,$a) $id]
1136 puts
"oops didn't find commit [shortids $id] in arc $a"
1139 incr i
[lindex
$varcrow($v) $a]
1140 set cached_commitrow
($id) $i
1144 # Returns 1 if a is on an earlier row than b, otherwise 0
1145 proc comes_before
{a b
} {
1146 global varcid varctok curview
1149 if {$a eq
$b ||
![info exists varcid
($v,$a)] || \
1150 ![info exists varcid
($v,$b)]} {
1153 if {$varcid($v,$a) != $varcid($v,$b)} {
1154 return [expr {[string compare
[lindex
$varctok($v) $varcid($v,$a)] \
1155 [lindex
$varctok($v) $varcid($v,$b)]] < 0}]
1157 return [expr {[rowofcommit
$a] < [rowofcommit
$b]}]
1160 proc bsearch
{l elt
} {
1161 if {[llength
$l] == 0 ||
$elt <= [lindex
$l 0]} {
1166 while {$hi - $lo > 1} {
1167 set mid
[expr {int
(($lo + $hi) / 2)}]
1168 set t
[lindex
$l $mid]
1171 } elseif
{$elt > $t} {
1180 # Make sure rows $start..$end-1 are valid in displayorder and parentlist
1181 proc make_disporder
{start end
} {
1182 global vrownum curview commitidx displayorder parentlist
1183 global varccommits varcorder parents vrowmod varcrow
1184 global d_valid_start d_valid_end
1186 if {$end > $vrowmod($curview)} {
1187 update_arcrows
$curview
1189 set ai
[bsearch
$vrownum($curview) $start]
1190 set start
[lindex
$vrownum($curview) $ai]
1191 set narc
[llength
$vrownum($curview)]
1192 for {set r
$start} {$ai < $narc && $r < $end} {incr ai
} {
1193 set a
[lindex
$varcorder($curview) $ai]
1194 set l
[llength
$displayorder]
1195 set al
[llength
$varccommits($curview,$a)]
1196 if {$l < $r + $al} {
1198 set pad
[ntimes
[expr {$r - $l}] {}]
1199 set displayorder
[concat
$displayorder $pad]
1200 set parentlist
[concat
$parentlist $pad]
1201 } elseif
{$l > $r} {
1202 set displayorder
[lrange
$displayorder 0 [expr {$r - 1}]]
1203 set parentlist
[lrange
$parentlist 0 [expr {$r - 1}]]
1205 foreach id
$varccommits($curview,$a) {
1206 lappend displayorder
$id
1207 lappend parentlist
$parents($curview,$id)
1209 } elseif
{[lindex
$displayorder [expr {$r + $al - 1}]] eq
{}} {
1211 foreach id
$varccommits($curview,$a) {
1212 lset displayorder
$i $id
1213 lset parentlist
$i $parents($curview,$id)
1221 proc commitonrow
{row
} {
1224 set id
[lindex
$displayorder $row]
1226 make_disporder
$row [expr {$row + 1}]
1227 set id
[lindex
$displayorder $row]
1232 proc closevarcs
{v
} {
1233 global varctok varccommits varcid parents children
1234 global cmitlisted commitidx commitinterest vtokmod
1236 set missing_parents
0
1238 set narcs
[llength
$varctok($v)]
1239 for {set a
1} {$a < $narcs} {incr a
} {
1240 set id
[lindex
$varccommits($v,$a) end
]
1241 foreach p
$parents($v,$id) {
1242 if {[info exists varcid
($v,$p)]} continue
1243 # add p as a new commit
1244 incr missing_parents
1245 set cmitlisted
($v,$p) 0
1246 set parents
($v,$p) {}
1247 if {[llength
$children($v,$p)] == 1 &&
1248 [llength
$parents($v,$id)] == 1} {
1251 set b
[newvarc
$v $p]
1253 set varcid
($v,$p) $b
1254 if {[string compare
[lindex
$varctok($v) $b] $vtokmod($v)] < 0} {
1257 lappend varccommits
($v,$b) $p
1259 if {[info exists commitinterest
($p)]} {
1260 foreach
script $commitinterest($p) {
1261 lappend scripts
[string map
[list
"%I" $p] $script]
1263 unset commitinterest
($id)
1267 if {$missing_parents > 0} {
1268 foreach s
$scripts {
1274 # Use $rwid as a substitute for $id, i.e. reparent $id's children to $rwid
1275 # Assumes we already have an arc for $rwid.
1276 proc rewrite_commit
{v id rwid
} {
1277 global children parents varcid varctok vtokmod varccommits
1279 foreach ch
$children($v,$id) {
1280 # make $rwid be $ch's parent in place of $id
1281 set i
[lsearch
-exact $parents($v,$ch) $id]
1283 puts
"oops rewrite_commit didn't find $id in parent list for $ch"
1285 set parents
($v,$ch) [lreplace
$parents($v,$ch) $i $i $rwid]
1286 # add $ch to $rwid's children and sort the list if necessary
1287 if {[llength
[lappend children
($v,$rwid) $ch]] > 1} {
1288 set children
($v,$rwid) [lsort
-command [list vtokcmp
$v] \
1289 $children($v,$rwid)]
1291 # fix the graph after joining $id to $rwid
1292 set a
$varcid($v,$ch)
1293 fix_reversal
$rwid $a $v
1294 # parentlist is wrong for the last element of arc $a
1295 # even if displayorder is right, hence the 3rd arg here
1296 modify_arc
$v $a [expr {[llength
$varccommits($v,$a)] - 1}]
1300 proc getcommitlines
{fd inst view updating
} {
1301 global cmitlisted commitinterest leftover
1302 global commitidx commitdata vdatemode
1303 global parents children curview hlview
1304 global idpending ordertok
1305 global varccommits varcid varctok vtokmod vfilelimit
1307 set stuff
[read $fd 500000]
1308 # git log doesn't terminate the last commit with a null...
1309 if {$stuff == {} && $leftover($inst) ne
{} && [eof
$fd]} {
1316 global commfd viewcomplete viewactive viewname
1317 global viewinstances
1319 set i
[lsearch
-exact $viewinstances($view) $inst]
1321 set viewinstances
($view) [lreplace
$viewinstances($view) $i $i]
1323 # set it blocking so we wait for the process to terminate
1324 fconfigure
$fd -blocking 1
1325 if {[catch
{close
$fd} err
]} {
1327 if {$view != $curview} {
1328 set fv
" for the \"$viewname($view)\" view"
1330 if {[string range
$err 0 4] == "usage"} {
1331 set err
"Gitk: error reading commits$fv:\
1332 bad arguments to git log."
1333 if {$viewname($view) eq
"Command line"} {
1335 " (Note: arguments to gitk are passed to git log\
1336 to allow selection of commits to be displayed.)"
1339 set err
"Error reading commits$fv: $err"
1343 if {[incr viewactive
($view) -1] <= 0} {
1344 set viewcomplete
($view) 1
1345 # Check if we have seen any ids listed as parents that haven't
1346 # appeared in the list
1350 if {$view == $curview} {
1359 set i
[string first
"\0" $stuff $start]
1361 append leftover
($inst) [string range
$stuff $start end
]
1365 set cmit
$leftover($inst)
1366 append cmit
[string range
$stuff 0 [expr {$i - 1}]]
1367 set leftover
($inst) {}
1369 set cmit
[string range
$stuff $start [expr {$i - 1}]]
1371 set start
[expr {$i + 1}]
1372 set j
[string first
"\n" $cmit]
1375 if {$j >= 0 && [string match
"commit *" $cmit]} {
1376 set ids
[string range
$cmit 7 [expr {$j - 1}]]
1377 if {[string match
{[-^
<>]*} $ids]} {
1378 switch
-- [string index
$ids 0] {
1384 set ids
[string range
$ids 1 end
]
1388 if {[string length
$id] != 40} {
1396 if {[string length
$shortcmit] > 80} {
1397 set shortcmit
"[string range $shortcmit 0 80]..."
1399 error_popup
"[mc "Can
't parse git log output:"] {$shortcmit}"
1402 set id [lindex $ids 0]
1405 if {!$listed && $updating && ![info exists varcid($vid)] &&
1406 $vfilelimit($view) ne {}} {
1407 # git log doesn't rewrite parents
for unlisted commits
1408 # when doing path limiting, so work around that here
1409 # by working out the rewritten parent with git rev-list
1410 # and if we already know about it, using the rewritten
1411 # parent as a substitute parent for $id's children.
1413 set rwid
[exec git rev-list
--first-parent --max-count=1 \
1414 $id -- $vfilelimit($view)]
1416 if {$rwid ne
{} && [info exists varcid
($view,$rwid)]} {
1417 # use $rwid in place of $id
1418 rewrite_commit
$view $id $rwid
1425 if {[info exists varcid
($vid)]} {
1426 if {$cmitlisted($vid) ||
!$listed} continue
1430 set olds
[lrange
$ids 1 end
]
1434 set commitdata
($id) [string range
$cmit [expr {$j + 1}] end
]
1435 set cmitlisted
($vid) $listed
1436 set parents
($vid) $olds
1437 if {![info exists children
($vid)]} {
1438 set children
($vid) {}
1439 } elseif
{$a == 0 && [llength
$children($vid)] == 1} {
1440 set k
[lindex
$children($vid) 0]
1441 if {[llength
$parents($view,$k)] == 1 &&
1442 (!$vdatemode($view) ||
1443 $varcid($view,$k) == [llength
$varctok($view)] - 1)} {
1444 set a
$varcid($view,$k)
1449 set a
[newvarc
$view $id]
1451 if {[string compare
[lindex
$varctok($view) $a] $vtokmod($view)] < 0} {
1454 if {![info exists varcid
($vid)]} {
1456 lappend varccommits
($view,$a) $id
1457 incr commitidx
($view)
1462 if {$i == 0 ||
[lsearch
-exact $olds $p] >= $i} {
1464 if {[llength
[lappend children
($vp) $id]] > 1 &&
1465 [vtokcmp
$view [lindex
$children($vp) end-1
] $id] > 0} {
1466 set children
($vp) [lsort
-command [list vtokcmp
$view] \
1468 catch
{unset ordertok
}
1470 if {[info exists varcid
($view,$p)]} {
1471 fix_reversal
$p $a $view
1477 if {[info exists commitinterest
($id)]} {
1478 foreach
script $commitinterest($id) {
1479 lappend scripts
[string map
[list
"%I" $id] $script]
1481 unset commitinterest
($id)
1486 global numcommits hlview
1488 if {$view == $curview} {
1489 set numcommits
$commitidx($view)
1492 if {[info exists hlview
] && $view == $hlview} {
1493 # we never actually get here...
1496 foreach s
$scripts {
1503 proc chewcommits
{} {
1504 global curview hlview viewcomplete
1505 global pending_select
1508 if {$viewcomplete($curview)} {
1509 global commitidx varctok
1510 global numcommits startmsecs
1512 if {[info exists pending_select
]} {
1514 reset_pending_select
{}
1516 if {[commitinview
$pending_select $curview]} {
1517 selectline
[rowofcommit
$pending_select] 1
1519 set row
[first_real_row
]
1523 if {$commitidx($curview) > 0} {
1524 #set ms [expr {[clock clicks -milliseconds] - $startmsecs}]
1525 #puts "overall $ms ms for $numcommits commits"
1526 #puts "[llength $varctok($view)] arcs, $commitidx($view) commits"
1528 show_status
[mc
"No commits selected"]
1535 proc readcommit
{id
} {
1536 if {[catch
{set contents
[exec git cat-file commit
$id]}]} return
1537 parsecommit
$id $contents 0
1540 proc parsecommit
{id contents listed
} {
1541 global commitinfo cdate
1550 set hdrend
[string first
"\n\n" $contents]
1552 # should never happen...
1553 set hdrend
[string length
$contents]
1555 set header
[string range
$contents 0 [expr {$hdrend - 1}]]
1556 set comment
[string range
$contents [expr {$hdrend + 2}] end
]
1557 foreach line
[split $header "\n"] {
1558 set tag
[lindex
$line 0]
1559 if {$tag == "author"} {
1560 set audate
[lindex
$line end-1
]
1561 set auname
[lrange
$line 1 end-2
]
1562 } elseif
{$tag == "committer"} {
1563 set comdate
[lindex
$line end-1
]
1564 set comname
[lrange
$line 1 end-2
]
1568 # take the first non-blank line of the comment as the headline
1569 set headline
[string trimleft
$comment]
1570 set i
[string first
"\n" $headline]
1572 set headline
[string range
$headline 0 $i]
1574 set headline
[string trimright
$headline]
1575 set i
[string first
"\r" $headline]
1577 set headline
[string trimright
[string range
$headline 0 $i]]
1580 # git log indents the comment by 4 spaces;
1581 # if we got this via git cat-file, add the indentation
1583 foreach line
[split $comment "\n"] {
1584 append newcomment
" "
1585 append newcomment
$line
1586 append newcomment
"\n"
1588 set comment
$newcomment
1590 if {$comdate != {}} {
1591 set cdate
($id) $comdate
1593 set commitinfo
($id) [list
$headline $auname $audate \
1594 $comname $comdate $comment]
1597 proc getcommit
{id
} {
1598 global commitdata commitinfo
1600 if {[info exists commitdata
($id)]} {
1601 parsecommit
$id $commitdata($id) 1
1604 if {![info exists commitinfo
($id)]} {
1605 set commitinfo
($id) [list
[mc
"No commit information available"]]
1612 global tagids idtags headids idheads tagobjid
1613 global otherrefids idotherrefs mainhead mainheadid
1614 global selecthead selectheadid
1616 foreach v
{tagids idtags headids idheads otherrefids idotherrefs
} {
1619 set refd
[open
[list | git show-ref
-d] r
]
1620 while {[gets
$refd line
] >= 0} {
1621 if {[string index
$line 40] ne
" "} continue
1622 set id
[string range
$line 0 39]
1623 set ref
[string range
$line 41 end
]
1624 if {![string match
"refs/*" $ref]} continue
1625 set name
[string range
$ref 5 end
]
1626 if {[string match
"remotes/*" $name]} {
1627 if {![string match
"*/HEAD" $name]} {
1628 set headids
($name) $id
1629 lappend idheads
($id) $name
1631 } elseif
{[string match
"heads/*" $name]} {
1632 set name
[string range
$name 6 end
]
1633 set headids
($name) $id
1634 lappend idheads
($id) $name
1635 } elseif
{[string match
"tags/*" $name]} {
1636 # this lets refs/tags/foo^{} overwrite refs/tags/foo,
1637 # which is what we want since the former is the commit ID
1638 set name
[string range
$name 5 end
]
1639 if {[string match
"*^{}" $name]} {
1640 set name
[string range
$name 0 end-3
]
1642 set tagobjid
($name) $id
1644 set tagids
($name) $id
1645 lappend idtags
($id) $name
1647 set otherrefids
($name) $id
1648 lappend idotherrefs
($id) $name
1655 set mainheadid
[exec git rev-parse HEAD
]
1656 set thehead
[exec git symbolic-ref HEAD
]
1657 if {[string match
"refs/heads/*" $thehead]} {
1658 set mainhead
[string range
$thehead 11 end
]
1662 if {$selecthead ne
{}} {
1664 set selectheadid
[exec git rev-parse
--verify $selecthead]
1669 # skip over fake commits
1670 proc first_real_row
{} {
1671 global nullid nullid2 numcommits
1673 for {set row
0} {$row < $numcommits} {incr row
} {
1674 set id
[commitonrow
$row]
1675 if {$id ne
$nullid && $id ne
$nullid2} {
1682 # update things for a head moved to a child of its previous location
1683 proc movehead
{id name
} {
1684 global headids idheads
1686 removehead
$headids($name) $name
1687 set headids
($name) $id
1688 lappend idheads
($id) $name
1691 # update things when a head has been removed
1692 proc removehead
{id name
} {
1693 global headids idheads
1695 if {$idheads($id) eq
$name} {
1698 set i
[lsearch
-exact $idheads($id) $name]
1700 set idheads
($id) [lreplace
$idheads($id) $i $i]
1703 unset headids
($name)
1706 proc show_error
{w top msg
} {
1707 message
$w.m
-text $msg -justify center
-aspect 400
1708 pack
$w.m
-side top
-fill x
-padx 20 -pady 20
1709 button
$w.ok
-text [mc OK
] -command "destroy $top"
1710 pack
$w.ok
-side bottom
-fill x
1711 bind $top <Visibility
> "grab $top; focus $top"
1712 bind $top <Key-Return
> "destroy $top"
1716 proc error_popup msg
{
1720 show_error
$w $w $msg
1723 proc confirm_popup msg
{
1729 message
$w.m
-text $msg -justify center
-aspect 400
1730 pack
$w.m
-side top
-fill x
-padx 20 -pady 20
1731 button
$w.ok
-text [mc OK
] -command "set confirm_ok 1; destroy $w"
1732 pack
$w.ok
-side left
-fill x
1733 button
$w.cancel
-text [mc Cancel
] -command "destroy $w"
1734 pack
$w.cancel
-side right
-fill x
1735 bind $w <Visibility
> "grab $w; focus $w"
1740 proc setoptions
{} {
1741 option add
*Panedwindow.showHandle
1 startupFile
1742 option add
*Panedwindow.sashRelief raised startupFile
1743 option add
*Button.font uifont startupFile
1744 option add
*Checkbutton.font uifont startupFile
1745 option add
*Radiobutton.font uifont startupFile
1746 option add
*Menu.font uifont startupFile
1747 option add
*Menubutton.font uifont startupFile
1748 option add
*Label.font uifont startupFile
1749 option add
*Message.font uifont startupFile
1750 option add
*Entry.font uifont startupFile
1753 proc makewindow
{} {
1754 global canv canv2 canv3 linespc charspc ctext cflist cscroll
1756 global findtype findtypemenu findloc findstring fstring geometry
1757 global entries sha1entry sha1string sha1but
1758 global diffcontextstring diffcontext
1760 global maincursor textcursor curtextcursor
1761 global rowctxmenu fakerowmenu mergemax wrapcomment
1762 global highlight_files gdttype
1763 global searchstring sstring
1764 global bgcolor fgcolor bglist fglist diffcolors selectbgcolor
1765 global headctxmenu progresscanv progressitem progresscoords statusw
1766 global fprogitem fprogcoord lastprogupdate progupdatepending
1767 global rprogitem rprogcoord rownumsel numcommits
1771 .bar add cascade
-label [mc
"File"] -menu .bar.
file
1773 .bar.
file add
command -label [mc
"Update"] -command updatecommits
1774 .bar.
file add
command -label [mc
"Reload"] -command reloadcommits
1775 .bar.
file add
command -label [mc
"Reread references"] -command rereadrefs
1776 .bar.
file add
command -label [mc
"List references"] -command showrefs
1777 .bar.
file add
command -label [mc
"Quit"] -command doquit
1779 .bar add cascade
-label [mc
"Edit"] -menu .bar.edit
1780 .bar.edit add
command -label [mc
"Preferences"] -command doprefs
1783 .bar add cascade
-label [mc
"View"] -menu .bar.view
1784 .bar.view add
command -label [mc
"New view..."] -command {newview
0}
1785 .bar.view add
command -label [mc
"Edit view..."] -command editview \
1787 .bar.view add
command -label [mc
"Delete view"] -command delview
-state disabled
1788 .bar.view add separator
1789 .bar.view add radiobutton
-label [mc
"All files"] -command {showview
0} \
1790 -variable selectedview
-value 0
1793 .bar add cascade
-label [mc
"Help"] -menu .bar.
help
1794 .bar.
help add
command -label [mc
"About gitk"] -command about
1795 .bar.
help add
command -label [mc
"Key bindings"] -command keys
1797 . configure
-menu .bar
1799 # the gui has upper and lower half, parts of a paned window.
1800 panedwindow .ctop
-orient vertical
1802 # possibly use assumed geometry
1803 if {![info exists geometry
(pwsash0
)]} {
1804 set geometry
(topheight
) [expr {15 * $linespc}]
1805 set geometry
(topwidth
) [expr {80 * $charspc}]
1806 set geometry
(botheight
) [expr {15 * $linespc}]
1807 set geometry
(botwidth
) [expr {50 * $charspc}]
1808 set geometry
(pwsash0
) "[expr {40 * $charspc}] 2"
1809 set geometry
(pwsash1
) "[expr {60 * $charspc}] 2"
1812 # the upper half will have a paned window, a scroll bar to the right, and some stuff below
1813 frame .tf
-height $geometry(topheight
) -width $geometry(topwidth
)
1815 panedwindow .tf.histframe.pwclist
-orient horizontal
-sashpad 0 -handlesize 4
1817 # create three canvases
1818 set cscroll .tf.histframe.csb
1819 set canv .tf.histframe.pwclist.canv
1821 -selectbackground $selectbgcolor \
1822 -background $bgcolor -bd 0 \
1823 -yscrollincr $linespc -yscrollcommand "scrollcanv $cscroll"
1824 .tf.histframe.pwclist add
$canv
1825 set canv2 .tf.histframe.pwclist.canv2
1827 -selectbackground $selectbgcolor \
1828 -background $bgcolor -bd 0 -yscrollincr $linespc
1829 .tf.histframe.pwclist add
$canv2
1830 set canv3 .tf.histframe.pwclist.canv3
1832 -selectbackground $selectbgcolor \
1833 -background $bgcolor -bd 0 -yscrollincr $linespc
1834 .tf.histframe.pwclist add
$canv3
1835 eval .tf.histframe.pwclist sash place
0 $geometry(pwsash0
)
1836 eval .tf.histframe.pwclist sash place
1 $geometry(pwsash1
)
1838 # a scroll bar to rule them
1839 scrollbar
$cscroll -command {allcanvs yview
} -highlightthickness 0
1840 pack
$cscroll -side right
-fill y
1841 bind .tf.histframe.pwclist
<Configure
> {resizeclistpanes
%W
%w
}
1842 lappend bglist
$canv $canv2 $canv3
1843 pack .tf.histframe.pwclist
-fill both
-expand 1 -side left
1845 # we have two button bars at bottom of top frame. Bar 1
1847 frame .tf.lbar
-height 15
1849 set sha1entry .tf.bar.sha1
1850 set entries
$sha1entry
1851 set sha1but .tf.bar.sha1label
1852 button
$sha1but -text [mc
"SHA1 ID: "] -state disabled
-relief flat \
1853 -command gotocommit
-width 8
1854 $sha1but conf
-disabledforeground [$sha1but cget
-foreground]
1855 pack .tf.bar.sha1label
-side left
1856 entry
$sha1entry -width 40 -font textfont
-textvariable sha1string
1857 trace add variable sha1string
write sha1change
1858 pack
$sha1entry -side left
-pady 2
1860 image create bitmap bm-left
-data {
1861 #define left_width 16
1862 #define left_height 16
1863 static unsigned char left_bits
[] = {
1864 0x00, 0x00, 0xc0, 0x01, 0xe0, 0x00, 0x70, 0x00, 0x38, 0x00, 0x1c, 0x00,
1865 0x0e, 0x00, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0x0e, 0x00, 0x1c, 0x00,
1866 0x38, 0x00, 0x70, 0x00, 0xe0, 0x00, 0xc0, 0x01};
1868 image create bitmap bm-right
-data {
1869 #define right_width 16
1870 #define right_height 16
1871 static unsigned char right_bits
[] = {
1872 0x00, 0x00, 0xc0, 0x01, 0x80, 0x03, 0x00, 0x07, 0x00, 0x0e, 0x00, 0x1c,
1873 0x00, 0x38, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0x00, 0x38, 0x00, 0x1c,
1874 0x00, 0x0e, 0x00, 0x07, 0x80, 0x03, 0xc0, 0x01};
1876 button .tf.bar.leftbut
-image bm-left
-command goback \
1877 -state disabled
-width 26
1878 pack .tf.bar.leftbut
-side left
-fill y
1879 button .tf.bar.rightbut
-image bm-right
-command goforw \
1880 -state disabled
-width 26
1881 pack .tf.bar.rightbut
-side left
-fill y
1883 label .tf.bar.rowlabel
-text [mc
"Row"]
1885 label .tf.bar.rownum
-width 7 -font textfont
-textvariable rownumsel \
1886 -relief sunken
-anchor e
1887 label .tf.bar.rowlabel2
-text "/"
1888 label .tf.bar.numcommits
-width 7 -font textfont
-textvariable numcommits \
1889 -relief sunken
-anchor e
1890 pack .tf.bar.rowlabel .tf.bar.rownum .tf.bar.rowlabel2 .tf.bar.numcommits \
1893 trace add variable selectedline
write selectedline_change
1895 # Status label and progress bar
1896 set statusw .tf.bar.status
1897 label
$statusw -width 15 -relief sunken
1898 pack
$statusw -side left
-padx 5
1899 set h
[expr {[font metrics uifont
-linespace] + 2}]
1900 set progresscanv .tf.bar.progress
1901 canvas
$progresscanv -relief sunken
-height $h -borderwidth 2
1902 set progressitem
[$progresscanv create rect
-1 0 0 $h -fill green
]
1903 set fprogitem
[$progresscanv create rect
-1 0 0 $h -fill yellow
]
1904 set rprogitem
[$progresscanv create rect
-1 0 0 $h -fill red
]
1905 pack
$progresscanv -side right
-expand 1 -fill x
1906 set progresscoords
{0 0}
1909 bind $progresscanv <Configure
> adjustprogress
1910 set lastprogupdate
[clock clicks
-milliseconds]
1911 set progupdatepending
0
1913 # build up the bottom bar of upper window
1914 label .tf.lbar.flabel
-text "[mc "Find
"] "
1915 button .tf.lbar.fnext
-text [mc
"next"] -command {dofind
1 1}
1916 button .tf.lbar.fprev
-text [mc
"prev"] -command {dofind
-1 1}
1917 label .tf.lbar.flab2
-text " [mc "commit
"] "
1918 pack .tf.lbar.flabel .tf.lbar.fnext .tf.lbar.fprev .tf.lbar.flab2 \
1920 set gdttype
[mc
"containing:"]
1921 set gm
[tk_optionMenu .tf.lbar.gdttype gdttype \
1922 [mc
"containing:"] \
1923 [mc
"touching paths:"] \
1924 [mc
"adding/removing string:"]]
1925 trace add variable gdttype
write gdttype_change
1926 pack .tf.lbar.gdttype
-side left
-fill y
1929 set fstring .tf.lbar.findstring
1930 lappend entries
$fstring
1931 entry
$fstring -width 30 -font textfont
-textvariable findstring
1932 trace add variable findstring
write find_change
1933 set findtype
[mc
"Exact"]
1934 set findtypemenu
[tk_optionMenu .tf.lbar.findtype \
1935 findtype
[mc
"Exact"] [mc
"IgnCase"] [mc
"Regexp"]]
1936 trace add variable findtype
write findcom_change
1937 set findloc
[mc
"All fields"]
1938 tk_optionMenu .tf.lbar.findloc findloc
[mc
"All fields"] [mc
"Headline"] \
1939 [mc
"Comments"] [mc
"Author"] [mc
"Committer"]
1940 trace add variable findloc
write find_change
1941 pack .tf.lbar.findloc
-side right
1942 pack .tf.lbar.findtype
-side right
1943 pack
$fstring -side left
-expand 1 -fill x
1945 # Finish putting the upper half of the viewer together
1946 pack .tf.lbar
-in .tf
-side bottom
-fill x
1947 pack .tf.bar
-in .tf
-side bottom
-fill x
1948 pack .tf.histframe
-fill both
-side top
-expand 1
1950 .ctop paneconfigure .tf
-height $geometry(topheight
)
1951 .ctop paneconfigure .tf
-width $geometry(topwidth
)
1953 # now build up the bottom
1954 panedwindow .pwbottom
-orient horizontal
1956 # lower left, a text box over search bar, scroll bar to the right
1957 # if we know window height, then that will set the lower text height, otherwise
1958 # we set lower text height which will drive window height
1959 if {[info exists geometry
(main
)]} {
1960 frame .bleft
-width $geometry(botwidth
)
1962 frame .bleft
-width $geometry(botwidth
) -height $geometry(botheight
)
1968 button .bleft.top.search
-text [mc
"Search"] -command dosearch
1969 pack .bleft.top.search
-side left
-padx 5
1970 set sstring .bleft.top.sstring
1971 entry
$sstring -width 20 -font textfont
-textvariable searchstring
1972 lappend entries
$sstring
1973 trace add variable searchstring
write incrsearch
1974 pack
$sstring -side left
-expand 1 -fill x
1975 radiobutton .bleft.mid.
diff -text [mc
"Diff"] \
1976 -command changediffdisp
-variable diffelide
-value {0 0}
1977 radiobutton .bleft.mid.old
-text [mc
"Old version"] \
1978 -command changediffdisp
-variable diffelide
-value {0 1}
1979 radiobutton .bleft.mid.new
-text [mc
"New version"] \
1980 -command changediffdisp
-variable diffelide
-value {1 0}
1981 label .bleft.mid.labeldiffcontext
-text " [mc "Lines of context
"]: "
1982 pack .bleft.mid.
diff .bleft.mid.old .bleft.mid.new
-side left
1983 spinbox .bleft.mid.diffcontext
-width 5 -font textfont \
1984 -from 1 -increment 1 -to 10000000 \
1985 -validate all
-validatecommand "diffcontextvalidate %P" \
1986 -textvariable diffcontextstring
1987 .bleft.mid.diffcontext
set $diffcontext
1988 trace add variable diffcontextstring
write diffcontextchange
1989 lappend entries .bleft.mid.diffcontext
1990 pack .bleft.mid.labeldiffcontext .bleft.mid.diffcontext
-side left
1991 checkbutton .bleft.mid.ignspace
-text [mc
"Ignore space change"] \
1992 -command changeignorespace
-variable ignorespace
1993 pack .bleft.mid.ignspace
-side left
-padx 5
1994 set ctext .bleft.bottom.ctext
1995 text
$ctext -background $bgcolor -foreground $fgcolor \
1996 -state disabled
-font textfont \
1997 -yscrollcommand scrolltext
-wrap none \
1998 -xscrollcommand ".bleft.bottom.sbhorizontal set"
2000 $ctext conf
-tabstyle wordprocessor
2002 scrollbar .bleft.bottom.sb
-command "$ctext yview"
2003 scrollbar .bleft.bottom.sbhorizontal
-command "$ctext xview" -orient h \
2005 pack .bleft.top
-side top
-fill x
2006 pack .bleft.mid
-side top
-fill x
2007 grid
$ctext .bleft.bottom.sb
-sticky nsew
2008 grid .bleft.bottom.sbhorizontal
-sticky ew
2009 grid columnconfigure .bleft.bottom
0 -weight 1
2010 grid rowconfigure .bleft.bottom
0 -weight 1
2011 grid rowconfigure .bleft.bottom
1 -weight 0
2012 pack .bleft.bottom
-side top
-fill both
-expand 1
2013 lappend bglist
$ctext
2014 lappend fglist
$ctext
2016 $ctext tag conf comment
-wrap $wrapcomment
2017 $ctext tag conf filesep
-font textfontbold
-back "#aaaaaa"
2018 $ctext tag conf hunksep
-fore [lindex
$diffcolors 2]
2019 $ctext tag conf d0
-fore [lindex
$diffcolors 0]
2020 $ctext tag conf d1
-fore [lindex
$diffcolors 1]
2021 $ctext tag conf m0
-fore red
2022 $ctext tag conf m1
-fore blue
2023 $ctext tag conf m2
-fore green
2024 $ctext tag conf m3
-fore purple
2025 $ctext tag conf
m4 -fore brown
2026 $ctext tag conf m5
-fore "#009090"
2027 $ctext tag conf m6
-fore magenta
2028 $ctext tag conf m7
-fore "#808000"
2029 $ctext tag conf m8
-fore "#009000"
2030 $ctext tag conf m9
-fore "#ff0080"
2031 $ctext tag conf m10
-fore cyan
2032 $ctext tag conf m11
-fore "#b07070"
2033 $ctext tag conf m12
-fore "#70b0f0"
2034 $ctext tag conf m13
-fore "#70f0b0"
2035 $ctext tag conf m14
-fore "#f0b070"
2036 $ctext tag conf m15
-fore "#ff70b0"
2037 $ctext tag conf mmax
-fore darkgrey
2039 $ctext tag conf mresult
-font textfontbold
2040 $ctext tag conf msep
-font textfontbold
2041 $ctext tag conf found
-back yellow
2043 .pwbottom add .bleft
2044 .pwbottom paneconfigure .bleft
-width $geometry(botwidth
)
2049 radiobutton .bright.mode.
patch -text [mc
"Patch"] \
2050 -command reselectline
-variable cmitmode
-value "patch"
2051 radiobutton .bright.mode.tree
-text [mc
"Tree"] \
2052 -command reselectline
-variable cmitmode
-value "tree"
2053 grid .bright.mode.
patch .bright.mode.tree
-sticky ew
2054 pack .bright.mode
-side top
-fill x
2055 set cflist .bright.cfiles
2056 set indent
[font measure mainfont
"nn"]
2058 -selectbackground $selectbgcolor \
2059 -background $bgcolor -foreground $fgcolor \
2061 -tabs [list
$indent [expr {2 * $indent}]] \
2062 -yscrollcommand ".bright.sb set" \
2063 -cursor [. cget
-cursor] \
2064 -spacing1 1 -spacing3 1
2065 lappend bglist
$cflist
2066 lappend fglist
$cflist
2067 scrollbar .bright.sb
-command "$cflist yview"
2068 pack .bright.sb
-side right
-fill y
2069 pack
$cflist -side left
-fill both
-expand 1
2070 $cflist tag configure highlight \
2071 -background [$cflist cget
-selectbackground]
2072 $cflist tag configure bold
-font mainfontbold
2074 .pwbottom add .bright
2077 # restore window width & height if known
2078 if {[info exists geometry
(main
)]} {
2079 if {[scan
$geometry(main
) "%dx%d" w h
] >= 2} {
2080 if {$w > [winfo screenwidth .
]} {
2081 set w
[winfo screenwidth .
]
2083 if {$h > [winfo screenheight .
]} {
2084 set h
[winfo screenheight .
]
2086 wm geometry .
"${w}x$h"
2090 if {[tk windowingsystem
] eq
{aqua
}} {
2096 bind .pwbottom
<Configure
> {resizecdetpanes
%W
%w
}
2097 pack .ctop
-fill both
-expand 1
2098 bindall
<1> {selcanvline
%W
%x
%y
}
2099 #bindall <B1-Motion> {selcanvline %W %x %y}
2100 if {[tk windowingsystem
] == "win32"} {
2101 bind .
<MouseWheel
> { windows_mousewheel_redirector
%W
%X
%Y
%D
}
2102 bind $ctext <MouseWheel
> { windows_mousewheel_redirector
%W
%X
%Y
%D
; break }
2104 bindall
<ButtonRelease-4
> "allcanvs yview scroll -5 units"
2105 bindall
<ButtonRelease-5
> "allcanvs yview scroll 5 units"
2106 if {[tk windowingsystem
] eq
"aqua"} {
2107 bindall
<MouseWheel
> {
2108 set delta
[expr {- (%D
)}]
2109 allcanvs yview scroll
$delta units
2113 bindall
<2> "canvscan mark %W %x %y"
2114 bindall
<B2-Motion
> "canvscan dragto %W %x %y"
2115 bindkey
<Home
> selfirstline
2116 bindkey
<End
> sellastline
2117 bind .
<Key-Up
> "selnextline -1"
2118 bind .
<Key-Down
> "selnextline 1"
2119 bind .
<Shift-Key-Up
> "dofind -1 0"
2120 bind .
<Shift-Key-Down
> "dofind 1 0"
2121 bindkey
<Key-Right
> "goforw"
2122 bindkey
<Key-Left
> "goback"
2123 bind .
<Key-Prior
> "selnextpage -1"
2124 bind .
<Key-Next
> "selnextpage 1"
2125 bind .
<$M1B-Home> "allcanvs yview moveto 0.0"
2126 bind .
<$M1B-End> "allcanvs yview moveto 1.0"
2127 bind .
<$M1B-Key-Up> "allcanvs yview scroll -1 units"
2128 bind .
<$M1B-Key-Down> "allcanvs yview scroll 1 units"
2129 bind .
<$M1B-Key-Prior> "allcanvs yview scroll -1 pages"
2130 bind .
<$M1B-Key-Next> "allcanvs yview scroll 1 pages"
2131 bindkey
<Key-Delete
> "$ctext yview scroll -1 pages"
2132 bindkey
<Key-BackSpace
> "$ctext yview scroll -1 pages"
2133 bindkey
<Key-space
> "$ctext yview scroll 1 pages"
2134 bindkey p
"selnextline -1"
2135 bindkey n
"selnextline 1"
2138 bindkey i
"selnextline -1"
2139 bindkey k
"selnextline 1"
2143 bindkey d
"$ctext yview scroll 18 units"
2144 bindkey u
"$ctext yview scroll -18 units"
2145 bindkey
/ {dofind
1 1}
2146 bindkey
<Key-Return
> {dofind
1 1}
2147 bindkey ?
{dofind
-1 1}
2149 bindkey
<F5
> updatecommits
2150 bind .
<$M1B-q> doquit
2151 bind .
<$M1B-f> {dofind
1 1}
2152 bind .
<$M1B-g> {dofind
1 0}
2153 bind .
<$M1B-r> dosearchback
2154 bind .
<$M1B-s> dosearch
2155 bind .
<$M1B-equal> {incrfont
1}
2156 bind .
<$M1B-plus> {incrfont
1}
2157 bind .
<$M1B-KP_Add> {incrfont
1}
2158 bind .
<$M1B-minus> {incrfont
-1}
2159 bind .
<$M1B-KP_Subtract> {incrfont
-1}
2160 wm protocol . WM_DELETE_WINDOW doquit
2161 bind .
<Destroy
> {stop_backends
}
2162 bind .
<Button-1
> "click %W"
2163 bind $fstring <Key-Return
> {dofind
1 1}
2164 bind $sha1entry <Key-Return
> gotocommit
2165 bind $sha1entry <<PasteSelection>> clearsha1
2166 bind $cflist <1> {sel_flist %W %x %y; break}
2167 bind $cflist <B1-Motion> {sel_flist %W %x %y; break}
2168 bind $cflist <ButtonRelease-1> {treeclick %W %x %y}
2169 bind $cflist <Button-3> {pop_flist_menu %W %X %Y %x %y}
2171 set maincursor [. cget -cursor]
2172 set textcursor [$ctext cget -cursor]
2173 set curtextcursor $textcursor
2175 set rowctxmenu .rowctxmenu
2176 menu $rowctxmenu -tearoff 0
2177 $rowctxmenu add command -label [mc "Diff this -> selected"] \
2178 -command {diffvssel 0}
2179 $rowctxmenu add command -label [mc "Diff selected -> this"] \
2180 -command {diffvssel 1}
2181 $rowctxmenu add command -label [mc "Make patch"] -command mkpatch
2182 $rowctxmenu add command -label [mc "Create tag"] -command mktag
2183 $rowctxmenu add command -label [mc "Write commit to file"] -command writecommit
2184 $rowctxmenu add command -label [mc "Create new branch"] -command mkbranch
2185 $rowctxmenu add command -label [mc "Cherry-pick this commit"] \
2187 $rowctxmenu add command -label [mc "Reset HEAD branch to here"] \
2190 set fakerowmenu .fakerowmenu
2191 menu $fakerowmenu -tearoff 0
2192 $fakerowmenu add command -label [mc "Diff this -> selected"] \
2193 -command {diffvssel 0}
2194 $fakerowmenu add command -label [mc "Diff selected -> this"] \
2195 -command {diffvssel 1}
2196 $fakerowmenu add command -label [mc "Make patch"] -command mkpatch
2197 # $fakerowmenu add command -label [mc "Commit"] -command {mkcommit 0}
2198 # $fakerowmenu add command -label [mc "Commit all"] -command {mkcommit 1}
2199 # $fakerowmenu add command -label [mc "Revert local changes"] -command revertlocal
2201 set headctxmenu .headctxmenu
2202 menu $headctxmenu -tearoff 0
2203 $headctxmenu add command -label [mc "Check out this branch"] \
2205 $headctxmenu add command -label [mc "Remove this branch"] \
2209 set flist_menu .flistctxmenu
2210 menu $flist_menu -tearoff 0
2211 $flist_menu add command -label [mc "Highlight this too"] \
2212 -command {flist_hl 0}
2213 $flist_menu add command -label [mc "Highlight this only"] \
2214 -command {flist_hl 1}
2215 $flist_menu add command -label [mc "External diff"] \
2216 -command {external_diff}
2219 # Windows sends all mouse wheel events to the current focused window, not
2220 # the one where the mouse hovers, so bind those events here and redirect
2221 # to the correct window
2222 proc windows_mousewheel_redirector {W X Y D} {
2223 global canv canv2 canv3
2224 set w [winfo containing -displayof $W $X $Y]
2226 set u [expr {$D < 0 ? 5 : -5}]
2227 if {$w == $canv || $w == $canv2 || $w == $canv3} {
2228 allcanvs yview scroll $u units
2231 $w yview scroll $u units
2237 # Update row number label when selectedline changes
2238 proc selectedline_change {n1 n2 op} {
2239 global selectedline rownumsel
2241 if {$selectedline eq {}} {
2244 set rownumsel [expr {$selectedline + 1}]
2248 # mouse-2 makes all windows scan vertically, but only the one
2249 # the cursor is in scans horizontally
2250 proc canvscan {op w x y} {
2251 global canv canv2 canv3
2252 foreach c [list $canv $canv2 $canv3] {
2261 proc scrollcanv {cscroll f0 f1} {
2262 $cscroll set $f0 $f1
2267 # when we make a key binding for the toplevel, make sure
2268 # it doesn't get triggered when that key is pressed in the
2269 # find string entry widget.
2270 proc bindkey {ev script} {
2273 set escript [bind Entry $ev]
2274 if {$escript == {}} {
2275 set escript [bind Entry <Key>]
2277 foreach e $entries {
2278 bind $e $ev "$escript; break"
2282 # set the focus back to the toplevel for any click outside
2285 global ctext entries
2286 foreach e [concat $entries $ctext] {
2287 if {$w == $e} return
2292 # Adjust the progress bar for a change in requested extent or canvas size
2293 proc adjustprogress {} {
2294 global progresscanv progressitem progresscoords
2295 global fprogitem fprogcoord lastprogupdate progupdatepending
2296 global rprogitem rprogcoord
2298 set w [expr {[winfo width $progresscanv] - 4}]
2299 set x0 [expr {$w * [lindex $progresscoords 0]}]
2300 set x1 [expr {$w * [lindex $progresscoords 1]}]
2301 set h [winfo height $progresscanv]
2302 $progresscanv coords $progressitem $x0 0 $x1 $h
2303 $progresscanv coords $fprogitem 0 0 [expr {$w * $fprogcoord}] $h
2304 $progresscanv coords $rprogitem 0 0 [expr {$w * $rprogcoord}] $h
2305 set now [clock clicks -milliseconds]
2306 if {$now >= $lastprogupdate + 100} {
2307 set progupdatepending 0
2309 } elseif {!$progupdatepending} {
2310 set progupdatepending 1
2311 after [expr {$lastprogupdate + 100 - $now}] doprogupdate
2315 proc doprogupdate {} {
2316 global lastprogupdate progupdatepending
2318 if {$progupdatepending} {
2319 set progupdatepending 0
2320 set lastprogupdate [clock clicks -milliseconds]
2325 proc savestuff {w} {
2326 global canv canv2 canv3 mainfont textfont uifont tabstop
2327 global stuffsaved findmergefiles maxgraphpct
2328 global maxwidth showneartags showlocalchanges
2329 global viewname viewfiles viewargs viewargscmd viewperm nextviewnum
2330 global cmitmode wrapcomment datetimeformat limitdiffs
2331 global colors bgcolor fgcolor diffcolors diffcontext selectbgcolor
2332 global autoselect extdifftool
2334 if {$stuffsaved} return
2335 if {![winfo viewable .]} return
2337 set f [open "~/.gitk-new" w]
2338 puts $f [list set mainfont $mainfont]
2339 puts $f [list set textfont $textfont]
2340 puts $f [list set uifont $uifont]
2341 puts $f [list set tabstop $tabstop]
2342 puts $f [list set findmergefiles $findmergefiles]
2343 puts $f [list set maxgraphpct $maxgraphpct]
2344 puts $f [list set maxwidth $maxwidth]
2345 puts $f [list set cmitmode $cmitmode]
2346 puts $f [list set wrapcomment $wrapcomment]
2347 puts $f [list set autoselect $autoselect]
2348 puts $f [list set showneartags $showneartags]
2349 puts $f [list set showlocalchanges $showlocalchanges]
2350 puts $f [list set datetimeformat $datetimeformat]
2351 puts $f [list set limitdiffs $limitdiffs]
2352 puts $f [list set bgcolor $bgcolor]
2353 puts $f [list set fgcolor $fgcolor]
2354 puts $f [list set colors $colors]
2355 puts $f [list set diffcolors $diffcolors]
2356 puts $f [list set diffcontext $diffcontext]
2357 puts $f [list set selectbgcolor $selectbgcolor]
2358 puts $f [list set extdifftool $extdifftool]
2360 puts $f "set geometry(main) [wm geometry .]"
2361 puts $f "set geometry(topwidth) [winfo width .tf]"
2362 puts $f "set geometry(topheight) [winfo height .tf]"
2363 puts $f "set geometry(pwsash0) \"[.tf.histframe.pwclist sash coord 0]\""
2364 puts $f "set geometry(pwsash1) \"[.tf.histframe.pwclist sash coord 1]\""
2365 puts $f "set geometry(botwidth) [winfo width .bleft]"
2366 puts $f "set geometry(botheight) [winfo height .bleft]"
2368 puts -nonewline $f "set permviews {"
2369 for {set v 0} {$v < $nextviewnum} {incr v} {
2370 if {$viewperm($v)} {
2371 puts $f "{[list $viewname($v) $viewfiles($v) $viewargs($v) $viewargscmd($v)]}"
2376 file rename -force "~/.gitk-new" "~/.gitk"
2381 proc resizeclistpanes {win w} {
2383 if {[info exists oldwidth($win)]} {
2384 set s0 [$win sash coord 0]
2385 set s1 [$win sash coord 1]
2387 set sash0 [expr {int($w/2 - 2)}]
2388 set sash1 [expr {int($w*5/6 - 2)}]
2390 set factor [expr {1.0 * $w / $oldwidth($win)}]
2391 set sash0 [expr {int($factor * [lindex $s0 0])}]
2392 set sash1 [expr {int($factor * [lindex $s1 0])}]
2396 if {$sash1 < $sash0 + 20} {
2397 set sash1 [expr {$sash0 + 20}]
2399 if {$sash1 > $w - 10} {
2400 set sash1 [expr {$w - 10}]
2401 if {$sash0 > $sash1 - 20} {
2402 set sash0 [expr {$sash1 - 20}]
2406 $win sash place 0 $sash0 [lindex $s0 1]
2407 $win sash place 1 $sash1 [lindex $s1 1]
2409 set oldwidth($win) $w
2412 proc resizecdetpanes {win w} {
2414 if {[info exists oldwidth($win)]} {
2415 set s0 [$win sash coord 0]
2417 set sash0 [expr {int($w*3/4 - 2)}]
2419 set factor [expr {1.0 * $w / $oldwidth($win)}]
2420 set sash0 [expr {int($factor * [lindex $s0 0])}]
2424 if {$sash0 > $w - 15} {
2425 set sash0 [expr {$w - 15}]
2428 $win sash place 0 $sash0 [lindex $s0 1]
2430 set oldwidth($win) $w
2433 proc allcanvs args {
2434 global canv canv2 canv3
2440 proc bindall {event action} {
2441 global canv canv2 canv3
2442 bind $canv $event $action
2443 bind $canv2 $event $action
2444 bind $canv3 $event $action
2450 if {[winfo exists $w]} {
2455 wm title $w [mc "About gitk"]
2456 message $w.m -text [mc "
2457 Gitk - a commit viewer for git
2459 Copyright © 2005-2008 Paul Mackerras
2461 Use and redistribute under the terms of the GNU General Public License"] \
2462 -justify center -aspect 400 -border 2 -bg white -relief groove
2463 pack $w.m -side top -fill x -padx 2 -pady 2
2464 button $w.ok -text [mc "Close"] -command "destroy $w" -default active
2465 pack $w.ok -side bottom
2466 bind $w <Visibility> "focus $w.ok"
2467 bind $w <Key-Escape> "destroy $w"
2468 bind $w <Key-Return> "destroy $w"
2473 if {[winfo exists $w]} {
2477 if {[tk windowingsystem] eq {aqua}} {
2483 wm title $w [mc "Gitk key bindings"]
2484 message $w.m -text "
2485 [mc "Gitk key bindings:"]
2487 [mc "<%s-Q> Quit" $M1T]
2488 [mc "<Home> Move to first commit"]
2489 [mc "<End> Move to last commit"]
2490 [mc "<Up>, p, i Move up one commit"]
2491 [mc "<Down>, n, k Move down one commit"]
2492 [mc "<Left>, z, j Go back in history list"]
2493 [mc "<Right>, x, l Go forward in history list"]
2494 [mc "<PageUp> Move up one page in commit list"]
2495 [mc "<PageDown> Move down one page in commit list"]
2496 [mc "<%s-Home> Scroll to top of commit list" $M1T]
2497 [mc "<%s-End> Scroll to bottom of commit list" $M1T]
2498 [mc "<%s-Up> Scroll commit list up one line" $M1T]
2499 [mc "<%s-Down> Scroll commit list down one line" $M1T]
2500 [mc "<%s-PageUp> Scroll commit list up one page" $M1T]
2501 [mc "<%s-PageDown> Scroll commit list down one page" $M1T]
2502 [mc "<Shift-Up> Find backwards (upwards, later commits)"]
2503 [mc "<Shift-Down> Find forwards (downwards, earlier commits)"]
2504 [mc "<Delete>, b Scroll diff view up one page"]
2505 [mc "<Backspace> Scroll diff view up one page"]
2506 [mc "<Space> Scroll diff view down one page"]
2507 [mc "u Scroll diff view up 18 lines"]
2508 [mc "d Scroll diff view down 18 lines"]
2509 [mc "<%s-F> Find" $M1T]
2510 [mc "<%s-G> Move to next find hit" $M1T]
2511 [mc "<Return> Move to next find hit"]
2512 [mc "/ Move to next find hit, or redo find"]
2513 [mc "? Move to previous find hit"]
2514 [mc "f Scroll diff view to next file"]
2515 [mc "<%s-S> Search for next hit in diff view" $M1T]
2516 [mc "<%s-R> Search for previous hit in diff view" $M1T]
2517 [mc "<%s-KP+> Increase font size" $M1T]
2518 [mc "<%s-plus> Increase font size" $M1T]
2519 [mc "<%s-KP-> Decrease font size" $M1T]
2520 [mc "<%s-minus> Decrease font size" $M1T]
2523 -justify left -bg white -border 2 -relief groove
2524 pack $w.m -side top -fill both -padx 2 -pady 2
2525 button $w.ok -text [mc "Close"] -command "destroy $w" -default active
2526 pack $w.ok -side bottom
2527 bind $w <Visibility> "focus $w.ok"
2528 bind $w <Key-Escape> "destroy $w"
2529 bind $w <Key-Return> "destroy $w"
2532 # Procedures for manipulating the file list window at the
2533 # bottom right of the overall window.
2535 proc treeview {w l openlevs} {
2536 global treecontents treediropen treeheight treeparent treeindex
2546 set treecontents() {}
2547 $w conf -state normal
2549 while {[string range $f 0 $prefixend] ne $prefix} {
2550 if {$lev <= $openlevs} {
2551 $w mark set e:$treeindex($prefix) "end -1c"
2552 $w mark gravity e:$treeindex($prefix) left
2554 set treeheight($prefix) $ht
2555 incr ht [lindex $htstack end]
2556 set htstack [lreplace $htstack end end]
2557 set prefixend [lindex $prefendstack end]
2558 set prefendstack [lreplace $prefendstack end end]
2559 set prefix [string range $prefix 0 $prefixend]
2562 set tail [string range $f [expr {$prefixend+1}] end]
2563 while {[set slash [string first "/" $tail]] >= 0} {
2566 lappend prefendstack $prefixend
2567 incr prefixend [expr {$slash + 1}]
2568 set d [string range $tail 0 $slash]
2569 lappend treecontents($prefix) $d
2570 set oldprefix $prefix
2572 set treecontents($prefix) {}
2573 set treeindex($prefix) [incr ix]
2574 set treeparent($prefix) $oldprefix
2575 set tail [string range $tail [expr {$slash+1}] end]
2576 if {$lev <= $openlevs} {
2578 set treediropen($prefix) [expr {$lev < $openlevs}]
2579 set bm [expr {$lev == $openlevs? "tri-rt": "tri-dn"}]
2580 $w mark set d:$ix "end -1c"
2581 $w mark gravity d:$ix left
2583 for {set i 0} {$i < $lev} {incr i} {append str "\t"}
2585 $w image create end -align center -image $bm -padx 1 \
2587 $w insert end $d [highlight_tag $prefix]
2588 $w mark set s:$ix "end -1c"
2589 $w mark gravity s:$ix left
2594 if {$lev <= $openlevs} {
2597 for {set i 0} {$i < $lev} {incr i} {append str "\t"}
2599 $w insert end $tail [highlight_tag $f]
2601 lappend treecontents($prefix) $tail
2604 while {$htstack ne {}} {
2605 set treeheight($prefix) $ht
2606 incr ht [lindex $htstack end]
2607 set htstack [lreplace $htstack end end]
2608 set prefixend [lindex $prefendstack end]
2609 set prefendstack [lreplace $prefendstack end end]
2610 set prefix [string range $prefix 0 $prefixend]
2612 $w conf -state disabled
2615 proc linetoelt {l} {
2616 global treeheight treecontents
2621 foreach e $treecontents($prefix) {
2626 if {[string index $e end] eq "/"} {
2627 set n $treeheight($prefix$e)
2639 proc highlight_tree {y prefix} {
2640 global treeheight treecontents cflist
2642 foreach e $treecontents($prefix) {
2644 if {[highlight_tag $path] ne {}} {
2645 $cflist tag add bold $y.0 "$y.0 lineend"
2648 if {[string index $e end] eq "/" && $treeheight($path) > 1} {
2649 set y [highlight_tree $y $path]
2655 proc treeclosedir {w dir} {
2656 global treediropen treeheight treeparent treeindex
2658 set ix $treeindex($dir)
2659 $w conf -state normal
2660 $w delete s:$ix e:$ix
2661 set treediropen($dir) 0
2662 $w image configure a:$ix -image tri-rt
2663 $w conf -state disabled
2664 set n [expr {1 - $treeheight($dir)}]
2665 while {$dir ne {}} {
2666 incr treeheight($dir) $n
2667 set dir $treeparent($dir)
2671 proc treeopendir {w dir} {
2672 global treediropen treeheight treeparent treecontents treeindex
2674 set ix $treeindex($dir)
2675 $w conf -state normal
2676 $w image configure a:$ix -image tri-dn
2677 $w mark set e:$ix s:$ix
2678 $w mark gravity e:$ix right
2681 set n [llength $treecontents($dir)]
2682 for {set x $dir} {$x ne {}} {set x $treeparent($x)} {
2685 incr treeheight($x) $n
2687 foreach e $treecontents($dir) {
2689 if {[string index $e end] eq "/"} {
2690 set iy $treeindex($de)
2691 $w mark set d:$iy e:$ix
2692 $w mark gravity d:$iy left
2693 $w insert e:$ix $str
2694 set treediropen($de) 0
2695 $w image create e:$ix -align center -image tri-rt -padx 1 \
2697 $w insert e:$ix $e [highlight_tag $de]
2698 $w mark set s:$iy e:$ix
2699 $w mark gravity s:$iy left
2700 set treeheight($de) 1
2702 $w insert e:$ix $str
2703 $w insert e:$ix $e [highlight_tag $de]
2706 $w mark gravity e:$ix left
2707 $w conf -state disabled
2708 set treediropen($dir) 1
2709 set top [lindex [split [$w index @0,0] .] 0]
2710 set ht [$w cget -height]
2711 set l [lindex [split [$w index s:$ix] .] 0]
2714 } elseif {$l + $n + 1 > $top + $ht} {
2715 set top [expr {$l + $n + 2 - $ht}]
2723 proc treeclick {w x y} {
2724 global treediropen cmitmode ctext cflist cflist_top
2726 if {$cmitmode ne "tree"} return
2727 if {![info exists cflist_top]} return
2728 set l [lindex [split [$w index "@$x,$y"] "."] 0]
2729 $cflist tag remove highlight $cflist_top.0 "$cflist_top.0 lineend"
2730 $cflist tag add highlight $l.0 "$l.0 lineend"
2736 set e [linetoelt $l]
2737 if {[string index $e end] ne "/"} {
2739 } elseif {$treediropen($e)} {
2746 proc setfilelist {id} {
2747 global treefilelist cflist
2749 treeview $cflist $treefilelist($id) 0
2752 image create bitmap tri-rt -background black -foreground blue -data {
2753 #define tri-rt_width 13
2754 #define tri-rt_height 13
2755 static unsigned char tri-rt_bits[] = {
2756 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x30, 0x00, 0x70, 0x00, 0xf0, 0x00,
2757 0xf0, 0x01, 0xf0, 0x00, 0x70, 0x00, 0x30, 0x00, 0x10, 0x00, 0x00, 0x00,
2760 #define tri-rt-mask_width 13
2761 #define tri-rt-mask_height 13
2762 static unsigned char tri-rt-mask_bits[] = {
2763 0x08, 0x00, 0x18, 0x00, 0x38, 0x00, 0x78, 0x00, 0xf8, 0x00, 0xf8, 0x01,
2764 0xf8, 0x03, 0xf8, 0x01, 0xf8, 0x00, 0x78, 0x00, 0x38, 0x00, 0x18, 0x00,
2767 image create bitmap tri-dn -background black -foreground blue -data {
2768 #define tri-dn_width 13
2769 #define tri-dn_height 13
2770 static unsigned char tri-dn_bits[] = {
2771 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x07, 0xf8, 0x03,
2772 0xf0, 0x01, 0xe0, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2775 #define tri-dn-mask_width 13
2776 #define tri-dn-mask_height 13
2777 static unsigned char tri-dn-mask_bits[] = {
2778 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x1f, 0xfe, 0x0f, 0xfc, 0x07,
2779 0xf8, 0x03, 0xf0, 0x01, 0xe0, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
2783 image create bitmap reficon-T -background black -foreground yellow -data {
2784 #define tagicon_width 13
2785 #define tagicon_height 9
2786 static unsigned char tagicon_bits[] = {
2787 0x00, 0x00, 0x00, 0x00, 0xf0, 0x07, 0xf8, 0x07,
2788 0xfc, 0x07, 0xf8, 0x07, 0xf0, 0x07, 0x00, 0x00, 0x00, 0x00};
2790 #define tagicon-mask_width 13
2791 #define tagicon-mask_height 9
2792 static unsigned char tagicon-mask_bits[] = {
2793 0x00, 0x00, 0xf0, 0x0f, 0xf8, 0x0f, 0xfc, 0x0f,
2794 0xfe, 0x0f, 0xfc, 0x0f, 0xf8, 0x0f, 0xf0, 0x0f, 0x00, 0x00};
2797 #define headicon_width 13
2798 #define headicon_height 9
2799 static unsigned char headicon_bits[] = {
2800 0x00, 0x00, 0x00, 0x00, 0xf8, 0x07, 0xf8, 0x07,
2801 0xf8, 0x07, 0xf8, 0x07, 0xf8, 0x07, 0x00, 0x00, 0x00, 0x00};
2804 #define headicon-mask_width 13
2805 #define headicon-mask_height 9
2806 static unsigned char headicon-mask_bits[] = {
2807 0x00, 0x00, 0xfc, 0x0f, 0xfc, 0x0f, 0xfc, 0x0f,
2808 0xfc, 0x0f, 0xfc, 0x0f, 0xfc, 0x0f, 0xfc, 0x0f, 0x00, 0x00};
2810 image create bitmap reficon-H -background black -foreground green \
2811 -data $rectdata -maskdata $rectmask
2812 image create bitmap reficon-o -background black -foreground "#ddddff" \
2813 -data $rectdata -maskdata $rectmask
2815 proc init_flist {first} {
2816 global cflist cflist_top difffilestart
2818 $cflist conf -state normal
2819 $cflist delete 0.0 end
2821 $cflist insert end $first
2823 $cflist tag add highlight 1.0 "1.0 lineend"
2825 catch {unset cflist_top}
2827 $cflist conf -state disabled
2828 set difffilestart {}
2831 proc highlight_tag {f} {
2832 global highlight_paths
2834 foreach p $highlight_paths {
2835 if {[string match $p $f]} {
2842 proc highlight_filelist {} {
2843 global cmitmode cflist
2845 $cflist conf -state normal
2846 if {$cmitmode ne "tree"} {
2847 set end [lindex [split [$cflist index end] .] 0]
2848 for {set l 2} {$l < $end} {incr l} {
2849 set line [$cflist get $l.0 "$l.0 lineend"]
2850 if {[highlight_tag $line] ne {}} {
2851 $cflist tag add bold $l.0 "$l.0 lineend"
2857 $cflist conf -state disabled
2860 proc unhighlight_filelist {} {
2863 $cflist conf -state normal
2864 $cflist tag remove bold 1.0 end
2865 $cflist conf -state disabled
2868 proc add_flist {fl} {
2871 $cflist conf -state normal
2873 $cflist insert end "\n"
2874 $cflist insert end $f [highlight_tag $f]
2876 $cflist conf -state disabled
2879 proc sel_flist {w x y} {
2880 global ctext difffilestart cflist cflist_top cmitmode
2882 if {$cmitmode eq "tree"} return
2883 if {![info exists cflist_top]} return
2884 set l [lindex [split [$w index "@$x,$y"] "."] 0]
2885 $cflist tag remove highlight $cflist_top.0 "$cflist_top.0 lineend"
2886 $cflist tag add highlight $l.0 "$l.0 lineend"
2891 catch {$ctext yview [lindex $difffilestart [expr {$l - 2}]]}
2895 proc pop_flist_menu {w X Y x y} {
2896 global ctext cflist cmitmode flist_menu flist_menu_file
2897 global treediffs diffids
2900 set l [lindex [split [$w index "@$x,$y"] "."] 0]
2902 if {$cmitmode eq "tree"} {
2903 set e [linetoelt $l]
2904 if {[string index $e end] eq "/"} return
2906 set e [lindex $treediffs($diffids) [expr {$l-2}]]
2908 set flist_menu_file $e
2909 set xdiffstate "normal"
2910 if {$cmitmode eq "tree"} {
2911 set xdiffstate "disabled"
2913 # Disable "External diff" item in tree mode
2914 $flist_menu entryconf 2 -state $xdiffstate
2915 tk_popup $flist_menu $X $Y
2918 proc flist_hl {only} {
2919 global flist_menu_file findstring gdttype
2921 set x [shellquote $flist_menu_file]
2922 if {$only || $findstring eq {} || $gdttype ne [mc "touching paths:"]} {
2925 append findstring " " $x
2927 set gdttype [mc "touching paths:"]
2930 proc save_file_from_commit {filename output what} {
2933 if {[catch {exec git show $filename -- > $output} err]} {
2934 if {[string match "fatal: bad revision *" $err]} {
2937 error_popup "Error getting \"$filename\" from $what: $err"
2943 proc external_diff_get_one_file {diffid filename diffdir} {
2944 global nullid nullid2 nullfile
2947 if {$diffid == $nullid} {
2948 set difffile [file join [file dirname $gitdir] $filename]
2949 if {[file exists $difffile]} {
2954 if {$diffid == $nullid2} {
2955 set difffile [file join $diffdir "\[index\] [file tail $filename]"]
2956 return [save_file_from_commit :$filename $difffile index]
2958 set difffile [file join $diffdir "\[$diffid\] [file tail $filename]"]
2959 return [save_file_from_commit $diffid:$filename $difffile \
2963 proc external_diff {} {
2964 global gitktmpdir nullid nullid2
2965 global flist_menu_file
2968 global gitdir extdifftool
2970 if {[llength $diffids] == 1} {
2971 # no reference commit given
2972 set diffidto [lindex $diffids 0]
2973 if {$diffidto eq $nullid} {
2974 # diffing working copy with index
2975 set diffidfrom $nullid2
2976 } elseif {$diffidto eq $nullid2} {
2977 # diffing index with HEAD
2978 set diffidfrom "HEAD"
2980 # use first parent commit
2981 global parentlist selectedline
2982 set diffidfrom [lindex $parentlist $selectedline 0]
2985 set diffidfrom [lindex $diffids 0]
2986 set diffidto [lindex $diffids 1]
2989 # make sure that several diffs wont collide
2990 if {![info exists gitktmpdir]} {
2991 set gitktmpdir [file join [file dirname $gitdir] \
2992 [format ".gitk-tmp.%s" [pid]]]
2993 if {[catch {file mkdir $gitktmpdir} err]} {
2994 error_popup "Error creating temporary directory $gitktmpdir: $err"
3001 set diffdir [file join $gitktmpdir $diffnum]
3002 if {[catch {file mkdir $diffdir} err]} {
3003 error_popup "Error creating temporary directory $diffdir: $err"
3007 # gather files to diff
3008 set difffromfile [external_diff_get_one_file $diffidfrom $flist_menu_file $diffdir]
3009 set difftofile [external_diff_get_one_file $diffidto $flist_menu_file $diffdir]
3011 if {$difffromfile ne {} && $difftofile ne {}} {
3012 set cmd [concat | [shellsplit $extdifftool] \
3013 [list $difffromfile $difftofile]]
3014 if {[catch {set fl [open $cmd r]} err]} {
3015 file delete -force $diffdir
3016 error_popup [mc "$extdifftool: command failed: $err"]
3018 fconfigure $fl -blocking 0
3019 filerun $fl [list delete_at_eof $fl $diffdir]
3024 # delete $dir when we see eof on $f (presumably because the child has exited)
3025 proc delete_at_eof {f dir} {
3026 while {[gets $f line] >= 0} {}
3028 if {[catch {close $f} err]} {
3029 error_popup "External diff viewer failed: $err"
3031 file delete -force $dir
3037 # Functions for adding and removing shell-type quoting
3039 proc shellquote {str} {
3040 if {![string match "*\['\"\\ \t]*" $str]} {
3043 if {![string match "*\['\"\\]*" $str]} {
3046 if {![string match "*'*" $str]} {
3049 return "\"[string map {\" \\\" \\ \\\\} $str]\""
3052 proc shellarglist {l} {
3058 append str [shellquote $a]
3063 proc shelldequote {str} {
3068 if {![regexp -start $used -indices "\['\"\\\\ \t]" $str first]} {
3069 append ret [string range $str $used end]
3070 set used [string length $str]
3073 set first [lindex $first 0]
3074 set ch [string index $str $first]
3075 if {$first > $used} {
3076 append ret [string range $str $used [expr {$first - 1}]]
3079 if {$ch eq " " || $ch eq "\t"} break
3082 set first [string first "'" $str $used]
3084 error "unmatched single-quote"
3086 append ret [string range $str $used [expr {$first - 1}]]
3091 if {$used >= [string length $str]} {
3092 error "trailing backslash"
3094 append ret [string index $str $used]
3099 if {![regexp -start $used -indices "\[\"\\\\]" $str first]} {
3100 error "unmatched double-quote"
3102 set first [lindex $first 0]
3103 set ch [string index $str $first]
3104 if {$first > $used} {
3105 append ret [string range $str $used [expr {$first - 1}]]
3108 if {$ch eq "\""} break
3110 append ret [string index $str $used]
3114 return [list $used $ret]
3117 proc shellsplit {str} {
3120 set str [string trimleft $str]
3121 if {$str eq {}} break
3122 set dq [shelldequote $str]
3123 set n [lindex $dq 0]
3124 set word [lindex $dq 1]
3125 set str [string range $str $n end]
3131 # Code to implement multiple views
3133 proc newview {ishighlight} {
3134 global nextviewnum newviewname newviewperm newishighlight
3135 global newviewargs revtreeargs viewargscmd newviewargscmd curview
3137 set newishighlight $ishighlight
3139 if {[winfo exists $top]} {
3143 set newviewname($nextviewnum) "[mc "View"] $nextviewnum"
3144 set newviewperm($nextviewnum) 0
3145 set newviewargs($nextviewnum) [shellarglist $revtreeargs]
3146 set newviewargscmd($nextviewnum) $viewargscmd($curview)
3147 vieweditor $top $nextviewnum [mc "Gitk view definition"]
3152 global viewname viewperm newviewname newviewperm
3153 global viewargs newviewargs viewargscmd newviewargscmd
3155 set top .gitkvedit-$curview
3156 if {[winfo exists $top]} {
3160 set newviewname($curview) $viewname($curview)
3161 set newviewperm($curview) $viewperm($curview)
3162 set newviewargs($curview) [shellarglist $viewargs($curview)]
3163 set newviewargscmd($curview) $viewargscmd($curview)
3164 vieweditor $top $curview "Gitk: edit view $viewname($curview)"
3167 proc vieweditor {top n title} {
3168 global newviewname newviewperm viewfiles bgcolor
3171 wm title $top $title
3172 label $top.nl -text [mc "Name"]
3173 entry $top.name -width 20 -textvariable newviewname($n)
3174 grid $top.nl $top.name -sticky w -pady 5
3175 checkbutton $top.perm -text [mc "Remember this view"] \
3176 -variable newviewperm($n)
3177 grid $top.perm - -pady 5 -sticky w
3178 message $top.al -aspect 1000 \
3179 -text [mc "Commits to include (arguments to git log):"]
3180 grid $top.al - -sticky w -pady 5
3181 entry $top.args -width 50 -textvariable newviewargs($n) \
3182 -background $bgcolor
3183 grid $top.args - -sticky ew -padx 5
3185 message $top.ac -aspect 1000 \
3186 -text [mc "Command to generate more commits to include:"]
3187 grid $top.ac - -sticky w -pady 5
3188 entry $top.argscmd -width 50 -textvariable newviewargscmd($n) \
3190 grid $top.argscmd - -sticky ew -padx 5
3192 message $top.l -aspect 1000 \
3193 -text [mc "Enter files and directories to include, one per line:"]
3194 grid $top.l - -sticky w
3195 text $top.t -width 40 -height 10 -background $bgcolor -font uifont
3196 if {[info exists viewfiles($n)]} {
3197 foreach f $viewfiles($n) {
3198 $top.t insert end $f
3199 $top.t insert end "\n"
3201 $top.t delete {end - 1c} end
3202 $top.t mark set insert 0.0
3204 grid $top.t - -sticky ew -padx 5
3206 button $top.buts.ok -text [mc "OK"] -command [list newviewok $top $n]
3207 button $top.buts.can -text [mc "Cancel"] -command [list destroy $top]
3208 grid $top.buts.ok $top.buts.can
3209 grid columnconfigure $top.buts 0 -weight 1 -uniform a
3210 grid columnconfigure $top.buts 1 -weight 1 -uniform a
3211 grid $top.buts - -pady 10 -sticky ew
3215 proc doviewmenu {m first cmd op argv} {
3216 set nmenu [$m index end]
3217 for {set i $first} {$i <= $nmenu} {incr i} {
3218 if {[$m entrycget $i -command] eq $cmd} {
3219 eval $m $op $i $argv
3225 proc allviewmenus {n op args} {
3228 doviewmenu .bar.view 5 [list showview $n] $op $args
3229 # doviewmenu $viewhlmenu 1 [list addvhighlight $n] $op $args
3232 proc newviewok {top n} {
3233 global nextviewnum newviewperm newviewname newishighlight
3234 global viewname viewfiles viewperm selectedview curview
3235 global viewargs newviewargs viewargscmd newviewargscmd viewhlmenu
3238 set newargs [shellsplit $newviewargs($n)]
3240 error_popup "[mc "Error in commit selection arguments:"] $err"
3246 foreach f [split [$top.t get 0.0 end] "\n"] {
3247 set ft [string trim $f]
3252 if {![info exists viewfiles($n)]} {
3253 # creating a new view
3255 set viewname($n) $newviewname($n)
3256 set viewperm($n) $newviewperm($n)
3257 set viewfiles($n) $files
3258 set viewargs($n) $newargs
3259 set viewargscmd($n) $newviewargscmd($n)
3261 if {!$newishighlight} {
3264 run addvhighlight $n
3267 # editing an existing view
3268 set viewperm($n) $newviewperm($n)
3269 if {$newviewname($n) ne $viewname($n)} {
3270 set viewname($n) $newviewname($n)
3271 doviewmenu .bar.view 5 [list showview $n] \
3272 entryconf [list -label $viewname($n)]
3273 # doviewmenu $viewhlmenu 1 [list addvhighlight $n] \
3274 # entryconf [list -label $viewname($n) -value $viewname($n)]
3276 if {$files ne $viewfiles($n) || $newargs ne $viewargs($n) || \
3277 $newviewargscmd($n) ne $viewargscmd($n)} {
3278 set viewfiles($n) $files
3279 set viewargs($n) $newargs
3280 set viewargscmd($n) $newviewargscmd($n)
3281 if {$curview == $n} {
3286 catch {destroy $top}
3290 global curview viewperm hlview selectedhlview
3292 if {$curview == 0} return
3293 if {[info exists hlview] && $hlview == $curview} {
3294 set selectedhlview [mc "None"]
3297 allviewmenus $curview delete
3298 set viewperm($curview) 0
3302 proc addviewmenu {n} {
3303 global viewname viewhlmenu
3305 .bar.view add radiobutton -label $viewname($n) \
3306 -command [list showview $n] -variable selectedview -value $n
3307 #$viewhlmenu add radiobutton -label $viewname($n) \
3308 # -command [list addvhighlight $n] -variable selectedhlview
3312 global curview cached_commitrow ordertok
3313 global displayorder parentlist rowidlist rowisopt rowfinal
3314 global colormap rowtextx nextcolor canvxmax
3315 global numcommits viewcomplete
3316 global selectedline currentid canv canvy0
3318 global pending_select mainheadid
3321 global hlview selectedhlview commitinterest
3323 if {$n == $curview} return
3325 set ymax [lindex [$canv cget -scrollregion] 3]
3326 set span [$canv yview]
3327 set ytop [expr {[lindex $span 0] * $ymax}]
3328 set ybot [expr {[lindex $span 1] * $ymax}]
3329 set yscreen [expr {($ybot - $ytop) / 2}]
3330 if {$selectedline ne {}} {
3331 set selid $currentid
3332 set y [yc $selectedline]
3333 if {$ytop < $y && $y < $ybot} {
3334 set yscreen [expr {$y - $ytop}]
3336 } elseif {[info exists pending_select]} {
3337 set selid $pending_select
3338 unset pending_select
3342 catch {unset treediffs}
3344 if {[info exists hlview] && $hlview == $n} {
3346 set selectedhlview [mc "None"]
3348 catch {unset commitinterest}
3349 catch {unset cached_commitrow}
3350 catch {unset ordertok}
3354 .bar.view entryconf [mc "Edit view..."] -state [expr {$n == 0? "disabled": "normal"}]
3355 .bar.view entryconf [mc "Delete view"] -state [expr {$n == 0? "disabled": "normal"}]
3358 if {![info exists viewcomplete($n)]} {
3368 set numcommits $commitidx($n)
3370 catch {unset colormap}
3371 catch {unset rowtextx}
3373 set canvxmax [$canv cget -width]
3379 if {$selid ne {} && [commitinview $selid $n]} {
3380 set row [rowofcommit $selid]
3381 # try to get the selected row in the same position on the screen
3382 set ymax [lindex [$canv cget -scrollregion] 3]
3383 set ytop [expr {[yc $row] - $yscreen}]
3387 set yf [expr {$ytop * 1.0 / $ymax}]
3389 allcanvs yview moveto $yf
3393 } elseif {!$viewcomplete($n)} {
3394 reset_pending_select $selid
3396 reset_pending_select {}
3398 if {[commitinview $pending_select $curview]} {
3399 selectline [rowofcommit $pending_select] 1
3401 set row [first_real_row]
3402 if {$row < $numcommits} {
3407 if {!$viewcomplete($n)} {
3408 if {$numcommits == 0} {
3409 show_status [mc "Reading commits..."]
3411 } elseif {$numcommits == 0} {
3412 show_status [mc "No commits selected"]
3416 # Stuff relating to the highlighting facility
3418 proc ishighlighted {id} {
3419 global vhighlights fhighlights nhighlights rhighlights
3421 if {[info exists nhighlights($id)] && $nhighlights($id) > 0} {
3422 return $nhighlights($id)
3424 if {[info exists vhighlights($id)] && $vhighlights($id) > 0} {
3425 return $vhighlights($id)
3427 if {[info exists fhighlights($id)] && $fhighlights($id) > 0} {
3428 return $fhighlights($id)
3430 if {[info exists rhighlights($id)] && $rhighlights($id) > 0} {
3431 return $rhighlights($id)
3436 proc bolden {row font} {
3437 global canv linehtag selectedline boldrows
3439 lappend boldrows $row
3440 $canv itemconf $linehtag($row) -font $font
3441 if {$row == $selectedline} {
3443 set t [eval $canv create rect [$canv bbox $linehtag($row)] \
3444 -outline {{}} -tags secsel \
3445 -fill [$canv cget -selectbackground]]
3450 proc bolden_name {row font} {
3451 global canv2 linentag selectedline boldnamerows
3453 lappend boldnamerows $row
3454 $canv2 itemconf $linentag($row) -font $font
3455 if {$row == $selectedline} {
3456 $canv2 delete secsel
3457 set t [eval $canv2 create rect [$canv2 bbox $linentag($row)] \
3458 -outline {{}} -tags secsel \
3459 -fill [$canv2 cget -selectbackground]]
3468 foreach row $boldrows {
3469 if {![ishighlighted [commitonrow $row]]} {
3470 bolden $row mainfont
3472 lappend stillbold $row
3475 set boldrows $stillbold
3478 proc addvhighlight {n} {
3479 global hlview viewcomplete curview vhl_done commitidx
3481 if {[info exists hlview]} {
3485 if {$n != $curview && ![info exists viewcomplete($n)]} {
3488 set vhl_done $commitidx($hlview)
3489 if {$vhl_done > 0} {
3494 proc delvhighlight {} {
3495 global hlview vhighlights
3497 if {![info exists hlview]} return
3499 catch {unset vhighlights}
3503 proc vhighlightmore {} {
3504 global hlview vhl_done commitidx vhighlights curview
3506 set max $commitidx($hlview)
3507 set vr [visiblerows]
3508 set r0 [lindex $vr 0]
3509 set r1 [lindex $vr 1]
3510 for {set i $vhl_done} {$i < $max} {incr i} {
3511 set id [commitonrow $i $hlview]
3512 if {[commitinview $id $curview]} {
3513 set row [rowofcommit $id]
3514 if {$r0 <= $row && $row <= $r1} {
3515 if {![highlighted $row]} {
3516 bolden $row mainfontbold
3518 set vhighlights($id) 1
3526 proc askvhighlight {row id} {
3527 global hlview vhighlights iddrawn
3529 if {[commitinview $id $hlview]} {
3530 if {[info exists iddrawn($id)] && ![ishighlighted $id]} {
3531 bolden $row mainfontbold
3533 set vhighlights($id) 1
3535 set vhighlights($id) 0
3539 proc hfiles_change {} {
3540 global highlight_files filehighlight fhighlights fh_serial
3541 global highlight_paths gdttype
3543 if {[info exists filehighlight]} {
3544 # delete previous highlights
3545 catch {close $filehighlight}
3547 catch {unset fhighlights}
3549 unhighlight_filelist
3551 set highlight_paths {}
3552 after cancel do_file_hl $fh_serial
3554 if {$highlight_files ne {}} {
3555 after 300 do_file_hl $fh_serial
3559 proc gdttype_change {name ix op} {
3560 global gdttype highlight_files findstring findpattern
3563 if {$findstring ne {}} {
3564 if {$gdttype eq [mc "containing:"]} {
3565 if {$highlight_files ne {}} {
3566 set highlight_files {}
3571 if {$findpattern ne {}} {
3575 set highlight_files $findstring
3580 # enable/disable findtype/findloc menus too
3583 proc find_change {name ix op} {
3584 global gdttype findstring highlight_files
3587 if {$gdttype eq [mc "containing:"]} {
3590 if {$highlight_files ne $findstring} {
3591 set highlight_files $findstring
3598 proc findcom_change args {
3599 global nhighlights boldnamerows
3600 global findpattern findtype findstring gdttype
3603 # delete previous highlights, if any
3604 foreach row $boldnamerows {
3605 bolden_name $row mainfont
3608 catch {unset nhighlights}
3611 if {$gdttype ne [mc "containing:"] || $findstring eq {}} {
3613 } elseif {$findtype eq [mc "Regexp"]} {
3614 set findpattern $findstring
3616 set e [string map {"*" "\\*" "?" "\\?" "\[" "\\\[" "\\" "\\\\"} \
3618 set findpattern "*$e*"
3622 proc makepatterns {l} {
3625 set ee [string map {"*" "\\*" "?" "\\?" "\[" "\\\[" "\\" "\\\\"} $e]
3626 if {[string index $ee end] eq "/"} {
3636 proc do_file_hl {serial} {
3637 global highlight_files filehighlight highlight_paths gdttype fhl_list
3639 if {$gdttype eq [mc "touching paths:"]} {
3640 if {[catch {set paths [shellsplit $highlight_files]}]} return
3641 set highlight_paths [makepatterns $paths]
3643 set gdtargs [concat -- $paths]
3644 } elseif {$gdttype eq [mc "adding/removing string:"]} {
3645 set gdtargs [list "-S$highlight_files"]
3647 # must be "containing:", i.e. we're searching commit info
3650 set cmd [concat | git diff-tree -r -s --stdin $gdtargs]
3651 set filehighlight [open $cmd r+]
3652 fconfigure $filehighlight -blocking 0
3653 filerun $filehighlight readfhighlight
3659 proc flushhighlights {} {
3660 global filehighlight fhl_list
3662 if {[info exists filehighlight]} {
3664 puts $filehighlight ""
3665 flush $filehighlight
3669 proc askfilehighlight {row id} {
3670 global filehighlight fhighlights fhl_list
3672 lappend fhl_list $id
3673 set fhighlights($id) -1
3674 puts $filehighlight $id
3677 proc readfhighlight {} {
3678 global filehighlight fhighlights curview iddrawn
3679 global fhl_list find_dirn
3681 if {![info exists filehighlight]} {
3685 while {[incr nr] <= 100 && [gets $filehighlight line] >= 0} {
3686 set line [string trim $line]
3687 set i [lsearch -exact $fhl_list $line]
3688 if {$i < 0} continue
3689 for {set j 0} {$j < $i} {incr j} {
3690 set id [lindex $fhl_list $j]
3691 set fhighlights($id) 0
3693 set fhl_list [lrange $fhl_list [expr {$i+1}] end]
3694 if {$line eq {}} continue
3695 if {![commitinview $line $curview]} continue
3696 set row [rowofcommit $line]
3697 if {[info exists iddrawn($line)] && ![ishighlighted $line]} {
3698 bolden $row mainfontbold
3700 set fhighlights($line) 1
3702 if {[eof $filehighlight]} {
3704 puts "oops, git diff-tree died"
3705 catch {close $filehighlight}
3709 if {[info exists find_dirn]} {
3715 proc doesmatch {f} {
3716 global findtype findpattern
3718 if {$findtype eq [mc "Regexp"]} {
3719 return [regexp $findpattern $f]
3720 } elseif {$findtype eq [mc "IgnCase"]} {
3721 return [string match -nocase $findpattern $f]
3723 return [string match $findpattern $f]
3727 proc askfindhighlight {row id} {
3728 global nhighlights commitinfo iddrawn
3730 global markingmatches
3732 if {![info exists commitinfo($id)]} {
3735 set info $commitinfo($id)
3737 set fldtypes [list [mc Headline] [mc Author] [mc Date] [mc Committer] [mc CDate] [mc Comments]]
3738 foreach f $info ty $fldtypes {
3739 if {($findloc eq [mc "All fields"] || $findloc eq $ty) &&
3741 if {$ty eq [mc "Author"]} {
3748 if {$isbold && [info exists iddrawn($id)]} {
3749 if {![ishighlighted $id]} {
3750 bolden $row mainfontbold
3752 bolden_name $row mainfontbold
3755 if {$markingmatches} {
3756 markrowmatches $row $id
3759 set nhighlights($id) $isbold
3762 proc markrowmatches {row id} {
3763 global canv canv2 linehtag linentag commitinfo findloc
3765 set headline [lindex $commitinfo($id) 0]
3766 set author [lindex $commitinfo($id) 1]
3767 $canv delete match$row
3768 $canv2 delete match$row
3769 if {$findloc eq [mc "All fields"] || $findloc eq [mc "Headline"]} {
3770 set m [findmatches $headline]
3772 markmatches $canv $row $headline $linehtag($row) $m \
3773 [$canv itemcget $linehtag($row) -font] $row
3776 if {$findloc eq [mc "All fields"] || $findloc eq [mc "Author"]} {
3777 set m [findmatches $author]
3779 markmatches $canv2 $row $author $linentag($row) $m \
3780 [$canv2 itemcget $linentag($row) -font] $row
3785 proc vrel_change {name ix op} {
3786 global highlight_related
3789 if {$highlight_related ne [mc "None"]} {
3794 # prepare for testing whether commits are descendents or ancestors of a
3795 proc rhighlight_sel {a} {
3796 global descendent desc_todo ancestor anc_todo
3797 global highlight_related
3799 catch {unset descendent}
3800 set desc_todo [list $a]
3801 catch {unset ancestor}
3802 set anc_todo [list $a]
3803 if {$highlight_related ne [mc "None"]} {
3809 proc rhighlight_none {} {
3812 catch {unset rhighlights}
3816 proc is_descendent {a} {
3817 global curview children descendent desc_todo
3820 set la [rowofcommit $a]
3824 for {set i 0} {$i < [llength $todo]} {incr i} {
3825 set do [lindex $todo $i]
3826 if {[rowofcommit $do] < $la} {
3827 lappend leftover $do
3830 foreach nk $children($v,$do) {
3831 if {![info exists descendent($nk)]} {
3832 set descendent($nk) 1
3840 set desc_todo [concat $leftover [lrange $todo [expr {$i+1}] end]]
3844 set descendent($a) 0
3845 set desc_todo $leftover
3848 proc is_ancestor {a} {
3849 global curview parents ancestor anc_todo
3852 set la [rowofcommit $a]
3856 for {set i 0} {$i < [llength $todo]} {incr i} {
3857 set do [lindex $todo $i]
3858 if {![commitinview $do $v] || [rowofcommit $do] > $la} {
3859 lappend leftover $do
3862 foreach np $parents($v,$do) {
3863 if {![info exists ancestor($np)]} {
3872 set anc_todo [concat $leftover [lrange $todo [expr {$i+1}] end]]
3877 set anc_todo $leftover
3880 proc askrelhighlight {row id} {
3881 global descendent highlight_related iddrawn rhighlights
3882 global selectedline ancestor
3884 if {$selectedline eq {}} return
3886 if {$highlight_related eq [mc "Descendant"] ||
3887 $highlight_related eq [mc "Not descendant"]} {
3888 if {![info exists descendent($id)]} {
3891 if {$descendent($id) == ($highlight_related eq [mc "Descendant"])} {
3894 } elseif {$highlight_related eq [mc "Ancestor"] ||
3895 $highlight_related eq [mc "Not ancestor"]} {
3896 if {![info exists ancestor($id)]} {
3899 if {$ancestor($id) == ($highlight_related eq [mc "Ancestor"])} {
3903 if {[info exists iddrawn($id)]} {
3904 if {$isbold && ![ishighlighted $id]} {
3905 bolden $row mainfontbold
3908 set rhighlights($id) $isbold
3911 # Graph layout functions
3913 proc shortids {ids} {
3916 if {[llength $id] > 1} {
3917 lappend res [shortids $id]
3918 } elseif {[regexp {^[0-9a-f]{40}$} $id]} {
3919 lappend res [string range $id 0 7]
3930 for {set mask 1} {$mask <= $n} {incr mask $mask} {
3931 if {($n & $mask) != 0} {
3932 set ret [concat $ret $o]
3934 set o [concat $o $o]
3939 proc ordertoken {id} {
3940 global ordertok curview varcid varcstart varctok curview parents children
3941 global nullid nullid2
3943 if {[info exists ordertok($id)]} {
3944 return $ordertok($id)
3949 if {[info exists varcid($curview,$id)]} {
3950 set a $varcid($curview,$id)
3951 set p [lindex $varcstart($curview) $a]
3953 set p [lindex $children($curview,$id) 0]
3955 if {[info exists ordertok($p)]} {
3956 set tok $ordertok($p)
3959 set id [first_real_child $curview,$p]
3962 set tok [lindex $varctok($curview) $varcid($curview,$p)]
3965 if {[llength $parents($curview,$id)] == 1} {
3966 lappend todo [list $p {}]
3968 set j [lsearch -exact $parents($curview,$id) $p]
3970 puts "oops didn't find [shortids $p] in parents of [shortids $id]"
3972 lappend todo [list $p [strrep $j]]
3975 for {set i [llength $todo]} {[incr i -1] >= 0} {} {
3976 set p [lindex $todo $i 0]
3977 append tok [lindex $todo $i 1]
3978 set ordertok($p) $tok
3980 set ordertok($origid) $tok
3984 # Work out where id should go in idlist so that order-token
3985 # values increase from left to right
3986 proc idcol {idlist id {i 0}} {
3987 set t [ordertoken $id]
3991 if {$i >= [llength $idlist] || $t < [ordertoken [lindex $idlist $i]]} {
3992 if {$i > [llength $idlist]} {
3993 set i [llength $idlist]
3995 while {[incr i -1] >= 0 && $t < [ordertoken [lindex $idlist $i]]} {}
3998 if {$t > [ordertoken [lindex $idlist $i]]} {
3999 while {[incr i] < [llength $idlist] &&
4000 $t >= [ordertoken [lindex $idlist $i]]} {}
4006 proc initlayout {} {
4007 global rowidlist rowisopt rowfinal displayorder parentlist
4008 global numcommits canvxmax canv
4010 global colormap rowtextx
4019 set canvxmax [$canv cget -width]
4020 catch {unset colormap}
4021 catch {unset rowtextx}
4025 proc setcanvscroll {} {
4026 global canv canv2 canv3 numcommits linespc canvxmax canvy0
4027 global lastscrollset lastscrollrows
4029 set ymax [expr {$canvy0 + ($numcommits - 0.5) * $linespc + 2}]
4030 $canv conf -scrollregion [list 0 0 $canvxmax $ymax]
4031 $canv2 conf -scrollregion [list 0 0 0 $ymax]
4032 $canv3 conf -scrollregion [list 0 0 0 $ymax]
4033 set lastscrollset [clock clicks -milliseconds]
4034 set lastscrollrows $numcommits
4037 proc visiblerows {} {
4038 global canv numcommits linespc
4040 set ymax [lindex [$canv cget -scrollregion] 3]
4041 if {$ymax eq {} || $ymax == 0} return
4043 set y0 [expr {int([lindex $f 0] * $ymax)}]
4044 set r0 [expr {int(($y0 - 3) / $linespc) - 1}]
4048 set y1 [expr {int([lindex $f 1] * $ymax)}]
4049 set r1 [expr {int(($y1 - 3) / $linespc) + 1}]
4050 if {$r1 >= $numcommits} {
4051 set r1 [expr {$numcommits - 1}]
4053 return [list $r0 $r1]
4056 proc layoutmore {} {
4057 global commitidx viewcomplete curview
4058 global numcommits pending_select curview
4059 global lastscrollset lastscrollrows commitinterest
4061 if {$lastscrollrows < 100 || $viewcomplete($curview) ||
4062 [clock clicks -milliseconds] - $lastscrollset > 500} {
4065 if {[info exists pending_select] &&
4066 [commitinview $pending_select $curview]} {
4068 selectline [rowofcommit $pending_select] 1
4073 proc doshowlocalchanges {} {
4074 global curview mainheadid
4076 if {$mainheadid eq {}} return
4077 if {[commitinview $mainheadid $curview]} {
4080 lappend commitinterest($mainheadid) {dodiffindex}
4084 proc dohidelocalchanges {} {
4085 global nullid nullid2 lserial curview
4087 if {[commitinview $nullid $curview]} {
4088 removefakerow $nullid
4090 if {[commitinview $nullid2 $curview]} {
4091 removefakerow $nullid2
4096 # spawn off a process to do git diff-index --cached HEAD
4097 proc dodiffindex {} {
4098 global lserial showlocalchanges
4101 if {!$showlocalchanges || !$isworktree} return
4103 set fd [open "|git diff-index --cached HEAD" r]
4104 fconfigure $fd -blocking 0
4105 set i [reg_instance $fd]
4106 filerun $fd [list readdiffindex $fd $lserial $i]
4109 proc readdiffindex {fd serial inst} {
4110 global mainheadid nullid nullid2 curview commitinfo commitdata lserial
4113 if {[gets $fd line] < 0} {
4119 # we only need to see one line and we don't really care what it says...
4122 if {$serial != $lserial} {
4126 # now see if there are any local changes not checked in to the index
4127 set fd [open "|git diff-files" r]
4128 fconfigure $fd -blocking 0
4129 set i [reg_instance $fd]
4130 filerun $fd [list readdifffiles $fd $serial $i]
4132 if {$isdiff && ![commitinview $nullid2 $curview]} {
4133 # add the line for the changes in the index to the graph
4134 set hl [mc "Local changes checked in to index but not committed"]
4135 set commitinfo($nullid2) [list $hl {} {} {} {} " $hl\n"]
4136 set commitdata($nullid2) "\n $hl\n"
4137 if {[commitinview $nullid $curview]} {
4138 removefakerow $nullid
4140 insertfakerow $nullid2 $mainheadid
4141 } elseif {!$isdiff && [commitinview $nullid2 $curview]} {
4142 removefakerow $nullid2
4147 proc readdifffiles {fd serial inst} {
4148 global mainheadid nullid nullid2 curview
4149 global commitinfo commitdata lserial
4152 if {[gets $fd line] < 0} {
4158 # we only need to see one line and we don't really care what it says...
4161 if {$serial != $lserial} {
4165 if {$isdiff && ![commitinview $nullid $curview]} {
4166 # add the line for the local diff to the graph
4167 set hl [mc "Local uncommitted changes, not checked in to index"]
4168 set commitinfo($nullid) [list $hl {} {} {} {} " $hl\n"]
4169 set commitdata($nullid) "\n $hl\n"
4170 if {[commitinview $nullid2 $curview]} {
4175 insertfakerow $nullid $p
4176 } elseif {!$isdiff && [commitinview $nullid $curview]} {
4177 removefakerow $nullid
4182 proc nextuse {id row} {
4183 global curview children
4185 if {[info exists children($curview,$id)]} {
4186 foreach kid $children($curview,$id) {
4187 if {![commitinview $kid $curview]} {
4190 if {[rowofcommit $kid] > $row} {
4191 return [rowofcommit $kid]
4195 if {[commitinview $id $curview]} {
4196 return [rowofcommit $id]
4201 proc prevuse {id row} {
4202 global curview children
4205 if {[info exists children($curview,$id)]} {
4206 foreach kid $children($curview,$id) {
4207 if {![commitinview $kid $curview]} break
4208 if {[rowofcommit $kid] < $row} {
4209 set ret [rowofcommit $kid]
4216 proc make_idlist {row} {
4217 global displayorder parentlist uparrowlen downarrowlen mingaplen
4218 global commitidx curview children
4220 set r [expr {$row - $mingaplen - $downarrowlen - 1}]
4224 set ra [expr {$row - $downarrowlen}]
4228 set rb [expr {$row + $uparrowlen}]
4229 if {$rb > $commitidx($curview)} {
4230 set rb $commitidx($curview)
4232 make_disporder $r [expr {$rb + 1}]
4234 for {} {$r < $ra} {incr r} {
4235 set nextid [lindex $displayorder [expr {$r + 1}]]
4236 foreach p [lindex $parentlist $r] {
4237 if {$p eq $nextid} continue
4238 set rn [nextuse $p $r]
4240 $rn <= $r + $downarrowlen + $mingaplen + $uparrowlen} {
4241 lappend ids [list [ordertoken $p] $p]
4245 for {} {$r < $row} {incr r} {
4246 set nextid [lindex $displayorder [expr {$r + 1}]]
4247 foreach p [lindex $parentlist $r] {
4248 if {$p eq $nextid} continue
4249 set rn [nextuse $p $r]
4250 if {$rn < 0 || $rn >= $row} {
4251 lappend ids [list [ordertoken $p] $p]
4255 set id [lindex $displayorder $row]
4256 lappend ids [list [ordertoken $id] $id]
4258 foreach p [lindex $parentlist $r] {
4259 set firstkid [lindex $children($curview,$p) 0]
4260 if {[rowofcommit $firstkid] < $row} {
4261 lappend ids [list [ordertoken $p] $p]
4265 set id [lindex $displayorder $r]
4267 set firstkid [lindex $children($curview,$id) 0]
4268 if {$firstkid ne {} && [rowofcommit $firstkid] < $row} {
4269 lappend ids [list [ordertoken $id] $id]
4274 foreach idx [lsort -unique $ids] {
4275 lappend idlist [lindex $idx 1]
4280 proc rowsequal {a b} {
4281 while {[set i [lsearch -exact $a {}]] >= 0} {
4282 set a [lreplace $a $i $i]
4284 while {[set i [lsearch -exact $b {}]] >= 0} {
4285 set b [lreplace $b $i $i]
4287 return [expr {$a eq $b}]
4290 proc makeupline {id row rend col} {
4291 global rowidlist uparrowlen downarrowlen mingaplen
4293 for {set r $rend} {1} {set r $rstart} {
4294 set rstart [prevuse $id $r]
4295 if {$rstart < 0} return
4296 if {$rstart < $row} break
4298 if {$rstart + $uparrowlen + $mingaplen + $downarrowlen < $rend} {
4299 set rstart [expr {$rend - $uparrowlen - 1}]
4301 for {set r $rstart} {[incr r] <= $row} {} {
4302 set idlist [lindex $rowidlist $r]
4303 if {$idlist ne {} && [lsearch -exact $idlist $id] < 0} {
4304 set col [idcol $idlist $id $col]
4305 lset rowidlist $r [linsert $idlist $col $id]
4311 proc layoutrows {row endrow} {
4312 global rowidlist rowisopt rowfinal displayorder
4313 global uparrowlen downarrowlen maxwidth mingaplen
4314 global children parentlist
4315 global commitidx viewcomplete curview
4317 make_disporder [expr {$row - 1}] [expr {$endrow + $uparrowlen}]
4320 set rm1 [expr {$row - 1}]
4321 foreach id [lindex $rowidlist $rm1] {
4326 set final [lindex $rowfinal $rm1]
4328 for {} {$row < $endrow} {incr row} {
4329 set rm1 [expr {$row - 1}]
4330 if {$rm1 < 0 || $idlist eq {}} {
4331 set idlist [make_idlist $row]
4334 set id [lindex $displayorder $rm1]
4335 set col [lsearch -exact $idlist $id]
4336 set idlist [lreplace $idlist $col $col]
4337 foreach p [lindex $parentlist $rm1] {
4338 if {[lsearch -exact $idlist $p] < 0} {
4339 set col [idcol $idlist $p $col]
4340 set idlist [linsert $idlist $col $p]
4341 # if not the first child, we have to insert a line going up
4342 if {$id ne [lindex $children($curview,$p) 0]} {
4343 makeupline $p $rm1 $row $col
4347 set id [lindex $displayorder $row]
4348 if {$row > $downarrowlen} {
4349 set termrow [expr {$row - $downarrowlen - 1}]
4350 foreach p [lindex $parentlist $termrow] {
4351 set i [lsearch -exact $idlist $p]
4352 if {$i < 0} continue
4353 set nr [nextuse $p $termrow]
4354 if {$nr < 0 || $nr >= $row + $mingaplen + $uparrowlen} {
4355 set idlist [lreplace $idlist $i $i]
4359 set col [lsearch -exact $idlist $id]
4361 set col [idcol $idlist $id]
4362 set idlist [linsert $idlist $col $id]
4363 if {$children($curview,$id) ne {}} {
4364 makeupline $id $rm1 $row $col
4367 set r [expr {$row + $uparrowlen - 1}]
4368 if {$r < $commitidx($curview)} {
4370 foreach p [lindex $parentlist $r] {
4371 if {[lsearch -exact $idlist $p] >= 0} continue
4372 set fk [lindex $children($curview,$p) 0]
4373 if {[rowofcommit $fk] < $row} {
4374 set x [idcol $idlist $p $x]
4375 set idlist [linsert $idlist $x $p]
4378 if {[incr r] < $commitidx($curview)} {
4379 set p [lindex $displayorder $r]
4380 if {[lsearch -exact $idlist $p] < 0} {
4381 set fk [lindex $children($curview,$p) 0]
4382 if {$fk ne {} && [rowofcommit $fk] < $row} {
4383 set x [idcol $idlist $p $x]
4384 set idlist [linsert $idlist $x $p]
4390 if {$final && !$viewcomplete($curview) &&
4391 $row + $uparrowlen + $mingaplen + $downarrowlen
4392 >= $commitidx($curview)} {
4395 set l [llength $rowidlist]
4397 lappend rowidlist $idlist
4399 lappend rowfinal $final
4400 } elseif {$row < $l} {
4401 if {![rowsequal $idlist [lindex $rowidlist $row]]} {
4402 lset rowidlist $row $idlist
4405 lset rowfinal $row $final
4407 set pad [ntimes [expr {$row - $l}] {}]
4408 set rowidlist [concat $rowidlist $pad]
4409 lappend rowidlist $idlist
4410 set rowfinal [concat $rowfinal $pad]
4411 lappend rowfinal $final
4412 set rowisopt [concat $rowisopt [ntimes [expr {$row - $l + 1}] 0]]
4418 proc changedrow {row} {
4419 global displayorder iddrawn rowisopt need_redisplay
4421 set l [llength $rowisopt]
4423 lset rowisopt $row 0
4424 if {$row + 1 < $l} {
4425 lset rowisopt [expr {$row + 1}] 0
4426 if {$row + 2 < $l} {
4427 lset rowisopt [expr {$row + 2}] 0
4431 set id [lindex $displayorder $row]
4432 if {[info exists iddrawn($id)]} {
4433 set need_redisplay 1
4437 proc insert_pad {row col npad} {
4440 set pad [ntimes $npad {}]
4441 set idlist [lindex $rowidlist $row]
4442 set bef [lrange $idlist 0 [expr {$col - 1}]]
4443 set aft [lrange $idlist $col end]
4444 set i [lsearch -exact $aft {}]
4446 set aft [lreplace $aft $i $i]
4448 lset rowidlist $row [concat $bef $pad $aft]
4452 proc optimize_rows {row col endrow} {
4453 global rowidlist rowisopt displayorder curview children
4458 for {} {$row < $endrow} {incr row; set col 0} {
4459 if {[lindex $rowisopt $row]} continue
4461 set y0 [expr {$row - 1}]
4462 set ym [expr {$row - 2}]
4463 set idlist [lindex $rowidlist $row]
4464 set previdlist [lindex $rowidlist $y0]
4465 if {$idlist eq {} || $previdlist eq {}} continue
4467 set pprevidlist [lindex $rowidlist $ym]
4468 if {$pprevidlist eq {}} continue
4474 for {} {$col < [llength $idlist]} {incr col} {
4475 set id [lindex $idlist $col]
4476 if {[lindex $previdlist $col] eq $id} continue
4481 set x0 [lsearch -exact $previdlist $id]
4482 if {$x0 < 0} continue
4483 set z [expr {$x0 - $col}]
4487 set xm [lsearch -exact $pprevidlist $id]
4489 set z0 [expr {$xm - $x0}]
4493 # if row y0 is the first child of $id then it's not an arrow
4494 if {[lindex $children($curview,$id) 0] ne
4495 [lindex $displayorder $y0]} {
4499 if {!$isarrow && $id ne [lindex $displayorder $row] &&
4500 [lsearch -exact [lindex $rowidlist [expr {$row+1}]] $id] < 0} {
4503 # Looking at lines from this row to the previous row,
4504 # make them go straight up if they end in an arrow on
4505 # the previous row; otherwise make them go straight up
4507 if {$z < -1 || ($z < 0 && $isarrow)} {
4508 # Line currently goes left too much;
4509 # insert pads in the previous row, then optimize it
4510 set npad [expr {-1 - $z + $isarrow}]
4511 insert_pad $y0 $x0 $npad
4513 optimize_rows $y0 $x0 $row
4515 set previdlist [lindex $rowidlist $y0]
4516 set x0 [lsearch -exact $previdlist $id]
4517 set z [expr {$x0 - $col}]
4519 set pprevidlist [lindex $rowidlist $ym]
4520 set xm [lsearch -exact $pprevidlist $id]
4521 set z0 [expr {$xm - $x0}]
4523 } elseif {$z > 1 || ($z > 0 && $isarrow)} {
4524 # Line currently goes right too much;
4525 # insert pads in this line
4526 set npad [expr {$z - 1 + $isarrow}]
4527 insert_pad $row $col $npad
4528 set idlist [lindex $rowidlist $row]
4530 set z [expr {$x0 - $col}]
4533 if {$z0 eq {} && !$isarrow && $ym >= 0} {
4534 # this line links to its first child on row $row-2
4535 set id [lindex $displayorder $ym]
4536 set xc [lsearch -exact $pprevidlist $id]
4538 set z0 [expr {$xc - $x0}]
4541 # avoid lines jigging left then immediately right
4542 if {$z0 ne {} && $z < 0 && $z0 > 0} {
4543 insert_pad $y0 $x0 1
4545 optimize_rows $y0 $x0 $row
4546 set previdlist [lindex $rowidlist $y0]
4550 # Find the first column that doesn't have a line going right
4551 for {set col [llength $idlist]} {[incr col -1] >= 0} {} {
4552 set id [lindex $idlist $col]
4553 if {$id eq {}} break
4554 set x0 [lsearch -exact $previdlist $id]
4556 # check if this is the link to the first child
4557 set kid [lindex $displayorder $y0]
4558 if {[lindex $children($curview,$id) 0] eq $kid} {
4559 # it is, work out offset to child
4560 set x0 [lsearch -exact $previdlist $kid]
4563 if {$x0 <= $col} break
4565 # Insert a pad at that column as long as it has a line and
4566 # isn't the last column
4567 if {$x0 >= 0 && [incr col] < [llength $idlist]} {
4568 set idlist [linsert $idlist $col {}]
4569 lset rowidlist $row $idlist
4577 global canvx0 linespc
4578 return [expr {$canvx0 + $col * $linespc}]
4582 global canvy0 linespc
4583 return [expr {$canvy0 + $row * $linespc}]
4586 proc linewidth {id} {
4587 global thickerline lthickness
4590 if {[info exists thickerline] && $id eq $thickerline} {
4591 set wid [expr {2 * $lthickness}]
4596 proc rowranges {id} {
4597 global curview children uparrowlen downarrowlen
4600 set kids $children($curview,$id)
4606 foreach child $kids {
4607 if {![commitinview $child $curview]} break
4608 set row [rowofcommit $child]
4609 if {![info exists prev]} {
4610 lappend ret [expr {$row + 1}]
4612 if {$row <= $prevrow} {
4613 puts "oops children of [shortids $id] out of order [shortids $child] $row <= [shortids $prev] $prevrow"
4615 # see if the line extends the whole way from prevrow to row
4616 if {$row > $prevrow + $uparrowlen + $downarrowlen &&
4617 [lsearch -exact [lindex $rowidlist \
4618 [expr {int(($row + $prevrow) / 2)}]] $id] < 0} {
4619 # it doesn't, see where it ends
4620 set r [expr {$prevrow + $downarrowlen}]
4621 if {[lsearch -exact [lindex $rowidlist $r] $id] < 0} {
4622 while {[incr r -1] > $prevrow &&
4623 [lsearch -exact [lindex $rowidlist $r] $id] < 0} {}
4625 while {[incr r] <= $row &&
4626 [lsearch -exact [lindex $rowidlist $r] $id] >= 0} {}
4630 # see where it starts up again
4631 set r [expr {$row - $uparrowlen}]
4632 if {[lsearch -exact [lindex $rowidlist $r] $id] < 0} {
4633 while {[incr r] < $row &&
4634 [lsearch -exact [lindex $rowidlist $r] $id] < 0} {}
4636 while {[incr r -1] >= $prevrow &&
4637 [lsearch -exact [lindex $rowidlist $r] $id] >= 0} {}
4643 if {$child eq $id} {
4652 proc drawlineseg {id row endrow arrowlow} {
4653 global rowidlist displayorder iddrawn linesegs
4654 global canv colormap linespc curview maxlinelen parentlist
4656 set cols [list [lsearch -exact [lindex $rowidlist $row] $id]]
4657 set le [expr {$row + 1}]
4660 set c [lsearch -exact [lindex $rowidlist $le] $id]
4666 set x [lindex $displayorder $le]
4671 if {[info exists iddrawn($x)] || $le == $endrow} {
4672 set c [lsearch -exact [lindex $rowidlist [expr {$le+1}]] $id]
4688 if {[info exists linesegs($id)]} {
4689 set lines $linesegs($id)
4691 set r0 [lindex $li 0]
4693 if {$r0 == $le && [lindex $li 1] - $row <= $maxlinelen} {
4703 set li [lindex $lines [expr {$i-1}]]
4704 set r1 [lindex $li 1]
4705 if {$r1 == $row && $le - [lindex $li 0] <= $maxlinelen} {
4710 set x [lindex $cols [expr {$le - $row}]]
4711 set xp [lindex $cols [expr {$le - 1 - $row}]]
4712 set dir [expr {$xp - $x}]
4714 set ith [lindex $lines $i 2]
4715 set coords [$canv coords $ith]
4716 set ah [$canv itemcget $ith -arrow]
4717 set arrowhigh [expr {$ah eq "first" || $ah eq "both"}]
4718 set x2 [lindex $cols [expr {$le + 1 - $row}]]
4719 if {$x2 ne {} && $x - $x2 == $dir} {
4720 set coords [lrange $coords 0 end-2]
4723 set coords [list [xc $le $x] [yc $le]]
4726 set itl [lindex $lines [expr {$i-1}] 2]
4727 set al [$canv itemcget $itl -arrow]
4728 set arrowlow [expr {$al eq "last" || $al eq "both"}]
4729 } elseif {$arrowlow} {
4730 if {[lsearch -exact [lindex $rowidlist [expr {$row-1}]] $id] >= 0 ||
4731 [lsearch -exact [lindex $parentlist [expr {$row-1}]] $id] >= 0} {
4735 set arrow [lindex {none first last both} [expr {$arrowhigh + 2*$arrowlow}]]
4736 for {set y $le} {[incr y -1] > $row} {} {
4738 set xp [lindex $cols [expr {$y - 1 - $row}]]
4739 set ndir [expr {$xp - $x}]
4740 if {$dir != $ndir || $xp < 0} {
4741 lappend coords [xc $y $x] [yc $y]
4747 # join parent line to first child
4748 set ch [lindex $displayorder $row]
4749 set xc [lsearch -exact [lindex $rowidlist $row] $ch]
4751 puts "oops: drawlineseg: child $ch not on row $row"
4752 } elseif {$xc != $x} {
4753 if {($arrowhigh && $le == $row + 1) || $dir == 0} {
4754 set d [expr {int(0.5 * $linespc)}]
4757 set x2 [expr {$x1 - $d}]
4759 set x2 [expr {$x1 + $d}]
4762 set y1 [expr {$y2 + $d}]
4763 lappend coords $x1 $y1 $x2 $y2
4764 } elseif {$xc < $x - 1} {
4765 lappend coords [xc $row [expr {$x-1}]] [yc $row]
4766 } elseif {$xc > $x + 1} {
4767 lappend coords [xc $row [expr {$x+1}]] [yc $row]
4771 lappend coords [xc $row $x] [yc $row]
4773 set xn [xc $row $xp]
4775 lappend coords $xn $yn
4779 set t [$canv create line $coords -width [linewidth $id] \
4780 -fill $colormap($id) -tags lines.$id -arrow $arrow]
4783 set lines [linsert $lines $i [list $row $le $t]]
4785 $canv coords $ith $coords
4786 if {$arrow ne $ah} {
4787 $canv itemconf $ith -arrow $arrow
4789 lset lines $i 0 $row
4792 set xo [lsearch -exact [lindex $rowidlist [expr {$row - 1}]] $id]
4793 set ndir [expr {$xo - $xp}]
4794 set clow [$canv coords $itl]
4795 if {$dir == $ndir} {
4796 set clow [lrange $clow 2 end]
4798 set coords [concat $coords $clow]
4800 lset lines [expr {$i-1}] 1 $le
4802 # coalesce two pieces
4804 set b [lindex $lines [expr {$i-1}] 0]
4805 set e [lindex $lines $i 1]
4806 set lines [lreplace $lines [expr {$i-1}] $i [list $b $e $itl]]
4808 $canv coords $itl $coords
4809 if {$arrow ne $al} {
4810 $canv itemconf $itl -arrow $arrow
4814 set linesegs($id) $lines
4818 proc drawparentlinks {id row} {
4819 global rowidlist canv colormap curview parentlist
4820 global idpos linespc
4822 set rowids [lindex $rowidlist $row]
4823 set col [lsearch -exact $rowids $id]
4824 if {$col < 0} return
4825 set olds [lindex $parentlist $row]
4826 set row2 [expr {$row + 1}]
4827 set x [xc $row $col]
4830 set d [expr {int(0.5 * $linespc)}]
4831 set ymid [expr {$y + $d}]
4832 set ids [lindex $rowidlist $row2]
4833 # rmx = right-most X coord used
4836 set i [lsearch -exact $ids $p]
4838 puts "oops, parent $p of $id not in list"
4841 set x2 [xc $row2 $i]
4845 set j [lsearch -exact $rowids $p]
4847 # drawlineseg will do this one for us
4851 # should handle duplicated parents here...
4852 set coords [list $x $y]
4854 # if attaching to a vertical segment, draw a smaller
4855 # slant for visual distinctness
4858 lappend coords [expr {$x2 + $d}] $y $x2 $ymid
4860 lappend coords [expr {$x2 - $d}] $y $x2 $ymid
4862 } elseif {$i < $col && $i < $j} {
4863 # segment slants towards us already
4864 lappend coords [xc $row $j] $y
4866 if {$i < $col - 1} {
4867 lappend coords [expr {$x2 + $linespc}] $y
4868 } elseif {$i > $col + 1} {
4869 lappend coords [expr {$x2 - $linespc}] $y
4871 lappend coords $x2 $y2
4874 lappend coords $x2 $y2
4876 set t [$canv create line $coords -width [linewidth $p] \
4877 -fill $colormap($p) -tags lines.$p]
4881 if {$rmx > [lindex $idpos($id) 1]} {
4882 lset idpos($id) 1 $rmx
4887 proc drawlines {id} {
4890 $canv itemconf lines.$id -width [linewidth $id]
4893 proc drawcmittext {id row col} {
4894 global linespc canv canv2 canv3 fgcolor curview
4895 global cmitlisted commitinfo rowidlist parentlist
4896 global rowtextx idpos idtags idheads idotherrefs
4897 global linehtag linentag linedtag selectedline
4898 global canvxmax boldrows boldnamerows fgcolor
4899 global mainheadid nullid nullid2 circleitem circlecolors
4901 # listed is 0 for boundary, 1 for normal, 2 for negative, 3 for left, 4 for right
4902 set listed $cmitlisted($curview,$id)
4903 if {$id eq $nullid} {
4905 } elseif {$id eq $nullid2} {
4907 } elseif {$id eq $mainheadid} {
4910 set ofill [lindex $circlecolors $listed]
4912 set x [xc $row $col]
4914 set orad [expr {$linespc / 3}]
4916 set t [$canv create oval [expr {$x - $orad}] [expr {$y - $orad}] \
4917 [expr {$x + $orad - 1}] [expr {$y + $orad - 1}] \
4918 -fill $ofill -outline $fgcolor -width 1 -tags circle]
4919 } elseif {$listed == 3} {
4920 # triangle pointing left for left-side commits
4921 set t [$canv create polygon \
4922 [expr {$x - $orad}] $y \
4923 [expr {$x + $orad - 1}] [expr {$y - $orad}] \
4924 [expr {$x + $orad - 1}] [expr {$y + $orad - 1}] \
4925 -fill $ofill -outline $fgcolor -width 1 -tags circle]
4927 # triangle pointing right for right-side commits
4928 set t [$canv create polygon \
4929 [expr {$x + $orad - 1}] $y \
4930 [expr {$x - $orad}] [expr {$y - $orad}] \
4931 [expr {$x - $orad}] [expr {$y + $orad - 1}] \
4932 -fill $ofill -outline $fgcolor -width 1 -tags circle]
4934 set circleitem($row) $t
4936 $canv bind $t <1> {selcanvline {} %x %y}
4937 set rmx [llength [lindex $rowidlist $row]]
4938 set olds [lindex $parentlist $row]
4940 set nextids [lindex $rowidlist [expr {$row + 1}]]
4942 set i [lsearch -exact $nextids $p]
4948 set xt [xc $row $rmx]
4949 set rowtextx($row) $xt
4950 set idpos($id) [list $x $xt $y]
4951 if {[info exists idtags($id)] || [info exists idheads($id)]
4952 || [info exists idotherrefs($id)]} {
4953 set xt [drawtags $id $x $xt $y]
4955 set headline [lindex $commitinfo($id) 0]
4956 set name [lindex $commitinfo($id) 1]
4957 set date [lindex $commitinfo($id) 2]
4958 set date [formatdate $date]
4961 set isbold [ishighlighted $id]
4963 lappend boldrows $row
4964 set font mainfontbold
4966 lappend boldnamerows $row
4967 set nfont mainfontbold
4970 set linehtag($row) [$canv create text $xt $y -anchor w -fill $fgcolor \
4971 -text $headline -font $font -tags text]
4972 $canv bind $linehtag($row) <Button-3> "rowmenu %X %Y $id"
4973 set linentag($row) [$canv2 create text 3 $y -anchor w -fill $fgcolor \
4974 -text $name -font $nfont -tags text]
4975 set linedtag($row) [$canv3 create text 3 $y -anchor w -fill $fgcolor \
4976 -text $date -font mainfont -tags text]
4977 if {$selectedline == $row} {
4980 set xr [expr {$xt + [font measure $font $headline]}]
4981 if {$xr > $canvxmax} {
4987 proc drawcmitrow {row} {
4988 global displayorder rowidlist nrows_drawn
4989 global iddrawn markingmatches
4990 global commitinfo numcommits
4991 global filehighlight fhighlights findpattern nhighlights
4992 global hlview vhighlights
4993 global highlight_related rhighlights
4995 if {$row >= $numcommits} return
4997 set id [lindex $displayorder $row]
4998 if {[info exists hlview] && ![info exists vhighlights($id)]} {
4999 askvhighlight $row $id
5001 if {[info exists filehighlight] && ![info exists fhighlights($id)]} {
5002 askfilehighlight $row $id
5004 if {$findpattern ne {} && ![info exists nhighlights($id)]} {
5005 askfindhighlight $row $id
5007 if {$highlight_related ne [mc "None"] && ![info exists rhighlights($id)]} {
5008 askrelhighlight $row $id
5010 if {![info exists iddrawn($id)]} {
5011 set col [lsearch -exact [lindex $rowidlist $row] $id]
5013 puts "oops, row $row id $id not in list"
5016 if {![info exists commitinfo($id)]} {
5020 drawcmittext $id $row $col
5024 if {$markingmatches} {
5025 markrowmatches $row $id
5029 proc drawcommits {row {endrow {}}} {
5030 global numcommits iddrawn displayorder curview need_redisplay
5031 global parentlist rowidlist rowfinal uparrowlen downarrowlen nrows_drawn
5036 if {$endrow eq {}} {
5039 if {$endrow >= $numcommits} {
5040 set endrow [expr {$numcommits - 1}]
5043 set rl1 [expr {$row - $downarrowlen - 3}]
5047 set ro1 [expr {$row - 3}]
5051 set r2 [expr {$endrow + $uparrowlen + 3}]
5052 if {$r2 > $numcommits} {
5055 for {set r $rl1} {$r < $r2} {incr r} {
5056 if {[lindex $rowidlist $r] ne {} && [lindex $rowfinal $r]} {
5060 set rl1 [expr {$r + 1}]
5066 optimize_rows $ro1 0 $r2
5067 if {$need_redisplay || $nrows_drawn > 2000} {
5072 # make the lines join to already-drawn rows either side
5073 set r [expr {$row - 1}]
5074 if {$r < 0 || ![info exists iddrawn([lindex $displayorder $r])]} {
5077 set er [expr {$endrow + 1}]
5078 if {$er >= $numcommits ||
5079 ![info exists iddrawn([lindex $displayorder $er])]} {
5082 for {} {$r <= $er} {incr r} {
5083 set id [lindex $displayorder $r]
5084 set wasdrawn [info exists iddrawn($id)]
5086 if {$r == $er} break
5087 set nextid [lindex $displayorder [expr {$r + 1}]]
5088 if {$wasdrawn && [info exists iddrawn($nextid)]} continue
5089 drawparentlinks $id $r
5091 set rowids [lindex $rowidlist $r]
5092 foreach lid $rowids {
5093 if {$lid eq {}} continue
5094 if {[info exists lineend($lid)] && $lineend($lid) > $r} continue
5096 # see if this is the first child of any of its parents
5097 foreach p [lindex $parentlist $r] {
5098 if {[lsearch -exact $rowids $p] < 0} {
5099 # make this line extend up to the child
5100 set lineend($p) [drawlineseg $p $r $er 0]
5104 set lineend($lid) [drawlineseg $lid $r $er 1]
5110 proc undolayout {row} {
5111 global uparrowlen mingaplen downarrowlen
5112 global rowidlist rowisopt rowfinal need_redisplay
5114 set r [expr {$row - ($uparrowlen + $mingaplen + $downarrowlen)}]
5118 if {[llength $rowidlist] > $r} {
5120 set rowidlist [lrange $rowidlist 0 $r]
5121 set rowfinal [lrange $rowfinal 0 $r]
5122 set rowisopt [lrange $rowisopt 0 $r]
5123 set need_redisplay 1
5128 proc drawvisible {} {
5129 global canv linespc curview vrowmod selectedline targetrow targetid
5130 global need_redisplay cscroll numcommits
5132 set fs [$canv yview]
5133 set ymax [lindex [$canv cget -scrollregion] 3]
5134 if {$ymax eq {} || $ymax == 0 || $numcommits == 0} return
5135 set f0 [lindex $fs 0]
5136 set f1 [lindex $fs 1]
5137 set y0 [expr {int($f0 * $ymax)}]
5138 set y1 [expr {int($f1 * $ymax)}]
5140 if {[info exists targetid]} {
5141 if {[commitinview $targetid $curview]} {
5142 set r [rowofcommit $targetid]
5143 if {$r != $targetrow} {
5144 # Fix up the scrollregion and change the scrolling position
5145 # now that our target row has moved.
5146 set diff [expr {($r - $targetrow) * $linespc}]
5149 set ymax [lindex [$canv cget -scrollregion] 3]
5152 set f0 [expr {$y0 / $ymax}]
5153 set f1 [expr {$y1 / $ymax}]
5154 allcanvs yview moveto $f0
5155 $cscroll set $f0 $f1
5156 set need_redisplay 1
5163 set row [expr {int(($y0 - 3) / $linespc) - 1}]
5164 set endrow [expr {int(($y1 - 3) / $linespc) + 1}]
5165 if {$endrow >= $vrowmod($curview)} {
5166 update_arcrows $curview
5168 if {$selectedline ne {} &&
5169 $row <= $selectedline && $selectedline <= $endrow} {
5170 set targetrow $selectedline
5171 } elseif {[info exists targetid]} {
5172 set targetrow [expr {int(($row + $endrow) / 2)}]
5174 if {[info exists targetrow]} {
5175 if {$targetrow >= $numcommits} {
5176 set targetrow [expr {$numcommits - 1}]
5178 set targetid [commitonrow $targetrow]
5180 drawcommits $row $endrow
5183 proc clear_display {} {
5184 global iddrawn linesegs need_redisplay nrows_drawn
5185 global vhighlights fhighlights nhighlights rhighlights
5186 global linehtag linentag linedtag boldrows boldnamerows
5189 catch {unset iddrawn}
5190 catch {unset linesegs}
5191 catch {unset linehtag}
5192 catch {unset linentag}
5193 catch {unset linedtag}
5196 catch {unset vhighlights}
5197 catch {unset fhighlights}
5198 catch {unset nhighlights}
5199 catch {unset rhighlights}
5200 set need_redisplay 0
5204 proc findcrossings {id} {
5205 global rowidlist parentlist numcommits displayorder
5209 foreach {s e} [rowranges $id] {
5210 if {$e >= $numcommits} {
5211 set e [expr {$numcommits - 1}]
5213 if {$e <= $s} continue
5214 for {set row $e} {[incr row -1] >= $s} {} {
5215 set x [lsearch -exact [lindex $rowidlist $row] $id]
5217 set olds [lindex $parentlist $row]
5218 set kid [lindex $displayorder $row]
5219 set kidx [lsearch -exact [lindex $rowidlist $row] $kid]
5220 if {$kidx < 0} continue
5221 set nextrow [lindex $rowidlist [expr {$row + 1}]]
5223 set px [lsearch -exact $nextrow $p]
5224 if {$px < 0} continue
5225 if {($kidx < $x && $x < $px) || ($px < $x && $x < $kidx)} {
5226 if {[lsearch -exact $ccross $p] >= 0} continue
5227 if {$x == $px + ($kidx < $px? -1: 1)} {
5229 } elseif {[lsearch -exact $cross $p] < 0} {
5236 return [concat $ccross {{}} $cross]
5239 proc assigncolor {id} {
5240 global colormap colors nextcolor
5241 global parents children children curview
5243 if {[info exists colormap($id)]} return
5244 set ncolors [llength $colors]
5245 if {[info exists children($curview,$id)]} {
5246 set kids $children($curview,$id)
5250 if {[llength $kids] == 1} {
5251 set child [lindex $kids 0]
5252 if {[info exists colormap($child)]
5253 && [llength $parents($curview,$child)] == 1} {
5254 set colormap($id) $colormap($child)
5260 foreach x [findcrossings $id] {
5262 # delimiter between corner crossings and other crossings
5263 if {[llength $badcolors] >= $ncolors - 1} break
5264 set origbad $badcolors
5266 if {[info exists colormap($x)]
5267 && [lsearch -exact $badcolors $colormap($x)] < 0} {
5268 lappend badcolors $colormap($x)
5271 if {[llength $badcolors] >= $ncolors} {
5272 set badcolors $origbad
5274 set origbad $badcolors
5275 if {[llength $badcolors] < $ncolors - 1} {
5276 foreach child $kids {
5277 if {[info exists colormap($child)]
5278 && [lsearch -exact $badcolors $colormap($child)] < 0} {
5279 lappend badcolors $colormap($child)
5281 foreach p $parents($curview,$child) {
5282 if {[info exists colormap($p)]
5283 && [lsearch -exact $badcolors $colormap($p)] < 0} {
5284 lappend badcolors $colormap($p)
5288 if {[llength $badcolors] >= $ncolors} {
5289 set badcolors $origbad
5292 for {set i 0} {$i <= $ncolors} {incr i} {
5293 set c [lindex $colors $nextcolor]
5294 if {[incr nextcolor] >= $ncolors} {
5297 if {[lsearch -exact $badcolors $c]} break
5299 set colormap($id) $c
5302 proc bindline {t id} {
5305 $canv bind $t <Enter> "lineenter %x %y $id"
5306 $canv bind $t <Motion> "linemotion %x %y $id"
5307 $canv bind $t <Leave> "lineleave $id"
5308 $canv bind $t <Button-1> "lineclick %x %y $id 1"
5311 proc drawtags {id x xt y1} {
5312 global idtags idheads idotherrefs mainhead
5313 global linespc lthickness
5314 global canv rowtextx curview fgcolor bgcolor
5319 if {[info exists idtags($id)]} {
5320 set marks $idtags($id)
5321 set ntags [llength $marks]
5323 if {[info exists idheads($id)]} {
5324 set marks [concat $marks $idheads($id)]
5325 set nheads [llength $idheads($id)]
5327 if {[info exists idotherrefs($id)]} {
5328 set marks [concat $marks $idotherrefs($id)]
5334 set delta [expr {int(0.5 * ($linespc - $lthickness))}]
5335 set yt [expr {$y1 - 0.5 * $linespc}]
5336 set yb [expr {$yt + $linespc - 1}]
5340 foreach tag $marks {
5342 if {$i >= $ntags && $i < $ntags + $nheads && $tag eq $mainhead} {
5343 set wid [font measure mainfontbold $tag]
5345 set wid [font measure mainfont $tag]
5349 set xt [expr {$xt + $delta + $wid + $lthickness + $linespc}]
5351 set t [$canv create line $x $y1 [lindex $xvals end] $y1 \
5352 -width $lthickness -fill black -tags tag.$id]
5354 foreach tag $marks x $xvals wid $wvals {
5355 set xl [expr {$x + $delta}]
5356 set xr [expr {$x + $delta + $wid + $lthickness}]
5358 if {[incr ntags -1] >= 0} {
5360 set t [$canv create polygon $x [expr {$yt + $delta}] $xl $yt \
5361 $xr $yt $xr $yb $xl $yb $x [expr {$yb - $delta}] \
5362 -width 1 -outline black -fill yellow -tags tag.$id]
5363 $canv bind $t <1> [list showtag $tag 1]
5364 set rowtextx([rowofcommit $id]) [expr {$xr + $linespc}]
5366 # draw a head or other ref
5367 if {[incr nheads -1] >= 0} {
5369 if {$tag eq $mainhead} {
5370 set font mainfontbold
5375 set xl [expr {$xl - $delta/2}]
5376 $canv create polygon $x $yt $xr $yt $xr $yb $x $yb \
5377 -width 1 -outline black -fill $col -tags tag.$id
5378 if {[regexp {^(remotes/.*/|remotes/)} $tag match remoteprefix]} {
5379 set rwid [font measure mainfont $remoteprefix]
5380 set xi [expr {$x + 1}]
5381 set yti [expr {$yt + 1}]
5382 set xri [expr {$x + $rwid}]
5383 $canv create polygon $xi $yti $xri $yti $xri $yb $xi $yb \
5384 -width 0 -fill "#ffddaa" -tags tag.$id
5387 set t [$canv create text $xl $y1 -anchor w -text $tag -fill $fgcolor \
5388 -font $font -tags [list tag.$id text]]
5390 $canv bind $t <1> [list showtag $tag 1]
5391 } elseif {$nheads >= 0} {
5392 $canv bind $t <Button-3> [list headmenu %X %Y $id $tag]
5398 proc xcoord {i level ln} {
5399 global canvx0 xspc1 xspc2
5401 set x [expr {$canvx0 + $i * $xspc1($ln)}]
5402 if {$i > 0 && $i == $level} {
5403 set x [expr {$x + 0.5 * ($xspc2 - $xspc1($ln))}]
5404 } elseif {$i > $level} {
5405 set x [expr {$x + $xspc2 - $xspc1($ln)}]
5410 proc show_status {msg} {
5414 $canv create text 3 3 -anchor nw -text $msg -font mainfont \
5415 -tags text -fill $fgcolor
5418 # Don't change the text pane cursor if it is currently the hand cursor,
5419 # showing that we are over a sha1 ID link.
5420 proc settextcursor {c} {
5421 global ctext curtextcursor
5423 if {[$ctext cget -cursor] == $curtextcursor} {
5424 $ctext config -cursor $c
5426 set curtextcursor $c
5429 proc nowbusy {what {name {}}} {
5430 global isbusy busyname statusw
5432 if {[array names isbusy] eq {}} {
5433 . config -cursor watch
5437 set busyname($what) $name
5439 $statusw conf -text $name
5443 proc notbusy {what} {
5444 global isbusy maincursor textcursor busyname statusw
5448 if {$busyname($what) ne {} &&
5449 [$statusw cget -text] eq $busyname($what)} {
5450 $statusw conf -text {}
5453 if {[array names isbusy] eq {}} {
5454 . config -cursor $maincursor
5455 settextcursor $textcursor
5459 proc findmatches {f} {
5460 global findtype findstring
5461 if {$findtype == [mc "Regexp"]} {
5462 set matches [regexp -indices -all -inline $findstring $f]
5465 if {$findtype == [mc "IgnCase"]} {
5466 set f [string tolower $f]
5467 set fs [string tolower $fs]
5471 set l [string length $fs]
5472 while {[set j [string first $fs $f $i]] >= 0} {
5473 lappend matches [list $j [expr {$j+$l-1}]]
5474 set i [expr {$j + $l}]
5480 proc dofind {{dirn 1} {wrap 1}} {
5481 global findstring findstartline findcurline selectedline numcommits
5482 global gdttype filehighlight fh_serial find_dirn findallowwrap
5484 if {[info exists find_dirn]} {
5485 if {$find_dirn == $dirn} return
5489 if {$findstring eq {} || $numcommits == 0} return
5490 if {$selectedline eq {}} {
5491 set findstartline [lindex [visiblerows] [expr {$dirn < 0}]]
5493 set findstartline $selectedline
5495 set findcurline $findstartline
5496 nowbusy finding [mc "Searching"]
5497 if {$gdttype ne [mc "containing:"] && ![info exists filehighlight]} {
5498 after cancel do_file_hl $fh_serial
5499 do_file_hl $fh_serial
5502 set findallowwrap $wrap
5506 proc stopfinding {} {
5507 global find_dirn findcurline fprogcoord
5509 if {[info exists find_dirn]} {
5519 global commitdata commitinfo numcommits findpattern findloc
5520 global findstartline findcurline findallowwrap
5521 global find_dirn gdttype fhighlights fprogcoord
5522 global curview varcorder vrownum varccommits vrowmod
5524 if {![info exists find_dirn]} {
5527 set fldtypes [list [mc "Headline"] [mc "Author"] [mc "Date"] [mc "Committer"] [mc "CDate"] [mc "Comments"]]
5530 if {$find_dirn > 0} {
5532 if {$l >= $numcommits} {
5535 if {$l <= $findstartline} {
5536 set lim [expr {$findstartline + 1}]
5539 set moretodo $findallowwrap
5546 if {$l >= $findstartline} {
5547 set lim [expr {$findstartline - 1}]
5550 set moretodo $findallowwrap
5553 set n [expr {($lim - $l) * $find_dirn}]
5558 if {$l + ($find_dirn > 0? $n: 1) > $vrowmod($curview)} {
5559 update_arcrows $curview
5563 set ai [bsearch $vrownum($curview) $l]
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]}]
5568 if {$gdttype eq [mc "containing:"]} {
5569 for {} {$n > 0} {incr n -1; incr l $find_dirn} {
5570 if {$l < $arow || $l >= $arowend} {
5572 set a [lindex $varcorder($curview) $ai]
5573 set arow [lindex $vrownum($curview) $ai]
5574 set ids [lindex $varccommits($curview,$a)]
5575 set arowend [expr {$arow + [llength $ids]}]
5577 set id [lindex $ids [expr {$l - $arow}]]
5578 # shouldn't happen unless git log doesn't give all the commits...
5579 if {![info exists commitdata($id)] ||
5580 ![doesmatch $commitdata($id)]} {
5583 if {![info exists commitinfo($id)]} {
5586 set info $commitinfo($id)
5587 foreach f $info ty $fldtypes {
5588 if {($findloc eq [mc "All fields"] || $findloc eq $ty) &&
5597 for {} {$n > 0} {incr n -1; incr l $find_dirn} {
5598 if {$l < $arow || $l >= $arowend} {
5600 set a [lindex $varcorder($curview) $ai]
5601 set arow [lindex $vrownum($curview) $ai]
5602 set ids [lindex $varccommits($curview,$a)]
5603 set arowend [expr {$arow + [llength $ids]}]
5605 set id [lindex $ids [expr {$l - $arow}]]
5606 if {![info exists fhighlights($id)]} {
5607 # this sets fhighlights($id) to -1
5608 askfilehighlight $l $id
5610 if {$fhighlights($id) > 0} {
5614 if {$fhighlights($id) < 0} {
5617 set findcurline [expr {$l - $find_dirn}]
5622 if {$found || ($domore && !$moretodo)} {
5638 set findcurline [expr {$l - $find_dirn}]
5640 set n [expr {($findcurline - $findstartline) * $find_dirn - 1}]
5644 set fprogcoord [expr {$n * 1.0 / $numcommits}]
5649 proc findselectline {l} {
5650 global findloc commentend ctext findcurline markingmatches gdttype
5652 set markingmatches 1
5655 if {$findloc == [mc "All fields"] || $findloc == [mc "Comments"]} {
5656 # highlight the matches in the comments
5657 set f [$ctext get 1.0 $commentend]
5658 set matches [findmatches $f]
5659 foreach match $matches {
5660 set start [lindex $match 0]
5661 set end [expr {[lindex $match 1] + 1}]
5662 $ctext tag add found "1.0 + $start c" "1.0 + $end c"
5668 # mark the bits of a headline or author that match a find string
5669 proc markmatches {canv l str tag matches font row} {
5672 set bbox [$canv bbox $tag]
5673 set x0 [lindex $bbox 0]
5674 set y0 [lindex $bbox 1]
5675 set y1 [lindex $bbox 3]
5676 foreach match $matches {
5677 set start [lindex $match 0]
5678 set end [lindex $match 1]
5679 if {$start > $end} continue
5680 set xoff [font measure $font [string range $str 0 [expr {$start-1}]]]
5681 set xlen [font measure $font [string range $str 0 [expr {$end}]]]
5682 set t [$canv create rect [expr {$x0+$xoff}] $y0 \
5683 [expr {$x0+$xlen+2}] $y1 \
5684 -outline {} -tags [list match$l matches] -fill yellow]
5686 if {$row == $selectedline} {
5687 $canv raise $t secsel
5692 proc unmarkmatches {} {
5693 global markingmatches
5695 allcanvs delete matches
5696 set markingmatches 0
5700 proc selcanvline {w x y} {
5701 global canv canvy0 ctext linespc
5703 set ymax [lindex [$canv cget -scrollregion] 3]
5704 if {$ymax == {}} return
5705 set yfrac [lindex [$canv yview] 0]
5706 set y [expr {$y + $yfrac * $ymax}]
5707 set l [expr {int(($y - $canvy0) / $linespc + 0.5)}]
5712 set xmax [lindex [$canv cget -scrollregion] 2]
5713 set xleft [expr {[lindex [$canv xview] 0] * $xmax}]
5714 if {![info exists rowtextx($l)] || $xleft + $x < $rowtextx($l)} return
5720 proc commit_descriptor {p} {
5722 if {![info exists commitinfo($p)]} {
5726 if {[llength $commitinfo($p)] > 1} {
5727 set l [lindex $commitinfo($p) 0]
5732 # append some text to the ctext widget, and make any SHA1 ID
5733 # that we know about be a clickable link.
5734 proc appendwithlinks {text tags} {
5735 global ctext linknum curview pendinglinks
5737 set start [$ctext index "end - 1c"]
5738 $ctext insert end $text $tags
5739 set links [regexp -indices -all -inline {[0-9a-f]{40}} $text]
5743 set linkid [string range $text $s $e]
5745 $ctext tag delete link$linknum
5746 $ctext tag add link$linknum "$start + $s c" "$start + $e c"
5747 setlink $linkid link$linknum
5752 proc setlink {id lk} {
5753 global curview ctext pendinglinks commitinterest
5755 if {[commitinview $id $curview]} {
5756 $ctext tag conf $lk -foreground blue -underline 1
5757 $ctext tag bind $lk <1> [list selectline [rowofcommit $id] 1]
5758 $ctext tag bind $lk <Enter> {linkcursor %W 1}
5759 $ctext tag bind $lk <Leave> {linkcursor %W -1}
5761 lappend pendinglinks($id) $lk
5762 lappend commitinterest($id) {makelink %I}
5766 proc makelink {id} {
5769 if {![info exists pendinglinks($id)]} return
5770 foreach lk $pendinglinks($id) {
5773 unset pendinglinks($id)
5776 proc linkcursor {w inc} {
5777 global linkentercount curtextcursor
5779 if {[incr linkentercount $inc] > 0} {
5780 $w configure -cursor hand2
5782 $w configure -cursor $curtextcursor
5783 if {$linkentercount < 0} {
5784 set linkentercount 0
5789 proc viewnextline {dir} {
5793 set ymax [lindex [$canv cget -scrollregion] 3]
5794 set wnow [$canv yview]
5795 set wtop [expr {[lindex $wnow 0] * $ymax}]
5796 set newtop [expr {$wtop + $dir * $linespc}]
5799 } elseif {$newtop > $ymax} {
5802 allcanvs yview moveto [expr {$newtop * 1.0 / $ymax}]
5805 # add a list of tag or branch names at position pos
5806 # returns the number of names inserted
5807 proc appendrefs {pos ids var} {
5808 global ctext linknum curview $var maxrefs
5810 if {[catch {$ctext index $pos}]} {
5813 $ctext conf -state normal
5814 $ctext delete $pos "$pos lineend"
5817 foreach tag [set $var\($id\)] {
5818 lappend tags [list $tag $id]
5821 if {[llength $tags] > $maxrefs} {
5822 $ctext insert $pos "many ([llength $tags])"
5824 set tags [lsort -index 0 -decreasing $tags]
5827 set id [lindex $ti 1]
5830 $ctext tag delete $lk
5831 $ctext insert $pos $sep
5832 $ctext insert $pos [lindex $ti 0] $lk
5837 $ctext conf -state disabled
5838 return [llength $tags]
5841 # called when we have finished computing the nearby tags
5842 proc dispneartags {delay} {
5843 global selectedline currentid showneartags tagphase
5845 if {$selectedline eq {} || !$showneartags} return
5846 after cancel dispnexttag
5848 after 200 dispnexttag
5851 after idle dispnexttag
5856 proc dispnexttag {} {
5857 global selectedline currentid showneartags tagphase ctext
5859 if {$selectedline eq {} || !$showneartags} return
5860 switch -- $tagphase {
5862 set dtags [desctags $currentid]
5864 appendrefs precedes $dtags idtags
5868 set atags [anctags $currentid]
5870 appendrefs follows $atags idtags
5874 set dheads [descheads $currentid]
5875 if {$dheads ne {}} {
5876 if {[appendrefs branch $dheads idheads] > 1
5877 && [$ctext get "branch -3c"] eq "h"} {
5878 # turn "Branch" into "Branches"
5879 $ctext conf -state normal
5880 $ctext insert "branch -2c" "es"
5881 $ctext conf -state disabled
5886 if {[incr tagphase] <= 2} {
5887 after idle dispnexttag
5891 proc make_secsel {l} {
5892 global linehtag linentag linedtag canv canv2 canv3
5894 if {![info exists linehtag($l)]} return
5896 set t [eval $canv create rect [$canv bbox $linehtag($l)] -outline {{}} \
5897 -tags secsel -fill [$canv cget -selectbackground]]
5899 $canv2 delete secsel
5900 set t [eval $canv2 create rect [$canv2 bbox $linentag($l)] -outline {{}} \
5901 -tags secsel -fill [$canv2 cget -selectbackground]]
5903 $canv3 delete secsel
5904 set t [eval $canv3 create rect [$canv3 bbox $linedtag($l)] -outline {{}} \
5905 -tags secsel -fill [$canv3 cget -selectbackground]]
5909 proc selectline {l isnew} {
5910 global canv ctext commitinfo selectedline
5911 global canvy0 linespc parents children curview
5912 global currentid sha1entry
5913 global commentend idtags linknum
5914 global mergemax numcommits pending_select
5915 global cmitmode showneartags allcommits
5916 global targetrow targetid lastscrollrows
5919 catch {unset pending_select}
5924 if {$l < 0 || $l >= $numcommits} return
5925 set id [commitonrow $l]
5930 if {$lastscrollrows < $numcommits} {
5934 set y [expr {$canvy0 + $l * $linespc}]
5935 set ymax [lindex [$canv cget -scrollregion] 3]
5936 set ytop [expr {$y - $linespc - 1}]
5937 set ybot [expr {$y + $linespc + 1}]
5938 set wnow [$canv yview]
5939 set wtop [expr {[lindex $wnow 0] * $ymax}]
5940 set wbot [expr {[lindex $wnow 1] * $ymax}]
5941 set wh [expr {$wbot - $wtop}]
5943 if {$ytop < $wtop} {
5944 if {$ybot < $wtop} {
5945 set newtop [expr {$y - $wh / 2.0}]
5948 if {$newtop > $wtop - $linespc} {
5949 set newtop [expr {$wtop - $linespc}]
5952 } elseif {$ybot > $wbot} {
5953 if {$ytop > $wbot} {
5954 set newtop [expr {$y - $wh / 2.0}]
5956 set newtop [expr {$ybot - $wh}]
5957 if {$newtop < $wtop + $linespc} {
5958 set newtop [expr {$wtop + $linespc}]
5962 if {$newtop != $wtop} {
5966 allcanvs yview moveto [expr {$newtop * 1.0 / $ymax}]
5973 addtohistory [list selbyid $id]
5976 $sha1entry delete 0 end
5977 $sha1entry insert 0 $id
5979 $sha1entry selection from 0
5980 $sha1entry selection to end
5984 $ctext conf -state normal
5987 if {![info exists commitinfo($id)]} {
5990 set info $commitinfo($id)
5991 set date [formatdate [lindex $info 2]]
5992 $ctext insert end "[mc "Author"]: [lindex $info 1] $date\n"
5993 set date [formatdate [lindex $info 4]]
5994 $ctext insert end "[mc "Committer"]: [lindex $info 3] $date\n"
5995 if {[info exists idtags($id)]} {
5996 $ctext insert end [mc "Tags:"]
5997 foreach tag $idtags($id) {
5998 $ctext insert end " $tag"
6000 $ctext insert end "\n"
6004 set olds $parents($curview,$id)
6005 if {[llength $olds] > 1} {
6008 if {$np >= $mergemax} {
6013 $ctext insert end "[mc "Parent"]: " $tag
6014 appendwithlinks [commit_descriptor $p] {}
6019 append headers "[mc "Parent"]: [commit_descriptor $p]"
6023 foreach c $children($curview,$id) {
6024 append headers "[mc "Child"]: [commit_descriptor $c]"
6027 # make anything that looks like a SHA1 ID be a clickable link
6028 appendwithlinks $headers {}
6029 if {$showneartags} {
6030 if {![info exists allcommits]} {
6033 $ctext insert end "[mc "Branch"]: "
6034 $ctext mark set branch "end -1c"
6035 $ctext mark gravity branch left
6036 $ctext insert end "\n[mc "Follows"]: "
6037 $ctext mark set follows "end -1c"
6038 $ctext mark gravity follows left
6039 $ctext insert end "\n[mc "Precedes"]: "
6040 $ctext mark set precedes "end -1c"
6041 $ctext mark gravity precedes left
6042 $ctext insert end "\n"
6045 $ctext insert end "\n"
6046 set comment [lindex $info 5]
6047 if {[string first "\r" $comment] >= 0} {
6048 set comment [string map {"\r" "\n "} $comment]
6050 appendwithlinks $comment {comment}
6052 $ctext tag remove found 1.0 end
6053 $ctext conf -state disabled
6054 set commentend [$ctext index "end - 1c"]
6056 init_flist [mc "Comments"]
6057 if {$cmitmode eq "tree"} {
6059 } elseif {[llength $olds] <= 1} {
6066 proc selfirstline {} {
6071 proc sellastline {} {
6074 set l [expr {$numcommits - 1}]
6078 proc selnextline {dir} {
6081 if {$selectedline eq {}} return
6082 set l [expr {$selectedline + $dir}]
6087 proc selnextpage {dir} {
6088 global canv linespc selectedline numcommits
6090 set lpp [expr {([winfo height $canv] - 2) / $linespc}]
6094 allcanvs yview scroll [expr {$dir * $lpp}] units
6096 if {$selectedline eq {}} return
6097 set l [expr {$selectedline + $dir * $lpp}]
6100 } elseif {$l >= $numcommits} {
6101 set l [expr $numcommits - 1]
6107 proc unselectline {} {
6108 global selectedline currentid
6111 catch {unset currentid}
6112 allcanvs delete secsel
6116 proc reselectline {} {
6119 if {$selectedline ne {}} {
6120 selectline $selectedline 0
6124 proc addtohistory {cmd} {
6125 global history historyindex curview
6127 set elt [list $curview $cmd]
6128 if {$historyindex > 0
6129 && [lindex $history [expr {$historyindex - 1}]] == $elt} {
6133 if {$historyindex < [llength $history]} {
6134 set history [lreplace $history $historyindex end $elt]
6136 lappend history $elt
6139 if {$historyindex > 1} {
6140 .tf.bar.leftbut conf -state normal
6142 .tf.bar.leftbut conf -state disabled
6144 .tf.bar.rightbut conf -state disabled
6150 set view [lindex $elt 0]
6151 set cmd [lindex $elt 1]
6152 if {$curview != $view} {
6159 global history historyindex
6162 if {$historyindex > 1} {
6163 incr historyindex -1
6164 godo [lindex $history [expr {$historyindex - 1}]]
6165 .tf.bar.rightbut conf -state normal
6167 if {$historyindex <= 1} {
6168 .tf.bar.leftbut conf -state disabled
6173 global history historyindex
6176 if {$historyindex < [llength $history]} {
6177 set cmd [lindex $history $historyindex]
6180 .tf.bar.leftbut conf -state normal
6182 if {$historyindex >= [llength $history]} {
6183 .tf.bar.rightbut conf -state disabled
6188 global treefilelist treeidlist diffids diffmergeid treepending
6189 global nullid nullid2
6192 catch {unset diffmergeid}
6193 if {![info exists treefilelist($id)]} {
6194 if {![info exists treepending]} {
6195 if {$id eq $nullid} {
6196 set cmd [list | git ls-files]
6197 } elseif {$id eq $nullid2} {
6198 set cmd [list | git ls-files --stage -t]
6200 set cmd [list | git ls-tree -r $id]
6202 if {[catch {set gtf [open $cmd r]}]} {
6206 set treefilelist($id) {}
6207 set treeidlist($id) {}
6208 fconfigure $gtf -blocking 0
6209 filerun $gtf [list gettreeline $gtf $id]
6216 proc gettreeline {gtf id} {
6217 global treefilelist treeidlist treepending cmitmode diffids nullid nullid2
6220 while {[incr nl] <= 1000 && [gets $gtf line] >= 0} {
6221 if {$diffids eq $nullid} {
6224 set i [string first "\t" $line]
6225 if {$i < 0} continue
6226 set fname [string range $line [expr {$i+1}] end]
6227 set line [string range $line 0 [expr {$i-1}]]
6228 if {$diffids ne $nullid2 && [lindex $line 1] ne "blob"} continue
6229 set sha1 [lindex $line 2]
6230 if {[string index $fname 0] eq "\""} {
6231 set fname [lindex $fname 0]
6233 lappend treeidlist($id) $sha1
6235 lappend treefilelist($id) $fname
6238 return [expr {$nl >= 1000? 2: 1}]
6242 if {$cmitmode ne "tree"} {
6243 if {![info exists diffmergeid]} {
6244 gettreediffs $diffids
6246 } elseif {$id ne $diffids} {
6255 global treefilelist treeidlist diffids nullid nullid2
6256 global ctext commentend
6258 set i [lsearch -exact $treefilelist($diffids) $f]
6260 puts "oops, $f not in list for id $diffids"
6263 if {$diffids eq $nullid} {
6264 if {[catch {set bf [open $f r]} err]} {
6265 puts "oops, can't read $f: $err"
6269 set blob [lindex $treeidlist($diffids) $i]
6270 if {[catch {set bf [open [concat | git cat-file blob $blob] r]} err]} {
6271 puts "oops, error reading blob $blob: $err"
6275 fconfigure $bf -blocking 0
6276 filerun $bf [list getblobline $bf $diffids]
6277 $ctext config -state normal
6278 clear_ctext $commentend
6279 $ctext insert end "\n"
6280 $ctext insert end "$f\n" filesep
6281 $ctext config -state disabled
6282 $ctext yview $commentend
6286 proc getblobline {bf id} {
6287 global diffids cmitmode ctext
6289 if {$id ne $diffids || $cmitmode ne "tree"} {
6293 $ctext config -state normal
6295 while {[incr nl] <= 1000 && [gets $bf line] >= 0} {
6296 $ctext insert end "$line\n"
6299 # delete last newline
6300 $ctext delete "end - 2c" "end - 1c"
6304 $ctext config -state disabled
6305 return [expr {$nl >= 1000? 2: 1}]
6308 proc mergediff {id} {
6309 global diffmergeid mdifffd
6313 global limitdiffs vfilelimit curview
6317 # this doesn't seem to actually affect anything...
6318 set cmd [concat | git diff-tree --no-commit-id --cc -U$diffcontext $id]
6319 if {$limitdiffs && $vfilelimit($curview) ne {}} {
6320 set cmd [concat $cmd -- $vfilelimit($curview)]
6322 if {[catch {set mdf [open $cmd r]} err]} {
6323 error_popup "[mc "Error getting merge diffs:"] $err"
6326 fconfigure $mdf -blocking 0
6327 set mdifffd($id) $mdf
6328 set np [llength $parents($curview,$id)]
6330 filerun $mdf [list getmergediffline $mdf $id $np]
6333 proc getmergediffline {mdf id np} {
6334 global diffmergeid ctext cflist mergemax
6335 global difffilestart mdifffd
6337 $ctext conf -state normal
6339 while {[incr nr] <= 1000 && [gets $mdf line] >= 0} {
6340 if {![info exists diffmergeid] || $id != $diffmergeid
6341 || $mdf != $mdifffd($id)} {
6345 if {[regexp {^diff --cc (.*)} $line match fname]} {
6346 # start of a new file
6347 $ctext insert end "\n"
6348 set here [$ctext index "end - 1c"]
6349 lappend difffilestart $here
6350 add_flist [list $fname]
6351 set l [expr {(78 - [string length $fname]) / 2}]
6352 set pad [string range "----------------------------------------" 1 $l]
6353 $ctext insert end "$pad $fname $pad\n" filesep
6354 } elseif {[regexp {^@@} $line]} {
6355 $ctext insert end "$line\n" hunksep
6356 } elseif {[regexp {^[0-9a-f]{40}$} $line] || [regexp {^index} $line]} {
6359 # parse the prefix - one ' ', '-' or '+' for each parent
6364 for {set j 0} {$j < $np} {incr j} {
6365 set c [string range $line $j $j]
6368 } elseif {$c == "-"} {
6370 } elseif {$c == "+"} {
6379 if {!$isbad && $minuses ne {} && $pluses eq {}} {
6380 # line doesn't appear in result, parents in $minuses have the line
6381 set num [lindex $minuses 0]
6382 } elseif {!$isbad && $pluses ne {} && $minuses eq {}} {
6383 # line appears in result, parents in $pluses don't have the line
6384 lappend tags mresult
6385 set num [lindex $spaces 0]
6388 if {$num >= $mergemax} {
6393 $ctext insert end "$line\n" $tags
6396 $ctext conf -state disabled
6401 return [expr {$nr >= 1000? 2: 1}]
6404 proc startdiff {ids} {
6405 global treediffs diffids treepending diffmergeid nullid nullid2
6409 catch {unset diffmergeid}
6410 if {![info exists treediffs($ids)] ||
6411 [lsearch -exact $ids $nullid] >= 0 ||
6412 [lsearch -exact $ids $nullid2] >= 0} {
6413 if {![info exists treepending]} {
6421 proc path_filter {filter name} {
6423 set l [string length $p]
6424 if {[string index $p end] eq "/"} {
6425 if {[string compare -length $l $p $name] == 0} {
6429 if {[string compare -length $l $p $name] == 0 &&
6430 ([string length $name] == $l ||
6431 [string index $name $l] eq "/")} {
6439 proc addtocflist {ids} {
6442 add_flist $treediffs($ids)
6446 proc diffcmd {ids flags} {
6447 global nullid nullid2
6449 set i [lsearch -exact $ids $nullid]
6450 set j [lsearch -exact $ids $nullid2]
6452 if {[llength $ids] > 1 && $j < 0} {
6453 # comparing working directory with some specific revision
6454 set cmd [concat | git diff-index $flags]
6456 lappend cmd -R [lindex $ids 1]
6458 lappend cmd [lindex $ids 0]
6461 # comparing working directory with index
6462 set cmd [concat | git diff-files $flags]
6467 } elseif {$j >= 0} {
6468 set cmd [concat | git diff-index --cached $flags]
6469 if {[llength $ids] > 1} {
6470 # comparing index with specific revision
6472 lappend cmd -R [lindex $ids 1]
6474 lappend cmd [lindex $ids 0]
6477 # comparing index with HEAD
6481 set cmd [concat | git diff-tree -r $flags $ids]
6486 proc gettreediffs {ids} {
6487 global treediff treepending
6489 if {[catch {set gdtf [open [diffcmd $ids {--no-commit-id}] r]}]} return
6491 set treepending $ids
6493 fconfigure $gdtf -blocking 0
6494 filerun $gdtf [list gettreediffline $gdtf $ids]
6497 proc gettreediffline {gdtf ids} {
6498 global treediff treediffs treepending diffids diffmergeid
6499 global cmitmode vfilelimit curview limitdiffs
6502 while {[incr nr] <= 1000 && [gets $gdtf line] >= 0} {
6503 set i [string first "\t" $line]
6505 set file [string range $line [expr {$i+1}] end]
6506 if {[string index $file 0] eq "\""} {
6507 set file [lindex $file 0]
6509 lappend treediff $file
6513 return [expr {$nr >= 1000? 2: 1}]
6516 if {$limitdiffs && $vfilelimit($curview) ne {}} {
6518 foreach f $treediff {
6519 if {[path_filter $vfilelimit($curview) $f]} {
6523 set treediffs($ids) $flist
6525 set treediffs($ids) $treediff
6528 if {$cmitmode eq "tree"} {
6530 } elseif {$ids != $diffids} {
6531 if {![info exists diffmergeid]} {
6532 gettreediffs $diffids
6540 # empty string or positive integer
6541 proc diffcontextvalidate {v} {
6542 return [regexp {^(|[1-9][0-9]*)$} $v]
6545 proc diffcontextchange {n1 n2 op} {
6546 global diffcontextstring diffcontext
6548 if {[string is integer -strict $diffcontextstring]} {
6549 if {$diffcontextstring > 0} {
6550 set diffcontext $diffcontextstring
6556 proc changeignorespace {} {
6560 proc getblobdiffs {ids} {
6561 global blobdifffd diffids env
6562 global diffinhdr treediffs
6565 global limitdiffs vfilelimit curview
6567 set cmd [diffcmd $ids "-p -C --no-commit-id -U$diffcontext"]
6571 if {$limitdiffs && $vfilelimit($curview) ne {}} {
6572 set cmd [concat $cmd -- $vfilelimit($curview)]
6574 if {[catch {set bdf [open $cmd r]} err]} {
6575 puts "error getting diffs: $err"
6579 fconfigure $bdf -blocking 0
6580 set blobdifffd($ids) $bdf
6581 filerun $bdf [list getblobdiffline $bdf $diffids]
6584 proc setinlist {var i val} {
6587 while {[llength [set $var]] < $i} {
6590 if {[llength [set $var]] == $i} {
6597 proc makediffhdr {fname ids} {
6598 global ctext curdiffstart treediffs
6600 set i [lsearch -exact $treediffs($ids) $fname]
6602 setinlist difffilestart $i $curdiffstart
6604 set l [expr {(78 - [string length $fname]) / 2}]
6605 set pad [string range "----------------------------------------" 1 $l]
6606 $ctext insert $curdiffstart "$pad $fname $pad" filesep
6609 proc getblobdiffline {bdf ids} {
6610 global diffids blobdifffd ctext curdiffstart
6611 global diffnexthead diffnextnote difffilestart
6612 global diffinhdr treediffs
6615 $ctext conf -state normal
6616 while {[incr nr] <= 1000 && [gets $bdf line] >= 0} {
6617 if {$ids != $diffids || $bdf != $blobdifffd($ids)} {
6621 if {![string compare -length 11 "diff --git " $line]} {
6622 # trim off "diff --git "
6623 set line [string range $line 11 end]
6625 # start of a new file
6626 $ctext insert end "\n"
6627 set curdiffstart [$ctext index "end - 1c"]
6628 $ctext insert end "\n" filesep
6629 # If the name hasn't changed the length will be odd,
6630 # the middle char will be a space, and the two bits either
6631 # side will be a/name and b/name, or "a/name" and "b/name".
6632 # If the name has changed we'll get "rename from" and
6633 # "rename to" or "copy from" and "copy to" lines following this,
6634 # and we'll use them to get the filenames.
6635 # This complexity is necessary because spaces in the filename(s)
6636 # don't get escaped.
6637 set l [string length $line]
6638 set i [expr {$l / 2}]
6639 if {!(($l & 1) && [string index $line $i] eq " " &&
6640 [string range $line 2 [expr {$i - 1}]] eq \
6641 [string range $line [expr {$i + 3}] end])} {
6644 # unescape if quoted and chop off the a/ from the front
6645 if {[string index $line 0] eq "\""} {
6646 set fname [string range [lindex $line 0] 2 end]
6648 set fname [string range $line 2 [expr {$i - 1}]]
6650 makediffhdr $fname $ids
6652 } elseif {[regexp {^@@ -([0-9]+)(,[0-9]+)? \+([0-9]+)(,[0-9]+)? @@(.*)} \
6653 $line match f1l f1c f2l f2c rest]} {
6654 $ctext insert end "$line\n" hunksep
6657 } elseif {$diffinhdr} {
6658 if {![string compare -length 12 "rename from " $line]} {
6659 set fname [string range $line [expr 6 + [string first " from " $line] ] end]
6660 if {[string index $fname 0] eq "\""} {
6661 set fname [lindex $fname 0]
6663 set i [lsearch -exact $treediffs($ids) $fname]
6665 setinlist difffilestart $i $curdiffstart
6667 } elseif {![string compare -length 10 $line "rename to "] ||
6668 ![string compare -length 8 $line "copy to "]} {
6669 set fname [string range $line [expr 4 + [string first " to " $line] ] end]
6670 if {[string index $fname 0] eq "\""} {
6671 set fname [lindex $fname 0]
6673 makediffhdr $fname $ids
6674 } elseif {[string compare -length 3 $line "---"] == 0} {
6677 } elseif {[string compare -length 3 $line "+++"] == 0} {
6681 $ctext insert end "$line\n" filesep
6684 set x [string range $line 0 0]
6685 if {$x == "-" || $x == "+"} {
6686 set tag [expr {$x == "+"}]
6687 $ctext insert end "$line\n" d$tag
6688 } elseif {$x == " "} {
6689 $ctext insert end "$line\n"
6691 # "\ No newline at end of file",
6692 # or something else we don't recognize
6693 $ctext insert end "$line\n" hunksep
6697 $ctext conf -state disabled
6702 return [expr {$nr >= 1000? 2: 1}]
6705 proc changediffdisp {} {
6706 global ctext diffelide
6708 $ctext tag conf d0 -elide [lindex $diffelide 0]
6709 $ctext tag conf d1 -elide [lindex $diffelide 1]
6712 proc highlightfile {loc cline} {
6713 global ctext cflist cflist_top
6716 $cflist tag remove highlight $cflist_top.0 "$cflist_top.0 lineend"
6717 $cflist tag add highlight $cline.0 "$cline.0 lineend"
6718 $cflist see $cline.0
6719 set cflist_top $cline
6723 global difffilestart ctext cmitmode
6725 if {$cmitmode eq "tree"} return
6728 set here [$ctext index @0,0]
6729 foreach loc $difffilestart {
6730 if {[$ctext compare $loc >= $here]} {
6731 highlightfile $prev $prevline
6737 highlightfile $prev $prevline
6741 global difffilestart ctext cmitmode
6743 if {$cmitmode eq "tree"} return
6744 set here [$ctext index @0,0]
6746 foreach loc $difffilestart {
6748 if {[$ctext compare $loc > $here]} {
6749 highlightfile $loc $line
6755 proc clear_ctext {{first 1.0}} {
6756 global ctext smarktop smarkbot
6759 set l [lindex [split $first .] 0]
6760 if {![info exists smarktop] || [$ctext compare $first < $smarktop.0]} {
6763 if {![info exists smarkbot] || [$ctext compare $first < $smarkbot.0]} {
6766 $ctext delete $first end
6767 if {$first eq "1.0"} {
6768 catch {unset pendinglinks}
6772 proc settabs {{firstab {}}} {
6773 global firsttabstop tabstop ctext have_tk85
6775 if {$firstab ne {} && $have_tk85} {
6776 set firsttabstop $firstab
6778 set w [font measure textfont "0"]
6779 if {$firsttabstop != 0} {
6780 $ctext conf -tabs [list [expr {($firsttabstop + $tabstop) * $w}] \
6781 [expr {($firsttabstop + 2 * $tabstop) * $w}]]
6782 } elseif {$have_tk85 || $tabstop != 8} {
6783 $ctext conf -tabs [expr {$tabstop * $w}]
6785 $ctext conf -tabs {}
6789 proc incrsearch {name ix op} {
6790 global ctext searchstring searchdirn
6792 $ctext tag remove found 1.0 end
6793 if {[catch {$ctext index anchor}]} {
6794 # no anchor set, use start of selection, or of visible area
6795 set sel [$ctext tag ranges sel]
6797 $ctext mark set anchor [lindex $sel 0]
6798 } elseif {$searchdirn eq "-forwards"} {
6799 $ctext mark set anchor @0,0
6801 $ctext mark set anchor @0,[winfo height $ctext]
6804 if {$searchstring ne {}} {
6805 set here [$ctext search $searchdirn -- $searchstring anchor]
6814 global sstring ctext searchstring searchdirn
6817 $sstring icursor end
6818 set searchdirn -forwards
6819 if {$searchstring ne {}} {
6820 set sel [$ctext tag ranges sel]
6822 set start "[lindex $sel 0] + 1c"
6823 } elseif {[catch {set start [$ctext index anchor]}]} {
6826 set match [$ctext search -count mlen -- $searchstring $start]
6827 $ctext tag remove sel 1.0 end
6833 set mend "$match + $mlen c"
6834 $ctext tag add sel $match $mend
6835 $ctext mark unset anchor
6839 proc dosearchback {} {
6840 global sstring ctext searchstring searchdirn
6843 $sstring icursor end
6844 set searchdirn -backwards
6845 if {$searchstring ne {}} {
6846 set sel [$ctext tag ranges sel]
6848 set start [lindex $sel 0]
6849 } elseif {[catch {set start [$ctext index anchor]}]} {
6850 set start @0,[winfo height $ctext]
6852 set match [$ctext search -backwards -count ml -- $searchstring $start]
6853 $ctext tag remove sel 1.0 end
6859 set mend "$match + $ml c"
6860 $ctext tag add sel $match $mend
6861 $ctext mark unset anchor
6865 proc searchmark {first last} {
6866 global ctext searchstring
6870 set match [$ctext search -count mlen -- $searchstring $mend $last.end]
6871 if {$match eq {}} break
6872 set mend "$match + $mlen c"
6873 $ctext tag add found $match $mend
6877 proc searchmarkvisible {doall} {
6878 global ctext smarktop smarkbot
6880 set topline [lindex [split [$ctext index @0,0] .] 0]
6881 set botline [lindex [split [$ctext index @0,[winfo height $ctext]] .] 0]
6882 if {$doall || $botline < $smarktop || $topline > $smarkbot} {
6883 # no overlap with previous
6884 searchmark $topline $botline
6885 set smarktop $topline
6886 set smarkbot $botline
6888 if {$topline < $smarktop} {
6889 searchmark $topline [expr {$smarktop-1}]
6890 set smarktop $topline
6892 if {$botline > $smarkbot} {
6893 searchmark [expr {$smarkbot+1}] $botline
6894 set smarkbot $botline
6899 proc scrolltext {f0 f1} {
6902 .bleft.bottom.sb set $f0 $f1
6903 if {$searchstring ne {}} {
6909 global linespc charspc canvx0 canvy0
6910 global xspc1 xspc2 lthickness
6912 set linespc [font metrics mainfont -linespace]
6913 set charspc [font measure mainfont "m"]
6914 set canvy0 [expr {int(3 + 0.5 * $linespc)}]
6915 set canvx0 [expr {int(3 + 0.5 * $linespc)}]
6916 set lthickness [expr {int($linespc / 9) + 1}]
6917 set xspc1(0) $linespc
6925 set ymax [lindex [$canv cget -scrollregion] 3]
6926 if {$ymax eq {} || $ymax == 0} return
6927 set span [$canv yview]
6930 allcanvs yview moveto [lindex $span 0]
6932 if {$selectedline ne {}} {
6933 selectline $selectedline 0
6934 allcanvs yview moveto [lindex $span 0]
6938 proc parsefont {f n} {
6941 set fontattr($f,family) [lindex $n 0]
6943 if {$s eq {} || $s == 0} {
6946 set s [expr {int(-$s / [winfo fpixels . 1p] + 0.5)}]
6948 set fontattr($f,size) $s
6949 set fontattr($f,weight) normal
6950 set fontattr($f,slant) roman
6951 foreach style [lrange $n 2 end] {
6954 "bold" {set fontattr($f,weight) $style}
6956 "italic" {set fontattr($f,slant) $style}
6961 proc fontflags {f {isbold 0}} {
6964 return [list -family $fontattr($f,family) -size $fontattr($f,size) \
6965 -weight [expr {$isbold? "bold": $fontattr($f,weight)}] \
6966 -slant $fontattr($f,slant)]
6972 set n [list $fontattr($f,family) $fontattr($f,size)]
6973 if {$fontattr($f,weight) eq "bold"} {
6976 if {$fontattr($f,slant) eq "italic"} {
6982 proc incrfont {inc} {
6983 global mainfont textfont ctext canv cflist showrefstop
6984 global stopped entries fontattr
6987 set s $fontattr(mainfont,size)
6992 set fontattr(mainfont,size) $s
6993 font config mainfont -size $s
6994 font config mainfontbold -size $s
6995 set mainfont [fontname mainfont]
6996 set s $fontattr(textfont,size)
7001 set fontattr(textfont,size) $s
7002 font config textfont -size $s
7003 font config textfontbold -size $s
7004 set textfont [fontname textfont]
7011 global sha1entry sha1string
7012 if {[string length $sha1string] == 40} {
7013 $sha1entry delete 0 end
7017 proc sha1change {n1 n2 op} {
7018 global sha1string currentid sha1but
7019 if {$sha1string == {}
7020 || ([info exists currentid] && $sha1string == $currentid)} {
7025 if {[$sha1but cget -state] == $state} return
7026 if {$state == "normal"} {
7027 $sha1but conf -state normal -relief raised -text "[mc "Goto:"] "
7029 $sha1but conf -state disabled -relief flat -text "[mc "SHA1 ID:"] "
7033 proc gotocommit {} {
7034 global sha1string tagids headids curview varcid
7036 if {$sha1string == {}
7037 || ([info exists currentid] && $sha1string == $currentid)} return
7038 if {[info exists tagids($sha1string)]} {
7039 set id $tagids($sha1string)
7040 } elseif {[info exists headids($sha1string)]} {
7041 set id $headids($sha1string)
7043 set id [string tolower $sha1string]
7044 if {[regexp {^[0-9a-f]{4,39}$} $id]} {
7045 set matches [array names varcid "$curview,$id*"]
7046 if {$matches ne {}} {
7047 if {[llength $matches] > 1} {
7048 error_popup [mc "Short SHA1 id %s is ambiguous" $id]
7051 set id [lindex [split [lindex $matches 0] ","] 1]
7055 if {[commitinview $id $curview]} {
7056 selectline [rowofcommit $id] 1
7059 if {[regexp {^[0-9a-fA-F]{4,}$} $sha1string]} {
7060 set msg [mc "SHA1 id %s is not known" $sha1string]
7062 set msg [mc "Tag/Head %s is not known" $sha1string]
7067 proc lineenter {x y id} {
7068 global hoverx hovery hoverid hovertimer
7069 global commitinfo canv
7071 if {![info exists commitinfo($id)] && ![getcommit $id]} return
7075 if {[info exists hovertimer]} {
7076 after cancel $hovertimer
7078 set hovertimer [after 500 linehover]
7082 proc linemotion {x y id} {
7083 global hoverx hovery hoverid hovertimer
7085 if {[info exists hoverid] && $id == $hoverid} {
7088 if {[info exists hovertimer]} {
7089 after cancel $hovertimer
7091 set hovertimer [after 500 linehover]
7095 proc lineleave {id} {
7096 global hoverid hovertimer canv
7098 if {[info exists hoverid] && $id == $hoverid} {
7100 if {[info exists hovertimer]} {
7101 after cancel $hovertimer
7109 global hoverx hovery hoverid hovertimer
7110 global canv linespc lthickness
7113 set text [lindex $commitinfo($hoverid) 0]
7114 set ymax [lindex [$canv cget -scrollregion] 3]
7115 if {$ymax == {}} return
7116 set yfrac [lindex [$canv yview] 0]
7117 set x [expr {$hoverx + 2 * $linespc}]
7118 set y [expr {$hovery + $yfrac * $ymax - $linespc / 2}]
7119 set x0 [expr {$x - 2 * $lthickness}]
7120 set y0 [expr {$y - 2 * $lthickness}]
7121 set x1 [expr {$x + [font measure mainfont $text] + 2 * $lthickness}]
7122 set y1 [expr {$y + $linespc + 2 * $lthickness}]
7123 set t [$canv create rectangle $x0 $y0 $x1 $y1 \
7124 -fill \#ffff80 -outline black -width 1 -tags hover]
7126 set t [$canv create text $x $y -anchor nw -text $text -tags hover \
7131 proc clickisonarrow {id y} {
7134 set ranges [rowranges $id]
7135 set thresh [expr {2 * $lthickness + 6}]
7136 set n [expr {[llength $ranges] - 1}]
7137 for {set i 1} {$i < $n} {incr i} {
7138 set row [lindex $ranges $i]
7139 if {abs([yc $row] - $y) < $thresh} {
7146 proc arrowjump {id n y} {
7149 # 1 <-> 2, 3 <-> 4, etc...
7150 set n [expr {(($n - 1) ^ 1) + 1}]
7151 set row [lindex [rowranges $id] $n]
7153 set ymax [lindex [$canv cget -scrollregion] 3]
7154 if {$ymax eq {} || $ymax <= 0} return
7155 set view [$canv yview]
7156 set yspan [expr {[lindex $view 1] - [lindex $view 0]}]
7157 set yfrac [expr {$yt / $ymax - $yspan / 2}]
7161 allcanvs yview moveto $yfrac
7164 proc lineclick {x y id isnew} {
7165 global ctext commitinfo children canv thickerline curview
7167 if {![info exists commitinfo($id)] && ![getcommit $id]} return
7172 # draw this line thicker than normal
7176 set ymax [lindex [$canv cget -scrollregion] 3]
7177 if {$ymax eq {}} return
7178 set yfrac [lindex [$canv yview] 0]
7179 set y [expr {$y + $yfrac * $ymax}]
7181 set dirn [clickisonarrow $id $y]
7183 arrowjump $id $dirn $y
7188 addtohistory [list lineclick $x $y $id 0]
7190 # fill the details pane with info about this line
7191 $ctext conf -state normal
7194 $ctext insert end "[mc "Parent"]:\t"
7195 $ctext insert end $id link0
7197 set info $commitinfo($id)
7198 $ctext insert end "\n\t[lindex $info 0]\n"
7199 $ctext insert end "\t[mc "Author"]:\t[lindex $info 1]\n"
7200 set date [formatdate [lindex $info 2]]
7201 $ctext insert end "\t[mc "Date"]:\t$date\n"
7202 set kids $children($curview,$id)
7204 $ctext insert end "\n[mc "Children"]:"
7206 foreach child $kids {
7208 if {![info exists commitinfo($child)] && ![getcommit $child]} continue
7209 set info $commitinfo($child)
7210 $ctext insert end "\n\t"
7211 $ctext insert end $child link$i
7212 setlink $child link$i
7213 $ctext insert end "\n\t[lindex $info 0]"
7214 $ctext insert end "\n\t[mc "Author"]:\t[lindex $info 1]"
7215 set date [formatdate [lindex $info 2]]
7216 $ctext insert end "\n\t[mc "Date"]:\t$date\n"
7219 $ctext conf -state disabled
7223 proc normalline {} {
7225 if {[info exists thickerline]} {
7234 if {[commitinview $id $curview]} {
7235 selectline [rowofcommit $id] 1
7241 if {![info exists startmstime]} {
7242 set startmstime [clock clicks -milliseconds]
7244 return [format "%.3f" [expr {([clock click -milliseconds] - $startmstime) / 1000.0}]]
7247 proc rowmenu {x y id} {
7248 global rowctxmenu selectedline rowmenuid curview
7249 global nullid nullid2 fakerowmenu mainhead
7253 if {$selectedline eq {} || [rowofcommit $id] eq $selectedline} {
7258 if {$id ne $nullid && $id ne $nullid2} {
7259 set menu $rowctxmenu
7260 if {$mainhead ne {}} {
7261 $menu entryconfigure 7 -label [mc "Reset %s branch to here" $mainhead]
7263 $menu entryconfigure 7 -label [mc "Detached head: can't reset" $mainhead] -state disabled
7266 set menu $fakerowmenu
7268 $menu entryconfigure [mc "Diff this -> selected"] -state $state
7269 $menu entryconfigure [mc "Diff selected -> this"] -state $state
7270 $menu entryconfigure [mc "Make patch"] -state $state
7271 tk_popup $menu $x $y
7274 proc diffvssel {dirn} {
7275 global rowmenuid selectedline
7277 if {$selectedline eq {}} return
7279 set oldid [commitonrow $selectedline]
7280 set newid $rowmenuid
7282 set oldid $rowmenuid
7283 set newid [commitonrow $selectedline]
7285 addtohistory [list doseldiff $oldid $newid]
7286 doseldiff $oldid $newid
7289 proc doseldiff {oldid newid} {
7293 $ctext conf -state normal
7295 init_flist [mc "Top"]
7296 $ctext insert end "[mc "From"] "
7297 $ctext insert end $oldid link0
7298 setlink $oldid link0
7299 $ctext insert end "\n "
7300 $ctext insert end [lindex $commitinfo($oldid) 0]
7301 $ctext insert end "\n\n[mc "To"] "
7302 $ctext insert end $newid link1
7303 setlink $newid link1
7304 $ctext insert end "\n "
7305 $ctext insert end [lindex $commitinfo($newid) 0]
7306 $ctext insert end "\n"
7307 $ctext conf -state disabled
7308 $ctext tag remove found 1.0 end
7309 startdiff [list $oldid $newid]
7313 global rowmenuid currentid commitinfo patchtop patchnum
7315 if {![info exists currentid]} return
7316 set oldid $currentid
7317 set oldhead [lindex $commitinfo($oldid) 0]
7318 set newid $rowmenuid
7319 set newhead [lindex $commitinfo($newid) 0]
7322 catch {destroy $top}
7324 label $top.title -text [mc "Generate patch"]
7325 grid $top.title - -pady 10
7326 label $top.from -text [mc "From:"]
7327 entry $top.fromsha1 -width 40 -relief flat
7328 $top.fromsha1 insert 0 $oldid
7329 $top.fromsha1 conf -state readonly
7330 grid $top.from $top.fromsha1 -sticky w
7331 entry $top.fromhead -width 60 -relief flat
7332 $top.fromhead insert 0 $oldhead
7333 $top.fromhead conf -state readonly
7334 grid x $top.fromhead -sticky w
7335 label $top.to -text [mc "To:"]
7336 entry $top.tosha1 -width 40 -relief flat
7337 $top.tosha1 insert 0 $newid
7338 $top.tosha1 conf -state readonly
7339 grid $top.to $top.tosha1 -sticky w
7340 entry $top.tohead -width 60 -relief flat
7341 $top.tohead insert 0 $newhead
7342 $top.tohead conf -state readonly
7343 grid x $top.tohead -sticky w
7344 button $top.rev -text [mc "Reverse"] -command mkpatchrev -padx 5
7345 grid $top.rev x -pady 10
7346 label $top.flab -text [mc "Output file:"]
7347 entry $top.fname -width 60
7348 $top.fname insert 0 [file normalize "patch$patchnum.patch"]
7350 grid $top.flab $top.fname -sticky w
7352 button $top.buts.gen -text [mc "Generate"] -command mkpatchgo
7353 button $top.buts.can -text [mc "Cancel"] -command mkpatchcan
7354 grid $top.buts.gen $top.buts.can
7355 grid columnconfigure $top.buts 0 -weight 1 -uniform a
7356 grid columnconfigure $top.buts 1 -weight 1 -uniform a
7357 grid $top.buts - -pady 10 -sticky ew
7361 proc mkpatchrev {} {
7364 set oldid [$patchtop.fromsha1 get]
7365 set oldhead [$patchtop.fromhead get]
7366 set newid [$patchtop.tosha1 get]
7367 set newhead [$patchtop.tohead get]
7368 foreach e [list fromsha1 fromhead tosha1 tohead] \
7369 v [list $newid $newhead $oldid $oldhead] {
7370 $patchtop.$e conf -state normal
7371 $patchtop.$e delete 0 end
7372 $patchtop.$e insert 0 $v
7373 $patchtop.$e conf -state readonly
7378 global patchtop nullid nullid2
7380 set oldid [$patchtop.fromsha1 get]
7381 set newid [$patchtop.tosha1 get]
7382 set fname [$patchtop.fname get]
7383 set cmd [diffcmd [list $oldid $newid] -p]
7384 # trim off the initial "|"
7385 set cmd [lrange $cmd 1 end]
7386 lappend cmd >$fname &
7387 if {[catch {eval exec $cmd} err]} {
7388 error_popup "[mc "Error creating patch:"] $err"
7390 catch {destroy $patchtop}
7394 proc mkpatchcan {} {
7397 catch {destroy $patchtop}
7402 global rowmenuid mktagtop commitinfo
7406 catch {destroy $top}
7408 label $top.title -text [mc "Create tag"]
7409 grid $top.title - -pady 10
7410 label $top.id -text [mc "ID:"]
7411 entry $top.sha1 -width 40 -relief flat
7412 $top.sha1 insert 0 $rowmenuid
7413 $top.sha1 conf -state readonly
7414 grid $top.id $top.sha1 -sticky w
7415 entry $top.head -width 60 -relief flat
7416 $top.head insert 0 [lindex $commitinfo($rowmenuid) 0]
7417 $top.head conf -state readonly
7418 grid x $top.head -sticky w
7419 label $top.tlab -text [mc "Tag name:"]
7420 entry $top.tag -width 60
7421 grid $top.tlab $top.tag -sticky w
7423 button $top.buts.gen -text [mc "Create"] -command mktaggo
7424 button $top.buts.can -text [mc "Cancel"] -command mktagcan
7425 grid $top.buts.gen $top.buts.can
7426 grid columnconfigure $top.buts 0 -weight 1 -uniform a
7427 grid columnconfigure $top.buts 1 -weight 1 -uniform a
7428 grid $top.buts - -pady 10 -sticky ew
7433 global mktagtop env tagids idtags
7435 set id [$mktagtop.sha1 get]
7436 set tag [$mktagtop.tag get]
7438 error_popup [mc "No tag name specified"]
7441 if {[info exists tagids($tag)]} {
7442 error_popup [mc "Tag \"%s\" already exists" $tag]
7446 exec git tag $tag $id
7448 error_popup "[mc "Error creating tag:"] $err"
7452 set tagids($tag) $id
7453 lappend idtags($id) $tag
7460 proc redrawtags {id} {
7461 global canv linehtag idpos currentid curview cmitlisted
7462 global canvxmax iddrawn circleitem mainheadid circlecolors
7464 if {![commitinview $id $curview]} return
7465 if {![info exists iddrawn($id)]} return
7466 set row [rowofcommit $id]
7467 if {$id eq $mainheadid} {
7470 set ofill [lindex $circlecolors $cmitlisted($curview,$id)]
7472 $canv itemconf $circleitem($row) -fill $ofill
7473 $canv delete tag.$id
7474 set xt [eval drawtags $id $idpos($id)]
7475 $canv coords $linehtag($row) $xt [lindex $idpos($id) 2]
7476 set text [$canv itemcget $linehtag($row) -text]
7477 set font [$canv itemcget $linehtag($row) -font]
7478 set xr [expr {$xt + [font measure $font $text]}]
7479 if {$xr > $canvxmax} {
7483 if {[info exists currentid] && $currentid == $id} {
7491 catch {destroy $mktagtop}
7500 proc writecommit {} {
7501 global rowmenuid wrcomtop commitinfo wrcomcmd
7503 set top .writecommit
7505 catch {destroy $top}
7507 label $top.title -text [mc "Write commit to file"]
7508 grid $top.title - -pady 10
7509 label $top.id -text [mc "ID:"]
7510 entry $top.sha1 -width 40 -relief flat
7511 $top.sha1 insert 0 $rowmenuid
7512 $top.sha1 conf -state readonly
7513 grid $top.id $top.sha1 -sticky w
7514 entry $top.head -width 60 -relief flat
7515 $top.head insert 0 [lindex $commitinfo($rowmenuid) 0]
7516 $top.head conf -state readonly
7517 grid x $top.head -sticky w
7518 label $top.clab -text [mc "Command:"]
7519 entry $top.cmd -width 60 -textvariable wrcomcmd
7520 grid $top.clab $top.cmd -sticky w -pady 10
7521 label $top.flab -text [mc "Output file:"]
7522 entry $top.fname -width 60
7523 $top.fname insert 0 [file normalize "commit-[string range $rowmenuid 0 6]"]
7524 grid $top.flab $top.fname -sticky w
7526 button $top.buts.gen -text [mc "Write"] -command wrcomgo
7527 button $top.buts.can -text [mc "Cancel"] -command wrcomcan
7528 grid $top.buts.gen $top.buts.can
7529 grid columnconfigure $top.buts 0 -weight 1 -uniform a
7530 grid columnconfigure $top.buts 1 -weight 1 -uniform a
7531 grid $top.buts - -pady 10 -sticky ew
7538 set id [$wrcomtop.sha1 get]
7539 set cmd "echo $id | [$wrcomtop.cmd get]"
7540 set fname [$wrcomtop.fname get]
7541 if {[catch {exec sh -c $cmd >$fname &} err]} {
7542 error_popup "[mc "Error writing commit:"] $err"
7544 catch {destroy $wrcomtop}
7551 catch {destroy $wrcomtop}
7556 global rowmenuid mkbrtop
7559 catch {destroy $top}
7561 label $top.title -text [mc "Create new branch"]
7562 grid $top.title - -pady 10
7563 label $top.id -text [mc "ID:"]
7564 entry $top.sha1 -width 40 -relief flat
7565 $top.sha1 insert 0 $rowmenuid
7566 $top.sha1 conf -state readonly
7567 grid $top.id $top.sha1 -sticky w
7568 label $top.nlab -text [mc "Name:"]
7569 entry $top.name -width 40
7570 grid $top.nlab $top.name -sticky w
7572 button $top.buts.go -text [mc "Create"] -command [list mkbrgo $top]
7573 button $top.buts.can -text [mc "Cancel"] -command "catch {destroy $top}"
7574 grid $top.buts.go $top.buts.can
7575 grid columnconfigure $top.buts 0 -weight 1 -uniform a
7576 grid columnconfigure $top.buts 1 -weight 1 -uniform a
7577 grid $top.buts - -pady 10 -sticky ew
7582 global headids idheads
7584 set name [$top.name get]
7585 set id [$top.sha1 get]
7587 error_popup [mc "Please specify a name for the new branch"]
7590 catch {destroy $top}
7594 exec git branch $name $id
7599 set headids($name) $id
7600 lappend idheads($id) $name
7609 proc cherrypick {} {
7610 global rowmenuid curview
7611 global mainhead mainheadid
7613 set oldhead [exec git rev-parse HEAD]
7614 set dheads [descheads $rowmenuid]
7615 if {$dheads ne {} && [lsearch -exact $dheads $oldhead] >= 0} {
7616 set ok [confirm_popup [mc "Commit %s is already\
7617 included in branch %s -- really re-apply it?" \
7618 [string range $rowmenuid 0 7] $mainhead]]
7621 nowbusy cherrypick [mc "Cherry-picking"]
7623 # Unfortunately git-cherry-pick writes stuff to stderr even when
7624 # no error occurs, and exec takes that as an indication of error...
7625 if {[catch {exec sh -c "git cherry-pick -r $rowmenuid 2>&1"} err]} {
7630 set newhead [exec git rev-parse HEAD]
7631 if {$newhead eq $oldhead} {
7633 error_popup [mc "No changes committed"]
7636 addnewchild $newhead $oldhead
7637 if {[commitinview $oldhead $curview]} {
7638 insertrow $newhead $oldhead $curview
7639 if {$mainhead ne {}} {
7640 movehead $newhead $mainhead
7641 movedhead $newhead $mainhead
7643 set mainheadid $newhead
7652 global mainhead rowmenuid confirm_ok resettype
7655 set w ".confirmreset"
7658 wm title $w [mc "Confirm reset"]
7659 message $w.m -text \
7660 [mc "Reset branch %s to %s?" $mainhead [string range $rowmenuid 0 7]] \
7661 -justify center -aspect 1000
7662 pack $w.m -side top -fill x -padx 20 -pady 20
7663 frame $w.f -relief sunken -border 2
7664 message $w.f.rt -text [mc "Reset type:"] -aspect 1000
7665 grid $w.f.rt -sticky w
7667 radiobutton $w.f.soft -value soft -variable resettype -justify left \
7668 -text [mc "Soft: Leave working tree and index untouched"]
7669 grid $w.f.soft -sticky w
7670 radiobutton $w.f.mixed -value mixed -variable resettype -justify left \
7671 -text [mc "Mixed: Leave working tree untouched, reset index"]
7672 grid $w.f.mixed -sticky w
7673 radiobutton $w.f.hard -value hard -variable resettype -justify left \
7674 -text [mc "Hard: Reset working tree and index\n(discard ALL local changes)"]
7675 grid $w.f.hard -sticky w
7676 pack $w.f -side top -fill x
7677 button $w.ok -text [mc OK] -command "set confirm_ok 1; destroy $w"
7678 pack $w.ok -side left -fill x -padx 20 -pady 20
7679 button $w.cancel -text [mc Cancel] -command "destroy $w"
7680 pack $w.cancel -side right -fill x -padx 20 -pady 20
7681 bind $w <Visibility> "grab $w; focus $w"
7683 if {!$confirm_ok} return
7684 if {[catch {set fd [open \
7685 [list | git reset --$resettype $rowmenuid 2>@1] r]} err]} {
7689 filerun $fd [list readresetstat $fd]
7690 nowbusy reset [mc "Resetting"]
7695 proc readresetstat {fd} {
7696 global mainhead mainheadid showlocalchanges rprogcoord
7698 if {[gets $fd line] >= 0} {
7699 if {[regexp {([0-9]+)% \(([0-9]+)/([0-9]+)\)} $line match p m n]} {
7700 set rprogcoord [expr {1.0 * $m / $n}]
7708 if {[catch {close $fd} err]} {
7711 set oldhead $mainheadid
7712 set newhead [exec git rev-parse HEAD]
7713 if {$newhead ne $oldhead} {
7714 movehead $newhead $mainhead
7715 movedhead $newhead $mainhead
7716 set mainheadid $newhead
7720 if {$showlocalchanges} {
7726 # context menu for a head
7727 proc headmenu {x y id head} {
7728 global headmenuid headmenuhead headctxmenu mainhead
7732 set headmenuhead $head
7734 if {$head eq $mainhead} {
7737 $headctxmenu entryconfigure 0 -state $state
7738 $headctxmenu entryconfigure 1 -state $state
7739 tk_popup $headctxmenu $x $y
7743 global headmenuid headmenuhead headids
7744 global showlocalchanges mainheadid
7746 # check the tree is clean first??
7747 nowbusy checkout [mc "Checking out"]
7751 set fd [open [list | git checkout $headmenuhead 2>@1] r]
7755 if {$showlocalchanges} {
7759 filerun $fd [list readcheckoutstat $fd $headmenuhead $headmenuid]
7763 proc readcheckoutstat {fd newhead newheadid} {
7764 global mainhead mainheadid headids showlocalchanges progresscoords
7766 if {[gets $fd line] >= 0} {
7767 if {[regexp {([0-9]+)% \(([0-9]+)/([0-9]+)\)} $line match p m n]} {
7768 set progresscoords [list 0 [expr {1.0 * $m / $n}]]
7773 set progresscoords {0 0}
7776 if {[catch {close $fd} err]} {
7779 set oldmainid $mainheadid
7780 set mainhead $newhead
7781 set mainheadid $newheadid
7782 redrawtags $oldmainid
7783 redrawtags $newheadid
7785 if {$showlocalchanges} {
7791 global headmenuid headmenuhead mainhead
7794 set head $headmenuhead
7796 # this check shouldn't be needed any more...
7797 if {$head eq $mainhead} {
7798 error_popup [mc "Cannot delete the currently checked-out branch"]
7801 set dheads [descheads $id]
7802 if {[llength $dheads] == 1 && $idheads($dheads) eq $head} {
7803 # the stuff on this branch isn't on any other branch
7804 if {![confirm_popup [mc "The commits on branch %s aren't on any other\
7805 branch.\nReally delete branch %s?" $head $head]]} return
7809 if {[catch {exec git branch -D $head} err]} {
7814 removehead $id $head
7815 removedhead $id $head
7822 # Display a list of tags and heads
7824 global showrefstop bgcolor fgcolor selectbgcolor
7825 global bglist fglist reflistfilter reflist maincursor
7828 set showrefstop $top
7829 if {[winfo exists $top]} {
7835 wm title $top [mc "Tags and heads: %s" [file tail [pwd]]]
7836 text $top.list -background $bgcolor -foreground $fgcolor \
7837 -selectbackground $selectbgcolor -font mainfont \
7838 -xscrollcommand "$top.xsb set" -yscrollcommand "$top.ysb set" \
7839 -width 30 -height 20 -cursor $maincursor \
7840 -spacing1 1 -spacing3 1 -state disabled
7841 $top.list tag configure highlight -background $selectbgcolor
7842 lappend bglist $top.list
7843 lappend fglist $top.list
7844 scrollbar $top.ysb -command "$top.list yview" -orient vertical
7845 scrollbar $top.xsb -command "$top.list xview" -orient horizontal
7846 grid $top.list $top.ysb -sticky nsew
7847 grid $top.xsb x -sticky ew
7849 label $top.f.l -text "[mc "Filter"]: "
7850 entry $top.f.e -width 20 -textvariable reflistfilter
7851 set reflistfilter "*"
7852 trace add variable reflistfilter write reflistfilter_change
7853 pack $top.f.e -side right -fill x -expand 1
7854 pack $top.f.l -side left
7855 grid $top.f - -sticky ew -pady 2
7856 button $top.close -command [list destroy $top] -text [mc "Close"]
7858 grid columnconfigure $top 0 -weight 1
7859 grid rowconfigure $top 0 -weight 1
7860 bind $top.list <1> {break}
7861 bind $top.list <B1-Motion> {break}
7862 bind $top.list <ButtonRelease-1> {sel_reflist %W %x %y; break}
7867 proc sel_reflist {w x y} {
7868 global showrefstop reflist headids tagids otherrefids
7870 if {![winfo exists $showrefstop]} return
7871 set l [lindex [split [$w index "@$x,$y"] "."] 0]
7872 set ref [lindex $reflist [expr {$l-1}]]
7873 set n [lindex $ref 0]
7874 switch -- [lindex $ref 1] {
7875 "H" {selbyid $headids($n)}
7876 "T" {selbyid $tagids($n)}
7877 "o" {selbyid $otherrefids($n)}
7879 $showrefstop.list tag add highlight $l.0 "$l.0 lineend"
7882 proc unsel_reflist {} {
7885 if {![info exists showrefstop] || ![winfo exists $showrefstop]} return
7886 $showrefstop.list tag remove highlight 0.0 end
7889 proc reflistfilter_change {n1 n2 op} {
7890 global reflistfilter
7892 after cancel refill_reflist
7893 after 200 refill_reflist
7896 proc refill_reflist {} {
7897 global reflist reflistfilter showrefstop headids tagids otherrefids
7898 global curview commitinterest
7900 if {![info exists showrefstop] || ![winfo exists $showrefstop]} return
7902 foreach n [array names headids] {
7903 if {[string match $reflistfilter $n]} {
7904 if {[commitinview $headids($n) $curview]} {
7905 lappend refs [list $n H]
7907 set commitinterest($headids($n)) {run refill_reflist}
7911 foreach n [array names tagids] {
7912 if {[string match $reflistfilter $n]} {
7913 if {[commitinview $tagids($n) $curview]} {
7914 lappend refs [list $n T]
7916 set commitinterest($tagids($n)) {run refill_reflist}
7920 foreach n [array names otherrefids] {
7921 if {[string match $reflistfilter $n]} {
7922 if {[commitinview $otherrefids($n) $curview]} {
7923 lappend refs [list $n o]
7925 set commitinterest($otherrefids($n)) {run refill_reflist}
7929 set refs [lsort -index 0 $refs]
7930 if {$refs eq $reflist} return
7932 # Update the contents of $showrefstop.list according to the
7933 # differences between $reflist (old) and $refs (new)
7934 $showrefstop.list conf -state normal
7935 $showrefstop.list insert end "\n"
7938 while {$i < [llength $reflist] || $j < [llength $refs]} {
7939 if {$i < [llength $reflist]} {
7940 if {$j < [llength $refs]} {
7941 set cmp [string compare [lindex $reflist $i 0] \
7942 [lindex $refs $j 0]]
7944 set cmp [string compare [lindex $reflist $i 1] \
7945 [lindex $refs $j 1]]
7955 $showrefstop.list delete "[expr {$j+1}].0" "[expr {$j+2}].0"
7963 set l [expr {$j + 1}]
7964 $showrefstop.list image create $l.0 -align baseline \
7965 -image reficon-[lindex $refs $j 1] -padx 2
7966 $showrefstop.list insert $l.1 "[lindex $refs $j 0]\n"
7972 # delete last newline
7973 $showrefstop.list delete end-2c end-1c
7974 $showrefstop.list conf -state disabled
7977 # Stuff for finding nearby tags
7978 proc getallcommits {} {
7979 global allcommits nextarc seeds allccache allcwait cachedarcs allcupdate
7980 global idheads idtags idotherrefs allparents tagobjid
7982 if {![info exists allcommits]} {
7988 set allccache [file join [gitdir] "gitk.cache"]
7990 set f [open $allccache r]
7999 set cmd [list | git rev-list --parents]
8000 set allcupdate [expr {$seeds ne {}}]
8004 set refs [concat [array names idheads] [array names idtags] \
8005 [array names idotherrefs]]
8008 foreach name [array names tagobjid] {
8009 lappend tagobjs $tagobjid($name)
8011 foreach id [lsort -unique $refs] {
8012 if {![info exists allparents($id)] &&
8013 [lsearch -exact $tagobjs $id] < 0} {
8024 set fd [open [concat $cmd $ids] r]
8025 fconfigure $fd -blocking 0
8028 filerun $fd [list getallclines $fd]
8034 # Since most commits have 1 parent and 1 child, we group strings of
8035 # such commits into "arcs" joining branch/merge points (BMPs), which
8036 # are commits that either don't have 1 parent or don't have 1 child.
8038 # arcnos(id) - incoming arcs for BMP, arc we're on for other nodes
8039 # arcout(id) - outgoing arcs for BMP
8040 # arcids(a) - list of IDs on arc including end but not start
8041 # arcstart(a) - BMP ID at start of arc
8042 # arcend(a) - BMP ID at end of arc
8043 # growing(a) - arc a is still growing
8044 # arctags(a) - IDs out of arcids (excluding end) that have tags
8045 # archeads(a) - IDs out of arcids (excluding end) that have heads
8046 # The start of an arc is at the descendent end, so "incoming" means
8047 # coming from descendents, and "outgoing" means going towards ancestors.
8049 proc getallclines {fd} {
8050 global allparents allchildren idtags idheads nextarc
8051 global arcnos arcids arctags arcout arcend arcstart archeads growing
8052 global seeds allcommits cachedarcs allcupdate
8055 while {[incr nid] <= 1000 && [gets $fd line] >= 0} {
8056 set id [lindex $line 0]
8057 if {[info exists allparents($id)]} {
8062 set olds [lrange $line 1 end]
8063 set allparents($id) $olds
8064 if {![info exists allchildren($id)]} {
8065 set allchildren($id) {}
8070 if {[llength $olds] == 1 && [llength $a] == 1} {
8071 lappend arcids($a) $id
8072 if {[info exists idtags($id)]} {
8073 lappend arctags($a) $id
8075 if {[info exists idheads($id)]} {
8076 lappend archeads($a) $id
8078 if {[info exists allparents($olds)]} {
8079 # seen parent already
8080 if {![info exists arcout($olds)]} {
8083 lappend arcids($a) $olds
8084 set arcend($a) $olds
8087 lappend allchildren($olds) $id
8088 lappend arcnos($olds) $a
8092 foreach a $arcnos($id) {
8093 lappend arcids($a) $id
8100 lappend allchildren($p) $id
8101 set a [incr nextarc]
8102 set arcstart($a) $id
8109 if {[info exists allparents($p)]} {
8110 # seen it already, may need to make a new branch
8111 if {![info exists arcout($p)]} {
8114 lappend arcids($a) $p
8118 lappend arcnos($p) $a
8123 global cached_dheads cached_dtags cached_atags
8124 catch {unset cached_dheads}
8125 catch {unset cached_dtags}
8126 catch {unset cached_atags}
8129 return [expr {$nid >= 1000? 2: 1}]
8133 fconfigure $fd -blocking 1
8136 # got an error reading the list of commits
8137 # if we were updating, try rereading the whole thing again
8143 error_popup "[mc "Error reading commit topology information;\
8144 branch and preceding/following tag information\
8145 will be incomplete."]\n($err)"
8148 if {[incr allcommits -1] == 0} {
8158 proc recalcarc {a} {
8159 global arctags archeads arcids idtags idheads
8163 foreach id [lrange $arcids($a) 0 end-1] {
8164 if {[info exists idtags($id)]} {
8167 if {[info exists idheads($id)]} {
8172 set archeads($a) $ah
8176 global arcnos arcids nextarc arctags archeads idtags idheads
8177 global arcstart arcend arcout allparents growing
8180 if {[llength $a] != 1} {
8181 puts "oops splitarc called but [llength $a] arcs already"
8185 set i [lsearch -exact $arcids($a) $p]
8187 puts "oops splitarc $p not in arc $a"
8190 set na [incr nextarc]
8191 if {[info exists arcend($a)]} {
8192 set arcend($na) $arcend($a)
8194 set l [lindex $allparents([lindex $arcids($a) end]) 0]
8195 set j [lsearch -exact $arcnos($l) $a]
8196 set arcnos($l) [lreplace $arcnos($l) $j $j $na]
8198 set tail [lrange $arcids($a) [expr {$i+1}] end]
8199 set arcids($a) [lrange $arcids($a) 0 $i]
8201 set arcstart($na) $p
8203 set arcids($na) $tail
8204 if {[info exists growing($a)]} {
8210 if {[llength $arcnos($id)] == 1} {
8213 set j [lsearch -exact $arcnos($id) $a]
8214 set arcnos($id) [lreplace $arcnos($id) $j $j $na]
8218 # reconstruct tags and heads lists
8219 if {$arctags($a) ne {} || $archeads($a) ne {}} {
8224 set archeads($na) {}
8228 # Update things for a new commit added that is a child of one
8229 # existing commit. Used when cherry-picking.
8230 proc addnewchild {id p} {
8231 global allparents allchildren idtags nextarc
8232 global arcnos arcids arctags arcout arcend arcstart archeads growing
8233 global seeds allcommits
8235 if {![info exists allcommits] || ![info exists arcnos($p)]} return
8236 set allparents($id) [list $p]
8237 set allchildren($id) {}
8240 lappend allchildren($p) $id
8241 set a [incr nextarc]
8242 set arcstart($a) $id
8245 set arcids($a) [list $p]
8247 if {![info exists arcout($p)]} {
8250 lappend arcnos($p) $a
8251 set arcout($id) [list $a]
8254 # This implements a cache for the topology information.
8255 # The cache saves, for each arc, the start and end of the arc,
8256 # the ids on the arc, and the outgoing arcs from the end.
8257 proc readcache {f} {
8258 global arcnos arcids arcout arcstart arcend arctags archeads nextarc
8259 global idtags idheads allparents cachedarcs possible_seeds seeds growing
8264 if {$lim - $a > 500} {
8265 set lim [expr {$a + 500}]
8269 # finish reading the cache and setting up arctags, etc.
8271 if {$line ne "1"} {error "bad final version"}
8273 foreach id [array names idtags] {
8274 if {[info exists arcnos($id)] && [llength $arcnos($id)] == 1 &&
8275 [llength $allparents($id)] == 1} {
8276 set a [lindex $arcnos($id) 0]
8277 if {$arctags($a) eq {}} {
8282 foreach id [array names idheads] {
8283 if {[info exists arcnos($id)] && [llength $arcnos($id)] == 1 &&
8284 [llength $allparents($id)] == 1} {
8285 set a [lindex $arcnos($id) 0]
8286 if {$archeads($a) eq {}} {
8291 foreach id [lsort -unique $possible_seeds] {
8292 if {$arcnos($id) eq {}} {
8298 while {[incr a] <= $lim} {
8300 if {[llength $line] != 3} {error "bad line"}
8301 set s [lindex $line 0]
8303 lappend arcout($s) $a
8304 if {![info exists arcnos($s)]} {
8305 lappend possible_seeds $s
8308 set e [lindex $line 1]
8313 if {![info exists arcout($e)]} {
8317 set arcids($a) [lindex $line 2]
8318 foreach id $arcids($a) {
8319 lappend allparents($s) $id
8321 lappend arcnos($id) $a
8323 if {![info exists allparents($s)]} {
8324 set allparents($s) {}
8329 set nextarc [expr {$a - 1}]
8342 global nextarc cachedarcs possible_seeds
8346 if {[llength $line] != 2 || [lindex $line 0] ne "1"} {error "bad version"}
8347 # make sure it's an integer
8348 set cachedarcs [expr {int([lindex $line 1])}]
8349 if {$cachedarcs < 0} {error "bad number of arcs"}
8351 set possible_seeds {}
8359 proc dropcache {err} {
8360 global allcwait nextarc cachedarcs seeds
8362 #puts "dropping cache ($err)"
8363 foreach v {arcnos arcout arcids arcstart arcend growing \
8364 arctags archeads allparents allchildren} {
8375 proc writecache {f} {
8376 global cachearc cachedarcs allccache
8377 global arcstart arcend arcnos arcids arcout
8381 if {$lim - $a > 1000} {
8382 set lim [expr {$a + 1000}]
8385 while {[incr a] <= $lim} {
8386 if {[info exists arcend($a)]} {
8387 puts $f [list $arcstart($a) $arcend($a) $arcids($a)]
8389 puts $f [list $arcstart($a) {} $arcids($a)]
8394 catch {file delete $allccache}
8395 #puts "writing cache failed ($err)"
8398 set cachearc [expr {$a - 1}]
8399 if {$a > $cachedarcs} {
8408 global nextarc cachedarcs cachearc allccache
8410 if {$nextarc == $cachedarcs} return
8412 set cachedarcs $nextarc
8414 set f [open $allccache w]
8415 puts $f [list 1 $cachedarcs]
8420 # Returns 1 if a is an ancestor of b, -1 if b is an ancestor of a,
8421 # or 0 if neither is true.
8422 proc anc_or_desc {a b} {
8423 global arcout arcstart arcend arcnos cached_isanc
8425 if {$arcnos($a) eq $arcnos($b)} {
8426 # Both are on the same arc(s); either both are the same BMP,
8427 # or if one is not a BMP, the other is also not a BMP or is
8428 # the BMP at end of the arc (and it only has 1 incoming arc).
8429 # Or both can be BMPs with no incoming arcs.
8430 if {$a eq $b || $arcnos($a) eq {}} {
8433 # assert {[llength $arcnos($a)] == 1}
8434 set arc [lindex $arcnos($a) 0]
8435 set i [lsearch -exact $arcids($arc) $a]
8436 set j [lsearch -exact $arcids($arc) $b]
8437 if {$i < 0 || $i > $j} {
8444 if {![info exists arcout($a)]} {
8445 set arc [lindex $arcnos($a) 0]
8446 if {[info exists arcend($arc)]} {
8447 set aend $arcend($arc)
8451 set a $arcstart($arc)
8455 if {![info exists arcout($b)]} {
8456 set arc [lindex $arcnos($b) 0]
8457 if {[info exists arcend($arc)]} {
8458 set bend $arcend($arc)
8462 set b $arcstart($arc)
8472 if {[info exists cached_isanc($a,$bend)]} {
8473 if {$cached_isanc($a,$bend)} {
8477 if {[info exists cached_isanc($b,$aend)]} {
8478 if {$cached_isanc($b,$aend)} {
8481 if {[info exists cached_isanc($a,$bend)]} {
8486 set todo [list $a $b]
8489 for {set i 0} {$i < [llength $todo]} {incr i} {
8490 set x [lindex $todo $i]
8491 if {$anc($x) eq {}} {
8494 foreach arc $arcnos($x) {
8495 set xd $arcstart($arc)
8497 set cached_isanc($a,$bend) 1
8498 set cached_isanc($b,$aend) 0
8500 } elseif {$xd eq $aend} {
8501 set cached_isanc($b,$aend) 1
8502 set cached_isanc($a,$bend) 0
8505 if {![info exists anc($xd)]} {
8506 set anc($xd) $anc($x)
8508 } elseif {$anc($xd) ne $anc($x)} {
8513 set cached_isanc($a,$bend) 0
8514 set cached_isanc($b,$aend) 0
8518 # This identifies whether $desc has an ancestor that is
8519 # a growing tip of the graph and which is not an ancestor of $anc
8520 # and returns 0 if so and 1 if not.
8521 # If we subsequently discover a tag on such a growing tip, and that
8522 # turns out to be a descendent of $anc (which it could, since we
8523 # don't necessarily see children before parents), then $desc
8524 # isn't a good choice to display as a descendent tag of
8525 # $anc (since it is the descendent of another tag which is
8526 # a descendent of $anc). Similarly, $anc isn't a good choice to
8527 # display as a ancestor tag of $desc.
8529 proc is_certain {desc anc} {
8530 global arcnos arcout arcstart arcend growing problems
8533 if {[llength $arcnos($anc)] == 1} {
8534 # tags on the same arc are certain
8535 if {$arcnos($desc) eq $arcnos($anc)} {
8538 if {![info exists arcout($anc)]} {
8539 # if $anc is partway along an arc, use the start of the arc instead
8540 set a [lindex $arcnos($anc) 0]
8541 set anc $arcstart($a)
8544 if {[llength $arcnos($desc)] > 1 || [info exists arcout($desc)]} {
8547 set a [lindex $arcnos($desc) 0]
8553 set anclist [list $x]
8557 for {set i 0} {$i < [llength $anclist] && ($nnh > 0 || $ngrowanc > 0)} {incr i} {
8558 set x [lindex $anclist $i]
8563 foreach a $arcout($x) {
8564 if {[info exists growing($a)]} {
8565 if {![info exists growanc($x)] && $dl($x)} {
8571 if {[info exists dl($y)]} {
8575 if {![info exists done($y)]} {
8578 if {[info exists growanc($x)]} {
8582 for {set k 0} {$k < [llength $xl]} {incr k} {
8583 set z [lindex $xl $k]
8584 foreach c $arcout($z) {
8585 if {[info exists arcend($c)]} {
8587 if {[info exists dl($v)] && $dl($v)} {
8589 if {![info exists done($v)]} {
8592 if {[info exists growanc($v)]} {
8602 } elseif {$y eq $anc || !$dl($x)} {
8613 foreach x [array names growanc] {
8622 proc validate_arctags {a} {
8623 global arctags idtags
8627 foreach id $arctags($a) {
8629 if {![info exists idtags($id)]} {
8630 set na [lreplace $na $i $i]
8637 proc validate_archeads {a} {
8638 global archeads idheads
8641 set na $archeads($a)
8642 foreach id $archeads($a) {
8644 if {![info exists idheads($id)]} {
8645 set na [lreplace $na $i $i]
8649 set archeads($a) $na
8652 # Return the list of IDs that have tags that are descendents of id,
8653 # ignoring IDs that are descendents of IDs already reported.
8654 proc desctags {id} {
8655 global arcnos arcstart arcids arctags idtags allparents
8656 global growing cached_dtags
8658 if {![info exists allparents($id)]} {
8661 set t1 [clock clicks -milliseconds]
8663 if {[llength $arcnos($id)] == 1 && [llength $allparents($id)] == 1} {
8664 # part-way along an arc; check that arc first
8665 set a [lindex $arcnos($id) 0]
8666 if {$arctags($a) ne {}} {
8668 set i [lsearch -exact $arcids($a) $id]
8670 foreach t $arctags($a) {
8671 set j [lsearch -exact $arcids($a) $t]
8679 set id $arcstart($a)
8680 if {[info exists idtags($id)]} {
8684 if {[info exists cached_dtags($id)]} {
8685 return $cached_dtags($id)
8692 for {set i 0} {$i < [llength $todo] && $nc > 0} {incr i} {
8693 set id [lindex $todo $i]
8695 set ta [info exists hastaggedancestor($id)]
8699 # ignore tags on starting node
8700 if {!$ta && $i > 0} {
8701 if {[info exists idtags($id)]} {
8704 } elseif {[info exists cached_dtags($id)]} {
8705 set tagloc($id) $cached_dtags($id)
8709 foreach a $arcnos($id) {
8711 if {!$ta && $arctags($a) ne {}} {
8713 if {$arctags($a) ne {}} {
8714 lappend tagloc($id) [lindex $arctags($a) end]
8717 if {$ta || $arctags($a) ne {}} {
8718 set tomark [list $d]
8719 for {set j 0} {$j < [llength $tomark]} {incr j} {
8720 set dd [lindex $tomark $j]
8721 if {![info exists hastaggedancestor($dd)]} {
8722 if {[info exists done($dd)]} {
8723 foreach b $arcnos($dd) {
8724 lappend tomark $arcstart($b)
8726 if {[info exists tagloc($dd)]} {
8729 } elseif {[info exists queued($dd)]} {
8732 set hastaggedancestor($dd) 1
8736 if {![info exists queued($d)]} {
8739 if {![info exists hastaggedancestor($d)]} {
8746 foreach id [array names tagloc] {
8747 if {![info exists hastaggedancestor($id)]} {
8748 foreach t $tagloc($id) {
8749 if {[lsearch -exact $tags $t] < 0} {
8755 set t2 [clock clicks -milliseconds]
8758 # remove tags that are descendents of other tags
8759 for {set i 0} {$i < [llength $tags]} {incr i} {
8760 set a [lindex $tags $i]
8761 for {set j 0} {$j < $i} {incr j} {
8762 set b [lindex $tags $j]
8763 set r [anc_or_desc $a $b]
8765 set tags [lreplace $tags $j $j]
8768 } elseif {$r == -1} {
8769 set tags [lreplace $tags $i $i]
8776 if {[array names growing] ne {}} {
8777 # graph isn't finished, need to check if any tag could get
8778 # eclipsed by another tag coming later. Simply ignore any
8779 # tags that could later get eclipsed.
8782 if {[is_certain $t $origid]} {
8786 if {$tags eq $ctags} {
8787 set cached_dtags($origid) $tags
8792 set cached_dtags($origid) $tags
8794 set t3 [clock clicks -milliseconds]
8795 if {0 && $t3 - $t1 >= 100} {
8796 puts "iterating descendents ($loopix/[llength $todo] nodes) took\
8797 [expr {$t2-$t1}]+[expr {$t3-$t2}]ms, $nc candidates left"
8803 global arcnos arcids arcout arcend arctags idtags allparents
8804 global growing cached_atags
8806 if {![info exists allparents($id)]} {
8809 set t1 [clock clicks -milliseconds]
8811 if {[llength $arcnos($id)] == 1 && [llength $allparents($id)] == 1} {
8812 # part-way along an arc; check that arc first
8813 set a [lindex $arcnos($id) 0]
8814 if {$arctags($a) ne {}} {
8816 set i [lsearch -exact $arcids($a) $id]
8817 foreach t $arctags($a) {
8818 set j [lsearch -exact $arcids($a) $t]
8824 if {![info exists arcend($a)]} {
8828 if {[info exists idtags($id)]} {
8832 if {[info exists cached_atags($id)]} {
8833 return $cached_atags($id)
8841 for {set i 0} {$i < [llength $todo] && $nc > 0} {incr i} {
8842 set id [lindex $todo $i]
8844 set td [info exists hastaggeddescendent($id)]
8848 # ignore tags on starting node
8849 if {!$td && $i > 0} {
8850 if {[info exists idtags($id)]} {
8853 } elseif {[info exists cached_atags($id)]} {
8854 set tagloc($id) $cached_atags($id)
8858 foreach a $arcout($id) {
8859 if {!$td && $arctags($a) ne {}} {
8861 if {$arctags($a) ne {}} {
8862 lappend tagloc($id) [lindex $arctags($a) 0]
8865 if {![info exists arcend($a)]} continue
8867 if {$td || $arctags($a) ne {}} {
8868 set tomark [list $d]
8869 for {set j 0} {$j < [llength $tomark]} {incr j} {
8870 set dd [lindex $tomark $j]
8871 if {![info exists hastaggeddescendent($dd)]} {
8872 if {[info exists done($dd)]} {
8873 foreach b $arcout($dd) {
8874 if {[info exists arcend($b)]} {
8875 lappend tomark $arcend($b)
8878 if {[info exists tagloc($dd)]} {
8881 } elseif {[info exists queued($dd)]} {
8884 set hastaggeddescendent($dd) 1
8888 if {![info exists queued($d)]} {
8891 if {![info exists hastaggeddescendent($d)]} {
8897 set t2 [clock clicks -milliseconds]
8900 foreach id [array names tagloc] {
8901 if {![info exists hastaggeddescendent($id)]} {
8902 foreach t $tagloc($id) {
8903 if {[lsearch -exact $tags $t] < 0} {
8910 # remove tags that are ancestors of other tags
8911 for {set i 0} {$i < [llength $tags]} {incr i} {
8912 set a [lindex $tags $i]
8913 for {set j 0} {$j < $i} {incr j} {
8914 set b [lindex $tags $j]
8915 set r [anc_or_desc $a $b]
8917 set tags [lreplace $tags $j $j]
8920 } elseif {$r == 1} {
8921 set tags [lreplace $tags $i $i]
8928 if {[array names growing] ne {}} {
8929 # graph isn't finished, need to check if any tag could get
8930 # eclipsed by another tag coming later. Simply ignore any
8931 # tags that could later get eclipsed.
8934 if {[is_certain $origid $t]} {
8938 if {$tags eq $ctags} {
8939 set cached_atags($origid) $tags
8944 set cached_atags($origid) $tags
8946 set t3 [clock clicks -milliseconds]
8947 if {0 && $t3 - $t1 >= 100} {
8948 puts "iterating ancestors ($loopix/[llength $todo] nodes) took\
8949 [expr {$t2-$t1}]+[expr {$t3-$t2}]ms, $nc candidates left"
8954 # Return the list of IDs that have heads that are descendents of id,
8955 # including id itself if it has a head.
8956 proc descheads {id} {
8957 global arcnos arcstart arcids archeads idheads cached_dheads
8960 if {![info exists allparents($id)]} {
8964 if {[llength $arcnos($id)] == 1 && [llength $allparents($id)] == 1} {
8965 # part-way along an arc; check it first
8966 set a [lindex $arcnos($id) 0]
8967 if {$archeads($a) ne {}} {
8968 validate_archeads $a
8969 set i [lsearch -exact $arcids($a) $id]
8970 foreach t $archeads($a) {
8971 set j [lsearch -exact $arcids($a) $t]
8976 set id $arcstart($a)
8982 for {set i 0} {$i < [llength $todo]} {incr i} {
8983 set id [lindex $todo $i]
8984 if {[info exists cached_dheads($id)]} {
8985 set ret [concat $ret $cached_dheads($id)]
8987 if {[info exists idheads($id)]} {
8990 foreach a $arcnos($id) {
8991 if {$archeads($a) ne {}} {
8992 validate_archeads $a
8993 if {$archeads($a) ne {}} {
8994 set ret [concat $ret $archeads($a)]
8998 if {![info exists seen($d)]} {
9005 set ret [lsort -unique $ret]
9006 set cached_dheads($origid) $ret
9007 return [concat $ret $aret]
9010 proc addedtag {id} {
9011 global arcnos arcout cached_dtags cached_atags
9013 if {![info exists arcnos($id)]} return
9014 if {![info exists arcout($id)]} {
9015 recalcarc [lindex $arcnos($id) 0]
9017 catch {unset cached_dtags}
9018 catch {unset cached_atags}
9021 proc addedhead {hid head} {
9022 global arcnos arcout cached_dheads
9024 if {![info exists arcnos($hid)]} return
9025 if {![info exists arcout($hid)]} {
9026 recalcarc [lindex $arcnos($hid) 0]
9028 catch {unset cached_dheads}
9031 proc removedhead {hid head} {
9032 global cached_dheads
9034 catch {unset cached_dheads}
9037 proc movedhead {hid head} {
9038 global arcnos arcout cached_dheads
9040 if {![info exists arcnos($hid)]} return
9041 if {![info exists arcout($hid)]} {
9042 recalcarc [lindex $arcnos($hid) 0]
9044 catch {unset cached_dheads}
9047 proc changedrefs {} {
9048 global cached_dheads cached_dtags cached_atags
9049 global arctags archeads arcnos arcout idheads idtags
9051 foreach id [concat [array names idheads] [array names idtags]] {
9052 if {[info exists arcnos($id)] && ![info exists arcout($id)]} {
9053 set a [lindex $arcnos($id) 0]
9054 if {![info exists donearc($a)]} {
9060 catch {unset cached_dtags}
9061 catch {unset cached_atags}
9062 catch {unset cached_dheads}
9065 proc rereadrefs {} {
9066 global idtags idheads idotherrefs mainheadid
9068 set refids [concat [array names idtags] \
9069 [array names idheads] [array names idotherrefs]]
9070 foreach id $refids {
9071 if {![info exists ref($id)]} {
9072 set ref($id) [listrefs $id]
9075 set oldmainhead $mainheadid
9078 set refids [lsort -unique [concat $refids [array names idtags] \
9079 [array names idheads] [array names idotherrefs]]]
9080 foreach id $refids {
9081 set v [listrefs $id]
9082 if {![info exists ref($id)] || $ref($id) != $v} {
9086 if {$oldmainhead ne $mainheadid} {
9087 redrawtags $oldmainhead
9088 redrawtags $mainheadid
9093 proc listrefs {id} {
9094 global idtags idheads idotherrefs
9097 if {[info exists idtags($id)]} {
9101 if {[info exists idheads($id)]} {
9105 if {[info exists idotherrefs($id)]} {
9106 set z $idotherrefs($id)
9108 return [list $x $y $z]
9111 proc showtag {tag isnew} {
9112 global ctext tagcontents tagids linknum tagobjid
9115 addtohistory [list showtag $tag 0]
9117 $ctext conf -state normal
9121 if {![info exists tagcontents($tag)]} {
9123 set tagcontents($tag) [exec git cat-file tag $tagobjid($tag)]
9126 if {[info exists tagcontents($tag)]} {
9127 set text $tagcontents($tag)
9129 set text "[mc "Tag"]: $tag\n[mc "Id"]: $tagids($tag)"
9131 appendwithlinks $text {}
9132 $ctext conf -state disabled
9144 if {[info exists gitktmpdir]} {
9145 catch {file delete -force $gitktmpdir}
9149 proc mkfontdisp {font top which} {
9150 global fontattr fontpref $font
9152 set fontpref($font) [set $font]
9153 button $top.${font}but -text $which -font optionfont \
9154 -command [list choosefont $font $which]
9155 label $top.$font -relief flat -font $font \
9156 -text $fontattr($font,family) -justify left
9157 grid x $top.${font}but $top.$font -sticky w
9160 proc choosefont {font which} {
9161 global fontparam fontlist fonttop fontattr
9163 set fontparam(which) $which
9164 set fontparam(font) $font
9165 set fontparam(family) [font actual $font -family]
9166 set fontparam(size) $fontattr($font,size)
9167 set fontparam(weight) $fontattr($font,weight)
9168 set fontparam(slant) $fontattr($font,slant)
9171 if {![winfo exists $top]} {
9173 eval font config sample [font actual $font]
9175 wm title $top [mc "Gitk font chooser"]
9176 label $top.l -textvariable fontparam(which)
9177 pack $top.l -side top
9178 set fontlist [lsort [font families]]
9180 listbox $top.f.fam -listvariable fontlist \
9181 -yscrollcommand [list $top.f.sb set]
9182 bind $top.f.fam <<ListboxSelect>> selfontfam
9183 scrollbar $top.f.sb -command [list $top.f.fam yview]
9184 pack $top.f.sb -side right -fill y
9185 pack $top.f.fam -side left -fill both -expand 1
9186 pack $top.f -side top -fill both -expand 1
9188 spinbox $top.g.size -from 4 -to 40 -width 4 \
9189 -textvariable fontparam(size) \
9190 -validatecommand {string is integer -strict %s}
9191 checkbutton $top.g.bold -padx 5 \
9192 -font {{Times New Roman} 12 bold} -text [mc "B"] -indicatoron 0 \
9193 -variable fontparam(weight) -onvalue bold -offvalue normal
9194 checkbutton $top.g.ital -padx 5 \
9195 -font {{Times New Roman} 12 italic} -text [mc "I"] -indicatoron 0 \
9196 -variable fontparam(slant) -onvalue italic -offvalue roman
9197 pack $top.g.size $top.g.bold $top.g.ital -side left
9198 pack $top.g -side top
9199 canvas $top.c -width 150 -height 50 -border 2 -relief sunk \
9201 $top.c create text 100 25 -anchor center -text $which -font sample \
9202 -fill black -tags text
9203 bind $top.c <Configure> [list centertext $top.c]
9204 pack $top.c -side top -fill x
9206 button $top.buts.ok -text [mc "OK"] -command fontok -default active
9207 button $top.buts.can -text [mc "Cancel"] -command fontcan -default normal
9208 grid $top.buts.ok $top.buts.can
9209 grid columnconfigure $top.buts 0 -weight 1 -uniform a
9210 grid columnconfigure $top.buts 1 -weight 1 -uniform a
9211 pack $top.buts -side bottom -fill x
9212 trace add variable fontparam write chg_fontparam
9215 $top.c itemconf text -text $which
9217 set i [lsearch -exact $fontlist $fontparam(family)]
9219 $top.f.fam selection set $i
9224 proc centertext {w} {
9225 $w coords text [expr {[winfo width $w] / 2}] [expr {[winfo height $w] / 2}]
9229 global fontparam fontpref prefstop
9231 set f $fontparam(font)
9232 set fontpref($f) [list $fontparam(family) $fontparam(size)]
9233 if {$fontparam(weight) eq "bold"} {
9234 lappend fontpref($f) "bold"
9236 if {$fontparam(slant) eq "italic"} {
9237 lappend fontpref($f) "italic"
9240 $w conf -text $fontparam(family) -font $fontpref($f)
9246 global fonttop fontparam
9248 if {[info exists fonttop]} {
9249 catch {destroy $fonttop}
9250 catch {font delete sample}
9256 proc selfontfam {} {
9257 global fonttop fontparam
9259 set i [$fonttop.f.fam curselection]
9261 set fontparam(family) [$fonttop.f.fam get $i]
9265 proc chg_fontparam {v sub op} {
9268 font config sample -$sub $fontparam($sub)
9272 global maxwidth maxgraphpct
9273 global oldprefs prefstop showneartags showlocalchanges
9274 global bgcolor fgcolor ctext diffcolors selectbgcolor
9275 global tabstop limitdiffs autoselect extdifftool
9279 if {[winfo exists $top]} {
9283 foreach v {maxwidth maxgraphpct showneartags showlocalchanges \
9284 limitdiffs tabstop} {
9285 set oldprefs($v) [set $v]
9288 wm title $top [mc "Gitk preferences"]
9289 label $top.ldisp -text [mc "Commit list display options"]
9290 grid $top.ldisp - -sticky w -pady 10
9291 label $top.spacer -text " "
9292 label $top.maxwidthl -text [mc "Maximum graph width (lines)"] \
9294 spinbox $top.maxwidth -from 0 -to 100 -width 4 -textvariable maxwidth
9295 grid $top.spacer $top.maxwidthl $top.maxwidth -sticky w
9296 label $top.maxpctl -text [mc "Maximum graph width (% of pane)"] \
9298 spinbox $top.maxpct -from 1 -to 100 -width 4 -textvariable maxgraphpct
9299 grid x $top.maxpctl $top.maxpct -sticky w
9300 frame $top.showlocal
9301 label $top.showlocal.l -text [mc "Show local changes"] -font optionfont
9302 checkbutton $top.showlocal.b -variable showlocalchanges
9303 pack $top.showlocal.b $top.showlocal.l -side left
9304 grid x $top.showlocal -sticky w
9305 frame $top.autoselect
9306 label $top.autoselect.l -text [mc "Auto-select SHA1"] -font optionfont
9307 checkbutton $top.autoselect.b -variable autoselect
9308 pack $top.autoselect.b $top.autoselect.l -side left
9309 grid x $top.autoselect -sticky w
9311 label $top.ddisp -text [mc "Diff display options"]
9312 grid $top.ddisp - -sticky w -pady 10
9313 label $top.tabstopl -text [mc "Tab spacing"] -font optionfont
9314 spinbox $top.tabstop -from 1 -to 20 -width 4 -textvariable tabstop
9315 grid x $top.tabstopl $top.tabstop -sticky w
9317 label $top.ntag.l -text [mc "Display nearby tags"] -font optionfont
9318 checkbutton $top.ntag.b -variable showneartags
9319 pack $top.ntag.b $top.ntag.l -side left
9320 grid x $top.ntag -sticky w
9322 label $top.ldiff.l -text [mc "Limit diffs to listed paths"] -font optionfont
9323 checkbutton $top.ldiff.b -variable limitdiffs
9324 pack $top.ldiff.b $top.ldiff.l -side left
9325 grid x $top.ldiff -sticky w
9327 entry $top.extdifft -textvariable extdifftool
9329 label $top.extdifff.l -text [mc "External diff tool" ] -font optionfont \
9331 button $top.extdifff.b -text [mc "Choose..."] -font optionfont \
9332 -command choose_extdiff
9333 pack $top.extdifff.l $top.extdifff.b -side left
9334 grid x $top.extdifff $top.extdifft -sticky w
9336 label $top.cdisp -text [mc "Colors: press to choose"]
9337 grid $top.cdisp - -sticky w -pady 10
9338 label $top.bg -padx 40 -relief sunk -background $bgcolor
9339 button $top.bgbut -text [mc "Background"] -font optionfont \
9340 -command [list choosecolor bgcolor {} $top.bg background setbg]
9341 grid x $top.bgbut $top.bg -sticky w
9342 label $top.fg -padx 40 -relief sunk -background $fgcolor
9343 button $top.fgbut -text [mc "Foreground"] -font optionfont \
9344 -command [list choosecolor fgcolor {} $top.fg foreground setfg]
9345 grid x $top.fgbut $top.fg -sticky w
9346 label $top.diffold -padx 40 -relief sunk -background [lindex $diffcolors 0]
9347 button $top.diffoldbut -text [mc "Diff: old lines"] -font optionfont \
9348 -command [list choosecolor diffcolors 0 $top.diffold "diff old lines" \
9349 [list $ctext tag conf d0 -foreground]]
9350 grid x $top.diffoldbut $top.diffold -sticky w
9351 label $top.diffnew -padx 40 -relief sunk -background [lindex $diffcolors 1]
9352 button $top.diffnewbut -text [mc "Diff: new lines"] -font optionfont \
9353 -command [list choosecolor diffcolors 1 $top.diffnew "diff new lines" \
9354 [list $ctext tag conf d1 -foreground]]
9355 grid x $top.diffnewbut $top.diffnew -sticky w
9356 label $top.hunksep -padx 40 -relief sunk -background [lindex $diffcolors 2]
9357 button $top.hunksepbut -text [mc "Diff: hunk header"] -font optionfont \
9358 -command [list choosecolor diffcolors 2 $top.hunksep \
9359 "diff hunk header" \
9360 [list $ctext tag conf hunksep -foreground]]
9361 grid x $top.hunksepbut $top.hunksep -sticky w
9362 label $top.selbgsep -padx 40 -relief sunk -background $selectbgcolor
9363 button $top.selbgbut -text [mc "Select bg"] -font optionfont \
9364 -command [list choosecolor selectbgcolor {} $top.selbgsep background setselbg]
9365 grid x $top.selbgbut $top.selbgsep -sticky w
9367 label $top.cfont -text [mc "Fonts: press to choose"]
9368 grid $top.cfont - -sticky w -pady 10
9369 mkfontdisp mainfont $top [mc "Main font"]
9370 mkfontdisp textfont $top [mc "Diff display font"]
9371 mkfontdisp uifont $top [mc "User interface font"]
9374 button $top.buts.ok -text [mc "OK"] -command prefsok -default active
9375 button $top.buts.can -text [mc "Cancel"] -command prefscan -default normal
9376 grid $top.buts.ok $top.buts.can
9377 grid columnconfigure $top.buts 0 -weight 1 -uniform a
9378 grid columnconfigure $top.buts 1 -weight 1 -uniform a
9379 grid $top.buts - - -pady 10 -sticky ew
9380 bind $top <Visibility> "focus $top.buts.ok"
9383 proc choose_extdiff {} {
9386 set prog [tk_getOpenFile -title "External diff tool" -multiple false]
9388 set extdifftool $prog
9392 proc choosecolor {v vi w x cmd} {
9395 set c [tk_chooseColor -initialcolor [lindex [set $v] $vi] \
9396 -title [mc "Gitk: choose color for %s" $x]]
9397 if {$c eq {}} return
9398 $w conf -background $c
9404 global bglist cflist
9406 $w configure -selectbackground $c
9408 $cflist tag configure highlight \
9409 -background [$cflist cget -selectbackground]
9410 allcanvs itemconf secsel -fill $c
9417 $w conf -background $c
9425 $w conf -foreground $c
9427 allcanvs itemconf text -fill $c
9428 $canv itemconf circle -outline $c
9432 global oldprefs prefstop
9434 foreach v {maxwidth maxgraphpct showneartags showlocalchanges \
9435 limitdiffs tabstop} {
9437 set $v $oldprefs($v)
9439 catch {destroy $prefstop}
9445 global maxwidth maxgraphpct
9446 global oldprefs prefstop showneartags showlocalchanges
9447 global fontpref mainfont textfont uifont
9448 global limitdiffs treediffs
9450 catch {destroy $prefstop}
9454 if {$mainfont ne $fontpref(mainfont)} {
9455 set mainfont $fontpref(mainfont)
9456 parsefont mainfont $mainfont
9457 eval font configure mainfont [fontflags mainfont]
9458 eval font configure mainfontbold [fontflags mainfont 1]
9462 if {$textfont ne $fontpref(textfont)} {
9463 set textfont $fontpref(textfont)
9464 parsefont textfont $textfont
9465 eval font configure textfont [fontflags textfont]
9466 eval font configure textfontbold [fontflags textfont 1]
9468 if {$uifont ne $fontpref(uifont)} {
9469 set uifont $fontpref(uifont)
9470 parsefont uifont $uifont
9471 eval font configure uifont [fontflags uifont]
9474 if {$showlocalchanges != $oldprefs(showlocalchanges)} {
9475 if {$showlocalchanges} {
9481 if {$limitdiffs != $oldprefs(limitdiffs)} {
9482 # treediffs elements are limited by path
9483 catch {unset treediffs}
9485 if {$fontchanged || $maxwidth != $oldprefs(maxwidth)
9486 || $maxgraphpct != $oldprefs(maxgraphpct)} {
9488 } elseif {$showneartags != $oldprefs(showneartags) ||
9489 $limitdiffs != $oldprefs(limitdiffs)} {
9494 proc formatdate {d} {
9495 global datetimeformat
9497 set d [clock format $d -format $datetimeformat]
9502 # This list of encoding names and aliases is distilled from
9503 # http://www.iana.org/assignments/character-sets.
9504 # Not all of them are supported by Tcl.
9505 set encoding_aliases {
9506 { ANSI_X3.4-1968 iso-ir-6 ANSI_X3.4-1986 ISO_646.irv:1991 ASCII
9507 ISO646-US US-ASCII us IBM367 cp367 csASCII }
9508 { ISO-10646-UTF-1 csISO10646UTF1 }
9509 { ISO_646.basic:1983 ref csISO646basic1983 }
9510 { INVARIANT csINVARIANT }
9511 { ISO_646.irv:1983 iso-ir-2 irv csISO2IntlRefVersion }
9512 { BS_4730 iso-ir-4 ISO646-GB gb uk csISO4UnitedKingdom }
9513 { NATS-SEFI iso-ir-8-1 csNATSSEFI }
9514 { NATS-SEFI-ADD iso-ir-8-2 csNATSSEFIADD }
9515 { NATS-DANO iso-ir-9-1 csNATSDANO }
9516 { NATS-DANO-ADD iso-ir-9-2 csNATSDANOADD }
9517 { SEN_850200_B iso-ir-10 FI ISO646-FI ISO646-SE se csISO10Swedish }
9518 { SEN_850200_C iso-ir-11 ISO646-SE2 se2 csISO11SwedishForNames }
9519 { KS_C_5601-1987 iso-ir-149 KS_C_5601-1989 KSC_5601 korean csKSC56011987 }
9520 { ISO-2022-KR csISO2022KR }
9522 { ISO-2022-JP csISO2022JP }
9523 { ISO-2022-JP-2 csISO2022JP2 }
9524 { JIS_C6220-1969-jp JIS_C6220-1969 iso-ir-13 katakana x0201-7
9526 { JIS_C6220-1969-ro iso-ir-14 jp ISO646-JP csISO14JISC6220ro }
9527 { IT iso-ir-15 ISO646-IT csISO15Italian }
9528 { PT iso-ir-16 ISO646-PT csISO16Portuguese }
9529 { ES iso-ir-17 ISO646-ES csISO17Spanish }
9530 { greek7-old iso-ir-18 csISO18Greek7Old }
9531 { latin-greek iso-ir-19 csISO19LatinGreek }
9532 { DIN_66003 iso-ir-21 de ISO646-DE csISO21German }
9533 { NF_Z_62-010_(1973) iso-ir-25 ISO646-FR1 csISO25French }
9534 { Latin-greek-1 iso-ir-27 csISO27LatinGreek1 }
9535 { ISO_5427 iso-ir-37 csISO5427Cyrillic }
9536 { JIS_C6226-1978 iso-ir-42 csISO42JISC62261978 }
9537 { BS_viewdata iso-ir-47 csISO47BSViewdata }
9538 { INIS iso-ir-49 csISO49INIS }
9539 { INIS-8 iso-ir-50 csISO50INIS8 }
9540 { INIS-cyrillic iso-ir-51 csISO51INISCyrillic }
9541 { ISO_5427:1981 iso-ir-54 ISO5427Cyrillic1981 }
9542 { ISO_5428:1980 iso-ir-55 csISO5428Greek }
9543 { GB_1988-80 iso-ir-57 cn ISO646-CN csISO57GB1988 }
9544 { GB_2312-80 iso-ir-58 chinese csISO58GB231280 }
9545 { NS_4551-1 iso-ir-60 ISO646-NO no csISO60DanishNorwegian
9547 { NS_4551-2 ISO646-NO2 iso-ir-61 no2 csISO61Norwegian2 }
9548 { NF_Z_62-010 iso-ir-69 ISO646-FR fr csISO69French }
9549 { videotex-suppl iso-ir-70 csISO70VideotexSupp1 }
9550 { PT2 iso-ir-84 ISO646-PT2 csISO84Portuguese2 }
9551 { ES2 iso-ir-85 ISO646-ES2 csISO85Spanish2 }
9552 { MSZ_7795.3 iso-ir-86 ISO646-HU hu csISO86Hungarian }
9553 { JIS_C6226-1983 iso-ir-87 x0208 JIS_X0208-1983 csISO87JISX0208 }
9554 { greek7 iso-ir-88 csISO88Greek7 }
9555 { ASMO_449 ISO_9036 arabic7 iso-ir-89 csISO89ASMO449 }
9556 { iso-ir-90 csISO90 }
9557 { JIS_C6229-1984-a iso-ir-91 jp-ocr-a csISO91JISC62291984a }
9558 { JIS_C6229-1984-b iso-ir-92 ISO646-JP-OCR-B jp-ocr-b
9559 csISO92JISC62991984b }
9560 { JIS_C6229-1984-b-add iso-ir-93 jp-ocr-b-add csISO93JIS62291984badd }
9561 { JIS_C6229-1984-hand iso-ir-94 jp-ocr-hand csISO94JIS62291984hand }
9562 { JIS_C6229-1984-hand-add iso-ir-95 jp-ocr-hand-add
9563 csISO95JIS62291984handadd }
9564 { JIS_C6229-1984-kana iso-ir-96 csISO96JISC62291984kana }
9565 { ISO_2033-1983 iso-ir-98 e13b csISO2033 }
9566 { ANSI_X3.110-1983 iso-ir-99 CSA_T500-1983 NAPLPS csISO99NAPLPS }
9567 { ISO_8859-1:1987 iso-ir-100 ISO_8859-1 ISO-8859-1 latin1 l1 IBM819
9569 { ISO_8859-2:1987 iso-ir-101 ISO_8859-2 ISO-8859-2 latin2 l2 csISOLatin2 }
9570 { T.61-7bit iso-ir-102 csISO102T617bit }
9571 { T.61-8bit T.61 iso-ir-103 csISO103T618bit }
9572 { ISO_8859-3:1988 iso-ir-109 ISO_8859-3 ISO-8859-3 latin3 l3 csISOLatin3 }
9573 { ISO_8859-4:1988 iso-ir-110 ISO_8859-4 ISO-8859-4 latin4 l4 csISOLatin4 }
9574 { ECMA-cyrillic iso-ir-111 KOI8-E csISO111ECMACyrillic }
9575 { CSA_Z243.4-1985-1 iso-ir-121 ISO646-CA csa7-1 ca csISO121Canadian1 }
9576 { CSA_Z243.4-1985-2 iso-ir-122 ISO646-CA2 csa7-2 csISO122Canadian2 }
9577 { CSA_Z243.4-1985-gr iso-ir-123 csISO123CSAZ24341985gr }
9578 { ISO_8859-6:1987 iso-ir-127 ISO_8859-6 ISO-8859-6 ECMA-114 ASMO-708
9579 arabic csISOLatinArabic }
9580 { ISO_8859-6-E csISO88596E ISO-8859-6-E }
9581 { ISO_8859-6-I csISO88596I ISO-8859-6-I }
9582 { ISO_8859-7:1987 iso-ir-126 ISO_8859-7 ISO-8859-7 ELOT_928 ECMA-118
9583 greek greek8 csISOLatinGreek }
9584 { T.101-G2 iso-ir-128 csISO128T101G2 }
9585 { ISO_8859-8:1988 iso-ir-138 ISO_8859-8 ISO-8859-8 hebrew
9587 { ISO_8859-8-E csISO88598E ISO-8859-8-E }
9588 { ISO_8859-8-I csISO88598I ISO-8859-8-I }
9589 { CSN_369103 iso-ir-139 csISO139CSN369103 }
9590 { JUS_I.B1.002 iso-ir-141 ISO646-YU js yu csISO141JUSIB1002 }
9591 { ISO_6937-2-add iso-ir-142 csISOTextComm }
9592 { IEC_P27-1 iso-ir-143 csISO143IECP271 }
9593 { ISO_8859-5:1988 iso-ir-144 ISO_8859-5 ISO-8859-5 cyrillic
9594 csISOLatinCyrillic }
9595 { JUS_I.B1.003-serb iso-ir-146 serbian csISO146Serbian }
9596 { JUS_I.B1.003-mac macedonian iso-ir-147 csISO147Macedonian }
9597 { ISO_8859-9:1989 iso-ir-148 ISO_8859-9 ISO-8859-9 latin5 l5 csISOLatin5 }
9598 { greek-ccitt iso-ir-150 csISO150 csISO150GreekCCITT }
9599 { NC_NC00-10:81 cuba iso-ir-151 ISO646-CU csISO151Cuba }
9600 { ISO_6937-2-25 iso-ir-152 csISO6937Add }
9601 { GOST_19768-74 ST_SEV_358-88 iso-ir-153 csISO153GOST1976874 }
9602 { ISO_8859-supp iso-ir-154 latin1-2-5 csISO8859Supp }
9603 { ISO_10367-box iso-ir-155 csISO10367Box }
9604 { ISO-8859-10 iso-ir-157 l6 ISO_8859-10:1992 csISOLatin6 latin6 }
9605 { latin-lap lap iso-ir-158 csISO158Lap }
9606 { JIS_X0212-1990 x0212 iso-ir-159 csISO159JISX02121990 }
9607 { DS_2089 DS2089 ISO646-DK dk csISO646Danish }
9610 { JIS_X0201 X0201 csHalfWidthKatakana }
9611 { KSC5636 ISO646-KR csKSC5636 }
9612 { ISO-10646-UCS-2 csUnicode }
9613 { ISO-10646-UCS-4 csUCS4 }
9614 { DEC-MCS dec csDECMCS }
9615 { hp-roman8 roman8 r8 csHPRoman8 }
9616 { macintosh mac csMacintosh }
9617 { IBM037 cp037 ebcdic-cp-us ebcdic-cp-ca ebcdic-cp-wt ebcdic-cp-nl
9619 { IBM038 EBCDIC-INT cp038 csIBM038 }
9620 { IBM273 CP273 csIBM273 }
9621 { IBM274 EBCDIC-BE CP274 csIBM274 }
9622 { IBM275 EBCDIC-BR cp275 csIBM275 }
9623 { IBM277 EBCDIC-CP-DK EBCDIC-CP-NO csIBM277 }
9624 { IBM278 CP278 ebcdic-cp-fi ebcdic-cp-se csIBM278 }
9625 { IBM280 CP280 ebcdic-cp-it csIBM280 }
9626 { IBM281 EBCDIC-JP-E cp281 csIBM281 }
9627 { IBM284 CP284 ebcdic-cp-es csIBM284 }
9628 { IBM285 CP285 ebcdic-cp-gb csIBM285 }
9629 { IBM290 cp290 EBCDIC-JP-kana csIBM290 }
9630 { IBM297 cp297 ebcdic-cp-fr csIBM297 }
9631 { IBM420 cp420 ebcdic-cp-ar1 csIBM420 }
9632 { IBM423 cp423 ebcdic-cp-gr csIBM423 }
9633 { IBM424 cp424 ebcdic-cp-he csIBM424 }
9634 { IBM437 cp437 437 csPC8CodePage437 }
9635 { IBM500 CP500 ebcdic-cp-be ebcdic-cp-ch csIBM500 }
9636 { IBM775 cp775 csPC775Baltic }
9637 { IBM850 cp850 850 csPC850Multilingual }
9638 { IBM851 cp851 851 csIBM851 }
9639 { IBM852 cp852 852 csPCp852 }
9640 { IBM855 cp855 855 csIBM855 }
9641 { IBM857 cp857 857 csIBM857 }
9642 { IBM860 cp860 860 csIBM860 }
9643 { IBM861 cp861 861 cp-is csIBM861 }
9644 { IBM862 cp862 862 csPC862LatinHebrew }
9645 { IBM863 cp863 863 csIBM863 }
9646 { IBM864 cp864 csIBM864 }
9647 { IBM865 cp865 865 csIBM865 }
9648 { IBM866 cp866 866 csIBM866 }
9649 { IBM868 CP868 cp-ar csIBM868 }
9650 { IBM869 cp869 869 cp-gr csIBM869 }
9651 { IBM870 CP870 ebcdic-cp-roece ebcdic-cp-yu csIBM870 }
9652 { IBM871 CP871 ebcdic-cp-is csIBM871 }
9653 { IBM880 cp880 EBCDIC-Cyrillic csIBM880 }
9654 { IBM891 cp891 csIBM891 }
9655 { IBM903 cp903 csIBM903 }
9656 { IBM904 cp904 904 csIBBM904 }
9657 { IBM905 CP905 ebcdic-cp-tr csIBM905 }
9658 { IBM918 CP918 ebcdic-cp-ar2 csIBM918 }
9659 { IBM1026 CP1026 csIBM1026 }
9660 { EBCDIC-AT-DE csIBMEBCDICATDE }
9661 { EBCDIC-AT-DE-A csEBCDICATDEA }
9662 { EBCDIC-CA-FR csEBCDICCAFR }
9663 { EBCDIC-DK-NO csEBCDICDKNO }
9664 { EBCDIC-DK-NO-A csEBCDICDKNOA }
9665 { EBCDIC-FI-SE csEBCDICFISE }
9666 { EBCDIC-FI-SE-A csEBCDICFISEA }
9667 { EBCDIC-FR csEBCDICFR }
9668 { EBCDIC-IT csEBCDICIT }
9669 { EBCDIC-PT csEBCDICPT }
9670 { EBCDIC-ES csEBCDICES }
9671 { EBCDIC-ES-A csEBCDICESA }
9672 { EBCDIC-ES-S csEBCDICESS }
9673 { EBCDIC-UK csEBCDICUK }
9674 { EBCDIC-US csEBCDICUS }
9675 { UNKNOWN-8BIT csUnknown8BiT }
9676 { MNEMONIC csMnemonic }
9681 { IBM00858 CCSID00858 CP00858 PC-Multilingual-850+euro }
9682 { IBM00924 CCSID00924 CP00924 ebcdic-Latin9--euro }
9683 { IBM01140 CCSID01140 CP01140 ebcdic-us-37+euro }
9684 { IBM01141 CCSID01141 CP01141 ebcdic-de-273+euro }
9685 { IBM01142 CCSID01142 CP01142 ebcdic-dk-277+euro ebcdic-no-277+euro }
9686 { IBM01143 CCSID01143 CP01143 ebcdic-fi-278+euro ebcdic-se-278+euro }
9687 { IBM01144 CCSID01144 CP01144 ebcdic-it-280+euro }
9688 { IBM01145 CCSID01145 CP01145 ebcdic-es-284+euro }
9689 { IBM01146 CCSID01146 CP01146 ebcdic-gb-285+euro }
9690 { IBM01147 CCSID01147 CP01147 ebcdic-fr-297+euro }
9691 { IBM01148 CCSID01148 CP01148 ebcdic-international-500+euro }
9692 { IBM01149 CCSID01149 CP01149 ebcdic-is-871+euro }
9693 { IBM1047 IBM-1047 }
9694 { PTCP154 csPTCP154 PT154 CP154 Cyrillic-Asian }
9695 { Amiga-1251 Ami1251 Amiga1251 Ami-1251 }
9696 { UNICODE-1-1 csUnicode11 }
9699 { UNICODE-1-1-UTF-7 csUnicode11UTF7 }
9700 { ISO-8859-14 iso-ir-199 ISO_8859-14:1998 ISO_8859-14 latin8 iso-celtic
9702 { ISO-8859-15 ISO_8859-15 Latin-9 }
9703 { ISO-8859-16 iso-ir-226 ISO_8859-16:2001 ISO_8859-16 latin10 l10 }
9704 { GBK CP936 MS936 windows-936 }
9705 { JIS_Encoding csJISEncoding }
9706 { Shift_JIS MS_Kanji csShiftJIS }
9707 { Extended_UNIX_Code_Packed_Format_for_Japanese csEUCPkdFmtJapanese
9709 { Extended_UNIX_Code_Fixed_Width_for_Japanese csEUCFixWidJapanese }
9710 { ISO-10646-UCS-Basic csUnicodeASCII }
9711 { ISO-10646-Unicode-Latin1 csUnicodeLatin1 ISO-10646 }
9712 { ISO-Unicode-IBM-1261 csUnicodeIBM1261 }
9713 { ISO-Unicode-IBM-1268 csUnicodeIBM1268 }
9714 { ISO-Unicode-IBM-1276 csUnicodeIBM1276 }
9715 { ISO-Unicode-IBM-1264 csUnicodeIBM1264 }
9716 { ISO-Unicode-IBM-1265 csUnicodeIBM1265 }
9717 { ISO-8859-1-Windows-3.0-Latin-1 csWindows30Latin1 }
9718 { ISO-8859-1-Windows-3.1-Latin-1 csWindows31Latin1 }
9719 { ISO-8859-2-Windows-Latin-2 csWindows31Latin2 }
9720 { ISO-8859-9-Windows-Latin-5 csWindows31Latin5 }
9721 { Adobe-Standard-Encoding csAdobeStandardEncoding }
9722 { Ventura-US csVenturaUS }
9723 { Ventura-International csVenturaInternational }
9724 { PC8-Danish-Norwegian csPC8DanishNorwegian }
9725 { PC8-Turkish csPC8Turkish }
9726 { IBM-Symbols csIBMSymbols }
9727 { IBM-Thai csIBMThai }
9728 { HP-Legal csHPLegal }
9729 { HP-Pi-font csHPPiFont }
9730 { HP-Math8 csHPMath8 }
9731 { Adobe-Symbol-Encoding csHPPSMath }
9732 { HP-DeskTop csHPDesktop }
9733 { Ventura-Math csVenturaMath }
9734 { Microsoft-Publishing csMicrosoftPublishing }
9735 { Windows-31J csWindows31J }
9740 proc tcl_encoding {enc} {
9741 global encoding_aliases
9742 set names [encoding names]
9743 set lcnames [string tolower $names]
9744 set enc [string tolower $enc]
9745 set i [lsearch -exact $lcnames $enc]
9747 # look for "isonnn" instead of "iso-nnn" or "iso_nnn"
9748 if {[regsub {^iso[-_]} $enc iso encx]} {
9749 set i [lsearch -exact $lcnames $encx]
9753 foreach l $encoding_aliases {
9754 set ll [string tolower $l]
9755 if {[lsearch -exact $ll $enc] < 0} continue
9756 # look through the aliases for one that tcl knows about
9758 set i [lsearch -exact $lcnames $e]
9760 if {[regsub {^iso[-_]} $e iso ex]} {
9761 set i [lsearch -exact $lcnames $ex]
9770 return [lindex $names $i]
9775 # First check that Tcl/Tk is recent enough
9776 if {[catch {package require Tk 8.4} err]} {
9777 show_error {} . [mc "Sorry, gitk cannot run with this version of Tcl/Tk.\n\
9778 Gitk requires at least Tcl/Tk 8.4."]
9783 set wrcomcmd "git diff-tree --stdin -p --pretty"
9787 set gitencoding [exec git config --get i18n.commitencoding]
9789 if {$gitencoding == ""} {
9790 set gitencoding "utf-8"
9792 set tclencoding [tcl_encoding $gitencoding]
9793 if {$tclencoding == {}} {
9794 puts stderr "Warning: encoding $gitencoding is not supported by Tcl/Tk"
9797 set mainfont {Helvetica 9}
9798 set textfont {Courier 9}
9799 set uifont {Helvetica 9 bold}
9801 set findmergefiles 0
9809 set cmitmode "patch"
9810 set wrapcomment "none"
9814 set showlocalchanges 1
9816 set datetimeformat "%Y-%m-%d %H:%M:%S"
9819 set extdifftool "meld"
9821 set colors {green red blue magenta darkgrey brown orange}
9824 set diffcolors {red "#00a000" blue}
9827 set selectbgcolor gray85
9829 set circlecolors {white blue gray blue blue}
9831 ## For msgcat loading, first locate the installation location.
9832 if { [info exists ::env(GITK_MSGSDIR)] } {
9833 ## Msgsdir was manually set in the environment.
9834 set gitk_msgsdir $::env(GITK_MSGSDIR)
9836 ## Let's guess the prefix from argv0.
9837 set gitk_prefix [file dirname [file dirname [file normalize $argv0]]]
9838 set gitk_libdir [file join $gitk_prefix share gitk lib]
9839 set gitk_msgsdir [file join $gitk_libdir msgs]
9843 ## Internationalization (i18n) through msgcat and gettext. See
9844 ## http://www.gnu.org/software/gettext/manual/html_node/Tcl.html
9845 package require msgcat
9846 namespace import ::msgcat::mc
9847 ## And eventually load the actual message catalog
9848 ::msgcat::mcload $gitk_msgsdir
9850 catch {source ~/.gitk}
9852 font create optionfont -family sans-serif -size -12
9854 parsefont mainfont $mainfont
9855 eval font create mainfont [fontflags mainfont]
9856 eval font create mainfontbold [fontflags mainfont 1]
9858 parsefont textfont $textfont
9859 eval font create textfont [fontflags textfont]
9860 eval font create textfontbold [fontflags textfont 1]
9862 parsefont uifont $uifont
9863 eval font create uifont [fontflags uifont]
9867 # check that we can find a .git directory somewhere...
9868 if {[catch {set gitdir [gitdir]}]} {
9869 show_error {} . [mc "Cannot find a git repository here."]
9872 if {![file isdirectory $gitdir]} {
9873 show_error {} . [mc "Cannot find the git directory \"%s\"." $gitdir]
9881 set cmdline_files {}
9883 set revtreeargscmd {}
9885 switch -glob -- $arg {
9888 set cmdline_files [lrange $argv [expr {$i + 1}] end]
9891 "--select-commit=*" {
9892 set selecthead [string range $arg 16 end]
9895 set revtreeargscmd [string range $arg 10 end]
9898 lappend revtreeargs $arg
9904 if {$selecthead eq "HEAD"} {
9908 if {$i >= [llength $argv] && $revtreeargs ne {}} {
9909 # no -- on command line, but some arguments (other than --argscmd)
9911 set f [eval exec git rev-parse --no-revs --no-flags $revtreeargs]
9912 set cmdline_files [split $f "\n"]
9913 set n [llength $cmdline_files]
9914 set revtreeargs [lrange $revtreeargs 0 end-$n]
9915 # Unfortunately git rev-parse doesn't produce an error when
9916 # something is both a revision and a filename. To be consistent
9917 # with git log and git rev-list, check revtreeargs for filenames.
9918 foreach arg $revtreeargs {
9919 if {[file exists $arg]} {
9920 show_error {} . [mc "Ambiguous argument '%s': both revision\
9926 # unfortunately we get both stdout and stderr in $err,
9927 # so look for "fatal:".
9928 set i [string first "fatal:" $err]
9930 set err [string range $err [expr {$i + 6}] end]
9932 show_error {} . "[mc "Bad arguments to gitk:"]\n$err"
9937 set nullid "0000000000000000000000000000000000000000"
9938 set nullid2 "0000000000000000000000000000000000000001"
9939 set nullfile "/dev/null"
9941 set have_tk85 [expr {[package vcompare $tk_version "8.5"] >= 0}]
9948 set highlight_paths {}
9950 set searchdirn -forwards
9954 set markingmatches 0
9955 set linkentercount 0
9956 set need_redisplay 0
9963 set selectedhlview [mc "None"]
9964 set highlight_related [mc "None"]
9965 set highlight_files {}
9969 set viewargscmd(0) {}
9979 set isworktree [expr {[exec git rev-parse --is-inside-work-tree] == "true"}]
9982 # wait for the window to become visible
9984 wm title . "[file tail $argv0]: [file tail [pwd]]"
9987 if {$cmdline_files ne {} || $revtreeargs ne {} || $revtreeargscmd ne {}} {
9988 # create a view for the files/dirs specified on the command line
9992 set viewname(1) [mc "Command line"]
9993 set viewfiles(1) $cmdline_files
9994 set viewargs(1) $revtreeargs
9995 set viewargscmd(1) $revtreeargscmd
9999 .bar.view entryconf [mc "Edit view..."] -state normal
10000 .bar.view entryconf [mc "Delete view"] -state normal
10003 if {[info exists permviews]} {
10004 foreach v $permviews {
10007 set viewname($n) [lindex $v 0]
10008 set viewfiles($n) [lindex $v 1]
10009 set viewargs($n) [lindex $v 2]
10010 set viewargscmd($n) [lindex $v 3]