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
"[mc "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
"[mc "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
"[mc "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}
2170 bind $cflist $ctxbut {pop_flist_menu %W %X %Y %x %y}
2172 set maincursor [. cget -cursor]
2173 set textcursor [$ctext cget -cursor]
2174 set curtextcursor $textcursor
2176 set rowctxmenu .rowctxmenu
2177 menu $rowctxmenu -tearoff 0
2178 $rowctxmenu add command -label [mc "Diff this -> selected"] \
2179 -command {diffvssel 0}
2180 $rowctxmenu add command -label [mc "Diff selected -> this"] \
2181 -command {diffvssel 1}
2182 $rowctxmenu add command -label [mc "Make patch"] -command mkpatch
2183 $rowctxmenu add command -label [mc "Create tag"] -command mktag
2184 $rowctxmenu add command -label [mc "Write commit to file"] -command writecommit
2185 $rowctxmenu add command -label [mc "Create new branch"] -command mkbranch
2186 $rowctxmenu add command -label [mc "Cherry-pick this commit"] \
2188 $rowctxmenu add command -label [mc "Reset HEAD branch to here"] \
2191 set fakerowmenu .fakerowmenu
2192 menu $fakerowmenu -tearoff 0
2193 $fakerowmenu add command -label [mc "Diff this -> selected"] \
2194 -command {diffvssel 0}
2195 $fakerowmenu add command -label [mc "Diff selected -> this"] \
2196 -command {diffvssel 1}
2197 $fakerowmenu add command -label [mc "Make patch"] -command mkpatch
2198 # $fakerowmenu add command -label [mc "Commit"] -command {mkcommit 0}
2199 # $fakerowmenu add command -label [mc "Commit all"] -command {mkcommit 1}
2200 # $fakerowmenu add command -label [mc "Revert local changes"] -command revertlocal
2202 set headctxmenu .headctxmenu
2203 menu $headctxmenu -tearoff 0
2204 $headctxmenu add command -label [mc "Check out this branch"] \
2206 $headctxmenu add command -label [mc "Remove this branch"] \
2210 set flist_menu .flistctxmenu
2211 menu $flist_menu -tearoff 0
2212 $flist_menu add command -label [mc "Highlight this too"] \
2213 -command {flist_hl 0}
2214 $flist_menu add command -label [mc "Highlight this only"] \
2215 -command {flist_hl 1}
2216 $flist_menu add command -label [mc "External diff"] \
2217 -command {external_diff}
2218 $flist_menu add command -label [mc "Blame parent commit"] \
2219 -command {external_blame 1}
2222 # Windows sends all mouse wheel events to the current focused window, not
2223 # the one where the mouse hovers, so bind those events here and redirect
2224 # to the correct window
2225 proc windows_mousewheel_redirector {W X Y D} {
2226 global canv canv2 canv3
2227 set w [winfo containing -displayof $W $X $Y]
2229 set u [expr {$D < 0 ? 5 : -5}]
2230 if {$w == $canv || $w == $canv2 || $w == $canv3} {
2231 allcanvs yview scroll $u units
2234 $w yview scroll $u units
2240 # Update row number label when selectedline changes
2241 proc selectedline_change {n1 n2 op} {
2242 global selectedline rownumsel
2244 if {$selectedline eq {}} {
2247 set rownumsel [expr {$selectedline + 1}]
2251 # mouse-2 makes all windows scan vertically, but only the one
2252 # the cursor is in scans horizontally
2253 proc canvscan {op w x y} {
2254 global canv canv2 canv3
2255 foreach c [list $canv $canv2 $canv3] {
2264 proc scrollcanv {cscroll f0 f1} {
2265 $cscroll set $f0 $f1
2270 # when we make a key binding for the toplevel, make sure
2271 # it doesn't get triggered when that key is pressed in the
2272 # find string entry widget.
2273 proc bindkey {ev script} {
2276 set escript [bind Entry $ev]
2277 if {$escript == {}} {
2278 set escript [bind Entry <Key>]
2280 foreach e $entries {
2281 bind $e $ev "$escript; break"
2285 # set the focus back to the toplevel for any click outside
2288 global ctext entries
2289 foreach e [concat $entries $ctext] {
2290 if {$w == $e} return
2295 # Adjust the progress bar for a change in requested extent or canvas size
2296 proc adjustprogress {} {
2297 global progresscanv progressitem progresscoords
2298 global fprogitem fprogcoord lastprogupdate progupdatepending
2299 global rprogitem rprogcoord
2301 set w [expr {[winfo width $progresscanv] - 4}]
2302 set x0 [expr {$w * [lindex $progresscoords 0]}]
2303 set x1 [expr {$w * [lindex $progresscoords 1]}]
2304 set h [winfo height $progresscanv]
2305 $progresscanv coords $progressitem $x0 0 $x1 $h
2306 $progresscanv coords $fprogitem 0 0 [expr {$w * $fprogcoord}] $h
2307 $progresscanv coords $rprogitem 0 0 [expr {$w * $rprogcoord}] $h
2308 set now [clock clicks -milliseconds]
2309 if {$now >= $lastprogupdate + 100} {
2310 set progupdatepending 0
2312 } elseif {!$progupdatepending} {
2313 set progupdatepending 1
2314 after [expr {$lastprogupdate + 100 - $now}] doprogupdate
2318 proc doprogupdate {} {
2319 global lastprogupdate progupdatepending
2321 if {$progupdatepending} {
2322 set progupdatepending 0
2323 set lastprogupdate [clock clicks -milliseconds]
2328 proc savestuff {w} {
2329 global canv canv2 canv3 mainfont textfont uifont tabstop
2330 global stuffsaved findmergefiles maxgraphpct
2331 global maxwidth showneartags showlocalchanges
2332 global viewname viewfiles viewargs viewargscmd viewperm nextviewnum
2333 global cmitmode wrapcomment datetimeformat limitdiffs
2334 global colors bgcolor fgcolor diffcolors diffcontext selectbgcolor
2335 global autoselect extdifftool
2337 if {$stuffsaved} return
2338 if {![winfo viewable .]} return
2340 set f [open "~/.gitk-new" w]
2341 puts $f [list set mainfont $mainfont]
2342 puts $f [list set textfont $textfont]
2343 puts $f [list set uifont $uifont]
2344 puts $f [list set tabstop $tabstop]
2345 puts $f [list set findmergefiles $findmergefiles]
2346 puts $f [list set maxgraphpct $maxgraphpct]
2347 puts $f [list set maxwidth $maxwidth]
2348 puts $f [list set cmitmode $cmitmode]
2349 puts $f [list set wrapcomment $wrapcomment]
2350 puts $f [list set autoselect $autoselect]
2351 puts $f [list set showneartags $showneartags]
2352 puts $f [list set showlocalchanges $showlocalchanges]
2353 puts $f [list set datetimeformat $datetimeformat]
2354 puts $f [list set limitdiffs $limitdiffs]
2355 puts $f [list set bgcolor $bgcolor]
2356 puts $f [list set fgcolor $fgcolor]
2357 puts $f [list set colors $colors]
2358 puts $f [list set diffcolors $diffcolors]
2359 puts $f [list set diffcontext $diffcontext]
2360 puts $f [list set selectbgcolor $selectbgcolor]
2361 puts $f [list set extdifftool $extdifftool]
2363 puts $f "set geometry(main) [wm geometry .]"
2364 puts $f "set geometry(topwidth) [winfo width .tf]"
2365 puts $f "set geometry(topheight) [winfo height .tf]"
2366 puts $f "set geometry(pwsash0) \"[.tf.histframe.pwclist sash coord 0]\""
2367 puts $f "set geometry(pwsash1) \"[.tf.histframe.pwclist sash coord 1]\""
2368 puts $f "set geometry(botwidth) [winfo width .bleft]"
2369 puts $f "set geometry(botheight) [winfo height .bleft]"
2371 puts -nonewline $f "set permviews {"
2372 for {set v 0} {$v < $nextviewnum} {incr v} {
2373 if {$viewperm($v)} {
2374 puts $f "{[list $viewname($v) $viewfiles($v) $viewargs($v) $viewargscmd($v)]}"
2379 file rename -force "~/.gitk-new" "~/.gitk"
2384 proc resizeclistpanes {win w} {
2386 if {[info exists oldwidth($win)]} {
2387 set s0 [$win sash coord 0]
2388 set s1 [$win sash coord 1]
2390 set sash0 [expr {int($w/2 - 2)}]
2391 set sash1 [expr {int($w*5/6 - 2)}]
2393 set factor [expr {1.0 * $w / $oldwidth($win)}]
2394 set sash0 [expr {int($factor * [lindex $s0 0])}]
2395 set sash1 [expr {int($factor * [lindex $s1 0])}]
2399 if {$sash1 < $sash0 + 20} {
2400 set sash1 [expr {$sash0 + 20}]
2402 if {$sash1 > $w - 10} {
2403 set sash1 [expr {$w - 10}]
2404 if {$sash0 > $sash1 - 20} {
2405 set sash0 [expr {$sash1 - 20}]
2409 $win sash place 0 $sash0 [lindex $s0 1]
2410 $win sash place 1 $sash1 [lindex $s1 1]
2412 set oldwidth($win) $w
2415 proc resizecdetpanes {win w} {
2417 if {[info exists oldwidth($win)]} {
2418 set s0 [$win sash coord 0]
2420 set sash0 [expr {int($w*3/4 - 2)}]
2422 set factor [expr {1.0 * $w / $oldwidth($win)}]
2423 set sash0 [expr {int($factor * [lindex $s0 0])}]
2427 if {$sash0 > $w - 15} {
2428 set sash0 [expr {$w - 15}]
2431 $win sash place 0 $sash0 [lindex $s0 1]
2433 set oldwidth($win) $w
2436 proc allcanvs args {
2437 global canv canv2 canv3
2443 proc bindall {event action} {
2444 global canv canv2 canv3
2445 bind $canv $event $action
2446 bind $canv2 $event $action
2447 bind $canv3 $event $action
2453 if {[winfo exists $w]} {
2458 wm title $w [mc "About gitk"]
2459 message $w.m -text [mc "
2460 Gitk - a commit viewer for git
2462 Copyright © 2005-2008 Paul Mackerras
2464 Use and redistribute under the terms of the GNU General Public License"] \
2465 -justify center -aspect 400 -border 2 -bg white -relief groove
2466 pack $w.m -side top -fill x -padx 2 -pady 2
2467 button $w.ok -text [mc "Close"] -command "destroy $w" -default active
2468 pack $w.ok -side bottom
2469 bind $w <Visibility> "focus $w.ok"
2470 bind $w <Key-Escape> "destroy $w"
2471 bind $w <Key-Return> "destroy $w"
2476 if {[winfo exists $w]} {
2480 if {[tk windowingsystem] eq {aqua}} {
2486 wm title $w [mc "Gitk key bindings"]
2487 message $w.m -text "
2488 [mc "Gitk key bindings:"]
2490 [mc "<%s-Q> Quit" $M1T]
2491 [mc "<Home> Move to first commit"]
2492 [mc "<End> Move to last commit"]
2493 [mc "<Up>, p, i Move up one commit"]
2494 [mc "<Down>, n, k Move down one commit"]
2495 [mc "<Left>, z, j Go back in history list"]
2496 [mc "<Right>, x, l Go forward in history list"]
2497 [mc "<PageUp> Move up one page in commit list"]
2498 [mc "<PageDown> Move down one page in commit list"]
2499 [mc "<%s-Home> Scroll to top of commit list" $M1T]
2500 [mc "<%s-End> Scroll to bottom of commit list" $M1T]
2501 [mc "<%s-Up> Scroll commit list up one line" $M1T]
2502 [mc "<%s-Down> Scroll commit list down one line" $M1T]
2503 [mc "<%s-PageUp> Scroll commit list up one page" $M1T]
2504 [mc "<%s-PageDown> Scroll commit list down one page" $M1T]
2505 [mc "<Shift-Up> Find backwards (upwards, later commits)"]
2506 [mc "<Shift-Down> Find forwards (downwards, earlier commits)"]
2507 [mc "<Delete>, b Scroll diff view up one page"]
2508 [mc "<Backspace> Scroll diff view up one page"]
2509 [mc "<Space> Scroll diff view down one page"]
2510 [mc "u Scroll diff view up 18 lines"]
2511 [mc "d Scroll diff view down 18 lines"]
2512 [mc "<%s-F> Find" $M1T]
2513 [mc "<%s-G> Move to next find hit" $M1T]
2514 [mc "<Return> Move to next find hit"]
2515 [mc "/ Move to next find hit, or redo find"]
2516 [mc "? Move to previous find hit"]
2517 [mc "f Scroll diff view to next file"]
2518 [mc "<%s-S> Search for next hit in diff view" $M1T]
2519 [mc "<%s-R> Search for previous hit in diff view" $M1T]
2520 [mc "<%s-KP+> Increase font size" $M1T]
2521 [mc "<%s-plus> Increase font size" $M1T]
2522 [mc "<%s-KP-> Decrease font size" $M1T]
2523 [mc "<%s-minus> Decrease font size" $M1T]
2526 -justify left -bg white -border 2 -relief groove
2527 pack $w.m -side top -fill both -padx 2 -pady 2
2528 button $w.ok -text [mc "Close"] -command "destroy $w" -default active
2529 pack $w.ok -side bottom
2530 bind $w <Visibility> "focus $w.ok"
2531 bind $w <Key-Escape> "destroy $w"
2532 bind $w <Key-Return> "destroy $w"
2535 # Procedures for manipulating the file list window at the
2536 # bottom right of the overall window.
2538 proc treeview {w l openlevs} {
2539 global treecontents treediropen treeheight treeparent treeindex
2549 set treecontents() {}
2550 $w conf -state normal
2552 while {[string range $f 0 $prefixend] ne $prefix} {
2553 if {$lev <= $openlevs} {
2554 $w mark set e:$treeindex($prefix) "end -1c"
2555 $w mark gravity e:$treeindex($prefix) left
2557 set treeheight($prefix) $ht
2558 incr ht [lindex $htstack end]
2559 set htstack [lreplace $htstack end end]
2560 set prefixend [lindex $prefendstack end]
2561 set prefendstack [lreplace $prefendstack end end]
2562 set prefix [string range $prefix 0 $prefixend]
2565 set tail [string range $f [expr {$prefixend+1}] end]
2566 while {[set slash [string first "/" $tail]] >= 0} {
2569 lappend prefendstack $prefixend
2570 incr prefixend [expr {$slash + 1}]
2571 set d [string range $tail 0 $slash]
2572 lappend treecontents($prefix) $d
2573 set oldprefix $prefix
2575 set treecontents($prefix) {}
2576 set treeindex($prefix) [incr ix]
2577 set treeparent($prefix) $oldprefix
2578 set tail [string range $tail [expr {$slash+1}] end]
2579 if {$lev <= $openlevs} {
2581 set treediropen($prefix) [expr {$lev < $openlevs}]
2582 set bm [expr {$lev == $openlevs? "tri-rt": "tri-dn"}]
2583 $w mark set d:$ix "end -1c"
2584 $w mark gravity d:$ix left
2586 for {set i 0} {$i < $lev} {incr i} {append str "\t"}
2588 $w image create end -align center -image $bm -padx 1 \
2590 $w insert end $d [highlight_tag $prefix]
2591 $w mark set s:$ix "end -1c"
2592 $w mark gravity s:$ix left
2597 if {$lev <= $openlevs} {
2600 for {set i 0} {$i < $lev} {incr i} {append str "\t"}
2602 $w insert end $tail [highlight_tag $f]
2604 lappend treecontents($prefix) $tail
2607 while {$htstack ne {}} {
2608 set treeheight($prefix) $ht
2609 incr ht [lindex $htstack end]
2610 set htstack [lreplace $htstack end end]
2611 set prefixend [lindex $prefendstack end]
2612 set prefendstack [lreplace $prefendstack end end]
2613 set prefix [string range $prefix 0 $prefixend]
2615 $w conf -state disabled
2618 proc linetoelt {l} {
2619 global treeheight treecontents
2624 foreach e $treecontents($prefix) {
2629 if {[string index $e end] eq "/"} {
2630 set n $treeheight($prefix$e)
2642 proc highlight_tree {y prefix} {
2643 global treeheight treecontents cflist
2645 foreach e $treecontents($prefix) {
2647 if {[highlight_tag $path] ne {}} {
2648 $cflist tag add bold $y.0 "$y.0 lineend"
2651 if {[string index $e end] eq "/" && $treeheight($path) > 1} {
2652 set y [highlight_tree $y $path]
2658 proc treeclosedir {w dir} {
2659 global treediropen treeheight treeparent treeindex
2661 set ix $treeindex($dir)
2662 $w conf -state normal
2663 $w delete s:$ix e:$ix
2664 set treediropen($dir) 0
2665 $w image configure a:$ix -image tri-rt
2666 $w conf -state disabled
2667 set n [expr {1 - $treeheight($dir)}]
2668 while {$dir ne {}} {
2669 incr treeheight($dir) $n
2670 set dir $treeparent($dir)
2674 proc treeopendir {w dir} {
2675 global treediropen treeheight treeparent treecontents treeindex
2677 set ix $treeindex($dir)
2678 $w conf -state normal
2679 $w image configure a:$ix -image tri-dn
2680 $w mark set e:$ix s:$ix
2681 $w mark gravity e:$ix right
2684 set n [llength $treecontents($dir)]
2685 for {set x $dir} {$x ne {}} {set x $treeparent($x)} {
2688 incr treeheight($x) $n
2690 foreach e $treecontents($dir) {
2692 if {[string index $e end] eq "/"} {
2693 set iy $treeindex($de)
2694 $w mark set d:$iy e:$ix
2695 $w mark gravity d:$iy left
2696 $w insert e:$ix $str
2697 set treediropen($de) 0
2698 $w image create e:$ix -align center -image tri-rt -padx 1 \
2700 $w insert e:$ix $e [highlight_tag $de]
2701 $w mark set s:$iy e:$ix
2702 $w mark gravity s:$iy left
2703 set treeheight($de) 1
2705 $w insert e:$ix $str
2706 $w insert e:$ix $e [highlight_tag $de]
2709 $w mark gravity e:$ix right
2710 $w conf -state disabled
2711 set treediropen($dir) 1
2712 set top [lindex [split [$w index @0,0] .] 0]
2713 set ht [$w cget -height]
2714 set l [lindex [split [$w index s:$ix] .] 0]
2717 } elseif {$l + $n + 1 > $top + $ht} {
2718 set top [expr {$l + $n + 2 - $ht}]
2726 proc treeclick {w x y} {
2727 global treediropen cmitmode ctext cflist cflist_top
2729 if {$cmitmode ne "tree"} return
2730 if {![info exists cflist_top]} return
2731 set l [lindex [split [$w index "@$x,$y"] "."] 0]
2732 $cflist tag remove highlight $cflist_top.0 "$cflist_top.0 lineend"
2733 $cflist tag add highlight $l.0 "$l.0 lineend"
2739 set e [linetoelt $l]
2740 if {[string index $e end] ne "/"} {
2742 } elseif {$treediropen($e)} {
2749 proc setfilelist {id} {
2750 global treefilelist cflist
2752 treeview $cflist $treefilelist($id) 0
2755 image create bitmap tri-rt -background black -foreground blue -data {
2756 #define tri-rt_width 13
2757 #define tri-rt_height 13
2758 static unsigned char tri-rt_bits[] = {
2759 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x30, 0x00, 0x70, 0x00, 0xf0, 0x00,
2760 0xf0, 0x01, 0xf0, 0x00, 0x70, 0x00, 0x30, 0x00, 0x10, 0x00, 0x00, 0x00,
2763 #define tri-rt-mask_width 13
2764 #define tri-rt-mask_height 13
2765 static unsigned char tri-rt-mask_bits[] = {
2766 0x08, 0x00, 0x18, 0x00, 0x38, 0x00, 0x78, 0x00, 0xf8, 0x00, 0xf8, 0x01,
2767 0xf8, 0x03, 0xf8, 0x01, 0xf8, 0x00, 0x78, 0x00, 0x38, 0x00, 0x18, 0x00,
2770 image create bitmap tri-dn -background black -foreground blue -data {
2771 #define tri-dn_width 13
2772 #define tri-dn_height 13
2773 static unsigned char tri-dn_bits[] = {
2774 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x07, 0xf8, 0x03,
2775 0xf0, 0x01, 0xe0, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2778 #define tri-dn-mask_width 13
2779 #define tri-dn-mask_height 13
2780 static unsigned char tri-dn-mask_bits[] = {
2781 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x1f, 0xfe, 0x0f, 0xfc, 0x07,
2782 0xf8, 0x03, 0xf0, 0x01, 0xe0, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
2786 image create bitmap reficon-T -background black -foreground yellow -data {
2787 #define tagicon_width 13
2788 #define tagicon_height 9
2789 static unsigned char tagicon_bits[] = {
2790 0x00, 0x00, 0x00, 0x00, 0xf0, 0x07, 0xf8, 0x07,
2791 0xfc, 0x07, 0xf8, 0x07, 0xf0, 0x07, 0x00, 0x00, 0x00, 0x00};
2793 #define tagicon-mask_width 13
2794 #define tagicon-mask_height 9
2795 static unsigned char tagicon-mask_bits[] = {
2796 0x00, 0x00, 0xf0, 0x0f, 0xf8, 0x0f, 0xfc, 0x0f,
2797 0xfe, 0x0f, 0xfc, 0x0f, 0xf8, 0x0f, 0xf0, 0x0f, 0x00, 0x00};
2800 #define headicon_width 13
2801 #define headicon_height 9
2802 static unsigned char headicon_bits[] = {
2803 0x00, 0x00, 0x00, 0x00, 0xf8, 0x07, 0xf8, 0x07,
2804 0xf8, 0x07, 0xf8, 0x07, 0xf8, 0x07, 0x00, 0x00, 0x00, 0x00};
2807 #define headicon-mask_width 13
2808 #define headicon-mask_height 9
2809 static unsigned char headicon-mask_bits[] = {
2810 0x00, 0x00, 0xfc, 0x0f, 0xfc, 0x0f, 0xfc, 0x0f,
2811 0xfc, 0x0f, 0xfc, 0x0f, 0xfc, 0x0f, 0xfc, 0x0f, 0x00, 0x00};
2813 image create bitmap reficon-H -background black -foreground green \
2814 -data $rectdata -maskdata $rectmask
2815 image create bitmap reficon-o -background black -foreground "#ddddff" \
2816 -data $rectdata -maskdata $rectmask
2818 proc init_flist {first} {
2819 global cflist cflist_top difffilestart
2821 $cflist conf -state normal
2822 $cflist delete 0.0 end
2824 $cflist insert end $first
2826 $cflist tag add highlight 1.0 "1.0 lineend"
2828 catch {unset cflist_top}
2830 $cflist conf -state disabled
2831 set difffilestart {}
2834 proc highlight_tag {f} {
2835 global highlight_paths
2837 foreach p $highlight_paths {
2838 if {[string match $p $f]} {
2845 proc highlight_filelist {} {
2846 global cmitmode cflist
2848 $cflist conf -state normal
2849 if {$cmitmode ne "tree"} {
2850 set end [lindex [split [$cflist index end] .] 0]
2851 for {set l 2} {$l < $end} {incr l} {
2852 set line [$cflist get $l.0 "$l.0 lineend"]
2853 if {[highlight_tag $line] ne {}} {
2854 $cflist tag add bold $l.0 "$l.0 lineend"
2860 $cflist conf -state disabled
2863 proc unhighlight_filelist {} {
2866 $cflist conf -state normal
2867 $cflist tag remove bold 1.0 end
2868 $cflist conf -state disabled
2871 proc add_flist {fl} {
2874 $cflist conf -state normal
2876 $cflist insert end "\n"
2877 $cflist insert end $f [highlight_tag $f]
2879 $cflist conf -state disabled
2882 proc sel_flist {w x y} {
2883 global ctext difffilestart cflist cflist_top cmitmode
2885 if {$cmitmode eq "tree"} return
2886 if {![info exists cflist_top]} return
2887 set l [lindex [split [$w index "@$x,$y"] "."] 0]
2888 $cflist tag remove highlight $cflist_top.0 "$cflist_top.0 lineend"
2889 $cflist tag add highlight $l.0 "$l.0 lineend"
2894 catch {$ctext yview [lindex $difffilestart [expr {$l - 2}]]}
2898 proc pop_flist_menu {w X Y x y} {
2899 global ctext cflist cmitmode flist_menu flist_menu_file
2900 global treediffs diffids
2903 set l [lindex [split [$w index "@$x,$y"] "."] 0]
2905 if {$cmitmode eq "tree"} {
2906 set e [linetoelt $l]
2907 if {[string index $e end] eq "/"} return
2909 set e [lindex $treediffs($diffids) [expr {$l-2}]]
2911 set flist_menu_file $e
2912 set xdiffstate "normal"
2913 if {$cmitmode eq "tree"} {
2914 set xdiffstate "disabled"
2916 # Disable "External diff" item in tree mode
2917 $flist_menu entryconf 2 -state $xdiffstate
2918 tk_popup $flist_menu $X $Y
2921 proc flist_hl {only} {
2922 global flist_menu_file findstring gdttype
2924 set x [shellquote $flist_menu_file]
2925 if {$only || $findstring eq {} || $gdttype ne [mc "touching paths:"]} {
2928 append findstring " " $x
2930 set gdttype [mc "touching paths:"]
2933 proc save_file_from_commit {filename output what} {
2936 if {[catch {exec git show $filename -- > $output} err]} {
2937 if {[string match "fatal: bad revision *" $err]} {
2940 error_popup "[mc "Error getting \"%s\" from %s:" $filename $what] $err"
2946 proc external_diff_get_one_file {diffid filename diffdir} {
2947 global nullid nullid2 nullfile
2950 if {$diffid == $nullid} {
2951 set difffile [file join [file dirname $gitdir] $filename]
2952 if {[file exists $difffile]} {
2957 if {$diffid == $nullid2} {
2958 set difffile [file join $diffdir "\[index\] [file tail $filename]"]
2959 return [save_file_from_commit :$filename $difffile index]
2961 set difffile [file join $diffdir "\[$diffid\] [file tail $filename]"]
2962 return [save_file_from_commit $diffid:$filename $difffile \
2966 proc external_diff {} {
2967 global gitktmpdir nullid nullid2
2968 global flist_menu_file
2971 global gitdir extdifftool
2973 if {[llength $diffids] == 1} {
2974 # no reference commit given
2975 set diffidto [lindex $diffids 0]
2976 if {$diffidto eq $nullid} {
2977 # diffing working copy with index
2978 set diffidfrom $nullid2
2979 } elseif {$diffidto eq $nullid2} {
2980 # diffing index with HEAD
2981 set diffidfrom "HEAD"
2983 # use first parent commit
2984 global parentlist selectedline
2985 set diffidfrom [lindex $parentlist $selectedline 0]
2988 set diffidfrom [lindex $diffids 0]
2989 set diffidto [lindex $diffids 1]
2992 # make sure that several diffs wont collide
2993 if {![info exists gitktmpdir]} {
2994 set gitktmpdir [file join [file dirname $gitdir] \
2995 [format ".gitk-tmp.%s" [pid]]]
2996 if {[catch {file mkdir $gitktmpdir} err]} {
2997 error_popup "[mc "Error creating temporary directory %s:" $gitktmpdir] $err"
3004 set diffdir [file join $gitktmpdir $diffnum]
3005 if {[catch {file mkdir $diffdir} err]} {
3006 error_popup "[mc "Error creating temporary directory %s:" $diffdir] $err"
3010 # gather files to diff
3011 set difffromfile [external_diff_get_one_file $diffidfrom $flist_menu_file $diffdir]
3012 set difftofile [external_diff_get_one_file $diffidto $flist_menu_file $diffdir]
3014 if {$difffromfile ne {} && $difftofile ne {}} {
3015 set cmd [concat | [shellsplit $extdifftool] \
3016 [list $difffromfile $difftofile]]
3017 if {[catch {set fl [open $cmd r]} err]} {
3018 file delete -force $diffdir
3019 error_popup "$extdifftool: [mc "command failed:"] $err"
3021 fconfigure $fl -blocking 0
3022 filerun $fl [list delete_at_eof $fl $diffdir]
3027 proc external_blame {parent_idx} {
3028 global flist_menu_file
3029 global nullid nullid2
3030 global parentlist selectedline currentid
3032 if {$parent_idx > 0} {
3033 set base_commit [lindex $parentlist $selectedline [expr {$parent_idx-1}]]
3035 set base_commit $currentid
3038 if {$base_commit eq {} || $base_commit eq $nullid || $base_commit eq $nullid2} {
3039 error_popup [mc "No such commit"]
3043 if {[catch {exec git gui blame $base_commit $flist_menu_file &} err]} {
3044 error_popup "[mc "git gui blame: command failed:"] $err"
3048 # delete $dir when we see eof on $f (presumably because the child has exited)
3049 proc delete_at_eof {f dir} {
3050 while {[gets $f line] >= 0} {}
3052 if {[catch {close $f} err]} {
3053 error_popup "[mc "External diff viewer failed:"] $err"
3055 file delete -force $dir
3061 # Functions for adding and removing shell-type quoting
3063 proc shellquote {str} {
3064 if {![string match "*\['\"\\ \t]*" $str]} {
3067 if {![string match "*\['\"\\]*" $str]} {
3070 if {![string match "*'*" $str]} {
3073 return "\"[string map {\" \\\" \\ \\\\} $str]\""
3076 proc shellarglist {l} {
3082 append str [shellquote $a]
3087 proc shelldequote {str} {
3092 if {![regexp -start $used -indices "\['\"\\\\ \t]" $str first]} {
3093 append ret [string range $str $used end]
3094 set used [string length $str]
3097 set first [lindex $first 0]
3098 set ch [string index $str $first]
3099 if {$first > $used} {
3100 append ret [string range $str $used [expr {$first - 1}]]
3103 if {$ch eq " " || $ch eq "\t"} break
3106 set first [string first "'" $str $used]
3108 error "unmatched single-quote"
3110 append ret [string range $str $used [expr {$first - 1}]]
3115 if {$used >= [string length $str]} {
3116 error "trailing backslash"
3118 append ret [string index $str $used]
3123 if {![regexp -start $used -indices "\[\"\\\\]" $str first]} {
3124 error "unmatched double-quote"
3126 set first [lindex $first 0]
3127 set ch [string index $str $first]
3128 if {$first > $used} {
3129 append ret [string range $str $used [expr {$first - 1}]]
3132 if {$ch eq "\""} break
3134 append ret [string index $str $used]
3138 return [list $used $ret]
3141 proc shellsplit {str} {
3144 set str [string trimleft $str]
3145 if {$str eq {}} break
3146 set dq [shelldequote $str]
3147 set n [lindex $dq 0]
3148 set word [lindex $dq 1]
3149 set str [string range $str $n end]
3155 # Code to implement multiple views
3157 proc newview {ishighlight} {
3158 global nextviewnum newviewname newviewperm newishighlight
3159 global newviewargs revtreeargs viewargscmd newviewargscmd curview
3161 set newishighlight $ishighlight
3163 if {[winfo exists $top]} {
3167 set newviewname($nextviewnum) "[mc "View"] $nextviewnum"
3168 set newviewperm($nextviewnum) 0
3169 set newviewargs($nextviewnum) [shellarglist $revtreeargs]
3170 set newviewargscmd($nextviewnum) $viewargscmd($curview)
3171 vieweditor $top $nextviewnum [mc "Gitk view definition"]
3176 global viewname viewperm newviewname newviewperm
3177 global viewargs newviewargs viewargscmd newviewargscmd
3179 set top .gitkvedit-$curview
3180 if {[winfo exists $top]} {
3184 set newviewname($curview) $viewname($curview)
3185 set newviewperm($curview) $viewperm($curview)
3186 set newviewargs($curview) [shellarglist $viewargs($curview)]
3187 set newviewargscmd($curview) $viewargscmd($curview)
3188 vieweditor $top $curview "Gitk: edit view $viewname($curview)"
3191 proc vieweditor {top n title} {
3192 global newviewname newviewperm viewfiles bgcolor
3195 wm title $top $title
3196 label $top.nl -text [mc "Name"]
3197 entry $top.name -width 20 -textvariable newviewname($n)
3198 grid $top.nl $top.name -sticky w -pady 5
3199 checkbutton $top.perm -text [mc "Remember this view"] \
3200 -variable newviewperm($n)
3201 grid $top.perm - -pady 5 -sticky w
3202 message $top.al -aspect 1000 \
3203 -text [mc "Commits to include (arguments to git log):"]
3204 grid $top.al - -sticky w -pady 5
3205 entry $top.args -width 50 -textvariable newviewargs($n) \
3206 -background $bgcolor
3207 grid $top.args - -sticky ew -padx 5
3209 message $top.ac -aspect 1000 \
3210 -text [mc "Command to generate more commits to include:"]
3211 grid $top.ac - -sticky w -pady 5
3212 entry $top.argscmd -width 50 -textvariable newviewargscmd($n) \
3214 grid $top.argscmd - -sticky ew -padx 5
3216 message $top.l -aspect 1000 \
3217 -text [mc "Enter files and directories to include, one per line:"]
3218 grid $top.l - -sticky w
3219 text $top.t -width 40 -height 10 -background $bgcolor -font uifont
3220 if {[info exists viewfiles($n)]} {
3221 foreach f $viewfiles($n) {
3222 $top.t insert end $f
3223 $top.t insert end "\n"
3225 $top.t delete {end - 1c} end
3226 $top.t mark set insert 0.0
3228 grid $top.t - -sticky ew -padx 5
3230 button $top.buts.ok -text [mc "OK"] -command [list newviewok $top $n]
3231 button $top.buts.can -text [mc "Cancel"] -command [list destroy $top]
3232 grid $top.buts.ok $top.buts.can
3233 grid columnconfigure $top.buts 0 -weight 1 -uniform a
3234 grid columnconfigure $top.buts 1 -weight 1 -uniform a
3235 grid $top.buts - -pady 10 -sticky ew
3239 proc doviewmenu {m first cmd op argv} {
3240 set nmenu [$m index end]
3241 for {set i $first} {$i <= $nmenu} {incr i} {
3242 if {[$m entrycget $i -command] eq $cmd} {
3243 eval $m $op $i $argv
3249 proc allviewmenus {n op args} {
3252 doviewmenu .bar.view 5 [list showview $n] $op $args
3253 # doviewmenu $viewhlmenu 1 [list addvhighlight $n] $op $args
3256 proc newviewok {top n} {
3257 global nextviewnum newviewperm newviewname newishighlight
3258 global viewname viewfiles viewperm selectedview curview
3259 global viewargs newviewargs viewargscmd newviewargscmd viewhlmenu
3262 set newargs [shellsplit $newviewargs($n)]
3264 error_popup "[mc "Error in commit selection arguments:"] $err"
3270 foreach f [split [$top.t get 0.0 end] "\n"] {
3271 set ft [string trim $f]
3276 if {![info exists viewfiles($n)]} {
3277 # creating a new view
3279 set viewname($n) $newviewname($n)
3280 set viewperm($n) $newviewperm($n)
3281 set viewfiles($n) $files
3282 set viewargs($n) $newargs
3283 set viewargscmd($n) $newviewargscmd($n)
3285 if {!$newishighlight} {
3288 run addvhighlight $n
3291 # editing an existing view
3292 set viewperm($n) $newviewperm($n)
3293 if {$newviewname($n) ne $viewname($n)} {
3294 set viewname($n) $newviewname($n)
3295 doviewmenu .bar.view 5 [list showview $n] \
3296 entryconf [list -label $viewname($n)]
3297 # doviewmenu $viewhlmenu 1 [list addvhighlight $n] \
3298 # entryconf [list -label $viewname($n) -value $viewname($n)]
3300 if {$files ne $viewfiles($n) || $newargs ne $viewargs($n) || \
3301 $newviewargscmd($n) ne $viewargscmd($n)} {
3302 set viewfiles($n) $files
3303 set viewargs($n) $newargs
3304 set viewargscmd($n) $newviewargscmd($n)
3305 if {$curview == $n} {
3310 catch {destroy $top}
3314 global curview viewperm hlview selectedhlview
3316 if {$curview == 0} return
3317 if {[info exists hlview] && $hlview == $curview} {
3318 set selectedhlview [mc "None"]
3321 allviewmenus $curview delete
3322 set viewperm($curview) 0
3326 proc addviewmenu {n} {
3327 global viewname viewhlmenu
3329 .bar.view add radiobutton -label $viewname($n) \
3330 -command [list showview $n] -variable selectedview -value $n
3331 #$viewhlmenu add radiobutton -label $viewname($n) \
3332 # -command [list addvhighlight $n] -variable selectedhlview
3336 global curview cached_commitrow ordertok
3337 global displayorder parentlist rowidlist rowisopt rowfinal
3338 global colormap rowtextx nextcolor canvxmax
3339 global numcommits viewcomplete
3340 global selectedline currentid canv canvy0
3342 global pending_select mainheadid
3345 global hlview selectedhlview commitinterest
3347 if {$n == $curview} return
3349 set ymax [lindex [$canv cget -scrollregion] 3]
3350 set span [$canv yview]
3351 set ytop [expr {[lindex $span 0] * $ymax}]
3352 set ybot [expr {[lindex $span 1] * $ymax}]
3353 set yscreen [expr {($ybot - $ytop) / 2}]
3354 if {$selectedline ne {}} {
3355 set selid $currentid
3356 set y [yc $selectedline]
3357 if {$ytop < $y && $y < $ybot} {
3358 set yscreen [expr {$y - $ytop}]
3360 } elseif {[info exists pending_select]} {
3361 set selid $pending_select
3362 unset pending_select
3366 catch {unset treediffs}
3368 if {[info exists hlview] && $hlview == $n} {
3370 set selectedhlview [mc "None"]
3372 catch {unset commitinterest}
3373 catch {unset cached_commitrow}
3374 catch {unset ordertok}
3378 .bar.view entryconf [mc "Edit view..."] -state [expr {$n == 0? "disabled": "normal"}]
3379 .bar.view entryconf [mc "Delete view"] -state [expr {$n == 0? "disabled": "normal"}]
3382 if {![info exists viewcomplete($n)]} {
3392 set numcommits $commitidx($n)
3394 catch {unset colormap}
3395 catch {unset rowtextx}
3397 set canvxmax [$canv cget -width]
3403 if {$selid ne {} && [commitinview $selid $n]} {
3404 set row [rowofcommit $selid]
3405 # try to get the selected row in the same position on the screen
3406 set ymax [lindex [$canv cget -scrollregion] 3]
3407 set ytop [expr {[yc $row] - $yscreen}]
3411 set yf [expr {$ytop * 1.0 / $ymax}]
3413 allcanvs yview moveto $yf
3417 } elseif {!$viewcomplete($n)} {
3418 reset_pending_select $selid
3420 reset_pending_select {}
3422 if {[commitinview $pending_select $curview]} {
3423 selectline [rowofcommit $pending_select] 1
3425 set row [first_real_row]
3426 if {$row < $numcommits} {
3431 if {!$viewcomplete($n)} {
3432 if {$numcommits == 0} {
3433 show_status [mc "Reading commits..."]
3435 } elseif {$numcommits == 0} {
3436 show_status [mc "No commits selected"]
3440 # Stuff relating to the highlighting facility
3442 proc ishighlighted {id} {
3443 global vhighlights fhighlights nhighlights rhighlights
3445 if {[info exists nhighlights($id)] && $nhighlights($id) > 0} {
3446 return $nhighlights($id)
3448 if {[info exists vhighlights($id)] && $vhighlights($id) > 0} {
3449 return $vhighlights($id)
3451 if {[info exists fhighlights($id)] && $fhighlights($id) > 0} {
3452 return $fhighlights($id)
3454 if {[info exists rhighlights($id)] && $rhighlights($id) > 0} {
3455 return $rhighlights($id)
3460 proc bolden {row font} {
3461 global canv linehtag selectedline boldrows
3463 lappend boldrows $row
3464 $canv itemconf $linehtag($row) -font $font
3465 if {$row == $selectedline} {
3467 set t [eval $canv create rect [$canv bbox $linehtag($row)] \
3468 -outline {{}} -tags secsel \
3469 -fill [$canv cget -selectbackground]]
3474 proc bolden_name {row font} {
3475 global canv2 linentag selectedline boldnamerows
3477 lappend boldnamerows $row
3478 $canv2 itemconf $linentag($row) -font $font
3479 if {$row == $selectedline} {
3480 $canv2 delete secsel
3481 set t [eval $canv2 create rect [$canv2 bbox $linentag($row)] \
3482 -outline {{}} -tags secsel \
3483 -fill [$canv2 cget -selectbackground]]
3492 foreach row $boldrows {
3493 if {![ishighlighted [commitonrow $row]]} {
3494 bolden $row mainfont
3496 lappend stillbold $row
3499 set boldrows $stillbold
3502 proc addvhighlight {n} {
3503 global hlview viewcomplete curview vhl_done commitidx
3505 if {[info exists hlview]} {
3509 if {$n != $curview && ![info exists viewcomplete($n)]} {
3512 set vhl_done $commitidx($hlview)
3513 if {$vhl_done > 0} {
3518 proc delvhighlight {} {
3519 global hlview vhighlights
3521 if {![info exists hlview]} return
3523 catch {unset vhighlights}
3527 proc vhighlightmore {} {
3528 global hlview vhl_done commitidx vhighlights curview
3530 set max $commitidx($hlview)
3531 set vr [visiblerows]
3532 set r0 [lindex $vr 0]
3533 set r1 [lindex $vr 1]
3534 for {set i $vhl_done} {$i < $max} {incr i} {
3535 set id [commitonrow $i $hlview]
3536 if {[commitinview $id $curview]} {
3537 set row [rowofcommit $id]
3538 if {$r0 <= $row && $row <= $r1} {
3539 if {![highlighted $row]} {
3540 bolden $row mainfontbold
3542 set vhighlights($id) 1
3550 proc askvhighlight {row id} {
3551 global hlview vhighlights iddrawn
3553 if {[commitinview $id $hlview]} {
3554 if {[info exists iddrawn($id)] && ![ishighlighted $id]} {
3555 bolden $row mainfontbold
3557 set vhighlights($id) 1
3559 set vhighlights($id) 0
3563 proc hfiles_change {} {
3564 global highlight_files filehighlight fhighlights fh_serial
3565 global highlight_paths gdttype
3567 if {[info exists filehighlight]} {
3568 # delete previous highlights
3569 catch {close $filehighlight}
3571 catch {unset fhighlights}
3573 unhighlight_filelist
3575 set highlight_paths {}
3576 after cancel do_file_hl $fh_serial
3578 if {$highlight_files ne {}} {
3579 after 300 do_file_hl $fh_serial
3583 proc gdttype_change {name ix op} {
3584 global gdttype highlight_files findstring findpattern
3587 if {$findstring ne {}} {
3588 if {$gdttype eq [mc "containing:"]} {
3589 if {$highlight_files ne {}} {
3590 set highlight_files {}
3595 if {$findpattern ne {}} {
3599 set highlight_files $findstring
3604 # enable/disable findtype/findloc menus too
3607 proc find_change {name ix op} {
3608 global gdttype findstring highlight_files
3611 if {$gdttype eq [mc "containing:"]} {
3614 if {$highlight_files ne $findstring} {
3615 set highlight_files $findstring
3622 proc findcom_change args {
3623 global nhighlights boldnamerows
3624 global findpattern findtype findstring gdttype
3627 # delete previous highlights, if any
3628 foreach row $boldnamerows {
3629 bolden_name $row mainfont
3632 catch {unset nhighlights}
3635 if {$gdttype ne [mc "containing:"] || $findstring eq {}} {
3637 } elseif {$findtype eq [mc "Regexp"]} {
3638 set findpattern $findstring
3640 set e [string map {"*" "\\*" "?" "\\?" "\[" "\\\[" "\\" "\\\\"} \
3642 set findpattern "*$e*"
3646 proc makepatterns {l} {
3649 set ee [string map {"*" "\\*" "?" "\\?" "\[" "\\\[" "\\" "\\\\"} $e]
3650 if {[string index $ee end] eq "/"} {
3660 proc do_file_hl {serial} {
3661 global highlight_files filehighlight highlight_paths gdttype fhl_list
3663 if {$gdttype eq [mc "touching paths:"]} {
3664 if {[catch {set paths [shellsplit $highlight_files]}]} return
3665 set highlight_paths [makepatterns $paths]
3667 set gdtargs [concat -- $paths]
3668 } elseif {$gdttype eq [mc "adding/removing string:"]} {
3669 set gdtargs [list "-S$highlight_files"]
3671 # must be "containing:", i.e. we're searching commit info
3674 set cmd [concat | git diff-tree -r -s --stdin $gdtargs]
3675 set filehighlight [open $cmd r+]
3676 fconfigure $filehighlight -blocking 0
3677 filerun $filehighlight readfhighlight
3683 proc flushhighlights {} {
3684 global filehighlight fhl_list
3686 if {[info exists filehighlight]} {
3688 puts $filehighlight ""
3689 flush $filehighlight
3693 proc askfilehighlight {row id} {
3694 global filehighlight fhighlights fhl_list
3696 lappend fhl_list $id
3697 set fhighlights($id) -1
3698 puts $filehighlight $id
3701 proc readfhighlight {} {
3702 global filehighlight fhighlights curview iddrawn
3703 global fhl_list find_dirn
3705 if {![info exists filehighlight]} {
3709 while {[incr nr] <= 100 && [gets $filehighlight line] >= 0} {
3710 set line [string trim $line]
3711 set i [lsearch -exact $fhl_list $line]
3712 if {$i < 0} continue
3713 for {set j 0} {$j < $i} {incr j} {
3714 set id [lindex $fhl_list $j]
3715 set fhighlights($id) 0
3717 set fhl_list [lrange $fhl_list [expr {$i+1}] end]
3718 if {$line eq {}} continue
3719 if {![commitinview $line $curview]} continue
3720 set row [rowofcommit $line]
3721 if {[info exists iddrawn($line)] && ![ishighlighted $line]} {
3722 bolden $row mainfontbold
3724 set fhighlights($line) 1
3726 if {[eof $filehighlight]} {
3728 puts "oops, git diff-tree died"
3729 catch {close $filehighlight}
3733 if {[info exists find_dirn]} {
3739 proc doesmatch {f} {
3740 global findtype findpattern
3742 if {$findtype eq [mc "Regexp"]} {
3743 return [regexp $findpattern $f]
3744 } elseif {$findtype eq [mc "IgnCase"]} {
3745 return [string match -nocase $findpattern $f]
3747 return [string match $findpattern $f]
3751 proc askfindhighlight {row id} {
3752 global nhighlights commitinfo iddrawn
3754 global markingmatches
3756 if {![info exists commitinfo($id)]} {
3759 set info $commitinfo($id)
3761 set fldtypes [list [mc Headline] [mc Author] [mc Date] [mc Committer] [mc CDate] [mc Comments]]
3762 foreach f $info ty $fldtypes {
3763 if {($findloc eq [mc "All fields"] || $findloc eq $ty) &&
3765 if {$ty eq [mc "Author"]} {
3772 if {$isbold && [info exists iddrawn($id)]} {
3773 if {![ishighlighted $id]} {
3774 bolden $row mainfontbold
3776 bolden_name $row mainfontbold
3779 if {$markingmatches} {
3780 markrowmatches $row $id
3783 set nhighlights($id) $isbold
3786 proc markrowmatches {row id} {
3787 global canv canv2 linehtag linentag commitinfo findloc
3789 set headline [lindex $commitinfo($id) 0]
3790 set author [lindex $commitinfo($id) 1]
3791 $canv delete match$row
3792 $canv2 delete match$row
3793 if {$findloc eq [mc "All fields"] || $findloc eq [mc "Headline"]} {
3794 set m [findmatches $headline]
3796 markmatches $canv $row $headline $linehtag($row) $m \
3797 [$canv itemcget $linehtag($row) -font] $row
3800 if {$findloc eq [mc "All fields"] || $findloc eq [mc "Author"]} {
3801 set m [findmatches $author]
3803 markmatches $canv2 $row $author $linentag($row) $m \
3804 [$canv2 itemcget $linentag($row) -font] $row
3809 proc vrel_change {name ix op} {
3810 global highlight_related
3813 if {$highlight_related ne [mc "None"]} {
3818 # prepare for testing whether commits are descendents or ancestors of a
3819 proc rhighlight_sel {a} {
3820 global descendent desc_todo ancestor anc_todo
3821 global highlight_related
3823 catch {unset descendent}
3824 set desc_todo [list $a]
3825 catch {unset ancestor}
3826 set anc_todo [list $a]
3827 if {$highlight_related ne [mc "None"]} {
3833 proc rhighlight_none {} {
3836 catch {unset rhighlights}
3840 proc is_descendent {a} {
3841 global curview children descendent desc_todo
3844 set la [rowofcommit $a]
3848 for {set i 0} {$i < [llength $todo]} {incr i} {
3849 set do [lindex $todo $i]
3850 if {[rowofcommit $do] < $la} {
3851 lappend leftover $do
3854 foreach nk $children($v,$do) {
3855 if {![info exists descendent($nk)]} {
3856 set descendent($nk) 1
3864 set desc_todo [concat $leftover [lrange $todo [expr {$i+1}] end]]
3868 set descendent($a) 0
3869 set desc_todo $leftover
3872 proc is_ancestor {a} {
3873 global curview parents ancestor anc_todo
3876 set la [rowofcommit $a]
3880 for {set i 0} {$i < [llength $todo]} {incr i} {
3881 set do [lindex $todo $i]
3882 if {![commitinview $do $v] || [rowofcommit $do] > $la} {
3883 lappend leftover $do
3886 foreach np $parents($v,$do) {
3887 if {![info exists ancestor($np)]} {
3896 set anc_todo [concat $leftover [lrange $todo [expr {$i+1}] end]]
3901 set anc_todo $leftover
3904 proc askrelhighlight {row id} {
3905 global descendent highlight_related iddrawn rhighlights
3906 global selectedline ancestor
3908 if {$selectedline eq {}} return
3910 if {$highlight_related eq [mc "Descendant"] ||
3911 $highlight_related eq [mc "Not descendant"]} {
3912 if {![info exists descendent($id)]} {
3915 if {$descendent($id) == ($highlight_related eq [mc "Descendant"])} {
3918 } elseif {$highlight_related eq [mc "Ancestor"] ||
3919 $highlight_related eq [mc "Not ancestor"]} {
3920 if {![info exists ancestor($id)]} {
3923 if {$ancestor($id) == ($highlight_related eq [mc "Ancestor"])} {
3927 if {[info exists iddrawn($id)]} {
3928 if {$isbold && ![ishighlighted $id]} {
3929 bolden $row mainfontbold
3932 set rhighlights($id) $isbold
3935 # Graph layout functions
3937 proc shortids {ids} {
3940 if {[llength $id] > 1} {
3941 lappend res [shortids $id]
3942 } elseif {[regexp {^[0-9a-f]{40}$} $id]} {
3943 lappend res [string range $id 0 7]
3954 for {set mask 1} {$mask <= $n} {incr mask $mask} {
3955 if {($n & $mask) != 0} {
3956 set ret [concat $ret $o]
3958 set o [concat $o $o]
3963 proc ordertoken {id} {
3964 global ordertok curview varcid varcstart varctok curview parents children
3965 global nullid nullid2
3967 if {[info exists ordertok($id)]} {
3968 return $ordertok($id)
3973 if {[info exists varcid($curview,$id)]} {
3974 set a $varcid($curview,$id)
3975 set p [lindex $varcstart($curview) $a]
3977 set p [lindex $children($curview,$id) 0]
3979 if {[info exists ordertok($p)]} {
3980 set tok $ordertok($p)
3983 set id [first_real_child $curview,$p]
3986 set tok [lindex $varctok($curview) $varcid($curview,$p)]
3989 if {[llength $parents($curview,$id)] == 1} {
3990 lappend todo [list $p {}]
3992 set j [lsearch -exact $parents($curview,$id) $p]
3994 puts "oops didn't find [shortids $p] in parents of [shortids $id]"
3996 lappend todo [list $p [strrep $j]]
3999 for {set i [llength $todo]} {[incr i -1] >= 0} {} {
4000 set p [lindex $todo $i 0]
4001 append tok [lindex $todo $i 1]
4002 set ordertok($p) $tok
4004 set ordertok($origid) $tok
4008 # Work out where id should go in idlist so that order-token
4009 # values increase from left to right
4010 proc idcol {idlist id {i 0}} {
4011 set t [ordertoken $id]
4015 if {$i >= [llength $idlist] || $t < [ordertoken [lindex $idlist $i]]} {
4016 if {$i > [llength $idlist]} {
4017 set i [llength $idlist]
4019 while {[incr i -1] >= 0 && $t < [ordertoken [lindex $idlist $i]]} {}
4022 if {$t > [ordertoken [lindex $idlist $i]]} {
4023 while {[incr i] < [llength $idlist] &&
4024 $t >= [ordertoken [lindex $idlist $i]]} {}
4030 proc initlayout {} {
4031 global rowidlist rowisopt rowfinal displayorder parentlist
4032 global numcommits canvxmax canv
4034 global colormap rowtextx
4043 set canvxmax [$canv cget -width]
4044 catch {unset colormap}
4045 catch {unset rowtextx}
4049 proc setcanvscroll {} {
4050 global canv canv2 canv3 numcommits linespc canvxmax canvy0
4051 global lastscrollset lastscrollrows
4053 set ymax [expr {$canvy0 + ($numcommits - 0.5) * $linespc + 2}]
4054 $canv conf -scrollregion [list 0 0 $canvxmax $ymax]
4055 $canv2 conf -scrollregion [list 0 0 0 $ymax]
4056 $canv3 conf -scrollregion [list 0 0 0 $ymax]
4057 set lastscrollset [clock clicks -milliseconds]
4058 set lastscrollrows $numcommits
4061 proc visiblerows {} {
4062 global canv numcommits linespc
4064 set ymax [lindex [$canv cget -scrollregion] 3]
4065 if {$ymax eq {} || $ymax == 0} return
4067 set y0 [expr {int([lindex $f 0] * $ymax)}]
4068 set r0 [expr {int(($y0 - 3) / $linespc) - 1}]
4072 set y1 [expr {int([lindex $f 1] * $ymax)}]
4073 set r1 [expr {int(($y1 - 3) / $linespc) + 1}]
4074 if {$r1 >= $numcommits} {
4075 set r1 [expr {$numcommits - 1}]
4077 return [list $r0 $r1]
4080 proc layoutmore {} {
4081 global commitidx viewcomplete curview
4082 global numcommits pending_select curview
4083 global lastscrollset lastscrollrows commitinterest
4085 if {$lastscrollrows < 100 || $viewcomplete($curview) ||
4086 [clock clicks -milliseconds] - $lastscrollset > 500} {
4089 if {[info exists pending_select] &&
4090 [commitinview $pending_select $curview]} {
4092 selectline [rowofcommit $pending_select] 1
4097 proc doshowlocalchanges {} {
4098 global curview mainheadid
4100 if {$mainheadid eq {}} return
4101 if {[commitinview $mainheadid $curview]} {
4104 lappend commitinterest($mainheadid) {dodiffindex}
4108 proc dohidelocalchanges {} {
4109 global nullid nullid2 lserial curview
4111 if {[commitinview $nullid $curview]} {
4112 removefakerow $nullid
4114 if {[commitinview $nullid2 $curview]} {
4115 removefakerow $nullid2
4120 # spawn off a process to do git diff-index --cached HEAD
4121 proc dodiffindex {} {
4122 global lserial showlocalchanges
4125 if {!$showlocalchanges || !$isworktree} return
4127 set fd [open "|git diff-index --cached HEAD" r]
4128 fconfigure $fd -blocking 0
4129 set i [reg_instance $fd]
4130 filerun $fd [list readdiffindex $fd $lserial $i]
4133 proc readdiffindex {fd serial inst} {
4134 global mainheadid nullid nullid2 curview commitinfo commitdata lserial
4137 if {[gets $fd line] < 0} {
4143 # we only need to see one line and we don't really care what it says...
4146 if {$serial != $lserial} {
4150 # now see if there are any local changes not checked in to the index
4151 set fd [open "|git diff-files" r]
4152 fconfigure $fd -blocking 0
4153 set i [reg_instance $fd]
4154 filerun $fd [list readdifffiles $fd $serial $i]
4156 if {$isdiff && ![commitinview $nullid2 $curview]} {
4157 # add the line for the changes in the index to the graph
4158 set hl [mc "Local changes checked in to index but not committed"]
4159 set commitinfo($nullid2) [list $hl {} {} {} {} " $hl\n"]
4160 set commitdata($nullid2) "\n $hl\n"
4161 if {[commitinview $nullid $curview]} {
4162 removefakerow $nullid
4164 insertfakerow $nullid2 $mainheadid
4165 } elseif {!$isdiff && [commitinview $nullid2 $curview]} {
4166 removefakerow $nullid2
4171 proc readdifffiles {fd serial inst} {
4172 global mainheadid nullid nullid2 curview
4173 global commitinfo commitdata lserial
4176 if {[gets $fd line] < 0} {
4182 # we only need to see one line and we don't really care what it says...
4185 if {$serial != $lserial} {
4189 if {$isdiff && ![commitinview $nullid $curview]} {
4190 # add the line for the local diff to the graph
4191 set hl [mc "Local uncommitted changes, not checked in to index"]
4192 set commitinfo($nullid) [list $hl {} {} {} {} " $hl\n"]
4193 set commitdata($nullid) "\n $hl\n"
4194 if {[commitinview $nullid2 $curview]} {
4199 insertfakerow $nullid $p
4200 } elseif {!$isdiff && [commitinview $nullid $curview]} {
4201 removefakerow $nullid
4206 proc nextuse {id row} {
4207 global curview children
4209 if {[info exists children($curview,$id)]} {
4210 foreach kid $children($curview,$id) {
4211 if {![commitinview $kid $curview]} {
4214 if {[rowofcommit $kid] > $row} {
4215 return [rowofcommit $kid]
4219 if {[commitinview $id $curview]} {
4220 return [rowofcommit $id]
4225 proc prevuse {id row} {
4226 global curview children
4229 if {[info exists children($curview,$id)]} {
4230 foreach kid $children($curview,$id) {
4231 if {![commitinview $kid $curview]} break
4232 if {[rowofcommit $kid] < $row} {
4233 set ret [rowofcommit $kid]
4240 proc make_idlist {row} {
4241 global displayorder parentlist uparrowlen downarrowlen mingaplen
4242 global commitidx curview children
4244 set r [expr {$row - $mingaplen - $downarrowlen - 1}]
4248 set ra [expr {$row - $downarrowlen}]
4252 set rb [expr {$row + $uparrowlen}]
4253 if {$rb > $commitidx($curview)} {
4254 set rb $commitidx($curview)
4256 make_disporder $r [expr {$rb + 1}]
4258 for {} {$r < $ra} {incr r} {
4259 set nextid [lindex $displayorder [expr {$r + 1}]]
4260 foreach p [lindex $parentlist $r] {
4261 if {$p eq $nextid} continue
4262 set rn [nextuse $p $r]
4264 $rn <= $r + $downarrowlen + $mingaplen + $uparrowlen} {
4265 lappend ids [list [ordertoken $p] $p]
4269 for {} {$r < $row} {incr r} {
4270 set nextid [lindex $displayorder [expr {$r + 1}]]
4271 foreach p [lindex $parentlist $r] {
4272 if {$p eq $nextid} continue
4273 set rn [nextuse $p $r]
4274 if {$rn < 0 || $rn >= $row} {
4275 lappend ids [list [ordertoken $p] $p]
4279 set id [lindex $displayorder $row]
4280 lappend ids [list [ordertoken $id] $id]
4282 foreach p [lindex $parentlist $r] {
4283 set firstkid [lindex $children($curview,$p) 0]
4284 if {[rowofcommit $firstkid] < $row} {
4285 lappend ids [list [ordertoken $p] $p]
4289 set id [lindex $displayorder $r]
4291 set firstkid [lindex $children($curview,$id) 0]
4292 if {$firstkid ne {} && [rowofcommit $firstkid] < $row} {
4293 lappend ids [list [ordertoken $id] $id]
4298 foreach idx [lsort -unique $ids] {
4299 lappend idlist [lindex $idx 1]
4304 proc rowsequal {a b} {
4305 while {[set i [lsearch -exact $a {}]] >= 0} {
4306 set a [lreplace $a $i $i]
4308 while {[set i [lsearch -exact $b {}]] >= 0} {
4309 set b [lreplace $b $i $i]
4311 return [expr {$a eq $b}]
4314 proc makeupline {id row rend col} {
4315 global rowidlist uparrowlen downarrowlen mingaplen
4317 for {set r $rend} {1} {set r $rstart} {
4318 set rstart [prevuse $id $r]
4319 if {$rstart < 0} return
4320 if {$rstart < $row} break
4322 if {$rstart + $uparrowlen + $mingaplen + $downarrowlen < $rend} {
4323 set rstart [expr {$rend - $uparrowlen - 1}]
4325 for {set r $rstart} {[incr r] <= $row} {} {
4326 set idlist [lindex $rowidlist $r]
4327 if {$idlist ne {} && [lsearch -exact $idlist $id] < 0} {
4328 set col [idcol $idlist $id $col]
4329 lset rowidlist $r [linsert $idlist $col $id]
4335 proc layoutrows {row endrow} {
4336 global rowidlist rowisopt rowfinal displayorder
4337 global uparrowlen downarrowlen maxwidth mingaplen
4338 global children parentlist
4339 global commitidx viewcomplete curview
4341 make_disporder [expr {$row - 1}] [expr {$endrow + $uparrowlen}]
4344 set rm1 [expr {$row - 1}]
4345 foreach id [lindex $rowidlist $rm1] {
4350 set final [lindex $rowfinal $rm1]
4352 for {} {$row < $endrow} {incr row} {
4353 set rm1 [expr {$row - 1}]
4354 if {$rm1 < 0 || $idlist eq {}} {
4355 set idlist [make_idlist $row]
4358 set id [lindex $displayorder $rm1]
4359 set col [lsearch -exact $idlist $id]
4360 set idlist [lreplace $idlist $col $col]
4361 foreach p [lindex $parentlist $rm1] {
4362 if {[lsearch -exact $idlist $p] < 0} {
4363 set col [idcol $idlist $p $col]
4364 set idlist [linsert $idlist $col $p]
4365 # if not the first child, we have to insert a line going up
4366 if {$id ne [lindex $children($curview,$p) 0]} {
4367 makeupline $p $rm1 $row $col
4371 set id [lindex $displayorder $row]
4372 if {$row > $downarrowlen} {
4373 set termrow [expr {$row - $downarrowlen - 1}]
4374 foreach p [lindex $parentlist $termrow] {
4375 set i [lsearch -exact $idlist $p]
4376 if {$i < 0} continue
4377 set nr [nextuse $p $termrow]
4378 if {$nr < 0 || $nr >= $row + $mingaplen + $uparrowlen} {
4379 set idlist [lreplace $idlist $i $i]
4383 set col [lsearch -exact $idlist $id]
4385 set col [idcol $idlist $id]
4386 set idlist [linsert $idlist $col $id]
4387 if {$children($curview,$id) ne {}} {
4388 makeupline $id $rm1 $row $col
4391 set r [expr {$row + $uparrowlen - 1}]
4392 if {$r < $commitidx($curview)} {
4394 foreach p [lindex $parentlist $r] {
4395 if {[lsearch -exact $idlist $p] >= 0} continue
4396 set fk [lindex $children($curview,$p) 0]
4397 if {[rowofcommit $fk] < $row} {
4398 set x [idcol $idlist $p $x]
4399 set idlist [linsert $idlist $x $p]
4402 if {[incr r] < $commitidx($curview)} {
4403 set p [lindex $displayorder $r]
4404 if {[lsearch -exact $idlist $p] < 0} {
4405 set fk [lindex $children($curview,$p) 0]
4406 if {$fk ne {} && [rowofcommit $fk] < $row} {
4407 set x [idcol $idlist $p $x]
4408 set idlist [linsert $idlist $x $p]
4414 if {$final && !$viewcomplete($curview) &&
4415 $row + $uparrowlen + $mingaplen + $downarrowlen
4416 >= $commitidx($curview)} {
4419 set l [llength $rowidlist]
4421 lappend rowidlist $idlist
4423 lappend rowfinal $final
4424 } elseif {$row < $l} {
4425 if {![rowsequal $idlist [lindex $rowidlist $row]]} {
4426 lset rowidlist $row $idlist
4429 lset rowfinal $row $final
4431 set pad [ntimes [expr {$row - $l}] {}]
4432 set rowidlist [concat $rowidlist $pad]
4433 lappend rowidlist $idlist
4434 set rowfinal [concat $rowfinal $pad]
4435 lappend rowfinal $final
4436 set rowisopt [concat $rowisopt [ntimes [expr {$row - $l + 1}] 0]]
4442 proc changedrow {row} {
4443 global displayorder iddrawn rowisopt need_redisplay
4445 set l [llength $rowisopt]
4447 lset rowisopt $row 0
4448 if {$row + 1 < $l} {
4449 lset rowisopt [expr {$row + 1}] 0
4450 if {$row + 2 < $l} {
4451 lset rowisopt [expr {$row + 2}] 0
4455 set id [lindex $displayorder $row]
4456 if {[info exists iddrawn($id)]} {
4457 set need_redisplay 1
4461 proc insert_pad {row col npad} {
4464 set pad [ntimes $npad {}]
4465 set idlist [lindex $rowidlist $row]
4466 set bef [lrange $idlist 0 [expr {$col - 1}]]
4467 set aft [lrange $idlist $col end]
4468 set i [lsearch -exact $aft {}]
4470 set aft [lreplace $aft $i $i]
4472 lset rowidlist $row [concat $bef $pad $aft]
4476 proc optimize_rows {row col endrow} {
4477 global rowidlist rowisopt displayorder curview children
4482 for {} {$row < $endrow} {incr row; set col 0} {
4483 if {[lindex $rowisopt $row]} continue
4485 set y0 [expr {$row - 1}]
4486 set ym [expr {$row - 2}]
4487 set idlist [lindex $rowidlist $row]
4488 set previdlist [lindex $rowidlist $y0]
4489 if {$idlist eq {} || $previdlist eq {}} continue
4491 set pprevidlist [lindex $rowidlist $ym]
4492 if {$pprevidlist eq {}} continue
4498 for {} {$col < [llength $idlist]} {incr col} {
4499 set id [lindex $idlist $col]
4500 if {[lindex $previdlist $col] eq $id} continue
4505 set x0 [lsearch -exact $previdlist $id]
4506 if {$x0 < 0} continue
4507 set z [expr {$x0 - $col}]
4511 set xm [lsearch -exact $pprevidlist $id]
4513 set z0 [expr {$xm - $x0}]
4517 # if row y0 is the first child of $id then it's not an arrow
4518 if {[lindex $children($curview,$id) 0] ne
4519 [lindex $displayorder $y0]} {
4523 if {!$isarrow && $id ne [lindex $displayorder $row] &&
4524 [lsearch -exact [lindex $rowidlist [expr {$row+1}]] $id] < 0} {
4527 # Looking at lines from this row to the previous row,
4528 # make them go straight up if they end in an arrow on
4529 # the previous row; otherwise make them go straight up
4531 if {$z < -1 || ($z < 0 && $isarrow)} {
4532 # Line currently goes left too much;
4533 # insert pads in the previous row, then optimize it
4534 set npad [expr {-1 - $z + $isarrow}]
4535 insert_pad $y0 $x0 $npad
4537 optimize_rows $y0 $x0 $row
4539 set previdlist [lindex $rowidlist $y0]
4540 set x0 [lsearch -exact $previdlist $id]
4541 set z [expr {$x0 - $col}]
4543 set pprevidlist [lindex $rowidlist $ym]
4544 set xm [lsearch -exact $pprevidlist $id]
4545 set z0 [expr {$xm - $x0}]
4547 } elseif {$z > 1 || ($z > 0 && $isarrow)} {
4548 # Line currently goes right too much;
4549 # insert pads in this line
4550 set npad [expr {$z - 1 + $isarrow}]
4551 insert_pad $row $col $npad
4552 set idlist [lindex $rowidlist $row]
4554 set z [expr {$x0 - $col}]
4557 if {$z0 eq {} && !$isarrow && $ym >= 0} {
4558 # this line links to its first child on row $row-2
4559 set id [lindex $displayorder $ym]
4560 set xc [lsearch -exact $pprevidlist $id]
4562 set z0 [expr {$xc - $x0}]
4565 # avoid lines jigging left then immediately right
4566 if {$z0 ne {} && $z < 0 && $z0 > 0} {
4567 insert_pad $y0 $x0 1
4569 optimize_rows $y0 $x0 $row
4570 set previdlist [lindex $rowidlist $y0]
4574 # Find the first column that doesn't have a line going right
4575 for {set col [llength $idlist]} {[incr col -1] >= 0} {} {
4576 set id [lindex $idlist $col]
4577 if {$id eq {}} break
4578 set x0 [lsearch -exact $previdlist $id]
4580 # check if this is the link to the first child
4581 set kid [lindex $displayorder $y0]
4582 if {[lindex $children($curview,$id) 0] eq $kid} {
4583 # it is, work out offset to child
4584 set x0 [lsearch -exact $previdlist $kid]
4587 if {$x0 <= $col} break
4589 # Insert a pad at that column as long as it has a line and
4590 # isn't the last column
4591 if {$x0 >= 0 && [incr col] < [llength $idlist]} {
4592 set idlist [linsert $idlist $col {}]
4593 lset rowidlist $row $idlist
4601 global canvx0 linespc
4602 return [expr {$canvx0 + $col * $linespc}]
4606 global canvy0 linespc
4607 return [expr {$canvy0 + $row * $linespc}]
4610 proc linewidth {id} {
4611 global thickerline lthickness
4614 if {[info exists thickerline] && $id eq $thickerline} {
4615 set wid [expr {2 * $lthickness}]
4620 proc rowranges {id} {
4621 global curview children uparrowlen downarrowlen
4624 set kids $children($curview,$id)
4630 foreach child $kids {
4631 if {![commitinview $child $curview]} break
4632 set row [rowofcommit $child]
4633 if {![info exists prev]} {
4634 lappend ret [expr {$row + 1}]
4636 if {$row <= $prevrow} {
4637 puts "oops children of [shortids $id] out of order [shortids $child] $row <= [shortids $prev] $prevrow"
4639 # see if the line extends the whole way from prevrow to row
4640 if {$row > $prevrow + $uparrowlen + $downarrowlen &&
4641 [lsearch -exact [lindex $rowidlist \
4642 [expr {int(($row + $prevrow) / 2)}]] $id] < 0} {
4643 # it doesn't, see where it ends
4644 set r [expr {$prevrow + $downarrowlen}]
4645 if {[lsearch -exact [lindex $rowidlist $r] $id] < 0} {
4646 while {[incr r -1] > $prevrow &&
4647 [lsearch -exact [lindex $rowidlist $r] $id] < 0} {}
4649 while {[incr r] <= $row &&
4650 [lsearch -exact [lindex $rowidlist $r] $id] >= 0} {}
4654 # see where it starts up again
4655 set r [expr {$row - $uparrowlen}]
4656 if {[lsearch -exact [lindex $rowidlist $r] $id] < 0} {
4657 while {[incr r] < $row &&
4658 [lsearch -exact [lindex $rowidlist $r] $id] < 0} {}
4660 while {[incr r -1] >= $prevrow &&
4661 [lsearch -exact [lindex $rowidlist $r] $id] >= 0} {}
4667 if {$child eq $id} {
4676 proc drawlineseg {id row endrow arrowlow} {
4677 global rowidlist displayorder iddrawn linesegs
4678 global canv colormap linespc curview maxlinelen parentlist
4680 set cols [list [lsearch -exact [lindex $rowidlist $row] $id]]
4681 set le [expr {$row + 1}]
4684 set c [lsearch -exact [lindex $rowidlist $le] $id]
4690 set x [lindex $displayorder $le]
4695 if {[info exists iddrawn($x)] || $le == $endrow} {
4696 set c [lsearch -exact [lindex $rowidlist [expr {$le+1}]] $id]
4712 if {[info exists linesegs($id)]} {
4713 set lines $linesegs($id)
4715 set r0 [lindex $li 0]
4717 if {$r0 == $le && [lindex $li 1] - $row <= $maxlinelen} {
4727 set li [lindex $lines [expr {$i-1}]]
4728 set r1 [lindex $li 1]
4729 if {$r1 == $row && $le - [lindex $li 0] <= $maxlinelen} {
4734 set x [lindex $cols [expr {$le - $row}]]
4735 set xp [lindex $cols [expr {$le - 1 - $row}]]
4736 set dir [expr {$xp - $x}]
4738 set ith [lindex $lines $i 2]
4739 set coords [$canv coords $ith]
4740 set ah [$canv itemcget $ith -arrow]
4741 set arrowhigh [expr {$ah eq "first" || $ah eq "both"}]
4742 set x2 [lindex $cols [expr {$le + 1 - $row}]]
4743 if {$x2 ne {} && $x - $x2 == $dir} {
4744 set coords [lrange $coords 0 end-2]
4747 set coords [list [xc $le $x] [yc $le]]
4750 set itl [lindex $lines [expr {$i-1}] 2]
4751 set al [$canv itemcget $itl -arrow]
4752 set arrowlow [expr {$al eq "last" || $al eq "both"}]
4753 } elseif {$arrowlow} {
4754 if {[lsearch -exact [lindex $rowidlist [expr {$row-1}]] $id] >= 0 ||
4755 [lsearch -exact [lindex $parentlist [expr {$row-1}]] $id] >= 0} {
4759 set arrow [lindex {none first last both} [expr {$arrowhigh + 2*$arrowlow}]]
4760 for {set y $le} {[incr y -1] > $row} {} {
4762 set xp [lindex $cols [expr {$y - 1 - $row}]]
4763 set ndir [expr {$xp - $x}]
4764 if {$dir != $ndir || $xp < 0} {
4765 lappend coords [xc $y $x] [yc $y]
4771 # join parent line to first child
4772 set ch [lindex $displayorder $row]
4773 set xc [lsearch -exact [lindex $rowidlist $row] $ch]
4775 puts "oops: drawlineseg: child $ch not on row $row"
4776 } elseif {$xc != $x} {
4777 if {($arrowhigh && $le == $row + 1) || $dir == 0} {
4778 set d [expr {int(0.5 * $linespc)}]
4781 set x2 [expr {$x1 - $d}]
4783 set x2 [expr {$x1 + $d}]
4786 set y1 [expr {$y2 + $d}]
4787 lappend coords $x1 $y1 $x2 $y2
4788 } elseif {$xc < $x - 1} {
4789 lappend coords [xc $row [expr {$x-1}]] [yc $row]
4790 } elseif {$xc > $x + 1} {
4791 lappend coords [xc $row [expr {$x+1}]] [yc $row]
4795 lappend coords [xc $row $x] [yc $row]
4797 set xn [xc $row $xp]
4799 lappend coords $xn $yn
4803 set t [$canv create line $coords -width [linewidth $id] \
4804 -fill $colormap($id) -tags lines.$id -arrow $arrow]
4807 set lines [linsert $lines $i [list $row $le $t]]
4809 $canv coords $ith $coords
4810 if {$arrow ne $ah} {
4811 $canv itemconf $ith -arrow $arrow
4813 lset lines $i 0 $row
4816 set xo [lsearch -exact [lindex $rowidlist [expr {$row - 1}]] $id]
4817 set ndir [expr {$xo - $xp}]
4818 set clow [$canv coords $itl]
4819 if {$dir == $ndir} {
4820 set clow [lrange $clow 2 end]
4822 set coords [concat $coords $clow]
4824 lset lines [expr {$i-1}] 1 $le
4826 # coalesce two pieces
4828 set b [lindex $lines [expr {$i-1}] 0]
4829 set e [lindex $lines $i 1]
4830 set lines [lreplace $lines [expr {$i-1}] $i [list $b $e $itl]]
4832 $canv coords $itl $coords
4833 if {$arrow ne $al} {
4834 $canv itemconf $itl -arrow $arrow
4838 set linesegs($id) $lines
4842 proc drawparentlinks {id row} {
4843 global rowidlist canv colormap curview parentlist
4844 global idpos linespc
4846 set rowids [lindex $rowidlist $row]
4847 set col [lsearch -exact $rowids $id]
4848 if {$col < 0} return
4849 set olds [lindex $parentlist $row]
4850 set row2 [expr {$row + 1}]
4851 set x [xc $row $col]
4854 set d [expr {int(0.5 * $linespc)}]
4855 set ymid [expr {$y + $d}]
4856 set ids [lindex $rowidlist $row2]
4857 # rmx = right-most X coord used
4860 set i [lsearch -exact $ids $p]
4862 puts "oops, parent $p of $id not in list"
4865 set x2 [xc $row2 $i]
4869 set j [lsearch -exact $rowids $p]
4871 # drawlineseg will do this one for us
4875 # should handle duplicated parents here...
4876 set coords [list $x $y]
4878 # if attaching to a vertical segment, draw a smaller
4879 # slant for visual distinctness
4882 lappend coords [expr {$x2 + $d}] $y $x2 $ymid
4884 lappend coords [expr {$x2 - $d}] $y $x2 $ymid
4886 } elseif {$i < $col && $i < $j} {
4887 # segment slants towards us already
4888 lappend coords [xc $row $j] $y
4890 if {$i < $col - 1} {
4891 lappend coords [expr {$x2 + $linespc}] $y
4892 } elseif {$i > $col + 1} {
4893 lappend coords [expr {$x2 - $linespc}] $y
4895 lappend coords $x2 $y2
4898 lappend coords $x2 $y2
4900 set t [$canv create line $coords -width [linewidth $p] \
4901 -fill $colormap($p) -tags lines.$p]
4905 if {$rmx > [lindex $idpos($id) 1]} {
4906 lset idpos($id) 1 $rmx
4911 proc drawlines {id} {
4914 $canv itemconf lines.$id -width [linewidth $id]
4917 proc drawcmittext {id row col} {
4918 global linespc canv canv2 canv3 fgcolor curview
4919 global cmitlisted commitinfo rowidlist parentlist
4920 global rowtextx idpos idtags idheads idotherrefs
4921 global linehtag linentag linedtag selectedline
4922 global canvxmax boldrows boldnamerows fgcolor
4923 global mainheadid nullid nullid2 circleitem circlecolors ctxbut
4925 # listed is 0 for boundary, 1 for normal, 2 for negative, 3 for left, 4 for right
4926 set listed $cmitlisted($curview,$id)
4927 if {$id eq $nullid} {
4929 } elseif {$id eq $nullid2} {
4931 } elseif {$id eq $mainheadid} {
4934 set ofill [lindex $circlecolors $listed]
4936 set x [xc $row $col]
4938 set orad [expr {$linespc / 3}]
4940 set t [$canv create oval [expr {$x - $orad}] [expr {$y - $orad}] \
4941 [expr {$x + $orad - 1}] [expr {$y + $orad - 1}] \
4942 -fill $ofill -outline $fgcolor -width 1 -tags circle]
4943 } elseif {$listed == 3} {
4944 # triangle pointing left for left-side commits
4945 set t [$canv create polygon \
4946 [expr {$x - $orad}] $y \
4947 [expr {$x + $orad - 1}] [expr {$y - $orad}] \
4948 [expr {$x + $orad - 1}] [expr {$y + $orad - 1}] \
4949 -fill $ofill -outline $fgcolor -width 1 -tags circle]
4951 # triangle pointing right for right-side commits
4952 set t [$canv create polygon \
4953 [expr {$x + $orad - 1}] $y \
4954 [expr {$x - $orad}] [expr {$y - $orad}] \
4955 [expr {$x - $orad}] [expr {$y + $orad - 1}] \
4956 -fill $ofill -outline $fgcolor -width 1 -tags circle]
4958 set circleitem($row) $t
4960 $canv bind $t <1> {selcanvline {} %x %y}
4961 set rmx [llength [lindex $rowidlist $row]]
4962 set olds [lindex $parentlist $row]
4964 set nextids [lindex $rowidlist [expr {$row + 1}]]
4966 set i [lsearch -exact $nextids $p]
4972 set xt [xc $row $rmx]
4973 set rowtextx($row) $xt
4974 set idpos($id) [list $x $xt $y]
4975 if {[info exists idtags($id)] || [info exists idheads($id)]
4976 || [info exists idotherrefs($id)]} {
4977 set xt [drawtags $id $x $xt $y]
4979 set headline [lindex $commitinfo($id) 0]
4980 set name [lindex $commitinfo($id) 1]
4981 set date [lindex $commitinfo($id) 2]
4982 set date [formatdate $date]
4985 set isbold [ishighlighted $id]
4987 lappend boldrows $row
4988 set font mainfontbold
4990 lappend boldnamerows $row
4991 set nfont mainfontbold
4994 set linehtag($row) [$canv create text $xt $y -anchor w -fill $fgcolor \
4995 -text $headline -font $font -tags text]
4996 $canv bind $linehtag($row) $ctxbut "rowmenu %X %Y $id"
4997 set linentag($row) [$canv2 create text 3 $y -anchor w -fill $fgcolor \
4998 -text $name -font $nfont -tags text]
4999 set linedtag($row) [$canv3 create text 3 $y -anchor w -fill $fgcolor \
5000 -text $date -font mainfont -tags text]
5001 if {$selectedline == $row} {
5004 set xr [expr {$xt + [font measure $font $headline]}]
5005 if {$xr > $canvxmax} {
5011 proc drawcmitrow {row} {
5012 global displayorder rowidlist nrows_drawn
5013 global iddrawn markingmatches
5014 global commitinfo numcommits
5015 global filehighlight fhighlights findpattern nhighlights
5016 global hlview vhighlights
5017 global highlight_related rhighlights
5019 if {$row >= $numcommits} return
5021 set id [lindex $displayorder $row]
5022 if {[info exists hlview] && ![info exists vhighlights($id)]} {
5023 askvhighlight $row $id
5025 if {[info exists filehighlight] && ![info exists fhighlights($id)]} {
5026 askfilehighlight $row $id
5028 if {$findpattern ne {} && ![info exists nhighlights($id)]} {
5029 askfindhighlight $row $id
5031 if {$highlight_related ne [mc "None"] && ![info exists rhighlights($id)]} {
5032 askrelhighlight $row $id
5034 if {![info exists iddrawn($id)]} {
5035 set col [lsearch -exact [lindex $rowidlist $row] $id]
5037 puts "oops, row $row id $id not in list"
5040 if {![info exists commitinfo($id)]} {
5044 drawcmittext $id $row $col
5048 if {$markingmatches} {
5049 markrowmatches $row $id
5053 proc drawcommits {row {endrow {}}} {
5054 global numcommits iddrawn displayorder curview need_redisplay
5055 global parentlist rowidlist rowfinal uparrowlen downarrowlen nrows_drawn
5060 if {$endrow eq {}} {
5063 if {$endrow >= $numcommits} {
5064 set endrow [expr {$numcommits - 1}]
5067 set rl1 [expr {$row - $downarrowlen - 3}]
5071 set ro1 [expr {$row - 3}]
5075 set r2 [expr {$endrow + $uparrowlen + 3}]
5076 if {$r2 > $numcommits} {
5079 for {set r $rl1} {$r < $r2} {incr r} {
5080 if {[lindex $rowidlist $r] ne {} && [lindex $rowfinal $r]} {
5084 set rl1 [expr {$r + 1}]
5090 optimize_rows $ro1 0 $r2
5091 if {$need_redisplay || $nrows_drawn > 2000} {
5096 # make the lines join to already-drawn rows either side
5097 set r [expr {$row - 1}]
5098 if {$r < 0 || ![info exists iddrawn([lindex $displayorder $r])]} {
5101 set er [expr {$endrow + 1}]
5102 if {$er >= $numcommits ||
5103 ![info exists iddrawn([lindex $displayorder $er])]} {
5106 for {} {$r <= $er} {incr r} {
5107 set id [lindex $displayorder $r]
5108 set wasdrawn [info exists iddrawn($id)]
5110 if {$r == $er} break
5111 set nextid [lindex $displayorder [expr {$r + 1}]]
5112 if {$wasdrawn && [info exists iddrawn($nextid)]} continue
5113 drawparentlinks $id $r
5115 set rowids [lindex $rowidlist $r]
5116 foreach lid $rowids {
5117 if {$lid eq {}} continue
5118 if {[info exists lineend($lid)] && $lineend($lid) > $r} continue
5120 # see if this is the first child of any of its parents
5121 foreach p [lindex $parentlist $r] {
5122 if {[lsearch -exact $rowids $p] < 0} {
5123 # make this line extend up to the child
5124 set lineend($p) [drawlineseg $p $r $er 0]
5128 set lineend($lid) [drawlineseg $lid $r $er 1]
5134 proc undolayout {row} {
5135 global uparrowlen mingaplen downarrowlen
5136 global rowidlist rowisopt rowfinal need_redisplay
5138 set r [expr {$row - ($uparrowlen + $mingaplen + $downarrowlen)}]
5142 if {[llength $rowidlist] > $r} {
5144 set rowidlist [lrange $rowidlist 0 $r]
5145 set rowfinal [lrange $rowfinal 0 $r]
5146 set rowisopt [lrange $rowisopt 0 $r]
5147 set need_redisplay 1
5152 proc drawvisible {} {
5153 global canv linespc curview vrowmod selectedline targetrow targetid
5154 global need_redisplay cscroll numcommits
5156 set fs [$canv yview]
5157 set ymax [lindex [$canv cget -scrollregion] 3]
5158 if {$ymax eq {} || $ymax == 0 || $numcommits == 0} return
5159 set f0 [lindex $fs 0]
5160 set f1 [lindex $fs 1]
5161 set y0 [expr {int($f0 * $ymax)}]
5162 set y1 [expr {int($f1 * $ymax)}]
5164 if {[info exists targetid]} {
5165 if {[commitinview $targetid $curview]} {
5166 set r [rowofcommit $targetid]
5167 if {$r != $targetrow} {
5168 # Fix up the scrollregion and change the scrolling position
5169 # now that our target row has moved.
5170 set diff [expr {($r - $targetrow) * $linespc}]
5173 set ymax [lindex [$canv cget -scrollregion] 3]
5176 set f0 [expr {$y0 / $ymax}]
5177 set f1 [expr {$y1 / $ymax}]
5178 allcanvs yview moveto $f0
5179 $cscroll set $f0 $f1
5180 set need_redisplay 1
5187 set row [expr {int(($y0 - 3) / $linespc) - 1}]
5188 set endrow [expr {int(($y1 - 3) / $linespc) + 1}]
5189 if {$endrow >= $vrowmod($curview)} {
5190 update_arcrows $curview
5192 if {$selectedline ne {} &&
5193 $row <= $selectedline && $selectedline <= $endrow} {
5194 set targetrow $selectedline
5195 } elseif {[info exists targetid]} {
5196 set targetrow [expr {int(($row + $endrow) / 2)}]
5198 if {[info exists targetrow]} {
5199 if {$targetrow >= $numcommits} {
5200 set targetrow [expr {$numcommits - 1}]
5202 set targetid [commitonrow $targetrow]
5204 drawcommits $row $endrow
5207 proc clear_display {} {
5208 global iddrawn linesegs need_redisplay nrows_drawn
5209 global vhighlights fhighlights nhighlights rhighlights
5210 global linehtag linentag linedtag boldrows boldnamerows
5213 catch {unset iddrawn}
5214 catch {unset linesegs}
5215 catch {unset linehtag}
5216 catch {unset linentag}
5217 catch {unset linedtag}
5220 catch {unset vhighlights}
5221 catch {unset fhighlights}
5222 catch {unset nhighlights}
5223 catch {unset rhighlights}
5224 set need_redisplay 0
5228 proc findcrossings {id} {
5229 global rowidlist parentlist numcommits displayorder
5233 foreach {s e} [rowranges $id] {
5234 if {$e >= $numcommits} {
5235 set e [expr {$numcommits - 1}]
5237 if {$e <= $s} continue
5238 for {set row $e} {[incr row -1] >= $s} {} {
5239 set x [lsearch -exact [lindex $rowidlist $row] $id]
5241 set olds [lindex $parentlist $row]
5242 set kid [lindex $displayorder $row]
5243 set kidx [lsearch -exact [lindex $rowidlist $row] $kid]
5244 if {$kidx < 0} continue
5245 set nextrow [lindex $rowidlist [expr {$row + 1}]]
5247 set px [lsearch -exact $nextrow $p]
5248 if {$px < 0} continue
5249 if {($kidx < $x && $x < $px) || ($px < $x && $x < $kidx)} {
5250 if {[lsearch -exact $ccross $p] >= 0} continue
5251 if {$x == $px + ($kidx < $px? -1: 1)} {
5253 } elseif {[lsearch -exact $cross $p] < 0} {
5260 return [concat $ccross {{}} $cross]
5263 proc assigncolor {id} {
5264 global colormap colors nextcolor
5265 global parents children children curview
5267 if {[info exists colormap($id)]} return
5268 set ncolors [llength $colors]
5269 if {[info exists children($curview,$id)]} {
5270 set kids $children($curview,$id)
5274 if {[llength $kids] == 1} {
5275 set child [lindex $kids 0]
5276 if {[info exists colormap($child)]
5277 && [llength $parents($curview,$child)] == 1} {
5278 set colormap($id) $colormap($child)
5284 foreach x [findcrossings $id] {
5286 # delimiter between corner crossings and other crossings
5287 if {[llength $badcolors] >= $ncolors - 1} break
5288 set origbad $badcolors
5290 if {[info exists colormap($x)]
5291 && [lsearch -exact $badcolors $colormap($x)] < 0} {
5292 lappend badcolors $colormap($x)
5295 if {[llength $badcolors] >= $ncolors} {
5296 set badcolors $origbad
5298 set origbad $badcolors
5299 if {[llength $badcolors] < $ncolors - 1} {
5300 foreach child $kids {
5301 if {[info exists colormap($child)]
5302 && [lsearch -exact $badcolors $colormap($child)] < 0} {
5303 lappend badcolors $colormap($child)
5305 foreach p $parents($curview,$child) {
5306 if {[info exists colormap($p)]
5307 && [lsearch -exact $badcolors $colormap($p)] < 0} {
5308 lappend badcolors $colormap($p)
5312 if {[llength $badcolors] >= $ncolors} {
5313 set badcolors $origbad
5316 for {set i 0} {$i <= $ncolors} {incr i} {
5317 set c [lindex $colors $nextcolor]
5318 if {[incr nextcolor] >= $ncolors} {
5321 if {[lsearch -exact $badcolors $c]} break
5323 set colormap($id) $c
5326 proc bindline {t id} {
5329 $canv bind $t <Enter> "lineenter %x %y $id"
5330 $canv bind $t <Motion> "linemotion %x %y $id"
5331 $canv bind $t <Leave> "lineleave $id"
5332 $canv bind $t <Button-1> "lineclick %x %y $id 1"
5335 proc drawtags {id x xt y1} {
5336 global idtags idheads idotherrefs mainhead
5337 global linespc lthickness
5338 global canv rowtextx curview fgcolor bgcolor ctxbut
5343 if {[info exists idtags($id)]} {
5344 set marks $idtags($id)
5345 set ntags [llength $marks]
5347 if {[info exists idheads($id)]} {
5348 set marks [concat $marks $idheads($id)]
5349 set nheads [llength $idheads($id)]
5351 if {[info exists idotherrefs($id)]} {
5352 set marks [concat $marks $idotherrefs($id)]
5358 set delta [expr {int(0.5 * ($linespc - $lthickness))}]
5359 set yt [expr {$y1 - 0.5 * $linespc}]
5360 set yb [expr {$yt + $linespc - 1}]
5364 foreach tag $marks {
5366 if {$i >= $ntags && $i < $ntags + $nheads && $tag eq $mainhead} {
5367 set wid [font measure mainfontbold $tag]
5369 set wid [font measure mainfont $tag]
5373 set xt [expr {$xt + $delta + $wid + $lthickness + $linespc}]
5375 set t [$canv create line $x $y1 [lindex $xvals end] $y1 \
5376 -width $lthickness -fill black -tags tag.$id]
5378 foreach tag $marks x $xvals wid $wvals {
5379 set xl [expr {$x + $delta}]
5380 set xr [expr {$x + $delta + $wid + $lthickness}]
5382 if {[incr ntags -1] >= 0} {
5384 set t [$canv create polygon $x [expr {$yt + $delta}] $xl $yt \
5385 $xr $yt $xr $yb $xl $yb $x [expr {$yb - $delta}] \
5386 -width 1 -outline black -fill yellow -tags tag.$id]
5387 $canv bind $t <1> [list showtag $tag 1]
5388 set rowtextx([rowofcommit $id]) [expr {$xr + $linespc}]
5390 # draw a head or other ref
5391 if {[incr nheads -1] >= 0} {
5393 if {$tag eq $mainhead} {
5394 set font mainfontbold
5399 set xl [expr {$xl - $delta/2}]
5400 $canv create polygon $x $yt $xr $yt $xr $yb $x $yb \
5401 -width 1 -outline black -fill $col -tags tag.$id
5402 if {[regexp {^(remotes/.*/|remotes/)} $tag match remoteprefix]} {
5403 set rwid [font measure mainfont $remoteprefix]
5404 set xi [expr {$x + 1}]
5405 set yti [expr {$yt + 1}]
5406 set xri [expr {$x + $rwid}]
5407 $canv create polygon $xi $yti $xri $yti $xri $yb $xi $yb \
5408 -width 0 -fill "#ffddaa" -tags tag.$id
5411 set t [$canv create text $xl $y1 -anchor w -text $tag -fill $fgcolor \
5412 -font $font -tags [list tag.$id text]]
5414 $canv bind $t <1> [list showtag $tag 1]
5415 } elseif {$nheads >= 0} {
5416 $canv bind $t $ctxbut [list headmenu %X %Y $id $tag]
5422 proc xcoord {i level ln} {
5423 global canvx0 xspc1 xspc2
5425 set x [expr {$canvx0 + $i * $xspc1($ln)}]
5426 if {$i > 0 && $i == $level} {
5427 set x [expr {$x + 0.5 * ($xspc2 - $xspc1($ln))}]
5428 } elseif {$i > $level} {
5429 set x [expr {$x + $xspc2 - $xspc1($ln)}]
5434 proc show_status {msg} {
5438 $canv create text 3 3 -anchor nw -text $msg -font mainfont \
5439 -tags text -fill $fgcolor
5442 # Don't change the text pane cursor if it is currently the hand cursor,
5443 # showing that we are over a sha1 ID link.
5444 proc settextcursor {c} {
5445 global ctext curtextcursor
5447 if {[$ctext cget -cursor] == $curtextcursor} {
5448 $ctext config -cursor $c
5450 set curtextcursor $c
5453 proc nowbusy {what {name {}}} {
5454 global isbusy busyname statusw
5456 if {[array names isbusy] eq {}} {
5457 . config -cursor watch
5461 set busyname($what) $name
5463 $statusw conf -text $name
5467 proc notbusy {what} {
5468 global isbusy maincursor textcursor busyname statusw
5472 if {$busyname($what) ne {} &&
5473 [$statusw cget -text] eq $busyname($what)} {
5474 $statusw conf -text {}
5477 if {[array names isbusy] eq {}} {
5478 . config -cursor $maincursor
5479 settextcursor $textcursor
5483 proc findmatches {f} {
5484 global findtype findstring
5485 if {$findtype == [mc "Regexp"]} {
5486 set matches [regexp -indices -all -inline $findstring $f]
5489 if {$findtype == [mc "IgnCase"]} {
5490 set f [string tolower $f]
5491 set fs [string tolower $fs]
5495 set l [string length $fs]
5496 while {[set j [string first $fs $f $i]] >= 0} {
5497 lappend matches [list $j [expr {$j+$l-1}]]
5498 set i [expr {$j + $l}]
5504 proc dofind {{dirn 1} {wrap 1}} {
5505 global findstring findstartline findcurline selectedline numcommits
5506 global gdttype filehighlight fh_serial find_dirn findallowwrap
5508 if {[info exists find_dirn]} {
5509 if {$find_dirn == $dirn} return
5513 if {$findstring eq {} || $numcommits == 0} return
5514 if {$selectedline eq {}} {
5515 set findstartline [lindex [visiblerows] [expr {$dirn < 0}]]
5517 set findstartline $selectedline
5519 set findcurline $findstartline
5520 nowbusy finding [mc "Searching"]
5521 if {$gdttype ne [mc "containing:"] && ![info exists filehighlight]} {
5522 after cancel do_file_hl $fh_serial
5523 do_file_hl $fh_serial
5526 set findallowwrap $wrap
5530 proc stopfinding {} {
5531 global find_dirn findcurline fprogcoord
5533 if {[info exists find_dirn]} {
5543 global commitdata commitinfo numcommits findpattern findloc
5544 global findstartline findcurline findallowwrap
5545 global find_dirn gdttype fhighlights fprogcoord
5546 global curview varcorder vrownum varccommits vrowmod
5548 if {![info exists find_dirn]} {
5551 set fldtypes [list [mc "Headline"] [mc "Author"] [mc "Date"] [mc "Committer"] [mc "CDate"] [mc "Comments"]]
5554 if {$find_dirn > 0} {
5556 if {$l >= $numcommits} {
5559 if {$l <= $findstartline} {
5560 set lim [expr {$findstartline + 1}]
5563 set moretodo $findallowwrap
5570 if {$l >= $findstartline} {
5571 set lim [expr {$findstartline - 1}]
5574 set moretodo $findallowwrap
5577 set n [expr {($lim - $l) * $find_dirn}]
5582 if {$l + ($find_dirn > 0? $n: 1) > $vrowmod($curview)} {
5583 update_arcrows $curview
5587 set ai [bsearch $vrownum($curview) $l]
5588 set a [lindex $varcorder($curview) $ai]
5589 set arow [lindex $vrownum($curview) $ai]
5590 set ids [lindex $varccommits($curview,$a)]
5591 set arowend [expr {$arow + [llength $ids]}]
5592 if {$gdttype eq [mc "containing:"]} {
5593 for {} {$n > 0} {incr n -1; incr l $find_dirn} {
5594 if {$l < $arow || $l >= $arowend} {
5596 set a [lindex $varcorder($curview) $ai]
5597 set arow [lindex $vrownum($curview) $ai]
5598 set ids [lindex $varccommits($curview,$a)]
5599 set arowend [expr {$arow + [llength $ids]}]
5601 set id [lindex $ids [expr {$l - $arow}]]
5602 # shouldn't happen unless git log doesn't give all the commits...
5603 if {![info exists commitdata($id)] ||
5604 ![doesmatch $commitdata($id)]} {
5607 if {![info exists commitinfo($id)]} {
5610 set info $commitinfo($id)
5611 foreach f $info ty $fldtypes {
5612 if {($findloc eq [mc "All fields"] || $findloc eq $ty) &&
5621 for {} {$n > 0} {incr n -1; incr l $find_dirn} {
5622 if {$l < $arow || $l >= $arowend} {
5624 set a [lindex $varcorder($curview) $ai]
5625 set arow [lindex $vrownum($curview) $ai]
5626 set ids [lindex $varccommits($curview,$a)]
5627 set arowend [expr {$arow + [llength $ids]}]
5629 set id [lindex $ids [expr {$l - $arow}]]
5630 if {![info exists fhighlights($id)]} {
5631 # this sets fhighlights($id) to -1
5632 askfilehighlight $l $id
5634 if {$fhighlights($id) > 0} {
5638 if {$fhighlights($id) < 0} {
5641 set findcurline [expr {$l - $find_dirn}]
5646 if {$found || ($domore && !$moretodo)} {
5662 set findcurline [expr {$l - $find_dirn}]
5664 set n [expr {($findcurline - $findstartline) * $find_dirn - 1}]
5668 set fprogcoord [expr {$n * 1.0 / $numcommits}]
5673 proc findselectline {l} {
5674 global findloc commentend ctext findcurline markingmatches gdttype
5676 set markingmatches 1
5679 if {$findloc == [mc "All fields"] || $findloc == [mc "Comments"]} {
5680 # highlight the matches in the comments
5681 set f [$ctext get 1.0 $commentend]
5682 set matches [findmatches $f]
5683 foreach match $matches {
5684 set start [lindex $match 0]
5685 set end [expr {[lindex $match 1] + 1}]
5686 $ctext tag add found "1.0 + $start c" "1.0 + $end c"
5692 # mark the bits of a headline or author that match a find string
5693 proc markmatches {canv l str tag matches font row} {
5696 set bbox [$canv bbox $tag]
5697 set x0 [lindex $bbox 0]
5698 set y0 [lindex $bbox 1]
5699 set y1 [lindex $bbox 3]
5700 foreach match $matches {
5701 set start [lindex $match 0]
5702 set end [lindex $match 1]
5703 if {$start > $end} continue
5704 set xoff [font measure $font [string range $str 0 [expr {$start-1}]]]
5705 set xlen [font measure $font [string range $str 0 [expr {$end}]]]
5706 set t [$canv create rect [expr {$x0+$xoff}] $y0 \
5707 [expr {$x0+$xlen+2}] $y1 \
5708 -outline {} -tags [list match$l matches] -fill yellow]
5710 if {$row == $selectedline} {
5711 $canv raise $t secsel
5716 proc unmarkmatches {} {
5717 global markingmatches
5719 allcanvs delete matches
5720 set markingmatches 0
5724 proc selcanvline {w x y} {
5725 global canv canvy0 ctext linespc
5727 set ymax [lindex [$canv cget -scrollregion] 3]
5728 if {$ymax == {}} return
5729 set yfrac [lindex [$canv yview] 0]
5730 set y [expr {$y + $yfrac * $ymax}]
5731 set l [expr {int(($y - $canvy0) / $linespc + 0.5)}]
5736 set xmax [lindex [$canv cget -scrollregion] 2]
5737 set xleft [expr {[lindex [$canv xview] 0] * $xmax}]
5738 if {![info exists rowtextx($l)] || $xleft + $x < $rowtextx($l)} return
5744 proc commit_descriptor {p} {
5746 if {![info exists commitinfo($p)]} {
5750 if {[llength $commitinfo($p)] > 1} {
5751 set l [lindex $commitinfo($p) 0]
5756 # append some text to the ctext widget, and make any SHA1 ID
5757 # that we know about be a clickable link.
5758 proc appendwithlinks {text tags} {
5759 global ctext linknum curview pendinglinks
5761 set start [$ctext index "end - 1c"]
5762 $ctext insert end $text $tags
5763 set links [regexp -indices -all -inline {[0-9a-f]{40}} $text]
5767 set linkid [string range $text $s $e]
5769 $ctext tag delete link$linknum
5770 $ctext tag add link$linknum "$start + $s c" "$start + $e c"
5771 setlink $linkid link$linknum
5776 proc setlink {id lk} {
5777 global curview ctext pendinglinks commitinterest
5779 if {[commitinview $id $curview]} {
5780 $ctext tag conf $lk -foreground blue -underline 1
5781 $ctext tag bind $lk <1> [list selectline [rowofcommit $id] 1]
5782 $ctext tag bind $lk <Enter> {linkcursor %W 1}
5783 $ctext tag bind $lk <Leave> {linkcursor %W -1}
5785 lappend pendinglinks($id) $lk
5786 lappend commitinterest($id) {makelink %I}
5790 proc makelink {id} {
5793 if {![info exists pendinglinks($id)]} return
5794 foreach lk $pendinglinks($id) {
5797 unset pendinglinks($id)
5800 proc linkcursor {w inc} {
5801 global linkentercount curtextcursor
5803 if {[incr linkentercount $inc] > 0} {
5804 $w configure -cursor hand2
5806 $w configure -cursor $curtextcursor
5807 if {$linkentercount < 0} {
5808 set linkentercount 0
5813 proc viewnextline {dir} {
5817 set ymax [lindex [$canv cget -scrollregion] 3]
5818 set wnow [$canv yview]
5819 set wtop [expr {[lindex $wnow 0] * $ymax}]
5820 set newtop [expr {$wtop + $dir * $linespc}]
5823 } elseif {$newtop > $ymax} {
5826 allcanvs yview moveto [expr {$newtop * 1.0 / $ymax}]
5829 # add a list of tag or branch names at position pos
5830 # returns the number of names inserted
5831 proc appendrefs {pos ids var} {
5832 global ctext linknum curview $var maxrefs
5834 if {[catch {$ctext index $pos}]} {
5837 $ctext conf -state normal
5838 $ctext delete $pos "$pos lineend"
5841 foreach tag [set $var\($id\)] {
5842 lappend tags [list $tag $id]
5845 if {[llength $tags] > $maxrefs} {
5846 $ctext insert $pos "many ([llength $tags])"
5848 set tags [lsort -index 0 -decreasing $tags]
5851 set id [lindex $ti 1]
5854 $ctext tag delete $lk
5855 $ctext insert $pos $sep
5856 $ctext insert $pos [lindex $ti 0] $lk
5861 $ctext conf -state disabled
5862 return [llength $tags]
5865 # called when we have finished computing the nearby tags
5866 proc dispneartags {delay} {
5867 global selectedline currentid showneartags tagphase
5869 if {$selectedline eq {} || !$showneartags} return
5870 after cancel dispnexttag
5872 after 200 dispnexttag
5875 after idle dispnexttag
5880 proc dispnexttag {} {
5881 global selectedline currentid showneartags tagphase ctext
5883 if {$selectedline eq {} || !$showneartags} return
5884 switch -- $tagphase {
5886 set dtags [desctags $currentid]
5888 appendrefs precedes $dtags idtags
5892 set atags [anctags $currentid]
5894 appendrefs follows $atags idtags
5898 set dheads [descheads $currentid]
5899 if {$dheads ne {}} {
5900 if {[appendrefs branch $dheads idheads] > 1
5901 && [$ctext get "branch -3c"] eq "h"} {
5902 # turn "Branch" into "Branches"
5903 $ctext conf -state normal
5904 $ctext insert "branch -2c" "es"
5905 $ctext conf -state disabled
5910 if {[incr tagphase] <= 2} {
5911 after idle dispnexttag
5915 proc make_secsel {l} {
5916 global linehtag linentag linedtag canv canv2 canv3
5918 if {![info exists linehtag($l)]} return
5920 set t [eval $canv create rect [$canv bbox $linehtag($l)] -outline {{}} \
5921 -tags secsel -fill [$canv cget -selectbackground]]
5923 $canv2 delete secsel
5924 set t [eval $canv2 create rect [$canv2 bbox $linentag($l)] -outline {{}} \
5925 -tags secsel -fill [$canv2 cget -selectbackground]]
5927 $canv3 delete secsel
5928 set t [eval $canv3 create rect [$canv3 bbox $linedtag($l)] -outline {{}} \
5929 -tags secsel -fill [$canv3 cget -selectbackground]]
5933 proc selectline {l isnew} {
5934 global canv ctext commitinfo selectedline
5935 global canvy0 linespc parents children curview
5936 global currentid sha1entry
5937 global commentend idtags linknum
5938 global mergemax numcommits pending_select
5939 global cmitmode showneartags allcommits
5940 global targetrow targetid lastscrollrows
5943 catch {unset pending_select}
5948 if {$l < 0 || $l >= $numcommits} return
5949 set id [commitonrow $l]
5954 if {$lastscrollrows < $numcommits} {
5958 set y [expr {$canvy0 + $l * $linespc}]
5959 set ymax [lindex [$canv cget -scrollregion] 3]
5960 set ytop [expr {$y - $linespc - 1}]
5961 set ybot [expr {$y + $linespc + 1}]
5962 set wnow [$canv yview]
5963 set wtop [expr {[lindex $wnow 0] * $ymax}]
5964 set wbot [expr {[lindex $wnow 1] * $ymax}]
5965 set wh [expr {$wbot - $wtop}]
5967 if {$ytop < $wtop} {
5968 if {$ybot < $wtop} {
5969 set newtop [expr {$y - $wh / 2.0}]
5972 if {$newtop > $wtop - $linespc} {
5973 set newtop [expr {$wtop - $linespc}]
5976 } elseif {$ybot > $wbot} {
5977 if {$ytop > $wbot} {
5978 set newtop [expr {$y - $wh / 2.0}]
5980 set newtop [expr {$ybot - $wh}]
5981 if {$newtop < $wtop + $linespc} {
5982 set newtop [expr {$wtop + $linespc}]
5986 if {$newtop != $wtop} {
5990 allcanvs yview moveto [expr {$newtop * 1.0 / $ymax}]
5997 addtohistory [list selbyid $id]
6000 $sha1entry delete 0 end
6001 $sha1entry insert 0 $id
6003 $sha1entry selection from 0
6004 $sha1entry selection to end
6008 $ctext conf -state normal
6011 if {![info exists commitinfo($id)]} {
6014 set info $commitinfo($id)
6015 set date [formatdate [lindex $info 2]]
6016 $ctext insert end "[mc "Author"]: [lindex $info 1] $date\n"
6017 set date [formatdate [lindex $info 4]]
6018 $ctext insert end "[mc "Committer"]: [lindex $info 3] $date\n"
6019 if {[info exists idtags($id)]} {
6020 $ctext insert end [mc "Tags:"]
6021 foreach tag $idtags($id) {
6022 $ctext insert end " $tag"
6024 $ctext insert end "\n"
6028 set olds $parents($curview,$id)
6029 if {[llength $olds] > 1} {
6032 if {$np >= $mergemax} {
6037 $ctext insert end "[mc "Parent"]: " $tag
6038 appendwithlinks [commit_descriptor $p] {}
6043 append headers "[mc "Parent"]: [commit_descriptor $p]"
6047 foreach c $children($curview,$id) {
6048 append headers "[mc "Child"]: [commit_descriptor $c]"
6051 # make anything that looks like a SHA1 ID be a clickable link
6052 appendwithlinks $headers {}
6053 if {$showneartags} {
6054 if {![info exists allcommits]} {
6057 $ctext insert end "[mc "Branch"]: "
6058 $ctext mark set branch "end -1c"
6059 $ctext mark gravity branch left
6060 $ctext insert end "\n[mc "Follows"]: "
6061 $ctext mark set follows "end -1c"
6062 $ctext mark gravity follows left
6063 $ctext insert end "\n[mc "Precedes"]: "
6064 $ctext mark set precedes "end -1c"
6065 $ctext mark gravity precedes left
6066 $ctext insert end "\n"
6069 $ctext insert end "\n"
6070 set comment [lindex $info 5]
6071 if {[string first "\r" $comment] >= 0} {
6072 set comment [string map {"\r" "\n "} $comment]
6074 appendwithlinks $comment {comment}
6076 $ctext tag remove found 1.0 end
6077 $ctext conf -state disabled
6078 set commentend [$ctext index "end - 1c"]
6080 init_flist [mc "Comments"]
6081 if {$cmitmode eq "tree"} {
6083 } elseif {[llength $olds] <= 1} {
6090 proc selfirstline {} {
6095 proc sellastline {} {
6098 set l [expr {$numcommits - 1}]
6102 proc selnextline {dir} {
6105 if {$selectedline eq {}} return
6106 set l [expr {$selectedline + $dir}]
6111 proc selnextpage {dir} {
6112 global canv linespc selectedline numcommits
6114 set lpp [expr {([winfo height $canv] - 2) / $linespc}]
6118 allcanvs yview scroll [expr {$dir * $lpp}] units
6120 if {$selectedline eq {}} return
6121 set l [expr {$selectedline + $dir * $lpp}]
6124 } elseif {$l >= $numcommits} {
6125 set l [expr $numcommits - 1]
6131 proc unselectline {} {
6132 global selectedline currentid
6135 catch {unset currentid}
6136 allcanvs delete secsel
6140 proc reselectline {} {
6143 if {$selectedline ne {}} {
6144 selectline $selectedline 0
6148 proc addtohistory {cmd} {
6149 global history historyindex curview
6151 set elt [list $curview $cmd]
6152 if {$historyindex > 0
6153 && [lindex $history [expr {$historyindex - 1}]] == $elt} {
6157 if {$historyindex < [llength $history]} {
6158 set history [lreplace $history $historyindex end $elt]
6160 lappend history $elt
6163 if {$historyindex > 1} {
6164 .tf.bar.leftbut conf -state normal
6166 .tf.bar.leftbut conf -state disabled
6168 .tf.bar.rightbut conf -state disabled
6174 set view [lindex $elt 0]
6175 set cmd [lindex $elt 1]
6176 if {$curview != $view} {
6183 global history historyindex
6186 if {$historyindex > 1} {
6187 incr historyindex -1
6188 godo [lindex $history [expr {$historyindex - 1}]]
6189 .tf.bar.rightbut conf -state normal
6191 if {$historyindex <= 1} {
6192 .tf.bar.leftbut conf -state disabled
6197 global history historyindex
6200 if {$historyindex < [llength $history]} {
6201 set cmd [lindex $history $historyindex]
6204 .tf.bar.leftbut conf -state normal
6206 if {$historyindex >= [llength $history]} {
6207 .tf.bar.rightbut conf -state disabled
6212 global treefilelist treeidlist diffids diffmergeid treepending
6213 global nullid nullid2
6216 catch {unset diffmergeid}
6217 if {![info exists treefilelist($id)]} {
6218 if {![info exists treepending]} {
6219 if {$id eq $nullid} {
6220 set cmd [list | git ls-files]
6221 } elseif {$id eq $nullid2} {
6222 set cmd [list | git ls-files --stage -t]
6224 set cmd [list | git ls-tree -r $id]
6226 if {[catch {set gtf [open $cmd r]}]} {
6230 set treefilelist($id) {}
6231 set treeidlist($id) {}
6232 fconfigure $gtf -blocking 0 -encoding binary
6233 filerun $gtf [list gettreeline $gtf $id]
6240 proc gettreeline {gtf id} {
6241 global treefilelist treeidlist treepending cmitmode diffids nullid nullid2
6244 while {[incr nl] <= 1000 && [gets $gtf line] >= 0} {
6245 if {$diffids eq $nullid} {
6248 set i [string first "\t" $line]
6249 if {$i < 0} continue
6250 set fname [string range $line [expr {$i+1}] end]
6251 set line [string range $line 0 [expr {$i-1}]]
6252 if {$diffids ne $nullid2 && [lindex $line 1] ne "blob"} continue
6253 set sha1 [lindex $line 2]
6254 lappend treeidlist($id) $sha1
6256 if {[string index $fname 0] eq "\""} {
6257 set fname [lindex $fname 0]
6259 set fname [encoding convertfrom $fname]
6260 lappend treefilelist($id) $fname
6263 return [expr {$nl >= 1000? 2: 1}]
6267 if {$cmitmode ne "tree"} {
6268 if {![info exists diffmergeid]} {
6269 gettreediffs $diffids
6271 } elseif {$id ne $diffids} {
6280 global treefilelist treeidlist diffids nullid nullid2
6281 global ctext commentend
6283 set i [lsearch -exact $treefilelist($diffids) $f]
6285 puts "oops, $f not in list for id $diffids"
6288 if {$diffids eq $nullid} {
6289 if {[catch {set bf [open $f r]} err]} {
6290 puts "oops, can't read $f: $err"
6294 set blob [lindex $treeidlist($diffids) $i]
6295 if {[catch {set bf [open [concat | git cat-file blob $blob] r]} err]} {
6296 puts "oops, error reading blob $blob: $err"
6300 fconfigure $bf -blocking 0 -encoding [get_path_encoding $f]
6301 filerun $bf [list getblobline $bf $diffids]
6302 $ctext config -state normal
6303 clear_ctext $commentend
6304 $ctext insert end "\n"
6305 $ctext insert end "$f\n" filesep
6306 $ctext config -state disabled
6307 $ctext yview $commentend
6311 proc getblobline {bf id} {
6312 global diffids cmitmode ctext
6314 if {$id ne $diffids || $cmitmode ne "tree"} {
6318 $ctext config -state normal
6320 while {[incr nl] <= 1000 && [gets $bf line] >= 0} {
6321 $ctext insert end "$line\n"
6324 # delete last newline
6325 $ctext delete "end - 2c" "end - 1c"
6329 $ctext config -state disabled
6330 return [expr {$nl >= 1000? 2: 1}]
6333 proc mergediff {id} {
6334 global diffmergeid mdifffd
6339 global limitdiffs vfilelimit curview
6343 # this doesn't seem to actually affect anything...
6344 set cmd [concat | git diff-tree --no-commit-id --cc -U$diffcontext $id]
6345 if {$limitdiffs && $vfilelimit($curview) ne {}} {
6346 set cmd [concat $cmd -- $vfilelimit($curview)]
6348 if {[catch {set mdf [open $cmd r]} err]} {
6349 error_popup "[mc "Error getting merge diffs:"] $err"
6352 fconfigure $mdf -blocking 0 -encoding binary
6353 set mdifffd($id) $mdf
6354 set np [llength $parents($curview,$id)]
6355 set diffencoding [get_path_encoding {}]
6357 filerun $mdf [list getmergediffline $mdf $id $np]
6360 proc getmergediffline {mdf id np} {
6361 global diffmergeid ctext cflist mergemax
6362 global difffilestart mdifffd
6365 $ctext conf -state normal
6367 while {[incr nr] <= 1000 && [gets $mdf line] >= 0} {
6368 if {![info exists diffmergeid] || $id != $diffmergeid
6369 || $mdf != $mdifffd($id)} {
6373 if {[regexp {^diff --cc (.*)} $line match fname]} {
6374 # start of a new file
6375 set fname [encoding convertfrom $fname]
6376 $ctext insert end "\n"
6377 set here [$ctext index "end - 1c"]
6378 lappend difffilestart $here
6379 add_flist [list $fname]
6380 set diffencoding [get_path_encoding $fname]
6381 set l [expr {(78 - [string length $fname]) / 2}]
6382 set pad [string range "----------------------------------------" 1 $l]
6383 $ctext insert end "$pad $fname $pad\n" filesep
6384 } elseif {[regexp {^@@} $line]} {
6385 set line [encoding convertfrom $diffencoding $line]
6386 $ctext insert end "$line\n" hunksep
6387 } elseif {[regexp {^[0-9a-f]{40}$} $line] || [regexp {^index} $line]} {
6390 set line [encoding convertfrom $diffencoding $line]
6391 # parse the prefix - one ' ', '-' or '+' for each parent
6396 for {set j 0} {$j < $np} {incr j} {
6397 set c [string range $line $j $j]
6400 } elseif {$c == "-"} {
6402 } elseif {$c == "+"} {
6411 if {!$isbad && $minuses ne {} && $pluses eq {}} {
6412 # line doesn't appear in result, parents in $minuses have the line
6413 set num [lindex $minuses 0]
6414 } elseif {!$isbad && $pluses ne {} && $minuses eq {}} {
6415 # line appears in result, parents in $pluses don't have the line
6416 lappend tags mresult
6417 set num [lindex $spaces 0]
6420 if {$num >= $mergemax} {
6425 $ctext insert end "$line\n" $tags
6428 $ctext conf -state disabled
6433 return [expr {$nr >= 1000? 2: 1}]
6436 proc startdiff {ids} {
6437 global treediffs diffids treepending diffmergeid nullid nullid2
6441 catch {unset diffmergeid}
6442 if {![info exists treediffs($ids)] ||
6443 [lsearch -exact $ids $nullid] >= 0 ||
6444 [lsearch -exact $ids $nullid2] >= 0} {
6445 if {![info exists treepending]} {
6453 proc path_filter {filter name} {
6455 set l [string length $p]
6456 if {[string index $p end] eq "/"} {
6457 if {[string compare -length $l $p $name] == 0} {
6461 if {[string compare -length $l $p $name] == 0 &&
6462 ([string length $name] == $l ||
6463 [string index $name $l] eq "/")} {
6471 proc addtocflist {ids} {
6474 add_flist $treediffs($ids)
6478 proc diffcmd {ids flags} {
6479 global nullid nullid2
6481 set i [lsearch -exact $ids $nullid]
6482 set j [lsearch -exact $ids $nullid2]
6484 if {[llength $ids] > 1 && $j < 0} {
6485 # comparing working directory with some specific revision
6486 set cmd [concat | git diff-index $flags]
6488 lappend cmd -R [lindex $ids 1]
6490 lappend cmd [lindex $ids 0]
6493 # comparing working directory with index
6494 set cmd [concat | git diff-files $flags]
6499 } elseif {$j >= 0} {
6500 set cmd [concat | git diff-index --cached $flags]
6501 if {[llength $ids] > 1} {
6502 # comparing index with specific revision
6504 lappend cmd -R [lindex $ids 1]
6506 lappend cmd [lindex $ids 0]
6509 # comparing index with HEAD
6513 set cmd [concat | git diff-tree -r $flags $ids]
6518 proc gettreediffs {ids} {
6519 global treediff treepending
6521 if {[catch {set gdtf [open [diffcmd $ids {--no-commit-id}] r]}]} return
6523 set treepending $ids
6525 fconfigure $gdtf -blocking 0 -encoding binary
6526 filerun $gdtf [list gettreediffline $gdtf $ids]
6529 proc gettreediffline {gdtf ids} {
6530 global treediff treediffs treepending diffids diffmergeid
6531 global cmitmode vfilelimit curview limitdiffs
6535 while {[incr nr] <= 1000 && [gets $gdtf line] >= 0} {
6536 set i [string first "\t" $line]
6538 set file [string range $line [expr {$i+1}] end]
6539 if {[string index $file 0] eq "\""} {
6540 set file [lindex $file 0]
6542 set file [encoding convertfrom $file]
6543 lappend treediff $file
6544 lappend sublist $file
6547 cache_gitattr encoding $sublist
6549 return [expr {$nr >= 1000? 2: 1}]
6552 if {$limitdiffs && $vfilelimit($curview) ne {}} {
6554 foreach f $treediff {
6555 if {[path_filter $vfilelimit($curview) $f]} {
6559 set treediffs($ids) $flist
6561 set treediffs($ids) $treediff
6564 if {$cmitmode eq "tree"} {
6566 } elseif {$ids != $diffids} {
6567 if {![info exists diffmergeid]} {
6568 gettreediffs $diffids
6576 # empty string or positive integer
6577 proc diffcontextvalidate {v} {
6578 return [regexp {^(|[1-9][0-9]*)$} $v]
6581 proc diffcontextchange {n1 n2 op} {
6582 global diffcontextstring diffcontext
6584 if {[string is integer -strict $diffcontextstring]} {
6585 if {$diffcontextstring > 0} {
6586 set diffcontext $diffcontextstring
6592 proc changeignorespace {} {
6596 proc getblobdiffs {ids} {
6597 global blobdifffd diffids env
6598 global diffinhdr treediffs
6601 global limitdiffs vfilelimit curview
6604 set cmd [diffcmd $ids "-p -C --no-commit-id -U$diffcontext"]
6608 if {$limitdiffs && $vfilelimit($curview) ne {}} {
6609 set cmd [concat $cmd -- $vfilelimit($curview)]
6611 if {[catch {set bdf [open $cmd r]} err]} {
6612 puts "error getting diffs: $err"
6616 set diffencoding [get_path_encoding {}]
6617 fconfigure $bdf -blocking 0 -encoding binary
6618 set blobdifffd($ids) $bdf
6619 filerun $bdf [list getblobdiffline $bdf $diffids]
6622 proc setinlist {var i val} {
6625 while {[llength [set $var]] < $i} {
6628 if {[llength [set $var]] == $i} {
6635 proc makediffhdr {fname ids} {
6636 global ctext curdiffstart treediffs
6638 set i [lsearch -exact $treediffs($ids) $fname]
6640 setinlist difffilestart $i $curdiffstart
6642 set l [expr {(78 - [string length $fname]) / 2}]
6643 set pad [string range "----------------------------------------" 1 $l]
6644 $ctext insert $curdiffstart "$pad $fname $pad" filesep
6647 proc getblobdiffline {bdf ids} {
6648 global diffids blobdifffd ctext curdiffstart
6649 global diffnexthead diffnextnote difffilestart
6650 global diffinhdr treediffs
6654 $ctext conf -state normal
6655 while {[incr nr] <= 1000 && [gets $bdf line] >= 0} {
6656 if {$ids != $diffids || $bdf != $blobdifffd($ids)} {
6660 if {![string compare -length 11 "diff --git " $line]} {
6661 # trim off "diff --git "
6662 set line [string range $line 11 end]
6664 # start of a new file
6665 $ctext insert end "\n"
6666 set curdiffstart [$ctext index "end - 1c"]
6667 $ctext insert end "\n" filesep
6668 # If the name hasn't changed the length will be odd,
6669 # the middle char will be a space, and the two bits either
6670 # side will be a/name and b/name, or "a/name" and "b/name".
6671 # If the name has changed we'll get "rename from" and
6672 # "rename to" or "copy from" and "copy to" lines following this,
6673 # and we'll use them to get the filenames.
6674 # This complexity is necessary because spaces in the filename(s)
6675 # don't get escaped.
6676 set l [string length $line]
6677 set i [expr {$l / 2}]
6678 if {!(($l & 1) && [string index $line $i] eq " " &&
6679 [string range $line 2 [expr {$i - 1}]] eq \
6680 [string range $line [expr {$i + 3}] end])} {
6683 # unescape if quoted and chop off the a/ from the front
6684 if {[string index $line 0] eq "\""} {
6685 set fname [string range [lindex $line 0] 2 end]
6687 set fname [string range $line 2 [expr {$i - 1}]]
6689 set fname [encoding convertfrom $fname]
6690 set diffencoding [get_path_encoding $fname]
6691 makediffhdr $fname $ids
6693 } elseif {[regexp {^@@ -([0-9]+)(,[0-9]+)? \+([0-9]+)(,[0-9]+)? @@(.*)} \
6694 $line match f1l f1c f2l f2c rest]} {
6695 set line [encoding convertfrom $diffencoding $line]
6696 $ctext insert end "$line\n" hunksep
6699 } elseif {$diffinhdr} {
6700 if {![string compare -length 12 "rename from " $line]} {
6701 set fname [string range $line [expr 6 + [string first " from " $line] ] end]
6702 if {[string index $fname 0] eq "\""} {
6703 set fname [lindex $fname 0]
6705 set fname [encoding convertfrom $fname]
6706 set i [lsearch -exact $treediffs($ids) $fname]
6708 setinlist difffilestart $i $curdiffstart
6710 } elseif {![string compare -length 10 $line "rename to "] ||
6711 ![string compare -length 8 $line "copy to "]} {
6712 set fname [string range $line [expr 4 + [string first " to " $line] ] end]
6713 if {[string index $fname 0] eq "\""} {
6714 set fname [lindex $fname 0]
6716 set fname [encoding convertfrom $fname]
6717 set diffencoding [get_path_encoding $fname]
6718 makediffhdr $fname $ids
6719 } elseif {[string compare -length 3 $line "---"] == 0} {
6722 } elseif {[string compare -length 3 $line "+++"] == 0} {
6726 $ctext insert end "$line\n" filesep
6729 set line [encoding convertfrom $diffencoding $line]
6730 set x [string range $line 0 0]
6731 if {$x == "-" || $x == "+"} {
6732 set tag [expr {$x == "+"}]
6733 $ctext insert end "$line\n" d$tag
6734 } elseif {$x == " "} {
6735 $ctext insert end "$line\n"
6737 # "\ No newline at end of file",
6738 # or something else we don't recognize
6739 $ctext insert end "$line\n" hunksep
6743 $ctext conf -state disabled
6748 return [expr {$nr >= 1000? 2: 1}]
6751 proc changediffdisp {} {
6752 global ctext diffelide
6754 $ctext tag conf d0 -elide [lindex $diffelide 0]
6755 $ctext tag conf d1 -elide [lindex $diffelide 1]
6758 proc highlightfile {loc cline} {
6759 global ctext cflist cflist_top
6762 $cflist tag remove highlight $cflist_top.0 "$cflist_top.0 lineend"
6763 $cflist tag add highlight $cline.0 "$cline.0 lineend"
6764 $cflist see $cline.0
6765 set cflist_top $cline
6769 global difffilestart ctext cmitmode
6771 if {$cmitmode eq "tree"} return
6774 set here [$ctext index @0,0]
6775 foreach loc $difffilestart {
6776 if {[$ctext compare $loc >= $here]} {
6777 highlightfile $prev $prevline
6783 highlightfile $prev $prevline
6787 global difffilestart ctext cmitmode
6789 if {$cmitmode eq "tree"} return
6790 set here [$ctext index @0,0]
6792 foreach loc $difffilestart {
6794 if {[$ctext compare $loc > $here]} {
6795 highlightfile $loc $line
6801 proc clear_ctext {{first 1.0}} {
6802 global ctext smarktop smarkbot
6805 set l [lindex [split $first .] 0]
6806 if {![info exists smarktop] || [$ctext compare $first < $smarktop.0]} {
6809 if {![info exists smarkbot] || [$ctext compare $first < $smarkbot.0]} {
6812 $ctext delete $first end
6813 if {$first eq "1.0"} {
6814 catch {unset pendinglinks}
6818 proc settabs {{firstab {}}} {
6819 global firsttabstop tabstop ctext have_tk85
6821 if {$firstab ne {} && $have_tk85} {
6822 set firsttabstop $firstab
6824 set w [font measure textfont "0"]
6825 if {$firsttabstop != 0} {
6826 $ctext conf -tabs [list [expr {($firsttabstop + $tabstop) * $w}] \
6827 [expr {($firsttabstop + 2 * $tabstop) * $w}]]
6828 } elseif {$have_tk85 || $tabstop != 8} {
6829 $ctext conf -tabs [expr {$tabstop * $w}]
6831 $ctext conf -tabs {}
6835 proc incrsearch {name ix op} {
6836 global ctext searchstring searchdirn
6838 $ctext tag remove found 1.0 end
6839 if {[catch {$ctext index anchor}]} {
6840 # no anchor set, use start of selection, or of visible area
6841 set sel [$ctext tag ranges sel]
6843 $ctext mark set anchor [lindex $sel 0]
6844 } elseif {$searchdirn eq "-forwards"} {
6845 $ctext mark set anchor @0,0
6847 $ctext mark set anchor @0,[winfo height $ctext]
6850 if {$searchstring ne {}} {
6851 set here [$ctext search $searchdirn -- $searchstring anchor]
6860 global sstring ctext searchstring searchdirn
6863 $sstring icursor end
6864 set searchdirn -forwards
6865 if {$searchstring ne {}} {
6866 set sel [$ctext tag ranges sel]
6868 set start "[lindex $sel 0] + 1c"
6869 } elseif {[catch {set start [$ctext index anchor]}]} {
6872 set match [$ctext search -count mlen -- $searchstring $start]
6873 $ctext tag remove sel 1.0 end
6879 set mend "$match + $mlen c"
6880 $ctext tag add sel $match $mend
6881 $ctext mark unset anchor
6885 proc dosearchback {} {
6886 global sstring ctext searchstring searchdirn
6889 $sstring icursor end
6890 set searchdirn -backwards
6891 if {$searchstring ne {}} {
6892 set sel [$ctext tag ranges sel]
6894 set start [lindex $sel 0]
6895 } elseif {[catch {set start [$ctext index anchor]}]} {
6896 set start @0,[winfo height $ctext]
6898 set match [$ctext search -backwards -count ml -- $searchstring $start]
6899 $ctext tag remove sel 1.0 end
6905 set mend "$match + $ml c"
6906 $ctext tag add sel $match $mend
6907 $ctext mark unset anchor
6911 proc searchmark {first last} {
6912 global ctext searchstring
6916 set match [$ctext search -count mlen -- $searchstring $mend $last.end]
6917 if {$match eq {}} break
6918 set mend "$match + $mlen c"
6919 $ctext tag add found $match $mend
6923 proc searchmarkvisible {doall} {
6924 global ctext smarktop smarkbot
6926 set topline [lindex [split [$ctext index @0,0] .] 0]
6927 set botline [lindex [split [$ctext index @0,[winfo height $ctext]] .] 0]
6928 if {$doall || $botline < $smarktop || $topline > $smarkbot} {
6929 # no overlap with previous
6930 searchmark $topline $botline
6931 set smarktop $topline
6932 set smarkbot $botline
6934 if {$topline < $smarktop} {
6935 searchmark $topline [expr {$smarktop-1}]
6936 set smarktop $topline
6938 if {$botline > $smarkbot} {
6939 searchmark [expr {$smarkbot+1}] $botline
6940 set smarkbot $botline
6945 proc scrolltext {f0 f1} {
6948 .bleft.bottom.sb set $f0 $f1
6949 if {$searchstring ne {}} {
6955 global linespc charspc canvx0 canvy0
6956 global xspc1 xspc2 lthickness
6958 set linespc [font metrics mainfont -linespace]
6959 set charspc [font measure mainfont "m"]
6960 set canvy0 [expr {int(3 + 0.5 * $linespc)}]
6961 set canvx0 [expr {int(3 + 0.5 * $linespc)}]
6962 set lthickness [expr {int($linespc / 9) + 1}]
6963 set xspc1(0) $linespc
6971 set ymax [lindex [$canv cget -scrollregion] 3]
6972 if {$ymax eq {} || $ymax == 0} return
6973 set span [$canv yview]
6976 allcanvs yview moveto [lindex $span 0]
6978 if {$selectedline ne {}} {
6979 selectline $selectedline 0
6980 allcanvs yview moveto [lindex $span 0]
6984 proc parsefont {f n} {
6987 set fontattr($f,family) [lindex $n 0]
6989 if {$s eq {} || $s == 0} {
6992 set s [expr {int(-$s / [winfo fpixels . 1p] + 0.5)}]
6994 set fontattr($f,size) $s
6995 set fontattr($f,weight) normal
6996 set fontattr($f,slant) roman
6997 foreach style [lrange $n 2 end] {
7000 "bold" {set fontattr($f,weight) $style}
7002 "italic" {set fontattr($f,slant) $style}
7007 proc fontflags {f {isbold 0}} {
7010 return [list -family $fontattr($f,family) -size $fontattr($f,size) \
7011 -weight [expr {$isbold? "bold": $fontattr($f,weight)}] \
7012 -slant $fontattr($f,slant)]
7018 set n [list $fontattr($f,family) $fontattr($f,size)]
7019 if {$fontattr($f,weight) eq "bold"} {
7022 if {$fontattr($f,slant) eq "italic"} {
7028 proc incrfont {inc} {
7029 global mainfont textfont ctext canv cflist showrefstop
7030 global stopped entries fontattr
7033 set s $fontattr(mainfont,size)
7038 set fontattr(mainfont,size) $s
7039 font config mainfont -size $s
7040 font config mainfontbold -size $s
7041 set mainfont [fontname mainfont]
7042 set s $fontattr(textfont,size)
7047 set fontattr(textfont,size) $s
7048 font config textfont -size $s
7049 font config textfontbold -size $s
7050 set textfont [fontname textfont]
7057 global sha1entry sha1string
7058 if {[string length $sha1string] == 40} {
7059 $sha1entry delete 0 end
7063 proc sha1change {n1 n2 op} {
7064 global sha1string currentid sha1but
7065 if {$sha1string == {}
7066 || ([info exists currentid] && $sha1string == $currentid)} {
7071 if {[$sha1but cget -state] == $state} return
7072 if {$state == "normal"} {
7073 $sha1but conf -state normal -relief raised -text "[mc "Goto:"] "
7075 $sha1but conf -state disabled -relief flat -text "[mc "SHA1 ID:"] "
7079 proc gotocommit {} {
7080 global sha1string tagids headids curview varcid
7082 if {$sha1string == {}
7083 || ([info exists currentid] && $sha1string == $currentid)} return
7084 if {[info exists tagids($sha1string)]} {
7085 set id $tagids($sha1string)
7086 } elseif {[info exists headids($sha1string)]} {
7087 set id $headids($sha1string)
7089 set id [string tolower $sha1string]
7090 if {[regexp {^[0-9a-f]{4,39}$} $id]} {
7091 set matches [array names varcid "$curview,$id*"]
7092 if {$matches ne {}} {
7093 if {[llength $matches] > 1} {
7094 error_popup [mc "Short SHA1 id %s is ambiguous" $id]
7097 set id [lindex [split [lindex $matches 0] ","] 1]
7101 if {[commitinview $id $curview]} {
7102 selectline [rowofcommit $id] 1
7105 if {[regexp {^[0-9a-fA-F]{4,}$} $sha1string]} {
7106 set msg [mc "SHA1 id %s is not known" $sha1string]
7108 set msg [mc "Tag/Head %s is not known" $sha1string]
7113 proc lineenter {x y id} {
7114 global hoverx hovery hoverid hovertimer
7115 global commitinfo canv
7117 if {![info exists commitinfo($id)] && ![getcommit $id]} return
7121 if {[info exists hovertimer]} {
7122 after cancel $hovertimer
7124 set hovertimer [after 500 linehover]
7128 proc linemotion {x y id} {
7129 global hoverx hovery hoverid hovertimer
7131 if {[info exists hoverid] && $id == $hoverid} {
7134 if {[info exists hovertimer]} {
7135 after cancel $hovertimer
7137 set hovertimer [after 500 linehover]
7141 proc lineleave {id} {
7142 global hoverid hovertimer canv
7144 if {[info exists hoverid] && $id == $hoverid} {
7146 if {[info exists hovertimer]} {
7147 after cancel $hovertimer
7155 global hoverx hovery hoverid hovertimer
7156 global canv linespc lthickness
7159 set text [lindex $commitinfo($hoverid) 0]
7160 set ymax [lindex [$canv cget -scrollregion] 3]
7161 if {$ymax == {}} return
7162 set yfrac [lindex [$canv yview] 0]
7163 set x [expr {$hoverx + 2 * $linespc}]
7164 set y [expr {$hovery + $yfrac * $ymax - $linespc / 2}]
7165 set x0 [expr {$x - 2 * $lthickness}]
7166 set y0 [expr {$y - 2 * $lthickness}]
7167 set x1 [expr {$x + [font measure mainfont $text] + 2 * $lthickness}]
7168 set y1 [expr {$y + $linespc + 2 * $lthickness}]
7169 set t [$canv create rectangle $x0 $y0 $x1 $y1 \
7170 -fill \#ffff80 -outline black -width 1 -tags hover]
7172 set t [$canv create text $x $y -anchor nw -text $text -tags hover \
7177 proc clickisonarrow {id y} {
7180 set ranges [rowranges $id]
7181 set thresh [expr {2 * $lthickness + 6}]
7182 set n [expr {[llength $ranges] - 1}]
7183 for {set i 1} {$i < $n} {incr i} {
7184 set row [lindex $ranges $i]
7185 if {abs([yc $row] - $y) < $thresh} {
7192 proc arrowjump {id n y} {
7195 # 1 <-> 2, 3 <-> 4, etc...
7196 set n [expr {(($n - 1) ^ 1) + 1}]
7197 set row [lindex [rowranges $id] $n]
7199 set ymax [lindex [$canv cget -scrollregion] 3]
7200 if {$ymax eq {} || $ymax <= 0} return
7201 set view [$canv yview]
7202 set yspan [expr {[lindex $view 1] - [lindex $view 0]}]
7203 set yfrac [expr {$yt / $ymax - $yspan / 2}]
7207 allcanvs yview moveto $yfrac
7210 proc lineclick {x y id isnew} {
7211 global ctext commitinfo children canv thickerline curview
7213 if {![info exists commitinfo($id)] && ![getcommit $id]} return
7218 # draw this line thicker than normal
7222 set ymax [lindex [$canv cget -scrollregion] 3]
7223 if {$ymax eq {}} return
7224 set yfrac [lindex [$canv yview] 0]
7225 set y [expr {$y + $yfrac * $ymax}]
7227 set dirn [clickisonarrow $id $y]
7229 arrowjump $id $dirn $y
7234 addtohistory [list lineclick $x $y $id 0]
7236 # fill the details pane with info about this line
7237 $ctext conf -state normal
7240 $ctext insert end "[mc "Parent"]:\t"
7241 $ctext insert end $id link0
7243 set info $commitinfo($id)
7244 $ctext insert end "\n\t[lindex $info 0]\n"
7245 $ctext insert end "\t[mc "Author"]:\t[lindex $info 1]\n"
7246 set date [formatdate [lindex $info 2]]
7247 $ctext insert end "\t[mc "Date"]:\t$date\n"
7248 set kids $children($curview,$id)
7250 $ctext insert end "\n[mc "Children"]:"
7252 foreach child $kids {
7254 if {![info exists commitinfo($child)] && ![getcommit $child]} continue
7255 set info $commitinfo($child)
7256 $ctext insert end "\n\t"
7257 $ctext insert end $child link$i
7258 setlink $child link$i
7259 $ctext insert end "\n\t[lindex $info 0]"
7260 $ctext insert end "\n\t[mc "Author"]:\t[lindex $info 1]"
7261 set date [formatdate [lindex $info 2]]
7262 $ctext insert end "\n\t[mc "Date"]:\t$date\n"
7265 $ctext conf -state disabled
7269 proc normalline {} {
7271 if {[info exists thickerline]} {
7280 if {[commitinview $id $curview]} {
7281 selectline [rowofcommit $id] 1
7287 if {![info exists startmstime]} {
7288 set startmstime [clock clicks -milliseconds]
7290 return [format "%.3f" [expr {([clock click -milliseconds] - $startmstime) / 1000.0}]]
7293 proc rowmenu {x y id} {
7294 global rowctxmenu selectedline rowmenuid curview
7295 global nullid nullid2 fakerowmenu mainhead
7299 if {$selectedline eq {} || [rowofcommit $id] eq $selectedline} {
7304 if {$id ne $nullid && $id ne $nullid2} {
7305 set menu $rowctxmenu
7306 if {$mainhead ne {}} {
7307 $menu entryconfigure 7 -label [mc "Reset %s branch to here" $mainhead]
7309 $menu entryconfigure 7 -label [mc "Detached head: can't reset" $mainhead] -state disabled
7312 set menu $fakerowmenu
7314 $menu entryconfigure [mc "Diff this -> selected"] -state $state
7315 $menu entryconfigure [mc "Diff selected -> this"] -state $state
7316 $menu entryconfigure [mc "Make patch"] -state $state
7317 tk_popup $menu $x $y
7320 proc diffvssel {dirn} {
7321 global rowmenuid selectedline
7323 if {$selectedline eq {}} return
7325 set oldid [commitonrow $selectedline]
7326 set newid $rowmenuid
7328 set oldid $rowmenuid
7329 set newid [commitonrow $selectedline]
7331 addtohistory [list doseldiff $oldid $newid]
7332 doseldiff $oldid $newid
7335 proc doseldiff {oldid newid} {
7339 $ctext conf -state normal
7341 init_flist [mc "Top"]
7342 $ctext insert end "[mc "From"] "
7343 $ctext insert end $oldid link0
7344 setlink $oldid link0
7345 $ctext insert end "\n "
7346 $ctext insert end [lindex $commitinfo($oldid) 0]
7347 $ctext insert end "\n\n[mc "To"] "
7348 $ctext insert end $newid link1
7349 setlink $newid link1
7350 $ctext insert end "\n "
7351 $ctext insert end [lindex $commitinfo($newid) 0]
7352 $ctext insert end "\n"
7353 $ctext conf -state disabled
7354 $ctext tag remove found 1.0 end
7355 startdiff [list $oldid $newid]
7359 global rowmenuid currentid commitinfo patchtop patchnum
7361 if {![info exists currentid]} return
7362 set oldid $currentid
7363 set oldhead [lindex $commitinfo($oldid) 0]
7364 set newid $rowmenuid
7365 set newhead [lindex $commitinfo($newid) 0]
7368 catch {destroy $top}
7370 label $top.title -text [mc "Generate patch"]
7371 grid $top.title - -pady 10
7372 label $top.from -text [mc "From:"]
7373 entry $top.fromsha1 -width 40 -relief flat
7374 $top.fromsha1 insert 0 $oldid
7375 $top.fromsha1 conf -state readonly
7376 grid $top.from $top.fromsha1 -sticky w
7377 entry $top.fromhead -width 60 -relief flat
7378 $top.fromhead insert 0 $oldhead
7379 $top.fromhead conf -state readonly
7380 grid x $top.fromhead -sticky w
7381 label $top.to -text [mc "To:"]
7382 entry $top.tosha1 -width 40 -relief flat
7383 $top.tosha1 insert 0 $newid
7384 $top.tosha1 conf -state readonly
7385 grid $top.to $top.tosha1 -sticky w
7386 entry $top.tohead -width 60 -relief flat
7387 $top.tohead insert 0 $newhead
7388 $top.tohead conf -state readonly
7389 grid x $top.tohead -sticky w
7390 button $top.rev -text [mc "Reverse"] -command mkpatchrev -padx 5
7391 grid $top.rev x -pady 10
7392 label $top.flab -text [mc "Output file:"]
7393 entry $top.fname -width 60
7394 $top.fname insert 0 [file normalize "patch$patchnum.patch"]
7396 grid $top.flab $top.fname -sticky w
7398 button $top.buts.gen -text [mc "Generate"] -command mkpatchgo
7399 button $top.buts.can -text [mc "Cancel"] -command mkpatchcan
7400 grid $top.buts.gen $top.buts.can
7401 grid columnconfigure $top.buts 0 -weight 1 -uniform a
7402 grid columnconfigure $top.buts 1 -weight 1 -uniform a
7403 grid $top.buts - -pady 10 -sticky ew
7407 proc mkpatchrev {} {
7410 set oldid [$patchtop.fromsha1 get]
7411 set oldhead [$patchtop.fromhead get]
7412 set newid [$patchtop.tosha1 get]
7413 set newhead [$patchtop.tohead get]
7414 foreach e [list fromsha1 fromhead tosha1 tohead] \
7415 v [list $newid $newhead $oldid $oldhead] {
7416 $patchtop.$e conf -state normal
7417 $patchtop.$e delete 0 end
7418 $patchtop.$e insert 0 $v
7419 $patchtop.$e conf -state readonly
7424 global patchtop nullid nullid2
7426 set oldid [$patchtop.fromsha1 get]
7427 set newid [$patchtop.tosha1 get]
7428 set fname [$patchtop.fname get]
7429 set cmd [diffcmd [list $oldid $newid] -p]
7430 # trim off the initial "|"
7431 set cmd [lrange $cmd 1 end]
7432 lappend cmd >$fname &
7433 if {[catch {eval exec $cmd} err]} {
7434 error_popup "[mc "Error creating patch:"] $err"
7436 catch {destroy $patchtop}
7440 proc mkpatchcan {} {
7443 catch {destroy $patchtop}
7448 global rowmenuid mktagtop commitinfo
7452 catch {destroy $top}
7454 label $top.title -text [mc "Create tag"]
7455 grid $top.title - -pady 10
7456 label $top.id -text [mc "ID:"]
7457 entry $top.sha1 -width 40 -relief flat
7458 $top.sha1 insert 0 $rowmenuid
7459 $top.sha1 conf -state readonly
7460 grid $top.id $top.sha1 -sticky w
7461 entry $top.head -width 60 -relief flat
7462 $top.head insert 0 [lindex $commitinfo($rowmenuid) 0]
7463 $top.head conf -state readonly
7464 grid x $top.head -sticky w
7465 label $top.tlab -text [mc "Tag name:"]
7466 entry $top.tag -width 60
7467 grid $top.tlab $top.tag -sticky w
7469 button $top.buts.gen -text [mc "Create"] -command mktaggo
7470 button $top.buts.can -text [mc "Cancel"] -command mktagcan
7471 grid $top.buts.gen $top.buts.can
7472 grid columnconfigure $top.buts 0 -weight 1 -uniform a
7473 grid columnconfigure $top.buts 1 -weight 1 -uniform a
7474 grid $top.buts - -pady 10 -sticky ew
7479 global mktagtop env tagids idtags
7481 set id [$mktagtop.sha1 get]
7482 set tag [$mktagtop.tag get]
7484 error_popup [mc "No tag name specified"]
7487 if {[info exists tagids($tag)]} {
7488 error_popup [mc "Tag \"%s\" already exists" $tag]
7492 exec git tag $tag $id
7494 error_popup "[mc "Error creating tag:"] $err"
7498 set tagids($tag) $id
7499 lappend idtags($id) $tag
7506 proc redrawtags {id} {
7507 global canv linehtag idpos currentid curview cmitlisted
7508 global canvxmax iddrawn circleitem mainheadid circlecolors
7510 if {![commitinview $id $curview]} return
7511 if {![info exists iddrawn($id)]} return
7512 set row [rowofcommit $id]
7513 if {$id eq $mainheadid} {
7516 set ofill [lindex $circlecolors $cmitlisted($curview,$id)]
7518 $canv itemconf $circleitem($row) -fill $ofill
7519 $canv delete tag.$id
7520 set xt [eval drawtags $id $idpos($id)]
7521 $canv coords $linehtag($row) $xt [lindex $idpos($id) 2]
7522 set text [$canv itemcget $linehtag($row) -text]
7523 set font [$canv itemcget $linehtag($row) -font]
7524 set xr [expr {$xt + [font measure $font $text]}]
7525 if {$xr > $canvxmax} {
7529 if {[info exists currentid] && $currentid == $id} {
7537 catch {destroy $mktagtop}
7546 proc writecommit {} {
7547 global rowmenuid wrcomtop commitinfo wrcomcmd
7549 set top .writecommit
7551 catch {destroy $top}
7553 label $top.title -text [mc "Write commit to file"]
7554 grid $top.title - -pady 10
7555 label $top.id -text [mc "ID:"]
7556 entry $top.sha1 -width 40 -relief flat
7557 $top.sha1 insert 0 $rowmenuid
7558 $top.sha1 conf -state readonly
7559 grid $top.id $top.sha1 -sticky w
7560 entry $top.head -width 60 -relief flat
7561 $top.head insert 0 [lindex $commitinfo($rowmenuid) 0]
7562 $top.head conf -state readonly
7563 grid x $top.head -sticky w
7564 label $top.clab -text [mc "Command:"]
7565 entry $top.cmd -width 60 -textvariable wrcomcmd
7566 grid $top.clab $top.cmd -sticky w -pady 10
7567 label $top.flab -text [mc "Output file:"]
7568 entry $top.fname -width 60
7569 $top.fname insert 0 [file normalize "commit-[string range $rowmenuid 0 6]"]
7570 grid $top.flab $top.fname -sticky w
7572 button $top.buts.gen -text [mc "Write"] -command wrcomgo
7573 button $top.buts.can -text [mc "Cancel"] -command wrcomcan
7574 grid $top.buts.gen $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
7584 set id [$wrcomtop.sha1 get]
7585 set cmd "echo $id | [$wrcomtop.cmd get]"
7586 set fname [$wrcomtop.fname get]
7587 if {[catch {exec sh -c $cmd >$fname &} err]} {
7588 error_popup "[mc "Error writing commit:"] $err"
7590 catch {destroy $wrcomtop}
7597 catch {destroy $wrcomtop}
7602 global rowmenuid mkbrtop
7605 catch {destroy $top}
7607 label $top.title -text [mc "Create new branch"]
7608 grid $top.title - -pady 10
7609 label $top.id -text [mc "ID:"]
7610 entry $top.sha1 -width 40 -relief flat
7611 $top.sha1 insert 0 $rowmenuid
7612 $top.sha1 conf -state readonly
7613 grid $top.id $top.sha1 -sticky w
7614 label $top.nlab -text [mc "Name:"]
7615 entry $top.name -width 40
7616 grid $top.nlab $top.name -sticky w
7618 button $top.buts.go -text [mc "Create"] -command [list mkbrgo $top]
7619 button $top.buts.can -text [mc "Cancel"] -command "catch {destroy $top}"
7620 grid $top.buts.go $top.buts.can
7621 grid columnconfigure $top.buts 0 -weight 1 -uniform a
7622 grid columnconfigure $top.buts 1 -weight 1 -uniform a
7623 grid $top.buts - -pady 10 -sticky ew
7628 global headids idheads
7630 set name [$top.name get]
7631 set id [$top.sha1 get]
7633 error_popup [mc "Please specify a name for the new branch"]
7636 catch {destroy $top}
7640 exec git branch $name $id
7645 set headids($name) $id
7646 lappend idheads($id) $name
7655 proc cherrypick {} {
7656 global rowmenuid curview
7657 global mainhead mainheadid
7659 set oldhead [exec git rev-parse HEAD]
7660 set dheads [descheads $rowmenuid]
7661 if {$dheads ne {} && [lsearch -exact $dheads $oldhead] >= 0} {
7662 set ok [confirm_popup [mc "Commit %s is already\
7663 included in branch %s -- really re-apply it?" \
7664 [string range $rowmenuid 0 7] $mainhead]]
7667 nowbusy cherrypick [mc "Cherry-picking"]
7669 # Unfortunately git-cherry-pick writes stuff to stderr even when
7670 # no error occurs, and exec takes that as an indication of error...
7671 if {[catch {exec sh -c "git cherry-pick -r $rowmenuid 2>&1"} err]} {
7676 set newhead [exec git rev-parse HEAD]
7677 if {$newhead eq $oldhead} {
7679 error_popup [mc "No changes committed"]
7682 addnewchild $newhead $oldhead
7683 if {[commitinview $oldhead $curview]} {
7684 insertrow $newhead $oldhead $curview
7685 if {$mainhead ne {}} {
7686 movehead $newhead $mainhead
7687 movedhead $newhead $mainhead
7689 set mainheadid $newhead
7698 global mainhead rowmenuid confirm_ok resettype
7701 set w ".confirmreset"
7704 wm title $w [mc "Confirm reset"]
7705 message $w.m -text \
7706 [mc "Reset branch %s to %s?" $mainhead [string range $rowmenuid 0 7]] \
7707 -justify center -aspect 1000
7708 pack $w.m -side top -fill x -padx 20 -pady 20
7709 frame $w.f -relief sunken -border 2
7710 message $w.f.rt -text [mc "Reset type:"] -aspect 1000
7711 grid $w.f.rt -sticky w
7713 radiobutton $w.f.soft -value soft -variable resettype -justify left \
7714 -text [mc "Soft: Leave working tree and index untouched"]
7715 grid $w.f.soft -sticky w
7716 radiobutton $w.f.mixed -value mixed -variable resettype -justify left \
7717 -text [mc "Mixed: Leave working tree untouched, reset index"]
7718 grid $w.f.mixed -sticky w
7719 radiobutton $w.f.hard -value hard -variable resettype -justify left \
7720 -text [mc "Hard: Reset working tree and index\n(discard ALL local changes)"]
7721 grid $w.f.hard -sticky w
7722 pack $w.f -side top -fill x
7723 button $w.ok -text [mc OK] -command "set confirm_ok 1; destroy $w"
7724 pack $w.ok -side left -fill x -padx 20 -pady 20
7725 button $w.cancel -text [mc Cancel] -command "destroy $w"
7726 pack $w.cancel -side right -fill x -padx 20 -pady 20
7727 bind $w <Visibility> "grab $w; focus $w"
7729 if {!$confirm_ok} return
7730 if {[catch {set fd [open \
7731 [list | git reset --$resettype $rowmenuid 2>@1] r]} err]} {
7735 filerun $fd [list readresetstat $fd]
7736 nowbusy reset [mc "Resetting"]
7741 proc readresetstat {fd} {
7742 global mainhead mainheadid showlocalchanges rprogcoord
7744 if {[gets $fd line] >= 0} {
7745 if {[regexp {([0-9]+)% \(([0-9]+)/([0-9]+)\)} $line match p m n]} {
7746 set rprogcoord [expr {1.0 * $m / $n}]
7754 if {[catch {close $fd} err]} {
7757 set oldhead $mainheadid
7758 set newhead [exec git rev-parse HEAD]
7759 if {$newhead ne $oldhead} {
7760 movehead $newhead $mainhead
7761 movedhead $newhead $mainhead
7762 set mainheadid $newhead
7766 if {$showlocalchanges} {
7772 # context menu for a head
7773 proc headmenu {x y id head} {
7774 global headmenuid headmenuhead headctxmenu mainhead
7778 set headmenuhead $head
7780 if {$head eq $mainhead} {
7783 $headctxmenu entryconfigure 0 -state $state
7784 $headctxmenu entryconfigure 1 -state $state
7785 tk_popup $headctxmenu $x $y
7789 global headmenuid headmenuhead headids
7790 global showlocalchanges mainheadid
7792 # check the tree is clean first??
7793 nowbusy checkout [mc "Checking out"]
7797 set fd [open [list | git checkout $headmenuhead 2>@1] r]
7801 if {$showlocalchanges} {
7805 filerun $fd [list readcheckoutstat $fd $headmenuhead $headmenuid]
7809 proc readcheckoutstat {fd newhead newheadid} {
7810 global mainhead mainheadid headids showlocalchanges progresscoords
7812 if {[gets $fd line] >= 0} {
7813 if {[regexp {([0-9]+)% \(([0-9]+)/([0-9]+)\)} $line match p m n]} {
7814 set progresscoords [list 0 [expr {1.0 * $m / $n}]]
7819 set progresscoords {0 0}
7822 if {[catch {close $fd} err]} {
7825 set oldmainid $mainheadid
7826 set mainhead $newhead
7827 set mainheadid $newheadid
7828 redrawtags $oldmainid
7829 redrawtags $newheadid
7831 if {$showlocalchanges} {
7837 global headmenuid headmenuhead mainhead
7840 set head $headmenuhead
7842 # this check shouldn't be needed any more...
7843 if {$head eq $mainhead} {
7844 error_popup [mc "Cannot delete the currently checked-out branch"]
7847 set dheads [descheads $id]
7848 if {[llength $dheads] == 1 && $idheads($dheads) eq $head} {
7849 # the stuff on this branch isn't on any other branch
7850 if {![confirm_popup [mc "The commits on branch %s aren't on any other\
7851 branch.\nReally delete branch %s?" $head $head]]} return
7855 if {[catch {exec git branch -D $head} err]} {
7860 removehead $id $head
7861 removedhead $id $head
7868 # Display a list of tags and heads
7870 global showrefstop bgcolor fgcolor selectbgcolor
7871 global bglist fglist reflistfilter reflist maincursor
7874 set showrefstop $top
7875 if {[winfo exists $top]} {
7881 wm title $top [mc "Tags and heads: %s" [file tail [pwd]]]
7882 text $top.list -background $bgcolor -foreground $fgcolor \
7883 -selectbackground $selectbgcolor -font mainfont \
7884 -xscrollcommand "$top.xsb set" -yscrollcommand "$top.ysb set" \
7885 -width 30 -height 20 -cursor $maincursor \
7886 -spacing1 1 -spacing3 1 -state disabled
7887 $top.list tag configure highlight -background $selectbgcolor
7888 lappend bglist $top.list
7889 lappend fglist $top.list
7890 scrollbar $top.ysb -command "$top.list yview" -orient vertical
7891 scrollbar $top.xsb -command "$top.list xview" -orient horizontal
7892 grid $top.list $top.ysb -sticky nsew
7893 grid $top.xsb x -sticky ew
7895 label $top.f.l -text "[mc "Filter"]: "
7896 entry $top.f.e -width 20 -textvariable reflistfilter
7897 set reflistfilter "*"
7898 trace add variable reflistfilter write reflistfilter_change
7899 pack $top.f.e -side right -fill x -expand 1
7900 pack $top.f.l -side left
7901 grid $top.f - -sticky ew -pady 2
7902 button $top.close -command [list destroy $top] -text [mc "Close"]
7904 grid columnconfigure $top 0 -weight 1
7905 grid rowconfigure $top 0 -weight 1
7906 bind $top.list <1> {break}
7907 bind $top.list <B1-Motion> {break}
7908 bind $top.list <ButtonRelease-1> {sel_reflist %W %x %y; break}
7913 proc sel_reflist {w x y} {
7914 global showrefstop reflist headids tagids otherrefids
7916 if {![winfo exists $showrefstop]} return
7917 set l [lindex [split [$w index "@$x,$y"] "."] 0]
7918 set ref [lindex $reflist [expr {$l-1}]]
7919 set n [lindex $ref 0]
7920 switch -- [lindex $ref 1] {
7921 "H" {selbyid $headids($n)}
7922 "T" {selbyid $tagids($n)}
7923 "o" {selbyid $otherrefids($n)}
7925 $showrefstop.list tag add highlight $l.0 "$l.0 lineend"
7928 proc unsel_reflist {} {
7931 if {![info exists showrefstop] || ![winfo exists $showrefstop]} return
7932 $showrefstop.list tag remove highlight 0.0 end
7935 proc reflistfilter_change {n1 n2 op} {
7936 global reflistfilter
7938 after cancel refill_reflist
7939 after 200 refill_reflist
7942 proc refill_reflist {} {
7943 global reflist reflistfilter showrefstop headids tagids otherrefids
7944 global curview commitinterest
7946 if {![info exists showrefstop] || ![winfo exists $showrefstop]} return
7948 foreach n [array names headids] {
7949 if {[string match $reflistfilter $n]} {
7950 if {[commitinview $headids($n) $curview]} {
7951 lappend refs [list $n H]
7953 set commitinterest($headids($n)) {run refill_reflist}
7957 foreach n [array names tagids] {
7958 if {[string match $reflistfilter $n]} {
7959 if {[commitinview $tagids($n) $curview]} {
7960 lappend refs [list $n T]
7962 set commitinterest($tagids($n)) {run refill_reflist}
7966 foreach n [array names otherrefids] {
7967 if {[string match $reflistfilter $n]} {
7968 if {[commitinview $otherrefids($n) $curview]} {
7969 lappend refs [list $n o]
7971 set commitinterest($otherrefids($n)) {run refill_reflist}
7975 set refs [lsort -index 0 $refs]
7976 if {$refs eq $reflist} return
7978 # Update the contents of $showrefstop.list according to the
7979 # differences between $reflist (old) and $refs (new)
7980 $showrefstop.list conf -state normal
7981 $showrefstop.list insert end "\n"
7984 while {$i < [llength $reflist] || $j < [llength $refs]} {
7985 if {$i < [llength $reflist]} {
7986 if {$j < [llength $refs]} {
7987 set cmp [string compare [lindex $reflist $i 0] \
7988 [lindex $refs $j 0]]
7990 set cmp [string compare [lindex $reflist $i 1] \
7991 [lindex $refs $j 1]]
8001 $showrefstop.list delete "[expr {$j+1}].0" "[expr {$j+2}].0"
8009 set l [expr {$j + 1}]
8010 $showrefstop.list image create $l.0 -align baseline \
8011 -image reficon-[lindex $refs $j 1] -padx 2
8012 $showrefstop.list insert $l.1 "[lindex $refs $j 0]\n"
8018 # delete last newline
8019 $showrefstop.list delete end-2c end-1c
8020 $showrefstop.list conf -state disabled
8023 # Stuff for finding nearby tags
8024 proc getallcommits {} {
8025 global allcommits nextarc seeds allccache allcwait cachedarcs allcupdate
8026 global idheads idtags idotherrefs allparents tagobjid
8028 if {![info exists allcommits]} {
8034 set allccache [file join [gitdir] "gitk.cache"]
8036 set f [open $allccache r]
8045 set cmd [list | git rev-list --parents]
8046 set allcupdate [expr {$seeds ne {}}]
8050 set refs [concat [array names idheads] [array names idtags] \
8051 [array names idotherrefs]]
8054 foreach name [array names tagobjid] {
8055 lappend tagobjs $tagobjid($name)
8057 foreach id [lsort -unique $refs] {
8058 if {![info exists allparents($id)] &&
8059 [lsearch -exact $tagobjs $id] < 0} {
8070 set fd [open [concat $cmd $ids] r]
8071 fconfigure $fd -blocking 0
8074 filerun $fd [list getallclines $fd]
8080 # Since most commits have 1 parent and 1 child, we group strings of
8081 # such commits into "arcs" joining branch/merge points (BMPs), which
8082 # are commits that either don't have 1 parent or don't have 1 child.
8084 # arcnos(id) - incoming arcs for BMP, arc we're on for other nodes
8085 # arcout(id) - outgoing arcs for BMP
8086 # arcids(a) - list of IDs on arc including end but not start
8087 # arcstart(a) - BMP ID at start of arc
8088 # arcend(a) - BMP ID at end of arc
8089 # growing(a) - arc a is still growing
8090 # arctags(a) - IDs out of arcids (excluding end) that have tags
8091 # archeads(a) - IDs out of arcids (excluding end) that have heads
8092 # The start of an arc is at the descendent end, so "incoming" means
8093 # coming from descendents, and "outgoing" means going towards ancestors.
8095 proc getallclines {fd} {
8096 global allparents allchildren idtags idheads nextarc
8097 global arcnos arcids arctags arcout arcend arcstart archeads growing
8098 global seeds allcommits cachedarcs allcupdate
8101 while {[incr nid] <= 1000 && [gets $fd line] >= 0} {
8102 set id [lindex $line 0]
8103 if {[info exists allparents($id)]} {
8108 set olds [lrange $line 1 end]
8109 set allparents($id) $olds
8110 if {![info exists allchildren($id)]} {
8111 set allchildren($id) {}
8116 if {[llength $olds] == 1 && [llength $a] == 1} {
8117 lappend arcids($a) $id
8118 if {[info exists idtags($id)]} {
8119 lappend arctags($a) $id
8121 if {[info exists idheads($id)]} {
8122 lappend archeads($a) $id
8124 if {[info exists allparents($olds)]} {
8125 # seen parent already
8126 if {![info exists arcout($olds)]} {
8129 lappend arcids($a) $olds
8130 set arcend($a) $olds
8133 lappend allchildren($olds) $id
8134 lappend arcnos($olds) $a
8138 foreach a $arcnos($id) {
8139 lappend arcids($a) $id
8146 lappend allchildren($p) $id
8147 set a [incr nextarc]
8148 set arcstart($a) $id
8155 if {[info exists allparents($p)]} {
8156 # seen it already, may need to make a new branch
8157 if {![info exists arcout($p)]} {
8160 lappend arcids($a) $p
8164 lappend arcnos($p) $a
8169 global cached_dheads cached_dtags cached_atags
8170 catch {unset cached_dheads}
8171 catch {unset cached_dtags}
8172 catch {unset cached_atags}
8175 return [expr {$nid >= 1000? 2: 1}]
8179 fconfigure $fd -blocking 1
8182 # got an error reading the list of commits
8183 # if we were updating, try rereading the whole thing again
8189 error_popup "[mc "Error reading commit topology information;\
8190 branch and preceding/following tag information\
8191 will be incomplete."]\n($err)"
8194 if {[incr allcommits -1] == 0} {
8204 proc recalcarc {a} {
8205 global arctags archeads arcids idtags idheads
8209 foreach id [lrange $arcids($a) 0 end-1] {
8210 if {[info exists idtags($id)]} {
8213 if {[info exists idheads($id)]} {
8218 set archeads($a) $ah
8222 global arcnos arcids nextarc arctags archeads idtags idheads
8223 global arcstart arcend arcout allparents growing
8226 if {[llength $a] != 1} {
8227 puts "oops splitarc called but [llength $a] arcs already"
8231 set i [lsearch -exact $arcids($a) $p]
8233 puts "oops splitarc $p not in arc $a"
8236 set na [incr nextarc]
8237 if {[info exists arcend($a)]} {
8238 set arcend($na) $arcend($a)
8240 set l [lindex $allparents([lindex $arcids($a) end]) 0]
8241 set j [lsearch -exact $arcnos($l) $a]
8242 set arcnos($l) [lreplace $arcnos($l) $j $j $na]
8244 set tail [lrange $arcids($a) [expr {$i+1}] end]
8245 set arcids($a) [lrange $arcids($a) 0 $i]
8247 set arcstart($na) $p
8249 set arcids($na) $tail
8250 if {[info exists growing($a)]} {
8256 if {[llength $arcnos($id)] == 1} {
8259 set j [lsearch -exact $arcnos($id) $a]
8260 set arcnos($id) [lreplace $arcnos($id) $j $j $na]
8264 # reconstruct tags and heads lists
8265 if {$arctags($a) ne {} || $archeads($a) ne {}} {
8270 set archeads($na) {}
8274 # Update things for a new commit added that is a child of one
8275 # existing commit. Used when cherry-picking.
8276 proc addnewchild {id p} {
8277 global allparents allchildren idtags nextarc
8278 global arcnos arcids arctags arcout arcend arcstart archeads growing
8279 global seeds allcommits
8281 if {![info exists allcommits] || ![info exists arcnos($p)]} return
8282 set allparents($id) [list $p]
8283 set allchildren($id) {}
8286 lappend allchildren($p) $id
8287 set a [incr nextarc]
8288 set arcstart($a) $id
8291 set arcids($a) [list $p]
8293 if {![info exists arcout($p)]} {
8296 lappend arcnos($p) $a
8297 set arcout($id) [list $a]
8300 # This implements a cache for the topology information.
8301 # The cache saves, for each arc, the start and end of the arc,
8302 # the ids on the arc, and the outgoing arcs from the end.
8303 proc readcache {f} {
8304 global arcnos arcids arcout arcstart arcend arctags archeads nextarc
8305 global idtags idheads allparents cachedarcs possible_seeds seeds growing
8310 if {$lim - $a > 500} {
8311 set lim [expr {$a + 500}]
8315 # finish reading the cache and setting up arctags, etc.
8317 if {$line ne "1"} {error "bad final version"}
8319 foreach id [array names idtags] {
8320 if {[info exists arcnos($id)] && [llength $arcnos($id)] == 1 &&
8321 [llength $allparents($id)] == 1} {
8322 set a [lindex $arcnos($id) 0]
8323 if {$arctags($a) eq {}} {
8328 foreach id [array names idheads] {
8329 if {[info exists arcnos($id)] && [llength $arcnos($id)] == 1 &&
8330 [llength $allparents($id)] == 1} {
8331 set a [lindex $arcnos($id) 0]
8332 if {$archeads($a) eq {}} {
8337 foreach id [lsort -unique $possible_seeds] {
8338 if {$arcnos($id) eq {}} {
8344 while {[incr a] <= $lim} {
8346 if {[llength $line] != 3} {error "bad line"}
8347 set s [lindex $line 0]
8349 lappend arcout($s) $a
8350 if {![info exists arcnos($s)]} {
8351 lappend possible_seeds $s
8354 set e [lindex $line 1]
8359 if {![info exists arcout($e)]} {
8363 set arcids($a) [lindex $line 2]
8364 foreach id $arcids($a) {
8365 lappend allparents($s) $id
8367 lappend arcnos($id) $a
8369 if {![info exists allparents($s)]} {
8370 set allparents($s) {}
8375 set nextarc [expr {$a - 1}]
8388 global nextarc cachedarcs possible_seeds
8392 if {[llength $line] != 2 || [lindex $line 0] ne "1"} {error "bad version"}
8393 # make sure it's an integer
8394 set cachedarcs [expr {int([lindex $line 1])}]
8395 if {$cachedarcs < 0} {error "bad number of arcs"}
8397 set possible_seeds {}
8405 proc dropcache {err} {
8406 global allcwait nextarc cachedarcs seeds
8408 #puts "dropping cache ($err)"
8409 foreach v {arcnos arcout arcids arcstart arcend growing \
8410 arctags archeads allparents allchildren} {
8421 proc writecache {f} {
8422 global cachearc cachedarcs allccache
8423 global arcstart arcend arcnos arcids arcout
8427 if {$lim - $a > 1000} {
8428 set lim [expr {$a + 1000}]
8431 while {[incr a] <= $lim} {
8432 if {[info exists arcend($a)]} {
8433 puts $f [list $arcstart($a) $arcend($a) $arcids($a)]
8435 puts $f [list $arcstart($a) {} $arcids($a)]
8440 catch {file delete $allccache}
8441 #puts "writing cache failed ($err)"
8444 set cachearc [expr {$a - 1}]
8445 if {$a > $cachedarcs} {
8454 global nextarc cachedarcs cachearc allccache
8456 if {$nextarc == $cachedarcs} return
8458 set cachedarcs $nextarc
8460 set f [open $allccache w]
8461 puts $f [list 1 $cachedarcs]
8466 # Returns 1 if a is an ancestor of b, -1 if b is an ancestor of a,
8467 # or 0 if neither is true.
8468 proc anc_or_desc {a b} {
8469 global arcout arcstart arcend arcnos cached_isanc
8471 if {$arcnos($a) eq $arcnos($b)} {
8472 # Both are on the same arc(s); either both are the same BMP,
8473 # or if one is not a BMP, the other is also not a BMP or is
8474 # the BMP at end of the arc (and it only has 1 incoming arc).
8475 # Or both can be BMPs with no incoming arcs.
8476 if {$a eq $b || $arcnos($a) eq {}} {
8479 # assert {[llength $arcnos($a)] == 1}
8480 set arc [lindex $arcnos($a) 0]
8481 set i [lsearch -exact $arcids($arc) $a]
8482 set j [lsearch -exact $arcids($arc) $b]
8483 if {$i < 0 || $i > $j} {
8490 if {![info exists arcout($a)]} {
8491 set arc [lindex $arcnos($a) 0]
8492 if {[info exists arcend($arc)]} {
8493 set aend $arcend($arc)
8497 set a $arcstart($arc)
8501 if {![info exists arcout($b)]} {
8502 set arc [lindex $arcnos($b) 0]
8503 if {[info exists arcend($arc)]} {
8504 set bend $arcend($arc)
8508 set b $arcstart($arc)
8518 if {[info exists cached_isanc($a,$bend)]} {
8519 if {$cached_isanc($a,$bend)} {
8523 if {[info exists cached_isanc($b,$aend)]} {
8524 if {$cached_isanc($b,$aend)} {
8527 if {[info exists cached_isanc($a,$bend)]} {
8532 set todo [list $a $b]
8535 for {set i 0} {$i < [llength $todo]} {incr i} {
8536 set x [lindex $todo $i]
8537 if {$anc($x) eq {}} {
8540 foreach arc $arcnos($x) {
8541 set xd $arcstart($arc)
8543 set cached_isanc($a,$bend) 1
8544 set cached_isanc($b,$aend) 0
8546 } elseif {$xd eq $aend} {
8547 set cached_isanc($b,$aend) 1
8548 set cached_isanc($a,$bend) 0
8551 if {![info exists anc($xd)]} {
8552 set anc($xd) $anc($x)
8554 } elseif {$anc($xd) ne $anc($x)} {
8559 set cached_isanc($a,$bend) 0
8560 set cached_isanc($b,$aend) 0
8564 # This identifies whether $desc has an ancestor that is
8565 # a growing tip of the graph and which is not an ancestor of $anc
8566 # and returns 0 if so and 1 if not.
8567 # If we subsequently discover a tag on such a growing tip, and that
8568 # turns out to be a descendent of $anc (which it could, since we
8569 # don't necessarily see children before parents), then $desc
8570 # isn't a good choice to display as a descendent tag of
8571 # $anc (since it is the descendent of another tag which is
8572 # a descendent of $anc). Similarly, $anc isn't a good choice to
8573 # display as a ancestor tag of $desc.
8575 proc is_certain {desc anc} {
8576 global arcnos arcout arcstart arcend growing problems
8579 if {[llength $arcnos($anc)] == 1} {
8580 # tags on the same arc are certain
8581 if {$arcnos($desc) eq $arcnos($anc)} {
8584 if {![info exists arcout($anc)]} {
8585 # if $anc is partway along an arc, use the start of the arc instead
8586 set a [lindex $arcnos($anc) 0]
8587 set anc $arcstart($a)
8590 if {[llength $arcnos($desc)] > 1 || [info exists arcout($desc)]} {
8593 set a [lindex $arcnos($desc) 0]
8599 set anclist [list $x]
8603 for {set i 0} {$i < [llength $anclist] && ($nnh > 0 || $ngrowanc > 0)} {incr i} {
8604 set x [lindex $anclist $i]
8609 foreach a $arcout($x) {
8610 if {[info exists growing($a)]} {
8611 if {![info exists growanc($x)] && $dl($x)} {
8617 if {[info exists dl($y)]} {
8621 if {![info exists done($y)]} {
8624 if {[info exists growanc($x)]} {
8628 for {set k 0} {$k < [llength $xl]} {incr k} {
8629 set z [lindex $xl $k]
8630 foreach c $arcout($z) {
8631 if {[info exists arcend($c)]} {
8633 if {[info exists dl($v)] && $dl($v)} {
8635 if {![info exists done($v)]} {
8638 if {[info exists growanc($v)]} {
8648 } elseif {$y eq $anc || !$dl($x)} {
8659 foreach x [array names growanc] {
8668 proc validate_arctags {a} {
8669 global arctags idtags
8673 foreach id $arctags($a) {
8675 if {![info exists idtags($id)]} {
8676 set na [lreplace $na $i $i]
8683 proc validate_archeads {a} {
8684 global archeads idheads
8687 set na $archeads($a)
8688 foreach id $archeads($a) {
8690 if {![info exists idheads($id)]} {
8691 set na [lreplace $na $i $i]
8695 set archeads($a) $na
8698 # Return the list of IDs that have tags that are descendents of id,
8699 # ignoring IDs that are descendents of IDs already reported.
8700 proc desctags {id} {
8701 global arcnos arcstart arcids arctags idtags allparents
8702 global growing cached_dtags
8704 if {![info exists allparents($id)]} {
8707 set t1 [clock clicks -milliseconds]
8709 if {[llength $arcnos($id)] == 1 && [llength $allparents($id)] == 1} {
8710 # part-way along an arc; check that arc first
8711 set a [lindex $arcnos($id) 0]
8712 if {$arctags($a) ne {}} {
8714 set i [lsearch -exact $arcids($a) $id]
8716 foreach t $arctags($a) {
8717 set j [lsearch -exact $arcids($a) $t]
8725 set id $arcstart($a)
8726 if {[info exists idtags($id)]} {
8730 if {[info exists cached_dtags($id)]} {
8731 return $cached_dtags($id)
8738 for {set i 0} {$i < [llength $todo] && $nc > 0} {incr i} {
8739 set id [lindex $todo $i]
8741 set ta [info exists hastaggedancestor($id)]
8745 # ignore tags on starting node
8746 if {!$ta && $i > 0} {
8747 if {[info exists idtags($id)]} {
8750 } elseif {[info exists cached_dtags($id)]} {
8751 set tagloc($id) $cached_dtags($id)
8755 foreach a $arcnos($id) {
8757 if {!$ta && $arctags($a) ne {}} {
8759 if {$arctags($a) ne {}} {
8760 lappend tagloc($id) [lindex $arctags($a) end]
8763 if {$ta || $arctags($a) ne {}} {
8764 set tomark [list $d]
8765 for {set j 0} {$j < [llength $tomark]} {incr j} {
8766 set dd [lindex $tomark $j]
8767 if {![info exists hastaggedancestor($dd)]} {
8768 if {[info exists done($dd)]} {
8769 foreach b $arcnos($dd) {
8770 lappend tomark $arcstart($b)
8772 if {[info exists tagloc($dd)]} {
8775 } elseif {[info exists queued($dd)]} {
8778 set hastaggedancestor($dd) 1
8782 if {![info exists queued($d)]} {
8785 if {![info exists hastaggedancestor($d)]} {
8792 foreach id [array names tagloc] {
8793 if {![info exists hastaggedancestor($id)]} {
8794 foreach t $tagloc($id) {
8795 if {[lsearch -exact $tags $t] < 0} {
8801 set t2 [clock clicks -milliseconds]
8804 # remove tags that are descendents of other tags
8805 for {set i 0} {$i < [llength $tags]} {incr i} {
8806 set a [lindex $tags $i]
8807 for {set j 0} {$j < $i} {incr j} {
8808 set b [lindex $tags $j]
8809 set r [anc_or_desc $a $b]
8811 set tags [lreplace $tags $j $j]
8814 } elseif {$r == -1} {
8815 set tags [lreplace $tags $i $i]
8822 if {[array names growing] ne {}} {
8823 # graph isn't finished, need to check if any tag could get
8824 # eclipsed by another tag coming later. Simply ignore any
8825 # tags that could later get eclipsed.
8828 if {[is_certain $t $origid]} {
8832 if {$tags eq $ctags} {
8833 set cached_dtags($origid) $tags
8838 set cached_dtags($origid) $tags
8840 set t3 [clock clicks -milliseconds]
8841 if {0 && $t3 - $t1 >= 100} {
8842 puts "iterating descendents ($loopix/[llength $todo] nodes) took\
8843 [expr {$t2-$t1}]+[expr {$t3-$t2}]ms, $nc candidates left"
8849 global arcnos arcids arcout arcend arctags idtags allparents
8850 global growing cached_atags
8852 if {![info exists allparents($id)]} {
8855 set t1 [clock clicks -milliseconds]
8857 if {[llength $arcnos($id)] == 1 && [llength $allparents($id)] == 1} {
8858 # part-way along an arc; check that arc first
8859 set a [lindex $arcnos($id) 0]
8860 if {$arctags($a) ne {}} {
8862 set i [lsearch -exact $arcids($a) $id]
8863 foreach t $arctags($a) {
8864 set j [lsearch -exact $arcids($a) $t]
8870 if {![info exists arcend($a)]} {
8874 if {[info exists idtags($id)]} {
8878 if {[info exists cached_atags($id)]} {
8879 return $cached_atags($id)
8887 for {set i 0} {$i < [llength $todo] && $nc > 0} {incr i} {
8888 set id [lindex $todo $i]
8890 set td [info exists hastaggeddescendent($id)]
8894 # ignore tags on starting node
8895 if {!$td && $i > 0} {
8896 if {[info exists idtags($id)]} {
8899 } elseif {[info exists cached_atags($id)]} {
8900 set tagloc($id) $cached_atags($id)
8904 foreach a $arcout($id) {
8905 if {!$td && $arctags($a) ne {}} {
8907 if {$arctags($a) ne {}} {
8908 lappend tagloc($id) [lindex $arctags($a) 0]
8911 if {![info exists arcend($a)]} continue
8913 if {$td || $arctags($a) ne {}} {
8914 set tomark [list $d]
8915 for {set j 0} {$j < [llength $tomark]} {incr j} {
8916 set dd [lindex $tomark $j]
8917 if {![info exists hastaggeddescendent($dd)]} {
8918 if {[info exists done($dd)]} {
8919 foreach b $arcout($dd) {
8920 if {[info exists arcend($b)]} {
8921 lappend tomark $arcend($b)
8924 if {[info exists tagloc($dd)]} {
8927 } elseif {[info exists queued($dd)]} {
8930 set hastaggeddescendent($dd) 1
8934 if {![info exists queued($d)]} {
8937 if {![info exists hastaggeddescendent($d)]} {
8943 set t2 [clock clicks -milliseconds]
8946 foreach id [array names tagloc] {
8947 if {![info exists hastaggeddescendent($id)]} {
8948 foreach t $tagloc($id) {
8949 if {[lsearch -exact $tags $t] < 0} {
8956 # remove tags that are ancestors of other tags
8957 for {set i 0} {$i < [llength $tags]} {incr i} {
8958 set a [lindex $tags $i]
8959 for {set j 0} {$j < $i} {incr j} {
8960 set b [lindex $tags $j]
8961 set r [anc_or_desc $a $b]
8963 set tags [lreplace $tags $j $j]
8966 } elseif {$r == 1} {
8967 set tags [lreplace $tags $i $i]
8974 if {[array names growing] ne {}} {
8975 # graph isn't finished, need to check if any tag could get
8976 # eclipsed by another tag coming later. Simply ignore any
8977 # tags that could later get eclipsed.
8980 if {[is_certain $origid $t]} {
8984 if {$tags eq $ctags} {
8985 set cached_atags($origid) $tags
8990 set cached_atags($origid) $tags
8992 set t3 [clock clicks -milliseconds]
8993 if {0 && $t3 - $t1 >= 100} {
8994 puts "iterating ancestors ($loopix/[llength $todo] nodes) took\
8995 [expr {$t2-$t1}]+[expr {$t3-$t2}]ms, $nc candidates left"
9000 # Return the list of IDs that have heads that are descendents of id,
9001 # including id itself if it has a head.
9002 proc descheads {id} {
9003 global arcnos arcstart arcids archeads idheads cached_dheads
9006 if {![info exists allparents($id)]} {
9010 if {[llength $arcnos($id)] == 1 && [llength $allparents($id)] == 1} {
9011 # part-way along an arc; check it first
9012 set a [lindex $arcnos($id) 0]
9013 if {$archeads($a) ne {}} {
9014 validate_archeads $a
9015 set i [lsearch -exact $arcids($a) $id]
9016 foreach t $archeads($a) {
9017 set j [lsearch -exact $arcids($a) $t]
9022 set id $arcstart($a)
9028 for {set i 0} {$i < [llength $todo]} {incr i} {
9029 set id [lindex $todo $i]
9030 if {[info exists cached_dheads($id)]} {
9031 set ret [concat $ret $cached_dheads($id)]
9033 if {[info exists idheads($id)]} {
9036 foreach a $arcnos($id) {
9037 if {$archeads($a) ne {}} {
9038 validate_archeads $a
9039 if {$archeads($a) ne {}} {
9040 set ret [concat $ret $archeads($a)]
9044 if {![info exists seen($d)]} {
9051 set ret [lsort -unique $ret]
9052 set cached_dheads($origid) $ret
9053 return [concat $ret $aret]
9056 proc addedtag {id} {
9057 global arcnos arcout cached_dtags cached_atags
9059 if {![info exists arcnos($id)]} return
9060 if {![info exists arcout($id)]} {
9061 recalcarc [lindex $arcnos($id) 0]
9063 catch {unset cached_dtags}
9064 catch {unset cached_atags}
9067 proc addedhead {hid head} {
9068 global arcnos arcout cached_dheads
9070 if {![info exists arcnos($hid)]} return
9071 if {![info exists arcout($hid)]} {
9072 recalcarc [lindex $arcnos($hid) 0]
9074 catch {unset cached_dheads}
9077 proc removedhead {hid head} {
9078 global cached_dheads
9080 catch {unset cached_dheads}
9083 proc movedhead {hid head} {
9084 global arcnos arcout cached_dheads
9086 if {![info exists arcnos($hid)]} return
9087 if {![info exists arcout($hid)]} {
9088 recalcarc [lindex $arcnos($hid) 0]
9090 catch {unset cached_dheads}
9093 proc changedrefs {} {
9094 global cached_dheads cached_dtags cached_atags
9095 global arctags archeads arcnos arcout idheads idtags
9097 foreach id [concat [array names idheads] [array names idtags]] {
9098 if {[info exists arcnos($id)] && ![info exists arcout($id)]} {
9099 set a [lindex $arcnos($id) 0]
9100 if {![info exists donearc($a)]} {
9106 catch {unset cached_dtags}
9107 catch {unset cached_atags}
9108 catch {unset cached_dheads}
9111 proc rereadrefs {} {
9112 global idtags idheads idotherrefs mainheadid
9114 set refids [concat [array names idtags] \
9115 [array names idheads] [array names idotherrefs]]
9116 foreach id $refids {
9117 if {![info exists ref($id)]} {
9118 set ref($id) [listrefs $id]
9121 set oldmainhead $mainheadid
9124 set refids [lsort -unique [concat $refids [array names idtags] \
9125 [array names idheads] [array names idotherrefs]]]
9126 foreach id $refids {
9127 set v [listrefs $id]
9128 if {![info exists ref($id)] || $ref($id) != $v} {
9132 if {$oldmainhead ne $mainheadid} {
9133 redrawtags $oldmainhead
9134 redrawtags $mainheadid
9139 proc listrefs {id} {
9140 global idtags idheads idotherrefs
9143 if {[info exists idtags($id)]} {
9147 if {[info exists idheads($id)]} {
9151 if {[info exists idotherrefs($id)]} {
9152 set z $idotherrefs($id)
9154 return [list $x $y $z]
9157 proc showtag {tag isnew} {
9158 global ctext tagcontents tagids linknum tagobjid
9161 addtohistory [list showtag $tag 0]
9163 $ctext conf -state normal
9167 if {![info exists tagcontents($tag)]} {
9169 set tagcontents($tag) [exec git cat-file tag $tagobjid($tag)]
9172 if {[info exists tagcontents($tag)]} {
9173 set text $tagcontents($tag)
9175 set text "[mc "Tag"]: $tag\n[mc "Id"]: $tagids($tag)"
9177 appendwithlinks $text {}
9178 $ctext conf -state disabled
9190 if {[info exists gitktmpdir]} {
9191 catch {file delete -force $gitktmpdir}
9195 proc mkfontdisp {font top which} {
9196 global fontattr fontpref $font
9198 set fontpref($font) [set $font]
9199 button $top.${font}but -text $which -font optionfont \
9200 -command [list choosefont $font $which]
9201 label $top.$font -relief flat -font $font \
9202 -text $fontattr($font,family) -justify left
9203 grid x $top.${font}but $top.$font -sticky w
9206 proc choosefont {font which} {
9207 global fontparam fontlist fonttop fontattr
9209 set fontparam(which) $which
9210 set fontparam(font) $font
9211 set fontparam(family) [font actual $font -family]
9212 set fontparam(size) $fontattr($font,size)
9213 set fontparam(weight) $fontattr($font,weight)
9214 set fontparam(slant) $fontattr($font,slant)
9217 if {![winfo exists $top]} {
9219 eval font config sample [font actual $font]
9221 wm title $top [mc "Gitk font chooser"]
9222 label $top.l -textvariable fontparam(which)
9223 pack $top.l -side top
9224 set fontlist [lsort [font families]]
9226 listbox $top.f.fam -listvariable fontlist \
9227 -yscrollcommand [list $top.f.sb set]
9228 bind $top.f.fam <<ListboxSelect>> selfontfam
9229 scrollbar $top.f.sb -command [list $top.f.fam yview]
9230 pack $top.f.sb -side right -fill y
9231 pack $top.f.fam -side left -fill both -expand 1
9232 pack $top.f -side top -fill both -expand 1
9234 spinbox $top.g.size -from 4 -to 40 -width 4 \
9235 -textvariable fontparam(size) \
9236 -validatecommand {string is integer -strict %s}
9237 checkbutton $top.g.bold -padx 5 \
9238 -font {{Times New Roman} 12 bold} -text [mc "B"] -indicatoron 0 \
9239 -variable fontparam(weight) -onvalue bold -offvalue normal
9240 checkbutton $top.g.ital -padx 5 \
9241 -font {{Times New Roman} 12 italic} -text [mc "I"] -indicatoron 0 \
9242 -variable fontparam(slant) -onvalue italic -offvalue roman
9243 pack $top.g.size $top.g.bold $top.g.ital -side left
9244 pack $top.g -side top
9245 canvas $top.c -width 150 -height 50 -border 2 -relief sunk \
9247 $top.c create text 100 25 -anchor center -text $which -font sample \
9248 -fill black -tags text
9249 bind $top.c <Configure> [list centertext $top.c]
9250 pack $top.c -side top -fill x
9252 button $top.buts.ok -text [mc "OK"] -command fontok -default active
9253 button $top.buts.can -text [mc "Cancel"] -command fontcan -default normal
9254 grid $top.buts.ok $top.buts.can
9255 grid columnconfigure $top.buts 0 -weight 1 -uniform a
9256 grid columnconfigure $top.buts 1 -weight 1 -uniform a
9257 pack $top.buts -side bottom -fill x
9258 trace add variable fontparam write chg_fontparam
9261 $top.c itemconf text -text $which
9263 set i [lsearch -exact $fontlist $fontparam(family)]
9265 $top.f.fam selection set $i
9270 proc centertext {w} {
9271 $w coords text [expr {[winfo width $w] / 2}] [expr {[winfo height $w] / 2}]
9275 global fontparam fontpref prefstop
9277 set f $fontparam(font)
9278 set fontpref($f) [list $fontparam(family) $fontparam(size)]
9279 if {$fontparam(weight) eq "bold"} {
9280 lappend fontpref($f) "bold"
9282 if {$fontparam(slant) eq "italic"} {
9283 lappend fontpref($f) "italic"
9286 $w conf -text $fontparam(family) -font $fontpref($f)
9292 global fonttop fontparam
9294 if {[info exists fonttop]} {
9295 catch {destroy $fonttop}
9296 catch {font delete sample}
9302 proc selfontfam {} {
9303 global fonttop fontparam
9305 set i [$fonttop.f.fam curselection]
9307 set fontparam(family) [$fonttop.f.fam get $i]
9311 proc chg_fontparam {v sub op} {
9314 font config sample -$sub $fontparam($sub)
9318 global maxwidth maxgraphpct
9319 global oldprefs prefstop showneartags showlocalchanges
9320 global bgcolor fgcolor ctext diffcolors selectbgcolor
9321 global tabstop limitdiffs autoselect extdifftool
9325 if {[winfo exists $top]} {
9329 foreach v {maxwidth maxgraphpct showneartags showlocalchanges \
9330 limitdiffs tabstop} {
9331 set oldprefs($v) [set $v]
9334 wm title $top [mc "Gitk preferences"]
9335 label $top.ldisp -text [mc "Commit list display options"]
9336 grid $top.ldisp - -sticky w -pady 10
9337 label $top.spacer -text " "
9338 label $top.maxwidthl -text [mc "Maximum graph width (lines)"] \
9340 spinbox $top.maxwidth -from 0 -to 100 -width 4 -textvariable maxwidth
9341 grid $top.spacer $top.maxwidthl $top.maxwidth -sticky w
9342 label $top.maxpctl -text [mc "Maximum graph width (% of pane)"] \
9344 spinbox $top.maxpct -from 1 -to 100 -width 4 -textvariable maxgraphpct
9345 grid x $top.maxpctl $top.maxpct -sticky w
9346 frame $top.showlocal
9347 label $top.showlocal.l -text [mc "Show local changes"] -font optionfont
9348 checkbutton $top.showlocal.b -variable showlocalchanges
9349 pack $top.showlocal.b $top.showlocal.l -side left
9350 grid x $top.showlocal -sticky w
9351 frame $top.autoselect
9352 label $top.autoselect.l -text [mc "Auto-select SHA1"] -font optionfont
9353 checkbutton $top.autoselect.b -variable autoselect
9354 pack $top.autoselect.b $top.autoselect.l -side left
9355 grid x $top.autoselect -sticky w
9357 label $top.ddisp -text [mc "Diff display options"]
9358 grid $top.ddisp - -sticky w -pady 10
9359 label $top.tabstopl -text [mc "Tab spacing"] -font optionfont
9360 spinbox $top.tabstop -from 1 -to 20 -width 4 -textvariable tabstop
9361 grid x $top.tabstopl $top.tabstop -sticky w
9363 label $top.ntag.l -text [mc "Display nearby tags"] -font optionfont
9364 checkbutton $top.ntag.b -variable showneartags
9365 pack $top.ntag.b $top.ntag.l -side left
9366 grid x $top.ntag -sticky w
9368 label $top.ldiff.l -text [mc "Limit diffs to listed paths"] -font optionfont
9369 checkbutton $top.ldiff.b -variable limitdiffs
9370 pack $top.ldiff.b $top.ldiff.l -side left
9371 grid x $top.ldiff -sticky w
9373 entry $top.extdifft -textvariable extdifftool
9375 label $top.extdifff.l -text [mc "External diff tool" ] -font optionfont \
9377 button $top.extdifff.b -text [mc "Choose..."] -font optionfont \
9378 -command choose_extdiff
9379 pack $top.extdifff.l $top.extdifff.b -side left
9380 grid x $top.extdifff $top.extdifft -sticky w
9382 label $top.cdisp -text [mc "Colors: press to choose"]
9383 grid $top.cdisp - -sticky w -pady 10
9384 label $top.bg -padx 40 -relief sunk -background $bgcolor
9385 button $top.bgbut -text [mc "Background"] -font optionfont \
9386 -command [list choosecolor bgcolor {} $top.bg background setbg]
9387 grid x $top.bgbut $top.bg -sticky w
9388 label $top.fg -padx 40 -relief sunk -background $fgcolor
9389 button $top.fgbut -text [mc "Foreground"] -font optionfont \
9390 -command [list choosecolor fgcolor {} $top.fg foreground setfg]
9391 grid x $top.fgbut $top.fg -sticky w
9392 label $top.diffold -padx 40 -relief sunk -background [lindex $diffcolors 0]
9393 button $top.diffoldbut -text [mc "Diff: old lines"] -font optionfont \
9394 -command [list choosecolor diffcolors 0 $top.diffold "diff old lines" \
9395 [list $ctext tag conf d0 -foreground]]
9396 grid x $top.diffoldbut $top.diffold -sticky w
9397 label $top.diffnew -padx 40 -relief sunk -background [lindex $diffcolors 1]
9398 button $top.diffnewbut -text [mc "Diff: new lines"] -font optionfont \
9399 -command [list choosecolor diffcolors 1 $top.diffnew "diff new lines" \
9400 [list $ctext tag conf d1 -foreground]]
9401 grid x $top.diffnewbut $top.diffnew -sticky w
9402 label $top.hunksep -padx 40 -relief sunk -background [lindex $diffcolors 2]
9403 button $top.hunksepbut -text [mc "Diff: hunk header"] -font optionfont \
9404 -command [list choosecolor diffcolors 2 $top.hunksep \
9405 "diff hunk header" \
9406 [list $ctext tag conf hunksep -foreground]]
9407 grid x $top.hunksepbut $top.hunksep -sticky w
9408 label $top.selbgsep -padx 40 -relief sunk -background $selectbgcolor
9409 button $top.selbgbut -text [mc "Select bg"] -font optionfont \
9410 -command [list choosecolor selectbgcolor {} $top.selbgsep background setselbg]
9411 grid x $top.selbgbut $top.selbgsep -sticky w
9413 label $top.cfont -text [mc "Fonts: press to choose"]
9414 grid $top.cfont - -sticky w -pady 10
9415 mkfontdisp mainfont $top [mc "Main font"]
9416 mkfontdisp textfont $top [mc "Diff display font"]
9417 mkfontdisp uifont $top [mc "User interface font"]
9420 button $top.buts.ok -text [mc "OK"] -command prefsok -default active
9421 button $top.buts.can -text [mc "Cancel"] -command prefscan -default normal
9422 grid $top.buts.ok $top.buts.can
9423 grid columnconfigure $top.buts 0 -weight 1 -uniform a
9424 grid columnconfigure $top.buts 1 -weight 1 -uniform a
9425 grid $top.buts - - -pady 10 -sticky ew
9426 bind $top <Visibility> "focus $top.buts.ok"
9429 proc choose_extdiff {} {
9432 set prog [tk_getOpenFile -title "External diff tool" -multiple false]
9434 set extdifftool $prog
9438 proc choosecolor {v vi w x cmd} {
9441 set c [tk_chooseColor -initialcolor [lindex [set $v] $vi] \
9442 -title [mc "Gitk: choose color for %s" $x]]
9443 if {$c eq {}} return
9444 $w conf -background $c
9450 global bglist cflist
9452 $w configure -selectbackground $c
9454 $cflist tag configure highlight \
9455 -background [$cflist cget -selectbackground]
9456 allcanvs itemconf secsel -fill $c
9463 $w conf -background $c
9471 $w conf -foreground $c
9473 allcanvs itemconf text -fill $c
9474 $canv itemconf circle -outline $c
9478 global oldprefs prefstop
9480 foreach v {maxwidth maxgraphpct showneartags showlocalchanges \
9481 limitdiffs tabstop} {
9483 set $v $oldprefs($v)
9485 catch {destroy $prefstop}
9491 global maxwidth maxgraphpct
9492 global oldprefs prefstop showneartags showlocalchanges
9493 global fontpref mainfont textfont uifont
9494 global limitdiffs treediffs
9496 catch {destroy $prefstop}
9500 if {$mainfont ne $fontpref(mainfont)} {
9501 set mainfont $fontpref(mainfont)
9502 parsefont mainfont $mainfont
9503 eval font configure mainfont [fontflags mainfont]
9504 eval font configure mainfontbold [fontflags mainfont 1]
9508 if {$textfont ne $fontpref(textfont)} {
9509 set textfont $fontpref(textfont)
9510 parsefont textfont $textfont
9511 eval font configure textfont [fontflags textfont]
9512 eval font configure textfontbold [fontflags textfont 1]
9514 if {$uifont ne $fontpref(uifont)} {
9515 set uifont $fontpref(uifont)
9516 parsefont uifont $uifont
9517 eval font configure uifont [fontflags uifont]
9520 if {$showlocalchanges != $oldprefs(showlocalchanges)} {
9521 if {$showlocalchanges} {
9527 if {$limitdiffs != $oldprefs(limitdiffs)} {
9528 # treediffs elements are limited by path
9529 catch {unset treediffs}
9531 if {$fontchanged || $maxwidth != $oldprefs(maxwidth)
9532 || $maxgraphpct != $oldprefs(maxgraphpct)} {
9534 } elseif {$showneartags != $oldprefs(showneartags) ||
9535 $limitdiffs != $oldprefs(limitdiffs)} {
9540 proc formatdate {d} {
9541 global datetimeformat
9543 set d [clock format $d -format $datetimeformat]
9548 # This list of encoding names and aliases is distilled from
9549 # http://www.iana.org/assignments/character-sets.
9550 # Not all of them are supported by Tcl.
9551 set encoding_aliases {
9552 { ANSI_X3.4-1968 iso-ir-6 ANSI_X3.4-1986 ISO_646.irv:1991 ASCII
9553 ISO646-US US-ASCII us IBM367 cp367 csASCII }
9554 { ISO-10646-UTF-1 csISO10646UTF1 }
9555 { ISO_646.basic:1983 ref csISO646basic1983 }
9556 { INVARIANT csINVARIANT }
9557 { ISO_646.irv:1983 iso-ir-2 irv csISO2IntlRefVersion }
9558 { BS_4730 iso-ir-4 ISO646-GB gb uk csISO4UnitedKingdom }
9559 { NATS-SEFI iso-ir-8-1 csNATSSEFI }
9560 { NATS-SEFI-ADD iso-ir-8-2 csNATSSEFIADD }
9561 { NATS-DANO iso-ir-9-1 csNATSDANO }
9562 { NATS-DANO-ADD iso-ir-9-2 csNATSDANOADD }
9563 { SEN_850200_B iso-ir-10 FI ISO646-FI ISO646-SE se csISO10Swedish }
9564 { SEN_850200_C iso-ir-11 ISO646-SE2 se2 csISO11SwedishForNames }
9565 { KS_C_5601-1987 iso-ir-149 KS_C_5601-1989 KSC_5601 korean csKSC56011987 }
9566 { ISO-2022-KR csISO2022KR }
9568 { ISO-2022-JP csISO2022JP }
9569 { ISO-2022-JP-2 csISO2022JP2 }
9570 { JIS_C6220-1969-jp JIS_C6220-1969 iso-ir-13 katakana x0201-7
9572 { JIS_C6220-1969-ro iso-ir-14 jp ISO646-JP csISO14JISC6220ro }
9573 { IT iso-ir-15 ISO646-IT csISO15Italian }
9574 { PT iso-ir-16 ISO646-PT csISO16Portuguese }
9575 { ES iso-ir-17 ISO646-ES csISO17Spanish }
9576 { greek7-old iso-ir-18 csISO18Greek7Old }
9577 { latin-greek iso-ir-19 csISO19LatinGreek }
9578 { DIN_66003 iso-ir-21 de ISO646-DE csISO21German }
9579 { NF_Z_62-010_(1973) iso-ir-25 ISO646-FR1 csISO25French }
9580 { Latin-greek-1 iso-ir-27 csISO27LatinGreek1 }
9581 { ISO_5427 iso-ir-37 csISO5427Cyrillic }
9582 { JIS_C6226-1978 iso-ir-42 csISO42JISC62261978 }
9583 { BS_viewdata iso-ir-47 csISO47BSViewdata }
9584 { INIS iso-ir-49 csISO49INIS }
9585 { INIS-8 iso-ir-50 csISO50INIS8 }
9586 { INIS-cyrillic iso-ir-51 csISO51INISCyrillic }
9587 { ISO_5427:1981 iso-ir-54 ISO5427Cyrillic1981 }
9588 { ISO_5428:1980 iso-ir-55 csISO5428Greek }
9589 { GB_1988-80 iso-ir-57 cn ISO646-CN csISO57GB1988 }
9590 { GB_2312-80 iso-ir-58 chinese csISO58GB231280 }
9591 { NS_4551-1 iso-ir-60 ISO646-NO no csISO60DanishNorwegian
9593 { NS_4551-2 ISO646-NO2 iso-ir-61 no2 csISO61Norwegian2 }
9594 { NF_Z_62-010 iso-ir-69 ISO646-FR fr csISO69French }
9595 { videotex-suppl iso-ir-70 csISO70VideotexSupp1 }
9596 { PT2 iso-ir-84 ISO646-PT2 csISO84Portuguese2 }
9597 { ES2 iso-ir-85 ISO646-ES2 csISO85Spanish2 }
9598 { MSZ_7795.3 iso-ir-86 ISO646-HU hu csISO86Hungarian }
9599 { JIS_C6226-1983 iso-ir-87 x0208 JIS_X0208-1983 csISO87JISX0208 }
9600 { greek7 iso-ir-88 csISO88Greek7 }
9601 { ASMO_449 ISO_9036 arabic7 iso-ir-89 csISO89ASMO449 }
9602 { iso-ir-90 csISO90 }
9603 { JIS_C6229-1984-a iso-ir-91 jp-ocr-a csISO91JISC62291984a }
9604 { JIS_C6229-1984-b iso-ir-92 ISO646-JP-OCR-B jp-ocr-b
9605 csISO92JISC62991984b }
9606 { JIS_C6229-1984-b-add iso-ir-93 jp-ocr-b-add csISO93JIS62291984badd }
9607 { JIS_C6229-1984-hand iso-ir-94 jp-ocr-hand csISO94JIS62291984hand }
9608 { JIS_C6229-1984-hand-add iso-ir-95 jp-ocr-hand-add
9609 csISO95JIS62291984handadd }
9610 { JIS_C6229-1984-kana iso-ir-96 csISO96JISC62291984kana }
9611 { ISO_2033-1983 iso-ir-98 e13b csISO2033 }
9612 { ANSI_X3.110-1983 iso-ir-99 CSA_T500-1983 NAPLPS csISO99NAPLPS }
9613 { ISO_8859-1:1987 iso-ir-100 ISO_8859-1 ISO-8859-1 latin1 l1 IBM819
9615 { ISO_8859-2:1987 iso-ir-101 ISO_8859-2 ISO-8859-2 latin2 l2 csISOLatin2 }
9616 { T.61-7bit iso-ir-102 csISO102T617bit }
9617 { T.61-8bit T.61 iso-ir-103 csISO103T618bit }
9618 { ISO_8859-3:1988 iso-ir-109 ISO_8859-3 ISO-8859-3 latin3 l3 csISOLatin3 }
9619 { ISO_8859-4:1988 iso-ir-110 ISO_8859-4 ISO-8859-4 latin4 l4 csISOLatin4 }
9620 { ECMA-cyrillic iso-ir-111 KOI8-E csISO111ECMACyrillic }
9621 { CSA_Z243.4-1985-1 iso-ir-121 ISO646-CA csa7-1 ca csISO121Canadian1 }
9622 { CSA_Z243.4-1985-2 iso-ir-122 ISO646-CA2 csa7-2 csISO122Canadian2 }
9623 { CSA_Z243.4-1985-gr iso-ir-123 csISO123CSAZ24341985gr }
9624 { ISO_8859-6:1987 iso-ir-127 ISO_8859-6 ISO-8859-6 ECMA-114 ASMO-708
9625 arabic csISOLatinArabic }
9626 { ISO_8859-6-E csISO88596E ISO-8859-6-E }
9627 { ISO_8859-6-I csISO88596I ISO-8859-6-I }
9628 { ISO_8859-7:1987 iso-ir-126 ISO_8859-7 ISO-8859-7 ELOT_928 ECMA-118
9629 greek greek8 csISOLatinGreek }
9630 { T.101-G2 iso-ir-128 csISO128T101G2 }
9631 { ISO_8859-8:1988 iso-ir-138 ISO_8859-8 ISO-8859-8 hebrew
9633 { ISO_8859-8-E csISO88598E ISO-8859-8-E }
9634 { ISO_8859-8-I csISO88598I ISO-8859-8-I }
9635 { CSN_369103 iso-ir-139 csISO139CSN369103 }
9636 { JUS_I.B1.002 iso-ir-141 ISO646-YU js yu csISO141JUSIB1002 }
9637 { ISO_6937-2-add iso-ir-142 csISOTextComm }
9638 { IEC_P27-1 iso-ir-143 csISO143IECP271 }
9639 { ISO_8859-5:1988 iso-ir-144 ISO_8859-5 ISO-8859-5 cyrillic
9640 csISOLatinCyrillic }
9641 { JUS_I.B1.003-serb iso-ir-146 serbian csISO146Serbian }
9642 { JUS_I.B1.003-mac macedonian iso-ir-147 csISO147Macedonian }
9643 { ISO_8859-9:1989 iso-ir-148 ISO_8859-9 ISO-8859-9 latin5 l5 csISOLatin5 }
9644 { greek-ccitt iso-ir-150 csISO150 csISO150GreekCCITT }
9645 { NC_NC00-10:81 cuba iso-ir-151 ISO646-CU csISO151Cuba }
9646 { ISO_6937-2-25 iso-ir-152 csISO6937Add }
9647 { GOST_19768-74 ST_SEV_358-88 iso-ir-153 csISO153GOST1976874 }
9648 { ISO_8859-supp iso-ir-154 latin1-2-5 csISO8859Supp }
9649 { ISO_10367-box iso-ir-155 csISO10367Box }
9650 { ISO-8859-10 iso-ir-157 l6 ISO_8859-10:1992 csISOLatin6 latin6 }
9651 { latin-lap lap iso-ir-158 csISO158Lap }
9652 { JIS_X0212-1990 x0212 iso-ir-159 csISO159JISX02121990 }
9653 { DS_2089 DS2089 ISO646-DK dk csISO646Danish }
9656 { JIS_X0201 X0201 csHalfWidthKatakana }
9657 { KSC5636 ISO646-KR csKSC5636 }
9658 { ISO-10646-UCS-2 csUnicode }
9659 { ISO-10646-UCS-4 csUCS4 }
9660 { DEC-MCS dec csDECMCS }
9661 { hp-roman8 roman8 r8 csHPRoman8 }
9662 { macintosh mac csMacintosh }
9663 { IBM037 cp037 ebcdic-cp-us ebcdic-cp-ca ebcdic-cp-wt ebcdic-cp-nl
9665 { IBM038 EBCDIC-INT cp038 csIBM038 }
9666 { IBM273 CP273 csIBM273 }
9667 { IBM274 EBCDIC-BE CP274 csIBM274 }
9668 { IBM275 EBCDIC-BR cp275 csIBM275 }
9669 { IBM277 EBCDIC-CP-DK EBCDIC-CP-NO csIBM277 }
9670 { IBM278 CP278 ebcdic-cp-fi ebcdic-cp-se csIBM278 }
9671 { IBM280 CP280 ebcdic-cp-it csIBM280 }
9672 { IBM281 EBCDIC-JP-E cp281 csIBM281 }
9673 { IBM284 CP284 ebcdic-cp-es csIBM284 }
9674 { IBM285 CP285 ebcdic-cp-gb csIBM285 }
9675 { IBM290 cp290 EBCDIC-JP-kana csIBM290 }
9676 { IBM297 cp297 ebcdic-cp-fr csIBM297 }
9677 { IBM420 cp420 ebcdic-cp-ar1 csIBM420 }
9678 { IBM423 cp423 ebcdic-cp-gr csIBM423 }
9679 { IBM424 cp424 ebcdic-cp-he csIBM424 }
9680 { IBM437 cp437 437 csPC8CodePage437 }
9681 { IBM500 CP500 ebcdic-cp-be ebcdic-cp-ch csIBM500 }
9682 { IBM775 cp775 csPC775Baltic }
9683 { IBM850 cp850 850 csPC850Multilingual }
9684 { IBM851 cp851 851 csIBM851 }
9685 { IBM852 cp852 852 csPCp852 }
9686 { IBM855 cp855 855 csIBM855 }
9687 { IBM857 cp857 857 csIBM857 }
9688 { IBM860 cp860 860 csIBM860 }
9689 { IBM861 cp861 861 cp-is csIBM861 }
9690 { IBM862 cp862 862 csPC862LatinHebrew }
9691 { IBM863 cp863 863 csIBM863 }
9692 { IBM864 cp864 csIBM864 }
9693 { IBM865 cp865 865 csIBM865 }
9694 { IBM866 cp866 866 csIBM866 }
9695 { IBM868 CP868 cp-ar csIBM868 }
9696 { IBM869 cp869 869 cp-gr csIBM869 }
9697 { IBM870 CP870 ebcdic-cp-roece ebcdic-cp-yu csIBM870 }
9698 { IBM871 CP871 ebcdic-cp-is csIBM871 }
9699 { IBM880 cp880 EBCDIC-Cyrillic csIBM880 }
9700 { IBM891 cp891 csIBM891 }
9701 { IBM903 cp903 csIBM903 }
9702 { IBM904 cp904 904 csIBBM904 }
9703 { IBM905 CP905 ebcdic-cp-tr csIBM905 }
9704 { IBM918 CP918 ebcdic-cp-ar2 csIBM918 }
9705 { IBM1026 CP1026 csIBM1026 }
9706 { EBCDIC-AT-DE csIBMEBCDICATDE }
9707 { EBCDIC-AT-DE-A csEBCDICATDEA }
9708 { EBCDIC-CA-FR csEBCDICCAFR }
9709 { EBCDIC-DK-NO csEBCDICDKNO }
9710 { EBCDIC-DK-NO-A csEBCDICDKNOA }
9711 { EBCDIC-FI-SE csEBCDICFISE }
9712 { EBCDIC-FI-SE-A csEBCDICFISEA }
9713 { EBCDIC-FR csEBCDICFR }
9714 { EBCDIC-IT csEBCDICIT }
9715 { EBCDIC-PT csEBCDICPT }
9716 { EBCDIC-ES csEBCDICES }
9717 { EBCDIC-ES-A csEBCDICESA }
9718 { EBCDIC-ES-S csEBCDICESS }
9719 { EBCDIC-UK csEBCDICUK }
9720 { EBCDIC-US csEBCDICUS }
9721 { UNKNOWN-8BIT csUnknown8BiT }
9722 { MNEMONIC csMnemonic }
9727 { IBM00858 CCSID00858 CP00858 PC-Multilingual-850+euro }
9728 { IBM00924 CCSID00924 CP00924 ebcdic-Latin9--euro }
9729 { IBM01140 CCSID01140 CP01140 ebcdic-us-37+euro }
9730 { IBM01141 CCSID01141 CP01141 ebcdic-de-273+euro }
9731 { IBM01142 CCSID01142 CP01142 ebcdic-dk-277+euro ebcdic-no-277+euro }
9732 { IBM01143 CCSID01143 CP01143 ebcdic-fi-278+euro ebcdic-se-278+euro }
9733 { IBM01144 CCSID01144 CP01144 ebcdic-it-280+euro }
9734 { IBM01145 CCSID01145 CP01145 ebcdic-es-284+euro }
9735 { IBM01146 CCSID01146 CP01146 ebcdic-gb-285+euro }
9736 { IBM01147 CCSID01147 CP01147 ebcdic-fr-297+euro }
9737 { IBM01148 CCSID01148 CP01148 ebcdic-international-500+euro }
9738 { IBM01149 CCSID01149 CP01149 ebcdic-is-871+euro }
9739 { IBM1047 IBM-1047 }
9740 { PTCP154 csPTCP154 PT154 CP154 Cyrillic-Asian }
9741 { Amiga-1251 Ami1251 Amiga1251 Ami-1251 }
9742 { UNICODE-1-1 csUnicode11 }
9745 { UNICODE-1-1-UTF-7 csUnicode11UTF7 }
9746 { ISO-8859-14 iso-ir-199 ISO_8859-14:1998 ISO_8859-14 latin8 iso-celtic
9748 { ISO-8859-15 ISO_8859-15 Latin-9 }
9749 { ISO-8859-16 iso-ir-226 ISO_8859-16:2001 ISO_8859-16 latin10 l10 }
9750 { GBK CP936 MS936 windows-936 }
9751 { JIS_Encoding csJISEncoding }
9752 { Shift_JIS MS_Kanji csShiftJIS ShiftJIS Shift-JIS }
9753 { Extended_UNIX_Code_Packed_Format_for_Japanese csEUCPkdFmtJapanese
9755 { Extended_UNIX_Code_Fixed_Width_for_Japanese csEUCFixWidJapanese }
9756 { ISO-10646-UCS-Basic csUnicodeASCII }
9757 { ISO-10646-Unicode-Latin1 csUnicodeLatin1 ISO-10646 }
9758 { ISO-Unicode-IBM-1261 csUnicodeIBM1261 }
9759 { ISO-Unicode-IBM-1268 csUnicodeIBM1268 }
9760 { ISO-Unicode-IBM-1276 csUnicodeIBM1276 }
9761 { ISO-Unicode-IBM-1264 csUnicodeIBM1264 }
9762 { ISO-Unicode-IBM-1265 csUnicodeIBM1265 }
9763 { ISO-8859-1-Windows-3.0-Latin-1 csWindows30Latin1 }
9764 { ISO-8859-1-Windows-3.1-Latin-1 csWindows31Latin1 }
9765 { ISO-8859-2-Windows-Latin-2 csWindows31Latin2 }
9766 { ISO-8859-9-Windows-Latin-5 csWindows31Latin5 }
9767 { Adobe-Standard-Encoding csAdobeStandardEncoding }
9768 { Ventura-US csVenturaUS }
9769 { Ventura-International csVenturaInternational }
9770 { PC8-Danish-Norwegian csPC8DanishNorwegian }
9771 { PC8-Turkish csPC8Turkish }
9772 { IBM-Symbols csIBMSymbols }
9773 { IBM-Thai csIBMThai }
9774 { HP-Legal csHPLegal }
9775 { HP-Pi-font csHPPiFont }
9776 { HP-Math8 csHPMath8 }
9777 { Adobe-Symbol-Encoding csHPPSMath }
9778 { HP-DeskTop csHPDesktop }
9779 { Ventura-Math csVenturaMath }
9780 { Microsoft-Publishing csMicrosoftPublishing }
9781 { Windows-31J csWindows31J }
9786 proc tcl_encoding {enc} {
9787 global encoding_aliases
9788 set names [encoding names]
9789 set lcnames [string tolower $names]
9790 set enc [string tolower $enc]
9791 set i [lsearch -exact $lcnames $enc]
9793 # look for "isonnn" instead of "iso-nnn" or "iso_nnn"
9794 if {[regsub {^(iso|cp|ibm|jis)[-_]} $enc {\1} encx]} {
9795 set i [lsearch -exact $lcnames $encx]
9799 foreach l $encoding_aliases {
9800 set ll [string tolower $l]
9801 if {[lsearch -exact $ll $enc] < 0} continue
9802 # look through the aliases for one that tcl knows about
9804 set i [lsearch -exact $lcnames $e]
9806 if {[regsub {^(iso|cp|ibm|jis)[-_]} $e {\1} ex]} {
9807 set i [lsearch -exact $lcnames $ex]
9816 return [lindex $names $i]
9821 proc gitattr {path attr default} {
9822 global path_attr_cache
9823 if {[info exists path_attr_cache($attr,$path)]} {
9824 set r $path_attr_cache($attr,$path)
9825 } elseif {[catch {set r [exec git check-attr $attr -- $path]}]} {
9828 set r [join [lrange [split $r :] 2 end] :]
9831 set path_attr_cache($attr,$path) $r
9832 if {$r eq {unspecified}} {
9838 proc cache_gitattr {attr pathlist} {
9839 global path_attr_cache
9841 foreach path $pathlist {
9842 if {[info exists path_attr_cache($attr,$path)]} continue
9843 lappend newlist $path
9845 while {$newlist ne {}} {
9846 set head [lrange $newlist 0 29]
9847 set newlist [lrange $newlist 30 end]
9848 if {![catch {set rlist [eval exec git check-attr $attr -- $head]}]} {
9849 foreach row [split $rlist "\n"] {
9850 set cols [split $row :]
9851 set path [lindex $cols 0]
9852 set value [join [lrange $cols 2 end] :]
9853 if {[string index $path 0] eq "\""} {
9854 set path [encoding convertfrom [lindex $path 0]]
9856 regsub {^ } $value {} value
9857 set path_attr_cache($attr,$path) $value
9864 proc get_path_encoding {path} {
9866 set tcl_enc [tcl_encoding $gui_encoding]
9867 if {$tcl_enc eq {}} {
9868 set tcl_enc [encoding system]
9871 set enc2 [tcl_encoding [gitattr $path encoding $tcl_enc]]
9879 # First check that Tcl/Tk is recent enough
9880 if {[catch {package require Tk 8.4} err]} {
9881 show_error {} . [mc "Sorry, gitk cannot run with this version of Tcl/Tk.\n\
9882 Gitk requires at least Tcl/Tk 8.4."]
9887 set wrcomcmd "git diff-tree --stdin -p --pretty"
9891 set gitencoding [exec git config --get i18n.commitencoding]
9893 if {$gitencoding == ""} {
9894 set gitencoding "utf-8"
9896 set tclencoding [tcl_encoding $gitencoding]
9897 if {$tclencoding == {}} {
9898 puts stderr "Warning: encoding $gitencoding is not supported by Tcl/Tk"
9901 set gui_encoding [encoding system]
9903 set gui_encoding [exec git config --get gui.encoding]
9906 set mainfont {Helvetica 9}
9907 set textfont {Courier 9}
9908 set uifont {Helvetica 9 bold}
9910 set findmergefiles 0
9918 set cmitmode "patch"
9919 set wrapcomment "none"
9923 set showlocalchanges 1
9925 set datetimeformat "%Y-%m-%d %H:%M:%S"
9928 set extdifftool "meld"
9930 set colors {green red blue magenta darkgrey brown orange}
9933 set diffcolors {red "#00a000" blue}
9936 set selectbgcolor gray85
9938 set circlecolors {white blue gray blue blue}
9940 # button for popping up context menus
9941 if {[tk windowingsystem] eq "aqua"} {
9942 set ctxbut <Button-2>
9944 set ctxbut <Button-3>
9947 ## For msgcat loading, first locate the installation location.
9948 if { [info exists ::env(GITK_MSGSDIR)] } {
9949 ## Msgsdir was manually set in the environment.
9950 set gitk_msgsdir $::env(GITK_MSGSDIR)
9952 ## Let's guess the prefix from argv0.
9953 set gitk_prefix [file dirname [file dirname [file normalize $argv0]]]
9954 set gitk_libdir [file join $gitk_prefix share gitk lib]
9955 set gitk_msgsdir [file join $gitk_libdir msgs]
9959 ## Internationalization (i18n) through msgcat and gettext. See
9960 ## http://www.gnu.org/software/gettext/manual/html_node/Tcl.html
9961 package require msgcat
9962 namespace import ::msgcat::mc
9963 ## And eventually load the actual message catalog
9964 ::msgcat::mcload $gitk_msgsdir
9966 catch {source ~/.gitk}
9968 font create optionfont -family sans-serif -size -12
9970 parsefont mainfont $mainfont
9971 eval font create mainfont [fontflags mainfont]
9972 eval font create mainfontbold [fontflags mainfont 1]
9974 parsefont textfont $textfont
9975 eval font create textfont [fontflags textfont]
9976 eval font create textfontbold [fontflags textfont 1]
9978 parsefont uifont $uifont
9979 eval font create uifont [fontflags uifont]
9983 # check that we can find a .git directory somewhere...
9984 if {[catch {set gitdir [gitdir]}]} {
9985 show_error {} . [mc "Cannot find a git repository here."]
9988 if {![file isdirectory $gitdir]} {
9989 show_error {} . [mc "Cannot find the git directory \"%s\"." $gitdir]
9997 set cmdline_files {}
9999 set revtreeargscmd {}
10000 foreach arg $argv {
10001 switch -glob -- $arg {
10004 set cmdline_files [lrange $argv [expr {$i + 1}] end]
10007 "--select-commit=*" {
10008 set selecthead [string range $arg 16 end]
10011 set revtreeargscmd [string range $arg 10 end]
10014 lappend revtreeargs $arg
10020 if {$selecthead eq "HEAD"} {
10024 if {$i >= [llength $argv] && $revtreeargs ne {}} {
10025 # no -- on command line, but some arguments (other than --argscmd)
10027 set f [eval exec git rev-parse --no-revs --no-flags $revtreeargs]
10028 set cmdline_files [split $f "\n"]
10029 set n [llength $cmdline_files]
10030 set revtreeargs [lrange $revtreeargs 0 end-$n]
10031 # Unfortunately git rev-parse doesn't produce an error when
10032 # something is both a revision and a filename. To be consistent
10033 # with git log and git rev-list, check revtreeargs for filenames.
10034 foreach arg $revtreeargs {
10035 if {[file exists $arg]} {
10036 show_error {} . [mc "Ambiguous argument '%s': both revision\
10037 and filename" $arg]
10042 # unfortunately we get both stdout and stderr in $err,
10043 # so look for "fatal:".
10044 set i [string first "fatal:" $err]
10046 set err [string range $err [expr {$i + 6}] end]
10048 show_error {} . "[mc "Bad arguments to gitk:"]\n$err"
10053 set nullid "0000000000000000000000000000000000000000"
10054 set nullid2 "0000000000000000000000000000000000000001"
10055 set nullfile "/dev/null"
10057 set have_tk85 [expr {[package vcompare $tk_version "8.5"] >= 0}]
10064 set highlight_paths {}
10066 set searchdirn -forwards
10068 set boldnamerows {}
10069 set diffelide {0 0}
10070 set markingmatches 0
10071 set linkentercount 0
10072 set need_redisplay 0
10079 set selectedhlview [mc "None"]
10080 set highlight_related [mc "None"]
10081 set highlight_files {}
10082 set viewfiles(0) {}
10085 set viewargscmd(0) {}
10087 set selectedline {}
10095 set isworktree [expr {[exec git rev-parse --is-inside-work-tree] == "true"}]
10098 # wait for the window to become visible
10099 tkwait visibility .
10100 wm title . "[file tail $argv0]: [file tail [pwd]]"
10103 if {$cmdline_files ne {} || $revtreeargs ne {} || $revtreeargscmd ne {}} {
10104 # create a view for the files/dirs specified on the command line
10108 set viewname(1) [mc "Command line"]
10109 set viewfiles(1) $cmdline_files
10110 set viewargs(1) $revtreeargs
10111 set viewargscmd(1) $revtreeargscmd
10115 .bar.view entryconf [mc "Edit view..."] -state normal
10116 .bar.view entryconf [mc "Delete view"] -state normal
10119 if {[info exists permviews]} {
10120 foreach v $permviews {
10123 set viewname($n) [lindex $v 0]
10124 set viewfiles($n) [lindex $v 1]
10125 set viewargs($n) [lindex $v 2]
10126 set viewargscmd($n) [lindex $v 3]