2 # Tcl ignores the next line -*- tcl -*- \
5 # Copyright © 2005-2016 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.
13 return [expr {[exec git rev-parse
--is-bare-repository] == "false" &&
14 [exec git rev-parse
--is-inside-git-dir] == "false"}]
19 set n
[file normalize
$gitdir]
20 if {[string match
"*/.git" $n]} {
21 set n
[string range
$n 0 end-5
]
28 if {[info exists _gitworktree
]} {
31 # v1.7.0 introduced --show-toplevel to return the canonical work-tree
32 if {[catch
{set _gitworktree
[exec git rev-parse
--show-toplevel]}]} {
33 # try to set work tree from environment, core.worktree or use
34 # cdup to obtain a relative path to the top of the worktree. If
35 # run from the top, the ./ prefix ensures normalize expands pwd.
36 if {[catch
{ set _gitworktree
$env(GIT_WORK_TREE
) }]} {
37 catch
{set _gitworktree
[exec git config
--get core.worktree
]}
38 if {$_gitworktree eq
""} {
39 set _gitworktree
[file normalize .
/[exec git rev-parse
--show-cdup]]
46 # A simple scheduler for compute-intensive stuff.
47 # The aim is to make sure that event handlers for GUI actions can
48 # run at least every 50-100 ms. Unfortunately fileevent handlers are
49 # run before X event handlers, so reading from a fast source can
50 # make the GUI completely unresponsive.
52 global isonrunq runq currunq
55 if {[info exists isonrunq
($script)]} return
56 if {$runq eq
{} && ![info exists currunq
]} {
59 lappend runq
[list
{} $script]
60 set isonrunq
($script) 1
63 proc filerun
{fd
script} {
64 fileevent
$fd readable
[list filereadable
$fd $script]
67 proc filereadable
{fd
script} {
70 fileevent
$fd readable
{}
71 if {$runq eq
{} && ![info exists currunq
]} {
74 lappend runq
[list
$fd $script]
80 for {set i
0} {$i < [llength
$runq]} {} {
81 if {[lindex
$runq $i 0] eq
$fd} {
82 set runq
[lreplace
$runq $i $i]
90 global isonrunq runq currunq
92 set tstart
[clock clicks
-milliseconds]
94 while {[llength
$runq] > 0} {
95 set fd
[lindex
$runq 0 0]
96 set script [lindex
$runq 0 1]
97 set currunq
[lindex
$runq 0]
98 set runq
[lrange
$runq 1 end
]
99 set repeat
[eval $script]
101 set t1
[clock clicks
-milliseconds]
102 set t
[expr {$t1 - $t0}]
103 if {$repeat ne
{} && $repeat} {
104 if {$fd eq
{} ||
$repeat == 2} {
105 # script returns 1 if it wants to be readded
106 # file readers return 2 if they could do more straight away
107 lappend runq
[list
$fd $script]
109 fileevent
$fd readable
[list filereadable
$fd $script]
111 } elseif
{$fd eq
{}} {
112 unset isonrunq
($script)
115 if {$t1 - $tstart >= 80} break
122 proc reg_instance
{fd
} {
123 global commfd leftover loginstance
125 set i
[incr loginstance
]
131 proc unmerged_files
{files
} {
134 # find the list of unmerged files
138 set fd
[open
"| git ls-files -u" r
]
140 show_error
{} .
"[mc "Couldn
't get list of unmerged files:"] $err"
143 while {[gets $fd line] >= 0} {
144 set i [string first "\t" $line]
146 set fname [string range $line [expr {$i+1}] end]
147 if {[lsearch -exact $mlist $fname] >= 0} continue
149 if {$files eq {} || [path_filter $files $fname]} {
157 proc parseviewargs {n arglist} {
158 global vdatemode vmergeonly vflags vdflags vrevs vfiltered vorigargs env
160 global worddiff git_version
164 set vinlinediff($n) 0
169 set origargs $arglist
173 foreach arg $arglist {
180 switch -glob -- $arg {
184 # remove from origargs in case we hit an unknown option
185 set origargs [lreplace $origargs $i $i]
189 "--no-renames" - "--full-index" - "--binary" - "--abbrev=*" -
190 "--find-copies-harder" - "-l*" - "--ext-diff" - "--no-ext-diff" -
191 "--src-prefix=*" - "--dst-prefix=*" - "--no-prefix" -
192 "-O*" - "--text" - "--full-diff" - "--ignore-space-at-eol" -
193 "--ignore-space-change" - "-U*" - "--unified=*" {
194 # These request or affect diff output, which we don't want.
195 # Some could be used to set our defaults for diff display.
196 lappend diffargs
$arg
198 "--raw" - "--patch-with-raw" - "--patch-with-stat" -
199 "--name-only" - "--name-status" - "--color" -
200 "--log-size" - "--pretty=*" - "--decorate" - "--abbrev-commit" -
201 "--cc" - "-z" - "--header" - "--parents" - "--boundary" -
202 "--no-color" - "-g" - "--walk-reflogs" - "--no-walk" -
203 "--timestamp" - "relative-date" - "--date=*" - "--stdin" -
204 "--objects" - "--objects-edge" - "--reverse" {
205 # These cause our parsing of git log's output to fail, or else
206 # they're options we want to set ourselves, so ignore them.
208 "--color-words*" - "--word-diff=color" {
209 # These trigger a word diff in the console interface,
210 # so help the user by enabling our own support
211 if {[package vcompare
$git_version "1.7.2"] >= 0} {
212 set worddiff
[mc
"Color words"]
216 if {[package vcompare
$git_version "1.7.2"] >= 0} {
217 set worddiff
[mc
"Markup words"]
220 "--stat=*" - "--numstat" - "--shortstat" - "--summary" -
221 "--check" - "--exit-code" - "--quiet" - "--topo-order" -
222 "--full-history" - "--dense" - "--sparse" -
223 "--follow" - "--left-right" - "--encoding=*" {
224 # These are harmless, and some are even useful
227 "--diff-filter=*" - "--no-merges" - "--unpacked" -
228 "--max-count=*" - "--skip=*" - "--since=*" - "--after=*" -
229 "--until=*" - "--before=*" - "--max-age=*" - "--min-age=*" -
230 "--author=*" - "--committer=*" - "--grep=*" - "-[iE]" -
231 "--remove-empty" - "--first-parent" - "--cherry-pick" -
232 "-S*" - "-G*" - "--pickaxe-all" - "--pickaxe-regex" -
233 "--simplify-by-decoration" {
234 # These mean that we get a subset of the commits
239 # Line-log with 'stuck' argument (unstuck form is
242 set vinlinediff
($n) 1
247 # This appears to be the only one that has a value as a
248 # separate word following it
258 # git rev-parse doesn't understand --merge
259 lappend revargs
--gitk-symmetric-diff-marker MERGE_HEAD...HEAD
261 "--no-replace-objects" {
262 set env
(GIT_NO_REPLACE_OBJECTS
) "1"
265 # Other flag arguments including -<n>
266 if {[string is digit
-strict [string range
$arg 1 end
]]} {
269 # a flag argument that we don't recognize;
270 # that means we can't optimize
276 # Non-flag arguments specify commits or ranges of commits
277 if {[string match
"*...*" $arg]} {
278 lappend revargs
--gitk-symmetric-diff-marker
284 set vdflags
($n) $diffargs
285 set vflags
($n) $glflags
286 set vrevs
($n) $revargs
287 set vfiltered
($n) $filtered
288 set vorigargs
($n) $origargs
292 proc parseviewrevs
{view revs
} {
293 global vposids vnegids
297 } elseif
{[lsearch
-exact $revs --all] >= 0} {
300 if {[catch
{set ids
[eval exec git rev-parse
$revs]} err
]} {
301 # we get stdout followed by stderr in $err
302 # for an unknown rev, git rev-parse echoes it and then errors out
303 set errlines
[split $err "\n"]
305 for {set l
0} {$l < [llength
$errlines]} {incr l
} {
306 set line
[lindex
$errlines $l]
307 if {!([string length
$line] == 40 && [string is xdigit
$line])} {
308 if {[string match
"fatal:*" $line]} {
309 if {[string match
"fatal: ambiguous argument*" $line]
311 if {[llength
$badrev] == 1} {
312 set err
"unknown revision $badrev"
314 set err
"unknown revisions: [join $badrev ", "]"
317 set err
[join [lrange
$errlines $l end
] "\n"]
324 error_popup
"[mc "Error parsing revisions
:"] $err"
331 foreach id
[split $ids "\n"] {
332 if {$id eq
"--gitk-symmetric-diff-marker"} {
334 } elseif
{[string match
"^*" $id]} {
341 lappend neg
[string range
$id 1 end
]
346 lset ret end
$id...
[lindex
$ret end
]
352 set vposids
($view) $pos
353 set vnegids
($view) $neg
357 # Start off a git log process and arrange to read its output
358 proc start_rev_list
{view
} {
359 global startmsecs commitidx viewcomplete curview
361 global viewargs viewargscmd viewfiles vfilelimit
362 global showlocalchanges
363 global viewactive viewinstances vmergeonly
364 global mainheadid viewmainheadid viewmainheadid_orig
365 global vcanopt vflags vrevs vorigargs
368 set startmsecs
[clock clicks
-milliseconds]
369 set commitidx
($view) 0
370 # these are set this way for the error exits
371 set viewcomplete
($view) 1
372 set viewactive
($view) 0
375 set args
$viewargs($view)
376 if {$viewargscmd($view) ne
{}} {
378 set str
[exec sh
-c $viewargscmd($view)]
380 error_popup
"[mc "Error executing
--argscmd command:"] $err"
383 set args
[concat
$args [split $str "\n"]]
385 set vcanopt
($view) [parseviewargs
$view $args]
387 set files
$viewfiles($view)
388 if {$vmergeonly($view)} {
389 set files
[unmerged_files
$files]
392 if {$nr_unmerged == 0} {
393 error_popup
[mc
"No files selected: --merge specified but\
394 no files are unmerged."]
396 error_popup
[mc
"No files selected: --merge specified but\
397 no unmerged files are within file limit."]
402 set vfilelimit
($view) $files
404 if {$vcanopt($view)} {
405 set revs
[parseviewrevs
$view $vrevs($view)]
409 set args
[concat
$vflags($view) $revs]
411 set args
$vorigargs($view)
415 set fd
[open
[concat | git log
--no-color -z --pretty=raw
$show_notes \
416 --parents --boundary $args "--" $files] r
]
418 error_popup
"[mc "Error executing git log
:"] $err"
421 set i
[reg_instance
$fd]
422 set viewinstances
($view) [list
$i]
423 set viewmainheadid
($view) $mainheadid
424 set viewmainheadid_orig
($view) $mainheadid
425 if {$files ne
{} && $mainheadid ne
{}} {
426 get_viewmainhead
$view
428 if {$showlocalchanges && $viewmainheadid($view) ne
{}} {
429 interestedin
$viewmainheadid($view) dodiffindex
431 fconfigure
$fd -blocking 0 -translation lf
-eofchar {}
432 if {$tclencoding != {}} {
433 fconfigure
$fd -encoding $tclencoding
435 filerun
$fd [list getcommitlines
$fd $i $view 0]
436 nowbusy
$view [mc
"Reading"]
437 set viewcomplete
($view) 0
438 set viewactive
($view) 1
442 proc stop_instance
{inst
} {
443 global commfd leftover
445 set fd
$commfd($inst)
449 if {$
::tcl_platform
(platform
) eq
{windows
}} {
450 exec taskkill
/pid
$pid
458 unset leftover
($inst)
461 proc stop_backends
{} {
464 foreach inst
[array names commfd
] {
469 proc stop_rev_list
{view
} {
472 foreach inst
$viewinstances($view) {
475 set viewinstances
($view) {}
478 proc reset_pending_select
{selid
} {
479 global pending_select mainheadid selectheadid
482 set pending_select
$selid
483 } elseif
{$selectheadid ne
{}} {
484 set pending_select
$selectheadid
486 set pending_select
$mainheadid
490 proc getcommits
{selid
} {
491 global canv curview need_redisplay viewactive
494 if {[start_rev_list
$curview]} {
495 reset_pending_select
$selid
496 show_status
[mc
"Reading commits..."]
499 show_status
[mc
"No commits selected"]
503 proc updatecommits
{} {
504 global curview vcanopt vorigargs vfilelimit viewinstances
505 global viewactive viewcomplete tclencoding
506 global startmsecs showneartags showlocalchanges
507 global mainheadid viewmainheadid viewmainheadid_orig pending_select
509 global varcid vposids vnegids vflags vrevs
512 set hasworktree
[hasworktree
]
515 if {$mainheadid ne
$viewmainheadid_orig($view)} {
516 if {$showlocalchanges} {
519 set viewmainheadid
($view) $mainheadid
520 set viewmainheadid_orig
($view) $mainheadid
521 if {$vfilelimit($view) ne
{}} {
522 get_viewmainhead
$view
525 if {$showlocalchanges} {
528 if {$vcanopt($view)} {
529 set oldpos
$vposids($view)
530 set oldneg
$vnegids($view)
531 set revs
[parseviewrevs
$view $vrevs($view)]
535 # note: getting the delta when negative refs change is hard,
536 # and could require multiple git log invocations, so in that
537 # case we ask git log for all the commits (not just the delta)
538 if {$oldneg eq
$vnegids($view)} {
541 # take out positive refs that we asked for before or
542 # that we have already seen
544 if {[string length
$rev] == 40} {
545 if {[lsearch
-exact $oldpos $rev] < 0
546 && ![info exists varcid
($view,$rev)]} {
551 lappend
$newrevs $rev
554 if {$npos == 0} return
556 set vposids
($view) [lsort
-unique [concat
$oldpos $vposids($view)]]
558 set args
[concat
$vflags($view) $revs --not $oldpos]
560 set args
$vorigargs($view)
563 set fd
[open
[concat | git log
--no-color -z --pretty=raw
$show_notes \
564 --parents --boundary $args "--" $vfilelimit($view)] r
]
566 error_popup
"[mc "Error executing git log
:"] $err"
569 if {$viewactive($view) == 0} {
570 set startmsecs
[clock clicks
-milliseconds]
572 set i
[reg_instance
$fd]
573 lappend viewinstances
($view) $i
574 fconfigure
$fd -blocking 0 -translation lf
-eofchar {}
575 if {$tclencoding != {}} {
576 fconfigure
$fd -encoding $tclencoding
578 filerun
$fd [list getcommitlines
$fd $i $view 1]
579 incr viewactive
($view)
580 set viewcomplete
($view) 0
581 reset_pending_select
{}
582 nowbusy
$view [mc
"Reading"]
588 proc reloadcommits
{} {
589 global curview viewcomplete selectedline currentid thickerline
590 global showneartags treediffs commitinterest cached_commitrow
591 global targetid commitinfo
594 if {$selectedline ne
{}} {
598 if {!$viewcomplete($curview)} {
599 stop_rev_list
$curview
603 unset -nocomplain currentid
604 unset -nocomplain thickerline
605 unset -nocomplain treediffs
612 unset -nocomplain commitinfo
613 unset -nocomplain commitinterest
614 unset -nocomplain cached_commitrow
615 unset -nocomplain targetid
621 # This makes a string representation of a positive integer which
622 # sorts as a string in numerical order
625 return [format
"%x" $n]
626 } elseif
{$n < 256} {
627 return [format
"x%.2x" $n]
628 } elseif
{$n < 65536} {
629 return [format
"y%.4x" $n]
631 return [format
"z%.8x" $n]
634 # Procedures used in reordering commits from git log (without
635 # --topo-order) into the order for display.
637 proc varcinit
{view
} {
638 global varcstart vupptr vdownptr vleftptr vbackptr varctok varcrow
639 global vtokmod varcmod vrowmod varcix vlastins
641 set varcstart
($view) {{}}
642 set vupptr
($view) {0}
643 set vdownptr
($view) {0}
644 set vleftptr
($view) {0}
645 set vbackptr
($view) {0}
646 set varctok
($view) {{}}
647 set varcrow
($view) {{}}
648 set vtokmod
($view) {}
651 set varcix
($view) {{}}
652 set vlastins
($view) {0}
655 proc resetvarcs
{view
} {
656 global varcid varccommits parents children vseedcount ordertok
659 foreach vid
[array names varcid
$view,*] {
664 foreach vid
[array names vshortids
$view,*] {
665 unset vshortids
($vid)
667 # some commits might have children but haven't been seen yet
668 foreach vid
[array names children
$view,*] {
671 foreach va
[array names varccommits
$view,*] {
672 unset varccommits
($va)
674 foreach vd
[array names vseedcount
$view,*] {
675 unset vseedcount
($vd)
677 unset -nocomplain ordertok
680 # returns a list of the commits with no children
682 global vdownptr vleftptr varcstart
685 set a
[lindex
$vdownptr($v) 0]
687 lappend ret
[lindex
$varcstart($v) $a]
688 set a
[lindex
$vleftptr($v) $a]
693 proc newvarc
{view id
} {
694 global varcid varctok parents children vdatemode
695 global vupptr vdownptr vleftptr vbackptr varcrow varcix varcstart
696 global commitdata commitinfo vseedcount varccommits vlastins
698 set a
[llength
$varctok($view)]
700 if {[llength
$children($vid)] == 0 ||
$vdatemode($view)} {
701 if {![info exists commitinfo
($id)]} {
702 parsecommit
$id $commitdata($id) 1
704 set cdate
[lindex
[lindex
$commitinfo($id) 4] 0]
705 if {![string is integer
-strict $cdate]} {
708 if {![info exists vseedcount
($view,$cdate)]} {
709 set vseedcount
($view,$cdate) -1
711 set c
[incr vseedcount
($view,$cdate)]
712 set cdate
[expr {$cdate ^
0xffffffff}]
713 set tok
"s[strrep $cdate][strrep $c]"
718 if {[llength
$children($vid)] > 0} {
719 set kid
[lindex
$children($vid) end
]
720 set k
$varcid($view,$kid)
721 if {[string compare
[lindex
$varctok($view) $k] $tok] > 0} {
724 set tok
[lindex
$varctok($view) $k]
728 set i
[lsearch
-exact $parents($view,$ki) $id]
729 set j
[expr {[llength
$parents($view,$ki)] - 1 - $i}]
730 append tok
[strrep
$j]
732 set c
[lindex
$vlastins($view) $ka]
733 if {$c == 0 ||
[string compare
$tok [lindex
$varctok($view) $c]] < 0} {
735 set b
[lindex
$vdownptr($view) $ka]
737 set b
[lindex
$vleftptr($view) $c]
739 while {$b != 0 && [string compare
$tok [lindex
$varctok($view) $b]] >= 0} {
741 set b
[lindex
$vleftptr($view) $c]
744 lset vdownptr
($view) $ka $a
745 lappend vbackptr
($view) 0
747 lset vleftptr
($view) $c $a
748 lappend vbackptr
($view) $c
750 lset vlastins
($view) $ka $a
751 lappend vupptr
($view) $ka
752 lappend vleftptr
($view) $b
754 lset vbackptr
($view) $b $a
756 lappend varctok
($view) $tok
757 lappend varcstart
($view) $id
758 lappend vdownptr
($view) 0
759 lappend varcrow
($view) {}
760 lappend varcix
($view) {}
761 set varccommits
($view,$a) {}
762 lappend vlastins
($view) 0
766 proc splitvarc
{p v
} {
767 global varcid varcstart varccommits varctok vtokmod
768 global vupptr vdownptr vleftptr vbackptr varcix varcrow vlastins
770 set oa
$varcid($v,$p)
771 set otok
[lindex
$varctok($v) $oa]
772 set ac
$varccommits($v,$oa)
773 set i
[lsearch
-exact $varccommits($v,$oa) $p]
775 set na
[llength
$varctok($v)]
776 # "%" sorts before "0"...
777 set tok
"$otok%[strrep $i]"
778 lappend varctok
($v) $tok
779 lappend varcrow
($v) {}
780 lappend varcix
($v) {}
781 set varccommits
($v,$oa) [lrange
$ac 0 [expr {$i - 1}]]
782 set varccommits
($v,$na) [lrange
$ac $i end
]
783 lappend varcstart
($v) $p
784 foreach id
$varccommits($v,$na) {
785 set varcid
($v,$id) $na
787 lappend vdownptr
($v) [lindex
$vdownptr($v) $oa]
788 lappend vlastins
($v) [lindex
$vlastins($v) $oa]
789 lset vdownptr
($v) $oa $na
790 lset vlastins
($v) $oa 0
791 lappend vupptr
($v) $oa
792 lappend vleftptr
($v) 0
793 lappend vbackptr
($v) 0
794 for {set b
[lindex
$vdownptr($v) $na]} {$b != 0} {set b
[lindex
$vleftptr($v) $b]} {
795 lset vupptr
($v) $b $na
797 if {[string compare
$otok $vtokmod($v)] <= 0} {
802 proc renumbervarc
{a v
} {
803 global parents children varctok varcstart varccommits
804 global vupptr vdownptr vleftptr vbackptr vlastins varcid vtokmod vdatemode
806 set t1
[clock clicks
-milliseconds]
812 if {[info exists isrelated
($a)]} {
814 set id
[lindex
$varccommits($v,$a) end
]
815 foreach p
$parents($v,$id) {
816 if {[info exists varcid
($v,$p)]} {
817 set isrelated
($varcid($v,$p)) 1
822 set b
[lindex
$vdownptr($v) $a]
825 set b
[lindex
$vleftptr($v) $a]
827 set a
[lindex
$vupptr($v) $a]
833 if {![info exists kidchanged
($a)]} continue
834 set id
[lindex
$varcstart($v) $a]
835 if {[llength
$children($v,$id)] > 1} {
836 set children
($v,$id) [lsort
-command [list vtokcmp
$v] \
839 set oldtok
[lindex
$varctok($v) $a]
840 if {!$vdatemode($v)} {
846 set kid
[last_real_child
$v,$id]
848 set k
$varcid($v,$kid)
849 if {[string compare
[lindex
$varctok($v) $k] $tok] > 0} {
852 set tok
[lindex
$varctok($v) $k]
856 set i
[lsearch
-exact $parents($v,$ki) $id]
857 set j
[expr {[llength
$parents($v,$ki)] - 1 - $i}]
858 append tok
[strrep
$j]
860 if {$tok eq
$oldtok} {
863 set id
[lindex
$varccommits($v,$a) end
]
864 foreach p
$parents($v,$id) {
865 if {[info exists varcid
($v,$p)]} {
866 set kidchanged
($varcid($v,$p)) 1
871 lset varctok
($v) $a $tok
872 set b
[lindex
$vupptr($v) $a]
874 if {[string compare
[lindex
$varctok($v) $ka] $vtokmod($v)] < 0} {
877 if {[string compare
[lindex
$varctok($v) $b] $vtokmod($v)] < 0} {
880 set c
[lindex
$vbackptr($v) $a]
881 set d
[lindex
$vleftptr($v) $a]
883 lset vdownptr
($v) $b $d
885 lset vleftptr
($v) $c $d
888 lset vbackptr
($v) $d $c
890 if {[lindex
$vlastins($v) $b] == $a} {
891 lset vlastins
($v) $b $c
893 lset vupptr
($v) $a $ka
894 set c
[lindex
$vlastins($v) $ka]
896 [string compare
$tok [lindex
$varctok($v) $c]] < 0} {
898 set b
[lindex
$vdownptr($v) $ka]
900 set b
[lindex
$vleftptr($v) $c]
903 [string compare
$tok [lindex
$varctok($v) $b]] >= 0} {
905 set b
[lindex
$vleftptr($v) $c]
908 lset vdownptr
($v) $ka $a
909 lset vbackptr
($v) $a 0
911 lset vleftptr
($v) $c $a
912 lset vbackptr
($v) $a $c
914 lset vleftptr
($v) $a $b
916 lset vbackptr
($v) $b $a
918 lset vlastins
($v) $ka $a
921 foreach id
[array names sortkids
] {
922 if {[llength
$children($v,$id)] > 1} {
923 set children
($v,$id) [lsort
-command [list vtokcmp
$v] \
927 set t2
[clock clicks
-milliseconds]
928 #puts "renumbervarc did [llength $todo] of $ntot arcs in [expr {$t2-$t1}]ms"
931 # Fix up the graph after we have found out that in view $v,
932 # $p (a commit that we have already seen) is actually the parent
933 # of the last commit in arc $a.
934 proc fix_reversal
{p a v
} {
935 global varcid varcstart varctok vupptr
937 set pa
$varcid($v,$p)
938 if {$p ne
[lindex
$varcstart($v) $pa]} {
940 set pa
$varcid($v,$p)
942 # seeds always need to be renumbered
943 if {[lindex
$vupptr($v) $pa] == 0 ||
944 [string compare
[lindex
$varctok($v) $a] \
945 [lindex
$varctok($v) $pa]] > 0} {
950 proc insertrow
{id p v
} {
951 global cmitlisted children parents varcid varctok vtokmod
952 global varccommits ordertok commitidx numcommits curview
953 global targetid targetrow vshortids
957 set cmitlisted
($vid) 1
958 set children
($vid) {}
959 set parents
($vid) [list
$p]
960 set a
[newvarc
$v $id]
962 lappend vshortids
($v,[string range
$id 0 3]) $id
963 if {[string compare
[lindex
$varctok($v) $a] $vtokmod($v)] < 0} {
966 lappend varccommits
($v,$a) $id
968 if {[llength
[lappend children
($vp) $id]] > 1} {
969 set children
($vp) [lsort
-command [list vtokcmp
$v] $children($vp)]
970 unset -nocomplain ordertok
972 fix_reversal
$p $a $v
974 if {$v == $curview} {
975 set numcommits
$commitidx($v)
977 if {[info exists targetid
]} {
978 if {![comes_before
$targetid $p]} {
985 proc insertfakerow
{id p
} {
986 global varcid varccommits parents children cmitlisted
987 global commitidx varctok vtokmod targetid targetrow curview numcommits
991 set i
[lsearch
-exact $varccommits($v,$a) $p]
993 puts
"oops: insertfakerow can't find [shortids $p] on arc $a"
996 set children
($v,$id) {}
997 set parents
($v,$id) [list
$p]
998 set varcid
($v,$id) $a
999 lappend children
($v,$p) $id
1000 set cmitlisted
($v,$id) 1
1001 set numcommits
[incr commitidx
($v)]
1002 # note we deliberately don't update varcstart($v) even if $i == 0
1003 set varccommits
($v,$a) [linsert
$varccommits($v,$a) $i $id]
1005 if {[info exists targetid
]} {
1006 if {![comes_before
$targetid $p]} {
1014 proc removefakerow
{id
} {
1015 global varcid varccommits parents children commitidx
1016 global varctok vtokmod cmitlisted currentid selectedline
1017 global targetid curview numcommits
1020 if {[llength
$parents($v,$id)] != 1} {
1021 puts
"oops: removefakerow [shortids $id] has [llength $parents($v,$id)] parents"
1024 set p
[lindex
$parents($v,$id) 0]
1025 set a
$varcid($v,$id)
1026 set i
[lsearch
-exact $varccommits($v,$a) $id]
1028 puts
"oops: removefakerow can't find [shortids $id] on arc $a"
1031 unset varcid
($v,$id)
1032 set varccommits
($v,$a) [lreplace
$varccommits($v,$a) $i $i]
1033 unset parents
($v,$id)
1034 unset children
($v,$id)
1035 unset cmitlisted
($v,$id)
1036 set numcommits
[incr commitidx
($v) -1]
1037 set j
[lsearch
-exact $children($v,$p) $id]
1039 set children
($v,$p) [lreplace
$children($v,$p) $j $j]
1042 if {[info exist currentid
] && $id eq
$currentid} {
1046 if {[info exists targetid
] && $targetid eq
$id} {
1053 proc real_children
{vp
} {
1054 global children nullid nullid2
1057 foreach id
$children($vp) {
1058 if {$id ne
$nullid && $id ne
$nullid2} {
1065 proc first_real_child
{vp
} {
1066 global children nullid nullid2
1068 foreach id
$children($vp) {
1069 if {$id ne
$nullid && $id ne
$nullid2} {
1076 proc last_real_child
{vp
} {
1077 global children nullid nullid2
1079 set kids
$children($vp)
1080 for {set i
[llength
$kids]} {[incr i
-1] >= 0} {} {
1081 set id
[lindex
$kids $i]
1082 if {$id ne
$nullid && $id ne
$nullid2} {
1089 proc vtokcmp
{v a b
} {
1090 global varctok varcid
1092 return [string compare
[lindex
$varctok($v) $varcid($v,$a)] \
1093 [lindex
$varctok($v) $varcid($v,$b)]]
1096 # This assumes that if lim is not given, the caller has checked that
1097 # arc a's token is less than $vtokmod($v)
1098 proc modify_arc
{v a
{lim
{}}} {
1099 global varctok vtokmod varcmod varcrow vupptr curview vrowmod varccommits
1102 set c
[string compare
[lindex
$varctok($v) $a] $vtokmod($v)]
1105 set r
[lindex
$varcrow($v) $a]
1106 if {$r ne
{} && $vrowmod($v) <= $r + $lim} return
1109 set vtokmod
($v) [lindex
$varctok($v) $a]
1111 if {$v == $curview} {
1112 while {$a != 0 && [lindex
$varcrow($v) $a] eq
{}} {
1113 set a
[lindex
$vupptr($v) $a]
1119 set lim
[llength
$varccommits($v,$a)]
1121 set r
[expr {[lindex
$varcrow($v) $a] + $lim}]
1128 proc update_arcrows
{v
} {
1129 global vtokmod varcmod vrowmod varcrow commitidx currentid selectedline
1130 global varcid vrownum varcorder varcix varccommits
1131 global vupptr vdownptr vleftptr varctok
1132 global displayorder parentlist curview cached_commitrow
1134 if {$vrowmod($v) == $commitidx($v)} return
1135 if {$v == $curview} {
1136 if {[llength
$displayorder] > $vrowmod($v)} {
1137 set displayorder
[lrange
$displayorder 0 [expr {$vrowmod($v) - 1}]]
1138 set parentlist
[lrange
$parentlist 0 [expr {$vrowmod($v) - 1}]]
1140 unset -nocomplain cached_commitrow
1142 set narctot
[expr {[llength
$varctok($v)] - 1}]
1144 while {$a != 0 && [lindex
$varcix($v) $a] eq
{}} {
1145 # go up the tree until we find something that has a row number,
1146 # or we get to a seed
1147 set a
[lindex
$vupptr($v) $a]
1150 set a
[lindex
$vdownptr($v) 0]
1153 set varcorder
($v) [list
$a]
1154 lset varcix
($v) $a 0
1155 lset varcrow
($v) $a 0
1159 set arcn
[lindex
$varcix($v) $a]
1160 if {[llength
$vrownum($v)] > $arcn + 1} {
1161 set vrownum
($v) [lrange
$vrownum($v) 0 $arcn]
1162 set varcorder
($v) [lrange
$varcorder($v) 0 $arcn]
1164 set row
[lindex
$varcrow($v) $a]
1168 incr row
[llength
$varccommits($v,$a)]
1169 # go down if possible
1170 set b
[lindex
$vdownptr($v) $a]
1172 # if not, go left, or go up until we can go left
1174 set b
[lindex
$vleftptr($v) $a]
1176 set a
[lindex
$vupptr($v) $a]
1182 lappend vrownum
($v) $row
1183 lappend varcorder
($v) $a
1184 lset varcix
($v) $a $arcn
1185 lset varcrow
($v) $a $row
1187 set vtokmod
($v) [lindex
$varctok($v) $p]
1189 set vrowmod
($v) $row
1190 if {[info exists currentid
]} {
1191 set selectedline
[rowofcommit
$currentid]
1195 # Test whether view $v contains commit $id
1196 proc commitinview
{id v
} {
1199 return [info exists varcid
($v,$id)]
1202 # Return the row number for commit $id in the current view
1203 proc rowofcommit
{id
} {
1204 global varcid varccommits varcrow curview cached_commitrow
1205 global varctok vtokmod
1208 if {![info exists varcid
($v,$id)]} {
1209 puts
"oops rowofcommit no arc for [shortids $id]"
1212 set a
$varcid($v,$id)
1213 if {[string compare
[lindex
$varctok($v) $a] $vtokmod($v)] >= 0} {
1216 if {[info exists cached_commitrow
($id)]} {
1217 return $cached_commitrow($id)
1219 set i
[lsearch
-exact $varccommits($v,$a) $id]
1221 puts
"oops didn't find commit [shortids $id] in arc $a"
1224 incr i
[lindex
$varcrow($v) $a]
1225 set cached_commitrow
($id) $i
1229 # Returns 1 if a is on an earlier row than b, otherwise 0
1230 proc comes_before
{a b
} {
1231 global varcid varctok curview
1234 if {$a eq
$b ||
![info exists varcid
($v,$a)] || \
1235 ![info exists varcid
($v,$b)]} {
1238 if {$varcid($v,$a) != $varcid($v,$b)} {
1239 return [expr {[string compare
[lindex
$varctok($v) $varcid($v,$a)] \
1240 [lindex
$varctok($v) $varcid($v,$b)]] < 0}]
1242 return [expr {[rowofcommit
$a] < [rowofcommit
$b]}]
1245 proc bsearch
{l elt
} {
1246 if {[llength
$l] == 0 ||
$elt <= [lindex
$l 0]} {
1251 while {$hi - $lo > 1} {
1252 set mid
[expr {int
(($lo + $hi) / 2)}]
1253 set t
[lindex
$l $mid]
1256 } elseif
{$elt > $t} {
1265 # Make sure rows $start..$end-1 are valid in displayorder and parentlist
1266 proc make_disporder
{start end
} {
1267 global vrownum curview commitidx displayorder parentlist
1268 global varccommits varcorder parents vrowmod varcrow
1269 global d_valid_start d_valid_end
1271 if {$end > $vrowmod($curview)} {
1272 update_arcrows
$curview
1274 set ai
[bsearch
$vrownum($curview) $start]
1275 set start
[lindex
$vrownum($curview) $ai]
1276 set narc
[llength
$vrownum($curview)]
1277 for {set r
$start} {$ai < $narc && $r < $end} {incr ai
} {
1278 set a
[lindex
$varcorder($curview) $ai]
1279 set l
[llength
$displayorder]
1280 set al
[llength
$varccommits($curview,$a)]
1281 if {$l < $r + $al} {
1283 set pad
[ntimes
[expr {$r - $l}] {}]
1284 set displayorder
[concat
$displayorder $pad]
1285 set parentlist
[concat
$parentlist $pad]
1286 } elseif
{$l > $r} {
1287 set displayorder
[lrange
$displayorder 0 [expr {$r - 1}]]
1288 set parentlist
[lrange
$parentlist 0 [expr {$r - 1}]]
1290 foreach id
$varccommits($curview,$a) {
1291 lappend displayorder
$id
1292 lappend parentlist
$parents($curview,$id)
1294 } elseif
{[lindex
$displayorder [expr {$r + $al - 1}]] eq
{}} {
1296 foreach id
$varccommits($curview,$a) {
1297 lset displayorder
$i $id
1298 lset parentlist
$i $parents($curview,$id)
1306 proc commitonrow
{row
} {
1309 set id
[lindex
$displayorder $row]
1311 make_disporder
$row [expr {$row + 1}]
1312 set id
[lindex
$displayorder $row]
1317 proc closevarcs
{v
} {
1318 global varctok varccommits varcid parents children
1319 global cmitlisted commitidx vtokmod curview numcommits
1321 set missing_parents
0
1323 set narcs
[llength
$varctok($v)]
1324 for {set a
1} {$a < $narcs} {incr a
} {
1325 set id
[lindex
$varccommits($v,$a) end
]
1326 foreach p
$parents($v,$id) {
1327 if {[info exists varcid
($v,$p)]} continue
1328 # add p as a new commit
1329 incr missing_parents
1330 set cmitlisted
($v,$p) 0
1331 set parents
($v,$p) {}
1332 if {[llength
$children($v,$p)] == 1 &&
1333 [llength
$parents($v,$id)] == 1} {
1336 set b
[newvarc
$v $p]
1338 set varcid
($v,$p) $b
1339 if {[string compare
[lindex
$varctok($v) $b] $vtokmod($v)] < 0} {
1342 lappend varccommits
($v,$b) $p
1344 if {$v == $curview} {
1345 set numcommits
$commitidx($v)
1347 set scripts
[check_interest
$p $scripts]
1350 if {$missing_parents > 0} {
1351 foreach s
$scripts {
1357 # Use $rwid as a substitute for $id, i.e. reparent $id's children to $rwid
1358 # Assumes we already have an arc for $rwid.
1359 proc rewrite_commit
{v id rwid
} {
1360 global children parents varcid varctok vtokmod varccommits
1362 foreach ch
$children($v,$id) {
1363 # make $rwid be $ch's parent in place of $id
1364 set i
[lsearch
-exact $parents($v,$ch) $id]
1366 puts
"oops rewrite_commit didn't find $id in parent list for $ch"
1368 set parents
($v,$ch) [lreplace
$parents($v,$ch) $i $i $rwid]
1369 # add $ch to $rwid's children and sort the list if necessary
1370 if {[llength
[lappend children
($v,$rwid) $ch]] > 1} {
1371 set children
($v,$rwid) [lsort
-command [list vtokcmp
$v] \
1372 $children($v,$rwid)]
1374 # fix the graph after joining $id to $rwid
1375 set a
$varcid($v,$ch)
1376 fix_reversal
$rwid $a $v
1377 # parentlist is wrong for the last element of arc $a
1378 # even if displayorder is right, hence the 3rd arg here
1379 modify_arc
$v $a [expr {[llength
$varccommits($v,$a)] - 1}]
1383 # Mechanism for registering a command to be executed when we come
1384 # across a particular commit. To handle the case when only the
1385 # prefix of the commit is known, the commitinterest array is now
1386 # indexed by the first 4 characters of the ID. Each element is a
1387 # list of id, cmd pairs.
1388 proc interestedin
{id cmd
} {
1389 global commitinterest
1391 lappend commitinterest
([string range
$id 0 3]) $id $cmd
1394 proc check_interest
{id scripts
} {
1395 global commitinterest
1397 set prefix
[string range
$id 0 3]
1398 if {[info exists commitinterest
($prefix)]} {
1400 foreach
{i
script} $commitinterest($prefix) {
1401 if {[string match
"$i*" $id]} {
1402 lappend scripts
[string map
[list
"%I" $id "%P" $i] $script]
1404 lappend newlist
$i $script
1407 if {$newlist ne
{}} {
1408 set commitinterest
($prefix) $newlist
1410 unset commitinterest
($prefix)
1416 proc getcommitlines
{fd inst view updating
} {
1417 global cmitlisted leftover
1418 global commitidx commitdata vdatemode
1419 global parents children curview hlview
1420 global idpending ordertok
1421 global varccommits varcid varctok vtokmod vfilelimit vshortids
1423 set stuff
[read $fd 500000]
1424 # git log doesn't terminate the last commit with a null...
1425 if {$stuff == {} && $leftover($inst) ne
{} && [eof
$fd]} {
1432 global commfd viewcomplete viewactive viewname
1433 global viewinstances
1435 set i
[lsearch
-exact $viewinstances($view) $inst]
1437 set viewinstances
($view) [lreplace
$viewinstances($view) $i $i]
1439 # set it blocking so we wait for the process to terminate
1440 fconfigure
$fd -blocking 1
1441 if {[catch
{close
$fd} err
]} {
1443 if {$view != $curview} {
1444 set fv
" for the \"$viewname($view)\" view"
1446 if {[string range
$err 0 4] == "usage"} {
1447 set err
"Gitk: error reading commits$fv:\
1448 bad arguments to git log."
1449 if {$viewname($view) eq
[mc
"Command line"]} {
1451 " (Note: arguments to gitk are passed to git log\
1452 to allow selection of commits to be displayed.)"
1455 set err
"Error reading commits$fv: $err"
1459 if {[incr viewactive
($view) -1] <= 0} {
1460 set viewcomplete
($view) 1
1461 # Check if we have seen any ids listed as parents that haven't
1462 # appeared in the list
1466 if {$view == $curview} {
1475 set i
[string first
"\0" $stuff $start]
1477 append leftover
($inst) [string range
$stuff $start end
]
1481 set cmit
$leftover($inst)
1482 append cmit
[string range
$stuff 0 [expr {$i - 1}]]
1483 set leftover
($inst) {}
1485 set cmit
[string range
$stuff $start [expr {$i - 1}]]
1487 set start
[expr {$i + 1}]
1488 set j
[string first
"\n" $cmit]
1491 if {$j >= 0 && [string match
"commit *" $cmit]} {
1492 set ids
[string range
$cmit 7 [expr {$j - 1}]]
1493 if {[string match
{[-^
<>]*} $ids]} {
1494 switch
-- [string index
$ids 0] {
1500 set ids
[string range
$ids 1 end
]
1504 if {[string length
$id] != 40} {
1512 if {[string length
$shortcmit] > 80} {
1513 set shortcmit
"[string range $shortcmit 0 80]..."
1515 error_popup
"[mc "Can
't parse git log output:"] {$shortcmit}"
1518 set id [lindex $ids 0]
1521 lappend vshortids($view,[string range $id 0 3]) $id
1523 if {!$listed && $updating && ![info exists varcid($vid)] &&
1524 $vfilelimit($view) ne {}} {
1525 # git log doesn't rewrite parents
for unlisted commits
1526 # when doing path limiting, so work around that here
1527 # by working out the rewritten parent with git rev-list
1528 # and if we already know about it, using the rewritten
1529 # parent as a substitute parent for $id's children.
1531 set rwid
[exec git rev-list
--first-parent --max-count=1 \
1532 $id -- $vfilelimit($view)]
1534 if {$rwid ne
{} && [info exists varcid
($view,$rwid)]} {
1535 # use $rwid in place of $id
1536 rewrite_commit
$view $id $rwid
1543 if {[info exists varcid
($vid)]} {
1544 if {$cmitlisted($vid) ||
!$listed} continue
1548 set olds
[lrange
$ids 1 end
]
1552 set commitdata
($id) [string range
$cmit [expr {$j + 1}] end
]
1553 set cmitlisted
($vid) $listed
1554 set parents
($vid) $olds
1555 if {![info exists children
($vid)]} {
1556 set children
($vid) {}
1557 } elseif
{$a == 0 && [llength
$children($vid)] == 1} {
1558 set k
[lindex
$children($vid) 0]
1559 if {[llength
$parents($view,$k)] == 1 &&
1560 (!$vdatemode($view) ||
1561 $varcid($view,$k) == [llength
$varctok($view)] - 1)} {
1562 set a
$varcid($view,$k)
1567 set a
[newvarc
$view $id]
1569 if {[string compare
[lindex
$varctok($view) $a] $vtokmod($view)] < 0} {
1572 if {![info exists varcid
($vid)]} {
1574 lappend varccommits
($view,$a) $id
1575 incr commitidx
($view)
1580 if {$i == 0 ||
[lsearch
-exact $olds $p] >= $i} {
1582 if {[llength
[lappend children
($vp) $id]] > 1 &&
1583 [vtokcmp
$view [lindex
$children($vp) end-1
] $id] > 0} {
1584 set children
($vp) [lsort
-command [list vtokcmp
$view] \
1586 unset -nocomplain ordertok
1588 if {[info exists varcid
($view,$p)]} {
1589 fix_reversal
$p $a $view
1595 set scripts
[check_interest
$id $scripts]
1599 global numcommits hlview
1601 if {$view == $curview} {
1602 set numcommits
$commitidx($view)
1605 if {[info exists hlview
] && $view == $hlview} {
1606 # we never actually get here...
1609 foreach s
$scripts {
1616 proc chewcommits
{} {
1617 global curview hlview viewcomplete
1618 global pending_select
1621 if {$viewcomplete($curview)} {
1622 global commitidx varctok
1623 global numcommits startmsecs
1625 if {[info exists pending_select
]} {
1627 reset_pending_select
{}
1629 if {[commitinview
$pending_select $curview]} {
1630 selectline
[rowofcommit
$pending_select] 1
1632 set row
[first_real_row
]
1636 if {$commitidx($curview) > 0} {
1637 #set ms [expr {[clock clicks -milliseconds] - $startmsecs}]
1638 #puts "overall $ms ms for $numcommits commits"
1639 #puts "[llength $varctok($view)] arcs, $commitidx($view) commits"
1641 show_status
[mc
"No commits selected"]
1648 proc do_readcommit
{id
} {
1651 # Invoke git-log to handle automatic encoding conversion
1652 set fd
[open
[concat | git log
--no-color --pretty=raw
-1 $id] r
]
1653 # Read the results using i18n.logoutputencoding
1654 fconfigure
$fd -translation lf
-eofchar {}
1655 if {$tclencoding != {}} {
1656 fconfigure
$fd -encoding $tclencoding
1658 set contents
[read $fd]
1660 # Remove the heading line
1661 regsub
{^commit
[0-9a-f]+\n} $contents {} contents
1666 proc readcommit
{id
} {
1667 if {[catch
{set contents
[do_readcommit
$id]}]} return
1668 parsecommit
$id $contents 1
1671 proc parsecommit
{id contents listed
} {
1681 set hdrend
[string first
"\n\n" $contents]
1683 # should never happen...
1684 set hdrend
[string length
$contents]
1686 set header
[string range
$contents 0 [expr {$hdrend - 1}]]
1687 set comment
[string range
$contents [expr {$hdrend + 2}] end
]
1688 foreach line
[split $header "\n"] {
1689 set line
[split $line " "]
1690 set tag
[lindex
$line 0]
1691 if {$tag == "author"} {
1692 set audate
[lrange
$line end-1 end
]
1693 set auname
[join [lrange
$line 1 end-2
] " "]
1694 } elseif
{$tag == "committer"} {
1695 set comdate
[lrange
$line end-1 end
]
1696 set comname
[join [lrange
$line 1 end-2
] " "]
1700 # take the first non-blank line of the comment as the headline
1701 set headline
[string trimleft
$comment]
1702 set i
[string first
"\n" $headline]
1704 set headline
[string range
$headline 0 $i]
1706 set headline
[string trimright
$headline]
1707 set i
[string first
"\r" $headline]
1709 set headline
[string trimright
[string range
$headline 0 $i]]
1712 # git log indents the comment by 4 spaces;
1713 # if we got this via git cat-file, add the indentation
1715 foreach line
[split $comment "\n"] {
1716 append newcomment
" "
1717 append newcomment
$line
1718 append newcomment
"\n"
1720 set comment
$newcomment
1722 set hasnote
[string first
"\nNotes:\n" $contents]
1724 # If there is diff output shown in the git-log stream, split it
1725 # out. But get rid of the empty line that always precedes the
1727 set i
[string first
"\n\ndiff" $comment]
1729 set diff [string range
$comment $i+1 end
]
1730 set comment
[string range
$comment 0 $i-1]
1732 set commitinfo
($id) [list
$headline $auname $audate \
1733 $comname $comdate $comment $hasnote $diff]
1736 proc getcommit
{id
} {
1737 global commitdata commitinfo
1739 if {[info exists commitdata
($id)]} {
1740 parsecommit
$id $commitdata($id) 1
1743 if {![info exists commitinfo
($id)]} {
1744 set commitinfo
($id) [list
[mc
"No commit information available"]]
1750 # Expand an abbreviated commit ID to a list of full 40-char IDs that match
1751 # and are present in the current view.
1752 # This is fairly slow...
1753 proc longid
{prefix
} {
1754 global varcid curview vshortids
1757 if {[string length
$prefix] >= 4} {
1758 set vshortid
$curview,[string range
$prefix 0 3]
1759 if {[info exists vshortids
($vshortid)]} {
1760 foreach id
$vshortids($vshortid) {
1761 if {[string match
"$prefix*" $id]} {
1762 if {[lsearch
-exact $ids $id] < 0} {
1764 if {[llength
$ids] >= 2} break
1770 foreach match
[array names varcid
"$curview,$prefix*"] {
1771 lappend ids
[lindex
[split $match ","] 1]
1772 if {[llength
$ids] >= 2} break
1779 global tagids idtags headids idheads tagobjid
1780 global otherrefids idotherrefs mainhead mainheadid
1781 global selecthead selectheadid
1784 foreach v
{tagids idtags headids idheads otherrefids idotherrefs
} {
1785 unset -nocomplain $v
1787 set refd
[open
[list | git show-ref
-d] r
]
1788 while {[gets
$refd line
] >= 0} {
1789 if {[string index
$line 40] ne
" "} continue
1790 set id
[string range
$line 0 39]
1791 set ref
[string range
$line 41 end
]
1792 if {![string match
"refs/*" $ref]} continue
1793 set name
[string range
$ref 5 end
]
1794 if {[string match
"remotes/*" $name]} {
1795 if {![string match
"*/HEAD" $name] && !$hideremotes} {
1796 set headids
($name) $id
1797 lappend idheads
($id) $name
1799 } elseif
{[string match
"heads/*" $name]} {
1800 set name
[string range
$name 6 end
]
1801 set headids
($name) $id
1802 lappend idheads
($id) $name
1803 } elseif
{[string match
"tags/*" $name]} {
1804 # this lets refs/tags/foo^{} overwrite refs/tags/foo,
1805 # which is what we want since the former is the commit ID
1806 set name
[string range
$name 5 end
]
1807 if {[string match
"*^{}" $name]} {
1808 set name
[string range
$name 0 end-3
]
1810 set tagobjid
($name) $id
1812 set tagids
($name) $id
1813 lappend idtags
($id) $name
1815 set otherrefids
($name) $id
1816 lappend idotherrefs
($id) $name
1823 set mainheadid
[exec git rev-parse HEAD
]
1824 set thehead
[exec git symbolic-ref HEAD
]
1825 if {[string match
"refs/heads/*" $thehead]} {
1826 set mainhead
[string range
$thehead 11 end
]
1830 if {$selecthead ne
{}} {
1832 set selectheadid
[exec git rev-parse
--verify $selecthead]
1837 # skip over fake commits
1838 proc first_real_row
{} {
1839 global nullid nullid2 numcommits
1841 for {set row
0} {$row < $numcommits} {incr row
} {
1842 set id
[commitonrow
$row]
1843 if {$id ne
$nullid && $id ne
$nullid2} {
1850 # update things for a head moved to a child of its previous location
1851 proc movehead
{id name
} {
1852 global headids idheads
1854 removehead
$headids($name) $name
1855 set headids
($name) $id
1856 lappend idheads
($id) $name
1859 # update things when a head has been removed
1860 proc removehead
{id name
} {
1861 global headids idheads
1863 if {$idheads($id) eq
$name} {
1866 set i
[lsearch
-exact $idheads($id) $name]
1868 set idheads
($id) [lreplace
$idheads($id) $i $i]
1871 unset headids
($name)
1874 proc ttk_toplevel
{w args
} {
1876 eval [linsert
$args 0 ::toplevel
$w]
1878 place
[ttk
::frame
$w._toplevel_background
] -x 0 -y 0 -relwidth 1 -relheight 1
1883 proc make_transient
{window origin
} {
1886 # In MacOS Tk 8.4 transient appears to work by setting
1887 # overrideredirect, which is utterly useless, since the
1888 # windows get no border, and are not even kept above
1890 if {!$have_tk85 && [tk windowingsystem
] eq
{aqua
}} return
1892 wm transient
$window $origin
1894 # Windows fails to place transient windows normally, so
1895 # schedule a callback to center them on the parent.
1896 if {[tk windowingsystem
] eq
{win32
}} {
1897 after idle
[list tk
::PlaceWindow
$window widget
$origin]
1901 proc show_error
{w top msg
} {
1903 if {![info exists NS
]} {set NS
""}
1904 if {[wm state
$top] eq
"withdrawn"} { wm deiconify
$top }
1905 message
$w.m
-text $msg -justify center
-aspect 400
1906 pack
$w.m
-side top
-fill x
-padx 20 -pady 20
1907 ${NS}::button
$w.ok
-default active
-text [mc OK
] -command "destroy $top"
1908 pack
$w.ok
-side bottom
-fill x
1909 bind $top <Visibility
> "grab $top; focus $top"
1910 bind $top <Key-Return
> "destroy $top"
1911 bind $top <Key-space
> "destroy $top"
1912 bind $top <Key-Escape
> "destroy $top"
1916 proc error_popup
{msg
{owner .
}} {
1917 if {[tk windowingsystem
] eq
"win32"} {
1918 tk_messageBox
-icon error
-type ok
-title [wm title .
] \
1919 -parent $owner -message $msg
1923 make_transient
$w $owner
1924 show_error
$w $w $msg
1928 proc confirm_popup
{msg
{owner .
}} {
1929 global confirm_ok NS
1933 make_transient
$w $owner
1934 message
$w.m
-text $msg -justify center
-aspect 400
1935 pack
$w.m
-side top
-fill x
-padx 20 -pady 20
1936 ${NS}::button
$w.ok
-text [mc OK
] -command "set confirm_ok 1; destroy $w"
1937 pack
$w.ok
-side left
-fill x
1938 ${NS}::button
$w.cancel
-text [mc Cancel
] -command "destroy $w"
1939 pack
$w.cancel
-side right
-fill x
1940 bind $w <Visibility
> "grab $w; focus $w"
1941 bind $w <Key-Return
> "set confirm_ok 1; destroy $w"
1942 bind $w <Key-space
> "set confirm_ok 1; destroy $w"
1943 bind $w <Key-Escape
> "destroy $w"
1944 tk
::PlaceWindow
$w widget
$owner
1949 proc setoptions
{} {
1952 if {[tk windowingsystem
] ne
"win32"} {
1953 option add
*Panedwindow.showHandle
1 startupFile
1954 option add
*Panedwindow.sashRelief raised startupFile
1955 if {[tk windowingsystem
] ne
"aqua"} {
1956 option add
*Menu.font uifont startupFile
1959 option add
*Menu.TearOff
0 startupFile
1961 option add
*Button.font uifont startupFile
1962 option add
*Checkbutton.font uifont startupFile
1963 option add
*Radiobutton.font uifont startupFile
1964 option add
*Menubutton.font uifont startupFile
1965 option add
*Label.font uifont startupFile
1966 option add
*Message.font uifont startupFile
1967 option add
*Entry.font textfont startupFile
1968 option add
*Text.font textfont startupFile
1969 option add
*Labelframe.font uifont startupFile
1970 option add
*Spinbox.font textfont startupFile
1971 option add
*Listbox.font mainfont startupFile
1974 proc setttkstyle
{} {
1975 eval font configure TkDefaultFont
[fontflags mainfont
]
1976 eval font configure TkTextFont
[fontflags textfont
]
1977 eval font configure TkHeadingFont
[fontflags mainfont
]
1978 eval font configure TkCaptionFont
[fontflags mainfont
] -weight bold
1979 eval font configure TkTooltipFont
[fontflags uifont
]
1980 eval font configure TkFixedFont
[fontflags textfont
]
1981 eval font configure TkIconFont
[fontflags uifont
]
1982 eval font configure TkMenuFont
[fontflags uifont
]
1983 eval font configure TkSmallCaptionFont
[fontflags uifont
]
1986 # Make a menu and submenus.
1987 # m is the window name for the menu, items is the list of menu items to add.
1988 # Each item is a list {mc label type description options...}
1989 # mc is ignored; it's so we can put mc there to alert xgettext
1990 # label is the string that appears in the menu
1991 # type is cascade, command or radiobutton (should add checkbutton)
1992 # description depends on type; it's the sublist for cascade, the
1993 # command to invoke for command, or {variable value} for radiobutton
1994 proc makemenu
{m items
} {
1996 if {[tk windowingsystem
] eq
{aqua
}} {
2002 set name
[mc
[lindex
$i 1]]
2003 set type [lindex
$i 2]
2004 set thing
[lindex
$i 3]
2005 set params
[list
$type]
2007 set u
[string first
"&" [string map
{&& x
} $name]]
2008 lappend params
-label [string map
{&& & & {}} $name]
2010 lappend params
-underline $u
2015 set submenu
[string tolower
[string map
{& ""} [lindex
$i 1]]]
2016 lappend params
-menu $m.
$submenu
2019 lappend params
-command $thing
2022 lappend params
-variable [lindex
$thing 0] \
2023 -value [lindex
$thing 1]
2026 set tail [lrange
$i 4 end
]
2027 regsub
-all {\yMeta1\y
} $tail $Meta1 tail
2028 eval $m add
$params $tail
2029 if {$type eq
"cascade"} {
2030 makemenu
$m.
$submenu $thing
2035 # translate string and remove ampersands
2037 return [string map
{&& & & {}} [mc
$str]]
2040 proc cleardropsel
{w
} {
2043 proc makedroplist
{w varname args
} {
2047 foreach label
$args {
2048 set cx
[string length
$label]
2049 if {$cx > $width} {set width
$cx}
2051 set gm
[ttk
::combobox
$w -width $width -state readonly\
2052 -textvariable $varname -values $args \
2053 -exportselection false
]
2054 bind $gm <<ComboboxSelected>> [list $gm selection clear]
2056 set gm [eval [linsert $args 0 tk_optionMenu $w $varname]]
2061 proc makewindow {} {
2062 global canv canv2 canv3 linespc charspc ctext cflist cscroll
2064 global findtype findtypemenu findloc findstring fstring geometry
2065 global entries sha1entry sha1string sha1but
2066 global diffcontextstring diffcontext
2068 global maincursor textcursor curtextcursor
2069 global rowctxmenu fakerowmenu mergemax wrapcomment
2070 global highlight_files gdttype
2071 global searchstring sstring
2072 global bgcolor fgcolor bglist fglist diffcolors selectbgcolor
2073 global uifgcolor uifgdisabledcolor
2074 global filesepbgcolor filesepfgcolor
2075 global mergecolors foundbgcolor currentsearchhitbgcolor
2076 global headctxmenu progresscanv progressitem progresscoords statusw
2077 global fprogitem fprogcoord lastprogupdate progupdatepending
2078 global rprogitem rprogcoord rownumsel numcommits
2079 global have_tk85 use_ttk NS
2083 # The "mc" arguments here are purely so that xgettext
2084 # sees the following string as needing to be translated
2086 mc "&File" cascade {
2087 {mc "&Update" command updatecommits -accelerator F5}
2088 {mc "&Reload" command reloadcommits -accelerator Shift-F5}
2089 {mc "Reread re&ferences" command rereadrefs}
2090 {mc "&List references" command showrefs -accelerator F2}
2092 {mc "Start git &gui" command {exec git gui &}}
2094 {mc "&Quit" command doquit -accelerator Meta1-Q}
2097 mc "&Edit" cascade {
2098 {mc "&Preferences" command doprefs}
2101 mc "&View" cascade {
2102 {mc "&New view..." command {newview 0} -accelerator Shift-F4}
2103 {mc "&Edit view..." command editview -state disabled -accelerator F4}
2104 {mc "&Delete view" command delview -state disabled}
2106 {mc "&All files" radiobutton {selectedview 0} -command {showview 0}}
2108 if {[tk windowingsystem] ne "aqua"} {
2110 mc "&Help" cascade {
2111 {mc "&About gitk" command about}
2112 {mc "&Key bindings" command keys}
2114 set bar [list $file $edit $view $help]
2116 proc ::tk::mac::ShowPreferences {} {doprefs}
2117 proc ::tk::mac::Quit {} {doquit}
2118 lset file end [lreplace [lindex $file end] end-1 end]
2120 xx "&Apple" cascade {
2121 {mc "&About gitk" command about}
2125 mc "&Help" cascade {
2126 {mc "&Key bindings" command keys}
2128 set bar [list $apple $file $view $help]
2131 . configure -menu .bar
2134 # cover the non-themed toplevel with a themed frame.
2135 place [ttk::frame ._main_background] -x 0 -y 0 -relwidth 1 -relheight 1
2138 # the gui has upper and lower half, parts of a paned window.
2139 ${NS}::panedwindow .ctop -orient vertical
2141 # possibly use assumed geometry
2142 if {![info exists geometry(pwsash0)]} {
2143 set geometry(topheight) [expr {15 * $linespc}]
2144 set geometry(topwidth) [expr {80 * $charspc}]
2145 set geometry(botheight) [expr {15 * $linespc}]
2146 set geometry(botwidth) [expr {50 * $charspc}]
2147 set geometry(pwsash0) [list [expr {40 * $charspc}] 2]
2148 set geometry(pwsash1) [list [expr {60 * $charspc}] 2]
2151 # the upper half will have a paned window, a scroll bar to the right, and some stuff below
2152 ${NS}::frame .tf -height $geometry(topheight) -width $geometry(topwidth)
2153 ${NS}::frame .tf.histframe
2154 ${NS}::panedwindow .tf.histframe.pwclist -orient horizontal
2156 .tf.histframe.pwclist configure -sashpad 0 -handlesize 4
2159 # create three canvases
2160 set cscroll .tf.histframe.csb
2161 set canv .tf.histframe.pwclist.canv
2163 -selectbackground $selectbgcolor \
2164 -background $bgcolor -bd 0 \
2165 -yscrollincr $linespc -yscrollcommand "scrollcanv $cscroll"
2166 .tf.histframe.pwclist add $canv
2167 set canv2 .tf.histframe.pwclist.canv2
2169 -selectbackground $selectbgcolor \
2170 -background $bgcolor -bd 0 -yscrollincr $linespc
2171 .tf.histframe.pwclist add $canv2
2172 set canv3 .tf.histframe.pwclist.canv3
2174 -selectbackground $selectbgcolor \
2175 -background $bgcolor -bd 0 -yscrollincr $linespc
2176 .tf.histframe.pwclist add $canv3
2178 bind .tf.histframe.pwclist <Map> {
2180 .tf.histframe.pwclist sashpos 1 [lindex $::geometry(pwsash1) 0]
2181 .tf.histframe.pwclist sashpos 0 [lindex $::geometry(pwsash0) 0]
2184 eval .tf.histframe.pwclist sash place 0 $geometry(pwsash0)
2185 eval .tf.histframe.pwclist sash place 1 $geometry(pwsash1)
2188 # a scroll bar to rule them
2189 ${NS}::scrollbar $cscroll -command {allcanvs yview}
2190 if {!$use_ttk} {$cscroll configure -highlightthickness 0}
2191 pack $cscroll -side right -fill y
2192 bind .tf.histframe.pwclist <Configure> {resizeclistpanes %W %w}
2193 lappend bglist $canv $canv2 $canv3
2194 pack .tf.histframe.pwclist -fill both -expand 1 -side left
2196 # we have two button bars at bottom of top frame. Bar 1
2197 ${NS}::frame .tf.bar
2198 ${NS}::frame .tf.lbar -height 15
2200 set sha1entry .tf.bar.sha1
2201 set entries $sha1entry
2202 set sha1but .tf.bar.sha1label
2203 button $sha1but -text "[mc "SHA1 ID:"] " -state disabled -relief flat \
2204 -command gotocommit -width 8
2205 $sha1but conf -disabledforeground [$sha1but cget -foreground]
2206 pack .tf.bar.sha1label -side left
2207 ${NS}::entry $sha1entry -width 40 -font textfont -textvariable sha1string
2208 trace add variable sha1string write sha1change
2209 pack $sha1entry -side left -pady 2
2212 #define left_width 16
2213 #define left_height 16
2214 static unsigned char left_bits[] = {
2215 0x00, 0x00, 0xc0, 0x01, 0xe0, 0x00, 0x70, 0x00, 0x38, 0x00, 0x1c, 0x00,
2216 0x0e, 0x00, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0x0e, 0x00, 0x1c, 0x00,
2217 0x38, 0x00, 0x70, 0x00, 0xe0, 0x00, 0xc0, 0x01};
2220 #define right_width 16
2221 #define right_height 16
2222 static unsigned char right_bits[] = {
2223 0x00, 0x00, 0xc0, 0x01, 0x80, 0x03, 0x00, 0x07, 0x00, 0x0e, 0x00, 0x1c,
2224 0x00, 0x38, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0x00, 0x38, 0x00, 0x1c,
2225 0x00, 0x0e, 0x00, 0x07, 0x80, 0x03, 0xc0, 0x01};
2227 image create bitmap bm-left -data $bm_left_data -foreground $uifgcolor
2228 image create bitmap bm-left-gray -data $bm_left_data -foreground $uifgdisabledcolor
2229 image create bitmap bm-right -data $bm_right_data -foreground $uifgcolor
2230 image create bitmap bm-right-gray -data $bm_right_data -foreground $uifgdisabledcolor
2232 ${NS}::button .tf.bar.leftbut -command goback -state disabled -width 26
2234 .tf.bar.leftbut configure -image [list bm-left disabled bm-left-gray]
2236 .tf.bar.leftbut configure -image bm-left
2238 pack .tf.bar.leftbut -side left -fill y
2239 ${NS}::button .tf.bar.rightbut -command goforw -state disabled -width 26
2241 .tf.bar.rightbut configure -image [list bm-right disabled bm-right-gray]
2243 .tf.bar.rightbut configure -image bm-right
2245 pack .tf.bar.rightbut -side left -fill y
2247 ${NS}::label .tf.bar.rowlabel -text [mc "Row"]
2249 ${NS}::label .tf.bar.rownum -width 7 -textvariable rownumsel \
2250 -relief sunken -anchor e
2251 ${NS}::label .tf.bar.rowlabel2 -text "/"
2252 ${NS}::label .tf.bar.numcommits -width 7 -textvariable numcommits \
2253 -relief sunken -anchor e
2254 pack .tf.bar.rowlabel .tf.bar.rownum .tf.bar.rowlabel2 .tf.bar.numcommits \
2257 foreach w {rownum numcommits} {.tf.bar.$w configure -font textfont}
2260 trace add variable selectedline write selectedline_change
2262 # Status label and progress bar
2263 set statusw .tf.bar.status
2264 ${NS}::label $statusw -width 15 -relief sunken
2265 pack $statusw -side left -padx 5
2267 set progresscanv [ttk::progressbar .tf.bar.progress]
2269 set h [expr {[font metrics uifont -linespace] + 2}]
2270 set progresscanv .tf.bar.progress
2271 canvas $progresscanv -relief sunken -height $h -borderwidth 2
2272 set progressitem [$progresscanv create rect -1 0 0 $h -fill "#00ff00"]
2273 set fprogitem [$progresscanv create rect -1 0 0 $h -fill yellow]
2274 set rprogitem [$progresscanv create rect -1 0 0 $h -fill red]
2276 pack $progresscanv -side right -expand 1 -fill x -padx {0 2}
2277 set progresscoords {0 0}
2280 bind $progresscanv <Configure> adjustprogress
2281 set lastprogupdate [clock clicks -milliseconds]
2282 set progupdatepending 0
2284 # build up the bottom bar of upper window
2285 ${NS}::label .tf.lbar.flabel -text "[mc "Find"] "
2288 #define down_width 16
2289 #define down_height 16
2290 static unsigned char down_bits[] = {
2291 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01,
2292 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01,
2293 0x87, 0xe1, 0x8e, 0x71, 0x9c, 0x39, 0xb8, 0x1d,
2294 0xf0, 0x0f, 0xe0, 0x07, 0xc0, 0x03, 0x80, 0x01};
2296 image create bitmap bm-down -data $bm_down_data -foreground $uifgcolor
2297 ${NS}::button .tf.lbar.fnext -width 26 -command {dofind 1 1}
2298 .tf.lbar.fnext configure -image bm-down
2302 #define up_height 16
2303 static unsigned char up_bits[] = {
2304 0x80, 0x01, 0xc0, 0x03, 0xe0, 0x07, 0xf0, 0x0f,
2305 0xb8, 0x1d, 0x9c, 0x39, 0x8e, 0x71, 0x87, 0xe1,
2306 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01,
2307 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01};
2309 image create bitmap bm-up -data $bm_up_data -foreground $uifgcolor
2310 ${NS}::button .tf.lbar.fprev -width 26 -command {dofind -1 1}
2311 .tf.lbar.fprev configure -image bm-up
2313 ${NS}::label .tf.lbar.flab2 -text " [mc "commit"] "
2315 pack .tf.lbar.flabel .tf.lbar.fnext .tf.lbar.fprev .tf.lbar.flab2 \
2317 set gdttype [mc "containing:"]
2318 set gm [makedroplist .tf.lbar.gdttype gdttype \
2319 [mc "containing:"] \
2320 [mc "touching paths:"] \
2321 [mc "adding/removing string:"] \
2322 [mc "changing lines matching:"]]
2323 trace add variable gdttype write gdttype_change
2324 pack .tf.lbar.gdttype -side left -fill y
2327 set fstring .tf.lbar.findstring
2328 lappend entries $fstring
2329 ${NS}::entry $fstring -width 30 -textvariable findstring
2330 trace add variable findstring write find_change
2331 set findtype [mc "Exact"]
2332 set findtypemenu [makedroplist .tf.lbar.findtype \
2333 findtype [mc "Exact"] [mc "IgnCase"] [mc "Regexp"]]
2334 trace add variable findtype write findcom_change
2335 set findloc [mc "All fields"]
2336 makedroplist .tf.lbar.findloc findloc [mc "All fields"] [mc "Headline"] \
2337 [mc "Comments"] [mc "Author"] [mc "Committer"]
2338 trace add variable findloc write find_change
2339 pack .tf.lbar.findloc -side right
2340 pack .tf.lbar.findtype -side right
2341 pack $fstring -side left -expand 1 -fill x
2343 # Finish putting the upper half of the viewer together
2344 pack .tf.lbar -in .tf -side bottom -fill x
2345 pack .tf.bar -in .tf -side bottom -fill x
2346 pack .tf.histframe -fill both -side top -expand 1
2349 .ctop paneconfigure .tf -height $geometry(topheight)
2350 .ctop paneconfigure .tf -width $geometry(topwidth)
2353 # now build up the bottom
2354 ${NS}::panedwindow .pwbottom -orient horizontal
2356 # lower left, a text box over search bar, scroll bar to the right
2357 # if we know window height, then that will set the lower text height, otherwise
2358 # we set lower text height which will drive window height
2359 if {[info exists geometry(main)]} {
2360 ${NS}::frame .bleft -width $geometry(botwidth)
2362 ${NS}::frame .bleft -width $geometry(botwidth) -height $geometry(botheight)
2364 ${NS}::frame .bleft.top
2365 ${NS}::frame .bleft.mid
2366 ${NS}::frame .bleft.bottom
2368 # gap between sub-widgets
2369 set wgap [font measure uifont "i"]
2371 ${NS}::button .bleft.top.search -text [mc "Search"] -command dosearch
2372 pack .bleft.top.search -side left -padx 5
2373 set sstring .bleft.top.sstring
2375 ${NS}::entry $sstring -width 20 -textvariable searchstring
2376 lappend entries $sstring
2377 trace add variable searchstring write incrsearch
2378 pack $sstring -side left -expand 1 -fill x
2379 ${NS}::radiobutton .bleft.mid.diff -text [mc "Diff"] \
2380 -command changediffdisp -variable diffelide -value {0 0}
2381 ${NS}::radiobutton .bleft.mid.old -text [mc "Old version"] \
2382 -command changediffdisp -variable diffelide -value {0 1}
2383 ${NS}::radiobutton .bleft.mid.new -text [mc "New version"] \
2384 -command changediffdisp -variable diffelide -value {1 0}
2386 ${NS}::label .bleft.mid.labeldiffcontext -text " [mc "Lines of context"]: "
2387 pack .bleft.mid.diff .bleft.mid.old .bleft.mid.new -side left -ipadx $wgap
2388 spinbox .bleft.mid.diffcontext -width 5 \
2389 -from 0 -increment 1 -to 10000000 \
2390 -validate all -validatecommand "diffcontextvalidate %P" \
2391 -textvariable diffcontextstring
2392 .bleft.mid.diffcontext set $diffcontext
2393 trace add variable diffcontextstring write diffcontextchange
2394 lappend entries .bleft.mid.diffcontext
2395 pack .bleft.mid.labeldiffcontext .bleft.mid.diffcontext -side left -ipadx $wgap
2396 ${NS}::checkbutton .bleft.mid.ignspace -text [mc "Ignore space change"] \
2397 -command changeignorespace -variable ignorespace
2398 pack .bleft.mid.ignspace -side left -padx 5
2400 set worddiff [mc "Line diff"]
2401 if {[package vcompare $git_version "1.7.2"] >= 0} {
2402 makedroplist .bleft.mid.worddiff worddiff [mc "Line diff"] \
2403 [mc "Markup words"] [mc "Color words"]
2404 trace add variable worddiff write changeworddiff
2405 pack .bleft.mid.worddiff -side left -padx 5
2408 set ctext .bleft.bottom.ctext
2409 text $ctext -background $bgcolor -foreground $fgcolor \
2410 -state disabled -undo 0 -font textfont \
2411 -yscrollcommand scrolltext -wrap none \
2412 -xscrollcommand ".bleft.bottom.sbhorizontal set"
2414 $ctext conf -tabstyle wordprocessor
2416 ${NS}::scrollbar .bleft.bottom.sb -command "$ctext yview"
2417 ${NS}::scrollbar .bleft.bottom.sbhorizontal -command "$ctext xview" -orient h
2418 pack .bleft.top -side top -fill x
2419 pack .bleft.mid -side top -fill x
2420 grid $ctext .bleft.bottom.sb -sticky nsew
2421 grid .bleft.bottom.sbhorizontal -sticky ew
2422 grid columnconfigure .bleft.bottom 0 -weight 1
2423 grid rowconfigure .bleft.bottom 0 -weight 1
2424 grid rowconfigure .bleft.bottom 1 -weight 0
2425 pack .bleft.bottom -side top -fill both -expand 1
2426 lappend bglist $ctext
2427 lappend fglist $ctext
2429 $ctext tag conf comment -wrap $wrapcomment
2430 $ctext tag conf filesep -font textfontbold -fore $filesepfgcolor -back $filesepbgcolor
2431 $ctext tag conf hunksep -fore [lindex $diffcolors 2]
2432 $ctext tag conf d0 -fore [lindex $diffcolors 0]
2433 $ctext tag conf dresult -fore [lindex $diffcolors 1]
2434 $ctext tag conf m0 -fore [lindex $mergecolors 0]
2435 $ctext tag conf m1 -fore [lindex $mergecolors 1]
2436 $ctext tag conf m2 -fore [lindex $mergecolors 2]
2437 $ctext tag conf m3 -fore [lindex $mergecolors 3]
2438 $ctext tag conf m4 -fore [lindex $mergecolors 4]
2439 $ctext tag conf m5 -fore [lindex $mergecolors 5]
2440 $ctext tag conf m6 -fore [lindex $mergecolors 6]
2441 $ctext tag conf m7 -fore [lindex $mergecolors 7]
2442 $ctext tag conf m8 -fore [lindex $mergecolors 8]
2443 $ctext tag conf m9 -fore [lindex $mergecolors 9]
2444 $ctext tag conf m10 -fore [lindex $mergecolors 10]
2445 $ctext tag conf m11 -fore [lindex $mergecolors 11]
2446 $ctext tag conf m12 -fore [lindex $mergecolors 12]
2447 $ctext tag conf m13 -fore [lindex $mergecolors 13]
2448 $ctext tag conf m14 -fore [lindex $mergecolors 14]
2449 $ctext tag conf m15 -fore [lindex $mergecolors 15]
2450 $ctext tag conf mmax -fore darkgrey
2452 $ctext tag conf mresult -font textfontbold
2453 $ctext tag conf msep -font textfontbold
2454 $ctext tag conf found -back $foundbgcolor
2455 $ctext tag conf currentsearchhit -back $currentsearchhitbgcolor
2456 $ctext tag conf wwrap -wrap word -lmargin2 1c
2457 $ctext tag conf bold -font textfontbold
2459 .pwbottom add .bleft
2461 .pwbottom paneconfigure .bleft -width $geometry(botwidth)
2465 ${NS}::frame .bright
2466 ${NS}::frame .bright.mode
2467 ${NS}::radiobutton .bright.mode.patch -text [mc "Patch"] \
2468 -command reselectline -variable cmitmode -value "patch"
2469 ${NS}::radiobutton .bright.mode.tree -text [mc "Tree"] \
2470 -command reselectline -variable cmitmode -value "tree"
2471 grid .bright.mode.patch .bright.mode.tree -sticky ew
2472 pack .bright.mode -side top -fill x
2473 set cflist .bright.cfiles
2474 set indent [font measure mainfont "nn"]
2476 -selectbackground $selectbgcolor \
2477 -background $bgcolor -foreground $fgcolor \
2479 -tabs [list $indent [expr {2 * $indent}]] \
2480 -yscrollcommand ".bright.sb set" \
2481 -cursor [. cget -cursor] \
2482 -spacing1 1 -spacing3 1
2483 lappend bglist $cflist
2484 lappend fglist $cflist
2485 ${NS}::scrollbar .bright.sb -command "$cflist yview"
2486 pack .bright.sb -side right -fill y
2487 pack $cflist -side left -fill both -expand 1
2488 $cflist tag configure highlight \
2489 -background [$cflist cget -selectbackground]
2490 $cflist tag configure bold -font mainfontbold
2492 .pwbottom add .bright
2495 # restore window width & height if known
2496 if {[info exists geometry(main)]} {
2497 if {[scan $geometry(main) "%dx%d" w h] >= 2} {
2498 if {$w > [winfo screenwidth .]} {
2499 set w [winfo screenwidth .]
2501 if {$h > [winfo screenheight .]} {
2502 set h [winfo screenheight .]
2504 wm geometry . "${w}x$h"
2508 if {[info exists geometry(state)] && $geometry(state) eq "zoomed"} {
2509 wm state . $geometry(state)
2512 if {[tk windowingsystem] eq {aqua}} {
2523 %W sashpos 0 $::geometry(topheight)
2525 bind .pwbottom <Map> {
2527 %W sashpos 0 $::geometry(botwidth)
2531 bind .pwbottom <Configure> {resizecdetpanes %W %w}
2532 pack .ctop -fill both -expand 1
2533 bindall <1> {selcanvline %W %x %y}
2534 #bindall <B1-Motion> {selcanvline %W %x %y}
2535 if {[tk windowingsystem] == "win32"} {
2536 bind . <MouseWheel> { windows_mousewheel_redirector %W %X %Y %D }
2537 bind $ctext <MouseWheel> { windows_mousewheel_redirector %W %X %Y %D ; break }
2539 bindall <ButtonRelease-4> "allcanvs yview scroll -5 units"
2540 bindall <ButtonRelease-5> "allcanvs yview scroll 5 units"
2541 bind $ctext <Button> {
2543 $ctext xview scroll -5 units
2544 } elseif {"%b" eq 7} {
2545 $ctext xview scroll 5 units
2548 if {[tk windowingsystem] eq "aqua"} {
2549 bindall <MouseWheel> {
2550 set delta [expr {- (%D)}]
2551 allcanvs yview scroll $delta units
2553 bindall <Shift-MouseWheel> {
2554 set delta [expr {- (%D)}]
2555 $canv xview scroll $delta units
2559 bindall <$::BM> "canvscan mark %W %x %y"
2560 bindall <B$::BM-Motion> "canvscan dragto %W %x %y"
2561 bind all <$M1B-Key-w> {destroy [winfo toplevel %W]}
2562 bind . <$M1B-Key-w> doquit
2563 bindkey <Home> selfirstline
2564 bindkey <End> sellastline
2565 bind . <Key-Up> "selnextline -1"
2566 bind . <Key-Down> "selnextline 1"
2567 bind . <Shift-Key-Up> "dofind -1 0"
2568 bind . <Shift-Key-Down> "dofind 1 0"
2569 bindkey <Key-Right> "goforw"
2570 bindkey <Key-Left> "goback"
2571 bind . <Key-Prior> "selnextpage -1"
2572 bind . <Key-Next> "selnextpage 1"
2573 bind . <$M1B-Home> "allcanvs yview moveto 0.0"
2574 bind . <$M1B-End> "allcanvs yview moveto 1.0"
2575 bind . <$M1B-Key-Up> "allcanvs yview scroll -1 units"
2576 bind . <$M1B-Key-Down> "allcanvs yview scroll 1 units"
2577 bind . <$M1B-Key-Prior> "allcanvs yview scroll -1 pages"
2578 bind . <$M1B-Key-Next> "allcanvs yview scroll 1 pages"
2579 bindkey <Key-Delete> "$ctext yview scroll -1 pages"
2580 bindkey <Key-BackSpace> "$ctext yview scroll -1 pages"
2581 bindkey <Key-space> "$ctext yview scroll 1 pages"
2582 bindkey p "selnextline -1"
2583 bindkey n "selnextline 1"
2586 bindkey k "selnextline -1"
2587 bindkey j "selnextline 1"
2591 bindkey d "$ctext yview scroll 18 units"
2592 bindkey u "$ctext yview scroll -18 units"
2593 bindkey g {$sha1entry delete 0 end; focus $sha1entry}
2594 bindkey / {focus $fstring}
2595 bindkey <Key-KP_Divide> {focus $fstring}
2596 bindkey <Key-Return> {dofind 1 1}
2597 bindkey ? {dofind -1 1}
2599 bind . <F5> updatecommits
2600 bindmodfunctionkey Shift 5 reloadcommits
2601 bind . <F2> showrefs
2602 bindmodfunctionkey Shift 4 {newview 0}
2603 bind . <F4> edit_or_newview
2604 bind . <$M1B-q> doquit
2605 bind . <$M1B-f> {dofind 1 1}
2606 bind . <$M1B-g> {dofind 1 0}
2607 bind . <$M1B-r> dosearchback
2608 bind . <$M1B-s> dosearch
2609 bind . <$M1B-equal> {incrfont 1}
2610 bind . <$M1B-plus> {incrfont 1}
2611 bind . <$M1B-KP_Add> {incrfont 1}
2612 bind . <$M1B-minus> {incrfont -1}
2613 bind . <$M1B-KP_Subtract> {incrfont -1}
2614 wm protocol . WM_DELETE_WINDOW doquit
2615 bind . <Destroy> {stop_backends}
2616 bind . <Button-1> "click %W"
2617 bind $fstring <Key-Return> {dofind 1 1}
2618 bind $sha1entry <Key-Return> {gotocommit; break}
2619 bind $sha1entry <<PasteSelection>> clearsha1
2620 bind $sha1entry <<Paste>> clearsha1
2621 bind $cflist <1> {sel_flist %W %x %y; break}
2622 bind $cflist <B1-Motion> {sel_flist %W %x %y; break}
2623 bind $cflist <ButtonRelease-1> {treeclick %W %x %y}
2625 bind $cflist $ctxbut {pop_flist_menu %W %X %Y %x %y}
2626 bind $ctext $ctxbut {pop_diff_menu %W %X %Y %x %y}
2627 bind $ctext <Button-1> {focus %W}
2628 bind $ctext <<Selection>> rehighlight_search_results
2629 for {set i 1} {$i < 10} {incr i} {
2630 bind . <$M1B-Key-$i> [list go_to_parent $i]
2633 set maincursor [. cget -cursor]
2634 set textcursor [$ctext cget -cursor]
2635 set curtextcursor $textcursor
2637 set rowctxmenu .rowctxmenu
2638 makemenu $rowctxmenu {
2639 {mc "Diff this -> selected" command {diffvssel 0}}
2640 {mc "Diff selected -> this" command {diffvssel 1}}
2641 {mc "Make patch" command mkpatch}
2642 {mc "Create tag" command mktag}
2643 {mc "Copy commit summary" command copysummary}
2644 {mc "Write commit to file" command writecommit}
2645 {mc "Create new branch" command mkbranch}
2646 {mc "Cherry-pick this commit" command cherrypick}
2647 {mc "Reset HEAD branch to here" command resethead}
2648 {mc "Mark this commit" command markhere}
2649 {mc "Return to mark" command gotomark}
2650 {mc "Find descendant of this and mark" command find_common_desc}
2651 {mc "Compare with marked commit" command compare_commits}
2652 {mc "Diff this -> marked commit" command {diffvsmark 0}}
2653 {mc "Diff marked commit -> this" command {diffvsmark 1}}
2654 {mc "Revert this commit" command revert}
2656 $rowctxmenu configure -tearoff 0
2658 set fakerowmenu .fakerowmenu
2659 makemenu $fakerowmenu {
2660 {mc "Diff this -> selected" command {diffvssel 0}}
2661 {mc "Diff selected -> this" command {diffvssel 1}}
2662 {mc "Make patch" command mkpatch}
2663 {mc "Diff this -> marked commit" command {diffvsmark 0}}
2664 {mc "Diff marked commit -> this" command {diffvsmark 1}}
2666 $fakerowmenu configure -tearoff 0
2668 set headctxmenu .headctxmenu
2669 makemenu $headctxmenu {
2670 {mc "Check out this branch" command cobranch}
2671 {mc "Rename this branch" command mvbranch}
2672 {mc "Remove this branch" command rmbranch}
2673 {mc "Copy branch name" command {clipboard clear; clipboard append $headmenuhead}}
2675 $headctxmenu configure -tearoff 0
2678 set flist_menu .flistctxmenu
2679 makemenu $flist_menu {
2680 {mc "Highlight this too" command {flist_hl 0}}
2681 {mc "Highlight this only" command {flist_hl 1}}
2682 {mc "External diff" command {external_diff}}
2683 {mc "Blame parent commit" command {external_blame 1}}
2684 {mc "Copy path" command {clipboard clear; clipboard append $flist_menu_file}}
2686 $flist_menu configure -tearoff 0
2689 set diff_menu .diffctxmenu
2690 makemenu $diff_menu {
2691 {mc "Show origin of this line" command show_line_source}
2692 {mc "Run git gui blame on this line" command {external_blame_diff}}
2694 $diff_menu configure -tearoff 0
2697 # Windows sends all mouse wheel events to the current focused window, not
2698 # the one where the mouse hovers, so bind those events here and redirect
2699 # to the correct window
2700 proc windows_mousewheel_redirector {W X Y D} {
2701 global canv canv2 canv3
2702 set w [winfo containing -displayof $W $X $Y]
2704 set u [expr {$D < 0 ? 5 : -5}]
2705 if {$w == $canv || $w == $canv2 || $w == $canv3} {
2706 allcanvs yview scroll $u units
2709 $w yview scroll $u units
2715 # Update row number label when selectedline changes
2716 proc selectedline_change {n1 n2 op} {
2717 global selectedline rownumsel
2719 if {$selectedline eq {}} {
2722 set rownumsel [expr {$selectedline + 1}]
2726 # mouse-2 makes all windows scan vertically, but only the one
2727 # the cursor is in scans horizontally
2728 proc canvscan {op w x y} {
2729 global canv canv2 canv3
2730 foreach c [list $canv $canv2 $canv3] {
2739 proc scrollcanv {cscroll f0 f1} {
2740 $cscroll set $f0 $f1
2745 # when we make a key binding for the toplevel, make sure
2746 # it doesn't get triggered when that key is pressed in the
2747 # find string entry widget.
2748 proc bindkey {ev script} {
2751 set escript [bind Entry $ev]
2752 if {$escript == {}} {
2753 set escript [bind Entry <Key>]
2755 foreach e $entries {
2756 bind $e $ev "$escript; break"
2760 proc bindmodfunctionkey {mod n script} {
2761 bind . <$mod-F$n> $script
2762 catch { bind . <$mod-XF86_Switch_VT_$n> $script }
2765 # set the focus back to the toplevel for any click outside
2768 global ctext entries
2769 foreach e [concat $entries $ctext] {
2770 if {$w == $e} return
2775 # Adjust the progress bar for a change in requested extent or canvas size
2776 proc adjustprogress {} {
2777 global progresscanv progressitem progresscoords
2778 global fprogitem fprogcoord lastprogupdate progupdatepending
2779 global rprogitem rprogcoord use_ttk
2782 $progresscanv configure -value [expr {int($fprogcoord * 100)}]
2786 set w [expr {[winfo width $progresscanv] - 4}]
2787 set x0 [expr {$w * [lindex $progresscoords 0]}]
2788 set x1 [expr {$w * [lindex $progresscoords 1]}]
2789 set h [winfo height $progresscanv]
2790 $progresscanv coords $progressitem $x0 0 $x1 $h
2791 $progresscanv coords $fprogitem 0 0 [expr {$w * $fprogcoord}] $h
2792 $progresscanv coords $rprogitem 0 0 [expr {$w * $rprogcoord}] $h
2793 set now [clock clicks -milliseconds]
2794 if {$now >= $lastprogupdate + 100} {
2795 set progupdatepending 0
2797 } elseif {!$progupdatepending} {
2798 set progupdatepending 1
2799 after [expr {$lastprogupdate + 100 - $now}] doprogupdate
2803 proc doprogupdate {} {
2804 global lastprogupdate progupdatepending
2806 if {$progupdatepending} {
2807 set progupdatepending 0
2808 set lastprogupdate [clock clicks -milliseconds]
2813 proc config_check_tmp_exists {tries_left} {
2814 global config_file_tmp
2816 if {[file exists $config_file_tmp]} {
2818 if {$tries_left > 0} {
2819 after 100 [list config_check_tmp_exists $tries_left]
2821 error_popup "There appears to be a stale $config_file_tmp\
2822 file, which will prevent gitk from saving its configuration on exit.\
2823 Please remove it if it is not being used by any existing gitk process."
2828 proc config_init_trace {name} {
2829 global config_variable_changed config_variable_original
2832 set config_variable_changed($name) 0
2833 set config_variable_original($name) $var
2836 proc config_variable_change_cb {name name2 op} {
2837 global config_variable_changed config_variable_original
2840 if {$op eq "write" &&
2841 (![info exists config_variable_original($name)] ||
2842 $config_variable_original($name) ne $var)} {
2843 set config_variable_changed($name) 1
2847 proc savestuff {w} {
2849 global config_file config_file_tmp
2850 global config_variables config_variable_changed
2853 upvar #0 viewname current_viewname
2854 upvar #0 viewfiles current_viewfiles
2855 upvar #0 viewargs current_viewargs
2856 upvar #0 viewargscmd current_viewargscmd
2857 upvar #0 viewperm current_viewperm
2858 upvar #0 nextviewnum current_nextviewnum
2859 upvar #0 use_ttk current_use_ttk
2861 if {$stuffsaved} return
2862 if {![winfo viewable .]} return
2866 while {[catch {set f [open $config_file_tmp {WRONLY CREAT EXCL}]}]} {
2867 if {[incr try_count] > 50} {
2868 error "Unable to write config file: $config_file_tmp exists"
2873 if {$::tcl_platform(platform) eq {windows}} {
2874 file attributes $config_file_tmp -hidden true
2876 if {[file exists $config_file]} {
2879 foreach var_name $config_variables {
2880 upvar #0 $var_name var
2881 upvar 0 $var_name old_var
2882 if {!$config_variable_changed($var_name) && [info exists old_var]} {
2883 puts $f [list set $var_name $old_var]
2885 puts $f [list set $var_name $var]
2889 puts $f "set geometry(main) [wm geometry .]"
2890 puts $f "set geometry(state) [wm state .]"
2891 puts $f "set geometry(topwidth) [winfo width .tf]"
2892 puts $f "set geometry(topheight) [winfo height .tf]"
2893 if {$current_use_ttk} {
2894 puts $f "set geometry(pwsash0) \"[.tf.histframe.pwclist sashpos 0] 1\""
2895 puts $f "set geometry(pwsash1) \"[.tf.histframe.pwclist sashpos 1] 1\""
2897 puts $f "set geometry(pwsash0) \"[.tf.histframe.pwclist sash coord 0]\""
2898 puts $f "set geometry(pwsash1) \"[.tf.histframe.pwclist sash coord 1]\""
2900 puts $f "set geometry(botwidth) [winfo width .bleft]"
2901 puts $f "set geometry(botheight) [winfo height .bleft]"
2903 array set view_save {}
2905 if {![info exists permviews]} { set permviews {} }
2906 foreach view $permviews {
2907 set view_save([lindex $view 0]) 1
2908 set views([lindex $view 0]) $view
2910 puts -nonewline $f "set permviews {"
2911 for {set v 1} {$v < $current_nextviewnum} {incr v} {
2912 if {$viewchanged($v)} {
2913 if {$current_viewperm($v)} {
2914 set views($current_viewname($v)) [list $current_viewname($v) $current_viewfiles($v) $current_viewargs($v) $current_viewargscmd($v)]
2916 set view_save($current_viewname($v)) 0
2920 # write old and updated view to their places and append remaining to the end
2921 foreach view $permviews {
2922 set view_name [lindex $view 0]
2923 if {$view_save($view_name)} {
2924 puts $f "{$views($view_name)}"
2926 unset views($view_name)
2928 foreach view_name [array names views] {
2929 puts $f "{$views($view_name)}"
2933 file rename -force $config_file_tmp $config_file
2936 puts "Error saving config: $err"
2939 file delete -force $config_file_tmp
2944 proc resizeclistpanes {win w} {
2945 global oldwidth use_ttk
2946 if {[info exists oldwidth($win)]} {
2948 set s0 [$win sashpos 0]
2949 set s1 [$win sashpos 1]
2951 set s0 [$win sash coord 0]
2952 set s1 [$win sash coord 1]
2955 set sash0 [expr {int($w/2 - 2)}]
2956 set sash1 [expr {int($w*5/6 - 2)}]
2958 set factor [expr {1.0 * $w / $oldwidth($win)}]
2959 set sash0 [expr {int($factor * [lindex $s0 0])}]
2960 set sash1 [expr {int($factor * [lindex $s1 0])}]
2964 if {$sash1 < $sash0 + 20} {
2965 set sash1 [expr {$sash0 + 20}]
2967 if {$sash1 > $w - 10} {
2968 set sash1 [expr {$w - 10}]
2969 if {$sash0 > $sash1 - 20} {
2970 set sash0 [expr {$sash1 - 20}]
2975 $win sashpos 0 $sash0
2976 $win sashpos 1 $sash1
2978 $win sash place 0 $sash0 [lindex $s0 1]
2979 $win sash place 1 $sash1 [lindex $s1 1]
2982 set oldwidth($win) $w
2985 proc resizecdetpanes {win w} {
2986 global oldwidth use_ttk
2987 if {[info exists oldwidth($win)]} {
2989 set s0 [$win sashpos 0]
2991 set s0 [$win sash coord 0]
2994 set sash0 [expr {int($w*3/4 - 2)}]
2996 set factor [expr {1.0 * $w / $oldwidth($win)}]
2997 set sash0 [expr {int($factor * [lindex $s0 0])}]
3001 if {$sash0 > $w - 15} {
3002 set sash0 [expr {$w - 15}]
3006 $win sashpos 0 $sash0
3008 $win sash place 0 $sash0 [lindex $s0 1]
3011 set oldwidth($win) $w
3014 proc allcanvs args {
3015 global canv canv2 canv3
3021 proc bindall {event action} {
3022 global canv canv2 canv3
3023 bind $canv $event $action
3024 bind $canv2 $event $action
3025 bind $canv3 $event $action
3031 if {[winfo exists $w]} {
3036 wm title $w [mc "About gitk"]
3038 message $w.m -text [mc "
3039 Gitk - a commit viewer for git
3041 Copyright \u00a9 2005-2016 Paul Mackerras
3043 Use and redistribute under the terms of the GNU General Public License"] \
3044 -justify center -aspect 400 -border 2 -bg $bgcolor -relief groove
3045 pack $w.m -side top -fill x -padx 2 -pady 2
3046 ${NS}::button $w.ok -text [mc "Close"] -command "destroy $w" -default active
3047 pack $w.ok -side bottom
3048 bind $w <Visibility> "focus $w.ok"
3049 bind $w <Key-Escape> "destroy $w"
3050 bind $w <Key-Return> "destroy $w"
3051 tk::PlaceWindow $w widget .
3057 if {[winfo exists $w]} {
3061 if {[tk windowingsystem] eq {aqua}} {
3067 wm title $w [mc "Gitk key bindings"]
3069 message $w.m -text "
3070 [mc "Gitk key bindings:"]
3072 [mc "<%s-Q> Quit" $M1T]
3073 [mc "<%s-W> Close window" $M1T]
3074 [mc "<Home> Move to first commit"]
3075 [mc "<End> Move to last commit"]
3076 [mc "<Up>, p, k Move up one commit"]
3077 [mc "<Down>, n, j Move down one commit"]
3078 [mc "<Left>, z, h Go back in history list"]
3079 [mc "<Right>, x, l Go forward in history list"]
3080 [mc "<%s-n> Go to n-th parent of current commit in history list" $M1T]
3081 [mc "<PageUp> Move up one page in commit list"]
3082 [mc "<PageDown> Move down one page in commit list"]
3083 [mc "<%s-Home> Scroll to top of commit list" $M1T]
3084 [mc "<%s-End> Scroll to bottom of commit list" $M1T]
3085 [mc "<%s-Up> Scroll commit list up one line" $M1T]
3086 [mc "<%s-Down> Scroll commit list down one line" $M1T]
3087 [mc "<%s-PageUp> Scroll commit list up one page" $M1T]
3088 [mc "<%s-PageDown> Scroll commit list down one page" $M1T]
3089 [mc "<Shift-Up> Find backwards (upwards, later commits)"]
3090 [mc "<Shift-Down> Find forwards (downwards, earlier commits)"]
3091 [mc "<Delete>, b Scroll diff view up one page"]
3092 [mc "<Backspace> Scroll diff view up one page"]
3093 [mc "<Space> Scroll diff view down one page"]
3094 [mc "u Scroll diff view up 18 lines"]
3095 [mc "d Scroll diff view down 18 lines"]
3096 [mc "<%s-F> Find" $M1T]
3097 [mc "<%s-G> Move to next find hit" $M1T]
3098 [mc "<Return> Move to next find hit"]
3099 [mc "g Go to commit"]
3100 [mc "/ Focus the search box"]
3101 [mc "? Move to previous find hit"]
3102 [mc "f Scroll diff view to next file"]
3103 [mc "<%s-S> Search for next hit in diff view" $M1T]
3104 [mc "<%s-R> Search for previous hit in diff view" $M1T]
3105 [mc "<%s-KP+> Increase font size" $M1T]
3106 [mc "<%s-plus> Increase font size" $M1T]
3107 [mc "<%s-KP-> Decrease font size" $M1T]
3108 [mc "<%s-minus> Decrease font size" $M1T]
3111 -justify left -bg $bgcolor -border 2 -relief groove
3112 pack $w.m -side top -fill both -padx 2 -pady 2
3113 ${NS}::button $w.ok -text [mc "Close"] -command "destroy $w" -default active
3114 bind $w <Key-Escape> [list destroy $w]
3115 pack $w.ok -side bottom
3116 bind $w <Visibility> "focus $w.ok"
3117 bind $w <Key-Escape> "destroy $w"
3118 bind $w <Key-Return> "destroy $w"
3121 # Procedures for manipulating the file list window at the
3122 # bottom right of the overall window.
3124 proc treeview {w l openlevs} {
3125 global treecontents treediropen treeheight treeparent treeindex
3135 set treecontents() {}
3136 $w conf -state normal
3138 while {[string range $f 0 $prefixend] ne $prefix} {
3139 if {$lev <= $openlevs} {
3140 $w mark set e:$treeindex($prefix) "end -1c"
3141 $w mark gravity e:$treeindex($prefix) left
3143 set treeheight($prefix) $ht
3144 incr ht [lindex $htstack end]
3145 set htstack [lreplace $htstack end end]
3146 set prefixend [lindex $prefendstack end]
3147 set prefendstack [lreplace $prefendstack end end]
3148 set prefix [string range $prefix 0 $prefixend]
3151 set tail [string range $f [expr {$prefixend+1}] end]
3152 while {[set slash [string first "/" $tail]] >= 0} {
3155 lappend prefendstack $prefixend
3156 incr prefixend [expr {$slash + 1}]
3157 set d [string range $tail 0 $slash]
3158 lappend treecontents($prefix) $d
3159 set oldprefix $prefix
3161 set treecontents($prefix) {}
3162 set treeindex($prefix) [incr ix]
3163 set treeparent($prefix) $oldprefix
3164 set tail [string range $tail [expr {$slash+1}] end]
3165 if {$lev <= $openlevs} {
3167 set treediropen($prefix) [expr {$lev < $openlevs}]
3168 set bm [expr {$lev == $openlevs? "tri-rt": "tri-dn"}]
3169 $w mark set d:$ix "end -1c"
3170 $w mark gravity d:$ix left
3172 for {set i 0} {$i < $lev} {incr i} {append str "\t"}
3174 $w image create end -align center -image $bm -padx 1 \
3176 $w insert end $d [highlight_tag $prefix]
3177 $w mark set s:$ix "end -1c"
3178 $w mark gravity s:$ix left
3183 if {$lev <= $openlevs} {
3186 for {set i 0} {$i < $lev} {incr i} {append str "\t"}
3188 $w insert end $tail [highlight_tag $f]
3190 lappend treecontents($prefix) $tail
3193 while {$htstack ne {}} {
3194 set treeheight($prefix) $ht
3195 incr ht [lindex $htstack end]
3196 set htstack [lreplace $htstack end end]
3197 set prefixend [lindex $prefendstack end]
3198 set prefendstack [lreplace $prefendstack end end]
3199 set prefix [string range $prefix 0 $prefixend]
3201 $w conf -state disabled
3204 proc linetoelt {l} {
3205 global treeheight treecontents
3210 foreach e $treecontents($prefix) {
3215 if {[string index $e end] eq "/"} {
3216 set n $treeheight($prefix$e)
3228 proc highlight_tree {y prefix} {
3229 global treeheight treecontents cflist
3231 foreach e $treecontents($prefix) {
3233 if {[highlight_tag $path] ne {}} {
3234 $cflist tag add bold $y.0 "$y.0 lineend"
3237 if {[string index $e end] eq "/" && $treeheight($path) > 1} {
3238 set y [highlight_tree $y $path]
3244 proc treeclosedir {w dir} {
3245 global treediropen treeheight treeparent treeindex
3247 set ix $treeindex($dir)
3248 $w conf -state normal
3249 $w delete s:$ix e:$ix
3250 set treediropen($dir) 0
3251 $w image configure a:$ix -image tri-rt
3252 $w conf -state disabled
3253 set n [expr {1 - $treeheight($dir)}]
3254 while {$dir ne {}} {
3255 incr treeheight($dir) $n
3256 set dir $treeparent($dir)
3260 proc treeopendir {w dir} {
3261 global treediropen treeheight treeparent treecontents treeindex
3263 set ix $treeindex($dir)
3264 $w conf -state normal
3265 $w image configure a:$ix -image tri-dn
3266 $w mark set e:$ix s:$ix
3267 $w mark gravity e:$ix right
3270 set n [llength $treecontents($dir)]
3271 for {set x $dir} {$x ne {}} {set x $treeparent($x)} {
3274 incr treeheight($x) $n
3276 foreach e $treecontents($dir) {
3278 if {[string index $e end] eq "/"} {
3279 set iy $treeindex($de)
3280 $w mark set d:$iy e:$ix
3281 $w mark gravity d:$iy left
3282 $w insert e:$ix $str
3283 set treediropen($de) 0
3284 $w image create e:$ix -align center -image tri-rt -padx 1 \
3286 $w insert e:$ix $e [highlight_tag $de]
3287 $w mark set s:$iy e:$ix
3288 $w mark gravity s:$iy left
3289 set treeheight($de) 1
3291 $w insert e:$ix $str
3292 $w insert e:$ix $e [highlight_tag $de]
3295 $w mark gravity e:$ix right
3296 $w conf -state disabled
3297 set treediropen($dir) 1
3298 set top [lindex [split [$w index @0,0] .] 0]
3299 set ht [$w cget -height]
3300 set l [lindex [split [$w index s:$ix] .] 0]
3303 } elseif {$l + $n + 1 > $top + $ht} {
3304 set top [expr {$l + $n + 2 - $ht}]
3312 proc treeclick {w x y} {
3313 global treediropen cmitmode ctext cflist cflist_top
3315 if {$cmitmode ne "tree"} return
3316 if {![info exists cflist_top]} return
3317 set l [lindex [split [$w index "@$x,$y"] "."] 0]
3318 $cflist tag remove highlight $cflist_top.0 "$cflist_top.0 lineend"
3319 $cflist tag add highlight $l.0 "$l.0 lineend"
3325 set e [linetoelt $l]
3326 if {[string index $e end] ne "/"} {
3328 } elseif {$treediropen($e)} {
3335 proc setfilelist {id} {
3336 global treefilelist cflist jump_to_here
3338 treeview $cflist $treefilelist($id) 0
3339 if {$jump_to_here ne {}} {
3340 set f [lindex $jump_to_here 0]
3341 if {[lsearch -exact $treefilelist($id) $f] >= 0} {
3347 image create bitmap tri-rt -background black -foreground blue -data {
3348 #define tri-rt_width 13
3349 #define tri-rt_height 13
3350 static unsigned char tri-rt_bits[] = {
3351 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x30, 0x00, 0x70, 0x00, 0xf0, 0x00,
3352 0xf0, 0x01, 0xf0, 0x00, 0x70, 0x00, 0x30, 0x00, 0x10, 0x00, 0x00, 0x00,
3355 #define tri-rt-mask_width 13
3356 #define tri-rt-mask_height 13
3357 static unsigned char tri-rt-mask_bits[] = {
3358 0x08, 0x00, 0x18, 0x00, 0x38, 0x00, 0x78, 0x00, 0xf8, 0x00, 0xf8, 0x01,
3359 0xf8, 0x03, 0xf8, 0x01, 0xf8, 0x00, 0x78, 0x00, 0x38, 0x00, 0x18, 0x00,
3362 image create bitmap tri-dn -background black -foreground blue -data {
3363 #define tri-dn_width 13
3364 #define tri-dn_height 13
3365 static unsigned char tri-dn_bits[] = {
3366 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x07, 0xf8, 0x03,
3367 0xf0, 0x01, 0xe0, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3370 #define tri-dn-mask_width 13
3371 #define tri-dn-mask_height 13
3372 static unsigned char tri-dn-mask_bits[] = {
3373 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x1f, 0xfe, 0x0f, 0xfc, 0x07,
3374 0xf8, 0x03, 0xf0, 0x01, 0xe0, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
3378 image create bitmap reficon-T -background black -foreground yellow -data {
3379 #define tagicon_width 13
3380 #define tagicon_height 9
3381 static unsigned char tagicon_bits[] = {
3382 0x00, 0x00, 0x00, 0x00, 0xf0, 0x07, 0xf8, 0x07,
3383 0xfc, 0x07, 0xf8, 0x07, 0xf0, 0x07, 0x00, 0x00, 0x00, 0x00};
3385 #define tagicon-mask_width 13
3386 #define tagicon-mask_height 9
3387 static unsigned char tagicon-mask_bits[] = {
3388 0x00, 0x00, 0xf0, 0x0f, 0xf8, 0x0f, 0xfc, 0x0f,
3389 0xfe, 0x0f, 0xfc, 0x0f, 0xf8, 0x0f, 0xf0, 0x0f, 0x00, 0x00};
3392 #define headicon_width 13
3393 #define headicon_height 9
3394 static unsigned char headicon_bits[] = {
3395 0x00, 0x00, 0x00, 0x00, 0xf8, 0x07, 0xf8, 0x07,
3396 0xf8, 0x07, 0xf8, 0x07, 0xf8, 0x07, 0x00, 0x00, 0x00, 0x00};
3399 #define headicon-mask_width 13
3400 #define headicon-mask_height 9
3401 static unsigned char headicon-mask_bits[] = {
3402 0x00, 0x00, 0xfc, 0x0f, 0xfc, 0x0f, 0xfc, 0x0f,
3403 0xfc, 0x0f, 0xfc, 0x0f, 0xfc, 0x0f, 0xfc, 0x0f, 0x00, 0x00};
3405 image create bitmap reficon-H -background black -foreground "#00ff00" \
3406 -data $rectdata -maskdata $rectmask
3407 image create bitmap reficon-o -background black -foreground "#ddddff" \
3408 -data $rectdata -maskdata $rectmask
3410 proc init_flist {first} {
3411 global cflist cflist_top difffilestart
3413 $cflist conf -state normal
3414 $cflist delete 0.0 end
3416 $cflist insert end $first
3418 $cflist tag add highlight 1.0 "1.0 lineend"
3420 unset -nocomplain cflist_top
3422 $cflist conf -state disabled
3423 set difffilestart {}
3426 proc highlight_tag {f} {
3427 global highlight_paths
3429 foreach p $highlight_paths {
3430 if {[string match $p $f]} {
3437 proc highlight_filelist {} {
3438 global cmitmode cflist
3440 $cflist conf -state normal
3441 if {$cmitmode ne "tree"} {
3442 set end [lindex [split [$cflist index end] .] 0]
3443 for {set l 2} {$l < $end} {incr l} {
3444 set line [$cflist get $l.0 "$l.0 lineend"]
3445 if {[highlight_tag $line] ne {}} {
3446 $cflist tag add bold $l.0 "$l.0 lineend"
3452 $cflist conf -state disabled
3455 proc unhighlight_filelist {} {
3458 $cflist conf -state normal
3459 $cflist tag remove bold 1.0 end
3460 $cflist conf -state disabled
3463 proc add_flist {fl} {
3466 $cflist conf -state normal
3468 $cflist insert end "\n"
3469 $cflist insert end $f [highlight_tag $f]
3471 $cflist conf -state disabled
3474 proc sel_flist {w x y} {
3475 global ctext difffilestart cflist cflist_top cmitmode
3477 if {$cmitmode eq "tree"} return
3478 if {![info exists cflist_top]} return
3479 set l [lindex [split [$w index "@$x,$y"] "."] 0]
3480 $cflist tag remove highlight $cflist_top.0 "$cflist_top.0 lineend"
3481 $cflist tag add highlight $l.0 "$l.0 lineend"
3486 catch {$ctext yview [lindex $difffilestart [expr {$l - 2}]]}
3488 suppress_highlighting_file_for_current_scrollpos
3491 proc pop_flist_menu {w X Y x y} {
3492 global ctext cflist cmitmode flist_menu flist_menu_file
3493 global treediffs diffids
3496 set l [lindex [split [$w index "@$x,$y"] "."] 0]
3498 if {$cmitmode eq "tree"} {
3499 set e [linetoelt $l]
3500 if {[string index $e end] eq "/"} return
3502 set e [lindex $treediffs($diffids) [expr {$l-2}]]
3504 set flist_menu_file $e
3505 set xdiffstate "normal"
3506 if {$cmitmode eq "tree"} {
3507 set xdiffstate "disabled"
3509 # Disable "External diff" item in tree mode
3510 $flist_menu entryconf 2 -state $xdiffstate
3511 tk_popup $flist_menu $X $Y
3514 proc find_ctext_fileinfo {line} {
3515 global ctext_file_names ctext_file_lines
3517 set ok [bsearch $ctext_file_lines $line]
3518 set tline [lindex $ctext_file_lines $ok]
3520 if {$ok >= [llength $ctext_file_lines] || $line < $tline} {
3523 return [list [lindex $ctext_file_names $ok] $tline]
3527 proc pop_diff_menu {w X Y x y} {
3528 global ctext diff_menu flist_menu_file
3529 global diff_menu_txtpos diff_menu_line
3530 global diff_menu_filebase
3532 set diff_menu_txtpos [split [$w index "@$x,$y"] "."]
3533 set diff_menu_line [lindex $diff_menu_txtpos 0]
3534 # don't pop up the menu on hunk-separator or file-separator lines
3535 if {[lsearch -glob [$ctext tag names $diff_menu_line.0] "*sep"] >= 0} {
3539 set f [find_ctext_fileinfo $diff_menu_line]
3540 if {$f eq {}} return
3541 set flist_menu_file [lindex $f 0]
3542 set diff_menu_filebase [lindex $f 1]
3543 tk_popup $diff_menu $X $Y
3546 proc flist_hl {only} {
3547 global flist_menu_file findstring gdttype
3549 set x [shellquote $flist_menu_file]
3550 if {$only || $findstring eq {} || $gdttype ne [mc "touching paths:"]} {
3553 append findstring " " $x
3555 set gdttype [mc "touching paths:"]
3558 proc gitknewtmpdir {} {
3559 global diffnum gitktmpdir gitdir env
3561 if {![info exists gitktmpdir]} {
3562 if {[info exists env(GITK_TMPDIR)]} {
3563 set tmpdir $env(GITK_TMPDIR)
3564 } elseif {[info exists env(TMPDIR)]} {
3565 set tmpdir $env(TMPDIR)
3569 set gitktmpformat [file join $tmpdir ".gitk-tmp.XXXXXX"]
3570 if {[catch {set gitktmpdir [exec mktemp -d $gitktmpformat]}]} {
3571 set gitktmpdir [file join $gitdir [format ".gitk-tmp.%s" [pid]]]
3573 if {[catch {file mkdir $gitktmpdir} err]} {
3574 error_popup "[mc "Error creating temporary directory %s:" $gitktmpdir] $err"
3581 set diffdir [file join $gitktmpdir $diffnum]
3582 if {[catch {file mkdir $diffdir} err]} {
3583 error_popup "[mc "Error creating temporary directory %s:" $diffdir] $err"
3589 proc save_file_from_commit {filename output what} {
3592 if {[catch {exec git show $filename -- > $output} err]} {
3593 if {[string match "fatal: bad revision *" $err]} {
3596 error_popup "[mc "Error getting \"%s\" from %s:" $filename $what] $err"
3602 proc external_diff_get_one_file {diffid filename diffdir} {
3603 global nullid nullid2 nullfile
3606 if {$diffid == $nullid} {
3607 set difffile [file join $worktree $filename]
3608 if {[file exists $difffile]} {
3613 if {$diffid == $nullid2} {
3614 set difffile [file join $diffdir "\[index\] [file tail $filename]"]
3615 return [save_file_from_commit :$filename $difffile index]
3617 set difffile [file join $diffdir "\[$diffid\] [file tail $filename]"]
3618 return [save_file_from_commit $diffid:$filename $difffile \
3622 proc external_diff {} {
3623 global nullid nullid2
3624 global flist_menu_file
3628 if {[llength $diffids] == 1} {
3629 # no reference commit given
3630 set diffidto [lindex $diffids 0]
3631 if {$diffidto eq $nullid} {
3632 # diffing working copy with index
3633 set diffidfrom $nullid2
3634 } elseif {$diffidto eq $nullid2} {
3635 # diffing index with HEAD
3636 set diffidfrom "HEAD"
3638 # use first parent commit
3639 global parentlist selectedline
3640 set diffidfrom [lindex $parentlist $selectedline 0]
3643 set diffidfrom [lindex $diffids 0]
3644 set diffidto [lindex $diffids 1]
3647 # make sure that several diffs wont collide
3648 set diffdir [gitknewtmpdir]
3649 if {$diffdir eq {}} return
3651 # gather files to diff
3652 set difffromfile [external_diff_get_one_file $diffidfrom $flist_menu_file $diffdir]
3653 set difftofile [external_diff_get_one_file $diffidto $flist_menu_file $diffdir]
3655 if {$difffromfile ne {} && $difftofile ne {}} {
3656 set cmd [list [shellsplit $extdifftool] $difffromfile $difftofile]
3657 if {[catch {set fl [open |$cmd r]} err]} {
3658 file delete -force $diffdir
3659 error_popup "$extdifftool: [mc "command failed:"] $err"
3661 fconfigure $fl -blocking 0
3662 filerun $fl [list delete_at_eof $fl $diffdir]
3667 proc find_hunk_blamespec {base line} {
3670 # Find and parse the hunk header
3671 set s_lix [$ctext search -backwards -regexp ^@@ "$line.0 lineend" $base.0]
3672 if {$s_lix eq {}} return
3674 set s_line [$ctext get $s_lix "$s_lix + 1 lines"]
3675 if {![regexp {^@@@*(( -\d+(,\d+)?)+) \+(\d+)(,\d+)? @@} $s_line \
3676 s_line old_specs osz osz1 new_line nsz]} {
3680 # base lines for the parents
3681 set base_lines [list $new_line]
3682 foreach old_spec [lrange [split $old_specs " "] 1 end] {
3683 if {![regexp -- {-(\d+)(,\d+)?} $old_spec \
3684 old_spec old_line osz]} {
3687 lappend base_lines $old_line
3690 # Now scan the lines to determine offset within the hunk
3691 set max_parent [expr {[llength $base_lines]-2}]
3693 set s_lno [lindex [split $s_lix "."] 0]
3695 # Determine if the line is removed
3696 set chunk [$ctext get $line.0 "$line.1 + $max_parent chars"]
3697 if {[string match {[-+ ]*} $chunk]} {
3698 set removed_idx [string first "-" $chunk]
3699 # Choose a parent index
3700 if {$removed_idx >= 0} {
3701 set parent $removed_idx
3703 set unchanged_idx [string first " " $chunk]
3704 if {$unchanged_idx >= 0} {
3705 set parent $unchanged_idx
3707 # blame the current commit
3711 # then count other lines that belong to it
3712 for {set i $line} {[incr i -1] > $s_lno} {} {
3713 set chunk [$ctext get $i.0 "$i.1 + $max_parent chars"]
3714 # Determine if the line is removed
3715 set removed_idx [string first "-" $chunk]
3717 set code [string index $chunk $parent]
3718 if {$code eq "-" || ($removed_idx < 0 && $code ne "+")} {
3722 if {$removed_idx < 0} {
3732 incr dline [lindex $base_lines $parent]
3733 return [list $parent $dline]
3736 proc external_blame_diff {} {
3737 global currentid cmitmode
3738 global diff_menu_txtpos diff_menu_line
3739 global diff_menu_filebase flist_menu_file
3741 if {$cmitmode eq "tree"} {
3743 set line [expr {$diff_menu_line - $diff_menu_filebase}]
3745 set hinfo [find_hunk_blamespec $diff_menu_filebase $diff_menu_line]
3747 set parent_idx [lindex $hinfo 0]
3748 set line [lindex $hinfo 1]
3755 external_blame $parent_idx $line
3758 # Find the SHA1 ID of the blob for file $fname in the index
3760 proc index_sha1 {fname} {
3761 set f [open [list | git ls-files -s $fname] r]
3762 while {[gets $f line] >= 0} {
3763 set info [lindex [split $line "\t"] 0]
3764 set stage [lindex $info 2]
3765 if {$stage eq "0" || $stage eq "2"} {
3767 return [lindex $info 1]
3774 # Turn an absolute path into one relative to the current directory
3775 proc make_relative {f} {
3776 if {[file pathtype $f] eq "relative"} {
3779 set elts [file split $f]
3780 set here [file split [pwd]]
3785 if {$ei < $hi || $ei >= [llength $elts] || [lindex $elts $ei] ne $d} {
3792 set elts [concat $res [lrange $elts $ei end]]
3793 return [eval file join $elts]
3796 proc external_blame {parent_idx {line {}}} {
3797 global flist_menu_file cdup
3798 global nullid nullid2
3799 global parentlist selectedline currentid
3801 if {$parent_idx > 0} {
3802 set base_commit [lindex $parentlist $selectedline [expr {$parent_idx-1}]]
3804 set base_commit $currentid
3807 if {$base_commit eq {} || $base_commit eq $nullid || $base_commit eq $nullid2} {
3808 error_popup [mc "No such commit"]
3812 set cmdline [list git gui blame]
3813 if {$line ne {} && $line > 1} {
3814 lappend cmdline "--line=$line"
3816 set f [file join $cdup $flist_menu_file]
3817 # Unfortunately it seems git gui blame doesn't like
3818 # being given an absolute path...
3819 set f [make_relative $f]
3820 lappend cmdline $base_commit $f
3821 if {[catch {eval exec $cmdline &} err]} {
3822 error_popup "[mc "git gui blame: command failed:"] $err"
3826 proc show_line_source {} {
3827 global cmitmode currentid parents curview blamestuff blameinst
3828 global diff_menu_line diff_menu_filebase flist_menu_file
3829 global nullid nullid2 gitdir cdup
3832 if {$cmitmode eq "tree"} {
3834 set line [expr {$diff_menu_line - $diff_menu_filebase}]
3836 set h [find_hunk_blamespec $diff_menu_filebase $diff_menu_line]
3837 if {$h eq {}} return
3838 set pi [lindex $h 0]
3840 mark_ctext_line $diff_menu_line
3844 if {$currentid eq $nullid} {
3846 # must be a merge in progress...
3848 # get the last line from .git/MERGE_HEAD
3849 set f [open [file join $gitdir MERGE_HEAD] r]
3850 set id [lindex [split [read $f] "\n"] end-1]
3853 error_popup [mc "Couldn't read merge head: %s" $err]
3856 } elseif {$parents($curview,$currentid) eq $nullid2} {
3857 # need to do the blame from the index
3859 set from_index [index_sha1 $flist_menu_file]
3861 error_popup [mc "Error reading index: %s" $err]
3865 set id $parents($curview,$currentid)
3868 set id [lindex $parents($curview,$currentid) $pi]
3870 set line [lindex $h 1]
3873 if {$from_index ne {}} {
3874 lappend blameargs | git cat-file blob $from_index
3876 lappend blameargs | git blame -p -L$line,+1
3877 if {$from_index ne {}} {
3878 lappend blameargs --contents -
3880 lappend blameargs $id
3882 lappend blameargs -- [file join $cdup $flist_menu_file]
3884 set f [open $blameargs r]
3886 error_popup [mc "Couldn't start git blame: %s" $err]
3889 nowbusy blaming [mc "Searching"]
3890 fconfigure $f -blocking 0
3891 set i [reg_instance $f]
3892 set blamestuff($i) {}
3894 filerun $f [list read_line_source $f $i]
3897 proc stopblaming {} {
3900 if {[info exists blameinst]} {
3901 stop_instance $blameinst
3907 proc read_line_source {fd inst} {
3908 global blamestuff curview commfd blameinst nullid nullid2
3910 while {[gets $fd line] >= 0} {
3911 lappend blamestuff($inst) $line
3919 fconfigure $fd -blocking 1
3920 if {[catch {close $fd} err]} {
3921 error_popup [mc "Error running git blame: %s" $err]
3926 set line [split [lindex $blamestuff($inst) 0] " "]
3927 set id [lindex $line 0]
3928 set lnum [lindex $line 1]
3929 if {[string length $id] == 40 && [string is xdigit $id] &&
3930 [string is digit -strict $lnum]} {
3931 # look for "filename" line
3932 foreach l $blamestuff($inst) {
3933 if {[string match "filename *" $l]} {
3934 set fname [string range $l 9 end]
3940 # all looks good, select it
3941 if {$id eq $nullid} {
3942 # blame uses all-zeroes to mean not committed,
3943 # which would mean a change in the index
3946 if {[commitinview $id $curview]} {
3947 selectline [rowofcommit $id] 1 [list $fname $lnum] 1
3949 error_popup [mc "That line comes from commit %s, \
3950 which is not in this view" [shortids $id]]
3953 puts "oops couldn't parse git blame output"
3958 # delete $dir when we see eof on $f (presumably because the child has exited)
3959 proc delete_at_eof {f dir} {
3960 while {[gets $f line] >= 0} {}
3962 if {[catch {close $f} err]} {
3963 error_popup "[mc "External diff viewer failed:"] $err"
3965 file delete -force $dir
3971 # Functions for adding and removing shell-type quoting
3973 proc shellquote {str} {
3974 if {![string match "*\['\"\\ \t]*" $str]} {
3977 if {![string match "*\['\"\\]*" $str]} {
3980 if {![string match "*'*" $str]} {
3983 return "\"[string map {\" \\\" \\ \\\\} $str]\""
3986 proc shellarglist {l} {
3992 append str [shellquote $a]
3997 proc shelldequote {str} {
4002 if {![regexp -start $used -indices "\['\"\\\\ \t]" $str first]} {
4003 append ret [string range $str $used end]
4004 set used [string length $str]
4007 set first [lindex $first 0]
4008 set ch [string index $str $first]
4009 if {$first > $used} {
4010 append ret [string range $str $used [expr {$first - 1}]]
4013 if {$ch eq " " || $ch eq "\t"} break
4016 set first [string first "'" $str $used]
4018 error "unmatched single-quote"
4020 append ret [string range $str $used [expr {$first - 1}]]
4025 if {$used >= [string length $str]} {
4026 error "trailing backslash"
4028 append ret [string index $str $used]
4033 if {![regexp -start $used -indices "\[\"\\\\]" $str first]} {
4034 error "unmatched double-quote"
4036 set first [lindex $first 0]
4037 set ch [string index $str $first]
4038 if {$first > $used} {
4039 append ret [string range $str $used [expr {$first - 1}]]
4042 if {$ch eq "\""} break
4044 append ret [string index $str $used]
4048 return [list $used $ret]
4051 proc shellsplit {str} {
4054 set str [string trimleft $str]
4055 if {$str eq {}} break
4056 set dq [shelldequote $str]
4057 set n [lindex $dq 0]
4058 set word [lindex $dq 1]
4059 set str [string range $str $n end]
4065 proc set_window_title {} {
4066 global appname curview viewname vrevs
4067 set rev [mc "All files"]
4068 if {$curview ne 0} {
4069 if {$viewname($curview) eq [mc "Command line"]} {
4070 set rev [string map {"--gitk-symmetric-diff-marker" "--merge"} $vrevs($curview)]
4072 set rev $viewname($curview)
4075 wm title . "[reponame]: $rev - $appname"
4078 # Code to implement multiple views
4080 proc newview {ishighlight} {
4081 global nextviewnum newviewname newishighlight
4082 global revtreeargs viewargscmd newviewopts curview
4084 set newishighlight $ishighlight
4086 if {[winfo exists $top]} {
4090 decode_view_opts $nextviewnum $revtreeargs
4091 set newviewname($nextviewnum) "[mc "View"] $nextviewnum"
4092 set newviewopts($nextviewnum,perm) 0
4093 set newviewopts($nextviewnum,cmd) $viewargscmd($curview)
4094 vieweditor $top $nextviewnum [mc "Gitk view definition"]
4097 set known_view_options {
4098 {perm b . {} {mc "Remember this view"}}
4099 {reflabel l + {} {mc "References (space separated list):"}}
4100 {refs t15 .. {} {mc "Branches & tags:"}}
4101 {allrefs b *. "--all" {mc "All refs"}}
4102 {branches b . "--branches" {mc "All (local) branches"}}
4103 {tags b . "--tags" {mc "All tags"}}
4104 {remotes b . "--remotes" {mc "All remote-tracking branches"}}
4105 {commitlbl l + {} {mc "Commit Info (regular expressions):"}}
4106 {author t15 .. "--author=*" {mc "Author:"}}
4107 {committer t15 . "--committer=*" {mc "Committer:"}}
4108 {loginfo t15 .. "--grep=*" {mc "Commit Message:"}}
4109 {allmatch b .. "--all-match" {mc "Matches all Commit Info criteria"}}
4110 {igrep b .. "--invert-grep" {mc "Matches no Commit Info criteria"}}
4111 {changes_l l + {} {mc "Changes to Files:"}}
4112 {pickaxe_s r0 . {} {mc "Fixed String"}}
4113 {pickaxe_t r1 . "--pickaxe-regex" {mc "Regular Expression"}}
4114 {pickaxe t15 .. "-S*" {mc "Search string:"}}
4115 {datelabel l + {} {mc "Commit Dates (\"2 weeks ago\", \"2009-03-17 15:27:38\", \"March 17, 2009 15:27:38\"):"}}
4116 {since t15 .. {"--since=*" "--after=*"} {mc "Since:"}}
4117 {until t15 . {"--until=*" "--before=*"} {mc "Until:"}}
4118 {limit_lbl l + {} {mc "Limit and/or skip a number of revisions (positive integer):"}}
4119 {limit t10 *. "--max-count=*" {mc "Number to show:"}}
4120 {skip t10 . "--skip=*" {mc "Number to skip:"}}
4121 {misc_lbl l + {} {mc "Miscellaneous options:"}}
4122 {dorder b *. {"--date-order" "-d"} {mc "Strictly sort by date"}}
4123 {lright b . "--left-right" {mc "Mark branch sides"}}
4124 {first b . "--first-parent" {mc "Limit to first parent"}}
4125 {smplhst b . "--simplify-by-decoration" {mc "Simple history"}}
4126 {args t50 *. {} {mc "Additional arguments to git log:"}}
4127 {allpaths path + {} {mc "Enter files and directories to include, one per line:"}}
4128 {cmd t50= + {} {mc "Command to generate more commits to include:"}}
4131 # Convert $newviewopts($n, ...) into args for git log.
4132 proc encode_view_opts {n} {
4133 global known_view_options newviewopts
4136 foreach opt $known_view_options {
4137 set patterns [lindex $opt 3]
4138 if {$patterns eq {}} continue
4139 set pattern [lindex $patterns 0]
4141 if {[lindex $opt 1] eq "b"} {
4142 set val $newviewopts($n,[lindex $opt 0])
4144 lappend rargs $pattern
4146 } elseif {[regexp {^r(\d+)$} [lindex $opt 1] type value]} {
4147 regexp {^(.*_)} [lindex $opt 0] uselessvar button_id
4148 set val $newviewopts($n,$button_id)
4149 if {$val eq $value} {
4150 lappend rargs $pattern
4153 set val $newviewopts($n,[lindex $opt 0])
4154 set val [string trim $val]
4156 set pfix [string range $pattern 0 end-1]
4157 lappend rargs $pfix$val
4161 set rargs [concat $rargs [shellsplit $newviewopts($n,refs)]]
4162 return [concat $rargs [shellsplit $newviewopts($n,args)]]
4165 # Fill $newviewopts($n, ...) based on args for git log.
4166 proc decode_view_opts {n view_args} {
4167 global known_view_options newviewopts
4169 foreach opt $known_view_options {
4170 set id [lindex $opt 0]
4171 if {[lindex $opt 1] eq "b"} {
4174 } elseif {[regexp {^r(\d+)$} [lindex $opt 1]]} {
4176 regexp {^(.*_)} $id uselessvar id
4182 set newviewopts($n,$id) $val
4186 foreach arg $view_args {
4187 if {[regexp -- {^-([0-9]+)$} $arg arg cnt]
4188 && ![info exists found(limit)]} {
4189 set newviewopts($n,limit) $cnt
4194 foreach opt $known_view_options {
4195 set id [lindex $opt 0]
4196 if {[info exists found($id)]} continue
4197 foreach pattern [lindex $opt 3] {
4198 if {![string match $pattern $arg]} continue
4199 if {[lindex $opt 1] eq "b"} {
4202 } elseif {[regexp {^r(\d+)$} [lindex $opt 1] match num]} {
4204 regexp {^(.*_)} $id uselessvar id
4208 set size [string length $pattern]
4209 set val [string range $arg [expr {$size-1}] end]
4211 set newviewopts($n,$id) $val
4215 if {[info exists val]} break
4217 if {[info exists val]} continue
4218 if {[regexp {^-} $arg]} {
4221 lappend refargs $arg
4224 set newviewopts($n,refs) [shellarglist $refargs]
4225 set newviewopts($n,args) [shellarglist $oargs]
4228 proc edit_or_newview {} {
4240 global viewname viewperm newviewname newviewopts
4241 global viewargs viewargscmd
4243 set top .gitkvedit-$curview
4244 if {[winfo exists $top]} {
4248 decode_view_opts $curview $viewargs($curview)
4249 set newviewname($curview) $viewname($curview)
4250 set newviewopts($curview,perm) $viewperm($curview)
4251 set newviewopts($curview,cmd) $viewargscmd($curview)
4252 vieweditor $top $curview "[mc "Gitk: edit view"] $viewname($curview)"
4255 proc vieweditor {top n title} {
4256 global newviewname newviewopts viewfiles bgcolor
4257 global known_view_options NS
4260 wm title $top [concat $title [mc "-- criteria for selecting revisions"]]
4261 make_transient $top .
4264 ${NS}::frame $top.nfr
4265 ${NS}::label $top.nl -text [mc "View Name"]
4266 ${NS}::entry $top.name -width 20 -textvariable newviewname($n)
4267 pack $top.nfr -in $top -fill x -pady 5 -padx 3
4268 pack $top.nl -in $top.nfr -side left -padx {0 5}
4269 pack $top.name -in $top.nfr -side left -padx {0 25}
4275 foreach opt $known_view_options {
4276 set id [lindex $opt 0]
4277 set type [lindex $opt 1]
4278 set flags [lindex $opt 2]
4279 set title [eval [lindex $opt 4]]
4282 if {$flags eq "+" || $flags eq "*"} {
4283 set cframe $top.fr$cnt
4285 ${NS}::frame $cframe
4286 pack $cframe -in $top -fill x -pady 3 -padx 3
4287 set cexpand [expr {$flags eq "*"}]
4288 } elseif {$flags eq ".." || $flags eq "*."} {
4289 set cframe $top.fr$cnt
4291 ${NS}::frame $cframe
4292 pack $cframe -in $top -fill x -pady 3 -padx [list 15 3]
4293 set cexpand [expr {$flags eq "*."}]
4299 ${NS}::label $cframe.l_$id -text $title
4300 pack $cframe.l_$id -in $cframe -side left -pady [list 3 0] -anchor w
4301 } elseif {$type eq "b"} {
4302 ${NS}::checkbutton $cframe.c_$id -text $title -variable newviewopts($n,$id)
4303 pack $cframe.c_$id -in $cframe -side left \
4304 -padx [list $lxpad 0] -expand $cexpand -anchor w
4305 } elseif {[regexp {^r(\d+)$} $type type sz]} {
4306 regexp {^(.*_)} $id uselessvar button_id
4307 ${NS}::radiobutton $cframe.c_$id -text $title -variable newviewopts($n,$button_id) -value $sz
4308 pack $cframe.c_$id -in $cframe -side left \
4309 -padx [list $lxpad 0] -expand $cexpand -anchor w
4310 } elseif {[regexp {^t(\d+)$} $type type sz]} {
4311 ${NS}::label $cframe.l_$id -text $title
4312 ${NS}::entry $cframe.e_$id -width $sz -background $bgcolor \
4313 -textvariable newviewopts($n,$id)
4314 pack $cframe.l_$id -in $cframe -side left -padx [list $lxpad 0]
4315 pack $cframe.e_$id -in $cframe -side left -expand 1 -fill x
4316 } elseif {[regexp {^t(\d+)=$} $type type sz]} {
4317 ${NS}::label $cframe.l_$id -text $title
4318 ${NS}::entry $cframe.e_$id -width $sz -background $bgcolor \
4319 -textvariable newviewopts($n,$id)
4320 pack $cframe.l_$id -in $cframe -side top -pady [list 3 0] -anchor w
4321 pack $cframe.e_$id -in $cframe -side top -fill x
4322 } elseif {$type eq "path"} {
4323 ${NS}::label $top.l -text $title
4324 pack $top.l -in $top -side top -pady [list 3 0] -anchor w -padx 3
4325 text $top.t -width 40 -height 5 -background $bgcolor
4326 if {[info exists viewfiles($n)]} {
4327 foreach f $viewfiles($n) {
4328 $top.t insert end $f
4329 $top.t insert end "\n"
4331 $top.t delete {end - 1c} end
4332 $top.t mark set insert 0.0
4334 pack $top.t -in $top -side top -pady [list 0 5] -fill both -expand 1 -padx 3
4338 ${NS}::frame $top.buts
4339 ${NS}::button $top.buts.ok -text [mc "OK"] -command [list newviewok $top $n]
4340 ${NS}::button $top.buts.apply -text [mc "Apply (F5)"] -command [list newviewok $top $n 1]
4341 ${NS}::button $top.buts.can -text [mc "Cancel"] -command [list destroy $top]
4342 bind $top <Control-Return> [list newviewok $top $n]
4343 bind $top <F5> [list newviewok $top $n 1]
4344 bind $top <Escape> [list destroy $top]
4345 grid $top.buts.ok $top.buts.apply $top.buts.can
4346 grid columnconfigure $top.buts 0 -weight 1 -uniform a
4347 grid columnconfigure $top.buts 1 -weight 1 -uniform a
4348 grid columnconfigure $top.buts 2 -weight 1 -uniform a
4349 pack $top.buts -in $top -side top -fill x
4353 proc doviewmenu {m first cmd op argv} {
4354 set nmenu [$m index end]
4355 for {set i $first} {$i <= $nmenu} {incr i} {
4356 if {[$m entrycget $i -command] eq $cmd} {
4357 eval $m $op $i $argv
4363 proc allviewmenus {n op args} {
4366 doviewmenu .bar.view 5 [list showview $n] $op $args
4367 # doviewmenu $viewhlmenu 1 [list addvhighlight $n] $op $args
4370 proc newviewok {top n {apply 0}} {
4371 global nextviewnum newviewperm newviewname newishighlight
4372 global viewname viewfiles viewperm viewchanged selectedview curview
4373 global viewargs viewargscmd newviewopts viewhlmenu
4376 set newargs [encode_view_opts $n]
4378 error_popup "[mc "Error in commit selection arguments:"] $err" $top
4382 foreach f [split [$top.t get 0.0 end] "\n"] {
4383 set ft [string trim $f]
4388 if {![info exists viewfiles($n)]} {
4389 # creating a new view
4391 set viewname($n) $newviewname($n)
4392 set viewperm($n) $newviewopts($n,perm)
4393 set viewchanged($n) 1
4394 set viewfiles($n) $files
4395 set viewargs($n) $newargs
4396 set viewargscmd($n) $newviewopts($n,cmd)
4398 if {!$newishighlight} {
4401 run addvhighlight $n
4404 # editing an existing view
4405 set viewperm($n) $newviewopts($n,perm)
4406 set viewchanged($n) 1
4407 if {$newviewname($n) ne $viewname($n)} {
4408 set viewname($n) $newviewname($n)
4409 doviewmenu .bar.view 5 [list showview $n] \
4410 entryconf [list -label $viewname($n)]
4411 # doviewmenu $viewhlmenu 1 [list addvhighlight $n] \
4412 # entryconf [list -label $viewname($n) -value $viewname($n)]
4414 if {$files ne $viewfiles($n) || $newargs ne $viewargs($n) || \
4415 $newviewopts($n,cmd) ne $viewargscmd($n)} {
4416 set viewfiles($n) $files
4417 set viewargs($n) $newargs
4418 set viewargscmd($n) $newviewopts($n,cmd)
4419 if {$curview == $n} {
4425 catch {destroy $top}
4429 global curview viewperm hlview selectedhlview viewchanged
4431 if {$curview == 0} return
4432 if {[info exists hlview] && $hlview == $curview} {
4433 set selectedhlview [mc "None"]
4436 allviewmenus $curview delete
4437 set viewperm($curview) 0
4438 set viewchanged($curview) 1
4442 proc addviewmenu {n} {
4443 global viewname viewhlmenu
4445 .bar.view add radiobutton -label $viewname($n) \
4446 -command [list showview $n] -variable selectedview -value $n
4447 #$viewhlmenu add radiobutton -label $viewname($n) \
4448 # -command [list addvhighlight $n] -variable selectedhlview
4452 global curview cached_commitrow ordertok
4453 global displayorder parentlist rowidlist rowisopt rowfinal
4454 global colormap rowtextx nextcolor canvxmax
4455 global numcommits viewcomplete
4456 global selectedline currentid canv canvy0
4458 global pending_select mainheadid
4461 global hlview selectedhlview commitinterest
4463 if {$n == $curview} return
4465 set ymax [lindex [$canv cget -scrollregion] 3]
4466 set span [$canv yview]
4467 set ytop [expr {[lindex $span 0] * $ymax}]
4468 set ybot [expr {[lindex $span 1] * $ymax}]
4469 set yscreen [expr {($ybot - $ytop) / 2}]
4470 if {$selectedline ne {}} {
4471 set selid $currentid
4472 set y [yc $selectedline]
4473 if {$ytop < $y && $y < $ybot} {
4474 set yscreen [expr {$y - $ytop}]
4476 } elseif {[info exists pending_select]} {
4477 set selid $pending_select
4478 unset pending_select
4482 unset -nocomplain treediffs
4484 if {[info exists hlview] && $hlview == $n} {
4486 set selectedhlview [mc "None"]
4488 unset -nocomplain commitinterest
4489 unset -nocomplain cached_commitrow
4490 unset -nocomplain ordertok
4494 .bar.view entryconf [mca "&Edit view..."] -state [expr {$n == 0? "disabled": "normal"}]
4495 .bar.view entryconf [mca "&Delete view"] -state [expr {$n == 0? "disabled": "normal"}]
4498 if {![info exists viewcomplete($n)]} {
4508 set numcommits $commitidx($n)
4510 unset -nocomplain colormap
4511 unset -nocomplain rowtextx
4513 set canvxmax [$canv cget -width]
4519 if {$selid ne {} && [commitinview $selid $n]} {
4520 set row [rowofcommit $selid]
4521 # try to get the selected row in the same position on the screen
4522 set ymax [lindex [$canv cget -scrollregion] 3]
4523 set ytop [expr {[yc $row] - $yscreen}]
4527 set yf [expr {$ytop * 1.0 / $ymax}]
4529 allcanvs yview moveto $yf
4533 } elseif {!$viewcomplete($n)} {
4534 reset_pending_select $selid
4536 reset_pending_select {}
4538 if {[commitinview $pending_select $curview]} {
4539 selectline [rowofcommit $pending_select] 1
4541 set row [first_real_row]
4542 if {$row < $numcommits} {
4547 if {!$viewcomplete($n)} {
4548 if {$numcommits == 0} {
4549 show_status [mc "Reading commits..."]
4551 } elseif {$numcommits == 0} {
4552 show_status [mc "No commits selected"]
4557 # Stuff relating to the highlighting facility
4559 proc ishighlighted {id} {
4560 global vhighlights fhighlights nhighlights rhighlights
4562 if {[info exists nhighlights($id)] && $nhighlights($id) > 0} {
4563 return $nhighlights($id)
4565 if {[info exists vhighlights($id)] && $vhighlights($id) > 0} {
4566 return $vhighlights($id)
4568 if {[info exists fhighlights($id)] && $fhighlights($id) > 0} {
4569 return $fhighlights($id)
4571 if {[info exists rhighlights($id)] && $rhighlights($id) > 0} {
4572 return $rhighlights($id)
4577 proc bolden {id font} {
4578 global canv linehtag currentid boldids need_redisplay markedid
4580 # need_redisplay = 1 means the display is stale and about to be redrawn
4581 if {$need_redisplay} return
4583 $canv itemconf $linehtag($id) -font $font
4584 if {[info exists currentid] && $id eq $currentid} {
4586 set t [eval $canv create rect [$canv bbox $linehtag($id)] \
4587 -outline {{}} -tags secsel \
4588 -fill [$canv cget -selectbackground]]
4591 if {[info exists markedid] && $id eq $markedid} {
4596 proc bolden_name {id font} {
4597 global canv2 linentag currentid boldnameids need_redisplay
4599 if {$need_redisplay} return
4600 lappend boldnameids $id
4601 $canv2 itemconf $linentag($id) -font $font
4602 if {[info exists currentid] && $id eq $currentid} {
4603 $canv2 delete secsel
4604 set t [eval $canv2 create rect [$canv2 bbox $linentag($id)] \
4605 -outline {{}} -tags secsel \
4606 -fill [$canv2 cget -selectbackground]]
4615 foreach id $boldids {
4616 if {![ishighlighted $id]} {
4619 lappend stillbold $id
4622 set boldids $stillbold
4625 proc addvhighlight {n} {
4626 global hlview viewcomplete curview vhl_done commitidx
4628 if {[info exists hlview]} {
4632 if {$n != $curview && ![info exists viewcomplete($n)]} {
4635 set vhl_done $commitidx($hlview)
4636 if {$vhl_done > 0} {
4641 proc delvhighlight {} {
4642 global hlview vhighlights
4644 if {![info exists hlview]} return
4646 unset -nocomplain vhighlights
4650 proc vhighlightmore {} {
4651 global hlview vhl_done commitidx vhighlights curview
4653 set max $commitidx($hlview)
4654 set vr [visiblerows]
4655 set r0 [lindex $vr 0]
4656 set r1 [lindex $vr 1]
4657 for {set i $vhl_done} {$i < $max} {incr i} {
4658 set id [commitonrow $i $hlview]
4659 if {[commitinview $id $curview]} {
4660 set row [rowofcommit $id]
4661 if {$r0 <= $row && $row <= $r1} {
4662 if {![highlighted $row]} {
4663 bolden $id mainfontbold
4665 set vhighlights($id) 1
4673 proc askvhighlight {row id} {
4674 global hlview vhighlights iddrawn
4676 if {[commitinview $id $hlview]} {
4677 if {[info exists iddrawn($id)] && ![ishighlighted $id]} {
4678 bolden $id mainfontbold
4680 set vhighlights($id) 1
4682 set vhighlights($id) 0
4686 proc hfiles_change {} {
4687 global highlight_files filehighlight fhighlights fh_serial
4688 global highlight_paths
4690 if {[info exists filehighlight]} {
4691 # delete previous highlights
4692 catch {close $filehighlight}
4694 unset -nocomplain fhighlights
4696 unhighlight_filelist
4698 set highlight_paths {}
4699 after cancel do_file_hl $fh_serial
4701 if {$highlight_files ne {}} {
4702 after 300 do_file_hl $fh_serial
4706 proc gdttype_change {name ix op} {
4707 global gdttype highlight_files findstring findpattern
4710 if {$findstring ne {}} {
4711 if {$gdttype eq [mc "containing:"]} {
4712 if {$highlight_files ne {}} {
4713 set highlight_files {}
4718 if {$findpattern ne {}} {
4722 set highlight_files $findstring
4727 # enable/disable findtype/findloc menus too
4730 proc find_change {name ix op} {
4731 global gdttype findstring highlight_files
4734 if {$gdttype eq [mc "containing:"]} {
4737 if {$highlight_files ne $findstring} {
4738 set highlight_files $findstring
4745 proc findcom_change args {
4746 global nhighlights boldnameids
4747 global findpattern findtype findstring gdttype
4750 # delete previous highlights, if any
4751 foreach id $boldnameids {
4752 bolden_name $id mainfont
4755 unset -nocomplain nhighlights
4758 if {$gdttype ne [mc "containing:"] || $findstring eq {}} {
4760 } elseif {$findtype eq [mc "Regexp"]} {
4761 set findpattern $findstring
4763 set e [string map {"*" "\\*" "?" "\\?" "\[" "\\\[" "\\" "\\\\"} \
4765 set findpattern "*$e*"
4769 proc makepatterns {l} {
4772 set ee [string map {"*" "\\*" "?" "\\?" "\[" "\\\[" "\\" "\\\\"} $e]
4773 if {[string index $ee end] eq "/"} {
4783 proc do_file_hl {serial} {
4784 global highlight_files filehighlight highlight_paths gdttype fhl_list
4785 global cdup findtype
4787 if {$gdttype eq [mc "touching paths:"]} {
4788 # If "exact" match then convert backslashes to forward slashes.
4789 # Most useful to support Windows-flavoured file paths.
4790 if {$findtype eq [mc "Exact"]} {
4791 set highlight_files [string map {"\\" "/"} $highlight_files]
4793 if {[catch {set paths [shellsplit $highlight_files]}]} return
4794 set highlight_paths [makepatterns $paths]
4796 set relative_paths {}
4797 foreach path $paths {
4798 lappend relative_paths [file join $cdup $path]
4800 set gdtargs [concat -- $relative_paths]
4801 } elseif {$gdttype eq [mc "adding/removing string:"]} {
4802 set gdtargs [list "-S$highlight_files"]
4803 } elseif {$gdttype eq [mc "changing lines matching:"]} {
4804 set gdtargs [list "-G$highlight_files"]
4806 # must be "containing:", i.e. we're searching commit info
4809 set cmd [concat | git diff-tree -r -s --stdin $gdtargs]
4810 set filehighlight [open $cmd r+]
4811 fconfigure $filehighlight -blocking 0
4812 filerun $filehighlight readfhighlight
4818 proc flushhighlights {} {
4819 global filehighlight fhl_list
4821 if {[info exists filehighlight]} {
4823 puts $filehighlight ""
4824 flush $filehighlight
4828 proc askfilehighlight {row id} {
4829 global filehighlight fhighlights fhl_list
4831 lappend fhl_list $id
4832 set fhighlights($id) -1
4833 puts $filehighlight $id
4836 proc readfhighlight {} {
4837 global filehighlight fhighlights curview iddrawn
4838 global fhl_list find_dirn
4840 if {![info exists filehighlight]} {
4844 while {[incr nr] <= 100 && [gets $filehighlight line] >= 0} {
4845 set line [string trim $line]
4846 set i [lsearch -exact $fhl_list $line]
4847 if {$i < 0} continue
4848 for {set j 0} {$j < $i} {incr j} {
4849 set id [lindex $fhl_list $j]
4850 set fhighlights($id) 0
4852 set fhl_list [lrange $fhl_list [expr {$i+1}] end]
4853 if {$line eq {}} continue
4854 if {![commitinview $line $curview]} continue
4855 if {[info exists iddrawn($line)] && ![ishighlighted $line]} {
4856 bolden $line mainfontbold
4858 set fhighlights($line) 1
4860 if {[eof $filehighlight]} {
4862 puts "oops, git diff-tree died"
4863 catch {close $filehighlight}
4867 if {[info exists find_dirn]} {
4873 proc doesmatch {f} {
4874 global findtype findpattern
4876 if {$findtype eq [mc "Regexp"]} {
4877 return [regexp $findpattern $f]
4878 } elseif {$findtype eq [mc "IgnCase"]} {
4879 return [string match -nocase $findpattern $f]
4881 return [string match $findpattern $f]
4885 proc askfindhighlight {row id} {
4886 global nhighlights commitinfo iddrawn
4888 global markingmatches
4890 if {![info exists commitinfo($id)]} {
4893 set info $commitinfo($id)
4895 set fldtypes [list [mc Headline] [mc Author] "" [mc Committer] "" [mc Comments]]
4896 foreach f $info ty $fldtypes {
4897 if {$ty eq ""} continue
4898 if {($findloc eq [mc "All fields"] || $findloc eq $ty) &&
4900 if {$ty eq [mc "Author"]} {
4907 if {$isbold && [info exists iddrawn($id)]} {
4908 if {![ishighlighted $id]} {
4909 bolden $id mainfontbold
4911 bolden_name $id mainfontbold
4914 if {$markingmatches} {
4915 markrowmatches $row $id
4918 set nhighlights($id) $isbold
4921 proc markrowmatches {row id} {
4922 global canv canv2 linehtag linentag commitinfo findloc
4924 set headline [lindex $commitinfo($id) 0]
4925 set author [lindex $commitinfo($id) 1]
4926 $canv delete match$row
4927 $canv2 delete match$row
4928 if {$findloc eq [mc "All fields"] || $findloc eq [mc "Headline"]} {
4929 set m [findmatches $headline]
4931 markmatches $canv $row $headline $linehtag($id) $m \
4932 [$canv itemcget $linehtag($id) -font] $row
4935 if {$findloc eq [mc "All fields"] || $findloc eq [mc "Author"]} {
4936 set m [findmatches $author]
4938 markmatches $canv2 $row $author $linentag($id) $m \
4939 [$canv2 itemcget $linentag($id) -font] $row
4944 proc vrel_change {name ix op} {
4945 global highlight_related
4948 if {$highlight_related ne [mc "None"]} {
4953 # prepare for testing whether commits are descendents or ancestors of a
4954 proc rhighlight_sel {a} {
4955 global descendent desc_todo ancestor anc_todo
4956 global highlight_related
4958 unset -nocomplain descendent
4959 set desc_todo [list $a]
4960 unset -nocomplain ancestor
4961 set anc_todo [list $a]
4962 if {$highlight_related ne [mc "None"]} {
4968 proc rhighlight_none {} {
4971 unset -nocomplain rhighlights
4975 proc is_descendent {a} {
4976 global curview children descendent desc_todo
4979 set la [rowofcommit $a]
4983 for {set i 0} {$i < [llength $todo]} {incr i} {
4984 set do [lindex $todo $i]
4985 if {[rowofcommit $do] < $la} {
4986 lappend leftover $do
4989 foreach nk $children($v,$do) {
4990 if {![info exists descendent($nk)]} {
4991 set descendent($nk) 1
4999 set desc_todo [concat $leftover [lrange $todo [expr {$i+1}] end]]
5003 set descendent($a) 0
5004 set desc_todo $leftover
5007 proc is_ancestor {a} {
5008 global curview parents ancestor anc_todo
5011 set la [rowofcommit $a]
5015 for {set i 0} {$i < [llength $todo]} {incr i} {
5016 set do [lindex $todo $i]
5017 if {![commitinview $do $v] || [rowofcommit $do] > $la} {
5018 lappend leftover $do
5021 foreach np $parents($v,$do) {
5022 if {![info exists ancestor($np)]} {
5031 set anc_todo [concat $leftover [lrange $todo [expr {$i+1}] end]]
5036 set anc_todo $leftover
5039 proc askrelhighlight {row id} {
5040 global descendent highlight_related iddrawn rhighlights
5041 global selectedline ancestor
5043 if {$selectedline eq {}} return
5045 if {$highlight_related eq [mc "Descendant"] ||
5046 $highlight_related eq [mc "Not descendant"]} {
5047 if {![info exists descendent($id)]} {
5050 if {$descendent($id) == ($highlight_related eq [mc "Descendant"])} {
5053 } elseif {$highlight_related eq [mc "Ancestor"] ||
5054 $highlight_related eq [mc "Not ancestor"]} {
5055 if {![info exists ancestor($id)]} {
5058 if {$ancestor($id) == ($highlight_related eq [mc "Ancestor"])} {
5062 if {[info exists iddrawn($id)]} {
5063 if {$isbold && ![ishighlighted $id]} {
5064 bolden $id mainfontbold
5067 set rhighlights($id) $isbold
5070 # Graph layout functions
5072 proc shortids {ids} {
5075 if {[llength $id] > 1} {
5076 lappend res [shortids $id]
5077 } elseif {[regexp {^[0-9a-f]{40}$} $id]} {
5078 lappend res [string range $id 0 7]
5089 for {set mask 1} {$mask <= $n} {incr mask $mask} {
5090 if {($n & $mask) != 0} {
5091 set ret [concat $ret $o]
5093 set o [concat $o $o]
5098 proc ordertoken {id} {
5099 global ordertok curview varcid varcstart varctok curview parents children
5100 global nullid nullid2
5102 if {[info exists ordertok($id)]} {
5103 return $ordertok($id)
5108 if {[info exists varcid($curview,$id)]} {
5109 set a $varcid($curview,$id)
5110 set p [lindex $varcstart($curview) $a]
5112 set p [lindex $children($curview,$id) 0]
5114 if {[info exists ordertok($p)]} {
5115 set tok $ordertok($p)
5118 set id [first_real_child $curview,$p]
5121 set tok [lindex $varctok($curview) $varcid($curview,$p)]
5124 if {[llength $parents($curview,$id)] == 1} {
5125 lappend todo [list $p {}]
5127 set j [lsearch -exact $parents($curview,$id) $p]
5129 puts "oops didn't find [shortids $p] in parents of [shortids $id]"
5131 lappend todo [list $p [strrep $j]]
5134 for {set i [llength $todo]} {[incr i -1] >= 0} {} {
5135 set p [lindex $todo $i 0]
5136 append tok [lindex $todo $i 1]
5137 set ordertok($p) $tok
5139 set ordertok($origid) $tok
5143 # Work out where id should go in idlist so that order-token
5144 # values increase from left to right
5145 proc idcol {idlist id {i 0}} {
5146 set t [ordertoken $id]
5150 if {$i >= [llength $idlist] || $t < [ordertoken [lindex $idlist $i]]} {
5151 if {$i > [llength $idlist]} {
5152 set i [llength $idlist]
5154 while {[incr i -1] >= 0 && $t < [ordertoken [lindex $idlist $i]]} {}
5157 if {$t > [ordertoken [lindex $idlist $i]]} {
5158 while {[incr i] < [llength $idlist] &&
5159 $t >= [ordertoken [lindex $idlist $i]]} {}
5165 proc initlayout {} {
5166 global rowidlist rowisopt rowfinal displayorder parentlist
5167 global numcommits canvxmax canv
5169 global colormap rowtextx
5178 set canvxmax [$canv cget -width]
5179 unset -nocomplain colormap
5180 unset -nocomplain rowtextx
5184 proc setcanvscroll {} {
5185 global canv canv2 canv3 numcommits linespc canvxmax canvy0
5186 global lastscrollset lastscrollrows
5188 set ymax [expr {$canvy0 + ($numcommits - 0.5) * $linespc + 2}]
5189 $canv conf -scrollregion [list 0 0 $canvxmax $ymax]
5190 $canv2 conf -scrollregion [list 0 0 0 $ymax]
5191 $canv3 conf -scrollregion [list 0 0 0 $ymax]
5192 set lastscrollset [clock clicks -milliseconds]
5193 set lastscrollrows $numcommits
5196 proc visiblerows {} {
5197 global canv numcommits linespc
5199 set ymax [lindex [$canv cget -scrollregion] 3]
5200 if {$ymax eq {} || $ymax == 0} return
5202 set y0 [expr {int([lindex $f 0] * $ymax)}]
5203 set r0 [expr {int(($y0 - 3) / $linespc) - 1}]
5207 set y1 [expr {int([lindex $f 1] * $ymax)}]
5208 set r1 [expr {int(($y1 - 3) / $linespc) + 1}]
5209 if {$r1 >= $numcommits} {
5210 set r1 [expr {$numcommits - 1}]
5212 return [list $r0 $r1]
5215 proc layoutmore {} {
5216 global commitidx viewcomplete curview
5217 global numcommits pending_select curview
5218 global lastscrollset lastscrollrows
5220 if {$lastscrollrows < 100 || $viewcomplete($curview) ||
5221 [clock clicks -milliseconds] - $lastscrollset > 500} {
5224 if {[info exists pending_select] &&
5225 [commitinview $pending_select $curview]} {
5227 selectline [rowofcommit $pending_select] 1
5232 # With path limiting, we mightn't get the actual HEAD commit,
5233 # so ask git rev-list what is the first ancestor of HEAD that
5234 # touches a file in the path limit.
5235 proc get_viewmainhead {view} {
5236 global viewmainheadid vfilelimit viewinstances mainheadid
5239 set rfd [open [concat | git rev-list -1 $mainheadid \
5240 -- $vfilelimit($view)] r]
5241 set j [reg_instance $rfd]
5242 lappend viewinstances($view) $j
5243 fconfigure $rfd -blocking 0
5244 filerun $rfd [list getviewhead $rfd $j $view]
5245 set viewmainheadid($curview) {}
5249 # git rev-list should give us just 1 line to use as viewmainheadid($view)
5250 proc getviewhead {fd inst view} {
5251 global viewmainheadid commfd curview viewinstances showlocalchanges
5254 if {[gets $fd line] < 0} {
5258 } elseif {[string length $line] == 40 && [string is xdigit $line]} {
5261 set viewmainheadid($view) $id
5264 set i [lsearch -exact $viewinstances($view) $inst]
5266 set viewinstances($view) [lreplace $viewinstances($view) $i $i]
5268 if {$showlocalchanges && $id ne {} && $view == $curview} {
5274 proc doshowlocalchanges {} {
5275 global curview viewmainheadid
5277 if {$viewmainheadid($curview) eq {}} return
5278 if {[commitinview $viewmainheadid($curview) $curview]} {
5281 interestedin $viewmainheadid($curview) dodiffindex
5285 proc dohidelocalchanges {} {
5286 global nullid nullid2 lserial curview
5288 if {[commitinview $nullid $curview]} {
5289 removefakerow $nullid
5291 if {[commitinview $nullid2 $curview]} {
5292 removefakerow $nullid2
5297 # spawn off a process to do git diff-index --cached HEAD
5298 proc dodiffindex {} {
5299 global lserial showlocalchanges vfilelimit curview
5300 global hasworktree git_version
5302 if {!$showlocalchanges || !$hasworktree} return
5304 if {[package vcompare $git_version "1.7.2"] >= 0} {
5305 set cmd "|git diff-index --cached --ignore-submodules=dirty HEAD"
5307 set cmd "|git diff-index --cached HEAD"
5309 if {$vfilelimit($curview) ne {}} {
5310 set cmd [concat $cmd -- $vfilelimit($curview)]
5312 set fd [open $cmd r]
5313 fconfigure $fd -blocking 0
5314 set i [reg_instance $fd]
5315 filerun $fd [list readdiffindex $fd $lserial $i]
5318 proc readdiffindex {fd serial inst} {
5319 global viewmainheadid nullid nullid2 curview commitinfo commitdata lserial
5323 if {[gets $fd line] < 0} {
5329 # we only need to see one line and we don't really care what it says...
5332 if {$serial != $lserial} {
5336 # now see if there are any local changes not checked in to the index
5337 set cmd "|git diff-files"
5338 if {$vfilelimit($curview) ne {}} {
5339 set cmd [concat $cmd -- $vfilelimit($curview)]
5341 set fd [open $cmd r]
5342 fconfigure $fd -blocking 0
5343 set i [reg_instance $fd]
5344 filerun $fd [list readdifffiles $fd $serial $i]
5346 if {$isdiff && ![commitinview $nullid2 $curview]} {
5347 # add the line for the changes in the index to the graph
5348 set hl [mc "Local changes checked in to index but not committed"]
5349 set commitinfo($nullid2) [list $hl {} {} {} {} " $hl\n"]
5350 set commitdata($nullid2) "\n $hl\n"
5351 if {[commitinview $nullid $curview]} {
5352 removefakerow $nullid
5354 insertfakerow $nullid2 $viewmainheadid($curview)
5355 } elseif {!$isdiff && [commitinview $nullid2 $curview]} {
5356 if {[commitinview $nullid $curview]} {
5357 removefakerow $nullid
5359 removefakerow $nullid2
5364 proc readdifffiles {fd serial inst} {
5365 global viewmainheadid nullid nullid2 curview
5366 global commitinfo commitdata lserial
5369 if {[gets $fd line] < 0} {
5375 # we only need to see one line and we don't really care what it says...
5378 if {$serial != $lserial} {
5382 if {$isdiff && ![commitinview $nullid $curview]} {
5383 # add the line for the local diff to the graph
5384 set hl [mc "Local uncommitted changes, not checked in to index"]
5385 set commitinfo($nullid) [list $hl {} {} {} {} " $hl\n"]
5386 set commitdata($nullid) "\n $hl\n"
5387 if {[commitinview $nullid2 $curview]} {
5390 set p $viewmainheadid($curview)
5392 insertfakerow $nullid $p
5393 } elseif {!$isdiff && [commitinview $nullid $curview]} {
5394 removefakerow $nullid
5399 proc nextuse {id row} {
5400 global curview children
5402 if {[info exists children($curview,$id)]} {
5403 foreach kid $children($curview,$id) {
5404 if {![commitinview $kid $curview]} {
5407 if {[rowofcommit $kid] > $row} {
5408 return [rowofcommit $kid]
5412 if {[commitinview $id $curview]} {
5413 return [rowofcommit $id]
5418 proc prevuse {id row} {
5419 global curview children
5422 if {[info exists children($curview,$id)]} {
5423 foreach kid $children($curview,$id) {
5424 if {![commitinview $kid $curview]} break
5425 if {[rowofcommit $kid] < $row} {
5426 set ret [rowofcommit $kid]
5433 proc make_idlist {row} {
5434 global displayorder parentlist uparrowlen downarrowlen mingaplen
5435 global commitidx curview children
5437 set r [expr {$row - $mingaplen - $downarrowlen - 1}]
5441 set ra [expr {$row - $downarrowlen}]
5445 set rb [expr {$row + $uparrowlen}]
5446 if {$rb > $commitidx($curview)} {
5447 set rb $commitidx($curview)
5449 make_disporder $r [expr {$rb + 1}]
5451 for {} {$r < $ra} {incr r} {
5452 set nextid [lindex $displayorder [expr {$r + 1}]]
5453 foreach p [lindex $parentlist $r] {
5454 if {$p eq $nextid} continue
5455 set rn [nextuse $p $r]
5457 $rn <= $r + $downarrowlen + $mingaplen + $uparrowlen} {
5458 lappend ids [list [ordertoken $p] $p]
5462 for {} {$r < $row} {incr r} {
5463 set nextid [lindex $displayorder [expr {$r + 1}]]
5464 foreach p [lindex $parentlist $r] {
5465 if {$p eq $nextid} continue
5466 set rn [nextuse $p $r]
5467 if {$rn < 0 || $rn >= $row} {
5468 lappend ids [list [ordertoken $p] $p]
5472 set id [lindex $displayorder $row]
5473 lappend ids [list [ordertoken $id] $id]
5475 foreach p [lindex $parentlist $r] {
5476 set firstkid [lindex $children($curview,$p) 0]
5477 if {[rowofcommit $firstkid] < $row} {
5478 lappend ids [list [ordertoken $p] $p]
5482 set id [lindex $displayorder $r]
5484 set firstkid [lindex $children($curview,$id) 0]
5485 if {$firstkid ne {} && [rowofcommit $firstkid] < $row} {
5486 lappend ids [list [ordertoken $id] $id]
5491 foreach idx [lsort -unique $ids] {
5492 lappend idlist [lindex $idx 1]
5497 proc rowsequal {a b} {
5498 while {[set i [lsearch -exact $a {}]] >= 0} {
5499 set a [lreplace $a $i $i]
5501 while {[set i [lsearch -exact $b {}]] >= 0} {
5502 set b [lreplace $b $i $i]
5504 return [expr {$a eq $b}]
5507 proc makeupline {id row rend col} {
5508 global rowidlist uparrowlen downarrowlen mingaplen
5510 for {set r $rend} {1} {set r $rstart} {
5511 set rstart [prevuse $id $r]
5512 if {$rstart < 0} return
5513 if {$rstart < $row} break
5515 if {$rstart + $uparrowlen + $mingaplen + $downarrowlen < $rend} {
5516 set rstart [expr {$rend - $uparrowlen - 1}]
5518 for {set r $rstart} {[incr r] <= $row} {} {
5519 set idlist [lindex $rowidlist $r]
5520 if {$idlist ne {} && [lsearch -exact $idlist $id] < 0} {
5521 set col [idcol $idlist $id $col]
5522 lset rowidlist $r [linsert $idlist $col $id]
5528 proc layoutrows {row endrow} {
5529 global rowidlist rowisopt rowfinal displayorder
5530 global uparrowlen downarrowlen maxwidth mingaplen
5531 global children parentlist
5532 global commitidx viewcomplete curview
5534 make_disporder [expr {$row - 1}] [expr {$endrow + $uparrowlen}]
5537 set rm1 [expr {$row - 1}]
5538 foreach id [lindex $rowidlist $rm1] {
5543 set final [lindex $rowfinal $rm1]
5545 for {} {$row < $endrow} {incr row} {
5546 set rm1 [expr {$row - 1}]
5547 if {$rm1 < 0 || $idlist eq {}} {
5548 set idlist [make_idlist $row]
5551 set id [lindex $displayorder $rm1]
5552 set col [lsearch -exact $idlist $id]
5553 set idlist [lreplace $idlist $col $col]
5554 foreach p [lindex $parentlist $rm1] {
5555 if {[lsearch -exact $idlist $p] < 0} {
5556 set col [idcol $idlist $p $col]
5557 set idlist [linsert $idlist $col $p]
5558 # if not the first child, we have to insert a line going up
5559 if {$id ne [lindex $children($curview,$p) 0]} {
5560 makeupline $p $rm1 $row $col
5564 set id [lindex $displayorder $row]
5565 if {$row > $downarrowlen} {
5566 set termrow [expr {$row - $downarrowlen - 1}]
5567 foreach p [lindex $parentlist $termrow] {
5568 set i [lsearch -exact $idlist $p]
5569 if {$i < 0} continue
5570 set nr [nextuse $p $termrow]
5571 if {$nr < 0 || $nr >= $row + $mingaplen + $uparrowlen} {
5572 set idlist [lreplace $idlist $i $i]
5576 set col [lsearch -exact $idlist $id]
5578 set col [idcol $idlist $id]
5579 set idlist [linsert $idlist $col $id]
5580 if {$children($curview,$id) ne {}} {
5581 makeupline $id $rm1 $row $col
5584 set r [expr {$row + $uparrowlen - 1}]
5585 if {$r < $commitidx($curview)} {
5587 foreach p [lindex $parentlist $r] {
5588 if {[lsearch -exact $idlist $p] >= 0} continue
5589 set fk [lindex $children($curview,$p) 0]
5590 if {[rowofcommit $fk] < $row} {
5591 set x [idcol $idlist $p $x]
5592 set idlist [linsert $idlist $x $p]
5595 if {[incr r] < $commitidx($curview)} {
5596 set p [lindex $displayorder $r]
5597 if {[lsearch -exact $idlist $p] < 0} {
5598 set fk [lindex $children($curview,$p) 0]
5599 if {$fk ne {} && [rowofcommit $fk] < $row} {
5600 set x [idcol $idlist $p $x]
5601 set idlist [linsert $idlist $x $p]
5607 if {$final && !$viewcomplete($curview) &&
5608 $row + $uparrowlen + $mingaplen + $downarrowlen
5609 >= $commitidx($curview)} {
5612 set l [llength $rowidlist]
5614 lappend rowidlist $idlist
5616 lappend rowfinal $final
5617 } elseif {$row < $l} {
5618 if {![rowsequal $idlist [lindex $rowidlist $row]]} {
5619 lset rowidlist $row $idlist
5622 lset rowfinal $row $final
5624 set pad [ntimes [expr {$row - $l}] {}]
5625 set rowidlist [concat $rowidlist $pad]
5626 lappend rowidlist $idlist
5627 set rowfinal [concat $rowfinal $pad]
5628 lappend rowfinal $final
5629 set rowisopt [concat $rowisopt [ntimes [expr {$row - $l + 1}] 0]]
5635 proc changedrow {row} {
5636 global displayorder iddrawn rowisopt need_redisplay
5638 set l [llength $rowisopt]
5640 lset rowisopt $row 0
5641 if {$row + 1 < $l} {
5642 lset rowisopt [expr {$row + 1}] 0
5643 if {$row + 2 < $l} {
5644 lset rowisopt [expr {$row + 2}] 0
5648 set id [lindex $displayorder $row]
5649 if {[info exists iddrawn($id)]} {
5650 set need_redisplay 1
5654 proc insert_pad {row col npad} {
5657 set pad [ntimes $npad {}]
5658 set idlist [lindex $rowidlist $row]
5659 set bef [lrange $idlist 0 [expr {$col - 1}]]
5660 set aft [lrange $idlist $col end]
5661 set i [lsearch -exact $aft {}]
5663 set aft [lreplace $aft $i $i]
5665 lset rowidlist $row [concat $bef $pad $aft]
5669 proc optimize_rows {row col endrow} {
5670 global rowidlist rowisopt displayorder curview children
5675 for {} {$row < $endrow} {incr row; set col 0} {
5676 if {[lindex $rowisopt $row]} continue
5678 set y0 [expr {$row - 1}]
5679 set ym [expr {$row - 2}]
5680 set idlist [lindex $rowidlist $row]
5681 set previdlist [lindex $rowidlist $y0]
5682 if {$idlist eq {} || $previdlist eq {}} continue
5684 set pprevidlist [lindex $rowidlist $ym]
5685 if {$pprevidlist eq {}} continue
5691 for {} {$col < [llength $idlist]} {incr col} {
5692 set id [lindex $idlist $col]
5693 if {[lindex $previdlist $col] eq $id} continue
5698 set x0 [lsearch -exact $previdlist $id]
5699 if {$x0 < 0} continue
5700 set z [expr {$x0 - $col}]
5704 set xm [lsearch -exact $pprevidlist $id]
5706 set z0 [expr {$xm - $x0}]
5710 # if row y0 is the first child of $id then it's not an arrow
5711 if {[lindex $children($curview,$id) 0] ne
5712 [lindex $displayorder $y0]} {
5716 if {!$isarrow && $id ne [lindex $displayorder $row] &&
5717 [lsearch -exact [lindex $rowidlist [expr {$row+1}]] $id] < 0} {
5720 # Looking at lines from this row to the previous row,
5721 # make them go straight up if they end in an arrow on
5722 # the previous row; otherwise make them go straight up
5724 if {$z < -1 || ($z < 0 && $isarrow)} {
5725 # Line currently goes left too much;
5726 # insert pads in the previous row, then optimize it
5727 set npad [expr {-1 - $z + $isarrow}]
5728 insert_pad $y0 $x0 $npad
5730 optimize_rows $y0 $x0 $row
5732 set previdlist [lindex $rowidlist $y0]
5733 set x0 [lsearch -exact $previdlist $id]
5734 set z [expr {$x0 - $col}]
5736 set pprevidlist [lindex $rowidlist $ym]
5737 set xm [lsearch -exact $pprevidlist $id]
5738 set z0 [expr {$xm - $x0}]
5740 } elseif {$z > 1 || ($z > 0 && $isarrow)} {
5741 # Line currently goes right too much;
5742 # insert pads in this line
5743 set npad [expr {$z - 1 + $isarrow}]
5744 insert_pad $row $col $npad
5745 set idlist [lindex $rowidlist $row]
5747 set z [expr {$x0 - $col}]
5750 if {$z0 eq {} && !$isarrow && $ym >= 0} {
5751 # this line links to its first child on row $row-2
5752 set id [lindex $displayorder $ym]
5753 set xc [lsearch -exact $pprevidlist $id]
5755 set z0 [expr {$xc - $x0}]
5758 # avoid lines jigging left then immediately right
5759 if {$z0 ne {} && $z < 0 && $z0 > 0} {
5760 insert_pad $y0 $x0 1
5762 optimize_rows $y0 $x0 $row
5763 set previdlist [lindex $rowidlist $y0]
5767 # Find the first column that doesn't have a line going right
5768 for {set col [llength $idlist]} {[incr col -1] >= 0} {} {
5769 set id [lindex $idlist $col]
5770 if {$id eq {}} break
5771 set x0 [lsearch -exact $previdlist $id]
5773 # check if this is the link to the first child
5774 set kid [lindex $displayorder $y0]
5775 if {[lindex $children($curview,$id) 0] eq $kid} {
5776 # it is, work out offset to child
5777 set x0 [lsearch -exact $previdlist $kid]
5780 if {$x0 <= $col} break
5782 # Insert a pad at that column as long as it has a line and
5783 # isn't the last column
5784 if {$x0 >= 0 && [incr col] < [llength $idlist]} {
5785 set idlist [linsert $idlist $col {}]
5786 lset rowidlist $row $idlist
5794 global canvx0 linespc
5795 return [expr {$canvx0 + $col * $linespc}]
5799 global canvy0 linespc
5800 return [expr {$canvy0 + $row * $linespc}]
5803 proc linewidth {id} {
5804 global thickerline lthickness
5807 if {[info exists thickerline] && $id eq $thickerline} {
5808 set wid [expr {2 * $lthickness}]
5813 proc rowranges {id} {
5814 global curview children uparrowlen downarrowlen
5817 set kids $children($curview,$id)
5823 foreach child $kids {
5824 if {![commitinview $child $curview]} break
5825 set row [rowofcommit $child]
5826 if {![info exists prev]} {
5827 lappend ret [expr {$row + 1}]
5829 if {$row <= $prevrow} {
5830 puts "oops children of [shortids $id] out of order [shortids $child] $row <= [shortids $prev] $prevrow"
5832 # see if the line extends the whole way from prevrow to row
5833 if {$row > $prevrow + $uparrowlen + $downarrowlen &&
5834 [lsearch -exact [lindex $rowidlist \
5835 [expr {int(($row + $prevrow) / 2)}]] $id] < 0} {
5836 # it doesn't, see where it ends
5837 set r [expr {$prevrow + $downarrowlen}]
5838 if {[lsearch -exact [lindex $rowidlist $r] $id] < 0} {
5839 while {[incr r -1] > $prevrow &&
5840 [lsearch -exact [lindex $rowidlist $r] $id] < 0} {}
5842 while {[incr r] <= $row &&
5843 [lsearch -exact [lindex $rowidlist $r] $id] >= 0} {}
5847 # see where it starts up again
5848 set r [expr {$row - $uparrowlen}]
5849 if {[lsearch -exact [lindex $rowidlist $r] $id] < 0} {
5850 while {[incr r] < $row &&
5851 [lsearch -exact [lindex $rowidlist $r] $id] < 0} {}
5853 while {[incr r -1] >= $prevrow &&
5854 [lsearch -exact [lindex $rowidlist $r] $id] >= 0} {}
5860 if {$child eq $id} {
5869 proc drawlineseg {id row endrow arrowlow} {
5870 global rowidlist displayorder iddrawn linesegs
5871 global canv colormap linespc curview maxlinelen parentlist
5873 set cols [list [lsearch -exact [lindex $rowidlist $row] $id]]
5874 set le [expr {$row + 1}]
5877 set c [lsearch -exact [lindex $rowidlist $le] $id]
5883 set x [lindex $displayorder $le]
5888 if {[info exists iddrawn($x)] || $le == $endrow} {
5889 set c [lsearch -exact [lindex $rowidlist [expr {$le+1}]] $id]
5905 if {[info exists linesegs($id)]} {
5906 set lines $linesegs($id)
5908 set r0 [lindex $li 0]
5910 if {$r0 == $le && [lindex $li 1] - $row <= $maxlinelen} {
5920 set li [lindex $lines [expr {$i-1}]]
5921 set r1 [lindex $li 1]
5922 if {$r1 == $row && $le - [lindex $li 0] <= $maxlinelen} {
5927 set x [lindex $cols [expr {$le - $row}]]
5928 set xp [lindex $cols [expr {$le - 1 - $row}]]
5929 set dir [expr {$xp - $x}]
5931 set ith [lindex $lines $i 2]
5932 set coords [$canv coords $ith]
5933 set ah [$canv itemcget $ith -arrow]
5934 set arrowhigh [expr {$ah eq "first" || $ah eq "both"}]
5935 set x2 [lindex $cols [expr {$le + 1 - $row}]]
5936 if {$x2 ne {} && $x - $x2 == $dir} {
5937 set coords [lrange $coords 0 end-2]
5940 set coords [list [xc $le $x] [yc $le]]
5943 set itl [lindex $lines [expr {$i-1}] 2]
5944 set al [$canv itemcget $itl -arrow]
5945 set arrowlow [expr {$al eq "last" || $al eq "both"}]
5946 } elseif {$arrowlow} {
5947 if {[lsearch -exact [lindex $rowidlist [expr {$row-1}]] $id] >= 0 ||
5948 [lsearch -exact [lindex $parentlist [expr {$row-1}]] $id] >= 0} {
5952 set arrow [lindex {none first last both} [expr {$arrowhigh + 2*$arrowlow}]]
5953 for {set y $le} {[incr y -1] > $row} {} {
5955 set xp [lindex $cols [expr {$y - 1 - $row}]]
5956 set ndir [expr {$xp - $x}]
5957 if {$dir != $ndir || $xp < 0} {
5958 lappend coords [xc $y $x] [yc $y]
5964 # join parent line to first child
5965 set ch [lindex $displayorder $row]
5966 set xc [lsearch -exact [lindex $rowidlist $row] $ch]
5968 puts "oops: drawlineseg: child $ch not on row $row"
5969 } elseif {$xc != $x} {
5970 if {($arrowhigh && $le == $row + 1) || $dir == 0} {
5971 set d [expr {int(0.5 * $linespc)}]
5974 set x2 [expr {$x1 - $d}]
5976 set x2 [expr {$x1 + $d}]
5979 set y1 [expr {$y2 + $d}]
5980 lappend coords $x1 $y1 $x2 $y2
5981 } elseif {$xc < $x - 1} {
5982 lappend coords [xc $row [expr {$x-1}]] [yc $row]
5983 } elseif {$xc > $x + 1} {
5984 lappend coords [xc $row [expr {$x+1}]] [yc $row]
5988 lappend coords [xc $row $x] [yc $row]
5990 set xn [xc $row $xp]
5992 lappend coords $xn $yn
5996 set t [$canv create line $coords -width [linewidth $id] \
5997 -fill $colormap($id) -tags lines.$id -arrow $arrow]
6000 set lines [linsert $lines $i [list $row $le $t]]
6002 $canv coords $ith $coords
6003 if {$arrow ne $ah} {
6004 $canv itemconf $ith -arrow $arrow
6006 lset lines $i 0 $row
6009 set xo [lsearch -exact [lindex $rowidlist [expr {$row - 1}]] $id]
6010 set ndir [expr {$xo - $xp}]
6011 set clow [$canv coords $itl]
6012 if {$dir == $ndir} {
6013 set clow [lrange $clow 2 end]
6015 set coords [concat $coords $clow]
6017 lset lines [expr {$i-1}] 1 $le
6019 # coalesce two pieces
6021 set b [lindex $lines [expr {$i-1}] 0]
6022 set e [lindex $lines $i 1]
6023 set lines [lreplace $lines [expr {$i-1}] $i [list $b $e $itl]]
6025 $canv coords $itl $coords
6026 if {$arrow ne $al} {
6027 $canv itemconf $itl -arrow $arrow
6031 set linesegs($id) $lines
6035 proc drawparentlinks {id row} {
6036 global rowidlist canv colormap curview parentlist
6037 global idpos linespc
6039 set rowids [lindex $rowidlist $row]
6040 set col [lsearch -exact $rowids $id]
6041 if {$col < 0} return
6042 set olds [lindex $parentlist $row]
6043 set row2 [expr {$row + 1}]
6044 set x [xc $row $col]
6047 set d [expr {int(0.5 * $linespc)}]
6048 set ymid [expr {$y + $d}]
6049 set ids [lindex $rowidlist $row2]
6050 # rmx = right-most X coord used
6053 set i [lsearch -exact $ids $p]
6055 puts "oops, parent $p of $id not in list"
6058 set x2 [xc $row2 $i]
6062 set j [lsearch -exact $rowids $p]
6064 # drawlineseg will do this one for us
6068 # should handle duplicated parents here...
6069 set coords [list $x $y]
6071 # if attaching to a vertical segment, draw a smaller
6072 # slant for visual distinctness
6075 lappend coords [expr {$x2 + $d}] $y $x2 $ymid
6077 lappend coords [expr {$x2 - $d}] $y $x2 $ymid
6079 } elseif {$i < $col && $i < $j} {
6080 # segment slants towards us already
6081 lappend coords [xc $row $j] $y
6083 if {$i < $col - 1} {
6084 lappend coords [expr {$x2 + $linespc}] $y
6085 } elseif {$i > $col + 1} {
6086 lappend coords [expr {$x2 - $linespc}] $y
6088 lappend coords $x2 $y2
6091 lappend coords $x2 $y2
6093 set t [$canv create line $coords -width [linewidth $p] \
6094 -fill $colormap($p) -tags lines.$p]
6098 if {$rmx > [lindex $idpos($id) 1]} {
6099 lset idpos($id) 1 $rmx
6104 proc drawlines {id} {
6107 $canv itemconf lines.$id -width [linewidth $id]
6110 proc drawcmittext {id row col} {
6111 global linespc canv canv2 canv3 fgcolor curview
6112 global cmitlisted commitinfo rowidlist parentlist
6113 global rowtextx idpos idtags idheads idotherrefs
6114 global linehtag linentag linedtag selectedline
6115 global canvxmax boldids boldnameids fgcolor markedid
6116 global mainheadid nullid nullid2 circleitem circlecolors ctxbut
6117 global mainheadcirclecolor workingfilescirclecolor indexcirclecolor
6118 global circleoutlinecolor
6120 # listed is 0 for boundary, 1 for normal, 2 for negative, 3 for left, 4 for right
6121 set listed $cmitlisted($curview,$id)
6122 if {$id eq $nullid} {
6123 set ofill $workingfilescirclecolor
6124 } elseif {$id eq $nullid2} {
6125 set ofill $indexcirclecolor
6126 } elseif {$id eq $mainheadid} {
6127 set ofill $mainheadcirclecolor
6129 set ofill [lindex $circlecolors $listed]
6131 set x [xc $row $col]
6133 set orad [expr {$linespc / 3}]
6135 set t [$canv create oval [expr {$x - $orad}] [expr {$y - $orad}] \
6136 [expr {$x + $orad - 1}] [expr {$y + $orad - 1}] \
6137 -fill $ofill -outline $circleoutlinecolor -width 1 -tags circle]
6138 } elseif {$listed == 3} {
6139 # triangle pointing left for left-side commits
6140 set t [$canv create polygon \
6141 [expr {$x - $orad}] $y \
6142 [expr {$x + $orad - 1}] [expr {$y - $orad}] \
6143 [expr {$x + $orad - 1}] [expr {$y + $orad - 1}] \
6144 -fill $ofill -outline $circleoutlinecolor -width 1 -tags circle]
6146 # triangle pointing right for right-side commits
6147 set t [$canv create polygon \
6148 [expr {$x + $orad - 1}] $y \
6149 [expr {$x - $orad}] [expr {$y - $orad}] \
6150 [expr {$x - $orad}] [expr {$y + $orad - 1}] \
6151 -fill $ofill -outline $circleoutlinecolor -width 1 -tags circle]
6153 set circleitem($row) $t
6155 $canv bind $t <1> {selcanvline {} %x %y}
6156 set rmx [llength [lindex $rowidlist $row]]
6157 set olds [lindex $parentlist $row]
6159 set nextids [lindex $rowidlist [expr {$row + 1}]]
6161 set i [lsearch -exact $nextids $p]
6167 set xt [xc $row $rmx]
6168 set rowtextx($row) $xt
6169 set idpos($id) [list $x $xt $y]
6170 if {[info exists idtags($id)] || [info exists idheads($id)]
6171 || [info exists idotherrefs($id)]} {
6172 set xt [drawtags $id $x $xt $y]
6174 if {[lindex $commitinfo($id) 6] > 0} {
6175 set xt [drawnotesign $xt $y]
6177 set headline [lindex $commitinfo($id) 0]
6178 set name [lindex $commitinfo($id) 1]
6179 set date [lindex $commitinfo($id) 2]
6180 set date [formatdate $date]
6183 set isbold [ishighlighted $id]
6186 set font mainfontbold
6188 lappend boldnameids $id
6189 set nfont mainfontbold
6192 set linehtag($id) [$canv create text $xt $y -anchor w -fill $fgcolor \
6193 -text $headline -font $font -tags text]
6194 $canv bind $linehtag($id) $ctxbut "rowmenu %X %Y $id"
6195 set linentag($id) [$canv2 create text 3 $y -anchor w -fill $fgcolor \
6196 -text $name -font $nfont -tags text]
6197 set linedtag($id) [$canv3 create text 3 $y -anchor w -fill $fgcolor \
6198 -text $date -font mainfont -tags text]
6199 if {$selectedline == $row} {
6202 if {[info exists markedid] && $markedid eq $id} {
6205 set xr [expr {$xt + [font measure $font $headline]}]
6206 if {$xr > $canvxmax} {
6212 proc drawcmitrow {row} {
6213 global displayorder rowidlist nrows_drawn
6214 global iddrawn markingmatches
6215 global commitinfo numcommits
6216 global filehighlight fhighlights findpattern nhighlights
6217 global hlview vhighlights
6218 global highlight_related rhighlights
6220 if {$row >= $numcommits} return
6222 set id [lindex $displayorder $row]
6223 if {[info exists hlview] && ![info exists vhighlights($id)]} {
6224 askvhighlight $row $id
6226 if {[info exists filehighlight] && ![info exists fhighlights($id)]} {
6227 askfilehighlight $row $id
6229 if {$findpattern ne {} && ![info exists nhighlights($id)]} {
6230 askfindhighlight $row $id
6232 if {$highlight_related ne [mc "None"] && ![info exists rhighlights($id)]} {
6233 askrelhighlight $row $id
6235 if {![info exists iddrawn($id)]} {
6236 set col [lsearch -exact [lindex $rowidlist $row] $id]
6238 puts "oops, row $row id $id not in list"
6241 if {![info exists commitinfo($id)]} {
6245 drawcmittext $id $row $col
6249 if {$markingmatches} {
6250 markrowmatches $row $id
6254 proc drawcommits {row {endrow {}}} {
6255 global numcommits iddrawn displayorder curview need_redisplay
6256 global parentlist rowidlist rowfinal uparrowlen downarrowlen nrows_drawn
6261 if {$endrow eq {}} {
6264 if {$endrow >= $numcommits} {
6265 set endrow [expr {$numcommits - 1}]
6268 set rl1 [expr {$row - $downarrowlen - 3}]
6272 set ro1 [expr {$row - 3}]
6276 set r2 [expr {$endrow + $uparrowlen + 3}]
6277 if {$r2 > $numcommits} {
6280 for {set r $rl1} {$r < $r2} {incr r} {
6281 if {[lindex $rowidlist $r] ne {} && [lindex $rowfinal $r]} {
6285 set rl1 [expr {$r + 1}]
6291 optimize_rows $ro1 0 $r2
6292 if {$need_redisplay || $nrows_drawn > 2000} {
6296 # make the lines join to already-drawn rows either side
6297 set r [expr {$row - 1}]
6298 if {$r < 0 || ![info exists iddrawn([lindex $displayorder $r])]} {
6301 set er [expr {$endrow + 1}]
6302 if {$er >= $numcommits ||
6303 ![info exists iddrawn([lindex $displayorder $er])]} {
6306 for {} {$r <= $er} {incr r} {
6307 set id [lindex $displayorder $r]
6308 set wasdrawn [info exists iddrawn($id)]
6310 if {$r == $er} break
6311 set nextid [lindex $displayorder [expr {$r + 1}]]
6312 if {$wasdrawn && [info exists iddrawn($nextid)]} continue
6313 drawparentlinks $id $r
6315 set rowids [lindex $rowidlist $r]
6316 foreach lid $rowids {
6317 if {$lid eq {}} continue
6318 if {[info exists lineend($lid)] && $lineend($lid) > $r} continue
6320 # see if this is the first child of any of its parents
6321 foreach p [lindex $parentlist $r] {
6322 if {[lsearch -exact $rowids $p] < 0} {
6323 # make this line extend up to the child
6324 set lineend($p) [drawlineseg $p $r $er 0]
6328 set lineend($lid) [drawlineseg $lid $r $er 1]
6334 proc undolayout {row} {
6335 global uparrowlen mingaplen downarrowlen
6336 global rowidlist rowisopt rowfinal need_redisplay
6338 set r [expr {$row - ($uparrowlen + $mingaplen + $downarrowlen)}]
6342 if {[llength $rowidlist] > $r} {
6344 set rowidlist [lrange $rowidlist 0 $r]
6345 set rowfinal [lrange $rowfinal 0 $r]
6346 set rowisopt [lrange $rowisopt 0 $r]
6347 set need_redisplay 1
6352 proc drawvisible {} {
6353 global canv linespc curview vrowmod selectedline targetrow targetid
6354 global need_redisplay cscroll numcommits
6356 set fs [$canv yview]
6357 set ymax [lindex [$canv cget -scrollregion] 3]
6358 if {$ymax eq {} || $ymax == 0 || $numcommits == 0} return
6359 set f0 [lindex $fs 0]
6360 set f1 [lindex $fs 1]
6361 set y0 [expr {int($f0 * $ymax)}]
6362 set y1 [expr {int($f1 * $ymax)}]
6364 if {[info exists targetid]} {
6365 if {[commitinview $targetid $curview]} {
6366 set r [rowofcommit $targetid]
6367 if {$r != $targetrow} {
6368 # Fix up the scrollregion and change the scrolling position
6369 # now that our target row has moved.
6370 set diff [expr {($r - $targetrow) * $linespc}]
6373 set ymax [lindex [$canv cget -scrollregion] 3]
6376 set f0 [expr {$y0 / $ymax}]
6377 set f1 [expr {$y1 / $ymax}]
6378 allcanvs yview moveto $f0
6379 $cscroll set $f0 $f1
6380 set need_redisplay 1
6387 set row [expr {int(($y0 - 3) / $linespc) - 1}]
6388 set endrow [expr {int(($y1 - 3) / $linespc) + 1}]
6389 if {$endrow >= $vrowmod($curview)} {
6390 update_arcrows $curview
6392 if {$selectedline ne {} &&
6393 $row <= $selectedline && $selectedline <= $endrow} {
6394 set targetrow $selectedline
6395 } elseif {[info exists targetid]} {
6396 set targetrow [expr {int(($row + $endrow) / 2)}]
6398 if {[info exists targetrow]} {
6399 if {$targetrow >= $numcommits} {
6400 set targetrow [expr {$numcommits - 1}]
6402 set targetid [commitonrow $targetrow]
6404 drawcommits $row $endrow
6407 proc clear_display {} {
6408 global iddrawn linesegs need_redisplay nrows_drawn
6409 global vhighlights fhighlights nhighlights rhighlights
6410 global linehtag linentag linedtag boldids boldnameids
6413 unset -nocomplain iddrawn
6414 unset -nocomplain linesegs
6415 unset -nocomplain linehtag
6416 unset -nocomplain linentag
6417 unset -nocomplain linedtag
6420 unset -nocomplain vhighlights
6421 unset -nocomplain fhighlights
6422 unset -nocomplain nhighlights
6423 unset -nocomplain rhighlights
6424 set need_redisplay 0
6428 proc findcrossings {id} {
6429 global rowidlist parentlist numcommits displayorder
6433 foreach {s e} [rowranges $id] {
6434 if {$e >= $numcommits} {
6435 set e [expr {$numcommits - 1}]
6437 if {$e <= $s} continue
6438 for {set row $e} {[incr row -1] >= $s} {} {
6439 set x [lsearch -exact [lindex $rowidlist $row] $id]
6441 set olds [lindex $parentlist $row]
6442 set kid [lindex $displayorder $row]
6443 set kidx [lsearch -exact [lindex $rowidlist $row] $kid]
6444 if {$kidx < 0} continue
6445 set nextrow [lindex $rowidlist [expr {$row + 1}]]
6447 set px [lsearch -exact $nextrow $p]
6448 if {$px < 0} continue
6449 if {($kidx < $x && $x < $px) || ($px < $x && $x < $kidx)} {
6450 if {[lsearch -exact $ccross $p] >= 0} continue
6451 if {$x == $px + ($kidx < $px? -1: 1)} {
6453 } elseif {[lsearch -exact $cross $p] < 0} {
6460 return [concat $ccross {{}} $cross]
6463 proc assigncolor {id} {
6464 global colormap colors nextcolor
6465 global parents children children curview
6467 if {[info exists colormap($id)]} return
6468 set ncolors [llength $colors]
6469 if {[info exists children($curview,$id)]} {
6470 set kids $children($curview,$id)
6474 if {[llength $kids] == 1} {
6475 set child [lindex $kids 0]
6476 if {[info exists colormap($child)]
6477 && [llength $parents($curview,$child)] == 1} {
6478 set colormap($id) $colormap($child)
6484 foreach x [findcrossings $id] {
6486 # delimiter between corner crossings and other crossings
6487 if {[llength $badcolors] >= $ncolors - 1} break
6488 set origbad $badcolors
6490 if {[info exists colormap($x)]
6491 && [lsearch -exact $badcolors $colormap($x)] < 0} {
6492 lappend badcolors $colormap($x)
6495 if {[llength $badcolors] >= $ncolors} {
6496 set badcolors $origbad
6498 set origbad $badcolors
6499 if {[llength $badcolors] < $ncolors - 1} {
6500 foreach child $kids {
6501 if {[info exists colormap($child)]
6502 && [lsearch -exact $badcolors $colormap($child)] < 0} {
6503 lappend badcolors $colormap($child)
6505 foreach p $parents($curview,$child) {
6506 if {[info exists colormap($p)]
6507 && [lsearch -exact $badcolors $colormap($p)] < 0} {
6508 lappend badcolors $colormap($p)
6512 if {[llength $badcolors] >= $ncolors} {
6513 set badcolors $origbad
6516 for {set i 0} {$i <= $ncolors} {incr i} {
6517 set c [lindex $colors $nextcolor]
6518 if {[incr nextcolor] >= $ncolors} {
6521 if {[lsearch -exact $badcolors $c]} break
6523 set colormap($id) $c
6526 proc bindline {t id} {
6529 $canv bind $t <Enter> "lineenter %x %y $id"
6530 $canv bind $t <Motion> "linemotion %x %y $id"
6531 $canv bind $t <Leave> "lineleave $id"
6532 $canv bind $t <Button-1> "lineclick %x %y $id 1"
6535 proc graph_pane_width {} {
6539 set g [.tf.histframe.pwclist sashpos 0]
6541 set g [.tf.histframe.pwclist sash coord 0]
6543 return [lindex $g 0]
6546 proc totalwidth {l font extra} {
6549 set tot [expr {$tot + [font measure $font $str] + $extra}]
6554 proc drawtags {id x xt y1} {
6555 global idtags idheads idotherrefs mainhead
6556 global linespc lthickness
6557 global canv rowtextx curview fgcolor bgcolor ctxbut
6558 global headbgcolor headfgcolor headoutlinecolor remotebgcolor
6559 global tagbgcolor tagfgcolor tagoutlinecolor
6568 set maxwidth [expr {[graph_pane_width] * $maxtagpct / 100}]
6569 set delta [expr {int(0.5 * ($linespc - $lthickness))}]
6570 set extra [expr {$delta + $lthickness + $linespc}]
6572 if {[info exists idtags($id)]} {
6573 set marks $idtags($id)
6574 set ntags [llength $marks]
6575 if {$ntags > $maxtags ||
6576 [totalwidth $marks mainfont $extra] > $maxwidth} {
6577 # show just a single "n tags..." tag
6580 set marks [list "tag..."]
6582 set marks [list [format "%d tags..." $ntags]]
6587 if {[info exists idheads($id)]} {
6588 set marks [concat $marks $idheads($id)]
6589 set nheads [llength $idheads($id)]
6591 if {[info exists idotherrefs($id)]} {
6592 set marks [concat $marks $idotherrefs($id)]
6598 set yt [expr {$y1 - 0.5 * $linespc}]
6599 set yb [expr {$yt + $linespc - 1}]
6603 foreach tag $marks {
6605 if {$i >= $ntags && $i < $ntags + $nheads && $tag eq $mainhead} {
6606 set wid [font measure mainfontbold $tag]
6608 set wid [font measure mainfont $tag]
6612 set xt [expr {$xt + $wid + $extra}]
6614 set t [$canv create line $x $y1 [lindex $xvals end] $y1 \
6615 -width $lthickness -fill $reflinecolor -tags tag.$id]
6617 foreach tag $marks x $xvals wid $wvals {
6618 set tag_quoted [string map {% %%} $tag]
6619 set xl [expr {$x + $delta}]
6620 set xr [expr {$x + $delta + $wid + $lthickness}]
6622 if {[incr ntags -1] >= 0} {
6624 set t [$canv create polygon $x [expr {$yt + $delta}] $xl $yt \
6625 $xr $yt $xr $yb $xl $yb $x [expr {$yb - $delta}] \
6626 -width 1 -outline $tagoutlinecolor -fill $tagbgcolor \
6629 set tagclick [list showtags $id 1]
6631 set tagclick [list showtag $tag_quoted 1]
6633 $canv bind $t <1> $tagclick
6634 set rowtextx([rowofcommit $id]) [expr {$xr + $linespc}]
6636 # draw a head or other ref
6637 if {[incr nheads -1] >= 0} {
6638 set col $headbgcolor
6639 if {$tag eq $mainhead} {
6640 set font mainfontbold
6645 set xl [expr {$xl - $delta/2}]
6646 $canv create polygon $x $yt $xr $yt $xr $yb $x $yb \
6647 -width 1 -outline black -fill $col -tags tag.$id
6648 if {[regexp {^(remotes/.*/|remotes/)} $tag match remoteprefix]} {
6649 set rwid [font measure mainfont $remoteprefix]
6650 set xi [expr {$x + 1}]
6651 set yti [expr {$yt + 1}]
6652 set xri [expr {$x + $rwid}]
6653 $canv create polygon $xi $yti $xri $yti $xri $yb $xi $yb \
6654 -width 0 -fill $remotebgcolor -tags tag.$id
6657 set t [$canv create text $xl $y1 -anchor w -text $tag -fill $headfgcolor \
6658 -font $font -tags [list tag.$id text]]
6660 $canv bind $t <1> $tagclick
6661 } elseif {$nheads >= 0} {
6662 $canv bind $t $ctxbut [list headmenu %X %Y $id $tag_quoted]
6668 proc drawnotesign {xt y} {
6669 global linespc canv fgcolor
6671 set orad [expr {$linespc / 3}]
6672 set t [$canv create rectangle [expr {$xt - $orad}] [expr {$y - $orad}] \
6673 [expr {$xt + $orad - 1}] [expr {$y + $orad - 1}] \
6674 -fill yellow -outline $fgcolor -width 1 -tags circle]
6675 set xt [expr {$xt + $orad * 3}]
6679 proc xcoord {i level ln} {
6680 global canvx0 xspc1 xspc2
6682 set x [expr {$canvx0 + $i * $xspc1($ln)}]
6683 if {$i > 0 && $i == $level} {
6684 set x [expr {$x + 0.5 * ($xspc2 - $xspc1($ln))}]
6685 } elseif {$i > $level} {
6686 set x [expr {$x + $xspc2 - $xspc1($ln)}]
6691 proc show_status {msg} {
6696 $canv create text 3 3 -anchor nw -text $msg -font mainfont \
6697 -tags text -fill $fgcolor
6700 # Don't change the text pane cursor if it is currently the hand cursor,
6701 # showing that we are over a sha1 ID link.
6702 proc settextcursor {c} {
6703 global ctext curtextcursor
6705 if {[$ctext cget -cursor] == $curtextcursor} {
6706 $ctext config -cursor $c
6708 set curtextcursor $c
6711 proc nowbusy {what {name {}}} {
6712 global isbusy busyname statusw
6714 if {[array names isbusy] eq {}} {
6715 . config -cursor watch
6719 set busyname($what) $name
6721 $statusw conf -text $name
6725 proc notbusy {what} {
6726 global isbusy maincursor textcursor busyname statusw
6730 if {$busyname($what) ne {} &&
6731 [$statusw cget -text] eq $busyname($what)} {
6732 $statusw conf -text {}
6735 if {[array names isbusy] eq {}} {
6736 . config -cursor $maincursor
6737 settextcursor $textcursor
6741 proc findmatches {f} {
6742 global findtype findstring
6743 if {$findtype == [mc "Regexp"]} {
6744 set matches [regexp -indices -all -inline $findstring $f]
6747 if {$findtype == [mc "IgnCase"]} {
6748 set f [string tolower $f]
6749 set fs [string tolower $fs]
6753 set l [string length $fs]
6754 while {[set j [string first $fs $f $i]] >= 0} {
6755 lappend matches [list $j [expr {$j+$l-1}]]
6756 set i [expr {$j + $l}]
6762 proc dofind {{dirn 1} {wrap 1}} {
6763 global findstring findstartline findcurline selectedline numcommits
6764 global gdttype filehighlight fh_serial find_dirn findallowwrap
6766 if {[info exists find_dirn]} {
6767 if {$find_dirn == $dirn} return
6771 if {$findstring eq {} || $numcommits == 0} return
6772 if {$selectedline eq {}} {
6773 set findstartline [lindex [visiblerows] [expr {$dirn < 0}]]
6775 set findstartline $selectedline
6777 set findcurline $findstartline
6778 nowbusy finding [mc "Searching"]
6779 if {$gdttype ne [mc "containing:"] && ![info exists filehighlight]} {
6780 after cancel do_file_hl $fh_serial
6781 do_file_hl $fh_serial
6784 set findallowwrap $wrap
6788 proc stopfinding {} {
6789 global find_dirn findcurline fprogcoord
6791 if {[info exists find_dirn]} {
6802 global commitdata commitinfo numcommits findpattern findloc
6803 global findstartline findcurline findallowwrap
6804 global find_dirn gdttype fhighlights fprogcoord
6805 global curview varcorder vrownum varccommits vrowmod
6807 if {![info exists find_dirn]} {
6810 set fldtypes [list [mc "Headline"] [mc "Author"] "" [mc "Committer"] "" [mc "Comments"]]
6813 if {$find_dirn > 0} {
6815 if {$l >= $numcommits} {
6818 if {$l <= $findstartline} {
6819 set lim [expr {$findstartline + 1}]
6822 set moretodo $findallowwrap
6829 if {$l >= $findstartline} {
6830 set lim [expr {$findstartline - 1}]
6833 set moretodo $findallowwrap
6836 set n [expr {($lim - $l) * $find_dirn}]
6841 if {$l + ($find_dirn > 0? $n: 1) > $vrowmod($curview)} {
6842 update_arcrows $curview
6846 set ai [bsearch $vrownum($curview) $l]
6847 set a [lindex $varcorder($curview) $ai]
6848 set arow [lindex $vrownum($curview) $ai]
6849 set ids [lindex $varccommits($curview,$a)]
6850 set arowend [expr {$arow + [llength $ids]}]
6851 if {$gdttype eq [mc "containing:"]} {
6852 for {} {$n > 0} {incr n -1; incr l $find_dirn} {
6853 if {$l < $arow || $l >= $arowend} {
6855 set a [lindex $varcorder($curview) $ai]
6856 set arow [lindex $vrownum($curview) $ai]
6857 set ids [lindex $varccommits($curview,$a)]
6858 set arowend [expr {$arow + [llength $ids]}]
6860 set id [lindex $ids [expr {$l - $arow}]]
6861 # shouldn't happen unless git log doesn't give all the commits...
6862 if {![info exists commitdata($id)] ||
6863 ![doesmatch $commitdata($id)]} {
6866 if {![info exists commitinfo($id)]} {
6869 set info $commitinfo($id)
6870 foreach f $info ty $fldtypes {
6871 if {$ty eq ""} continue
6872 if {($findloc eq [mc "All fields"] || $findloc eq $ty) &&
6881 for {} {$n > 0} {incr n -1; incr l $find_dirn} {
6882 if {$l < $arow || $l >= $arowend} {
6884 set a [lindex $varcorder($curview) $ai]
6885 set arow [lindex $vrownum($curview) $ai]
6886 set ids [lindex $varccommits($curview,$a)]
6887 set arowend [expr {$arow + [llength $ids]}]
6889 set id [lindex $ids [expr {$l - $arow}]]
6890 if {![info exists fhighlights($id)]} {
6891 # this sets fhighlights($id) to -1
6892 askfilehighlight $l $id
6894 if {$fhighlights($id) > 0} {
6898 if {$fhighlights($id) < 0} {
6901 set findcurline [expr {$l - $find_dirn}]
6906 if {$found || ($domore && !$moretodo)} {
6922 set findcurline [expr {$l - $find_dirn}]
6924 set n [expr {($findcurline - $findstartline) * $find_dirn - 1}]
6928 set fprogcoord [expr {$n * 1.0 / $numcommits}]
6933 proc findselectline {l} {
6934 global findloc commentend ctext findcurline markingmatches gdttype
6936 set markingmatches [expr {$gdttype eq [mc "containing:"]}]
6939 if {$markingmatches &&
6940 ($findloc eq [mc "All fields"] || $findloc eq [mc "Comments"])} {
6941 # highlight the matches in the comments
6942 set f [$ctext get 1.0 $commentend]
6943 set matches [findmatches $f]
6944 foreach match $matches {
6945 set start [lindex $match 0]
6946 set end [expr {[lindex $match 1] + 1}]
6947 $ctext tag add found "1.0 + $start c" "1.0 + $end c"
6953 # mark the bits of a headline or author that match a find string
6954 proc markmatches {canv l str tag matches font row} {
6957 set bbox [$canv bbox $tag]
6958 set x0 [lindex $bbox 0]
6959 set y0 [lindex $bbox 1]
6960 set y1 [lindex $bbox 3]
6961 foreach match $matches {
6962 set start [lindex $match 0]
6963 set end [lindex $match 1]
6964 if {$start > $end} continue
6965 set xoff [font measure $font [string range $str 0 [expr {$start-1}]]]
6966 set xlen [font measure $font [string range $str 0 [expr {$end}]]]
6967 set t [$canv create rect [expr {$x0+$xoff}] $y0 \
6968 [expr {$x0+$xlen+2}] $y1 \
6969 -outline {} -tags [list match$l matches] -fill yellow]
6971 if {$row == $selectedline} {
6972 $canv raise $t secsel
6977 proc unmarkmatches {} {
6978 global markingmatches
6980 allcanvs delete matches
6981 set markingmatches 0
6985 proc selcanvline {w x y} {
6986 global canv canvy0 ctext linespc
6988 set ymax [lindex [$canv cget -scrollregion] 3]
6989 if {$ymax == {}} return
6990 set yfrac [lindex [$canv yview] 0]
6991 set y [expr {$y + $yfrac * $ymax}]
6992 set l [expr {int(($y - $canvy0) / $linespc + 0.5)}]
6997 set xmax [lindex [$canv cget -scrollregion] 2]
6998 set xleft [expr {[lindex [$canv xview] 0] * $xmax}]
6999 if {![info exists rowtextx($l)] || $xleft + $x < $rowtextx($l)} return
7005 proc commit_descriptor {p} {
7007 if {![info exists commitinfo($p)]} {
7011 if {[llength $commitinfo($p)] > 1} {
7012 set l [lindex $commitinfo($p) 0]
7017 # append some text to the ctext widget, and make any SHA1 ID
7018 # that we know about be a clickable link.
7019 proc appendwithlinks {text tags} {
7020 global ctext linknum curview
7022 set start [$ctext index "end - 1c"]
7023 $ctext insert end $text $tags
7024 set links [regexp -indices -all -inline {(?:\m|-g)[0-9a-f]{6,40}\M} $text]
7028 set linkid [string range $text $s $e]
7030 $ctext tag delete link$linknum
7031 $ctext tag add link$linknum "$start + $s c" "$start + $e c"
7032 setlink $linkid link$linknum
7037 proc setlink {id lk} {
7038 global curview ctext pendinglinks
7041 if {[string range $id 0 1] eq "-g"} {
7042 set id [string range $id 2 end]
7046 if {[string length $id] < 40} {
7047 set matches [longid $id]
7048 if {[llength $matches] > 0} {
7049 if {[llength $matches] > 1} return
7051 set id [lindex $matches 0]
7054 set known [commitinview $id $curview]
7057 $ctext tag conf $lk -foreground $linkfgcolor -underline 1
7058 $ctext tag bind $lk <1> [list selbyid $id]
7059 $ctext tag bind $lk <Enter> {linkcursor %W 1}
7060 $ctext tag bind $lk <Leave> {linkcursor %W -1}
7062 lappend pendinglinks($id) $lk
7063 interestedin $id {makelink %P}
7067 proc appendshortlink {id {pre {}} {post {}}} {
7068 global ctext linknum
7070 $ctext insert end $pre
7071 $ctext tag delete link$linknum
7072 $ctext insert end [string range $id 0 7] link$linknum
7073 $ctext insert end $post
7074 setlink $id link$linknum
7078 proc makelink {id} {
7081 if {![info exists pendinglinks($id)]} return
7082 foreach lk $pendinglinks($id) {
7085 unset pendinglinks($id)
7088 proc linkcursor {w inc} {
7089 global linkentercount curtextcursor
7091 if {[incr linkentercount $inc] > 0} {
7092 $w configure -cursor hand2
7094 $w configure -cursor $curtextcursor
7095 if {$linkentercount < 0} {
7096 set linkentercount 0
7101 proc viewnextline {dir} {
7105 set ymax [lindex [$canv cget -scrollregion] 3]
7106 set wnow [$canv yview]
7107 set wtop [expr {[lindex $wnow 0] * $ymax}]
7108 set newtop [expr {$wtop + $dir * $linespc}]
7111 } elseif {$newtop > $ymax} {
7114 allcanvs yview moveto [expr {$newtop * 1.0 / $ymax}]
7117 # add a list of tag or branch names at position pos
7118 # returns the number of names inserted
7119 proc appendrefs {pos ids var} {
7120 global ctext linknum curview $var maxrefs visiblerefs mainheadid
7122 if {[catch {$ctext index $pos}]} {
7125 $ctext conf -state normal
7126 $ctext delete $pos "$pos lineend"
7129 foreach tag [set $var\($id\)] {
7130 lappend tags [list $tag $id]
7135 set tags [lsort -index 0 -decreasing $tags]
7138 if {[llength $tags] > $maxrefs} {
7139 # If we are displaying heads, and there are too many,
7140 # see if there are some important heads to display.
7141 # Currently that are the current head and heads listed in $visiblerefs option
7143 if {$var eq "idheads"} {
7146 set hname [lindex $ti 0]
7147 set id [lindex $ti 1]
7148 if {([lsearch -exact $visiblerefs $hname] != -1 || $id eq $mainheadid) &&
7149 [llength $itags] < $maxrefs} {
7158 set str [mc "and many more"]
7163 $ctext insert $pos "$str ([llength $tags])"
7164 set nutags [llength $tags]
7169 set id [lindex $ti 1]
7172 $ctext tag delete $lk
7173 $ctext insert $pos $sep
7174 $ctext insert $pos [lindex $ti 0] $lk
7178 $ctext tag add wwrap "$pos linestart" "$pos lineend"
7179 $ctext conf -state disabled
7180 return [expr {[llength $tags] + $nutags}]
7183 # called when we have finished computing the nearby tags
7184 proc dispneartags {delay} {
7185 global selectedline currentid showneartags tagphase
7187 if {$selectedline eq {} || !$showneartags} return
7188 after cancel dispnexttag
7190 after 200 dispnexttag
7193 after idle dispnexttag
7198 proc dispnexttag {} {
7199 global selectedline currentid showneartags tagphase ctext
7201 if {$selectedline eq {} || !$showneartags} return
7202 switch -- $tagphase {
7204 set dtags [desctags $currentid]
7206 appendrefs precedes $dtags idtags
7210 set atags [anctags $currentid]
7212 appendrefs follows $atags idtags
7216 set dheads [descheads $currentid]
7217 if {$dheads ne {}} {
7218 if {[appendrefs branch $dheads idheads] > 1
7219 && [$ctext get "branch -3c"] eq "h"} {
7220 # turn "Branch" into "Branches"
7221 $ctext conf -state normal
7222 $ctext insert "branch -2c" "es"
7223 $ctext conf -state disabled
7228 if {[incr tagphase] <= 2} {
7229 after idle dispnexttag
7233 proc make_secsel {id} {
7234 global linehtag linentag linedtag canv canv2 canv3
7236 if {![info exists linehtag($id)]} return
7238 set t [eval $canv create rect [$canv bbox $linehtag($id)] -outline {{}} \
7239 -tags secsel -fill [$canv cget -selectbackground]]
7241 $canv2 delete secsel
7242 set t [eval $canv2 create rect [$canv2 bbox $linentag($id)] -outline {{}} \
7243 -tags secsel -fill [$canv2 cget -selectbackground]]
7245 $canv3 delete secsel
7246 set t [eval $canv3 create rect [$canv3 bbox $linedtag($id)] -outline {{}} \
7247 -tags secsel -fill [$canv3 cget -selectbackground]]
7251 proc make_idmark {id} {
7252 global linehtag canv fgcolor
7254 if {![info exists linehtag($id)]} return
7256 set t [eval $canv create rect [$canv bbox $linehtag($id)] \
7257 -tags markid -outline $fgcolor]
7261 proc selectline {l isnew {desired_loc {}} {switch_to_patch 0}} {
7262 global canv ctext commitinfo selectedline
7263 global canvy0 linespc parents children curview
7264 global currentid sha1entry
7265 global commentend idtags linknum
7266 global mergemax numcommits pending_select
7267 global cmitmode showneartags allcommits
7268 global targetrow targetid lastscrollrows
7269 global autoselect autosellen jump_to_here
7272 unset -nocomplain pending_select
7277 if {$l < 0 || $l >= $numcommits} return
7278 set id [commitonrow $l]
7283 if {$lastscrollrows < $numcommits} {
7287 if {$cmitmode ne "patch" && $switch_to_patch} {
7288 set cmitmode "patch"
7291 set y [expr {$canvy0 + $l * $linespc}]
7292 set ymax [lindex [$canv cget -scrollregion] 3]
7293 set ytop [expr {$y - $linespc - 1}]
7294 set ybot [expr {$y + $linespc + 1}]
7295 set wnow [$canv yview]
7296 set wtop [expr {[lindex $wnow 0] * $ymax}]
7297 set wbot [expr {[lindex $wnow 1] * $ymax}]
7298 set wh [expr {$wbot - $wtop}]
7300 if {$ytop < $wtop} {
7301 if {$ybot < $wtop} {
7302 set newtop [expr {$y - $wh / 2.0}]
7305 if {$newtop > $wtop - $linespc} {
7306 set newtop [expr {$wtop - $linespc}]
7309 } elseif {$ybot > $wbot} {
7310 if {$ytop > $wbot} {
7311 set newtop [expr {$y - $wh / 2.0}]
7313 set newtop [expr {$ybot - $wh}]
7314 if {$newtop < $wtop + $linespc} {
7315 set newtop [expr {$wtop + $linespc}]
7319 if {$newtop != $wtop} {
7323 allcanvs yview moveto [expr {$newtop * 1.0 / $ymax}]
7330 addtohistory [list selbyid $id 0] savecmitpos
7333 $sha1entry delete 0 end
7334 $sha1entry insert 0 $id
7336 $sha1entry selection range 0 $autosellen
7340 $ctext conf -state normal
7343 if {![info exists commitinfo($id)]} {
7346 set info $commitinfo($id)
7347 set date [formatdate [lindex $info 2]]
7348 $ctext insert end "[mc "Author"]: [lindex $info 1] $date\n"
7349 set date [formatdate [lindex $info 4]]
7350 $ctext insert end "[mc "Committer"]: [lindex $info 3] $date\n"
7351 if {[info exists idtags($id)]} {
7352 $ctext insert end [mc "Tags:"]
7353 foreach tag $idtags($id) {
7354 $ctext insert end " $tag"
7356 $ctext insert end "\n"
7360 set olds $parents($curview,$id)
7361 if {[llength $olds] > 1} {
7364 if {$np >= $mergemax} {
7369 $ctext insert end "[mc "Parent"]: " $tag
7370 appendwithlinks [commit_descriptor $p] {}
7375 append headers "[mc "Parent"]: [commit_descriptor $p]"
7379 foreach c $children($curview,$id) {
7380 append headers "[mc "Child"]: [commit_descriptor $c]"
7383 # make anything that looks like a SHA1 ID be a clickable link
7384 appendwithlinks $headers {}
7385 if {$showneartags} {
7386 if {![info exists allcommits]} {
7389 $ctext insert end "[mc "Branch"]: "
7390 $ctext mark set branch "end -1c"
7391 $ctext mark gravity branch left
7392 $ctext insert end "\n[mc "Follows"]: "
7393 $ctext mark set follows "end -1c"
7394 $ctext mark gravity follows left
7395 $ctext insert end "\n[mc "Precedes"]: "
7396 $ctext mark set precedes "end -1c"
7397 $ctext mark gravity precedes left
7398 $ctext insert end "\n"
7401 $ctext insert end "\n"
7402 set comment [lindex $info 5]
7403 if {[string first "\r" $comment] >= 0} {
7404 set comment [string map {"\r" "\n "} $comment]
7406 appendwithlinks $comment {comment}
7408 $ctext tag remove found 1.0 end
7409 $ctext conf -state disabled
7410 set commentend [$ctext index "end - 1c"]
7412 set jump_to_here $desired_loc
7413 init_flist [mc "Comments"]
7414 if {$cmitmode eq "tree"} {
7416 } elseif {$vinlinediff($curview) == 1} {
7418 } elseif {[llength $olds] <= 1} {
7425 proc selfirstline {} {
7430 proc sellastline {} {
7433 set l [expr {$numcommits - 1}]
7437 proc selnextline {dir} {
7440 if {$selectedline eq {}} return
7441 set l [expr {$selectedline + $dir}]
7446 proc selnextpage {dir} {
7447 global canv linespc selectedline numcommits
7449 set lpp [expr {([winfo height $canv] - 2) / $linespc}]
7453 allcanvs yview scroll [expr {$dir * $lpp}] units
7455 if {$selectedline eq {}} return
7456 set l [expr {$selectedline + $dir * $lpp}]
7459 } elseif {$l >= $numcommits} {
7460 set l [expr $numcommits - 1]
7466 proc unselectline {} {
7467 global selectedline currentid
7470 unset -nocomplain currentid
7471 allcanvs delete secsel
7475 proc reselectline {} {
7478 if {$selectedline ne {}} {
7479 selectline $selectedline 0
7483 proc addtohistory {cmd {saveproc {}}} {
7484 global history historyindex curview
7488 set elt [list $curview $cmd $saveproc {}]
7489 if {$historyindex > 0
7490 && [lindex $history [expr {$historyindex - 1}]] == $elt} {
7494 if {$historyindex < [llength $history]} {
7495 set history [lreplace $history $historyindex end $elt]
7497 lappend history $elt
7500 if {$historyindex > 1} {
7501 .tf.bar.leftbut conf -state normal
7503 .tf.bar.leftbut conf -state disabled
7505 .tf.bar.rightbut conf -state disabled
7508 # save the scrolling position of the diff display pane
7509 proc save_position {} {
7510 global historyindex history
7512 if {$historyindex < 1} return
7513 set hi [expr {$historyindex - 1}]
7514 set fn [lindex $history $hi 2]
7516 lset history $hi 3 [eval $fn]
7520 proc unset_posvars {} {
7523 if {[info exists last_posvars]} {
7524 foreach {var val} $last_posvars {
7526 unset -nocomplain $var
7533 global curview last_posvars
7535 set view [lindex $elt 0]
7536 set cmd [lindex $elt 1]
7537 set pv [lindex $elt 3]
7538 if {$curview != $view} {
7542 foreach {var val} $pv {
7546 set last_posvars $pv
7551 global history historyindex
7554 if {$historyindex > 1} {
7556 incr historyindex -1
7557 godo [lindex $history [expr {$historyindex - 1}]]
7558 .tf.bar.rightbut conf -state normal
7560 if {$historyindex <= 1} {
7561 .tf.bar.leftbut conf -state disabled
7566 global history historyindex
7569 if {$historyindex < [llength $history]} {
7571 set cmd [lindex $history $historyindex]
7574 .tf.bar.leftbut conf -state normal
7576 if {$historyindex >= [llength $history]} {
7577 .tf.bar.rightbut conf -state disabled
7581 proc go_to_parent {i} {
7582 global parents curview targetid
7583 set ps $parents($curview,$targetid)
7584 if {[llength $ps] >= $i} {
7585 selbyid [lindex $ps [expr $i - 1]]
7590 global treefilelist treeidlist diffids diffmergeid treepending
7591 global nullid nullid2
7594 unset -nocomplain diffmergeid
7595 if {![info exists treefilelist($id)]} {
7596 if {![info exists treepending]} {
7597 if {$id eq $nullid} {
7598 set cmd [list | git ls-files]
7599 } elseif {$id eq $nullid2} {
7600 set cmd [list | git ls-files --stage -t]
7602 set cmd [list | git ls-tree -r $id]
7604 if {[catch {set gtf [open $cmd r]}]} {
7608 set treefilelist($id) {}
7609 set treeidlist($id) {}
7610 fconfigure $gtf -blocking 0 -encoding binary
7611 filerun $gtf [list gettreeline $gtf $id]
7618 proc gettreeline {gtf id} {
7619 global treefilelist treeidlist treepending cmitmode diffids nullid nullid2
7622 while {[incr nl] <= 1000 && [gets $gtf line] >= 0} {
7623 if {$diffids eq $nullid} {
7626 set i [string first "\t" $line]
7627 if {$i < 0} continue
7628 set fname [string range $line [expr {$i+1}] end]
7629 set line [string range $line 0 [expr {$i-1}]]
7630 if {$diffids ne $nullid2 && [lindex $line 1] ne "blob"} continue
7631 set sha1 [lindex $line 2]
7632 lappend treeidlist($id) $sha1
7634 if {[string index $fname 0] eq "\""} {
7635 set fname [lindex $fname 0]
7637 set fname [encoding convertfrom $fname]
7638 lappend treefilelist($id) $fname
7641 return [expr {$nl >= 1000? 2: 1}]
7645 if {$cmitmode ne "tree"} {
7646 if {![info exists diffmergeid]} {
7647 gettreediffs $diffids
7649 } elseif {$id ne $diffids} {
7658 global treefilelist treeidlist diffids nullid nullid2
7659 global ctext_file_names ctext_file_lines
7660 global ctext commentend
7662 set i [lsearch -exact $treefilelist($diffids) $f]
7664 puts "oops, $f not in list for id $diffids"
7667 if {$diffids eq $nullid} {
7668 if {[catch {set bf [open $f r]} err]} {
7669 puts "oops, can't read $f: $err"
7673 set blob [lindex $treeidlist($diffids) $i]
7674 if {[catch {set bf [open [concat | git cat-file blob $blob] r]} err]} {
7675 puts "oops, error reading blob $blob: $err"
7679 fconfigure $bf -blocking 0 -encoding [get_path_encoding $f]
7680 filerun $bf [list getblobline $bf $diffids]
7681 $ctext config -state normal
7682 clear_ctext $commentend
7683 lappend ctext_file_names $f
7684 lappend ctext_file_lines [lindex [split $commentend "."] 0]
7685 $ctext insert end "\n"
7686 $ctext insert end "$f\n" filesep
7687 $ctext config -state disabled
7688 $ctext yview $commentend
7692 proc getblobline {bf id} {
7693 global diffids cmitmode ctext
7695 if {$id ne $diffids || $cmitmode ne "tree"} {
7699 $ctext config -state normal
7701 while {[incr nl] <= 1000 && [gets $bf line] >= 0} {
7702 $ctext insert end "$line\n"
7705 global jump_to_here ctext_file_names commentend
7707 # delete last newline
7708 $ctext delete "end - 2c" "end - 1c"
7710 if {$jump_to_here ne {} &&
7711 [lindex $jump_to_here 0] eq [lindex $ctext_file_names 0]} {
7712 set lnum [expr {[lindex $jump_to_here 1] +
7713 [lindex [split $commentend .] 0]}]
7714 mark_ctext_line $lnum
7716 $ctext config -state disabled
7719 $ctext config -state disabled
7720 return [expr {$nl >= 1000? 2: 1}]
7723 proc mark_ctext_line {lnum} {
7724 global ctext markbgcolor
7726 $ctext tag delete omark
7727 $ctext tag add omark $lnum.0 "$lnum.0 + 1 line"
7728 $ctext tag conf omark -background $markbgcolor
7732 proc mergediff {id} {
7734 global diffids treediffs
7735 global parents curview
7739 set treediffs($id) {}
7740 set np [llength $parents($curview,$id)]
7745 proc startdiff {ids} {
7746 global treediffs diffids treepending diffmergeid nullid nullid2
7750 unset -nocomplain diffmergeid
7751 if {![info exists treediffs($ids)] ||
7752 [lsearch -exact $ids $nullid] >= 0 ||
7753 [lsearch -exact $ids $nullid2] >= 0} {
7754 if {![info exists treepending]} {
7762 proc showinlinediff {ids} {
7763 global commitinfo commitdata ctext
7766 set info $commitinfo($ids)
7767 set diff [lindex $info 7]
7768 set difflines [split $diff "\n"]
7774 foreach line $difflines {
7775 if {![string compare -length 5 "diff " $line]} {
7777 } elseif {$inhdr && ![string compare -length 4 "+++ " $line]} {
7778 # offset also accounts for the b/ prefix
7779 lappend treediff [string range $line 6 end]
7784 set treediffs($ids) $treediff
7787 $ctext conf -state normal
7788 foreach line $difflines {
7789 parseblobdiffline $ids $line
7791 maybe_scroll_ctext 1
7792 $ctext conf -state disabled
7795 # If the filename (name) is under any of the passed filter paths
7796 # then return true to include the file in the listing.
7797 proc path_filter {filter name} {
7798 set worktree [gitworktree]
7800 set fq_p [file normalize $p]
7801 set fq_n [file normalize [file join $worktree $name]]
7802 if {[string match [file normalize $fq_p]* $fq_n]} {
7809 proc addtocflist {ids} {
7812 add_flist $treediffs($ids)
7816 proc diffcmd {ids flags} {
7817 global log_showroot nullid nullid2 git_version
7819 set i [lsearch -exact $ids $nullid]
7820 set j [lsearch -exact $ids $nullid2]
7822 if {[llength $ids] > 1 && $j < 0} {
7823 # comparing working directory with some specific revision
7824 set cmd [concat | git diff-index $flags]
7826 lappend cmd -R [lindex $ids 1]
7828 lappend cmd [lindex $ids 0]
7831 # comparing working directory with index
7832 set cmd [concat | git diff-files $flags]
7837 } elseif {$j >= 0} {
7838 if {[package vcompare $git_version "1.7.2"] >= 0} {
7839 set flags "$flags --ignore-submodules=dirty"
7841 set cmd [concat | git diff-index --cached $flags]
7842 if {[llength $ids] > 1} {
7843 # comparing index with specific revision
7845 lappend cmd -R [lindex $ids 1]
7847 lappend cmd [lindex $ids 0]
7850 # comparing index with HEAD
7854 if {$log_showroot} {
7855 lappend flags --root
7857 set cmd [concat | git diff-tree -r $flags $ids]
7862 proc gettreediffs {ids} {
7863 global treediff treepending limitdiffs vfilelimit curview
7865 set cmd [diffcmd $ids {--no-commit-id}]
7866 if {$limitdiffs && $vfilelimit($curview) ne {}} {
7867 set cmd [concat $cmd -- $vfilelimit($curview)]
7869 if {[catch {set gdtf [open $cmd r]}]} return
7871 set treepending $ids
7873 fconfigure $gdtf -blocking 0 -encoding binary
7874 filerun $gdtf [list gettreediffline $gdtf $ids]
7877 proc gettreediffline {gdtf ids} {
7878 global treediff treediffs treepending diffids diffmergeid
7879 global cmitmode vfilelimit curview limitdiffs perfile_attrs
7884 if {$perfile_attrs} {
7885 # cache_gitattr is slow, and even slower on win32 where we
7886 # have to invoke it for only about 30 paths at a time
7888 if {[tk windowingsystem] == "win32"} {
7892 while {[incr nr] <= $max && [gets $gdtf line] >= 0} {
7893 set i [string first "\t" $line]
7895 set file [string range $line [expr {$i+1}] end]
7896 if {[string index $file 0] eq "\""} {
7897 set file [lindex $file 0]
7899 set file [encoding convertfrom $file]
7900 if {$file ne [lindex $treediff end]} {
7901 lappend treediff $file
7902 lappend sublist $file
7906 if {$perfile_attrs} {
7907 cache_gitattr encoding $sublist
7910 return [expr {$nr >= $max? 2: 1}]
7913 set treediffs($ids) $treediff
7915 if {$cmitmode eq "tree" && [llength $diffids] == 1} {
7917 } elseif {$ids != $diffids} {
7918 if {![info exists diffmergeid]} {
7919 gettreediffs $diffids
7927 # empty string or positive integer
7928 proc diffcontextvalidate {v} {
7929 return [regexp {^(|[1-9][0-9]*)$} $v]
7932 proc diffcontextchange {n1 n2 op} {
7933 global diffcontextstring diffcontext
7935 if {[string is integer -strict $diffcontextstring]} {
7936 if {$diffcontextstring >= 0} {
7937 set diffcontext $diffcontextstring
7943 proc changeignorespace {} {
7947 proc changeworddiff {name ix op} {
7951 proc initblobdiffvars {} {
7952 global diffencoding targetline diffnparents
7953 global diffinhdr currdiffsubmod diffseehere
7957 set diffencoding [get_path_encoding {}]
7958 set currdiffsubmod ""
7962 proc getblobdiffs {ids} {
7963 global blobdifffd diffids env
7968 global limitdiffs vfilelimit curview
7972 if {[package vcompare $git_version "1.6.1"] >= 0} {
7973 set textconv "--textconv"
7976 if {[package vcompare $git_version "1.6.6"] >= 0} {
7977 set submodule "--submodule"
7979 set cmd [diffcmd $ids "-p $textconv $submodule -C --cc --no-commit-id -U$diffcontext"]
7983 if {$worddiff ne [mc "Line diff"]} {
7984 append cmd " --word-diff=porcelain"
7986 if {$limitdiffs && $vfilelimit($curview) ne {}} {
7987 set cmd [concat $cmd -- $vfilelimit($curview)]
7989 if {[catch {set bdf [open $cmd r]} err]} {
7990 error_popup [mc "Error getting diffs: %s" $err]
7993 fconfigure $bdf -blocking 0 -encoding binary -eofchar {}
7994 set blobdifffd($ids) $bdf
7996 filerun $bdf [list getblobdiffline $bdf $diffids]
7999 proc savecmitpos {} {
8000 global ctext cmitmode
8002 if {$cmitmode eq "tree"} {
8005 return [list target_scrollpos [$ctext index @0,0]]
8008 proc savectextpos {} {
8011 return [list target_scrollpos [$ctext index @0,0]]
8014 proc maybe_scroll_ctext {ateof} {
8015 global ctext target_scrollpos
8017 if {![info exists target_scrollpos]} return
8019 set nlines [expr {[winfo height $ctext]
8020 / [font metrics textfont -linespace]}]
8021 if {[$ctext compare "$target_scrollpos + $nlines lines" <= end]} return
8023 $ctext yview $target_scrollpos
8024 unset target_scrollpos
8027 proc setinlist {var i val} {
8030 while {[llength [set $var]] < $i} {
8033 if {[llength [set $var]] == $i} {
8040 proc makediffhdr {fname ids} {
8041 global ctext curdiffstart treediffs diffencoding
8042 global ctext_file_names jump_to_here targetline diffline
8044 set fname [encoding convertfrom $fname]
8045 set diffencoding [get_path_encoding $fname]
8046 set i [lsearch -exact $treediffs($ids) $fname]
8048 setinlist difffilestart $i $curdiffstart
8050 lset ctext_file_names end $fname
8051 set l [expr {(78 - [string length $fname]) / 2}]
8052 set pad [string range "----------------------------------------" 1 $l]
8053 $ctext insert $curdiffstart "$pad $fname $pad" filesep
8055 if {$jump_to_here ne {} && [lindex $jump_to_here 0] eq $fname} {
8056 set targetline [lindex $jump_to_here 1]
8061 proc blobdiffmaybeseehere {ateof} {
8063 if {$diffseehere >= 0} {
8064 mark_ctext_line [lindex [split $diffseehere .] 0]
8066 maybe_scroll_ctext $ateof
8069 proc getblobdiffline {bdf ids} {
8070 global diffids blobdifffd
8074 $ctext conf -state normal
8075 while {[incr nr] <= 1000 && [gets $bdf line] >= 0} {
8076 if {$ids != $diffids || $bdf != $blobdifffd($ids)} {
8077 # Older diff read. Abort it.
8079 if {$ids != $diffids} {
8080 array unset blobdifffd $ids
8084 parseblobdiffline $ids $line
8086 $ctext conf -state disabled
8087 blobdiffmaybeseehere [eof $bdf]
8090 array unset blobdifffd $ids
8093 return [expr {$nr >= 1000? 2: 1}]
8096 proc parseblobdiffline {ids line} {
8097 global ctext curdiffstart
8098 global diffnexthead diffnextnote difffilestart
8099 global ctext_file_names ctext_file_lines
8100 global diffinhdr treediffs mergemax diffnparents
8101 global diffencoding jump_to_here targetline diffline currdiffsubmod
8102 global worddiff diffseehere
8104 if {![string compare -length 5 "diff " $line]} {
8105 if {![regexp {^diff (--cc|--git) } $line m type]} {
8106 set line [encoding convertfrom $line]
8107 $ctext insert end "$line\n" hunksep
8110 # start of a new file
8112 $ctext insert end "\n"
8113 set curdiffstart [$ctext index "end - 1c"]
8114 lappend ctext_file_names ""
8115 lappend ctext_file_lines [lindex [split $curdiffstart "."] 0]
8116 $ctext insert end "\n" filesep
8118 if {$type eq "--cc"} {
8119 # start of a new file in a merge diff
8120 set fname [string range $line 10 end]
8121 if {[lsearch -exact $treediffs($ids) $fname] < 0} {
8122 lappend treediffs($ids) $fname
8123 add_flist [list $fname]
8127 set line [string range $line 11 end]
8128 # If the name hasn't changed the length will be odd,
8129 # the middle char will be a space, and the two bits either
8130 # side will be a/name and b/name, or "a/name" and "b/name".
8131 # If the name has changed we'll get "rename from" and
8132 # "rename to" or "copy from" and "copy to" lines following
8133 # this, and we'll use them to get the filenames.
8134 # This complexity is necessary because spaces in the
8135 # filename(s) don't get escaped.
8136 set l [string length $line]
8137 set i [expr {$l / 2}]
8138 if {!(($l & 1) && [string index $line $i] eq " " &&
8139 [string range $line 2 [expr {$i - 1}]] eq \
8140 [string range $line [expr {$i + 3}] end])} {
8143 # unescape if quoted and chop off the a/ from the front
8144 if {[string index $line 0] eq "\""} {
8145 set fname [string range [lindex $line 0] 2 end]
8147 set fname [string range $line 2 [expr {$i - 1}]]
8150 makediffhdr $fname $ids
8152 } elseif {![string compare -length 16 "* Unmerged path " $line]} {
8153 set fname [encoding convertfrom [string range $line 16 end]]
8154 $ctext insert end "\n"
8155 set curdiffstart [$ctext index "end - 1c"]
8156 lappend ctext_file_names $fname
8157 lappend ctext_file_lines [lindex [split $curdiffstart "."] 0]
8158 $ctext insert end "$line\n" filesep
8159 set i [lsearch -exact $treediffs($ids) $fname]
8161 setinlist difffilestart $i $curdiffstart
8164 } elseif {![string compare -length 2 "@@" $line]} {
8165 regexp {^@@+} $line ats
8166 set line [encoding convertfrom $diffencoding $line]
8167 $ctext insert end "$line\n" hunksep
8168 if {[regexp { \+(\d+),\d+ @@} $line m nl]} {
8171 set diffnparents [expr {[string length $ats] - 1}]
8174 } elseif {![string compare -length 10 "Submodule " $line]} {
8175 # start of a new submodule
8176 if {[regexp -indices "\[0-9a-f\]+\\.\\." $line nameend]} {
8177 set fname [string range $line 10 [expr [lindex $nameend 0] - 2]]
8179 set fname [string range $line 10 [expr [string first "contains " $line] - 2]]
8181 if {$currdiffsubmod != $fname} {
8182 $ctext insert end "\n"; # Add newline after commit message
8184 set curdiffstart [$ctext index "end - 1c"]
8185 lappend ctext_file_names ""
8186 if {$currdiffsubmod != $fname} {
8187 lappend ctext_file_lines $fname
8188 makediffhdr $fname $ids
8189 set currdiffsubmod $fname
8190 $ctext insert end "\n$line\n" filesep
8192 $ctext insert end "$line\n" filesep
8194 } elseif {![string compare -length 3 " >" $line]} {
8195 set $currdiffsubmod ""
8196 set line [encoding convertfrom $diffencoding $line]
8197 $ctext insert end "$line\n" dresult
8198 } elseif {![string compare -length 3 " <" $line]} {
8199 set $currdiffsubmod ""
8200 set line [encoding convertfrom $diffencoding $line]
8201 $ctext insert end "$line\n" d0
8202 } elseif {$diffinhdr} {
8203 if {![string compare -length 12 "rename from " $line]} {
8204 set fname [string range $line [expr 6 + [string first " from " $line] ] end]
8205 if {[string index $fname 0] eq "\""} {
8206 set fname [lindex $fname 0]
8208 set fname [encoding convertfrom $fname]
8209 set i [lsearch -exact $treediffs($ids) $fname]
8211 setinlist difffilestart $i $curdiffstart
8213 } elseif {![string compare -length 10 $line "rename to "] ||
8214 ![string compare -length 8 $line "copy to "]} {
8215 set fname [string range $line [expr 4 + [string first " to " $line] ] end]
8216 if {[string index $fname 0] eq "\""} {
8217 set fname [lindex $fname 0]
8219 makediffhdr $fname $ids
8220 } elseif {[string compare -length 3 $line "---"] == 0} {
8223 } elseif {[string compare -length 3 $line "+++"] == 0} {
8227 $ctext insert end "$line\n" filesep
8230 set line [string map {\x1A ^Z} \
8231 [encoding convertfrom $diffencoding $line]]
8232 # parse the prefix - one ' ', '-' or '+' for each parent
8233 set prefix [string range $line 0 [expr {$diffnparents - 1}]]
8234 set tag [expr {$diffnparents > 1? "m": "d"}]
8235 set dowords [expr {$worddiff ne [mc "Line diff"] && $diffnparents == 1}]
8236 set words_pre_markup ""
8237 set words_post_markup ""
8238 if {[string trim $prefix " -+"] eq {}} {
8239 # prefix only has " ", "-" and "+" in it: normal diff line
8240 set num [string first "-" $prefix]
8242 set line [string range $line 1 end]
8245 # removed line, first parent with line is $num
8246 if {$num >= $mergemax} {
8249 if {$dowords && $worddiff eq [mc "Markup words"]} {
8250 $ctext insert end "\[-$line-\]" $tag$num
8252 $ctext insert end "$line" $tag$num
8255 $ctext insert end "\n" $tag$num
8259 if {[string first "+" $prefix] >= 0} {
8261 lappend tags ${tag}result
8262 if {$diffnparents > 1} {
8263 set num [string first " " $prefix]
8265 if {$num >= $mergemax} {
8271 set words_pre_markup "{+"
8272 set words_post_markup "+}"
8274 if {$targetline ne {}} {
8275 if {$diffline == $targetline} {
8276 set diffseehere [$ctext index "end - 1 chars"]
8282 if {$dowords && $worddiff eq [mc "Markup words"]} {
8283 $ctext insert end "$words_pre_markup$line$words_post_markup" $tags
8285 $ctext insert end "$line" $tags
8288 $ctext insert end "\n" $tags
8291 } elseif {$dowords && $prefix eq "~"} {
8292 $ctext insert end "\n" {}
8294 # "\ No newline at end of file",
8295 # or something else we don't recognize
8296 $ctext insert end "$line\n" hunksep
8301 proc changediffdisp {} {
8302 global ctext diffelide
8304 $ctext tag conf d0 -elide [lindex $diffelide 0]
8305 $ctext tag conf dresult -elide [lindex $diffelide 1]
8308 proc highlightfile {cline} {
8309 global cflist cflist_top
8311 if {![info exists cflist_top]} return
8313 $cflist tag remove highlight $cflist_top.0 "$cflist_top.0 lineend"
8314 $cflist tag add highlight $cline.0 "$cline.0 lineend"
8315 $cflist see $cline.0
8316 set cflist_top $cline
8319 proc highlightfile_for_scrollpos {topidx} {
8320 global cmitmode difffilestart
8322 if {$cmitmode eq "tree"} return
8323 if {![info exists difffilestart]} return
8325 set top [lindex [split $topidx .] 0]
8326 if {$difffilestart eq {} || $top < [lindex $difffilestart 0]} {
8329 highlightfile [expr {[bsearch $difffilestart $top] + 2}]
8334 global difffilestart ctext cmitmode
8336 if {$cmitmode eq "tree"} return
8338 set here [$ctext index @0,0]
8339 foreach loc $difffilestart {
8340 if {[$ctext compare $loc >= $here]} {
8350 global difffilestart ctext cmitmode
8352 if {$cmitmode eq "tree"} return
8353 set here [$ctext index @0,0]
8354 foreach loc $difffilestart {
8355 if {[$ctext compare $loc > $here]} {
8362 proc clear_ctext {{first 1.0}} {
8363 global ctext smarktop smarkbot
8364 global ctext_file_names ctext_file_lines
8367 set l [lindex [split $first .] 0]
8368 if {![info exists smarktop] || [$ctext compare $first < $smarktop.0]} {
8371 if {![info exists smarkbot] || [$ctext compare $first < $smarkbot.0]} {
8374 $ctext delete $first end
8375 if {$first eq "1.0"} {
8376 unset -nocomplain pendinglinks
8378 set ctext_file_names {}
8379 set ctext_file_lines {}
8382 proc settabs {{firstab {}}} {
8383 global firsttabstop tabstop ctext have_tk85
8385 if {$firstab ne {} && $have_tk85} {
8386 set firsttabstop $firstab
8388 set w [font measure textfont "0"]
8389 if {$firsttabstop != 0} {
8390 $ctext conf -tabs [list [expr {($firsttabstop + $tabstop) * $w}] \
8391 [expr {($firsttabstop + 2 * $tabstop) * $w}]]
8392 } elseif {$have_tk85 || $tabstop != 8} {
8393 $ctext conf -tabs [expr {$tabstop * $w}]
8395 $ctext conf -tabs {}
8399 proc incrsearch {name ix op} {
8400 global ctext searchstring searchdirn
8402 if {[catch {$ctext index anchor}]} {
8403 # no anchor set, use start of selection, or of visible area
8404 set sel [$ctext tag ranges sel]
8406 $ctext mark set anchor [lindex $sel 0]
8407 } elseif {$searchdirn eq "-forwards"} {
8408 $ctext mark set anchor @0,0
8410 $ctext mark set anchor @0,[winfo height $ctext]
8413 if {$searchstring ne {}} {
8414 set here [$ctext search -count mlen $searchdirn -- $searchstring anchor]
8417 set mend "$here + $mlen c"
8418 $ctext tag remove sel 1.0 end
8419 $ctext tag add sel $here $mend
8420 suppress_highlighting_file_for_current_scrollpos
8421 highlightfile_for_scrollpos $here
8424 rehighlight_search_results
8428 global sstring ctext searchstring searchdirn
8431 $sstring icursor end
8432 set searchdirn -forwards
8433 if {$searchstring ne {}} {
8434 set sel [$ctext tag ranges sel]
8436 set start "[lindex $sel 0] + 1c"
8437 } elseif {[catch {set start [$ctext index anchor]}]} {
8440 set match [$ctext search -count mlen -- $searchstring $start]
8441 $ctext tag remove sel 1.0 end
8447 suppress_highlighting_file_for_current_scrollpos
8448 highlightfile_for_scrollpos $match
8449 set mend "$match + $mlen c"
8450 $ctext tag add sel $match $mend
8451 $ctext mark unset anchor
8452 rehighlight_search_results
8456 proc dosearchback {} {
8457 global sstring ctext searchstring searchdirn
8460 $sstring icursor end
8461 set searchdirn -backwards
8462 if {$searchstring ne {}} {
8463 set sel [$ctext tag ranges sel]
8465 set start [lindex $sel 0]
8466 } elseif {[catch {set start [$ctext index anchor]}]} {
8467 set start @0,[winfo height $ctext]
8469 set match [$ctext search -backwards -count ml -- $searchstring $start]
8470 $ctext tag remove sel 1.0 end
8476 suppress_highlighting_file_for_current_scrollpos
8477 highlightfile_for_scrollpos $match
8478 set mend "$match + $ml c"
8479 $ctext tag add sel $match $mend
8480 $ctext mark unset anchor
8481 rehighlight_search_results
8485 proc rehighlight_search_results {} {
8486 global ctext searchstring
8488 $ctext tag remove found 1.0 end
8489 $ctext tag remove currentsearchhit 1.0 end
8491 if {$searchstring ne {}} {
8496 proc searchmark {first last} {
8497 global ctext searchstring
8499 set sel [$ctext tag ranges sel]
8503 set match [$ctext search -count mlen -- $searchstring $mend $last.end]
8504 if {$match eq {}} break
8505 set mend "$match + $mlen c"
8506 if {$sel ne {} && [$ctext compare $match == [lindex $sel 0]]} {
8507 $ctext tag add currentsearchhit $match $mend
8509 $ctext tag add found $match $mend
8514 proc searchmarkvisible {doall} {
8515 global ctext smarktop smarkbot
8517 set topline [lindex [split [$ctext index @0,0] .] 0]
8518 set botline [lindex [split [$ctext index @0,[winfo height $ctext]] .] 0]
8519 if {$doall || $botline < $smarktop || $topline > $smarkbot} {
8520 # no overlap with previous
8521 searchmark $topline $botline
8522 set smarktop $topline
8523 set smarkbot $botline
8525 if {$topline < $smarktop} {
8526 searchmark $topline [expr {$smarktop-1}]
8527 set smarktop $topline
8529 if {$botline > $smarkbot} {
8530 searchmark [expr {$smarkbot+1}] $botline
8531 set smarkbot $botline
8536 proc suppress_highlighting_file_for_current_scrollpos {} {
8537 global ctext suppress_highlighting_file_for_this_scrollpos
8539 set suppress_highlighting_file_for_this_scrollpos [$ctext index @0,0]
8542 proc scrolltext {f0 f1} {
8543 global searchstring cmitmode ctext
8544 global suppress_highlighting_file_for_this_scrollpos
8546 set topidx [$ctext index @0,0]
8547 if {![info exists suppress_highlighting_file_for_this_scrollpos]
8548 || $topidx ne $suppress_highlighting_file_for_this_scrollpos} {
8549 highlightfile_for_scrollpos $topidx
8552 unset -nocomplain suppress_highlighting_file_for_this_scrollpos
8554 .bleft.bottom.sb set $f0 $f1
8555 if {$searchstring ne {}} {
8561 global linespc charspc canvx0 canvy0
8562 global xspc1 xspc2 lthickness
8564 set linespc [font metrics mainfont -linespace]
8565 set charspc [font measure mainfont "m"]
8566 set canvy0 [expr {int(3 + 0.5 * $linespc)}]
8567 set canvx0 [expr {int(3 + 0.5 * $linespc)}]
8568 set lthickness [expr {int($linespc / 9) + 1}]
8569 set xspc1(0) $linespc
8577 set ymax [lindex [$canv cget -scrollregion] 3]
8578 if {$ymax eq {} || $ymax == 0} return
8579 set span [$canv yview]
8582 allcanvs yview moveto [lindex $span 0]
8584 if {$selectedline ne {}} {
8585 selectline $selectedline 0
8586 allcanvs yview moveto [lindex $span 0]
8590 proc parsefont {f n} {
8593 set fontattr($f,family) [lindex $n 0]
8595 if {$s eq {} || $s == 0} {
8598 set s [expr {int(-$s / [winfo fpixels . 1p] + 0.5)}]
8600 set fontattr($f,size) $s
8601 set fontattr($f,weight) normal
8602 set fontattr($f,slant) roman
8603 foreach style [lrange $n 2 end] {
8606 "bold" {set fontattr($f,weight) $style}
8608 "italic" {set fontattr($f,slant) $style}
8613 proc fontflags {f {isbold 0}} {
8616 return [list -family $fontattr($f,family) -size $fontattr($f,size) \
8617 -weight [expr {$isbold? "bold": $fontattr($f,weight)}] \
8618 -slant $fontattr($f,slant)]
8624 set n [list $fontattr($f,family) $fontattr($f,size)]
8625 if {$fontattr($f,weight) eq "bold"} {
8628 if {$fontattr($f,slant) eq "italic"} {
8634 proc incrfont {inc} {
8635 global mainfont textfont ctext canv cflist showrefstop
8636 global stopped entries fontattr
8639 set s $fontattr(mainfont,size)
8644 set fontattr(mainfont,size) $s
8645 font config mainfont -size $s
8646 font config mainfontbold -size $s
8647 set mainfont [fontname mainfont]
8648 set s $fontattr(textfont,size)
8653 set fontattr(textfont,size) $s
8654 font config textfont -size $s
8655 font config textfontbold -size $s
8656 set textfont [fontname textfont]
8663 global sha1entry sha1string
8664 if {[string length $sha1string] == 40} {
8665 $sha1entry delete 0 end
8669 proc sha1change {n1 n2 op} {
8670 global sha1string currentid sha1but
8671 if {$sha1string == {}
8672 || ([info exists currentid] && $sha1string == $currentid)} {
8677 if {[$sha1but cget -state] == $state} return
8678 if {$state == "normal"} {
8679 $sha1but conf -state normal -relief raised -text "[mc "Goto:"] "
8681 $sha1but conf -state disabled -relief flat -text "[mc "SHA1 ID:"] "
8685 proc gotocommit {} {
8686 global sha1string tagids headids curview varcid
8688 if {$sha1string == {}
8689 || ([info exists currentid] && $sha1string == $currentid)} return
8690 if {[info exists tagids($sha1string)]} {
8691 set id $tagids($sha1string)
8692 } elseif {[info exists headids($sha1string)]} {
8693 set id $headids($sha1string)
8695 set id [string tolower $sha1string]
8696 if {[regexp {^[0-9a-f]{4,39}$} $id]} {
8697 set matches [longid $id]
8698 if {$matches ne {}} {
8699 if {[llength $matches] > 1} {
8700 error_popup [mc "Short SHA1 id %s is ambiguous" $id]
8703 set id [lindex $matches 0]
8706 if {[catch {set id [exec git rev-parse --verify $sha1string]}]} {
8707 error_popup [mc "Revision %s is not known" $sha1string]
8712 if {[commitinview $id $curview]} {
8713 selectline [rowofcommit $id] 1
8716 if {[regexp {^[0-9a-fA-F]{4,}$} $sha1string]} {
8717 set msg [mc "SHA1 id %s is not known" $sha1string]
8719 set msg [mc "Revision %s is not in the current view" $sha1string]
8724 proc lineenter {x y id} {
8725 global hoverx hovery hoverid hovertimer
8726 global commitinfo canv
8728 if {![info exists commitinfo($id)] && ![getcommit $id]} return
8732 if {[info exists hovertimer]} {
8733 after cancel $hovertimer
8735 set hovertimer [after 500 linehover]
8739 proc linemotion {x y id} {
8740 global hoverx hovery hoverid hovertimer
8742 if {[info exists hoverid] && $id == $hoverid} {
8745 if {[info exists hovertimer]} {
8746 after cancel $hovertimer
8748 set hovertimer [after 500 linehover]
8752 proc lineleave {id} {
8753 global hoverid hovertimer canv
8755 if {[info exists hoverid] && $id == $hoverid} {
8757 if {[info exists hovertimer]} {
8758 after cancel $hovertimer
8766 global hoverx hovery hoverid hovertimer
8767 global canv linespc lthickness
8768 global linehoverbgcolor linehoverfgcolor linehoveroutlinecolor
8772 set text [lindex $commitinfo($hoverid) 0]
8773 set ymax [lindex [$canv cget -scrollregion] 3]
8774 if {$ymax == {}} return
8775 set yfrac [lindex [$canv yview] 0]
8776 set x [expr {$hoverx + 2 * $linespc}]
8777 set y [expr {$hovery + $yfrac * $ymax - $linespc / 2}]
8778 set x0 [expr {$x - 2 * $lthickness}]
8779 set y0 [expr {$y - 2 * $lthickness}]
8780 set x1 [expr {$x + [font measure mainfont $text] + 2 * $lthickness}]
8781 set y1 [expr {$y + $linespc + 2 * $lthickness}]
8782 set t [$canv create rectangle $x0 $y0 $x1 $y1 \
8783 -fill $linehoverbgcolor -outline $linehoveroutlinecolor \
8784 -width 1 -tags hover]
8786 set t [$canv create text $x $y -anchor nw -text $text -tags hover \
8787 -font mainfont -fill $linehoverfgcolor]
8791 proc clickisonarrow {id y} {
8794 set ranges [rowranges $id]
8795 set thresh [expr {2 * $lthickness + 6}]
8796 set n [expr {[llength $ranges] - 1}]
8797 for {set i 1} {$i < $n} {incr i} {
8798 set row [lindex $ranges $i]
8799 if {abs([yc $row] - $y) < $thresh} {
8806 proc arrowjump {id n y} {
8809 # 1 <-> 2, 3 <-> 4, etc...
8810 set n [expr {(($n - 1) ^ 1) + 1}]
8811 set row [lindex [rowranges $id] $n]
8813 set ymax [lindex [$canv cget -scrollregion] 3]
8814 if {$ymax eq {} || $ymax <= 0} return
8815 set view [$canv yview]
8816 set yspan [expr {[lindex $view 1] - [lindex $view 0]}]
8817 set yfrac [expr {$yt / $ymax - $yspan / 2}]
8821 allcanvs yview moveto $yfrac
8824 proc lineclick {x y id isnew} {
8825 global ctext commitinfo children canv thickerline curview
8827 if {![info exists commitinfo($id)] && ![getcommit $id]} return
8832 # draw this line thicker than normal
8836 set ymax [lindex [$canv cget -scrollregion] 3]
8837 if {$ymax eq {}} return
8838 set yfrac [lindex [$canv yview] 0]
8839 set y [expr {$y + $yfrac * $ymax}]
8841 set dirn [clickisonarrow $id $y]
8843 arrowjump $id $dirn $y
8848 addtohistory [list lineclick $x $y $id 0] savectextpos
8850 # fill the details pane with info about this line
8851 $ctext conf -state normal
8854 $ctext insert end "[mc "Parent"]:\t"
8855 $ctext insert end $id link0
8857 set info $commitinfo($id)
8858 $ctext insert end "\n\t[lindex $info 0]\n"
8859 $ctext insert end "\t[mc "Author"]:\t[lindex $info 1]\n"
8860 set date [formatdate [lindex $info 2]]
8861 $ctext insert end "\t[mc "Date"]:\t$date\n"
8862 set kids $children($curview,$id)
8864 $ctext insert end "\n[mc "Children"]:"
8866 foreach child $kids {
8868 if {![info exists commitinfo($child)] && ![getcommit $child]} continue
8869 set info $commitinfo($child)
8870 $ctext insert end "\n\t"
8871 $ctext insert end $child link$i
8872 setlink $child link$i
8873 $ctext insert end "\n\t[lindex $info 0]"
8874 $ctext insert end "\n\t[mc "Author"]:\t[lindex $info 1]"
8875 set date [formatdate [lindex $info 2]]
8876 $ctext insert end "\n\t[mc "Date"]:\t$date\n"
8879 maybe_scroll_ctext 1
8880 $ctext conf -state disabled
8884 proc normalline {} {
8886 if {[info exists thickerline]} {
8893 proc selbyid {id {isnew 1}} {
8895 if {[commitinview $id $curview]} {
8896 selectline [rowofcommit $id] $isnew
8902 if {![info exists startmstime]} {
8903 set startmstime [clock clicks -milliseconds]
8905 return [format "%.3f" [expr {([clock click -milliseconds] - $startmstime) / 1000.0}]]
8908 proc rowmenu {x y id} {
8909 global rowctxmenu selectedline rowmenuid curview
8910 global nullid nullid2 fakerowmenu mainhead markedid
8914 if {$selectedline eq {} || [rowofcommit $id] eq $selectedline} {
8919 if {[info exists markedid] && $markedid ne $id} {
8924 if {$id ne $nullid && $id ne $nullid2} {
8925 set menu $rowctxmenu
8926 if {$mainhead ne {}} {
8927 $menu entryconfigure 8 -label [mc "Reset %s branch to here" $mainhead] -state normal
8929 $menu entryconfigure 8 -label [mc "Detached head: can't reset" $mainhead] -state disabled
8931 $menu entryconfigure 10 -state $mstate
8932 $menu entryconfigure 11 -state $mstate
8933 $menu entryconfigure 12 -state $mstate
8935 set menu $fakerowmenu
8937 $menu entryconfigure [mca "Diff this -> selected"] -state $state
8938 $menu entryconfigure [mca "Diff selected -> this"] -state $state
8939 $menu entryconfigure [mca "Make patch"] -state $state
8940 $menu entryconfigure [mca "Diff this -> marked commit"] -state $mstate
8941 $menu entryconfigure [mca "Diff marked commit -> this"] -state $mstate
8942 tk_popup $menu $x $y
8946 global rowmenuid markedid canv
8948 set markedid $rowmenuid
8949 make_idmark $markedid
8955 if {[info exists markedid]} {
8960 proc replace_by_kids {l r} {
8961 global curview children
8963 set id [commitonrow $r]
8964 set l [lreplace $l 0 0]
8965 foreach kid $children($curview,$id) {
8966 lappend l [rowofcommit $kid]
8968 return [lsort -integer -decreasing -unique $l]
8971 proc find_common_desc {} {
8972 global markedid rowmenuid curview children
8974 if {![info exists markedid]} return
8975 if {![commitinview $markedid $curview] ||
8976 ![commitinview $rowmenuid $curview]} return
8977 #set t1 [clock clicks -milliseconds]
8978 set l1 [list [rowofcommit $markedid]]
8979 set l2 [list [rowofcommit $rowmenuid]]
8981 set r1 [lindex $l1 0]
8982 set r2 [lindex $l2 0]
8983 if {$r1 eq {} || $r2 eq {}} break
8989 set l1 [replace_by_kids $l1 $r1]
8991 set l2 [replace_by_kids $l2 $r2]
8994 #set t2 [clock clicks -milliseconds]
8995 #puts "took [expr {$t2-$t1}]ms"
8998 proc compare_commits {} {
8999 global markedid rowmenuid curview children
9001 if {![info exists markedid]} return
9002 if {![commitinview $markedid $curview]} return
9003 addtohistory [list do_cmp_commits $markedid $rowmenuid]
9004 do_cmp_commits $markedid $rowmenuid
9007 proc getpatchid {id} {
9010 if {![info exists patchids($id)]} {
9011 set cmd [diffcmd [list $id] {-p --root}]
9012 # trim off the initial "|"
9013 set cmd [lrange $cmd 1 end]
9015 set x [eval exec $cmd | git patch-id]
9016 set patchids($id) [lindex $x 0]
9018 set patchids($id) "error"
9021 return $patchids($id)
9024 proc do_cmp_commits {a b} {
9025 global ctext curview parents children patchids commitinfo
9027 $ctext conf -state normal
9030 for {set i 0} {$i < 100} {incr i} {
9033 if {[llength $parents($curview,$a)] > 1} {
9034 appendshortlink $a [mc "Skipping merge commit "] "\n"
9037 set patcha [getpatchid $a]
9039 if {[llength $parents($curview,$b)] > 1} {
9040 appendshortlink $b [mc "Skipping merge commit "] "\n"
9043 set patchb [getpatchid $b]
9045 if {!$skipa && !$skipb} {
9046 set heada [lindex $commitinfo($a) 0]
9047 set headb [lindex $commitinfo($b) 0]
9048 if {$patcha eq "error"} {
9049 appendshortlink $a [mc "Error getting patch ID for "] \
9050 [mc " - stopping\n"]
9053 if {$patchb eq "error"} {
9054 appendshortlink $b [mc "Error getting patch ID for "] \
9055 [mc " - stopping\n"]
9058 if {$patcha eq $patchb} {
9059 if {$heada eq $headb} {
9060 appendshortlink $a [mc "Commit "]
9061 appendshortlink $b " == " " $heada\n"
9063 appendshortlink $a [mc "Commit "] " $heada\n"
9064 appendshortlink $b [mc " is the same patch as\n "] \
9070 $ctext insert end "\n"
9071 appendshortlink $a [mc "Commit "] " $heada\n"
9072 appendshortlink $b [mc " differs from\n "] \
9074 $ctext insert end [mc "Diff of commits:\n\n"]
9075 $ctext conf -state disabled
9082 set kids [real_children $curview,$a]
9083 if {[llength $kids] != 1} {
9084 $ctext insert end "\n"
9085 appendshortlink $a [mc "Commit "] \
9086 [mc " has %s children - stopping\n" [llength $kids]]
9089 set a [lindex $kids 0]
9092 set kids [real_children $curview,$b]
9093 if {[llength $kids] != 1} {
9094 appendshortlink $b [mc "Commit "] \
9095 [mc " has %s children - stopping\n" [llength $kids]]
9098 set b [lindex $kids 0]
9101 $ctext conf -state disabled
9104 proc diffcommits {a b} {
9105 global diffcontext diffids blobdifffd diffinhdr currdiffsubmod
9107 set tmpdir [gitknewtmpdir]
9108 set fna [file join $tmpdir "commit-[string range $a 0 7]"]
9109 set fnb [file join $tmpdir "commit-[string range $b 0 7]"]
9111 exec git diff-tree -p --pretty $a >$fna
9112 exec git diff-tree -p --pretty $b >$fnb
9114 error_popup [mc "Error writing commit to file: %s" $err]
9118 set fd [open "| diff -U$diffcontext $fna $fnb" r]
9120 error_popup [mc "Error diffing commits: %s" $err]
9123 set diffids [list commits $a $b]
9124 set blobdifffd($diffids) $fd
9126 set currdiffsubmod ""
9127 filerun $fd [list getblobdiffline $fd $diffids]
9130 proc diffvssel {dirn} {
9131 global rowmenuid selectedline
9133 if {$selectedline eq {}} return
9135 set oldid [commitonrow $selectedline]
9136 set newid $rowmenuid
9138 set oldid $rowmenuid
9139 set newid [commitonrow $selectedline]
9141 addtohistory [list doseldiff $oldid $newid] savectextpos
9142 doseldiff $oldid $newid
9145 proc diffvsmark {dirn} {
9146 global rowmenuid markedid
9148 if {![info exists markedid]} return
9151 set newid $rowmenuid
9153 set oldid $rowmenuid
9156 addtohistory [list doseldiff $oldid $newid] savectextpos
9157 doseldiff $oldid $newid
9160 proc doseldiff {oldid newid} {
9164 $ctext conf -state normal
9166 init_flist [mc "Top"]
9167 $ctext insert end "[mc "From"] "
9168 $ctext insert end $oldid link0
9169 setlink $oldid link0
9170 $ctext insert end "\n "
9171 $ctext insert end [lindex $commitinfo($oldid) 0]
9172 $ctext insert end "\n\n[mc "To"] "
9173 $ctext insert end $newid link1
9174 setlink $newid link1
9175 $ctext insert end "\n "
9176 $ctext insert end [lindex $commitinfo($newid) 0]
9177 $ctext insert end "\n"
9178 $ctext conf -state disabled
9179 $ctext tag remove found 1.0 end
9180 startdiff [list $oldid $newid]
9184 global rowmenuid currentid commitinfo patchtop patchnum NS
9186 if {![info exists currentid]} return
9187 set oldid $currentid
9188 set oldhead [lindex $commitinfo($oldid) 0]
9189 set newid $rowmenuid
9190 set newhead [lindex $commitinfo($newid) 0]
9193 catch {destroy $top}
9195 make_transient $top .
9196 ${NS}::label $top.title -text [mc "Generate patch"]
9197 grid $top.title - -pady 10
9198 ${NS}::label $top.from -text [mc "From:"]
9199 ${NS}::entry $top.fromsha1 -width 40
9200 $top.fromsha1 insert 0 $oldid
9201 $top.fromsha1 conf -state readonly
9202 grid $top.from $top.fromsha1 -sticky w
9203 ${NS}::entry $top.fromhead -width 60
9204 $top.fromhead insert 0 $oldhead
9205 $top.fromhead conf -state readonly
9206 grid x $top.fromhead -sticky w
9207 ${NS}::label $top.to -text [mc "To:"]
9208 ${NS}::entry $top.tosha1 -width 40
9209 $top.tosha1 insert 0 $newid
9210 $top.tosha1 conf -state readonly
9211 grid $top.to $top.tosha1 -sticky w
9212 ${NS}::entry $top.tohead -width 60
9213 $top.tohead insert 0 $newhead
9214 $top.tohead conf -state readonly
9215 grid x $top.tohead -sticky w
9216 ${NS}::button $top.rev -text [mc "Reverse"] -command mkpatchrev
9217 grid $top.rev x -pady 10 -padx 5
9218 ${NS}::label $top.flab -text [mc "Output file:"]
9219 ${NS}::entry $top.fname -width 60
9220 $top.fname insert 0 [file normalize "patch$patchnum.patch"]
9222 grid $top.flab $top.fname -sticky w
9223 ${NS}::frame $top.buts
9224 ${NS}::button $top.buts.gen -text [mc "Generate"] -command mkpatchgo
9225 ${NS}::button $top.buts.can -text [mc "Cancel"] -command mkpatchcan
9226 bind $top <Key-Return> mkpatchgo
9227 bind $top <Key-Escape> mkpatchcan
9228 grid $top.buts.gen $top.buts.can
9229 grid columnconfigure $top.buts 0 -weight 1 -uniform a
9230 grid columnconfigure $top.buts 1 -weight 1 -uniform a
9231 grid $top.buts - -pady 10 -sticky ew
9235 proc mkpatchrev {} {
9238 set oldid [$patchtop.fromsha1 get]
9239 set oldhead [$patchtop.fromhead get]
9240 set newid [$patchtop.tosha1 get]
9241 set newhead [$patchtop.tohead get]
9242 foreach e [list fromsha1 fromhead tosha1 tohead] \
9243 v [list $newid $newhead $oldid $oldhead] {
9244 $patchtop.$e conf -state normal
9245 $patchtop.$e delete 0 end
9246 $patchtop.$e insert 0 $v
9247 $patchtop.$e conf -state readonly
9252 global patchtop nullid nullid2
9254 set oldid [$patchtop.fromsha1 get]
9255 set newid [$patchtop.tosha1 get]
9256 set fname [$patchtop.fname get]
9257 set cmd [diffcmd [list $oldid $newid] -p]
9258 # trim off the initial "|"
9259 set cmd [lrange $cmd 1 end]
9260 lappend cmd >$fname &
9261 if {[catch {eval exec $cmd} err]} {
9262 error_popup "[mc "Error creating patch:"] $err" $patchtop
9264 catch {destroy $patchtop}
9268 proc mkpatchcan {} {
9271 catch {destroy $patchtop}
9276 global rowmenuid mktagtop commitinfo NS
9280 catch {destroy $top}
9282 make_transient $top .
9283 ${NS}::label $top.title -text [mc "Create tag"]
9284 grid $top.title - -pady 10
9285 ${NS}::label $top.id -text [mc "ID:"]
9286 ${NS}::entry $top.sha1 -width 40
9287 $top.sha1 insert 0 $rowmenuid
9288 $top.sha1 conf -state readonly
9289 grid $top.id $top.sha1 -sticky w
9290 ${NS}::entry $top.head -width 60
9291 $top.head insert 0 [lindex $commitinfo($rowmenuid) 0]
9292 $top.head conf -state readonly
9293 grid x $top.head -sticky w
9294 ${NS}::label $top.tlab -text [mc "Tag name:"]
9295 ${NS}::entry $top.tag -width 60
9296 grid $top.tlab $top.tag -sticky w
9297 ${NS}::label $top.op -text [mc "Tag message is optional"]
9298 grid $top.op -columnspan 2 -sticky we
9299 ${NS}::label $top.mlab -text [mc "Tag message:"]
9300 ${NS}::entry $top.msg -width 60
9301 grid $top.mlab $top.msg -sticky w
9302 ${NS}::frame $top.buts
9303 ${NS}::button $top.buts.gen -text [mc "Create"] -command mktaggo
9304 ${NS}::button $top.buts.can -text [mc "Cancel"] -command mktagcan
9305 bind $top <Key-Return> mktaggo
9306 bind $top <Key-Escape> mktagcan
9307 grid $top.buts.gen $top.buts.can
9308 grid columnconfigure $top.buts 0 -weight 1 -uniform a
9309 grid columnconfigure $top.buts 1 -weight 1 -uniform a
9310 grid $top.buts - -pady 10 -sticky ew
9315 global mktagtop env tagids idtags
9317 set id [$mktagtop.sha1 get]
9318 set tag [$mktagtop.tag get]
9319 set msg [$mktagtop.msg get]
9321 error_popup [mc "No tag name specified"] $mktagtop
9324 if {[info exists tagids($tag)]} {
9325 error_popup [mc "Tag \"%s\" already exists" $tag] $mktagtop
9330 exec git tag -a -m $msg $tag $id
9332 exec git tag $tag $id
9335 error_popup "[mc "Error creating tag:"] $err" $mktagtop
9339 set tagids($tag) $id
9340 lappend idtags($id) $tag
9348 proc redrawtags {id} {
9349 global canv linehtag idpos currentid curview cmitlisted markedid
9350 global canvxmax iddrawn circleitem mainheadid circlecolors
9351 global mainheadcirclecolor
9353 if {![commitinview $id $curview]} return
9354 if {![info exists iddrawn($id)]} return
9355 set row [rowofcommit $id]
9356 if {$id eq $mainheadid} {
9357 set ofill $mainheadcirclecolor
9359 set ofill [lindex $circlecolors $cmitlisted($curview,$id)]
9361 $canv itemconf $circleitem($row) -fill $ofill
9362 $canv delete tag.$id
9363 set xt [eval drawtags $id $idpos($id)]
9364 $canv coords $linehtag($id) $xt [lindex $idpos($id) 2]
9365 set text [$canv itemcget $linehtag($id) -text]
9366 set font [$canv itemcget $linehtag($id) -font]
9367 set xr [expr {$xt + [font measure $font $text]}]
9368 if {$xr > $canvxmax} {
9372 if {[info exists currentid] && $currentid == $id} {
9375 if {[info exists markedid] && $markedid eq $id} {
9383 catch {destroy $mktagtop}
9388 if {![domktag]} return
9392 proc copysummary {} {
9393 global rowmenuid autosellen
9395 set format "%h (\"%s\", %ad)"
9396 set cmd [list git show -s --pretty=format:$format --date=short]
9397 if {$autosellen < 40} {
9398 lappend cmd --abbrev=$autosellen
9400 set summary [eval exec $cmd $rowmenuid]
9403 clipboard append $summary
9406 proc writecommit {} {
9407 global rowmenuid wrcomtop commitinfo wrcomcmd NS
9409 set top .writecommit
9411 catch {destroy $top}
9413 make_transient $top .
9414 ${NS}::label $top.title -text [mc "Write commit to file"]
9415 grid $top.title - -pady 10
9416 ${NS}::label $top.id -text [mc "ID:"]
9417 ${NS}::entry $top.sha1 -width 40
9418 $top.sha1 insert 0 $rowmenuid
9419 $top.sha1 conf -state readonly
9420 grid $top.id $top.sha1 -sticky w
9421 ${NS}::entry $top.head -width 60
9422 $top.head insert 0 [lindex $commitinfo($rowmenuid) 0]
9423 $top.head conf -state readonly
9424 grid x $top.head -sticky w
9425 ${NS}::label $top.clab -text [mc "Command:"]
9426 ${NS}::entry $top.cmd -width 60 -textvariable wrcomcmd
9427 grid $top.clab $top.cmd -sticky w -pady 10
9428 ${NS}::label $top.flab -text [mc "Output file:"]
9429 ${NS}::entry $top.fname -width 60
9430 $top.fname insert 0 [file normalize "commit-[string range $rowmenuid 0 6]"]
9431 grid $top.flab $top.fname -sticky w
9432 ${NS}::frame $top.buts
9433 ${NS}::button $top.buts.gen -text [mc "Write"] -command wrcomgo
9434 ${NS}::button $top.buts.can -text [mc "Cancel"] -command wrcomcan
9435 bind $top <Key-Return> wrcomgo
9436 bind $top <Key-Escape> wrcomcan
9437 grid $top.buts.gen $top.buts.can
9438 grid columnconfigure $top.buts 0 -weight 1 -uniform a
9439 grid columnconfigure $top.buts 1 -weight 1 -uniform a
9440 grid $top.buts - -pady 10 -sticky ew
9447 set id [$wrcomtop.sha1 get]
9448 set cmd "echo $id | [$wrcomtop.cmd get]"
9449 set fname [$wrcomtop.fname get]
9450 if {[catch {exec sh -c $cmd >$fname &} err]} {
9451 error_popup "[mc "Error writing commit:"] $err" $wrcomtop
9453 catch {destroy $wrcomtop}
9460 catch {destroy $wrcomtop}
9467 set top .branchdialog
9470 set val(id) $rowmenuid
9471 set val(command) [list mkbrgo $top]
9473 set ui(title) [mc "Create branch"]
9474 set ui(accept) [mc "Create"]
9476 branchdia $top val ui
9481 global headmenuid headmenuhead
9483 set top .branchdialog
9485 set val(name) $headmenuhead
9486 set val(id) $headmenuid
9487 set val(command) [list mvbrgo $top $headmenuhead]
9489 set ui(title) [mc "Rename branch %s" $headmenuhead]
9490 set ui(accept) [mc "Rename"]
9492 branchdia $top val ui
9495 proc branchdia {top valvar uivar} {
9496 global NS commitinfo
9497 upvar $valvar val $uivar ui
9499 catch {destroy $top}
9501 make_transient $top .
9502 ${NS}::label $top.title -text $ui(title)
9503 grid $top.title - -pady 10
9504 ${NS}::label $top.id -text [mc "ID:"]
9505 ${NS}::entry $top.sha1 -width 40
9506 $top.sha1 insert 0 $val(id)
9507 $top.sha1 conf -state readonly
9508 grid $top.id $top.sha1 -sticky w
9509 ${NS}::entry $top.head -width 60
9510 $top.head insert 0 [lindex $commitinfo($val(id)) 0]
9511 $top.head conf -state readonly
9512 grid x $top.head -sticky ew
9513 grid columnconfigure $top 1 -weight 1
9514 ${NS}::label $top.nlab -text [mc "Name:"]
9515 ${NS}::entry $top.name -width 40
9516 $top.name insert 0 $val(name)
9517 grid $top.nlab $top.name -sticky w
9518 ${NS}::frame $top.buts
9519 ${NS}::button $top.buts.go -text $ui(accept) -command $val(command)
9520 ${NS}::button $top.buts.can -text [mc "Cancel"] -command "catch {destroy $top}"
9521 bind $top <Key-Return> $val(command)
9522 bind $top <Key-Escape> "catch {destroy $top}"
9523 grid $top.buts.go $top.buts.can
9524 grid columnconfigure $top.buts 0 -weight 1 -uniform a
9525 grid columnconfigure $top.buts 1 -weight 1 -uniform a
9526 grid $top.buts - -pady 10 -sticky ew
9531 global headids idheads
9533 set name [$top.name get]
9534 set id [$top.sha1 get]
9538 error_popup [mc "Please specify a name for the new branch"] $top
9541 if {[info exists headids($name)]} {
9542 if {![confirm_popup [mc \
9543 "Branch '%s' already exists. Overwrite?" $name] $top]} {
9546 set old_id $headids($name)
9549 catch {destroy $top}
9550 lappend cmdargs $name $id
9554 eval exec git branch $cmdargs
9560 if {$old_id ne {}} {
9566 set headids($name) $id
9567 lappend idheads($id) $name
9576 proc mvbrgo {top prevname} {
9577 global headids idheads mainhead mainheadid
9579 set name [$top.name get]
9580 set id [$top.sha1 get]
9582 if {$name eq $prevname} {
9583 catch {destroy $top}
9587 error_popup [mc "Please specify a new name for the branch"] $top
9590 catch {destroy $top}
9591 lappend cmdargs -m $prevname $name
9592 nowbusy renamebranch
9595 eval exec git branch $cmdargs
9597 notbusy renamebranch
9600 notbusy renamebranch
9601 removehead $id $prevname
9602 removedhead $id $prevname
9603 set headids($name) $id
9604 lappend idheads($id) $name
9606 if {$prevname eq $mainhead} {
9616 proc exec_citool {tool_args {baseid {}}} {
9617 global commitinfo env
9619 set save_env [array get env GIT_AUTHOR_*]
9621 if {$baseid ne {}} {
9622 if {![info exists commitinfo($baseid)]} {
9625 set author [lindex $commitinfo($baseid) 1]
9626 set date [lindex $commitinfo($baseid) 2]
9627 if {[regexp {^\s*(\S.*\S|\S)\s*<(.*)>\s*$} \
9628 $author author name email]
9630 set env(GIT_AUTHOR_NAME) $name
9631 set env(GIT_AUTHOR_EMAIL) $email
9632 set env(GIT_AUTHOR_DATE) $date
9636 eval exec git citool $tool_args &
9638 array unset env GIT_AUTHOR_*
9639 array set env $save_env
9642 proc cherrypick {} {
9643 global rowmenuid curview
9644 global mainhead mainheadid
9647 set oldhead [exec git rev-parse HEAD]
9648 set dheads [descheads $rowmenuid]
9649 if {$dheads ne {} && [lsearch -exact $dheads $oldhead] >= 0} {
9650 set ok [confirm_popup [mc "Commit %s is already\
9651 included in branch %s -- really re-apply it?" \
9652 [string range $rowmenuid 0 7] $mainhead]]
9655 nowbusy cherrypick [mc "Cherry-picking"]
9657 # Unfortunately git-cherry-pick writes stuff to stderr even when
9658 # no error occurs, and exec takes that as an indication of error...
9659 if {[catch {exec sh -c "git cherry-pick -r $rowmenuid 2>&1"} err]} {
9662 {Entry '(.*)' (would be overwritten by merge|not uptodate)} \
9664 error_popup [mc "Cherry-pick failed because of local changes\
9665 to file '%s'.\nPlease commit, reset or stash\
9666 your changes and try again." $fname]
9667 } elseif {[regexp -line \
9668 {^(CONFLICT \(.*\):|Automatic cherry-pick failed|error: could not apply)} \
9670 if {[confirm_popup [mc "Cherry-pick failed because of merge\
9671 conflict.\nDo you wish to run git citool to\
9673 # Force citool to read MERGE_MSG
9674 file delete [file join $gitdir "GITGUI_MSG"]
9675 exec_citool {} $rowmenuid
9683 set newhead [exec git rev-parse HEAD]
9684 if {$newhead eq $oldhead} {
9686 error_popup [mc "No changes committed"]
9689 addnewchild $newhead $oldhead
9690 if {[commitinview $oldhead $curview]} {
9691 # XXX this isn't right if we have a path limit...
9692 insertrow $newhead $oldhead $curview
9693 if {$mainhead ne {}} {
9694 movehead $newhead $mainhead
9695 movedhead $newhead $mainhead
9697 set mainheadid $newhead
9706 global rowmenuid curview
9707 global mainhead mainheadid
9710 set oldhead [exec git rev-parse HEAD]
9711 set dheads [descheads $rowmenuid]
9712 if { $dheads eq {} || [lsearch -exact $dheads $oldhead] == -1 } {
9713 set ok [confirm_popup [mc "Commit %s is not\
9714 included in branch %s -- really revert it?" \
9715 [string range $rowmenuid 0 7] $mainhead]]
9718 nowbusy revert [mc "Reverting"]
9721 if [catch {exec git revert --no-edit $rowmenuid} err] {
9723 if [regexp {files would be overwritten by merge:(\n(( |\t)+[^\n]+\n)+)}\
9725 regsub {\n( |\t)+} $files "\n" files
9726 error_popup [mc "Revert failed because of local changes to\
9727 the following files:%s Please commit, reset or stash \
9728 your changes and try again." $files]
9729 } elseif [regexp {error: could not revert} $err] {
9730 if [confirm_popup [mc "Revert failed because of merge conflict.\n\
9731 Do you wish to run git citool to resolve it?"]] {
9732 # Force citool to read MERGE_MSG
9733 file delete [file join $gitdir "GITGUI_MSG"]
9734 exec_citool {} $rowmenuid
9736 } else { error_popup $err }
9741 set newhead [exec git rev-parse HEAD]
9742 if { $newhead eq $oldhead } {
9744 error_popup [mc "No changes committed"]
9748 addnewchild $newhead $oldhead
9750 if [commitinview $oldhead $curview] {
9751 # XXX this isn't right if we have a path limit...
9752 insertrow $newhead $oldhead $curview
9753 if {$mainhead ne {}} {
9754 movehead $newhead $mainhead
9755 movedhead $newhead $mainhead
9757 set mainheadid $newhead
9767 global mainhead rowmenuid confirm_ok resettype NS
9770 set w ".confirmreset"
9773 wm title $w [mc "Confirm reset"]
9774 ${NS}::label $w.m -text \
9775 [mc "Reset branch %s to %s?" $mainhead [string range $rowmenuid 0 7]]
9776 pack $w.m -side top -fill x -padx 20 -pady 20
9777 ${NS}::labelframe $w.f -text [mc "Reset type:"]
9779 ${NS}::radiobutton $w.f.soft -value soft -variable resettype \
9780 -text [mc "Soft: Leave working tree and index untouched"]
9781 grid $w.f.soft -sticky w
9782 ${NS}::radiobutton $w.f.mixed -value mixed -variable resettype \
9783 -text [mc "Mixed: Leave working tree untouched, reset index"]
9784 grid $w.f.mixed -sticky w
9785 ${NS}::radiobutton $w.f.hard -value hard -variable resettype \
9786 -text [mc "Hard: Reset working tree and index\n(discard ALL local changes)"]
9787 grid $w.f.hard -sticky w
9788 pack $w.f -side top -fill x -padx 4
9789 ${NS}::button $w.ok -text [mc OK] -command "set confirm_ok 1; destroy $w"
9790 pack $w.ok -side left -fill x -padx 20 -pady 20
9791 ${NS}::button $w.cancel -text [mc Cancel] -command "destroy $w"
9792 bind $w <Key-Escape> [list destroy $w]
9793 pack $w.cancel -side right -fill x -padx 20 -pady 20
9794 bind $w <Visibility> "grab $w; focus $w"
9796 if {!$confirm_ok} return
9797 if {[catch {set fd [open \
9798 [list | git reset --$resettype $rowmenuid 2>@1] r]} err]} {
9802 filerun $fd [list readresetstat $fd]
9803 nowbusy reset [mc "Resetting"]
9808 proc readresetstat {fd} {
9809 global mainhead mainheadid showlocalchanges rprogcoord
9811 if {[gets $fd line] >= 0} {
9812 if {[regexp {([0-9]+)% \(([0-9]+)/([0-9]+)\)} $line match p m n]} {
9813 set rprogcoord [expr {1.0 * $m / $n}]
9821 if {[catch {close $fd} err]} {
9824 set oldhead $mainheadid
9825 set newhead [exec git rev-parse HEAD]
9826 if {$newhead ne $oldhead} {
9827 movehead $newhead $mainhead
9828 movedhead $newhead $mainhead
9829 set mainheadid $newhead
9833 if {$showlocalchanges} {
9839 # context menu for a head
9840 proc headmenu {x y id head} {
9841 global headmenuid headmenuhead headctxmenu mainhead headids
9845 set headmenuhead $head
9846 array set state {0 normal 1 normal 2 normal}
9847 if {[string match "remotes/*" $head]} {
9848 set localhead [string range $head [expr [string last / $head] + 1] end]
9849 if {[info exists headids($localhead)]} {
9850 set state(0) disabled
9852 array set state {1 disabled 2 disabled}
9854 if {$head eq $mainhead} {
9855 array set state {0 disabled 2 disabled}
9858 $headctxmenu entryconfigure $i -state $state($i)
9860 tk_popup $headctxmenu $x $y
9864 global headmenuid headmenuhead headids
9865 global showlocalchanges
9867 # check the tree is clean first??
9868 set newhead $headmenuhead
9869 set command [list | git checkout]
9870 if {[string match "remotes/*" $newhead]} {
9872 set newhead [string range $newhead [expr [string last / $newhead] + 1] end]
9873 # The following check is redundant - the menu option should
9874 # be disabled to begin with...
9875 if {[info exists headids($newhead)]} {
9876 error_popup [mc "A local branch named %s exists already" $newhead]
9879 lappend command -b $newhead --track $remote
9881 lappend command $newhead
9883 lappend command 2>@1
9884 nowbusy checkout [mc "Checking out"]
9888 set fd [open $command r]
9892 if {$showlocalchanges} {
9896 filerun $fd [list readcheckoutstat $fd $newhead $headmenuid]
9900 proc readcheckoutstat {fd newhead newheadid} {
9901 global mainhead mainheadid headids idheads showlocalchanges progresscoords
9902 global viewmainheadid curview
9904 if {[gets $fd line] >= 0} {
9905 if {[regexp {([0-9]+)% \(([0-9]+)/([0-9]+)\)} $line match p m n]} {
9906 set progresscoords [list 0 [expr {1.0 * $m / $n}]]
9911 set progresscoords {0 0}
9914 if {[catch {close $fd} err]} {
9918 set oldmainid $mainheadid
9919 if {! [info exists headids($newhead)]} {
9920 set headids($newhead) $newheadid
9921 lappend idheads($newheadid) $newhead
9922 addedhead $newheadid $newhead
9924 set mainhead $newhead
9925 set mainheadid $newheadid
9926 set viewmainheadid($curview) $newheadid
9927 redrawtags $oldmainid
9928 redrawtags $newheadid
9930 if {$showlocalchanges} {
9936 global headmenuid headmenuhead mainhead
9939 set head $headmenuhead
9941 # this check shouldn't be needed any more...
9942 if {$head eq $mainhead} {
9943 error_popup [mc "Cannot delete the currently checked-out branch"]
9946 set dheads [descheads $id]
9947 if {[llength $dheads] == 1 && $idheads($dheads) eq $head} {
9948 # the stuff on this branch isn't on any other branch
9949 if {![confirm_popup [mc "The commits on branch %s aren't on any other\
9950 branch.\nReally delete branch %s?" $head $head]]} return
9954 if {[catch {exec git branch -D $head} err]} {
9959 removehead $id $head
9960 removedhead $id $head
9967 # Display a list of tags and heads
9969 global showrefstop bgcolor fgcolor selectbgcolor NS
9970 global bglist fglist reflistfilter reflist maincursor
9973 set showrefstop $top
9974 if {[winfo exists $top]} {
9980 wm title $top [mc "Tags and heads: %s" [file tail [pwd]]]
9981 make_transient $top .
9982 text $top.list -background $bgcolor -foreground $fgcolor \
9983 -selectbackground $selectbgcolor -font mainfont \
9984 -xscrollcommand "$top.xsb set" -yscrollcommand "$top.ysb set" \
9985 -width 30 -height 20 -cursor $maincursor \
9986 -spacing1 1 -spacing3 1 -state disabled
9987 $top.list tag configure highlight -background $selectbgcolor
9988 if {![lsearch -exact $bglist $top.list]} {
9989 lappend bglist $top.list
9990 lappend fglist $top.list
9992 ${NS}::scrollbar $top.ysb -command "$top.list yview" -orient vertical
9993 ${NS}::scrollbar $top.xsb -command "$top.list xview" -orient horizontal
9994 grid $top.list $top.ysb -sticky nsew
9995 grid $top.xsb x -sticky ew
9997 ${NS}::label $top.f.l -text "[mc "Filter"]: "
9998 ${NS}::entry $top.f.e -width 20 -textvariable reflistfilter
9999 set reflistfilter "*"
10000 trace add variable reflistfilter write reflistfilter_change
10001 pack $top.f.e -side right -fill x -expand 1
10002 pack $top.f.l -side left
10003 grid $top.f - -sticky ew -pady 2
10004 ${NS}::button $top.close -command [list destroy $top] -text [mc "Close"]
10005 bind $top <Key-Escape> [list destroy $top]
10007 grid columnconfigure $top 0 -weight 1
10008 grid rowconfigure $top 0 -weight 1
10009 bind $top.list <1> {break}
10010 bind $top.list <B1-Motion> {break}
10011 bind $top.list <ButtonRelease-1> {sel_reflist %W %x %y; break}
10016 proc sel_reflist {w x y} {
10017 global showrefstop reflist headids tagids otherrefids
10019 if {![winfo exists $showrefstop]} return
10020 set l [lindex [split [$w index "@$x,$y"] "."] 0]
10021 set ref [lindex $reflist [expr {$l-1}]]
10022 set n [lindex $ref 0]
10023 switch -- [lindex $ref 1] {
10024 "H" {selbyid $headids($n)}
10025 "T" {selbyid $tagids($n)}
10026 "o" {selbyid $otherrefids($n)}
10028 $showrefstop.list tag add highlight $l.0 "$l.0 lineend"
10031 proc unsel_reflist {} {
10034 if {![info exists showrefstop] || ![winfo exists $showrefstop]} return
10035 $showrefstop.list tag remove highlight 0.0 end
10038 proc reflistfilter_change {n1 n2 op} {
10039 global reflistfilter
10041 after cancel refill_reflist
10042 after 200 refill_reflist
10045 proc refill_reflist {} {
10046 global reflist reflistfilter showrefstop headids tagids otherrefids
10049 if {![info exists showrefstop] || ![winfo exists $showrefstop]} return
10051 foreach n [array names headids] {
10052 if {[string match $reflistfilter $n]} {
10053 if {[commitinview $headids($n) $curview]} {
10054 lappend refs [list $n H]
10056 interestedin $headids($n) {run refill_reflist}
10060 foreach n [array names tagids] {
10061 if {[string match $reflistfilter $n]} {
10062 if {[commitinview $tagids($n) $curview]} {
10063 lappend refs [list $n T]
10065 interestedin $tagids($n) {run refill_reflist}
10069 foreach n [array names otherrefids] {
10070 if {[string match $reflistfilter $n]} {
10071 if {[commitinview $otherrefids($n) $curview]} {
10072 lappend refs [list $n o]
10074 interestedin $otherrefids($n) {run refill_reflist}
10078 set refs [lsort -index 0 $refs]
10079 if {$refs eq $reflist} return
10081 # Update the contents of $showrefstop.list according to the
10082 # differences between $reflist (old) and $refs (new)
10083 $showrefstop.list conf -state normal
10084 $showrefstop.list insert end "\n"
10087 while {$i < [llength $reflist] || $j < [llength $refs]} {
10088 if {$i < [llength $reflist]} {
10089 if {$j < [llength $refs]} {
10090 set cmp [string compare [lindex $reflist $i 0] \
10091 [lindex $refs $j 0]]
10093 set cmp [string compare [lindex $reflist $i 1] \
10094 [lindex $refs $j 1]]
10104 $showrefstop.list delete "[expr {$j+1}].0" "[expr {$j+2}].0"
10112 set l [expr {$j + 1}]
10113 $showrefstop.list image create $l.0 -align baseline \
10114 -image reficon-[lindex $refs $j 1] -padx 2
10115 $showrefstop.list insert $l.1 "[lindex $refs $j 0]\n"
10121 # delete last newline
10122 $showrefstop.list delete end-2c end-1c
10123 $showrefstop.list conf -state disabled
10126 # Stuff for finding nearby tags
10127 proc getallcommits {} {
10128 global allcommits nextarc seeds allccache allcwait cachedarcs allcupdate
10129 global idheads idtags idotherrefs allparents tagobjid
10132 if {![info exists allcommits]} {
10138 set allccache [file join $gitdir "gitk.cache"]
10140 set f [open $allccache r]
10149 set cmd [list | git rev-list --parents]
10150 set allcupdate [expr {$seeds ne {}}]
10151 if {!$allcupdate} {
10154 set refs [concat [array names idheads] [array names idtags] \
10155 [array names idotherrefs]]
10158 foreach name [array names tagobjid] {
10159 lappend tagobjs $tagobjid($name)
10161 foreach id [lsort -unique $refs] {
10162 if {![info exists allparents($id)] &&
10163 [lsearch -exact $tagobjs $id] < 0} {
10168 foreach id $seeds {
10174 set fd [open [concat $cmd $ids] r]
10175 fconfigure $fd -blocking 0
10178 filerun $fd [list getallclines $fd]
10184 # Since most commits have 1 parent and 1 child, we group strings of
10185 # such commits into "arcs" joining branch/merge points (BMPs), which
10186 # are commits that either don't have 1 parent or don't have 1 child.
10188 # arcnos(id) - incoming arcs for BMP, arc we're on for other nodes
10189 # arcout(id) - outgoing arcs for BMP
10190 # arcids(a) - list of IDs on arc including end but not start
10191 # arcstart(a) - BMP ID at start of arc
10192 # arcend(a) - BMP ID at end of arc
10193 # growing(a) - arc a is still growing
10194 # arctags(a) - IDs out of arcids (excluding end) that have tags
10195 # archeads(a) - IDs out of arcids (excluding end) that have heads
10196 # The start of an arc is at the descendent end, so "incoming" means
10197 # coming from descendents, and "outgoing" means going towards ancestors.
10199 proc getallclines {fd} {
10200 global allparents allchildren idtags idheads nextarc
10201 global arcnos arcids arctags arcout arcend arcstart archeads growing
10202 global seeds allcommits cachedarcs allcupdate
10205 while {[incr nid] <= 1000 && [gets $fd line] >= 0} {
10206 set id [lindex $line 0]
10207 if {[info exists allparents($id)]} {
10212 set olds [lrange $line 1 end]
10213 set allparents($id) $olds
10214 if {![info exists allchildren($id)]} {
10215 set allchildren($id) {}
10220 if {[llength $olds] == 1 && [llength $a] == 1} {
10221 lappend arcids($a) $id
10222 if {[info exists idtags($id)]} {
10223 lappend arctags($a) $id
10225 if {[info exists idheads($id)]} {
10226 lappend archeads($a) $id
10228 if {[info exists allparents($olds)]} {
10229 # seen parent already
10230 if {![info exists arcout($olds)]} {
10233 lappend arcids($a) $olds
10234 set arcend($a) $olds
10237 lappend allchildren($olds) $id
10238 lappend arcnos($olds) $a
10242 foreach a $arcnos($id) {
10243 lappend arcids($a) $id
10250 lappend allchildren($p) $id
10251 set a [incr nextarc]
10252 set arcstart($a) $id
10253 set archeads($a) {}
10255 set archeads($a) {}
10259 if {[info exists allparents($p)]} {
10260 # seen it already, may need to make a new branch
10261 if {![info exists arcout($p)]} {
10264 lappend arcids($a) $p
10268 lappend arcnos($p) $a
10270 set arcout($id) $ao
10273 global cached_dheads cached_dtags cached_atags
10274 unset -nocomplain cached_dheads
10275 unset -nocomplain cached_dtags
10276 unset -nocomplain cached_atags
10279 return [expr {$nid >= 1000? 2: 1}]
10283 fconfigure $fd -blocking 1
10286 # got an error reading the list of commits
10287 # if we were updating, try rereading the whole thing again
10293 error_popup "[mc "Error reading commit topology information;\
10294 branch and preceding/following tag information\
10295 will be incomplete."]\n($err)"
10298 if {[incr allcommits -1] == 0} {
10308 proc recalcarc {a} {
10309 global arctags archeads arcids idtags idheads
10313 foreach id [lrange $arcids($a) 0 end-1] {
10314 if {[info exists idtags($id)]} {
10317 if {[info exists idheads($id)]} {
10321 set arctags($a) $at
10322 set archeads($a) $ah
10325 proc splitarc {p} {
10326 global arcnos arcids nextarc arctags archeads idtags idheads
10327 global arcstart arcend arcout allparents growing
10330 if {[llength $a] != 1} {
10331 puts "oops splitarc called but [llength $a] arcs already"
10334 set a [lindex $a 0]
10335 set i [lsearch -exact $arcids($a) $p]
10337 puts "oops splitarc $p not in arc $a"
10340 set na [incr nextarc]
10341 if {[info exists arcend($a)]} {
10342 set arcend($na) $arcend($a)
10344 set l [lindex $allparents([lindex $arcids($a) end]) 0]
10345 set j [lsearch -exact $arcnos($l) $a]
10346 set arcnos($l) [lreplace $arcnos($l) $j $j $na]
10348 set tail [lrange $arcids($a) [expr {$i+1}] end]
10349 set arcids($a) [lrange $arcids($a) 0 $i]
10351 set arcstart($na) $p
10353 set arcids($na) $tail
10354 if {[info exists growing($a)]} {
10360 if {[llength $arcnos($id)] == 1} {
10361 set arcnos($id) $na
10363 set j [lsearch -exact $arcnos($id) $a]
10364 set arcnos($id) [lreplace $arcnos($id) $j $j $na]
10368 # reconstruct tags and heads lists
10369 if {$arctags($a) ne {} || $archeads($a) ne {}} {
10373 set arctags($na) {}
10374 set archeads($na) {}
10378 # Update things for a new commit added that is a child of one
10379 # existing commit. Used when cherry-picking.
10380 proc addnewchild {id p} {
10381 global allparents allchildren idtags nextarc
10382 global arcnos arcids arctags arcout arcend arcstart archeads growing
10383 global seeds allcommits
10385 if {![info exists allcommits] || ![info exists arcnos($p)]} return
10386 set allparents($id) [list $p]
10387 set allchildren($id) {}
10390 lappend allchildren($p) $id
10391 set a [incr nextarc]
10392 set arcstart($a) $id
10393 set archeads($a) {}
10395 set arcids($a) [list $p]
10397 if {![info exists arcout($p)]} {
10400 lappend arcnos($p) $a
10401 set arcout($id) [list $a]
10404 # This implements a cache for the topology information.
10405 # The cache saves, for each arc, the start and end of the arc,
10406 # the ids on the arc, and the outgoing arcs from the end.
10407 proc readcache {f} {
10408 global arcnos arcids arcout arcstart arcend arctags archeads nextarc
10409 global idtags idheads allparents cachedarcs possible_seeds seeds growing
10413 set lim $cachedarcs
10414 if {$lim - $a > 500} {
10415 set lim [expr {$a + 500}]
10419 # finish reading the cache and setting up arctags, etc.
10421 if {$line ne "1"} {error "bad final version"}
10423 foreach id [array names idtags] {
10424 if {[info exists arcnos($id)] && [llength $arcnos($id)] == 1 &&
10425 [llength $allparents($id)] == 1} {
10426 set a [lindex $arcnos($id) 0]
10427 if {$arctags($a) eq {}} {
10432 foreach id [array names idheads] {
10433 if {[info exists arcnos($id)] && [llength $arcnos($id)] == 1 &&
10434 [llength $allparents($id)] == 1} {
10435 set a [lindex $arcnos($id) 0]
10436 if {$archeads($a) eq {}} {
10441 foreach id [lsort -unique $possible_seeds] {
10442 if {$arcnos($id) eq {}} {
10448 while {[incr a] <= $lim} {
10450 if {[llength $line] != 3} {error "bad line"}
10451 set s [lindex $line 0]
10452 set arcstart($a) $s
10453 lappend arcout($s) $a
10454 if {![info exists arcnos($s)]} {
10455 lappend possible_seeds $s
10458 set e [lindex $line 1]
10463 if {![info exists arcout($e)]} {
10467 set arcids($a) [lindex $line 2]
10468 foreach id $arcids($a) {
10469 lappend allparents($s) $id
10471 lappend arcnos($id) $a
10473 if {![info exists allparents($s)]} {
10474 set allparents($s) {}
10477 set archeads($a) {}
10479 set nextarc [expr {$a - 1}]
10491 proc getcache {f} {
10492 global nextarc cachedarcs possible_seeds
10496 if {[llength $line] != 2 || [lindex $line 0] ne "1"} {error "bad version"}
10497 # make sure it's an integer
10498 set cachedarcs [expr {int([lindex $line 1])}]
10499 if {$cachedarcs < 0} {error "bad number of arcs"}
10501 set possible_seeds {}
10509 proc dropcache {err} {
10510 global allcwait nextarc cachedarcs seeds
10512 #puts "dropping cache ($err)"
10513 foreach v {arcnos arcout arcids arcstart arcend growing \
10514 arctags archeads allparents allchildren} {
10516 unset -nocomplain $v
10525 proc writecache {f} {
10526 global cachearc cachedarcs allccache
10527 global arcstart arcend arcnos arcids arcout
10530 set lim $cachedarcs
10531 if {$lim - $a > 1000} {
10532 set lim [expr {$a + 1000}]
10535 while {[incr a] <= $lim} {
10536 if {[info exists arcend($a)]} {
10537 puts $f [list $arcstart($a) $arcend($a) $arcids($a)]
10539 puts $f [list $arcstart($a) {} $arcids($a)]
10544 catch {file delete $allccache}
10545 #puts "writing cache failed ($err)"
10548 set cachearc [expr {$a - 1}]
10549 if {$a > $cachedarcs} {
10557 proc savecache {} {
10558 global nextarc cachedarcs cachearc allccache
10560 if {$nextarc == $cachedarcs} return
10562 set cachedarcs $nextarc
10564 set f [open $allccache w]
10565 puts $f [list 1 $cachedarcs]
10570 # Returns 1 if a is an ancestor of b, -1 if b is an ancestor of a,
10571 # or 0 if neither is true.
10572 proc anc_or_desc {a b} {
10573 global arcout arcstart arcend arcnos cached_isanc
10575 if {$arcnos($a) eq $arcnos($b)} {
10576 # Both are on the same arc(s); either both are the same BMP,
10577 # or if one is not a BMP, the other is also not a BMP or is
10578 # the BMP at end of the arc (and it only has 1 incoming arc).
10579 # Or both can be BMPs with no incoming arcs.
10580 if {$a eq $b || $arcnos($a) eq {}} {
10583 # assert {[llength $arcnos($a)] == 1}
10584 set arc [lindex $arcnos($a) 0]
10585 set i [lsearch -exact $arcids($arc) $a]
10586 set j [lsearch -exact $arcids($arc) $b]
10587 if {$i < 0 || $i > $j} {
10594 if {![info exists arcout($a)]} {
10595 set arc [lindex $arcnos($a) 0]
10596 if {[info exists arcend($arc)]} {
10597 set aend $arcend($arc)
10601 set a $arcstart($arc)
10605 if {![info exists arcout($b)]} {
10606 set arc [lindex $arcnos($b) 0]
10607 if {[info exists arcend($arc)]} {
10608 set bend $arcend($arc)
10612 set b $arcstart($arc)
10622 if {[info exists cached_isanc($a,$bend)]} {
10623 if {$cached_isanc($a,$bend)} {
10627 if {[info exists cached_isanc($b,$aend)]} {
10628 if {$cached_isanc($b,$aend)} {
10631 if {[info exists cached_isanc($a,$bend)]} {
10636 set todo [list $a $b]
10639 for {set i 0} {$i < [llength $todo]} {incr i} {
10640 set x [lindex $todo $i]
10641 if {$anc($x) eq {}} {
10644 foreach arc $arcnos($x) {
10645 set xd $arcstart($arc)
10646 if {$xd eq $bend} {
10647 set cached_isanc($a,$bend) 1
10648 set cached_isanc($b,$aend) 0
10650 } elseif {$xd eq $aend} {
10651 set cached_isanc($b,$aend) 1
10652 set cached_isanc($a,$bend) 0
10655 if {![info exists anc($xd)]} {
10656 set anc($xd) $anc($x)
10658 } elseif {$anc($xd) ne $anc($x)} {
10663 set cached_isanc($a,$bend) 0
10664 set cached_isanc($b,$aend) 0
10668 # This identifies whether $desc has an ancestor that is
10669 # a growing tip of the graph and which is not an ancestor of $anc
10670 # and returns 0 if so and 1 if not.
10671 # If we subsequently discover a tag on such a growing tip, and that
10672 # turns out to be a descendent of $anc (which it could, since we
10673 # don't necessarily see children before parents), then $desc
10674 # isn't a good choice to display as a descendent tag of
10675 # $anc (since it is the descendent of another tag which is
10676 # a descendent of $anc). Similarly, $anc isn't a good choice to
10677 # display as a ancestor tag of $desc.
10679 proc is_certain {desc anc} {
10680 global arcnos arcout arcstart arcend growing problems
10683 if {[llength $arcnos($anc)] == 1} {
10684 # tags on the same arc are certain
10685 if {$arcnos($desc) eq $arcnos($anc)} {
10688 if {![info exists arcout($anc)]} {
10689 # if $anc is partway along an arc, use the start of the arc instead
10690 set a [lindex $arcnos($anc) 0]
10691 set anc $arcstart($a)
10694 if {[llength $arcnos($desc)] > 1 || [info exists arcout($desc)]} {
10697 set a [lindex $arcnos($desc) 0]
10703 set anclist [list $x]
10707 for {set i 0} {$i < [llength $anclist] && ($nnh > 0 || $ngrowanc > 0)} {incr i} {
10708 set x [lindex $anclist $i]
10713 foreach a $arcout($x) {
10714 if {[info exists growing($a)]} {
10715 if {![info exists growanc($x)] && $dl($x)} {
10721 if {[info exists dl($y)]} {
10725 if {![info exists done($y)]} {
10728 if {[info exists growanc($x)]} {
10732 for {set k 0} {$k < [llength $xl]} {incr k} {
10733 set z [lindex $xl $k]
10734 foreach c $arcout($z) {
10735 if {[info exists arcend($c)]} {
10737 if {[info exists dl($v)] && $dl($v)} {
10739 if {![info exists done($v)]} {
10742 if {[info exists growanc($v)]} {
10752 } elseif {$y eq $anc || !$dl($x)} {
10763 foreach x [array names growanc] {
10772 proc validate_arctags {a} {
10773 global arctags idtags
10776 set na $arctags($a)
10777 foreach id $arctags($a) {
10779 if {![info exists idtags($id)]} {
10780 set na [lreplace $na $i $i]
10784 set arctags($a) $na
10787 proc validate_archeads {a} {
10788 global archeads idheads
10791 set na $archeads($a)
10792 foreach id $archeads($a) {
10794 if {![info exists idheads($id)]} {
10795 set na [lreplace $na $i $i]
10799 set archeads($a) $na
10802 # Return the list of IDs that have tags that are descendents of id,
10803 # ignoring IDs that are descendents of IDs already reported.
10804 proc desctags {id} {
10805 global arcnos arcstart arcids arctags idtags allparents
10806 global growing cached_dtags
10808 if {![info exists allparents($id)]} {
10811 set t1 [clock clicks -milliseconds]
10813 if {[llength $arcnos($id)] == 1 && [llength $allparents($id)] == 1} {
10814 # part-way along an arc; check that arc first
10815 set a [lindex $arcnos($id) 0]
10816 if {$arctags($a) ne {}} {
10817 validate_arctags $a
10818 set i [lsearch -exact $arcids($a) $id]
10820 foreach t $arctags($a) {
10821 set j [lsearch -exact $arcids($a) $t]
10822 if {$j >= $i} break
10829 set id $arcstart($a)
10830 if {[info exists idtags($id)]} {
10834 if {[info exists cached_dtags($id)]} {
10835 return $cached_dtags($id)
10839 set todo [list $id]
10842 for {set i 0} {$i < [llength $todo] && $nc > 0} {incr i} {
10843 set id [lindex $todo $i]
10845 set ta [info exists hastaggedancestor($id)]
10849 # ignore tags on starting node
10850 if {!$ta && $i > 0} {
10851 if {[info exists idtags($id)]} {
10852 set tagloc($id) $id
10854 } elseif {[info exists cached_dtags($id)]} {
10855 set tagloc($id) $cached_dtags($id)
10859 foreach a $arcnos($id) {
10860 set d $arcstart($a)
10861 if {!$ta && $arctags($a) ne {}} {
10862 validate_arctags $a
10863 if {$arctags($a) ne {}} {
10864 lappend tagloc($id) [lindex $arctags($a) end]
10867 if {$ta || $arctags($a) ne {}} {
10868 set tomark [list $d]
10869 for {set j 0} {$j < [llength $tomark]} {incr j} {
10870 set dd [lindex $tomark $j]
10871 if {![info exists hastaggedancestor($dd)]} {
10872 if {[info exists done($dd)]} {
10873 foreach b $arcnos($dd) {
10874 lappend tomark $arcstart($b)
10876 if {[info exists tagloc($dd)]} {
10879 } elseif {[info exists queued($dd)]} {
10882 set hastaggedancestor($dd) 1
10886 if {![info exists queued($d)]} {
10889 if {![info exists hastaggedancestor($d)]} {
10896 foreach id [array names tagloc] {
10897 if {![info exists hastaggedancestor($id)]} {
10898 foreach t $tagloc($id) {
10899 if {[lsearch -exact $tags $t] < 0} {
10905 set t2 [clock clicks -milliseconds]
10908 # remove tags that are descendents of other tags
10909 for {set i 0} {$i < [llength $tags]} {incr i} {
10910 set a [lindex $tags $i]
10911 for {set j 0} {$j < $i} {incr j} {
10912 set b [lindex $tags $j]
10913 set r [anc_or_desc $a $b]
10915 set tags [lreplace $tags $j $j]
10918 } elseif {$r == -1} {
10919 set tags [lreplace $tags $i $i]
10926 if {[array names growing] ne {}} {
10927 # graph isn't finished, need to check if any tag could get
10928 # eclipsed by another tag coming later. Simply ignore any
10929 # tags that could later get eclipsed.
10932 if {[is_certain $t $origid]} {
10936 if {$tags eq $ctags} {
10937 set cached_dtags($origid) $tags
10942 set cached_dtags($origid) $tags
10944 set t3 [clock clicks -milliseconds]
10945 if {0 && $t3 - $t1 >= 100} {
10946 puts "iterating descendents ($loopix/[llength $todo] nodes) took\
10947 [expr {$t2-$t1}]+[expr {$t3-$t2}]ms, $nc candidates left"
10952 proc anctags {id} {
10953 global arcnos arcids arcout arcend arctags idtags allparents
10954 global growing cached_atags
10956 if {![info exists allparents($id)]} {
10959 set t1 [clock clicks -milliseconds]
10961 if {[llength $arcnos($id)] == 1 && [llength $allparents($id)] == 1} {
10962 # part-way along an arc; check that arc first
10963 set a [lindex $arcnos($id) 0]
10964 if {$arctags($a) ne {}} {
10965 validate_arctags $a
10966 set i [lsearch -exact $arcids($a) $id]
10967 foreach t $arctags($a) {
10968 set j [lsearch -exact $arcids($a) $t]
10974 if {![info exists arcend($a)]} {
10978 if {[info exists idtags($id)]} {
10982 if {[info exists cached_atags($id)]} {
10983 return $cached_atags($id)
10987 set todo [list $id]
10991 for {set i 0} {$i < [llength $todo] && $nc > 0} {incr i} {
10992 set id [lindex $todo $i]
10994 set td [info exists hastaggeddescendent($id)]
10998 # ignore tags on starting node
10999 if {!$td && $i > 0} {
11000 if {[info exists idtags($id)]} {
11001 set tagloc($id) $id
11003 } elseif {[info exists cached_atags($id)]} {
11004 set tagloc($id) $cached_atags($id)
11008 foreach a $arcout($id) {
11009 if {!$td && $arctags($a) ne {}} {
11010 validate_arctags $a
11011 if {$arctags($a) ne {}} {
11012 lappend tagloc($id) [lindex $arctags($a) 0]
11015 if {![info exists arcend($a)]} continue
11017 if {$td || $arctags($a) ne {}} {
11018 set tomark [list $d]
11019 for {set j 0} {$j < [llength $tomark]} {incr j} {
11020 set dd [lindex $tomark $j]
11021 if {![info exists hastaggeddescendent($dd)]} {
11022 if {[info exists done($dd)]} {
11023 foreach b $arcout($dd) {
11024 if {[info exists arcend($b)]} {
11025 lappend tomark $arcend($b)
11028 if {[info exists tagloc($dd)]} {
11031 } elseif {[info exists queued($dd)]} {
11034 set hastaggeddescendent($dd) 1
11038 if {![info exists queued($d)]} {
11041 if {![info exists hastaggeddescendent($d)]} {
11047 set t2 [clock clicks -milliseconds]
11050 foreach id [array names tagloc] {
11051 if {![info exists hastaggeddescendent($id)]} {
11052 foreach t $tagloc($id) {
11053 if {[lsearch -exact $tags $t] < 0} {
11060 # remove tags that are ancestors of other tags
11061 for {set i 0} {$i < [llength $tags]} {incr i} {
11062 set a [lindex $tags $i]
11063 for {set j 0} {$j < $i} {incr j} {
11064 set b [lindex $tags $j]
11065 set r [anc_or_desc $a $b]
11067 set tags [lreplace $tags $j $j]
11070 } elseif {$r == 1} {
11071 set tags [lreplace $tags $i $i]
11078 if {[array names growing] ne {}} {
11079 # graph isn't finished, need to check if any tag could get
11080 # eclipsed by another tag coming later. Simply ignore any
11081 # tags that could later get eclipsed.
11084 if {[is_certain $origid $t]} {
11088 if {$tags eq $ctags} {
11089 set cached_atags($origid) $tags
11094 set cached_atags($origid) $tags
11096 set t3 [clock clicks -milliseconds]
11097 if {0 && $t3 - $t1 >= 100} {
11098 puts "iterating ancestors ($loopix/[llength $todo] nodes) took\
11099 [expr {$t2-$t1}]+[expr {$t3-$t2}]ms, $nc candidates left"
11104 # Return the list of IDs that have heads that are descendents of id,
11105 # including id itself if it has a head.
11106 proc descheads {id} {
11107 global arcnos arcstart arcids archeads idheads cached_dheads
11108 global allparents arcout
11110 if {![info exists allparents($id)]} {
11114 if {![info exists arcout($id)]} {
11115 # part-way along an arc; check it first
11116 set a [lindex $arcnos($id) 0]
11117 if {$archeads($a) ne {}} {
11118 validate_archeads $a
11119 set i [lsearch -exact $arcids($a) $id]
11120 foreach t $archeads($a) {
11121 set j [lsearch -exact $arcids($a) $t]
11126 set id $arcstart($a)
11129 set todo [list $id]
11132 for {set i 0} {$i < [llength $todo]} {incr i} {
11133 set id [lindex $todo $i]
11134 if {[info exists cached_dheads($id)]} {
11135 set ret [concat $ret $cached_dheads($id)]
11137 if {[info exists idheads($id)]} {
11140 foreach a $arcnos($id) {
11141 if {$archeads($a) ne {}} {
11142 validate_archeads $a
11143 if {$archeads($a) ne {}} {
11144 set ret [concat $ret $archeads($a)]
11147 set d $arcstart($a)
11148 if {![info exists seen($d)]} {
11155 set ret [lsort -unique $ret]
11156 set cached_dheads($origid) $ret
11157 return [concat $ret $aret]
11160 proc addedtag {id} {
11161 global arcnos arcout cached_dtags cached_atags
11163 if {![info exists arcnos($id)]} return
11164 if {![info exists arcout($id)]} {
11165 recalcarc [lindex $arcnos($id) 0]
11167 unset -nocomplain cached_dtags
11168 unset -nocomplain cached_atags
11171 proc addedhead {hid head} {
11172 global arcnos arcout cached_dheads
11174 if {![info exists arcnos($hid)]} return
11175 if {![info exists arcout($hid)]} {
11176 recalcarc [lindex $arcnos($hid) 0]
11178 unset -nocomplain cached_dheads
11181 proc removedhead {hid head} {
11182 global cached_dheads
11184 unset -nocomplain cached_dheads
11187 proc movedhead {hid head} {
11188 global arcnos arcout cached_dheads
11190 if {![info exists arcnos($hid)]} return
11191 if {![info exists arcout($hid)]} {
11192 recalcarc [lindex $arcnos($hid) 0]
11194 unset -nocomplain cached_dheads
11197 proc changedrefs {} {
11198 global cached_dheads cached_dtags cached_atags cached_tagcontent
11199 global arctags archeads arcnos arcout idheads idtags
11201 foreach id [concat [array names idheads] [array names idtags]] {
11202 if {[info exists arcnos($id)] && ![info exists arcout($id)]} {
11203 set a [lindex $arcnos($id) 0]
11204 if {![info exists donearc($a)]} {
11210 unset -nocomplain cached_tagcontent
11211 unset -nocomplain cached_dtags
11212 unset -nocomplain cached_atags
11213 unset -nocomplain cached_dheads
11216 proc rereadrefs {} {
11217 global idtags idheads idotherrefs mainheadid
11219 set refids [concat [array names idtags] \
11220 [array names idheads] [array names idotherrefs]]
11221 foreach id $refids {
11222 if {![info exists ref($id)]} {
11223 set ref($id) [listrefs $id]
11226 set oldmainhead $mainheadid
11229 set refids [lsort -unique [concat $refids [array names idtags] \
11230 [array names idheads] [array names idotherrefs]]]
11231 foreach id $refids {
11232 set v [listrefs $id]
11233 if {![info exists ref($id)] || $ref($id) != $v} {
11237 if {$oldmainhead ne $mainheadid} {
11238 redrawtags $oldmainhead
11239 redrawtags $mainheadid
11244 proc listrefs {id} {
11245 global idtags idheads idotherrefs
11248 if {[info exists idtags($id)]} {
11252 if {[info exists idheads($id)]} {
11253 set y $idheads($id)
11256 if {[info exists idotherrefs($id)]} {
11257 set z $idotherrefs($id)
11259 return [list $x $y $z]
11262 proc add_tag_ctext {tag} {
11263 global ctext cached_tagcontent tagids
11265 if {![info exists cached_tagcontent($tag)]} {
11267 set cached_tagcontent($tag) [exec git cat-file -p $tag]
11270 $ctext insert end "[mc "Tag"]: $tag\n" bold
11271 if {[info exists cached_tagcontent($tag)]} {
11272 set text $cached_tagcontent($tag)
11274 set text "[mc "Id"]: $tagids($tag)"
11276 appendwithlinks $text {}
11279 proc showtag {tag isnew} {
11280 global ctext cached_tagcontent tagids linknum tagobjid
11283 addtohistory [list showtag $tag 0] savectextpos
11285 $ctext conf -state normal
11290 maybe_scroll_ctext 1
11291 $ctext conf -state disabled
11295 proc showtags {id isnew} {
11296 global idtags ctext linknum
11299 addtohistory [list showtags $id 0] savectextpos
11301 $ctext conf -state normal
11306 foreach tag $idtags($id) {
11307 $ctext insert end $sep
11311 maybe_scroll_ctext 1
11312 $ctext conf -state disabled
11324 if {[info exists gitktmpdir]} {
11325 catch {file delete -force $gitktmpdir}
11329 proc mkfontdisp {font top which} {
11330 global fontattr fontpref $font NS use_ttk
11332 set fontpref($font) [set $font]
11333 ${NS}::button $top.${font}but -text $which \
11334 -command [list choosefont $font $which]
11335 ${NS}::label $top.$font -relief flat -font $font \
11336 -text $fontattr($font,family) -justify left
11337 grid x $top.${font}but $top.$font -sticky w
11340 proc choosefont {font which} {
11341 global fontparam fontlist fonttop fontattr
11344 set fontparam(which) $which
11345 set fontparam(font) $font
11346 set fontparam(family) [font actual $font -family]
11347 set fontparam(size) $fontattr($font,size)
11348 set fontparam(weight) $fontattr($font,weight)
11349 set fontparam(slant) $fontattr($font,slant)
11352 if {![winfo exists $top]} {
11354 eval font config sample [font actual $font]
11356 make_transient $top $prefstop
11357 wm title $top [mc "Gitk font chooser"]
11358 ${NS}::label $top.l -textvariable fontparam(which)
11359 pack $top.l -side top
11360 set fontlist [lsort [font families]]
11361 ${NS}::frame $top.f
11362 listbox $top.f.fam -listvariable fontlist \
11363 -yscrollcommand [list $top.f.sb set]
11364 bind $top.f.fam <<ListboxSelect>> selfontfam
11365 ${NS}::scrollbar $top.f.sb -command [list $top.f.fam yview]
11366 pack $top.f.sb -side right -fill y
11367 pack $top.f.fam -side left -fill both -expand 1
11368 pack $top.f -side top -fill both -expand 1
11369 ${NS}::frame $top.g
11370 spinbox $top.g.size -from 4 -to 40 -width 4 \
11371 -textvariable fontparam(size) \
11372 -validatecommand {string is integer -strict %s}
11373 checkbutton $top.g.bold -padx 5 \
11374 -font {{Times New Roman} 12 bold} -text [mc "B"] -indicatoron 0 \
11375 -variable fontparam(weight) -onvalue bold -offvalue normal
11376 checkbutton $top.g.ital -padx 5 \
11377 -font {{Times New Roman} 12 italic} -text [mc "I"] -indicatoron 0 \
11378 -variable fontparam(slant) -onvalue italic -offvalue roman
11379 pack $top.g.size $top.g.bold $top.g.ital -side left
11380 pack $top.g -side top
11381 canvas $top.c -width 150 -height 50 -border 2 -relief sunk \
11383 $top.c create text 100 25 -anchor center -text $which -font sample \
11384 -fill black -tags text
11385 bind $top.c <Configure> [list centertext $top.c]
11386 pack $top.c -side top -fill x
11387 ${NS}::frame $top.buts
11388 ${NS}::button $top.buts.ok -text [mc "OK"] -command fontok -default active
11389 ${NS}::button $top.buts.can -text [mc "Cancel"] -command fontcan -default normal
11390 bind $top <Key-Return> fontok
11391 bind $top <Key-Escape> fontcan
11392 grid $top.buts.ok $top.buts.can
11393 grid columnconfigure $top.buts 0 -weight 1 -uniform a
11394 grid columnconfigure $top.buts 1 -weight 1 -uniform a
11395 pack $top.buts -side bottom -fill x
11396 trace add variable fontparam write chg_fontparam
11399 $top.c itemconf text -text $which
11401 set i [lsearch -exact $fontlist $fontparam(family)]
11403 $top.f.fam selection set $i
11408 proc centertext {w} {
11409 $w coords text [expr {[winfo width $w] / 2}] [expr {[winfo height $w] / 2}]
11413 global fontparam fontpref prefstop
11415 set f $fontparam(font)
11416 set fontpref($f) [list $fontparam(family) $fontparam(size)]
11417 if {$fontparam(weight) eq "bold"} {
11418 lappend fontpref($f) "bold"
11420 if {$fontparam(slant) eq "italic"} {
11421 lappend fontpref($f) "italic"
11423 set w $prefstop.notebook.fonts.$f
11424 $w conf -text $fontparam(family) -font $fontpref($f)
11430 global fonttop fontparam
11432 if {[info exists fonttop]} {
11433 catch {destroy $fonttop}
11434 catch {font delete sample}
11440 if {[package vsatisfies [package provide Tk] 8.6]} {
11441 # In Tk 8.6 we have a native font chooser dialog. Overwrite the above
11442 # function to make use of it.
11443 proc choosefont {font which} {
11444 tk fontchooser configure -title $which -font $font \
11445 -command [list on_choosefont $font $which]
11446 tk fontchooser show
11448 proc on_choosefont {font which newfont} {
11450 puts stderr "$font $newfont"
11451 array set f [font actual $newfont]
11452 set fontparam(which) $which
11453 set fontparam(font) $font
11454 set fontparam(family) $f(-family)
11455 set fontparam(size) $f(-size)
11456 set fontparam(weight) $f(-weight)
11457 set fontparam(slant) $f(-slant)
11462 proc selfontfam {} {
11463 global fonttop fontparam
11465 set i [$fonttop.f.fam curselection]
11467 set fontparam(family) [$fonttop.f.fam get $i]
11471 proc chg_fontparam {v sub op} {
11474 font config sample -$sub $fontparam($sub)
11477 # Create a property sheet tab page
11478 proc create_prefs_page {w} {
11480 set parent [join [lrange [split $w .] 0 end-1] .]
11481 if {[winfo class $parent] eq "TNotebook"} {
11484 ${NS}::labelframe $w
11488 proc prefspage_general {notebook} {
11489 global NS maxwidth maxgraphpct showneartags showlocalchanges
11490 global tabstop limitdiffs autoselect autosellen extdifftool perfile_attrs
11491 global hideremotes want_ttk have_ttk maxrefs
11493 set page [create_prefs_page $notebook.general]
11495 ${NS}::label $page.ldisp -text [mc "Commit list display options"]
11496 grid $page.ldisp - -sticky w -pady 10
11497 ${NS}::label $page.spacer -text " "
11498 ${NS}::label $page.maxwidthl -text [mc "Maximum graph width (lines)"]
11499 spinbox $page.maxwidth -from 0 -to 100 -width 4 -textvariable maxwidth
11500 grid $page.spacer $page.maxwidthl $page.maxwidth -sticky w
11501 #xgettext:no-tcl-format
11502 ${NS}::label $page.maxpctl -text [mc "Maximum graph width (% of pane)"]
11503 spinbox $page.maxpct -from 1 -to 100 -width 4 -textvariable maxgraphpct
11504 grid x $page.maxpctl $page.maxpct -sticky w
11505 ${NS}::checkbutton $page.showlocal -text [mc "Show local changes"] \
11506 -variable showlocalchanges
11507 grid x $page.showlocal -sticky w
11508 ${NS}::checkbutton $page.autoselect -text [mc "Auto-select SHA1 (length)"] \
11509 -variable autoselect
11510 spinbox $page.autosellen -from 1 -to 40 -width 4 -textvariable autosellen
11511 grid x $page.autoselect $page.autosellen -sticky w
11512 ${NS}::checkbutton $page.hideremotes -text [mc "Hide remote refs"] \
11513 -variable hideremotes
11514 grid x $page.hideremotes -sticky w
11516 ${NS}::label $page.ddisp -text [mc "Diff display options"]
11517 grid $page.ddisp - -sticky w -pady 10
11518 ${NS}::label $page.tabstopl -text [mc "Tab spacing"]
11519 spinbox $page.tabstop -from 1 -to 20 -width 4 -textvariable tabstop
11520 grid x $page.tabstopl $page.tabstop -sticky w
11521 ${NS}::checkbutton $page.ntag -text [mc "Display nearby tags/heads"] \
11522 -variable showneartags
11523 grid x $page.ntag -sticky w
11524 ${NS}::label $page.maxrefsl -text [mc "Maximum # tags/heads to show"]
11525 spinbox $page.maxrefs -from 1 -to 1000 -width 4 -textvariable maxrefs
11526 grid x $page.maxrefsl $page.maxrefs -sticky w
11527 ${NS}::checkbutton $page.ldiff -text [mc "Limit diffs to listed paths"] \
11528 -variable limitdiffs
11529 grid x $page.ldiff -sticky w
11530 ${NS}::checkbutton $page.lattr -text [mc "Support per-file encodings"] \
11531 -variable perfile_attrs
11532 grid x $page.lattr -sticky w
11534 ${NS}::entry $page.extdifft -textvariable extdifftool
11535 ${NS}::frame $page.extdifff
11536 ${NS}::label $page.extdifff.l -text [mc "External diff tool" ]
11537 ${NS}::button $page.extdifff.b -text [mc "Choose..."] -command choose_extdiff
11538 pack $page.extdifff.l $page.extdifff.b -side left
11539 pack configure $page.extdifff.l -padx 10
11540 grid x $page.extdifff $page.extdifft -sticky ew
11542 ${NS}::label $page.lgen -text [mc "General options"]
11543 grid $page.lgen - -sticky w -pady 10
11544 ${NS}::checkbutton $page.want_ttk -variable want_ttk \
11545 -text [mc "Use themed widgets"]
11547 ${NS}::label $page.ttk_note -text [mc "(change requires restart)"]
11549 ${NS}::label $page.ttk_note -text [mc "(currently unavailable)"]
11551 grid x $page.want_ttk $page.ttk_note -sticky w
11555 proc prefspage_colors {notebook} {
11556 global NS uicolor bgcolor fgcolor ctext diffcolors selectbgcolor markbgcolor
11558 set page [create_prefs_page $notebook.colors]
11560 ${NS}::label $page.cdisp -text [mc "Colors: press to choose"]
11561 grid $page.cdisp - -sticky w -pady 10
11562 label $page.ui -padx 40 -relief sunk -background $uicolor
11563 ${NS}::button $page.uibut -text [mc "Interface"] \
11564 -command [list choosecolor uicolor {} $page.ui [mc "interface"] setui]
11565 grid x $page.uibut $page.ui -sticky w
11566 label $page.bg -padx 40 -relief sunk -background $bgcolor
11567 ${NS}::button $page.bgbut -text [mc "Background"] \
11568 -command [list choosecolor bgcolor {} $page.bg [mc "background"] setbg]
11569 grid x $page.bgbut $page.bg -sticky w
11570 label $page.fg -padx 40 -relief sunk -background $fgcolor
11571 ${NS}::button $page.fgbut -text [mc "Foreground"] \
11572 -command [list choosecolor fgcolor {} $page.fg [mc "foreground"] setfg]
11573 grid x $page.fgbut $page.fg -sticky w
11574 label $page.diffold -padx 40 -relief sunk -background [lindex $diffcolors 0]
11575 ${NS}::button $page.diffoldbut -text [mc "Diff: old lines"] \
11576 -command [list choosecolor diffcolors 0 $page.diffold [mc "diff old lines"] \
11577 [list $ctext tag conf d0 -foreground]]
11578 grid x $page.diffoldbut $page.diffold -sticky w
11579 label $page.diffnew -padx 40 -relief sunk -background [lindex $diffcolors 1]
11580 ${NS}::button $page.diffnewbut -text [mc "Diff: new lines"] \
11581 -command [list choosecolor diffcolors 1 $page.diffnew [mc "diff new lines"] \
11582 [list $ctext tag conf dresult -foreground]]
11583 grid x $page.diffnewbut $page.diffnew -sticky w
11584 label $page.hunksep -padx 40 -relief sunk -background [lindex $diffcolors 2]
11585 ${NS}::button $page.hunksepbut -text [mc "Diff: hunk header"] \
11586 -command [list choosecolor diffcolors 2 $page.hunksep \
11587 [mc "diff hunk header"] \
11588 [list $ctext tag conf hunksep -foreground]]
11589 grid x $page.hunksepbut $page.hunksep -sticky w
11590 label $page.markbgsep -padx 40 -relief sunk -background $markbgcolor
11591 ${NS}::button $page.markbgbut -text [mc "Marked line bg"] \
11592 -command [list choosecolor markbgcolor {} $page.markbgsep \
11593 [mc "marked line background"] \
11594 [list $ctext tag conf omark -background]]
11595 grid x $page.markbgbut $page.markbgsep -sticky w
11596 label $page.selbgsep -padx 40 -relief sunk -background $selectbgcolor
11597 ${NS}::button $page.selbgbut -text [mc "Select bg"] \
11598 -command [list choosecolor selectbgcolor {} $page.selbgsep [mc "background"] setselbg]
11599 grid x $page.selbgbut $page.selbgsep -sticky w
11603 proc prefspage_fonts {notebook} {
11605 set page [create_prefs_page $notebook.fonts]
11606 ${NS}::label $page.cfont -text [mc "Fonts: press to choose"]
11607 grid $page.cfont - -sticky w -pady 10
11608 mkfontdisp mainfont $page [mc "Main font"]
11609 mkfontdisp textfont $page [mc "Diff display font"]
11610 mkfontdisp uifont $page [mc "User interface font"]
11615 global maxwidth maxgraphpct use_ttk NS
11616 global oldprefs prefstop showneartags showlocalchanges
11617 global uicolor bgcolor fgcolor ctext diffcolors selectbgcolor markbgcolor
11618 global tabstop limitdiffs autoselect autosellen extdifftool perfile_attrs
11619 global hideremotes want_ttk have_ttk
11623 if {[winfo exists $top]} {
11627 foreach v {maxwidth maxgraphpct showneartags showlocalchanges \
11628 limitdiffs tabstop perfile_attrs hideremotes want_ttk} {
11629 set oldprefs($v) [set $v]
11632 wm title $top [mc "Gitk preferences"]
11633 make_transient $top .
11635 if {[set use_notebook [expr {$use_ttk && [info command ::ttk::notebook] ne ""}]]} {
11636 set notebook [ttk::notebook $top.notebook]
11638 set notebook [${NS}::frame $top.notebook -borderwidth 0 -relief flat]
11641 lappend pages [prefspage_general $notebook] [mc "General"]
11642 lappend pages [prefspage_colors $notebook] [mc "Colors"]
11643 lappend pages [prefspage_fonts $notebook] [mc "Fonts"]
11645 foreach {page title} $pages {
11646 if {$use_notebook} {
11647 $notebook add $page -text $title
11649 set btn [${NS}::button $notebook.b_[string map {. X} $page] \
11650 -text $title -command [list raise $page]]
11651 $page configure -text $title
11652 grid $btn -row 0 -column [incr col] -sticky w
11653 grid $page -row 1 -column 0 -sticky news -columnspan 100
11657 if {!$use_notebook} {
11658 grid columnconfigure $notebook 0 -weight 1
11659 grid rowconfigure $notebook 1 -weight 1
11660 raise [lindex $pages 0]
11663 grid $notebook -sticky news -padx 2 -pady 2
11664 grid rowconfigure $top 0 -weight 1
11665 grid columnconfigure $top 0 -weight 1
11667 ${NS}::frame $top.buts
11668 ${NS}::button $top.buts.ok -text [mc "OK"] -command prefsok -default active
11669 ${NS}::button $top.buts.can -text [mc "Cancel"] -command prefscan -default normal
11670 bind $top <Key-Return> prefsok
11671 bind $top <Key-Escape> prefscan
11672 grid $top.buts.ok $top.buts.can
11673 grid columnconfigure $top.buts 0 -weight 1 -uniform a
11674 grid columnconfigure $top.buts 1 -weight 1 -uniform a
11675 grid $top.buts - - -pady 10 -sticky ew
11676 grid columnconfigure $top 2 -weight 1
11677 bind $top <Visibility> [list focus $top.buts.ok]
11680 proc choose_extdiff {} {
11683 set prog [tk_getOpenFile -title [mc "External diff tool"] -multiple false]
11685 set extdifftool $prog
11689 proc choosecolor {v vi w x cmd} {
11692 set c [tk_chooseColor -initialcolor [lindex [set $v] $vi] \
11693 -title [mc "Gitk: choose color for %s" $x]]
11694 if {$c eq {}} return
11695 $w conf -background $c
11700 proc setselbg {c} {
11701 global bglist cflist
11702 foreach w $bglist {
11703 if {[winfo exists $w]} {
11704 $w configure -selectbackground $c
11707 $cflist tag configure highlight \
11708 -background [$cflist cget -selectbackground]
11709 allcanvs itemconf secsel -fill $c
11712 # This sets the background color and the color scheme for the whole UI.
11713 # For some reason, tk_setPalette chooses a nasty dark red for selectColor
11714 # if we don't specify one ourselves, which makes the checkbuttons and
11715 # radiobuttons look bad. This chooses white for selectColor if the
11716 # background color is light, or black if it is dark.
11718 if {[tk windowingsystem] eq "win32"} { return }
11719 set bg [winfo rgb . $c]
11721 if {[lindex $bg 0] + 1.5 * [lindex $bg 1] + 0.5 * [lindex $bg 2] > 100000} {
11724 tk_setPalette background $c selectColor $selc
11730 foreach w $bglist {
11731 if {[winfo exists $w]} {
11732 $w conf -background $c
11740 foreach w $fglist {
11741 if {[winfo exists $w]} {
11742 $w conf -foreground $c
11745 allcanvs itemconf text -fill $c
11746 $canv itemconf circle -outline $c
11747 $canv itemconf markid -outline $c
11751 global oldprefs prefstop
11753 foreach v {maxwidth maxgraphpct showneartags showlocalchanges \
11754 limitdiffs tabstop perfile_attrs hideremotes want_ttk} {
11756 set $v $oldprefs($v)
11758 catch {destroy $prefstop}
11764 global maxwidth maxgraphpct
11765 global oldprefs prefstop showneartags showlocalchanges
11766 global fontpref mainfont textfont uifont
11767 global limitdiffs treediffs perfile_attrs
11770 catch {destroy $prefstop}
11774 if {$mainfont ne $fontpref(mainfont)} {
11775 set mainfont $fontpref(mainfont)
11776 parsefont mainfont $mainfont
11777 eval font configure mainfont [fontflags mainfont]
11778 eval font configure mainfontbold [fontflags mainfont 1]
11782 if {$textfont ne $fontpref(textfont)} {
11783 set textfont $fontpref(textfont)
11784 parsefont textfont $textfont
11785 eval font configure textfont [fontflags textfont]
11786 eval font configure textfontbold [fontflags textfont 1]
11788 if {$uifont ne $fontpref(uifont)} {
11789 set uifont $fontpref(uifont)
11790 parsefont uifont $uifont
11791 eval font configure uifont [fontflags uifont]
11794 if {$showlocalchanges != $oldprefs(showlocalchanges)} {
11795 if {$showlocalchanges} {
11801 if {$limitdiffs != $oldprefs(limitdiffs) ||
11802 ($perfile_attrs && !$oldprefs(perfile_attrs))} {
11803 # treediffs elements are limited by path;
11804 # won't have encodings cached if perfile_attrs was just turned on
11805 unset -nocomplain treediffs
11807 if {$fontchanged || $maxwidth != $oldprefs(maxwidth)
11808 || $maxgraphpct != $oldprefs(maxgraphpct)} {
11810 } elseif {$showneartags != $oldprefs(showneartags) ||
11811 $limitdiffs != $oldprefs(limitdiffs)} {
11814 if {$hideremotes != $oldprefs(hideremotes)} {
11819 proc formatdate {d} {
11820 global datetimeformat
11822 # If $datetimeformat includes a timezone, display in the
11823 # timezone of the argument. Otherwise, display in local time.
11824 if {[string match {*%[zZ]*} $datetimeformat]} {
11825 if {[catch {set d [clock format [lindex $d 0] -timezone [lindex $d 1] -format $datetimeformat]}]} {
11826 # Tcl < 8.5 does not support -timezone. Emulate it by
11827 # setting TZ (e.g. TZ=<-0430>+04:30).
11829 if {[info exists env(TZ)]} {
11830 set savedTZ $env(TZ)
11832 set zone [lindex $d 1]
11833 set sign [string map {+ - - +} [string index $zone 0]]
11834 set env(TZ) <$zone>$sign[string range $zone 1 2]:[string range $zone 3 4]
11835 set d [clock format [lindex $d 0] -format $datetimeformat]
11836 if {[info exists savedTZ]} {
11837 set env(TZ) $savedTZ
11843 set d [clock format [lindex $d 0] -format $datetimeformat]
11849 # This list of encoding names and aliases is distilled from
11850 # http://www.iana.org/assignments/character-sets.
11851 # Not all of them are supported by Tcl.
11852 set encoding_aliases {
11853 { ANSI_X3.4-1968 iso-ir-6 ANSI_X3.4-1986 ISO_646.irv:1991 ASCII
11854 ISO646-US US-ASCII us IBM367 cp367 csASCII }
11855 { ISO-10646-UTF-1 csISO10646UTF1 }
11856 { ISO_646.basic:1983 ref csISO646basic1983 }
11857 { INVARIANT csINVARIANT }
11858 { ISO_646.irv:1983 iso-ir-2 irv csISO2IntlRefVersion }
11859 { BS_4730 iso-ir-4 ISO646-GB gb uk csISO4UnitedKingdom }
11860 { NATS-SEFI iso-ir-8-1 csNATSSEFI }
11861 { NATS-SEFI-ADD iso-ir-8-2 csNATSSEFIADD }
11862 { NATS-DANO iso-ir-9-1 csNATSDANO }
11863 { NATS-DANO-ADD iso-ir-9-2 csNATSDANOADD }
11864 { SEN_850200_B iso-ir-10 FI ISO646-FI ISO646-SE se csISO10Swedish }
11865 { SEN_850200_C iso-ir-11 ISO646-SE2 se2 csISO11SwedishForNames }
11866 { KS_C_5601-1987 iso-ir-149 KS_C_5601-1989 KSC_5601 korean csKSC56011987 }
11867 { ISO-2022-KR csISO2022KR }
11869 { ISO-2022-JP csISO2022JP }
11870 { ISO-2022-JP-2 csISO2022JP2 }
11871 { JIS_C6220-1969-jp JIS_C6220-1969 iso-ir-13 katakana x0201-7
11872 csISO13JISC6220jp }
11873 { JIS_C6220-1969-ro iso-ir-14 jp ISO646-JP csISO14JISC6220ro }
11874 { IT iso-ir-15 ISO646-IT csISO15Italian }
11875 { PT iso-ir-16 ISO646-PT csISO16Portuguese }
11876 { ES iso-ir-17 ISO646-ES csISO17Spanish }
11877 { greek7-old iso-ir-18 csISO18Greek7Old }
11878 { latin-greek iso-ir-19 csISO19LatinGreek }
11879 { DIN_66003 iso-ir-21 de ISO646-DE csISO21German }
11880 { NF_Z_62-010_(1973) iso-ir-25 ISO646-FR1 csISO25French }
11881 { Latin-greek-1 iso-ir-27 csISO27LatinGreek1 }
11882 { ISO_5427 iso-ir-37 csISO5427Cyrillic }
11883 { JIS_C6226-1978 iso-ir-42 csISO42JISC62261978 }
11884 { BS_viewdata iso-ir-47 csISO47BSViewdata }
11885 { INIS iso-ir-49 csISO49INIS }
11886 { INIS-8 iso-ir-50 csISO50INIS8 }
11887 { INIS-cyrillic iso-ir-51 csISO51INISCyrillic }
11888 { ISO_5427:1981 iso-ir-54 ISO5427Cyrillic1981 }
11889 { ISO_5428:1980 iso-ir-55 csISO5428Greek }
11890 { GB_1988-80 iso-ir-57 cn ISO646-CN csISO57GB1988 }
11891 { GB_2312-80 iso-ir-58 chinese csISO58GB231280 }
11892 { NS_4551-1 iso-ir-60 ISO646-NO no csISO60DanishNorwegian
11893 csISO60Norwegian1 }
11894 { NS_4551-2 ISO646-NO2 iso-ir-61 no2 csISO61Norwegian2 }
11895 { NF_Z_62-010 iso-ir-69 ISO646-FR fr csISO69French }
11896 { videotex-suppl iso-ir-70 csISO70VideotexSupp1 }
11897 { PT2 iso-ir-84 ISO646-PT2 csISO84Portuguese2 }
11898 { ES2 iso-ir-85 ISO646-ES2 csISO85Spanish2 }
11899 { MSZ_7795.3 iso-ir-86 ISO646-HU hu csISO86Hungarian }
11900 { JIS_C6226-1983 iso-ir-87 x0208 JIS_X0208-1983 csISO87JISX0208 }
11901 { greek7 iso-ir-88 csISO88Greek7 }
11902 { ASMO_449 ISO_9036 arabic7 iso-ir-89 csISO89ASMO449 }
11903 { iso-ir-90 csISO90 }
11904 { JIS_C6229-1984-a iso-ir-91 jp-ocr-a csISO91JISC62291984a }
11905 { JIS_C6229-1984-b iso-ir-92 ISO646-JP-OCR-B jp-ocr-b
11906 csISO92JISC62991984b }
11907 { JIS_C6229-1984-b-add iso-ir-93 jp-ocr-b-add csISO93JIS62291984badd }
11908 { JIS_C6229-1984-hand iso-ir-94 jp-ocr-hand csISO94JIS62291984hand }
11909 { JIS_C6229-1984-hand-add iso-ir-95 jp-ocr-hand-add
11910 csISO95JIS62291984handadd }
11911 { JIS_C6229-1984-kana iso-ir-96 csISO96JISC62291984kana }
11912 { ISO_2033-1983 iso-ir-98 e13b csISO2033 }
11913 { ANSI_X3.110-1983 iso-ir-99 CSA_T500-1983 NAPLPS csISO99NAPLPS }
11914 { ISO_8859-1:1987 iso-ir-100 ISO_8859-1 ISO-8859-1 latin1 l1 IBM819
11915 CP819 csISOLatin1 }
11916 { ISO_8859-2:1987 iso-ir-101 ISO_8859-2 ISO-8859-2 latin2 l2 csISOLatin2 }
11917 { T.61-7bit iso-ir-102 csISO102T617bit }
11918 { T.61-8bit T.61 iso-ir-103 csISO103T618bit }
11919 { ISO_8859-3:1988 iso-ir-109 ISO_8859-3 ISO-8859-3 latin3 l3 csISOLatin3 }
11920 { ISO_8859-4:1988 iso-ir-110 ISO_8859-4 ISO-8859-4 latin4 l4 csISOLatin4 }
11921 { ECMA-cyrillic iso-ir-111 KOI8-E csISO111ECMACyrillic }
11922 { CSA_Z243.4-1985-1 iso-ir-121 ISO646-CA csa7-1 ca csISO121Canadian1 }
11923 { CSA_Z243.4-1985-2 iso-ir-122 ISO646-CA2 csa7-2 csISO122Canadian2 }
11924 { CSA_Z243.4-1985-gr iso-ir-123 csISO123CSAZ24341985gr }
11925 { ISO_8859-6:1987 iso-ir-127 ISO_8859-6 ISO-8859-6 ECMA-114 ASMO-708
11926 arabic csISOLatinArabic }
11927 { ISO_8859-6-E csISO88596E ISO-8859-6-E }
11928 { ISO_8859-6-I csISO88596I ISO-8859-6-I }
11929 { ISO_8859-7:1987 iso-ir-126 ISO_8859-7 ISO-8859-7 ELOT_928 ECMA-118
11930 greek greek8 csISOLatinGreek }
11931 { T.101-G2 iso-ir-128 csISO128T101G2 }
11932 { ISO_8859-8:1988 iso-ir-138 ISO_8859-8 ISO-8859-8 hebrew
11934 { ISO_8859-8-E csISO88598E ISO-8859-8-E }
11935 { ISO_8859-8-I csISO88598I ISO-8859-8-I }
11936 { CSN_369103 iso-ir-139 csISO139CSN369103 }
11937 { JUS_I.B1.002 iso-ir-141 ISO646-YU js yu csISO141JUSIB1002 }
11938 { ISO_6937-2-add iso-ir-142 csISOTextComm }
11939 { IEC_P27-1 iso-ir-143 csISO143IECP271 }
11940 { ISO_8859-5:1988 iso-ir-144 ISO_8859-5 ISO-8859-5 cyrillic
11941 csISOLatinCyrillic }
11942 { JUS_I.B1.003-serb iso-ir-146 serbian csISO146Serbian }
11943 { JUS_I.B1.003-mac macedonian iso-ir-147 csISO147Macedonian }
11944 { ISO_8859-9:1989 iso-ir-148 ISO_8859-9 ISO-8859-9 latin5 l5 csISOLatin5 }
11945 { greek-ccitt iso-ir-150 csISO150 csISO150GreekCCITT }
11946 { NC_NC00-10:81 cuba iso-ir-151 ISO646-CU csISO151Cuba }
11947 { ISO_6937-2-25 iso-ir-152 csISO6937Add }
11948 { GOST_19768-74 ST_SEV_358-88 iso-ir-153 csISO153GOST1976874 }
11949 { ISO_8859-supp iso-ir-154 latin1-2-5 csISO8859Supp }
11950 { ISO_10367-box iso-ir-155 csISO10367Box }
11951 { ISO-8859-10 iso-ir-157 l6 ISO_8859-10:1992 csISOLatin6 latin6 }
11952 { latin-lap lap iso-ir-158 csISO158Lap }
11953 { JIS_X0212-1990 x0212 iso-ir-159 csISO159JISX02121990 }
11954 { DS_2089 DS2089 ISO646-DK dk csISO646Danish }
11957 { JIS_X0201 X0201 csHalfWidthKatakana }
11958 { KSC5636 ISO646-KR csKSC5636 }
11959 { ISO-10646-UCS-2 csUnicode }
11960 { ISO-10646-UCS-4 csUCS4 }
11961 { DEC-MCS dec csDECMCS }
11962 { hp-roman8 roman8 r8 csHPRoman8 }
11963 { macintosh mac csMacintosh }
11964 { IBM037 cp037 ebcdic-cp-us ebcdic-cp-ca ebcdic-cp-wt ebcdic-cp-nl
11966 { IBM038 EBCDIC-INT cp038 csIBM038 }
11967 { IBM273 CP273 csIBM273 }
11968 { IBM274 EBCDIC-BE CP274 csIBM274 }
11969 { IBM275 EBCDIC-BR cp275 csIBM275 }
11970 { IBM277 EBCDIC-CP-DK EBCDIC-CP-NO csIBM277 }
11971 { IBM278 CP278 ebcdic-cp-fi ebcdic-cp-se csIBM278 }
11972 { IBM280 CP280 ebcdic-cp-it csIBM280 }
11973 { IBM281 EBCDIC-JP-E cp281 csIBM281 }
11974 { IBM284 CP284 ebcdic-cp-es csIBM284 }
11975 { IBM285 CP285 ebcdic-cp-gb csIBM285 }
11976 { IBM290 cp290 EBCDIC-JP-kana csIBM290 }
11977 { IBM297 cp297 ebcdic-cp-fr csIBM297 }
11978 { IBM420 cp420 ebcdic-cp-ar1 csIBM420 }
11979 { IBM423 cp423 ebcdic-cp-gr csIBM423 }
11980 { IBM424 cp424 ebcdic-cp-he csIBM424 }
11981 { IBM437 cp437 437 csPC8CodePage437 }
11982 { IBM500 CP500 ebcdic-cp-be ebcdic-cp-ch csIBM500 }
11983 { IBM775 cp775 csPC775Baltic }
11984 { IBM850 cp850 850 csPC850Multilingual }
11985 { IBM851 cp851 851 csIBM851 }
11986 { IBM852 cp852 852 csPCp852 }
11987 { IBM855 cp855 855 csIBM855 }
11988 { IBM857 cp857 857 csIBM857 }
11989 { IBM860 cp860 860 csIBM860 }
11990 { IBM861 cp861 861 cp-is csIBM861 }
11991 { IBM862 cp862 862 csPC862LatinHebrew }
11992 { IBM863 cp863 863 csIBM863 }
11993 { IBM864 cp864 csIBM864 }
11994 { IBM865 cp865 865 csIBM865 }
11995 { IBM866 cp866 866 csIBM866 }
11996 { IBM868 CP868 cp-ar csIBM868 }
11997 { IBM869 cp869 869 cp-gr csIBM869 }
11998 { IBM870 CP870 ebcdic-cp-roece ebcdic-cp-yu csIBM870 }
11999 { IBM871 CP871 ebcdic-cp-is csIBM871 }
12000 { IBM880 cp880 EBCDIC-Cyrillic csIBM880 }
12001 { IBM891 cp891 csIBM891 }
12002 { IBM903 cp903 csIBM903 }
12003 { IBM904 cp904 904 csIBBM904 }
12004 { IBM905 CP905 ebcdic-cp-tr csIBM905 }
12005 { IBM918 CP918 ebcdic-cp-ar2 csIBM918 }
12006 { IBM1026 CP1026 csIBM1026 }
12007 { EBCDIC-AT-DE csIBMEBCDICATDE }
12008 { EBCDIC-AT-DE-A csEBCDICATDEA }
12009 { EBCDIC-CA-FR csEBCDICCAFR }
12010 { EBCDIC-DK-NO csEBCDICDKNO }
12011 { EBCDIC-DK-NO-A csEBCDICDKNOA }
12012 { EBCDIC-FI-SE csEBCDICFISE }
12013 { EBCDIC-FI-SE-A csEBCDICFISEA }
12014 { EBCDIC-FR csEBCDICFR }
12015 { EBCDIC-IT csEBCDICIT }
12016 { EBCDIC-PT csEBCDICPT }
12017 { EBCDIC-ES csEBCDICES }
12018 { EBCDIC-ES-A csEBCDICESA }
12019 { EBCDIC-ES-S csEBCDICESS }
12020 { EBCDIC-UK csEBCDICUK }
12021 { EBCDIC-US csEBCDICUS }
12022 { UNKNOWN-8BIT csUnknown8BiT }
12023 { MNEMONIC csMnemonic }
12025 { VISCII csVISCII }
12028 { IBM00858 CCSID00858 CP00858 PC-Multilingual-850+euro }
12029 { IBM00924 CCSID00924 CP00924 ebcdic-Latin9--euro }
12030 { IBM01140 CCSID01140 CP01140 ebcdic-us-37+euro }
12031 { IBM01141 CCSID01141 CP01141 ebcdic-de-273+euro }
12032 { IBM01142 CCSID01142 CP01142 ebcdic-dk-277+euro ebcdic-no-277+euro }
12033 { IBM01143 CCSID01143 CP01143 ebcdic-fi-278+euro ebcdic-se-278+euro }
12034 { IBM01144 CCSID01144 CP01144 ebcdic-it-280+euro }
12035 { IBM01145 CCSID01145 CP01145 ebcdic-es-284+euro }
12036 { IBM01146 CCSID01146 CP01146 ebcdic-gb-285+euro }
12037 { IBM01147 CCSID01147 CP01147 ebcdic-fr-297+euro }
12038 { IBM01148 CCSID01148 CP01148 ebcdic-international-500+euro }
12039 { IBM01149 CCSID01149 CP01149 ebcdic-is-871+euro }
12040 { IBM1047 IBM-1047 }
12041 { PTCP154 csPTCP154 PT154 CP154 Cyrillic-Asian }
12042 { Amiga-1251 Ami1251 Amiga1251 Ami-1251 }
12043 { UNICODE-1-1 csUnicode11 }
12044 { CESU-8 csCESU-8 }
12045 { BOCU-1 csBOCU-1 }
12046 { UNICODE-1-1-UTF-7 csUnicode11UTF7 }
12047 { ISO-8859-14 iso-ir-199 ISO_8859-14:1998 ISO_8859-14 latin8 iso-celtic
12049 { ISO-8859-15 ISO_8859-15 Latin-9 }
12050 { ISO-8859-16 iso-ir-226 ISO_8859-16:2001 ISO_8859-16 latin10 l10 }
12051 { GBK CP936 MS936 windows-936 }
12052 { JIS_Encoding csJISEncoding }
12053 { Shift_JIS MS_Kanji csShiftJIS ShiftJIS Shift-JIS }
12054 { Extended_UNIX_Code_Packed_Format_for_Japanese csEUCPkdFmtJapanese
12056 { Extended_UNIX_Code_Fixed_Width_for_Japanese csEUCFixWidJapanese }
12057 { ISO-10646-UCS-Basic csUnicodeASCII }
12058 { ISO-10646-Unicode-Latin1 csUnicodeLatin1 ISO-10646 }
12059 { ISO-Unicode-IBM-1261 csUnicodeIBM1261 }
12060 { ISO-Unicode-IBM-1268 csUnicodeIBM1268 }
12061 { ISO-Unicode-IBM-1276 csUnicodeIBM1276 }
12062 { ISO-Unicode-IBM-1264 csUnicodeIBM1264 }
12063 { ISO-Unicode-IBM-1265 csUnicodeIBM1265 }
12064 { ISO-8859-1-Windows-3.0-Latin-1 csWindows30Latin1 }
12065 { ISO-8859-1-Windows-3.1-Latin-1 csWindows31Latin1 }
12066 { ISO-8859-2-Windows-Latin-2 csWindows31Latin2 }
12067 { ISO-8859-9-Windows-Latin-5 csWindows31Latin5 }
12068 { Adobe-Standard-Encoding csAdobeStandardEncoding }
12069 { Ventura-US csVenturaUS }
12070 { Ventura-International csVenturaInternational }
12071 { PC8-Danish-Norwegian csPC8DanishNorwegian }
12072 { PC8-Turkish csPC8Turkish }
12073 { IBM-Symbols csIBMSymbols }
12074 { IBM-Thai csIBMThai }
12075 { HP-Legal csHPLegal }
12076 { HP-Pi-font csHPPiFont }
12077 { HP-Math8 csHPMath8 }
12078 { Adobe-Symbol-Encoding csHPPSMath }
12079 { HP-DeskTop csHPDesktop }
12080 { Ventura-Math csVenturaMath }
12081 { Microsoft-Publishing csMicrosoftPublishing }
12082 { Windows-31J csWindows31J }
12083 { GB2312 csGB2312 }
12087 proc tcl_encoding {enc} {
12088 global encoding_aliases tcl_encoding_cache
12089 if {[info exists tcl_encoding_cache($enc)]} {
12090 return $tcl_encoding_cache($enc)
12092 set names [encoding names]
12093 set lcnames [string tolower $names]
12094 set enc [string tolower $enc]
12095 set i [lsearch -exact $lcnames $enc]
12097 # look for "isonnn" instead of "iso-nnn" or "iso_nnn"
12098 if {[regsub {^(iso|cp|ibm|jis)[-_]} $enc {\1} encx]} {
12099 set i [lsearch -exact $lcnames $encx]
12103 foreach l $encoding_aliases {
12104 set ll [string tolower $l]
12105 if {[lsearch -exact $ll $enc] < 0} continue
12106 # look through the aliases for one that tcl knows about
12108 set i [lsearch -exact $lcnames $e]
12110 if {[regsub {^(iso|cp|ibm|jis)[-_]} $e {\1} ex]} {
12111 set i [lsearch -exact $lcnames $ex]
12121 set tclenc [lindex $names $i]
12123 set tcl_encoding_cache($enc) $tclenc
12127 proc gitattr {path attr default} {
12128 global path_attr_cache
12129 if {[info exists path_attr_cache($attr,$path)]} {
12130 set r $path_attr_cache($attr,$path)
12132 set r "unspecified"
12133 if {![catch {set line [exec git check-attr $attr -- $path]}]} {
12134 regexp "(.*): $attr: (.*)" $line m f r
12136 set path_attr_cache($attr,$path) $r
12138 if {$r eq "unspecified"} {
12144 proc cache_gitattr {attr pathlist} {
12145 global path_attr_cache
12147 foreach path $pathlist {
12148 if {![info exists path_attr_cache($attr,$path)]} {
12149 lappend newlist $path
12153 if {[tk windowingsystem] == "win32"} {
12154 # windows has a 32k limit on the arguments to a command...
12157 while {$newlist ne {}} {
12158 set head [lrange $newlist 0 [expr {$lim - 1}]]
12159 set newlist [lrange $newlist $lim end]
12160 if {![catch {set rlist [eval exec git check-attr $attr -- $head]}]} {
12161 foreach row [split $rlist "\n"] {
12162 if {[regexp "(.*): $attr: (.*)" $row m path value]} {
12163 if {[string index $path 0] eq "\""} {
12164 set path [encoding convertfrom [lindex $path 0]]
12166 set path_attr_cache($attr,$path) $value
12173 proc get_path_encoding {path} {
12174 global gui_encoding perfile_attrs
12175 set tcl_enc $gui_encoding
12176 if {$path ne {} && $perfile_attrs} {
12177 set enc2 [tcl_encoding [gitattr $path encoding $tcl_enc]]
12185 ## For msgcat loading, first locate the installation location.
12186 if { [info exists ::env(GITK_MSGSDIR)] } {
12187 ## Msgsdir was manually set in the environment.
12188 set gitk_msgsdir $::env(GITK_MSGSDIR)
12190 ## Let's guess the prefix from argv0.
12191 set gitk_prefix [file dirname [file dirname [file normalize $argv0]]]
12192 set gitk_libdir [file join $gitk_prefix share gitk lib]
12193 set gitk_msgsdir [file join $gitk_libdir msgs]
12197 ## Internationalization (i18n) through msgcat and gettext. See
12198 ## http://www.gnu.org/software/gettext/manual/html_node/Tcl.html
12199 package require msgcat
12200 namespace import ::msgcat::mc
12201 ## And eventually load the actual message catalog
12202 ::msgcat::mcload $gitk_msgsdir
12204 # First check that Tcl/Tk is recent enough
12205 if {[catch {package require Tk 8.4} err]} {
12206 show_error {} . [mc "Sorry, gitk cannot run with this version of Tcl/Tk.\n\
12207 Gitk requires at least Tcl/Tk 8.4."]
12211 # on OSX bring the current Wish process window to front
12212 if {[tk windowingsystem] eq "aqua"} {
12213 exec osascript -e [format {
12214 tell application "System Events"
12215 set frontmost of processes whose unix id is %d to true
12220 # Unset GIT_TRACE var if set
12221 if { [info exists ::env(GIT_TRACE)] } {
12222 unset ::env(GIT_TRACE)
12226 set wrcomcmd "git diff-tree --stdin -p --pretty=email"
12230 set gitencoding [exec git config --get i18n.commitencoding]
12233 set gitencoding [exec git config --get i18n.logoutputencoding]
12235 if {$gitencoding == ""} {
12236 set gitencoding "utf-8"
12238 set tclencoding [tcl_encoding $gitencoding]
12239 if {$tclencoding == {}} {
12240 puts stderr "Warning: encoding $gitencoding is not supported by Tcl/Tk"
12243 set gui_encoding [encoding system]
12245 set enc [exec git config --get gui.encoding]
12247 set tclenc [tcl_encoding $enc]
12248 if {$tclenc ne {}} {
12249 set gui_encoding $tclenc
12251 puts stderr "Warning: encoding $enc is not supported by Tcl/Tk"
12256 set log_showroot true
12258 set log_showroot [exec git config --bool --get log.showroot]
12261 if {[tk windowingsystem] eq "aqua"} {
12262 set mainfont {{Lucida Grande} 9}
12263 set textfont {Monaco 9}
12264 set uifont {{Lucida Grande} 9 bold}
12265 } elseif {![catch {::tk::pkgconfig get fontsystem} xft] && $xft eq "xft"} {
12267 set mainfont {sans 9}
12268 set textfont {monospace 9}
12269 set uifont {sans 9 bold}
12271 set mainfont {Helvetica 9}
12272 set textfont {Courier 9}
12273 set uifont {Helvetica 9 bold}
12276 set findmergefiles 0
12284 set cmitmode "patch"
12285 set wrapcomment "none"
12289 set visiblerefs {"master"}
12291 set showlocalchanges 1
12293 set datetimeformat "%Y-%m-%d %H:%M:%S"
12296 set perfile_attrs 0
12299 if {[tk windowingsystem] eq "aqua"} {
12300 set extdifftool "opendiff"
12302 set extdifftool "meld"
12305 set colors {"#00ff00" red blue magenta darkgrey brown orange}
12306 if {[tk windowingsystem] eq "win32"} {
12307 set uicolor SystemButtonFace
12308 set uifgcolor SystemButtonText
12309 set uifgdisabledcolor SystemDisabledText
12310 set bgcolor SystemWindow
12311 set fgcolor SystemWindowText
12312 set selectbgcolor SystemHighlight
12315 set uifgcolor black
12316 set uifgdisabledcolor "#999"
12319 set selectbgcolor gray85
12321 set diffcolors {red "#00a000" blue}
12323 set mergecolors {red blue "#00ff00" purple brown "#009090" magenta "#808000" "#009000" "#ff0080" cyan "#b07070" "#70b0f0" "#70f0b0" "#f0b070" "#ff70b0"}
12326 set markbgcolor "#e0e0ff"
12328 set headbgcolor "#00ff00"
12329 set headfgcolor black
12330 set headoutlinecolor black
12331 set remotebgcolor #ffddaa
12332 set tagbgcolor yellow
12333 set tagfgcolor black
12334 set tagoutlinecolor black
12335 set reflinecolor black
12336 set filesepbgcolor #aaaaaa
12337 set filesepfgcolor black
12338 set linehoverbgcolor #ffff80
12339 set linehoverfgcolor black
12340 set linehoveroutlinecolor black
12341 set mainheadcirclecolor yellow
12342 set workingfilescirclecolor red
12343 set indexcirclecolor "#00ff00"
12344 set circlecolors {white blue gray blue blue}
12345 set linkfgcolor blue
12346 set circleoutlinecolor $fgcolor
12347 set foundbgcolor yellow
12348 set currentsearchhitbgcolor orange
12350 # button for popping up context menus
12351 if {[tk windowingsystem] eq "aqua"} {
12352 set ctxbut <Button-2>
12354 set ctxbut <Button-3>
12358 # follow the XDG base directory specification by default. See
12359 # http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
12360 if {[info exists env(XDG_CONFIG_HOME)] && $env(XDG_CONFIG_HOME) ne ""} {
12361 # XDG_CONFIG_HOME environment variable is set
12362 set config_file [file join $env(XDG_CONFIG_HOME) git gitk]
12363 set config_file_tmp [file join $env(XDG_CONFIG_HOME) git gitk-tmp]
12365 # default XDG_CONFIG_HOME
12366 set config_file "~/.config/git/gitk"
12367 set config_file_tmp "~/.config/git/gitk-tmp"
12369 if {![file exists $config_file]} {
12370 # for backward compatibility use the old config file if it exists
12371 if {[file exists "~/.gitk"]} {
12372 set config_file "~/.gitk"
12373 set config_file_tmp "~/.gitk-tmp"
12374 } elseif {![file exists [file dirname $config_file]]} {
12375 file mkdir [file dirname $config_file]
12378 source $config_file
12380 config_check_tmp_exists 50
12382 set config_variables {
12383 mainfont textfont uifont tabstop findmergefiles maxgraphpct maxwidth
12384 cmitmode wrapcomment autoselect autosellen showneartags maxrefs visiblerefs
12385 hideremotes showlocalchanges datetimeformat limitdiffs uicolor want_ttk
12386 bgcolor fgcolor uifgcolor uifgdisabledcolor colors diffcolors mergecolors
12387 markbgcolor diffcontext selectbgcolor foundbgcolor currentsearchhitbgcolor
12388 extdifftool perfile_attrs headbgcolor headfgcolor headoutlinecolor
12389 remotebgcolor tagbgcolor tagfgcolor tagoutlinecolor reflinecolor
12390 filesepbgcolor filesepfgcolor linehoverbgcolor linehoverfgcolor
12391 linehoveroutlinecolor mainheadcirclecolor workingfilescirclecolor
12392 indexcirclecolor circlecolors linkfgcolor circleoutlinecolor
12394 foreach var $config_variables {
12395 config_init_trace $var
12396 trace add variable $var write config_variable_change_cb
12399 parsefont mainfont $mainfont
12400 eval font create mainfont [fontflags mainfont]
12401 eval font create mainfontbold [fontflags mainfont 1]
12403 parsefont textfont $textfont
12404 eval font create textfont [fontflags textfont]
12405 eval font create textfontbold [fontflags textfont 1]
12407 parsefont uifont $uifont
12408 eval font create uifont [fontflags uifont]
12414 # check that we can find a .git directory somewhere...
12415 if {[catch {set gitdir [exec git rev-parse --git-dir]}]} {
12416 show_error {} . [mc "Cannot find a git repository here."]
12421 set selectheadid {}
12424 set cmdline_files {}
12426 set revtreeargscmd {}
12427 foreach arg $argv {
12428 switch -glob -- $arg {
12431 set cmdline_files [lrange $argv [expr {$i + 1}] end]
12434 "--select-commit=*" {
12435 set selecthead [string range $arg 16 end]
12438 set revtreeargscmd [string range $arg 10 end]
12441 lappend revtreeargs $arg
12447 if {$selecthead eq "HEAD"} {
12451 if {$i >= [llength $argv] && $revtreeargs ne {}} {
12452 # no -- on command line, but some arguments (other than --argscmd)
12454 set f [eval exec git rev-parse --no-revs --no-flags $revtreeargs]
12455 set cmdline_files [split $f "\n"]
12456 set n [llength $cmdline_files]
12457 set revtreeargs [lrange $revtreeargs 0 end-$n]
12458 # Unfortunately git rev-parse doesn't produce an error when
12459 # something is both a revision and a filename. To be consistent
12460 # with git log and git rev-list, check revtreeargs for filenames.
12461 foreach arg $revtreeargs {
12462 if {[file exists $arg]} {
12463 show_error {} . [mc "Ambiguous argument '%s': both revision\
12464 and filename" $arg]
12469 # unfortunately we get both stdout and stderr in $err,
12470 # so look for "fatal:".
12471 set i [string first "fatal:" $err]
12473 set err [string range $err [expr {$i + 6}] end]
12475 show_error {} . "[mc "Bad arguments to gitk:"]\n$err"
12480 set nullid "0000000000000000000000000000000000000000"
12481 set nullid2 "0000000000000000000000000000000000000001"
12482 set nullfile "/dev/null"
12484 set have_tk85 [expr {[package vcompare $tk_version "8.5"] >= 0}]
12485 if {![info exists have_ttk]} {
12486 set have_ttk [llength [info commands ::ttk::style]]
12488 set use_ttk [expr {$have_ttk && $want_ttk}]
12489 set NS [expr {$use_ttk ? "ttk" : ""}]
12495 regexp {^git version ([\d.]*\d)} [exec git version] _ git_version
12498 if {[package vcompare $git_version "1.6.6.2"] >= 0} {
12499 set show_notes "--show-notes"
12509 set highlight_paths {}
12511 set searchdirn -forwards
12514 set diffelide {0 0}
12515 set markingmatches 0
12516 set linkentercount 0
12517 set need_redisplay 0
12524 set selectedhlview [mc "None"]
12525 set highlight_related [mc "None"]
12526 set highlight_files {}
12527 set viewfiles(0) {}
12529 set viewchanged(0) 0
12531 set viewargscmd(0) {}
12533 set selectedline {}
12541 set hasworktree [hasworktree]
12543 if {[expr {[exec git rev-parse --is-inside-work-tree] == "true"}]} {
12544 set cdup [exec git rev-parse --show-cdup]
12546 set worktree [exec git rev-parse --show-toplevel]
12550 image create photo gitlogo -width 16 -height 16
12552 image create photo gitlogominus -width 4 -height 2
12553 gitlogominus put #C00000 -to 0 0 4 2
12554 gitlogo copy gitlogominus -to 1 5
12555 gitlogo copy gitlogominus -to 6 5
12556 gitlogo copy gitlogominus -to 11 5
12557 image delete gitlogominus
12559 image create photo gitlogoplus -width 4 -height 4
12560 gitlogoplus put #008000 -to 1 0 3 4
12561 gitlogoplus put #008000 -to 0 1 4 3
12562 gitlogo copy gitlogoplus -to 1 9
12563 gitlogo copy gitlogoplus -to 6 9
12564 gitlogo copy gitlogoplus -to 11 9
12565 image delete gitlogoplus
12567 image create photo gitlogo32 -width 32 -height 32
12568 gitlogo32 copy gitlogo -zoom 2 2
12570 wm iconphoto . -default gitlogo gitlogo32
12572 # wait for the window to become visible
12573 tkwait visibility .
12578 if {$cmdline_files ne {} || $revtreeargs ne {} || $revtreeargscmd ne {}} {
12579 # create a view for the files/dirs specified on the command line
12583 set viewname(1) [mc "Command line"]
12584 set viewfiles(1) $cmdline_files
12585 set viewargs(1) $revtreeargs
12586 set viewargscmd(1) $revtreeargscmd
12588 set viewchanged(1) 0
12591 .bar.view entryconf [mca "&Edit view..."] -state normal
12592 .bar.view entryconf [mca "&Delete view"] -state normal
12595 if {[info exists permviews]} {
12596 foreach v $permviews {
12599 set viewname($n) [lindex $v 0]
12600 set viewfiles($n) [lindex $v 1]
12601 set viewargs($n) [lindex $v 2]
12602 set viewargscmd($n) [lindex $v 3]
12604 set viewchanged($n) 0
12609 if {[tk windowingsystem] eq "win32"} {
12617 # indent-tabs-mode: t