2 # Tcl ignores the next line -*- tcl -*- \
5 # Copyright © 2005-2009 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.
14 if {[info exists env
(GIT_DIR
)]} {
17 return [exec git rev-parse
--git-dir]
23 if {[info exists _gitworktree
]} {
26 # v1.7.0 introduced --show-toplevel to return the canonical work-tree
27 if {[catch
{set _gitworktree
[exec git rev-parse
--show-toplevel]}]} {
28 # try to set work tree from environment, core.worktree or use
29 # cdup to obtain a relative path to the top of the worktree. If
30 # run from the top, the ./ prefix ensures normalize expands pwd.
31 if {[catch
{ set _gitworktree
$env(GIT_WORK_TREE
) }]} {
32 catch
{set _gitworktree
[exec git config
--get core.worktree
]}
33 if {$_gitworktree eq
""} {
34 set _gitworktree
[file normalize .
/[exec git rev-parse
--show-cdup]]
41 # A simple scheduler for compute-intensive stuff.
42 # The aim is to make sure that event handlers for GUI actions can
43 # run at least every 50-100 ms. Unfortunately fileevent handlers are
44 # run before X event handlers, so reading from a fast source can
45 # make the GUI completely unresponsive.
47 global isonrunq runq currunq
50 if {[info exists isonrunq
($script)]} return
51 if {$runq eq
{} && ![info exists currunq
]} {
54 lappend runq
[list
{} $script]
55 set isonrunq
($script) 1
58 proc filerun
{fd
script} {
59 fileevent
$fd readable
[list filereadable
$fd $script]
62 proc filereadable
{fd
script} {
65 fileevent
$fd readable
{}
66 if {$runq eq
{} && ![info exists currunq
]} {
69 lappend runq
[list
$fd $script]
75 for {set i
0} {$i < [llength
$runq]} {} {
76 if {[lindex
$runq $i 0] eq
$fd} {
77 set runq
[lreplace
$runq $i $i]
85 global isonrunq runq currunq
87 set tstart
[clock clicks
-milliseconds]
89 while {[llength
$runq] > 0} {
90 set fd
[lindex
$runq 0 0]
91 set script [lindex
$runq 0 1]
92 set currunq
[lindex
$runq 0]
93 set runq
[lrange
$runq 1 end
]
94 set repeat
[eval $script]
96 set t1
[clock clicks
-milliseconds]
97 set t
[expr {$t1 - $t0}]
98 if {$repeat ne
{} && $repeat} {
99 if {$fd eq
{} ||
$repeat == 2} {
100 # script returns 1 if it wants to be readded
101 # file readers return 2 if they could do more straight away
102 lappend runq
[list
$fd $script]
104 fileevent
$fd readable
[list filereadable
$fd $script]
106 } elseif
{$fd eq
{}} {
107 unset isonrunq
($script)
110 if {$t1 - $tstart >= 80} break
117 proc reg_instance
{fd
} {
118 global commfd leftover loginstance
120 set i
[incr loginstance
]
126 proc unmerged_files
{files
} {
129 # find the list of unmerged files
133 set fd
[open
"| git ls-files -u" r
]
135 show_error
{} .
"[mc "Couldn
't get list of unmerged files:"] $err"
138 while {[gets $fd line] >= 0} {
139 set i [string first "\t" $line]
141 set fname [string range $line [expr {$i+1}] end]
142 if {[lsearch -exact $mlist $fname] >= 0} continue
144 if {$files eq {} || [path_filter $files $fname]} {
152 proc parseviewargs {n arglist} {
153 global vdatemode vmergeonly vflags vdflags vrevs vfiltered vorigargs env
154 global worddiff git_version
162 set origargs $arglist
166 foreach arg $arglist {
173 switch -glob -- $arg {
177 # remove from origargs in case we hit an unknown option
178 set origargs [lreplace $origargs $i $i]
182 "--no-renames" - "--full-index" - "--binary" - "--abbrev=*" -
183 "--find-copies-harder" - "-l*" - "--ext-diff" - "--no-ext-diff" -
184 "--src-prefix=*" - "--dst-prefix=*" - "--no-prefix" -
185 "-O*" - "--text" - "--full-diff" - "--ignore-space-at-eol" -
186 "--ignore-space-change" - "-U*" - "--unified=*" {
187 # These request or affect diff output, which we don't want.
188 # Some could be used to set our defaults for diff display.
189 lappend diffargs
$arg
191 "--raw" - "--patch-with-raw" - "--patch-with-stat" -
192 "--name-only" - "--name-status" - "--color" -
193 "--log-size" - "--pretty=*" - "--decorate" - "--abbrev-commit" -
194 "--cc" - "-z" - "--header" - "--parents" - "--boundary" -
195 "--no-color" - "-g" - "--walk-reflogs" - "--no-walk" -
196 "--timestamp" - "relative-date" - "--date=*" - "--stdin" -
197 "--objects" - "--objects-edge" - "--reverse" {
198 # These cause our parsing of git log's output to fail, or else
199 # they're options we want to set ourselves, so ignore them.
201 "--color-words*" - "--word-diff=color" {
202 # These trigger a word diff in the console interface,
203 # so help the user by enabling our own support
204 if {[package vcompare
$git_version "1.7.2"] >= 0} {
205 set worddiff
[mc
"Color words"]
209 if {[package vcompare
$git_version "1.7.2"] >= 0} {
210 set worddiff
[mc
"Markup words"]
213 "--stat=*" - "--numstat" - "--shortstat" - "--summary" -
214 "--check" - "--exit-code" - "--quiet" - "--topo-order" -
215 "--full-history" - "--dense" - "--sparse" -
216 "--follow" - "--left-right" - "--encoding=*" {
217 # These are harmless, and some are even useful
220 "--diff-filter=*" - "--no-merges" - "--unpacked" -
221 "--max-count=*" - "--skip=*" - "--since=*" - "--after=*" -
222 "--until=*" - "--before=*" - "--max-age=*" - "--min-age=*" -
223 "--author=*" - "--committer=*" - "--grep=*" - "-[iE]" -
224 "--remove-empty" - "--first-parent" - "--cherry-pick" -
225 "-S*" - "--pickaxe-all" - "--pickaxe-regex" -
226 "--simplify-by-decoration" {
227 # These mean that we get a subset of the commits
232 # This appears to be the only one that has a value as a
233 # separate word following it
243 # git rev-parse doesn't understand --merge
244 lappend revargs
--gitk-symmetric-diff-marker MERGE_HEAD...HEAD
246 "--no-replace-objects" {
247 set env
(GIT_NO_REPLACE_OBJECTS
) "1"
250 # Other flag arguments including -<n>
251 if {[string is digit
-strict [string range
$arg 1 end
]]} {
254 # a flag argument that we don't recognize;
255 # that means we can't optimize
261 # Non-flag arguments specify commits or ranges of commits
262 if {[string match
"*...*" $arg]} {
263 lappend revargs
--gitk-symmetric-diff-marker
269 set vdflags
($n) $diffargs
270 set vflags
($n) $glflags
271 set vrevs
($n) $revargs
272 set vfiltered
($n) $filtered
273 set vorigargs
($n) $origargs
277 proc parseviewrevs
{view revs
} {
278 global vposids vnegids
283 if {[catch
{set ids
[eval exec git rev-parse
$revs]} err
]} {
284 # we get stdout followed by stderr in $err
285 # for an unknown rev, git rev-parse echoes it and then errors out
286 set errlines
[split $err "\n"]
288 for {set l
0} {$l < [llength
$errlines]} {incr l
} {
289 set line
[lindex
$errlines $l]
290 if {!([string length
$line] == 40 && [string is xdigit
$line])} {
291 if {[string match
"fatal:*" $line]} {
292 if {[string match
"fatal: ambiguous argument*" $line]
294 if {[llength
$badrev] == 1} {
295 set err
"unknown revision $badrev"
297 set err
"unknown revisions: [join $badrev ", "]"
300 set err
[join [lrange
$errlines $l end
] "\n"]
307 error_popup
"[mc "Error parsing revisions
:"] $err"
314 foreach id
[split $ids "\n"] {
315 if {$id eq
"--gitk-symmetric-diff-marker"} {
317 } elseif
{[string match
"^*" $id]} {
324 lappend neg
[string range
$id 1 end
]
329 lset ret end
$id...
[lindex
$ret end
]
335 set vposids
($view) $pos
336 set vnegids
($view) $neg
340 # Start off a git log process and arrange to read its output
341 proc start_rev_list
{view
} {
342 global startmsecs commitidx viewcomplete curview
344 global viewargs viewargscmd viewfiles vfilelimit
345 global showlocalchanges
346 global viewactive viewinstances vmergeonly
347 global mainheadid viewmainheadid viewmainheadid_orig
348 global vcanopt vflags vrevs vorigargs
351 set startmsecs
[clock clicks
-milliseconds]
352 set commitidx
($view) 0
353 # these are set this way for the error exits
354 set viewcomplete
($view) 1
355 set viewactive
($view) 0
358 set args
$viewargs($view)
359 if {$viewargscmd($view) ne
{}} {
361 set str
[exec sh
-c $viewargscmd($view)]
363 error_popup
"[mc "Error executing
--argscmd command:"] $err"
366 set args
[concat
$args [split $str "\n"]]
368 set vcanopt
($view) [parseviewargs
$view $args]
370 set files
$viewfiles($view)
371 if {$vmergeonly($view)} {
372 set files
[unmerged_files
$files]
375 if {$nr_unmerged == 0} {
376 error_popup
[mc
"No files selected: --merge specified but\
377 no files are unmerged."]
379 error_popup
[mc
"No files selected: --merge specified but\
380 no unmerged files are within file limit."]
385 set vfilelimit
($view) $files
387 if {$vcanopt($view)} {
388 set revs
[parseviewrevs
$view $vrevs($view)]
392 set args
[limit_arg_length
[concat
$vflags($view) $revs]]
394 set args
$vorigargs($view)
398 set fd
[open
[concat | git log
--no-color -z --pretty=raw
$show_notes \
399 --parents --boundary $args "--" $files] r
]
401 error_popup
"[mc "Error executing git log
:"] $err"
404 set i
[reg_instance
$fd]
405 set viewinstances
($view) [list
$i]
406 set viewmainheadid
($view) $mainheadid
407 set viewmainheadid_orig
($view) $mainheadid
408 if {$files ne
{} && $mainheadid ne
{}} {
409 get_viewmainhead
$view
411 if {$showlocalchanges && $viewmainheadid($view) ne
{}} {
412 interestedin
$viewmainheadid($view) dodiffindex
414 fconfigure
$fd -blocking 0 -translation lf
-eofchar {}
415 if {$tclencoding != {}} {
416 fconfigure
$fd -encoding $tclencoding
418 filerun
$fd [list getcommitlines
$fd $i $view 0]
419 nowbusy
$view [mc
"Reading"]
420 set viewcomplete
($view) 0
421 set viewactive
($view) 1
425 proc stop_instance
{inst
} {
426 global commfd leftover
428 set fd
$commfd($inst)
432 if {$
::tcl_platform
(platform
) eq
{windows
}} {
441 unset leftover
($inst)
444 proc stop_backends
{} {
447 foreach inst
[array names commfd
] {
452 proc stop_rev_list
{view
} {
455 foreach inst
$viewinstances($view) {
458 set viewinstances
($view) {}
461 proc reset_pending_select
{selid
} {
462 global pending_select mainheadid selectheadid
465 set pending_select
$selid
466 } elseif
{$selectheadid ne
{}} {
467 set pending_select
$selectheadid
469 set pending_select
$mainheadid
473 proc getcommits
{selid
} {
474 global canv curview need_redisplay viewactive
477 if {[start_rev_list
$curview]} {
478 reset_pending_select
$selid
479 show_status
[mc
"Reading commits..."]
482 show_status
[mc
"No commits selected"]
486 proc updatecommits
{} {
487 global curview vcanopt vorigargs vfilelimit viewinstances
488 global viewactive viewcomplete tclencoding
489 global startmsecs showneartags showlocalchanges
490 global mainheadid viewmainheadid viewmainheadid_orig pending_select
492 global varcid vposids vnegids vflags vrevs
495 set isworktree
[expr {[exec git rev-parse
--is-inside-work-tree] == "true"}]
498 if {$mainheadid ne
$viewmainheadid_orig($view)} {
499 if {$showlocalchanges} {
502 set viewmainheadid
($view) $mainheadid
503 set viewmainheadid_orig
($view) $mainheadid
504 if {$vfilelimit($view) ne
{}} {
505 get_viewmainhead
$view
508 if {$showlocalchanges} {
511 if {$vcanopt($view)} {
512 set oldpos
$vposids($view)
513 set oldneg
$vnegids($view)
514 set revs
[parseviewrevs
$view $vrevs($view)]
518 # note: getting the delta when negative refs change is hard,
519 # and could require multiple git log invocations, so in that
520 # case we ask git log for all the commits (not just the delta)
521 if {$oldneg eq
$vnegids($view)} {
524 # take out positive refs that we asked for before or
525 # that we have already seen
527 if {[string length
$rev] == 40} {
528 if {[lsearch
-exact $oldpos $rev] < 0
529 && ![info exists varcid
($view,$rev)]} {
534 lappend
$newrevs $rev
537 if {$npos == 0} return
539 set vposids
($view) [lsort
-unique [concat
$oldpos $vposids($view)]]
541 set args
[concat
$vflags($view) $revs --not $oldpos]
543 set args
$vorigargs($view)
546 set fd
[open
[concat | git log
--no-color -z --pretty=raw
$show_notes \
547 --parents --boundary $args "--" $vfilelimit($view)] r
]
549 error_popup
"[mc "Error executing git log
:"] $err"
552 if {$viewactive($view) == 0} {
553 set startmsecs
[clock clicks
-milliseconds]
555 set i
[reg_instance
$fd]
556 lappend viewinstances
($view) $i
557 fconfigure
$fd -blocking 0 -translation lf
-eofchar {}
558 if {$tclencoding != {}} {
559 fconfigure
$fd -encoding $tclencoding
561 filerun
$fd [list getcommitlines
$fd $i $view 1]
562 incr viewactive
($view)
563 set viewcomplete
($view) 0
564 reset_pending_select
{}
565 nowbusy
$view [mc
"Reading"]
571 proc reloadcommits
{} {
572 global curview viewcomplete selectedline currentid thickerline
573 global showneartags treediffs commitinterest cached_commitrow
577 if {$selectedline ne
{}} {
581 if {!$viewcomplete($curview)} {
582 stop_rev_list
$curview
586 catch
{unset currentid
}
587 catch
{unset thickerline
}
588 catch
{unset treediffs
}
595 catch
{unset commitinterest
}
596 catch
{unset cached_commitrow
}
597 catch
{unset targetid
}
603 # This makes a string representation of a positive integer which
604 # sorts as a string in numerical order
607 return [format
"%x" $n]
608 } elseif
{$n < 256} {
609 return [format
"x%.2x" $n]
610 } elseif
{$n < 65536} {
611 return [format
"y%.4x" $n]
613 return [format
"z%.8x" $n]
616 # Procedures used in reordering commits from git log (without
617 # --topo-order) into the order for display.
619 proc varcinit
{view
} {
620 global varcstart vupptr vdownptr vleftptr vbackptr varctok varcrow
621 global vtokmod varcmod vrowmod varcix vlastins
623 set varcstart
($view) {{}}
624 set vupptr
($view) {0}
625 set vdownptr
($view) {0}
626 set vleftptr
($view) {0}
627 set vbackptr
($view) {0}
628 set varctok
($view) {{}}
629 set varcrow
($view) {{}}
630 set vtokmod
($view) {}
633 set varcix
($view) {{}}
634 set vlastins
($view) {0}
637 proc resetvarcs
{view
} {
638 global varcid varccommits parents children vseedcount ordertok
640 foreach vid
[array names varcid
$view,*] {
645 # some commits might have children but haven't been seen yet
646 foreach vid
[array names children
$view,*] {
649 foreach va
[array names varccommits
$view,*] {
650 unset varccommits
($va)
652 foreach vd
[array names vseedcount
$view,*] {
653 unset vseedcount
($vd)
655 catch
{unset ordertok
}
658 # returns a list of the commits with no children
660 global vdownptr vleftptr varcstart
663 set a
[lindex
$vdownptr($v) 0]
665 lappend ret
[lindex
$varcstart($v) $a]
666 set a
[lindex
$vleftptr($v) $a]
671 proc newvarc
{view id
} {
672 global varcid varctok parents children vdatemode
673 global vupptr vdownptr vleftptr vbackptr varcrow varcix varcstart
674 global commitdata commitinfo vseedcount varccommits vlastins
676 set a
[llength
$varctok($view)]
678 if {[llength
$children($vid)] == 0 ||
$vdatemode($view)} {
679 if {![info exists commitinfo
($id)]} {
680 parsecommit
$id $commitdata($id) 1
682 set cdate
[lindex
$commitinfo($id) 4]
683 if {![string is integer
-strict $cdate]} {
686 if {![info exists vseedcount
($view,$cdate)]} {
687 set vseedcount
($view,$cdate) -1
689 set c
[incr vseedcount
($view,$cdate)]
690 set cdate
[expr {$cdate ^
0xffffffff}]
691 set tok
"s[strrep $cdate][strrep $c]"
696 if {[llength
$children($vid)] > 0} {
697 set kid
[lindex
$children($vid) end
]
698 set k
$varcid($view,$kid)
699 if {[string compare
[lindex
$varctok($view) $k] $tok] > 0} {
702 set tok
[lindex
$varctok($view) $k]
706 set i
[lsearch
-exact $parents($view,$ki) $id]
707 set j
[expr {[llength
$parents($view,$ki)] - 1 - $i}]
708 append tok
[strrep
$j]
710 set c
[lindex
$vlastins($view) $ka]
711 if {$c == 0 ||
[string compare
$tok [lindex
$varctok($view) $c]] < 0} {
713 set b
[lindex
$vdownptr($view) $ka]
715 set b
[lindex
$vleftptr($view) $c]
717 while {$b != 0 && [string compare
$tok [lindex
$varctok($view) $b]] >= 0} {
719 set b
[lindex
$vleftptr($view) $c]
722 lset vdownptr
($view) $ka $a
723 lappend vbackptr
($view) 0
725 lset vleftptr
($view) $c $a
726 lappend vbackptr
($view) $c
728 lset vlastins
($view) $ka $a
729 lappend vupptr
($view) $ka
730 lappend vleftptr
($view) $b
732 lset vbackptr
($view) $b $a
734 lappend varctok
($view) $tok
735 lappend varcstart
($view) $id
736 lappend vdownptr
($view) 0
737 lappend varcrow
($view) {}
738 lappend varcix
($view) {}
739 set varccommits
($view,$a) {}
740 lappend vlastins
($view) 0
744 proc splitvarc
{p v
} {
745 global varcid varcstart varccommits varctok vtokmod
746 global vupptr vdownptr vleftptr vbackptr varcix varcrow vlastins
748 set oa
$varcid($v,$p)
749 set otok
[lindex
$varctok($v) $oa]
750 set ac
$varccommits($v,$oa)
751 set i
[lsearch
-exact $varccommits($v,$oa) $p]
753 set na
[llength
$varctok($v)]
754 # "%" sorts before "0"...
755 set tok
"$otok%[strrep $i]"
756 lappend varctok
($v) $tok
757 lappend varcrow
($v) {}
758 lappend varcix
($v) {}
759 set varccommits
($v,$oa) [lrange
$ac 0 [expr {$i - 1}]]
760 set varccommits
($v,$na) [lrange
$ac $i end
]
761 lappend varcstart
($v) $p
762 foreach id
$varccommits($v,$na) {
763 set varcid
($v,$id) $na
765 lappend vdownptr
($v) [lindex
$vdownptr($v) $oa]
766 lappend vlastins
($v) [lindex
$vlastins($v) $oa]
767 lset vdownptr
($v) $oa $na
768 lset vlastins
($v) $oa 0
769 lappend vupptr
($v) $oa
770 lappend vleftptr
($v) 0
771 lappend vbackptr
($v) 0
772 for {set b
[lindex
$vdownptr($v) $na]} {$b != 0} {set b
[lindex
$vleftptr($v) $b]} {
773 lset vupptr
($v) $b $na
775 if {[string compare
$otok $vtokmod($v)] <= 0} {
780 proc renumbervarc
{a v
} {
781 global parents children varctok varcstart varccommits
782 global vupptr vdownptr vleftptr vbackptr vlastins varcid vtokmod vdatemode
784 set t1
[clock clicks
-milliseconds]
790 if {[info exists isrelated
($a)]} {
792 set id
[lindex
$varccommits($v,$a) end
]
793 foreach p
$parents($v,$id) {
794 if {[info exists varcid
($v,$p)]} {
795 set isrelated
($varcid($v,$p)) 1
800 set b
[lindex
$vdownptr($v) $a]
803 set b
[lindex
$vleftptr($v) $a]
805 set a
[lindex
$vupptr($v) $a]
811 if {![info exists kidchanged
($a)]} continue
812 set id
[lindex
$varcstart($v) $a]
813 if {[llength
$children($v,$id)] > 1} {
814 set children
($v,$id) [lsort
-command [list vtokcmp
$v] \
817 set oldtok
[lindex
$varctok($v) $a]
818 if {!$vdatemode($v)} {
824 set kid
[last_real_child
$v,$id]
826 set k
$varcid($v,$kid)
827 if {[string compare
[lindex
$varctok($v) $k] $tok] > 0} {
830 set tok
[lindex
$varctok($v) $k]
834 set i
[lsearch
-exact $parents($v,$ki) $id]
835 set j
[expr {[llength
$parents($v,$ki)] - 1 - $i}]
836 append tok
[strrep
$j]
838 if {$tok eq
$oldtok} {
841 set id
[lindex
$varccommits($v,$a) end
]
842 foreach p
$parents($v,$id) {
843 if {[info exists varcid
($v,$p)]} {
844 set kidchanged
($varcid($v,$p)) 1
849 lset varctok
($v) $a $tok
850 set b
[lindex
$vupptr($v) $a]
852 if {[string compare
[lindex
$varctok($v) $ka] $vtokmod($v)] < 0} {
855 if {[string compare
[lindex
$varctok($v) $b] $vtokmod($v)] < 0} {
858 set c
[lindex
$vbackptr($v) $a]
859 set d
[lindex
$vleftptr($v) $a]
861 lset vdownptr
($v) $b $d
863 lset vleftptr
($v) $c $d
866 lset vbackptr
($v) $d $c
868 if {[lindex
$vlastins($v) $b] == $a} {
869 lset vlastins
($v) $b $c
871 lset vupptr
($v) $a $ka
872 set c
[lindex
$vlastins($v) $ka]
874 [string compare
$tok [lindex
$varctok($v) $c]] < 0} {
876 set b
[lindex
$vdownptr($v) $ka]
878 set b
[lindex
$vleftptr($v) $c]
881 [string compare
$tok [lindex
$varctok($v) $b]] >= 0} {
883 set b
[lindex
$vleftptr($v) $c]
886 lset vdownptr
($v) $ka $a
887 lset vbackptr
($v) $a 0
889 lset vleftptr
($v) $c $a
890 lset vbackptr
($v) $a $c
892 lset vleftptr
($v) $a $b
894 lset vbackptr
($v) $b $a
896 lset vlastins
($v) $ka $a
899 foreach id
[array names sortkids
] {
900 if {[llength
$children($v,$id)] > 1} {
901 set children
($v,$id) [lsort
-command [list vtokcmp
$v] \
905 set t2
[clock clicks
-milliseconds]
906 #puts "renumbervarc did [llength $todo] of $ntot arcs in [expr {$t2-$t1}]ms"
909 # Fix up the graph after we have found out that in view $v,
910 # $p (a commit that we have already seen) is actually the parent
911 # of the last commit in arc $a.
912 proc fix_reversal
{p a v
} {
913 global varcid varcstart varctok vupptr
915 set pa
$varcid($v,$p)
916 if {$p ne
[lindex
$varcstart($v) $pa]} {
918 set pa
$varcid($v,$p)
920 # seeds always need to be renumbered
921 if {[lindex
$vupptr($v) $pa] == 0 ||
922 [string compare
[lindex
$varctok($v) $a] \
923 [lindex
$varctok($v) $pa]] > 0} {
928 proc insertrow
{id p v
} {
929 global cmitlisted children parents varcid varctok vtokmod
930 global varccommits ordertok commitidx numcommits curview
931 global targetid targetrow
935 set cmitlisted
($vid) 1
936 set children
($vid) {}
937 set parents
($vid) [list
$p]
938 set a
[newvarc
$v $id]
940 if {[string compare
[lindex
$varctok($v) $a] $vtokmod($v)] < 0} {
943 lappend varccommits
($v,$a) $id
945 if {[llength
[lappend children
($vp) $id]] > 1} {
946 set children
($vp) [lsort
-command [list vtokcmp
$v] $children($vp)]
947 catch
{unset ordertok
}
949 fix_reversal
$p $a $v
951 if {$v == $curview} {
952 set numcommits
$commitidx($v)
954 if {[info exists targetid
]} {
955 if {![comes_before
$targetid $p]} {
962 proc insertfakerow
{id p
} {
963 global varcid varccommits parents children cmitlisted
964 global commitidx varctok vtokmod targetid targetrow curview numcommits
968 set i
[lsearch
-exact $varccommits($v,$a) $p]
970 puts
"oops: insertfakerow can't find [shortids $p] on arc $a"
973 set children
($v,$id) {}
974 set parents
($v,$id) [list
$p]
975 set varcid
($v,$id) $a
976 lappend children
($v,$p) $id
977 set cmitlisted
($v,$id) 1
978 set numcommits
[incr commitidx
($v)]
979 # note we deliberately don't update varcstart($v) even if $i == 0
980 set varccommits
($v,$a) [linsert
$varccommits($v,$a) $i $id]
982 if {[info exists targetid
]} {
983 if {![comes_before
$targetid $p]} {
991 proc removefakerow
{id
} {
992 global varcid varccommits parents children commitidx
993 global varctok vtokmod cmitlisted currentid selectedline
994 global targetid curview numcommits
997 if {[llength
$parents($v,$id)] != 1} {
998 puts
"oops: removefakerow [shortids $id] has [llength $parents($v,$id)] parents"
1001 set p
[lindex
$parents($v,$id) 0]
1002 set a
$varcid($v,$id)
1003 set i
[lsearch
-exact $varccommits($v,$a) $id]
1005 puts
"oops: removefakerow can't find [shortids $id] on arc $a"
1008 unset varcid
($v,$id)
1009 set varccommits
($v,$a) [lreplace
$varccommits($v,$a) $i $i]
1010 unset parents
($v,$id)
1011 unset children
($v,$id)
1012 unset cmitlisted
($v,$id)
1013 set numcommits
[incr commitidx
($v) -1]
1014 set j
[lsearch
-exact $children($v,$p) $id]
1016 set children
($v,$p) [lreplace
$children($v,$p) $j $j]
1019 if {[info exist currentid
] && $id eq
$currentid} {
1023 if {[info exists targetid
] && $targetid eq
$id} {
1030 proc real_children
{vp
} {
1031 global children nullid nullid2
1034 foreach id
$children($vp) {
1035 if {$id ne
$nullid && $id ne
$nullid2} {
1042 proc first_real_child
{vp
} {
1043 global children nullid nullid2
1045 foreach id
$children($vp) {
1046 if {$id ne
$nullid && $id ne
$nullid2} {
1053 proc last_real_child
{vp
} {
1054 global children nullid nullid2
1056 set kids
$children($vp)
1057 for {set i
[llength
$kids]} {[incr i
-1] >= 0} {} {
1058 set id
[lindex
$kids $i]
1059 if {$id ne
$nullid && $id ne
$nullid2} {
1066 proc vtokcmp
{v a b
} {
1067 global varctok varcid
1069 return [string compare
[lindex
$varctok($v) $varcid($v,$a)] \
1070 [lindex
$varctok($v) $varcid($v,$b)]]
1073 # This assumes that if lim is not given, the caller has checked that
1074 # arc a's token is less than $vtokmod($v)
1075 proc modify_arc
{v a
{lim
{}}} {
1076 global varctok vtokmod varcmod varcrow vupptr curview vrowmod varccommits
1079 set c
[string compare
[lindex
$varctok($v) $a] $vtokmod($v)]
1082 set r
[lindex
$varcrow($v) $a]
1083 if {$r ne
{} && $vrowmod($v) <= $r + $lim} return
1086 set vtokmod
($v) [lindex
$varctok($v) $a]
1088 if {$v == $curview} {
1089 while {$a != 0 && [lindex
$varcrow($v) $a] eq
{}} {
1090 set a
[lindex
$vupptr($v) $a]
1096 set lim
[llength
$varccommits($v,$a)]
1098 set r
[expr {[lindex
$varcrow($v) $a] + $lim}]
1105 proc update_arcrows
{v
} {
1106 global vtokmod varcmod vrowmod varcrow commitidx currentid selectedline
1107 global varcid vrownum varcorder varcix varccommits
1108 global vupptr vdownptr vleftptr varctok
1109 global displayorder parentlist curview cached_commitrow
1111 if {$vrowmod($v) == $commitidx($v)} return
1112 if {$v == $curview} {
1113 if {[llength
$displayorder] > $vrowmod($v)} {
1114 set displayorder
[lrange
$displayorder 0 [expr {$vrowmod($v) - 1}]]
1115 set parentlist
[lrange
$parentlist 0 [expr {$vrowmod($v) - 1}]]
1117 catch
{unset cached_commitrow
}
1119 set narctot
[expr {[llength
$varctok($v)] - 1}]
1121 while {$a != 0 && [lindex
$varcix($v) $a] eq
{}} {
1122 # go up the tree until we find something that has a row number,
1123 # or we get to a seed
1124 set a
[lindex
$vupptr($v) $a]
1127 set a
[lindex
$vdownptr($v) 0]
1130 set varcorder
($v) [list
$a]
1131 lset varcix
($v) $a 0
1132 lset varcrow
($v) $a 0
1136 set arcn
[lindex
$varcix($v) $a]
1137 if {[llength
$vrownum($v)] > $arcn + 1} {
1138 set vrownum
($v) [lrange
$vrownum($v) 0 $arcn]
1139 set varcorder
($v) [lrange
$varcorder($v) 0 $arcn]
1141 set row
[lindex
$varcrow($v) $a]
1145 incr row
[llength
$varccommits($v,$a)]
1146 # go down if possible
1147 set b
[lindex
$vdownptr($v) $a]
1149 # if not, go left, or go up until we can go left
1151 set b
[lindex
$vleftptr($v) $a]
1153 set a
[lindex
$vupptr($v) $a]
1159 lappend vrownum
($v) $row
1160 lappend varcorder
($v) $a
1161 lset varcix
($v) $a $arcn
1162 lset varcrow
($v) $a $row
1164 set vtokmod
($v) [lindex
$varctok($v) $p]
1166 set vrowmod
($v) $row
1167 if {[info exists currentid
]} {
1168 set selectedline
[rowofcommit
$currentid]
1172 # Test whether view $v contains commit $id
1173 proc commitinview
{id v
} {
1176 return [info exists varcid
($v,$id)]
1179 # Return the row number for commit $id in the current view
1180 proc rowofcommit
{id
} {
1181 global varcid varccommits varcrow curview cached_commitrow
1182 global varctok vtokmod
1185 if {![info exists varcid
($v,$id)]} {
1186 puts
"oops rowofcommit no arc for [shortids $id]"
1189 set a
$varcid($v,$id)
1190 if {[string compare
[lindex
$varctok($v) $a] $vtokmod($v)] >= 0} {
1193 if {[info exists cached_commitrow
($id)]} {
1194 return $cached_commitrow($id)
1196 set i
[lsearch
-exact $varccommits($v,$a) $id]
1198 puts
"oops didn't find commit [shortids $id] in arc $a"
1201 incr i
[lindex
$varcrow($v) $a]
1202 set cached_commitrow
($id) $i
1206 # Returns 1 if a is on an earlier row than b, otherwise 0
1207 proc comes_before
{a b
} {
1208 global varcid varctok curview
1211 if {$a eq
$b ||
![info exists varcid
($v,$a)] || \
1212 ![info exists varcid
($v,$b)]} {
1215 if {$varcid($v,$a) != $varcid($v,$b)} {
1216 return [expr {[string compare
[lindex
$varctok($v) $varcid($v,$a)] \
1217 [lindex
$varctok($v) $varcid($v,$b)]] < 0}]
1219 return [expr {[rowofcommit
$a] < [rowofcommit
$b]}]
1222 proc bsearch
{l elt
} {
1223 if {[llength
$l] == 0 ||
$elt <= [lindex
$l 0]} {
1228 while {$hi - $lo > 1} {
1229 set mid
[expr {int
(($lo + $hi) / 2)}]
1230 set t
[lindex
$l $mid]
1233 } elseif
{$elt > $t} {
1242 # Make sure rows $start..$end-1 are valid in displayorder and parentlist
1243 proc make_disporder
{start end
} {
1244 global vrownum curview commitidx displayorder parentlist
1245 global varccommits varcorder parents vrowmod varcrow
1246 global d_valid_start d_valid_end
1248 if {$end > $vrowmod($curview)} {
1249 update_arcrows
$curview
1251 set ai
[bsearch
$vrownum($curview) $start]
1252 set start
[lindex
$vrownum($curview) $ai]
1253 set narc
[llength
$vrownum($curview)]
1254 for {set r
$start} {$ai < $narc && $r < $end} {incr ai
} {
1255 set a
[lindex
$varcorder($curview) $ai]
1256 set l
[llength
$displayorder]
1257 set al
[llength
$varccommits($curview,$a)]
1258 if {$l < $r + $al} {
1260 set pad
[ntimes
[expr {$r - $l}] {}]
1261 set displayorder
[concat
$displayorder $pad]
1262 set parentlist
[concat
$parentlist $pad]
1263 } elseif
{$l > $r} {
1264 set displayorder
[lrange
$displayorder 0 [expr {$r - 1}]]
1265 set parentlist
[lrange
$parentlist 0 [expr {$r - 1}]]
1267 foreach id
$varccommits($curview,$a) {
1268 lappend displayorder
$id
1269 lappend parentlist
$parents($curview,$id)
1271 } elseif
{[lindex
$displayorder [expr {$r + $al - 1}]] eq
{}} {
1273 foreach id
$varccommits($curview,$a) {
1274 lset displayorder
$i $id
1275 lset parentlist
$i $parents($curview,$id)
1283 proc commitonrow
{row
} {
1286 set id
[lindex
$displayorder $row]
1288 make_disporder
$row [expr {$row + 1}]
1289 set id
[lindex
$displayorder $row]
1294 proc closevarcs
{v
} {
1295 global varctok varccommits varcid parents children
1296 global cmitlisted commitidx vtokmod
1298 set missing_parents
0
1300 set narcs
[llength
$varctok($v)]
1301 for {set a
1} {$a < $narcs} {incr a
} {
1302 set id
[lindex
$varccommits($v,$a) end
]
1303 foreach p
$parents($v,$id) {
1304 if {[info exists varcid
($v,$p)]} continue
1305 # add p as a new commit
1306 incr missing_parents
1307 set cmitlisted
($v,$p) 0
1308 set parents
($v,$p) {}
1309 if {[llength
$children($v,$p)] == 1 &&
1310 [llength
$parents($v,$id)] == 1} {
1313 set b
[newvarc
$v $p]
1315 set varcid
($v,$p) $b
1316 if {[string compare
[lindex
$varctok($v) $b] $vtokmod($v)] < 0} {
1319 lappend varccommits
($v,$b) $p
1321 set scripts
[check_interest
$p $scripts]
1324 if {$missing_parents > 0} {
1325 foreach s
$scripts {
1331 # Use $rwid as a substitute for $id, i.e. reparent $id's children to $rwid
1332 # Assumes we already have an arc for $rwid.
1333 proc rewrite_commit
{v id rwid
} {
1334 global children parents varcid varctok vtokmod varccommits
1336 foreach ch
$children($v,$id) {
1337 # make $rwid be $ch's parent in place of $id
1338 set i
[lsearch
-exact $parents($v,$ch) $id]
1340 puts
"oops rewrite_commit didn't find $id in parent list for $ch"
1342 set parents
($v,$ch) [lreplace
$parents($v,$ch) $i $i $rwid]
1343 # add $ch to $rwid's children and sort the list if necessary
1344 if {[llength
[lappend children
($v,$rwid) $ch]] > 1} {
1345 set children
($v,$rwid) [lsort
-command [list vtokcmp
$v] \
1346 $children($v,$rwid)]
1348 # fix the graph after joining $id to $rwid
1349 set a
$varcid($v,$ch)
1350 fix_reversal
$rwid $a $v
1351 # parentlist is wrong for the last element of arc $a
1352 # even if displayorder is right, hence the 3rd arg here
1353 modify_arc
$v $a [expr {[llength
$varccommits($v,$a)] - 1}]
1357 # Mechanism for registering a command to be executed when we come
1358 # across a particular commit. To handle the case when only the
1359 # prefix of the commit is known, the commitinterest array is now
1360 # indexed by the first 4 characters of the ID. Each element is a
1361 # list of id, cmd pairs.
1362 proc interestedin
{id cmd
} {
1363 global commitinterest
1365 lappend commitinterest
([string range
$id 0 3]) $id $cmd
1368 proc check_interest
{id scripts
} {
1369 global commitinterest
1371 set prefix
[string range
$id 0 3]
1372 if {[info exists commitinterest
($prefix)]} {
1374 foreach
{i
script} $commitinterest($prefix) {
1375 if {[string match
"$i*" $id]} {
1376 lappend scripts
[string map
[list
"%I" $id "%P" $i] $script]
1378 lappend newlist
$i $script
1381 if {$newlist ne
{}} {
1382 set commitinterest
($prefix) $newlist
1384 unset commitinterest
($prefix)
1390 proc getcommitlines
{fd inst view updating
} {
1391 global cmitlisted leftover
1392 global commitidx commitdata vdatemode
1393 global parents children curview hlview
1394 global idpending ordertok
1395 global varccommits varcid varctok vtokmod vfilelimit
1397 set stuff
[read $fd 500000]
1398 # git log doesn't terminate the last commit with a null...
1399 if {$stuff == {} && $leftover($inst) ne
{} && [eof
$fd]} {
1406 global commfd viewcomplete viewactive viewname
1407 global viewinstances
1409 set i
[lsearch
-exact $viewinstances($view) $inst]
1411 set viewinstances
($view) [lreplace
$viewinstances($view) $i $i]
1413 # set it blocking so we wait for the process to terminate
1414 fconfigure
$fd -blocking 1
1415 if {[catch
{close
$fd} err
]} {
1417 if {$view != $curview} {
1418 set fv
" for the \"$viewname($view)\" view"
1420 if {[string range
$err 0 4] == "usage"} {
1421 set err
"Gitk: error reading commits$fv:\
1422 bad arguments to git log."
1423 if {$viewname($view) eq
"Command line"} {
1425 " (Note: arguments to gitk are passed to git log\
1426 to allow selection of commits to be displayed.)"
1429 set err
"Error reading commits$fv: $err"
1433 if {[incr viewactive
($view) -1] <= 0} {
1434 set viewcomplete
($view) 1
1435 # Check if we have seen any ids listed as parents that haven't
1436 # appeared in the list
1440 if {$view == $curview} {
1449 set i
[string first
"\0" $stuff $start]
1451 append leftover
($inst) [string range
$stuff $start end
]
1455 set cmit
$leftover($inst)
1456 append cmit
[string range
$stuff 0 [expr {$i - 1}]]
1457 set leftover
($inst) {}
1459 set cmit
[string range
$stuff $start [expr {$i - 1}]]
1461 set start
[expr {$i + 1}]
1462 set j
[string first
"\n" $cmit]
1465 if {$j >= 0 && [string match
"commit *" $cmit]} {
1466 set ids
[string range
$cmit 7 [expr {$j - 1}]]
1467 if {[string match
{[-^
<>]*} $ids]} {
1468 switch
-- [string index
$ids 0] {
1474 set ids
[string range
$ids 1 end
]
1478 if {[string length
$id] != 40} {
1486 if {[string length
$shortcmit] > 80} {
1487 set shortcmit
"[string range $shortcmit 0 80]..."
1489 error_popup
"[mc "Can
't parse git log output:"] {$shortcmit}"
1492 set id [lindex $ids 0]
1495 if {!$listed && $updating && ![info exists varcid($vid)] &&
1496 $vfilelimit($view) ne {}} {
1497 # git log doesn't rewrite parents
for unlisted commits
1498 # when doing path limiting, so work around that here
1499 # by working out the rewritten parent with git rev-list
1500 # and if we already know about it, using the rewritten
1501 # parent as a substitute parent for $id's children.
1503 set rwid
[exec git rev-list
--first-parent --max-count=1 \
1504 $id -- $vfilelimit($view)]
1506 if {$rwid ne
{} && [info exists varcid
($view,$rwid)]} {
1507 # use $rwid in place of $id
1508 rewrite_commit
$view $id $rwid
1515 if {[info exists varcid
($vid)]} {
1516 if {$cmitlisted($vid) ||
!$listed} continue
1520 set olds
[lrange
$ids 1 end
]
1524 set commitdata
($id) [string range
$cmit [expr {$j + 1}] end
]
1525 set cmitlisted
($vid) $listed
1526 set parents
($vid) $olds
1527 if {![info exists children
($vid)]} {
1528 set children
($vid) {}
1529 } elseif
{$a == 0 && [llength
$children($vid)] == 1} {
1530 set k
[lindex
$children($vid) 0]
1531 if {[llength
$parents($view,$k)] == 1 &&
1532 (!$vdatemode($view) ||
1533 $varcid($view,$k) == [llength
$varctok($view)] - 1)} {
1534 set a
$varcid($view,$k)
1539 set a
[newvarc
$view $id]
1541 if {[string compare
[lindex
$varctok($view) $a] $vtokmod($view)] < 0} {
1544 if {![info exists varcid
($vid)]} {
1546 lappend varccommits
($view,$a) $id
1547 incr commitidx
($view)
1552 if {$i == 0 ||
[lsearch
-exact $olds $p] >= $i} {
1554 if {[llength
[lappend children
($vp) $id]] > 1 &&
1555 [vtokcmp
$view [lindex
$children($vp) end-1
] $id] > 0} {
1556 set children
($vp) [lsort
-command [list vtokcmp
$view] \
1558 catch
{unset ordertok
}
1560 if {[info exists varcid
($view,$p)]} {
1561 fix_reversal
$p $a $view
1567 set scripts
[check_interest
$id $scripts]
1571 global numcommits hlview
1573 if {$view == $curview} {
1574 set numcommits
$commitidx($view)
1577 if {[info exists hlview
] && $view == $hlview} {
1578 # we never actually get here...
1581 foreach s
$scripts {
1588 proc chewcommits
{} {
1589 global curview hlview viewcomplete
1590 global pending_select
1593 if {$viewcomplete($curview)} {
1594 global commitidx varctok
1595 global numcommits startmsecs
1597 if {[info exists pending_select
]} {
1599 reset_pending_select
{}
1601 if {[commitinview
$pending_select $curview]} {
1602 selectline
[rowofcommit
$pending_select] 1
1604 set row
[first_real_row
]
1608 if {$commitidx($curview) > 0} {
1609 #set ms [expr {[clock clicks -milliseconds] - $startmsecs}]
1610 #puts "overall $ms ms for $numcommits commits"
1611 #puts "[llength $varctok($view)] arcs, $commitidx($view) commits"
1613 show_status
[mc
"No commits selected"]
1620 proc do_readcommit
{id
} {
1623 # Invoke git-log to handle automatic encoding conversion
1624 set fd
[open
[concat | git log
--no-color --pretty=raw
-1 $id] r
]
1625 # Read the results using i18n.logoutputencoding
1626 fconfigure
$fd -translation lf
-eofchar {}
1627 if {$tclencoding != {}} {
1628 fconfigure
$fd -encoding $tclencoding
1630 set contents
[read $fd]
1632 # Remove the heading line
1633 regsub
{^commit
[0-9a-f]+\n} $contents {} contents
1638 proc readcommit
{id
} {
1639 if {[catch
{set contents
[do_readcommit
$id]}]} return
1640 parsecommit
$id $contents 1
1643 proc parsecommit
{id contents listed
} {
1644 global commitinfo cdate
1653 set hdrend
[string first
"\n\n" $contents]
1655 # should never happen...
1656 set hdrend
[string length
$contents]
1658 set header
[string range
$contents 0 [expr {$hdrend - 1}]]
1659 set comment
[string range
$contents [expr {$hdrend + 2}] end
]
1660 foreach line
[split $header "\n"] {
1661 set line
[split $line " "]
1662 set tag
[lindex
$line 0]
1663 if {$tag == "author"} {
1664 set audate
[lindex
$line end-1
]
1665 set auname
[join [lrange
$line 1 end-2
] " "]
1666 } elseif
{$tag == "committer"} {
1667 set comdate
[lindex
$line end-1
]
1668 set comname
[join [lrange
$line 1 end-2
] " "]
1672 # take the first non-blank line of the comment as the headline
1673 set headline
[string trimleft
$comment]
1674 set i
[string first
"\n" $headline]
1676 set headline
[string range
$headline 0 $i]
1678 set headline
[string trimright
$headline]
1679 set i
[string first
"\r" $headline]
1681 set headline
[string trimright
[string range
$headline 0 $i]]
1684 # git log indents the comment by 4 spaces;
1685 # if we got this via git cat-file, add the indentation
1687 foreach line
[split $comment "\n"] {
1688 append newcomment
" "
1689 append newcomment
$line
1690 append newcomment
"\n"
1692 set comment
$newcomment
1694 if {$comdate != {}} {
1695 set cdate
($id) $comdate
1697 set commitinfo
($id) [list
$headline $auname $audate \
1698 $comname $comdate $comment]
1701 proc getcommit
{id
} {
1702 global commitdata commitinfo
1704 if {[info exists commitdata
($id)]} {
1705 parsecommit
$id $commitdata($id) 1
1708 if {![info exists commitinfo
($id)]} {
1709 set commitinfo
($id) [list
[mc
"No commit information available"]]
1715 # Expand an abbreviated commit ID to a list of full 40-char IDs that match
1716 # and are present in the current view.
1717 # This is fairly slow...
1718 proc longid
{prefix
} {
1719 global varcid curview
1722 foreach match
[array names varcid
"$curview,$prefix*"] {
1723 lappend ids
[lindex
[split $match ","] 1]
1729 global tagids idtags headids idheads tagobjid
1730 global otherrefids idotherrefs mainhead mainheadid
1731 global selecthead selectheadid
1734 foreach v
{tagids idtags headids idheads otherrefids idotherrefs
} {
1737 set refd
[open
[list | git show-ref
-d] r
]
1738 while {[gets
$refd line
] >= 0} {
1739 if {[string index
$line 40] ne
" "} continue
1740 set id
[string range
$line 0 39]
1741 set ref
[string range
$line 41 end
]
1742 if {![string match
"refs/*" $ref]} continue
1743 set name
[string range
$ref 5 end
]
1744 if {[string match
"remotes/*" $name]} {
1745 if {![string match
"*/HEAD" $name] && !$hideremotes} {
1746 set headids
($name) $id
1747 lappend idheads
($id) $name
1749 } elseif
{[string match
"heads/*" $name]} {
1750 set name
[string range
$name 6 end
]
1751 set headids
($name) $id
1752 lappend idheads
($id) $name
1753 } elseif
{[string match
"tags/*" $name]} {
1754 # this lets refs/tags/foo^{} overwrite refs/tags/foo,
1755 # which is what we want since the former is the commit ID
1756 set name
[string range
$name 5 end
]
1757 if {[string match
"*^{}" $name]} {
1758 set name
[string range
$name 0 end-3
]
1760 set tagobjid
($name) $id
1762 set tagids
($name) $id
1763 lappend idtags
($id) $name
1765 set otherrefids
($name) $id
1766 lappend idotherrefs
($id) $name
1773 set mainheadid
[exec git rev-parse HEAD
]
1774 set thehead
[exec git symbolic-ref HEAD
]
1775 if {[string match
"refs/heads/*" $thehead]} {
1776 set mainhead
[string range
$thehead 11 end
]
1780 if {$selecthead ne
{}} {
1782 set selectheadid
[exec git rev-parse
--verify $selecthead]
1787 # skip over fake commits
1788 proc first_real_row
{} {
1789 global nullid nullid2 numcommits
1791 for {set row
0} {$row < $numcommits} {incr row
} {
1792 set id
[commitonrow
$row]
1793 if {$id ne
$nullid && $id ne
$nullid2} {
1800 # update things for a head moved to a child of its previous location
1801 proc movehead
{id name
} {
1802 global headids idheads
1804 removehead
$headids($name) $name
1805 set headids
($name) $id
1806 lappend idheads
($id) $name
1809 # update things when a head has been removed
1810 proc removehead
{id name
} {
1811 global headids idheads
1813 if {$idheads($id) eq
$name} {
1816 set i
[lsearch
-exact $idheads($id) $name]
1818 set idheads
($id) [lreplace
$idheads($id) $i $i]
1821 unset headids
($name)
1824 proc ttk_toplevel
{w args
} {
1826 eval [linsert
$args 0 ::toplevel
$w]
1828 place
[ttk
::frame
$w._toplevel_background
] -x 0 -y 0 -relwidth 1 -relheight 1
1833 proc make_transient
{window origin
} {
1836 # In MacOS Tk 8.4 transient appears to work by setting
1837 # overrideredirect, which is utterly useless, since the
1838 # windows get no border, and are not even kept above
1840 if {!$have_tk85 && [tk windowingsystem
] eq
{aqua
}} return
1842 wm transient
$window $origin
1844 # Windows fails to place transient windows normally, so
1845 # schedule a callback to center them on the parent.
1846 if {[tk windowingsystem
] eq
{win32
}} {
1847 after idle
[list tk
::PlaceWindow
$window widget
$origin]
1851 proc show_error
{w top msg
{mc mc
}} {
1853 if {![info exists NS
]} {set NS
""}
1854 if {[wm state
$top] eq
"withdrawn"} { wm deiconify
$top }
1855 message
$w.m
-text $msg -justify center
-aspect 400
1856 pack
$w.m
-side top
-fill x
-padx 20 -pady 20
1857 ${NS}::button
$w.ok
-default active
-text [$mc OK
] -command "destroy $top"
1858 pack
$w.ok
-side bottom
-fill x
1859 bind $top <Visibility
> "grab $top; focus $top"
1860 bind $top <Key-Return
> "destroy $top"
1861 bind $top <Key-space
> "destroy $top"
1862 bind $top <Key-Escape
> "destroy $top"
1866 proc error_popup
{msg
{owner .
}} {
1867 if {[tk windowingsystem
] eq
"win32"} {
1868 tk_messageBox
-icon error
-type ok
-title [wm title .
] \
1869 -parent $owner -message $msg
1873 make_transient
$w $owner
1874 show_error
$w $w $msg
1878 proc confirm_popup
{msg
{owner .
}} {
1879 global confirm_ok NS
1883 make_transient
$w $owner
1884 message
$w.m
-text $msg -justify center
-aspect 400
1885 pack
$w.m
-side top
-fill x
-padx 20 -pady 20
1886 ${NS}::button
$w.ok
-text [mc OK
] -command "set confirm_ok 1; destroy $w"
1887 pack
$w.ok
-side left
-fill x
1888 ${NS}::button
$w.cancel
-text [mc Cancel
] -command "destroy $w"
1889 pack
$w.cancel
-side right
-fill x
1890 bind $w <Visibility
> "grab $w; focus $w"
1891 bind $w <Key-Return
> "set confirm_ok 1; destroy $w"
1892 bind $w <Key-space
> "set confirm_ok 1; destroy $w"
1893 bind $w <Key-Escape
> "destroy $w"
1894 tk
::PlaceWindow
$w widget
$owner
1899 proc setoptions
{} {
1900 if {[tk windowingsystem
] ne
"win32"} {
1901 option add
*Panedwindow.showHandle
1 startupFile
1902 option add
*Panedwindow.sashRelief raised startupFile
1903 if {[tk windowingsystem
] ne
"aqua"} {
1904 option add
*Menu.font uifont startupFile
1907 option add
*Menu.TearOff
0 startupFile
1909 option add
*Button.font uifont startupFile
1910 option add
*Checkbutton.font uifont startupFile
1911 option add
*Radiobutton.font uifont startupFile
1912 option add
*Menubutton.font uifont startupFile
1913 option add
*Label.font uifont startupFile
1914 option add
*Message.font uifont startupFile
1915 option add
*Entry.font textfont startupFile
1916 option add
*Text.font textfont startupFile
1917 option add
*Labelframe.font uifont startupFile
1918 option add
*Spinbox.font textfont startupFile
1919 option add
*Listbox.font mainfont startupFile
1922 # Make a menu and submenus.
1923 # m is the window name for the menu, items is the list of menu items to add.
1924 # Each item is a list {mc label type description options...}
1925 # mc is ignored; it's so we can put mc there to alert xgettext
1926 # label is the string that appears in the menu
1927 # type is cascade, command or radiobutton (should add checkbutton)
1928 # description depends on type; it's the sublist for cascade, the
1929 # command to invoke for command, or {variable value} for radiobutton
1930 proc makemenu
{m items
} {
1932 if {[tk windowingsystem
] eq
{aqua
}} {
1938 set name
[mc
[lindex
$i 1]]
1939 set type [lindex
$i 2]
1940 set thing
[lindex
$i 3]
1941 set params
[list
$type]
1943 set u
[string first
"&" [string map
{&& x
} $name]]
1944 lappend params
-label [string map
{&& & & {}} $name]
1946 lappend params
-underline $u
1951 set submenu
[string tolower
[string map
{& ""} [lindex
$i 1]]]
1952 lappend params
-menu $m.
$submenu
1955 lappend params
-command $thing
1958 lappend params
-variable [lindex
$thing 0] \
1959 -value [lindex
$thing 1]
1962 set tail [lrange
$i 4 end
]
1963 regsub
-all {\yMeta1\y
} $tail $Meta1 tail
1964 eval $m add
$params $tail
1965 if {$type eq
"cascade"} {
1966 makemenu
$m.
$submenu $thing
1971 # translate string and remove ampersands
1973 return [string map
{&& & & {}} [mc
$str]]
1976 proc makedroplist
{w varname args
} {
1980 foreach label
$args {
1981 set cx
[string length
$label]
1982 if {$cx > $width} {set width
$cx}
1984 set gm
[ttk
::combobox
$w -width $width -state readonly\
1985 -textvariable $varname -values $args]
1987 set gm
[eval [linsert
$args 0 tk_optionMenu
$w $varname]]
1992 proc makewindow
{} {
1993 global canv canv2 canv3 linespc charspc ctext cflist cscroll
1995 global findtype findtypemenu findloc findstring fstring geometry
1996 global entries sha1entry sha1string sha1but
1997 global diffcontextstring diffcontext
1999 global maincursor textcursor curtextcursor
2000 global rowctxmenu fakerowmenu mergemax wrapcomment
2001 global highlight_files gdttype
2002 global searchstring sstring
2003 global bgcolor fgcolor bglist fglist diffcolors selectbgcolor
2004 global headctxmenu progresscanv progressitem progresscoords statusw
2005 global fprogitem fprogcoord lastprogupdate progupdatepending
2006 global rprogitem rprogcoord rownumsel numcommits
2007 global have_tk85 use_ttk NS
2011 # The "mc" arguments here are purely so that xgettext
2012 # sees the following string as needing to be translated
2015 {mc
"Update" command updatecommits
-accelerator F5
}
2016 {mc
"Reload" command reloadcommits
-accelerator Meta1-F5
}
2017 {mc
"Reread references" command rereadrefs
}
2018 {mc
"List references" command showrefs
-accelerator F2
}
2020 {mc
"Start git gui" command {exec git gui
&}}
2022 {mc
"Quit" command doquit
-accelerator Meta1-Q
}
2026 {mc
"Preferences" command doprefs
}
2030 {mc
"New view..." command {newview
0} -accelerator Shift-F4
}
2031 {mc
"Edit view..." command editview
-state disabled
-accelerator F4
}
2032 {mc
"Delete view" command delview
-state disabled
}
2034 {mc
"All files" radiobutton
{selectedview
0} -command {showview
0}}
2036 if {[tk windowingsystem
] ne
"aqua"} {
2039 {mc
"About gitk" command about
}
2040 {mc
"Key bindings" command keys
}
2042 set bar
[list
$file $edit $view $help]
2044 proc
::tk
::mac
::ShowPreferences
{} {doprefs
}
2045 proc
::tk
::mac
::Quit
{} {doquit
}
2046 lset
file end
[lreplace
[lindex
$file end
] end-1 end
]
2048 xx
"Apple" cascade
{
2049 {mc
"About gitk" command about
}
2054 {mc
"Key bindings" command keys
}
2056 set bar
[list
$apple $file $view $help]
2059 . configure
-menu .bar
2062 # cover the non-themed toplevel with a themed frame.
2063 place
[ttk
::frame ._main_background
] -x 0 -y 0 -relwidth 1 -relheight 1
2066 # the gui has upper and lower half, parts of a paned window.
2067 ${NS}::panedwindow .ctop
-orient vertical
2069 # possibly use assumed geometry
2070 if {![info exists geometry
(pwsash0
)]} {
2071 set geometry
(topheight
) [expr {15 * $linespc}]
2072 set geometry
(topwidth
) [expr {80 * $charspc}]
2073 set geometry
(botheight
) [expr {15 * $linespc}]
2074 set geometry
(botwidth
) [expr {50 * $charspc}]
2075 set geometry
(pwsash0
) [list
[expr {40 * $charspc}] 2]
2076 set geometry
(pwsash1
) [list
[expr {60 * $charspc}] 2]
2079 # the upper half will have a paned window, a scroll bar to the right, and some stuff below
2080 ${NS}::frame .tf
-height $geometry(topheight
) -width $geometry(topwidth
)
2081 ${NS}::frame .tf.histframe
2082 ${NS}::panedwindow .tf.histframe.pwclist
-orient horizontal
2084 .tf.histframe.pwclist configure
-sashpad 0 -handlesize 4
2087 # create three canvases
2088 set cscroll .tf.histframe.csb
2089 set canv .tf.histframe.pwclist.canv
2091 -selectbackground $selectbgcolor \
2092 -background $bgcolor -bd 0 \
2093 -yscrollincr $linespc -yscrollcommand "scrollcanv $cscroll"
2094 .tf.histframe.pwclist add
$canv
2095 set canv2 .tf.histframe.pwclist.canv2
2097 -selectbackground $selectbgcolor \
2098 -background $bgcolor -bd 0 -yscrollincr $linespc
2099 .tf.histframe.pwclist add
$canv2
2100 set canv3 .tf.histframe.pwclist.canv3
2102 -selectbackground $selectbgcolor \
2103 -background $bgcolor -bd 0 -yscrollincr $linespc
2104 .tf.histframe.pwclist add
$canv3
2106 bind .tf.histframe.pwclist
<Map
> {
2108 .tf.histframe.pwclist sashpos
1 [lindex $
::geometry
(pwsash1
) 0]
2109 .tf.histframe.pwclist sashpos
0 [lindex $
::geometry
(pwsash0
) 0]
2112 eval .tf.histframe.pwclist sash place
0 $geometry(pwsash0
)
2113 eval .tf.histframe.pwclist sash place
1 $geometry(pwsash1
)
2116 # a scroll bar to rule them
2117 ${NS}::scrollbar
$cscroll -command {allcanvs yview
}
2118 if {!$use_ttk} {$cscroll configure
-highlightthickness 0}
2119 pack
$cscroll -side right
-fill y
2120 bind .tf.histframe.pwclist
<Configure
> {resizeclistpanes
%W
%w
}
2121 lappend bglist
$canv $canv2 $canv3
2122 pack .tf.histframe.pwclist
-fill both
-expand 1 -side left
2124 # we have two button bars at bottom of top frame. Bar 1
2125 ${NS}::frame .tf.bar
2126 ${NS}::frame .tf.lbar
-height 15
2128 set sha1entry .tf.bar.sha1
2129 set entries
$sha1entry
2130 set sha1but .tf.bar.sha1label
2131 button
$sha1but -text "[mc "SHA1 ID
:"] " -state disabled
-relief flat \
2132 -command gotocommit
-width 8
2133 $sha1but conf
-disabledforeground [$sha1but cget
-foreground]
2134 pack .tf.bar.sha1label
-side left
2135 ${NS}::entry
$sha1entry -width 40 -font textfont
-textvariable sha1string
2136 trace add variable sha1string
write sha1change
2137 pack
$sha1entry -side left
-pady 2
2139 image create bitmap bm-left
-data {
2140 #define left_width 16
2141 #define left_height 16
2142 static unsigned char left_bits
[] = {
2143 0x00, 0x00, 0xc0, 0x01, 0xe0, 0x00, 0x70, 0x00, 0x38, 0x00, 0x1c, 0x00,
2144 0x0e, 0x00, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0x0e, 0x00, 0x1c, 0x00,
2145 0x38, 0x00, 0x70, 0x00, 0xe0, 0x00, 0xc0, 0x01};
2147 image create bitmap bm-right
-data {
2148 #define right_width 16
2149 #define right_height 16
2150 static unsigned char right_bits
[] = {
2151 0x00, 0x00, 0xc0, 0x01, 0x80, 0x03, 0x00, 0x07, 0x00, 0x0e, 0x00, 0x1c,
2152 0x00, 0x38, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0x00, 0x38, 0x00, 0x1c,
2153 0x00, 0x0e, 0x00, 0x07, 0x80, 0x03, 0xc0, 0x01};
2155 ${NS}::button .tf.bar.leftbut
-image bm-left
-command goback \
2156 -state disabled
-width 26
2157 pack .tf.bar.leftbut
-side left
-fill y
2158 ${NS}::button .tf.bar.rightbut
-image bm-right
-command goforw \
2159 -state disabled
-width 26
2160 pack .tf.bar.rightbut
-side left
-fill y
2162 ${NS}::label .tf.bar.rowlabel
-text [mc
"Row"]
2164 ${NS}::label .tf.bar.rownum
-width 7 -textvariable rownumsel \
2165 -relief sunken
-anchor e
2166 ${NS}::label .tf.bar.rowlabel2
-text "/"
2167 ${NS}::label .tf.bar.numcommits
-width 7 -textvariable numcommits \
2168 -relief sunken
-anchor e
2169 pack .tf.bar.rowlabel .tf.bar.rownum .tf.bar.rowlabel2 .tf.bar.numcommits \
2172 foreach w
{rownum numcommits
} {.tf.bar.
$w configure
-font textfont
}
2175 trace add variable selectedline
write selectedline_change
2177 # Status label and progress bar
2178 set statusw .tf.bar.status
2179 ${NS}::label
$statusw -width 15 -relief sunken
2180 pack
$statusw -side left
-padx 5
2182 set progresscanv
[ttk
::progressbar .tf.bar.progress
]
2184 set h
[expr {[font metrics uifont
-linespace] + 2}]
2185 set progresscanv .tf.bar.progress
2186 canvas
$progresscanv -relief sunken
-height $h -borderwidth 2
2187 set progressitem
[$progresscanv create rect
-1 0 0 $h -fill green
]
2188 set fprogitem
[$progresscanv create rect
-1 0 0 $h -fill yellow
]
2189 set rprogitem
[$progresscanv create rect
-1 0 0 $h -fill red
]
2191 pack
$progresscanv -side right
-expand 1 -fill x
-padx {0 2}
2192 set progresscoords
{0 0}
2195 bind $progresscanv <Configure
> adjustprogress
2196 set lastprogupdate
[clock clicks
-milliseconds]
2197 set progupdatepending
0
2199 # build up the bottom bar of upper window
2200 ${NS}::label .tf.lbar.flabel
-text "[mc "Find
"] "
2201 ${NS}::button .tf.lbar.fnext
-text [mc
"next"] -command {dofind
1 1}
2202 ${NS}::button .tf.lbar.fprev
-text [mc
"prev"] -command {dofind
-1 1}
2203 ${NS}::label .tf.lbar.flab2
-text " [mc "commit
"] "
2204 pack .tf.lbar.flabel .tf.lbar.fnext .tf.lbar.fprev .tf.lbar.flab2 \
2206 set gdttype
[mc
"containing:"]
2207 set gm
[makedroplist .tf.lbar.gdttype gdttype \
2208 [mc
"containing:"] \
2209 [mc
"touching paths:"] \
2210 [mc
"adding/removing string:"]]
2211 trace add variable gdttype
write gdttype_change
2212 pack .tf.lbar.gdttype
-side left
-fill y
2215 set fstring .tf.lbar.findstring
2216 lappend entries
$fstring
2217 ${NS}::entry
$fstring -width 30 -textvariable findstring
2218 trace add variable findstring
write find_change
2219 set findtype
[mc
"Exact"]
2220 set findtypemenu
[makedroplist .tf.lbar.findtype \
2221 findtype
[mc
"Exact"] [mc
"IgnCase"] [mc
"Regexp"]]
2222 trace add variable findtype
write findcom_change
2223 set findloc
[mc
"All fields"]
2224 makedroplist .tf.lbar.findloc findloc
[mc
"All fields"] [mc
"Headline"] \
2225 [mc
"Comments"] [mc
"Author"] [mc
"Committer"]
2226 trace add variable findloc
write find_change
2227 pack .tf.lbar.findloc
-side right
2228 pack .tf.lbar.findtype
-side right
2229 pack
$fstring -side left
-expand 1 -fill x
2231 # Finish putting the upper half of the viewer together
2232 pack .tf.lbar
-in .tf
-side bottom
-fill x
2233 pack .tf.bar
-in .tf
-side bottom
-fill x
2234 pack .tf.histframe
-fill both
-side top
-expand 1
2237 .ctop paneconfigure .tf
-height $geometry(topheight
)
2238 .ctop paneconfigure .tf
-width $geometry(topwidth
)
2241 # now build up the bottom
2242 ${NS}::panedwindow .pwbottom
-orient horizontal
2244 # lower left, a text box over search bar, scroll bar to the right
2245 # if we know window height, then that will set the lower text height, otherwise
2246 # we set lower text height which will drive window height
2247 if {[info exists geometry
(main
)]} {
2248 ${NS}::frame .bleft
-width $geometry(botwidth
)
2250 ${NS}::frame .bleft
-width $geometry(botwidth
) -height $geometry(botheight
)
2252 ${NS}::frame .bleft.top
2253 ${NS}::frame .bleft.mid
2254 ${NS}::frame .bleft.bottom
2256 ${NS}::button .bleft.top.search
-text [mc
"Search"] -command dosearch
2257 pack .bleft.top.search
-side left
-padx 5
2258 set sstring .bleft.top.sstring
2260 ${NS}::entry
$sstring -width 20 -textvariable searchstring
2261 lappend entries
$sstring
2262 trace add variable searchstring
write incrsearch
2263 pack
$sstring -side left
-expand 1 -fill x
2264 ${NS}::radiobutton .bleft.mid.
diff -text [mc
"Diff"] \
2265 -command changediffdisp
-variable diffelide
-value {0 0}
2266 ${NS}::radiobutton .bleft.mid.old
-text [mc
"Old version"] \
2267 -command changediffdisp
-variable diffelide
-value {0 1}
2268 ${NS}::radiobutton .bleft.mid.new
-text [mc
"New version"] \
2269 -command changediffdisp
-variable diffelide
-value {1 0}
2270 ${NS}::label .bleft.mid.labeldiffcontext
-text " [mc "Lines of context
"]: "
2271 pack .bleft.mid.
diff .bleft.mid.old .bleft.mid.new
-side left
2272 spinbox .bleft.mid.diffcontext
-width 5 \
2273 -from 0 -increment 1 -to 10000000 \
2274 -validate all
-validatecommand "diffcontextvalidate %P" \
2275 -textvariable diffcontextstring
2276 .bleft.mid.diffcontext
set $diffcontext
2277 trace add variable diffcontextstring
write diffcontextchange
2278 lappend entries .bleft.mid.diffcontext
2279 pack .bleft.mid.labeldiffcontext .bleft.mid.diffcontext
-side left
2280 ${NS}::checkbutton .bleft.mid.ignspace
-text [mc
"Ignore space change"] \
2281 -command changeignorespace
-variable ignorespace
2282 pack .bleft.mid.ignspace
-side left
-padx 5
2284 set worddiff
[mc
"Line diff"]
2285 if {[package vcompare
$git_version "1.7.2"] >= 0} {
2286 makedroplist .bleft.mid.worddiff worddiff
[mc
"Line diff"] \
2287 [mc
"Markup words"] [mc
"Color words"]
2288 trace add variable worddiff
write changeworddiff
2289 pack .bleft.mid.worddiff
-side left
-padx 5
2292 set ctext .bleft.bottom.ctext
2293 text
$ctext -background $bgcolor -foreground $fgcolor \
2294 -state disabled
-font textfont \
2295 -yscrollcommand scrolltext
-wrap none \
2296 -xscrollcommand ".bleft.bottom.sbhorizontal set"
2298 $ctext conf
-tabstyle wordprocessor
2300 ${NS}::scrollbar .bleft.bottom.sb
-command "$ctext yview"
2301 ${NS}::scrollbar .bleft.bottom.sbhorizontal
-command "$ctext xview" -orient h
2302 pack .bleft.top
-side top
-fill x
2303 pack .bleft.mid
-side top
-fill x
2304 grid
$ctext .bleft.bottom.sb
-sticky nsew
2305 grid .bleft.bottom.sbhorizontal
-sticky ew
2306 grid columnconfigure .bleft.bottom
0 -weight 1
2307 grid rowconfigure .bleft.bottom
0 -weight 1
2308 grid rowconfigure .bleft.bottom
1 -weight 0
2309 pack .bleft.bottom
-side top
-fill both
-expand 1
2310 lappend bglist
$ctext
2311 lappend fglist
$ctext
2313 $ctext tag conf comment
-wrap $wrapcomment
2314 $ctext tag conf filesep
-font textfontbold
-back "#aaaaaa"
2315 $ctext tag conf hunksep
-fore [lindex
$diffcolors 2]
2316 $ctext tag conf d0
-fore [lindex
$diffcolors 0]
2317 $ctext tag conf dresult
-fore [lindex
$diffcolors 1]
2318 $ctext tag conf m0
-fore red
2319 $ctext tag conf m1
-fore blue
2320 $ctext tag conf m2
-fore green
2321 $ctext tag conf m3
-fore purple
2322 $ctext tag conf
m4 -fore brown
2323 $ctext tag conf m5
-fore "#009090"
2324 $ctext tag conf m6
-fore magenta
2325 $ctext tag conf m7
-fore "#808000"
2326 $ctext tag conf m8
-fore "#009000"
2327 $ctext tag conf m9
-fore "#ff0080"
2328 $ctext tag conf m10
-fore cyan
2329 $ctext tag conf m11
-fore "#b07070"
2330 $ctext tag conf m12
-fore "#70b0f0"
2331 $ctext tag conf m13
-fore "#70f0b0"
2332 $ctext tag conf m14
-fore "#f0b070"
2333 $ctext tag conf m15
-fore "#ff70b0"
2334 $ctext tag conf mmax
-fore darkgrey
2336 $ctext tag conf mresult
-font textfontbold
2337 $ctext tag conf msep
-font textfontbold
2338 $ctext tag conf found
-back yellow
2340 .pwbottom add .bleft
2342 .pwbottom paneconfigure .bleft
-width $geometry(botwidth
)
2346 ${NS}::frame .bright
2347 ${NS}::frame .bright.mode
2348 ${NS}::radiobutton .bright.mode.
patch -text [mc
"Patch"] \
2349 -command reselectline
-variable cmitmode
-value "patch"
2350 ${NS}::radiobutton .bright.mode.tree
-text [mc
"Tree"] \
2351 -command reselectline
-variable cmitmode
-value "tree"
2352 grid .bright.mode.
patch .bright.mode.tree
-sticky ew
2353 pack .bright.mode
-side top
-fill x
2354 set cflist .bright.cfiles
2355 set indent
[font measure mainfont
"nn"]
2357 -selectbackground $selectbgcolor \
2358 -background $bgcolor -foreground $fgcolor \
2360 -tabs [list
$indent [expr {2 * $indent}]] \
2361 -yscrollcommand ".bright.sb set" \
2362 -cursor [. cget
-cursor] \
2363 -spacing1 1 -spacing3 1
2364 lappend bglist
$cflist
2365 lappend fglist
$cflist
2366 ${NS}::scrollbar .bright.sb
-command "$cflist yview"
2367 pack .bright.sb
-side right
-fill y
2368 pack
$cflist -side left
-fill both
-expand 1
2369 $cflist tag configure highlight \
2370 -background [$cflist cget
-selectbackground]
2371 $cflist tag configure bold
-font mainfontbold
2373 .pwbottom add .bright
2376 # restore window width & height if known
2377 if {[info exists geometry
(main
)]} {
2378 if {[scan
$geometry(main
) "%dx%d" w h
] >= 2} {
2379 if {$w > [winfo screenwidth .
]} {
2380 set w
[winfo screenwidth .
]
2382 if {$h > [winfo screenheight .
]} {
2383 set h
[winfo screenheight .
]
2385 wm geometry .
"${w}x$h"
2389 if {[info exists geometry
(state
)] && $geometry(state
) eq
"zoomed"} {
2390 wm state .
$geometry(state
)
2393 if {[tk windowingsystem
] eq
{aqua
}} {
2404 %W sashpos
0 $
::geometry
(topheight
)
2406 bind .pwbottom
<Map
> {
2408 %W sashpos
0 $
::geometry
(botwidth
)
2412 bind .pwbottom
<Configure
> {resizecdetpanes
%W
%w
}
2413 pack .ctop
-fill both
-expand 1
2414 bindall
<1> {selcanvline
%W
%x
%y
}
2415 #bindall <B1-Motion> {selcanvline %W %x %y}
2416 if {[tk windowingsystem
] == "win32"} {
2417 bind .
<MouseWheel
> { windows_mousewheel_redirector
%W
%X
%Y
%D
}
2418 bind $ctext <MouseWheel
> { windows_mousewheel_redirector
%W
%X
%Y
%D
; break }
2420 bindall
<ButtonRelease-4
> "allcanvs yview scroll -5 units"
2421 bindall
<ButtonRelease-5
> "allcanvs yview scroll 5 units"
2422 if {[tk windowingsystem
] eq
"aqua"} {
2423 bindall
<MouseWheel
> {
2424 set delta
[expr {- (%D
)}]
2425 allcanvs yview scroll
$delta units
2427 bindall
<Shift-MouseWheel
> {
2428 set delta
[expr {- (%D
)}]
2429 $canv xview scroll
$delta units
2433 bindall
<$
::BM
> "canvscan mark %W %x %y"
2434 bindall
<B$
::BM-Motion
> "canvscan dragto %W %x %y"
2435 bind all
<$M1B-Key-w> {destroy
[winfo toplevel
%W
]}
2436 bind .
<$M1B-Key-w> doquit
2437 bindkey
<Home
> selfirstline
2438 bindkey
<End
> sellastline
2439 bind .
<Key-Up
> "selnextline -1"
2440 bind .
<Key-Down
> "selnextline 1"
2441 bind .
<Shift-Key-Up
> "dofind -1 0"
2442 bind .
<Shift-Key-Down
> "dofind 1 0"
2443 bindkey
<Key-Right
> "goforw"
2444 bindkey
<Key-Left
> "goback"
2445 bind .
<Key-Prior
> "selnextpage -1"
2446 bind .
<Key-Next
> "selnextpage 1"
2447 bind .
<$M1B-Home> "allcanvs yview moveto 0.0"
2448 bind .
<$M1B-End> "allcanvs yview moveto 1.0"
2449 bind .
<$M1B-Key-Up> "allcanvs yview scroll -1 units"
2450 bind .
<$M1B-Key-Down> "allcanvs yview scroll 1 units"
2451 bind .
<$M1B-Key-Prior> "allcanvs yview scroll -1 pages"
2452 bind .
<$M1B-Key-Next> "allcanvs yview scroll 1 pages"
2453 bindkey
<Key-Delete
> "$ctext yview scroll -1 pages"
2454 bindkey
<Key-BackSpace
> "$ctext yview scroll -1 pages"
2455 bindkey
<Key-space
> "$ctext yview scroll 1 pages"
2456 bindkey p
"selnextline -1"
2457 bindkey n
"selnextline 1"
2460 bindkey i
"selnextline -1"
2461 bindkey k
"selnextline 1"
2465 bindkey d
"$ctext yview scroll 18 units"
2466 bindkey u
"$ctext yview scroll -18 units"
2467 bindkey
/ {focus
$fstring}
2468 bindkey
<Key-KP_Divide
> {focus
$fstring}
2469 bindkey
<Key-Return
> {dofind
1 1}
2470 bindkey ?
{dofind
-1 1}
2472 bind .
<F5
> updatecommits
2473 bind .
<$M1B-F5> reloadcommits
2474 bind .
<F2
> showrefs
2475 bind .
<Shift-F4
> {newview
0}
2476 catch
{ bind .
<Shift-Key-XF86_Switch_VT_4
> {newview
0} }
2477 bind .
<F4
> edit_or_newview
2478 bind .
<$M1B-q> doquit
2479 bind .
<$M1B-f> {dofind
1 1}
2480 bind .
<$M1B-g> {dofind
1 0}
2481 bind .
<$M1B-r> dosearchback
2482 bind .
<$M1B-s> dosearch
2483 bind .
<$M1B-equal> {incrfont
1}
2484 bind .
<$M1B-plus> {incrfont
1}
2485 bind .
<$M1B-KP_Add> {incrfont
1}
2486 bind .
<$M1B-minus> {incrfont
-1}
2487 bind .
<$M1B-KP_Subtract> {incrfont
-1}
2488 wm protocol . WM_DELETE_WINDOW doquit
2489 bind .
<Destroy
> {stop_backends
}
2490 bind .
<Button-1
> "click %W"
2491 bind $fstring <Key-Return
> {dofind
1 1}
2492 bind $sha1entry <Key-Return
> {gotocommit
; break}
2493 bind $sha1entry <<PasteSelection>> clearsha1
2494 bind $cflist <1> {sel_flist %W %x %y; break}
2495 bind $cflist <B1-Motion> {sel_flist %W %x %y; break}
2496 bind $cflist <ButtonRelease-1> {treeclick %W %x %y}
2498 bind $cflist $ctxbut {pop_flist_menu %W %X %Y %x %y}
2499 bind $ctext $ctxbut {pop_diff_menu %W %X %Y %x %y}
2500 bind $ctext <Button-1> {focus %W}
2502 set maincursor [. cget -cursor]
2503 set textcursor [$ctext cget -cursor]
2504 set curtextcursor $textcursor
2506 set rowctxmenu .rowctxmenu
2507 makemenu $rowctxmenu {
2508 {mc "Diff this -> selected" command {diffvssel 0}}
2509 {mc "Diff selected -> this" command {diffvssel 1}}
2510 {mc "Make patch" command mkpatch}
2511 {mc "Create tag" command mktag}
2512 {mc "Write commit to file" command writecommit}
2513 {mc "Create new branch" command mkbranch}
2514 {mc "Cherry-pick this commit" command cherrypick}
2515 {mc "Reset HEAD branch to here" command resethead}
2516 {mc "Mark this commit" command markhere}
2517 {mc "Return to mark" command gotomark}
2518 {mc "Find descendant of this and mark" command find_common_desc}
2519 {mc "Compare with marked commit" command compare_commits}
2521 $rowctxmenu configure -tearoff 0
2523 set fakerowmenu .fakerowmenu
2524 makemenu $fakerowmenu {
2525 {mc "Diff this -> selected" command {diffvssel 0}}
2526 {mc "Diff selected -> this" command {diffvssel 1}}
2527 {mc "Make patch" command mkpatch}
2529 $fakerowmenu configure -tearoff 0
2531 set headctxmenu .headctxmenu
2532 makemenu $headctxmenu {
2533 {mc "Check out this branch" command cobranch}
2534 {mc "Remove this branch" command rmbranch}
2536 $headctxmenu configure -tearoff 0
2539 set flist_menu .flistctxmenu
2540 makemenu $flist_menu {
2541 {mc "Highlight this too" command {flist_hl 0}}
2542 {mc "Highlight this only" command {flist_hl 1}}
2543 {mc "External diff" command {external_diff}}
2544 {mc "Blame parent commit" command {external_blame 1}}
2546 $flist_menu configure -tearoff 0
2549 set diff_menu .diffctxmenu
2550 makemenu $diff_menu {
2551 {mc "Show origin of this line" command show_line_source}
2552 {mc "Run git gui blame on this line" command {external_blame_diff}}
2554 $diff_menu configure -tearoff 0
2557 # Windows sends all mouse wheel events to the current focused window, not
2558 # the one where the mouse hovers, so bind those events here and redirect
2559 # to the correct window
2560 proc windows_mousewheel_redirector {W X Y D} {
2561 global canv canv2 canv3
2562 set w [winfo containing -displayof $W $X $Y]
2564 set u [expr {$D < 0 ? 5 : -5}]
2565 if {$w == $canv || $w == $canv2 || $w == $canv3} {
2566 allcanvs yview scroll $u units
2569 $w yview scroll $u units
2575 # Update row number label when selectedline changes
2576 proc selectedline_change {n1 n2 op} {
2577 global selectedline rownumsel
2579 if {$selectedline eq {}} {
2582 set rownumsel [expr {$selectedline + 1}]
2586 # mouse-2 makes all windows scan vertically, but only the one
2587 # the cursor is in scans horizontally
2588 proc canvscan {op w x y} {
2589 global canv canv2 canv3
2590 foreach c [list $canv $canv2 $canv3] {
2599 proc scrollcanv {cscroll f0 f1} {
2600 $cscroll set $f0 $f1
2605 # when we make a key binding for the toplevel, make sure
2606 # it doesn't get triggered when that key is pressed in the
2607 # find string entry widget.
2608 proc bindkey {ev script} {
2611 set escript [bind Entry $ev]
2612 if {$escript == {}} {
2613 set escript [bind Entry <Key>]
2615 foreach e $entries {
2616 bind $e $ev "$escript; break"
2620 # set the focus back to the toplevel for any click outside
2623 global ctext entries
2624 foreach e [concat $entries $ctext] {
2625 if {$w == $e} return
2630 # Adjust the progress bar for a change in requested extent or canvas size
2631 proc adjustprogress {} {
2632 global progresscanv progressitem progresscoords
2633 global fprogitem fprogcoord lastprogupdate progupdatepending
2634 global rprogitem rprogcoord use_ttk
2637 $progresscanv configure -value [expr {int($fprogcoord * 100)}]
2641 set w [expr {[winfo width $progresscanv] - 4}]
2642 set x0 [expr {$w * [lindex $progresscoords 0]}]
2643 set x1 [expr {$w * [lindex $progresscoords 1]}]
2644 set h [winfo height $progresscanv]
2645 $progresscanv coords $progressitem $x0 0 $x1 $h
2646 $progresscanv coords $fprogitem 0 0 [expr {$w * $fprogcoord}] $h
2647 $progresscanv coords $rprogitem 0 0 [expr {$w * $rprogcoord}] $h
2648 set now [clock clicks -milliseconds]
2649 if {$now >= $lastprogupdate + 100} {
2650 set progupdatepending 0
2652 } elseif {!$progupdatepending} {
2653 set progupdatepending 1
2654 after [expr {$lastprogupdate + 100 - $now}] doprogupdate
2658 proc doprogupdate {} {
2659 global lastprogupdate progupdatepending
2661 if {$progupdatepending} {
2662 set progupdatepending 0
2663 set lastprogupdate [clock clicks -milliseconds]
2668 proc savestuff {w} {
2669 global canv canv2 canv3 mainfont textfont uifont tabstop
2670 global stuffsaved findmergefiles maxgraphpct
2671 global maxwidth showneartags showlocalchanges
2672 global viewname viewfiles viewargs viewargscmd viewperm nextviewnum
2673 global cmitmode wrapcomment datetimeformat limitdiffs
2674 global colors uicolor bgcolor fgcolor diffcolors diffcontext selectbgcolor
2675 global autoselect autosellen extdifftool perfile_attrs markbgcolor use_ttk
2676 global hideremotes want_ttk
2678 if {$stuffsaved} return
2679 if {![winfo viewable .]} return
2681 if {[file exists ~/.gitk-new]} {file delete -force ~/.gitk-new}
2682 set f [open "~/.gitk-new" w]
2683 if {$::tcl_platform(platform) eq {windows}} {
2684 file attributes "~/.gitk-new" -hidden true
2686 puts $f [list set mainfont $mainfont]
2687 puts $f [list set textfont $textfont]
2688 puts $f [list set uifont $uifont]
2689 puts $f [list set tabstop $tabstop]
2690 puts $f [list set findmergefiles $findmergefiles]
2691 puts $f [list set maxgraphpct $maxgraphpct]
2692 puts $f [list set maxwidth $maxwidth]
2693 puts $f [list set cmitmode $cmitmode]
2694 puts $f [list set wrapcomment $wrapcomment]
2695 puts $f [list set autoselect $autoselect]
2696 puts $f [list set autosellen $autosellen]
2697 puts $f [list set showneartags $showneartags]
2698 puts $f [list set hideremotes $hideremotes]
2699 puts $f [list set showlocalchanges $showlocalchanges]
2700 puts $f [list set datetimeformat $datetimeformat]
2701 puts $f [list set limitdiffs $limitdiffs]
2702 puts $f [list set uicolor $uicolor]
2703 puts $f [list set want_ttk $want_ttk]
2704 puts $f [list set bgcolor $bgcolor]
2705 puts $f [list set fgcolor $fgcolor]
2706 puts $f [list set colors $colors]
2707 puts $f [list set diffcolors $diffcolors]
2708 puts $f [list set markbgcolor $markbgcolor]
2709 puts $f [list set diffcontext $diffcontext]
2710 puts $f [list set selectbgcolor $selectbgcolor]
2711 puts $f [list set extdifftool $extdifftool]
2712 puts $f [list set perfile_attrs $perfile_attrs]
2714 puts $f "set geometry(main) [wm geometry .]"
2715 puts $f "set geometry(state) [wm state .]"
2716 puts $f "set geometry(topwidth) [winfo width .tf]"
2717 puts $f "set geometry(topheight) [winfo height .tf]"
2719 puts $f "set geometry(pwsash0) \"[.tf.histframe.pwclist sashpos 0] 1\""
2720 puts $f "set geometry(pwsash1) \"[.tf.histframe.pwclist sashpos 1] 1\""
2722 puts $f "set geometry(pwsash0) \"[.tf.histframe.pwclist sash coord 0]\""
2723 puts $f "set geometry(pwsash1) \"[.tf.histframe.pwclist sash coord 1]\""
2725 puts $f "set geometry(botwidth) [winfo width .bleft]"
2726 puts $f "set geometry(botheight) [winfo height .bleft]"
2728 puts -nonewline $f "set permviews {"
2729 for {set v 0} {$v < $nextviewnum} {incr v} {
2730 if {$viewperm($v)} {
2731 puts $f "{[list $viewname($v) $viewfiles($v) $viewargs($v) $viewargscmd($v)]}"
2736 file rename -force "~/.gitk-new" "~/.gitk"
2741 proc resizeclistpanes {win w} {
2742 global oldwidth use_ttk
2743 if {[info exists oldwidth($win)]} {
2745 set s0 [$win sashpos 0]
2746 set s1 [$win sashpos 1]
2748 set s0 [$win sash coord 0]
2749 set s1 [$win sash coord 1]
2752 set sash0 [expr {int($w/2 - 2)}]
2753 set sash1 [expr {int($w*5/6 - 2)}]
2755 set factor [expr {1.0 * $w / $oldwidth($win)}]
2756 set sash0 [expr {int($factor * [lindex $s0 0])}]
2757 set sash1 [expr {int($factor * [lindex $s1 0])}]
2761 if {$sash1 < $sash0 + 20} {
2762 set sash1 [expr {$sash0 + 20}]
2764 if {$sash1 > $w - 10} {
2765 set sash1 [expr {$w - 10}]
2766 if {$sash0 > $sash1 - 20} {
2767 set sash0 [expr {$sash1 - 20}]
2772 $win sashpos 0 $sash0
2773 $win sashpos 1 $sash1
2775 $win sash place 0 $sash0 [lindex $s0 1]
2776 $win sash place 1 $sash1 [lindex $s1 1]
2779 set oldwidth($win) $w
2782 proc resizecdetpanes {win w} {
2783 global oldwidth use_ttk
2784 if {[info exists oldwidth($win)]} {
2786 set s0 [$win sashpos 0]
2788 set s0 [$win sash coord 0]
2791 set sash0 [expr {int($w*3/4 - 2)}]
2793 set factor [expr {1.0 * $w / $oldwidth($win)}]
2794 set sash0 [expr {int($factor * [lindex $s0 0])}]
2798 if {$sash0 > $w - 15} {
2799 set sash0 [expr {$w - 15}]
2803 $win sashpos 0 $sash0
2805 $win sash place 0 $sash0 [lindex $s0 1]
2808 set oldwidth($win) $w
2811 proc allcanvs args {
2812 global canv canv2 canv3
2818 proc bindall {event action} {
2819 global canv canv2 canv3
2820 bind $canv $event $action
2821 bind $canv2 $event $action
2822 bind $canv3 $event $action
2828 if {[winfo exists $w]} {
2833 wm title $w [mc "About gitk"]
2835 message $w.m -text [mc "
2836 Gitk - a commit viewer for git
2838 Copyright \u00a9 2005-2010 Paul Mackerras
2840 Use and redistribute under the terms of the GNU General Public License"] \
2841 -justify center -aspect 400 -border 2 -bg white -relief groove
2842 pack $w.m -side top -fill x -padx 2 -pady 2
2843 ${NS}::button $w.ok -text [mc "Close"] -command "destroy $w" -default active
2844 pack $w.ok -side bottom
2845 bind $w <Visibility> "focus $w.ok"
2846 bind $w <Key-Escape> "destroy $w"
2847 bind $w <Key-Return> "destroy $w"
2848 tk::PlaceWindow $w widget .
2854 if {[winfo exists $w]} {
2858 if {[tk windowingsystem] eq {aqua}} {
2864 wm title $w [mc "Gitk key bindings"]
2866 message $w.m -text "
2867 [mc "Gitk key bindings:"]
2869 [mc "<%s-Q> Quit" $M1T]
2870 [mc "<%s-W> Close window" $M1T]
2871 [mc "<Home> Move to first commit"]
2872 [mc "<End> Move to last commit"]
2873 [mc "<Up>, p, i Move up one commit"]
2874 [mc "<Down>, n, k Move down one commit"]
2875 [mc "<Left>, z, j Go back in history list"]
2876 [mc "<Right>, x, l Go forward in history list"]
2877 [mc "<PageUp> Move up one page in commit list"]
2878 [mc "<PageDown> Move down one page in commit list"]
2879 [mc "<%s-Home> Scroll to top of commit list" $M1T]
2880 [mc "<%s-End> Scroll to bottom of commit list" $M1T]
2881 [mc "<%s-Up> Scroll commit list up one line" $M1T]
2882 [mc "<%s-Down> Scroll commit list down one line" $M1T]
2883 [mc "<%s-PageUp> Scroll commit list up one page" $M1T]
2884 [mc "<%s-PageDown> Scroll commit list down one page" $M1T]
2885 [mc "<Shift-Up> Find backwards (upwards, later commits)"]
2886 [mc "<Shift-Down> Find forwards (downwards, earlier commits)"]
2887 [mc "<Delete>, b Scroll diff view up one page"]
2888 [mc "<Backspace> Scroll diff view up one page"]
2889 [mc "<Space> Scroll diff view down one page"]
2890 [mc "u Scroll diff view up 18 lines"]
2891 [mc "d Scroll diff view down 18 lines"]
2892 [mc "<%s-F> Find" $M1T]
2893 [mc "<%s-G> Move to next find hit" $M1T]
2894 [mc "<Return> Move to next find hit"]
2895 [mc "/ Focus the search box"]
2896 [mc "? Move to previous find hit"]
2897 [mc "f Scroll diff view to next file"]
2898 [mc "<%s-S> Search for next hit in diff view" $M1T]
2899 [mc "<%s-R> Search for previous hit in diff view" $M1T]
2900 [mc "<%s-KP+> Increase font size" $M1T]
2901 [mc "<%s-plus> Increase font size" $M1T]
2902 [mc "<%s-KP-> Decrease font size" $M1T]
2903 [mc "<%s-minus> Decrease font size" $M1T]
2906 -justify left -bg white -border 2 -relief groove
2907 pack $w.m -side top -fill both -padx 2 -pady 2
2908 ${NS}::button $w.ok -text [mc "Close"] -command "destroy $w" -default active
2909 bind $w <Key-Escape> [list destroy $w]
2910 pack $w.ok -side bottom
2911 bind $w <Visibility> "focus $w.ok"
2912 bind $w <Key-Escape> "destroy $w"
2913 bind $w <Key-Return> "destroy $w"
2916 # Procedures for manipulating the file list window at the
2917 # bottom right of the overall window.
2919 proc treeview {w l openlevs} {
2920 global treecontents treediropen treeheight treeparent treeindex
2930 set treecontents() {}
2931 $w conf -state normal
2933 while {[string range $f 0 $prefixend] ne $prefix} {
2934 if {$lev <= $openlevs} {
2935 $w mark set e:$treeindex($prefix) "end -1c"
2936 $w mark gravity e:$treeindex($prefix) left
2938 set treeheight($prefix) $ht
2939 incr ht [lindex $htstack end]
2940 set htstack [lreplace $htstack end end]
2941 set prefixend [lindex $prefendstack end]
2942 set prefendstack [lreplace $prefendstack end end]
2943 set prefix [string range $prefix 0 $prefixend]
2946 set tail [string range $f [expr {$prefixend+1}] end]
2947 while {[set slash [string first "/" $tail]] >= 0} {
2950 lappend prefendstack $prefixend
2951 incr prefixend [expr {$slash + 1}]
2952 set d [string range $tail 0 $slash]
2953 lappend treecontents($prefix) $d
2954 set oldprefix $prefix
2956 set treecontents($prefix) {}
2957 set treeindex($prefix) [incr ix]
2958 set treeparent($prefix) $oldprefix
2959 set tail [string range $tail [expr {$slash+1}] end]
2960 if {$lev <= $openlevs} {
2962 set treediropen($prefix) [expr {$lev < $openlevs}]
2963 set bm [expr {$lev == $openlevs? "tri-rt": "tri-dn"}]
2964 $w mark set d:$ix "end -1c"
2965 $w mark gravity d:$ix left
2967 for {set i 0} {$i < $lev} {incr i} {append str "\t"}
2969 $w image create end -align center -image $bm -padx 1 \
2971 $w insert end $d [highlight_tag $prefix]
2972 $w mark set s:$ix "end -1c"
2973 $w mark gravity s:$ix left
2978 if {$lev <= $openlevs} {
2981 for {set i 0} {$i < $lev} {incr i} {append str "\t"}
2983 $w insert end $tail [highlight_tag $f]
2985 lappend treecontents($prefix) $tail
2988 while {$htstack ne {}} {
2989 set treeheight($prefix) $ht
2990 incr ht [lindex $htstack end]
2991 set htstack [lreplace $htstack end end]
2992 set prefixend [lindex $prefendstack end]
2993 set prefendstack [lreplace $prefendstack end end]
2994 set prefix [string range $prefix 0 $prefixend]
2996 $w conf -state disabled
2999 proc linetoelt {l} {
3000 global treeheight treecontents
3005 foreach e $treecontents($prefix) {
3010 if {[string index $e end] eq "/"} {
3011 set n $treeheight($prefix$e)
3023 proc highlight_tree {y prefix} {
3024 global treeheight treecontents cflist
3026 foreach e $treecontents($prefix) {
3028 if {[highlight_tag $path] ne {}} {
3029 $cflist tag add bold $y.0 "$y.0 lineend"
3032 if {[string index $e end] eq "/" && $treeheight($path) > 1} {
3033 set y [highlight_tree $y $path]
3039 proc treeclosedir {w dir} {
3040 global treediropen treeheight treeparent treeindex
3042 set ix $treeindex($dir)
3043 $w conf -state normal
3044 $w delete s:$ix e:$ix
3045 set treediropen($dir) 0
3046 $w image configure a:$ix -image tri-rt
3047 $w conf -state disabled
3048 set n [expr {1 - $treeheight($dir)}]
3049 while {$dir ne {}} {
3050 incr treeheight($dir) $n
3051 set dir $treeparent($dir)
3055 proc treeopendir {w dir} {
3056 global treediropen treeheight treeparent treecontents treeindex
3058 set ix $treeindex($dir)
3059 $w conf -state normal
3060 $w image configure a:$ix -image tri-dn
3061 $w mark set e:$ix s:$ix
3062 $w mark gravity e:$ix right
3065 set n [llength $treecontents($dir)]
3066 for {set x $dir} {$x ne {}} {set x $treeparent($x)} {
3069 incr treeheight($x) $n
3071 foreach e $treecontents($dir) {
3073 if {[string index $e end] eq "/"} {
3074 set iy $treeindex($de)
3075 $w mark set d:$iy e:$ix
3076 $w mark gravity d:$iy left
3077 $w insert e:$ix $str
3078 set treediropen($de) 0
3079 $w image create e:$ix -align center -image tri-rt -padx 1 \
3081 $w insert e:$ix $e [highlight_tag $de]
3082 $w mark set s:$iy e:$ix
3083 $w mark gravity s:$iy left
3084 set treeheight($de) 1
3086 $w insert e:$ix $str
3087 $w insert e:$ix $e [highlight_tag $de]
3090 $w mark gravity e:$ix right
3091 $w conf -state disabled
3092 set treediropen($dir) 1
3093 set top [lindex [split [$w index @0,0] .] 0]
3094 set ht [$w cget -height]
3095 set l [lindex [split [$w index s:$ix] .] 0]
3098 } elseif {$l + $n + 1 > $top + $ht} {
3099 set top [expr {$l + $n + 2 - $ht}]
3107 proc treeclick {w x y} {
3108 global treediropen cmitmode ctext cflist cflist_top
3110 if {$cmitmode ne "tree"} return
3111 if {![info exists cflist_top]} return
3112 set l [lindex [split [$w index "@$x,$y"] "."] 0]
3113 $cflist tag remove highlight $cflist_top.0 "$cflist_top.0 lineend"
3114 $cflist tag add highlight $l.0 "$l.0 lineend"
3120 set e [linetoelt $l]
3121 if {[string index $e end] ne "/"} {
3123 } elseif {$treediropen($e)} {
3130 proc setfilelist {id} {
3131 global treefilelist cflist jump_to_here
3133 treeview $cflist $treefilelist($id) 0
3134 if {$jump_to_here ne {}} {
3135 set f [lindex $jump_to_here 0]
3136 if {[lsearch -exact $treefilelist($id) $f] >= 0} {
3142 image create bitmap tri-rt -background black -foreground blue -data {
3143 #define tri-rt_width 13
3144 #define tri-rt_height 13
3145 static unsigned char tri-rt_bits[] = {
3146 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x30, 0x00, 0x70, 0x00, 0xf0, 0x00,
3147 0xf0, 0x01, 0xf0, 0x00, 0x70, 0x00, 0x30, 0x00, 0x10, 0x00, 0x00, 0x00,
3150 #define tri-rt-mask_width 13
3151 #define tri-rt-mask_height 13
3152 static unsigned char tri-rt-mask_bits[] = {
3153 0x08, 0x00, 0x18, 0x00, 0x38, 0x00, 0x78, 0x00, 0xf8, 0x00, 0xf8, 0x01,
3154 0xf8, 0x03, 0xf8, 0x01, 0xf8, 0x00, 0x78, 0x00, 0x38, 0x00, 0x18, 0x00,
3157 image create bitmap tri-dn -background black -foreground blue -data {
3158 #define tri-dn_width 13
3159 #define tri-dn_height 13
3160 static unsigned char tri-dn_bits[] = {
3161 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x07, 0xf8, 0x03,
3162 0xf0, 0x01, 0xe0, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3165 #define tri-dn-mask_width 13
3166 #define tri-dn-mask_height 13
3167 static unsigned char tri-dn-mask_bits[] = {
3168 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x1f, 0xfe, 0x0f, 0xfc, 0x07,
3169 0xf8, 0x03, 0xf0, 0x01, 0xe0, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
3173 image create bitmap reficon-T -background black -foreground yellow -data {
3174 #define tagicon_width 13
3175 #define tagicon_height 9
3176 static unsigned char tagicon_bits[] = {
3177 0x00, 0x00, 0x00, 0x00, 0xf0, 0x07, 0xf8, 0x07,
3178 0xfc, 0x07, 0xf8, 0x07, 0xf0, 0x07, 0x00, 0x00, 0x00, 0x00};
3180 #define tagicon-mask_width 13
3181 #define tagicon-mask_height 9
3182 static unsigned char tagicon-mask_bits[] = {
3183 0x00, 0x00, 0xf0, 0x0f, 0xf8, 0x0f, 0xfc, 0x0f,
3184 0xfe, 0x0f, 0xfc, 0x0f, 0xf8, 0x0f, 0xf0, 0x0f, 0x00, 0x00};
3187 #define headicon_width 13
3188 #define headicon_height 9
3189 static unsigned char headicon_bits[] = {
3190 0x00, 0x00, 0x00, 0x00, 0xf8, 0x07, 0xf8, 0x07,
3191 0xf8, 0x07, 0xf8, 0x07, 0xf8, 0x07, 0x00, 0x00, 0x00, 0x00};
3194 #define headicon-mask_width 13
3195 #define headicon-mask_height 9
3196 static unsigned char headicon-mask_bits[] = {
3197 0x00, 0x00, 0xfc, 0x0f, 0xfc, 0x0f, 0xfc, 0x0f,
3198 0xfc, 0x0f, 0xfc, 0x0f, 0xfc, 0x0f, 0xfc, 0x0f, 0x00, 0x00};
3200 image create bitmap reficon-H -background black -foreground green \
3201 -data $rectdata -maskdata $rectmask
3202 image create bitmap reficon-o -background black -foreground "#ddddff" \
3203 -data $rectdata -maskdata $rectmask
3205 proc init_flist {first} {
3206 global cflist cflist_top difffilestart
3208 $cflist conf -state normal
3209 $cflist delete 0.0 end
3211 $cflist insert end $first
3213 $cflist tag add highlight 1.0 "1.0 lineend"
3215 catch {unset cflist_top}
3217 $cflist conf -state disabled
3218 set difffilestart {}
3221 proc highlight_tag {f} {
3222 global highlight_paths
3224 foreach p $highlight_paths {
3225 if {[string match $p $f]} {
3232 proc highlight_filelist {} {
3233 global cmitmode cflist
3235 $cflist conf -state normal
3236 if {$cmitmode ne "tree"} {
3237 set end [lindex [split [$cflist index end] .] 0]
3238 for {set l 2} {$l < $end} {incr l} {
3239 set line [$cflist get $l.0 "$l.0 lineend"]
3240 if {[highlight_tag $line] ne {}} {
3241 $cflist tag add bold $l.0 "$l.0 lineend"
3247 $cflist conf -state disabled
3250 proc unhighlight_filelist {} {
3253 $cflist conf -state normal
3254 $cflist tag remove bold 1.0 end
3255 $cflist conf -state disabled
3258 proc add_flist {fl} {
3261 $cflist conf -state normal
3263 $cflist insert end "\n"
3264 $cflist insert end $f [highlight_tag $f]
3266 $cflist conf -state disabled
3269 proc sel_flist {w x y} {
3270 global ctext difffilestart cflist cflist_top cmitmode
3272 if {$cmitmode eq "tree"} return
3273 if {![info exists cflist_top]} return
3274 set l [lindex [split [$w index "@$x,$y"] "."] 0]
3275 $cflist tag remove highlight $cflist_top.0 "$cflist_top.0 lineend"
3276 $cflist tag add highlight $l.0 "$l.0 lineend"
3281 catch {$ctext yview [lindex $difffilestart [expr {$l - 2}]]}
3285 proc pop_flist_menu {w X Y x y} {
3286 global ctext cflist cmitmode flist_menu flist_menu_file
3287 global treediffs diffids
3290 set l [lindex [split [$w index "@$x,$y"] "."] 0]
3292 if {$cmitmode eq "tree"} {
3293 set e [linetoelt $l]
3294 if {[string index $e end] eq "/"} return
3296 set e [lindex $treediffs($diffids) [expr {$l-2}]]
3298 set flist_menu_file $e
3299 set xdiffstate "normal"
3300 if {$cmitmode eq "tree"} {
3301 set xdiffstate "disabled"
3303 # Disable "External diff" item in tree mode
3304 $flist_menu entryconf 2 -state $xdiffstate
3305 tk_popup $flist_menu $X $Y
3308 proc find_ctext_fileinfo {line} {
3309 global ctext_file_names ctext_file_lines
3311 set ok [bsearch $ctext_file_lines $line]
3312 set tline [lindex $ctext_file_lines $ok]
3314 if {$ok >= [llength $ctext_file_lines] || $line < $tline} {
3317 return [list [lindex $ctext_file_names $ok] $tline]
3321 proc pop_diff_menu {w X Y x y} {
3322 global ctext diff_menu flist_menu_file
3323 global diff_menu_txtpos diff_menu_line
3324 global diff_menu_filebase
3326 set diff_menu_txtpos [split [$w index "@$x,$y"] "."]
3327 set diff_menu_line [lindex $diff_menu_txtpos 0]
3328 # don't pop up the menu on hunk-separator or file-separator lines
3329 if {[lsearch -glob [$ctext tag names $diff_menu_line.0] "*sep"] >= 0} {
3333 set f [find_ctext_fileinfo $diff_menu_line]
3334 if {$f eq {}} return
3335 set flist_menu_file [lindex $f 0]
3336 set diff_menu_filebase [lindex $f 1]
3337 tk_popup $diff_menu $X $Y
3340 proc flist_hl {only} {
3341 global flist_menu_file findstring gdttype
3343 set x [shellquote $flist_menu_file]
3344 if {$only || $findstring eq {} || $gdttype ne [mc "touching paths:"]} {
3347 append findstring " " $x
3349 set gdttype [mc "touching paths:"]
3352 proc gitknewtmpdir {} {
3353 global diffnum gitktmpdir gitdir
3355 if {![info exists gitktmpdir]} {
3356 set gitktmpdir [file join [file dirname $gitdir] \
3357 [format ".gitk-tmp.%s" [pid]]]
3358 if {[catch {file mkdir $gitktmpdir} err]} {
3359 error_popup "[mc "Error creating temporary directory %s:" $gitktmpdir] $err"
3366 set diffdir [file join $gitktmpdir $diffnum]
3367 if {[catch {file mkdir $diffdir} err]} {
3368 error_popup "[mc "Error creating temporary directory %s:" $diffdir] $err"
3374 proc save_file_from_commit {filename output what} {
3377 if {[catch {exec git show $filename -- > $output} err]} {
3378 if {[string match "fatal: bad revision *" $err]} {
3381 error_popup "[mc "Error getting \"%s\" from %s:" $filename $what] $err"
3387 proc external_diff_get_one_file {diffid filename diffdir} {
3388 global nullid nullid2 nullfile
3391 if {$diffid == $nullid} {
3392 set difffile [file join [file dirname $gitdir] $filename]
3393 if {[file exists $difffile]} {
3398 if {$diffid == $nullid2} {
3399 set difffile [file join $diffdir "\[index\] [file tail $filename]"]
3400 return [save_file_from_commit :$filename $difffile index]
3402 set difffile [file join $diffdir "\[$diffid\] [file tail $filename]"]
3403 return [save_file_from_commit $diffid:$filename $difffile \
3407 proc external_diff {} {
3408 global nullid nullid2
3409 global flist_menu_file
3413 if {[llength $diffids] == 1} {
3414 # no reference commit given
3415 set diffidto [lindex $diffids 0]
3416 if {$diffidto eq $nullid} {
3417 # diffing working copy with index
3418 set diffidfrom $nullid2
3419 } elseif {$diffidto eq $nullid2} {
3420 # diffing index with HEAD
3421 set diffidfrom "HEAD"
3423 # use first parent commit
3424 global parentlist selectedline
3425 set diffidfrom [lindex $parentlist $selectedline 0]
3428 set diffidfrom [lindex $diffids 0]
3429 set diffidto [lindex $diffids 1]
3432 # make sure that several diffs wont collide
3433 set diffdir [gitknewtmpdir]
3434 if {$diffdir eq {}} return
3436 # gather files to diff
3437 set difffromfile [external_diff_get_one_file $diffidfrom $flist_menu_file $diffdir]
3438 set difftofile [external_diff_get_one_file $diffidto $flist_menu_file $diffdir]
3440 if {$difffromfile ne {} && $difftofile ne {}} {
3441 set cmd [list [shellsplit $extdifftool] $difffromfile $difftofile]
3442 if {[catch {set fl [open |$cmd r]} err]} {
3443 file delete -force $diffdir
3444 error_popup "$extdifftool: [mc "command failed:"] $err"
3446 fconfigure $fl -blocking 0
3447 filerun $fl [list delete_at_eof $fl $diffdir]
3452 proc find_hunk_blamespec {base line} {
3455 # Find and parse the hunk header
3456 set s_lix [$ctext search -backwards -regexp ^@@ "$line.0 lineend" $base.0]
3457 if {$s_lix eq {}} return
3459 set s_line [$ctext get $s_lix "$s_lix + 1 lines"]
3460 if {![regexp {^@@@*(( -\d+(,\d+)?)+) \+(\d+)(,\d+)? @@} $s_line \
3461 s_line old_specs osz osz1 new_line nsz]} {
3465 # base lines for the parents
3466 set base_lines [list $new_line]
3467 foreach old_spec [lrange [split $old_specs " "] 1 end] {
3468 if {![regexp -- {-(\d+)(,\d+)?} $old_spec \
3469 old_spec old_line osz]} {
3472 lappend base_lines $old_line
3475 # Now scan the lines to determine offset within the hunk
3476 set max_parent [expr {[llength $base_lines]-2}]
3478 set s_lno [lindex [split $s_lix "."] 0]
3480 # Determine if the line is removed
3481 set chunk [$ctext get $line.0 "$line.1 + $max_parent chars"]
3482 if {[string match {[-+ ]*} $chunk]} {
3483 set removed_idx [string first "-" $chunk]
3484 # Choose a parent index
3485 if {$removed_idx >= 0} {
3486 set parent $removed_idx
3488 set unchanged_idx [string first " " $chunk]
3489 if {$unchanged_idx >= 0} {
3490 set parent $unchanged_idx
3492 # blame the current commit
3496 # then count other lines that belong to it
3497 for {set i $line} {[incr i -1] > $s_lno} {} {
3498 set chunk [$ctext get $i.0 "$i.1 + $max_parent chars"]
3499 # Determine if the line is removed
3500 set removed_idx [string first "-" $chunk]
3502 set code [string index $chunk $parent]
3503 if {$code eq "-" || ($removed_idx < 0 && $code ne "+")} {
3507 if {$removed_idx < 0} {
3517 incr dline [lindex $base_lines $parent]
3518 return [list $parent $dline]
3521 proc external_blame_diff {} {
3522 global currentid cmitmode
3523 global diff_menu_txtpos diff_menu_line
3524 global diff_menu_filebase flist_menu_file
3526 if {$cmitmode eq "tree"} {
3528 set line [expr {$diff_menu_line - $diff_menu_filebase}]
3530 set hinfo [find_hunk_blamespec $diff_menu_filebase $diff_menu_line]
3532 set parent_idx [lindex $hinfo 0]
3533 set line [lindex $hinfo 1]
3540 external_blame $parent_idx $line
3543 # Find the SHA1 ID of the blob for file $fname in the index
3545 proc index_sha1 {fname} {
3546 set f [open [list | git ls-files -s $fname] r]
3547 while {[gets $f line] >= 0} {
3548 set info [lindex [split $line "\t"] 0]
3549 set stage [lindex $info 2]
3550 if {$stage eq "0" || $stage eq "2"} {
3552 return [lindex $info 1]
3559 # Turn an absolute path into one relative to the current directory
3560 proc make_relative {f} {
3561 if {[file pathtype $f] eq "relative"} {
3564 set elts [file split $f]
3565 set here [file split [pwd]]
3570 if {$ei < $hi || $ei >= [llength $elts] || [lindex $elts $ei] ne $d} {
3577 set elts [concat $res [lrange $elts $ei end]]
3578 return [eval file join $elts]
3581 proc external_blame {parent_idx {line {}}} {
3582 global flist_menu_file gitdir
3583 global nullid nullid2
3584 global parentlist selectedline currentid
3586 if {$parent_idx > 0} {
3587 set base_commit [lindex $parentlist $selectedline [expr {$parent_idx-1}]]
3589 set base_commit $currentid
3592 if {$base_commit eq {} || $base_commit eq $nullid || $base_commit eq $nullid2} {
3593 error_popup [mc "No such commit"]
3597 set cmdline [list git gui blame]
3598 if {$line ne {} && $line > 1} {
3599 lappend cmdline "--line=$line"
3601 set f [file join [file dirname $gitdir] $flist_menu_file]
3602 # Unfortunately it seems git gui blame doesn't like
3603 # being given an absolute path...
3604 set f [make_relative $f]
3605 lappend cmdline $base_commit $f
3606 if {[catch {eval exec $cmdline &} err]} {
3607 error_popup "[mc "git gui blame: command failed:"] $err"
3611 proc show_line_source {} {
3612 global cmitmode currentid parents curview blamestuff blameinst
3613 global diff_menu_line diff_menu_filebase flist_menu_file
3614 global nullid nullid2 gitdir
3617 if {$cmitmode eq "tree"} {
3619 set line [expr {$diff_menu_line - $diff_menu_filebase}]
3621 set h [find_hunk_blamespec $diff_menu_filebase $diff_menu_line]
3622 if {$h eq {}} return
3623 set pi [lindex $h 0]
3625 mark_ctext_line $diff_menu_line
3629 if {$currentid eq $nullid} {
3631 # must be a merge in progress...
3633 # get the last line from .git/MERGE_HEAD
3634 set f [open [file join $gitdir MERGE_HEAD] r]
3635 set id [lindex [split [read $f] "\n"] end-1]
3638 error_popup [mc "Couldn't read merge head: %s" $err]
3641 } elseif {$parents($curview,$currentid) eq $nullid2} {
3642 # need to do the blame from the index
3644 set from_index [index_sha1 $flist_menu_file]
3646 error_popup [mc "Error reading index: %s" $err]
3650 set id $parents($curview,$currentid)
3653 set id [lindex $parents($curview,$currentid) $pi]
3655 set line [lindex $h 1]
3658 if {$from_index ne {}} {
3659 lappend blameargs | git cat-file blob $from_index
3661 lappend blameargs | git blame -p -L$line,+1
3662 if {$from_index ne {}} {
3663 lappend blameargs --contents -
3665 lappend blameargs $id
3667 lappend blameargs -- [file join [file dirname $gitdir] $flist_menu_file]
3669 set f [open $blameargs r]
3671 error_popup [mc "Couldn't start git blame: %s" $err]
3674 nowbusy blaming [mc "Searching"]
3675 fconfigure $f -blocking 0
3676 set i [reg_instance $f]
3677 set blamestuff($i) {}
3679 filerun $f [list read_line_source $f $i]
3682 proc stopblaming {} {
3685 if {[info exists blameinst]} {
3686 stop_instance $blameinst
3692 proc read_line_source {fd inst} {
3693 global blamestuff curview commfd blameinst nullid nullid2
3695 while {[gets $fd line] >= 0} {
3696 lappend blamestuff($inst) $line
3704 fconfigure $fd -blocking 1
3705 if {[catch {close $fd} err]} {
3706 error_popup [mc "Error running git blame: %s" $err]
3711 set line [split [lindex $blamestuff($inst) 0] " "]
3712 set id [lindex $line 0]
3713 set lnum [lindex $line 1]
3714 if {[string length $id] == 40 && [string is xdigit $id] &&
3715 [string is digit -strict $lnum]} {
3716 # look for "filename" line
3717 foreach l $blamestuff($inst) {
3718 if {[string match "filename *" $l]} {
3719 set fname [string range $l 9 end]
3725 # all looks good, select it
3726 if {$id eq $nullid} {
3727 # blame uses all-zeroes to mean not committed,
3728 # which would mean a change in the index
3731 if {[commitinview $id $curview]} {
3732 selectline [rowofcommit $id] 1 [list $fname $lnum]
3734 error_popup [mc "That line comes from commit %s, \
3735 which is not in this view" [shortids $id]]
3738 puts "oops couldn't parse git blame output"
3743 # delete $dir when we see eof on $f (presumably because the child has exited)
3744 proc delete_at_eof {f dir} {
3745 while {[gets $f line] >= 0} {}
3747 if {[catch {close $f} err]} {
3748 error_popup "[mc "External diff viewer failed:"] $err"
3750 file delete -force $dir
3756 # Functions for adding and removing shell-type quoting
3758 proc shellquote {str} {
3759 if {![string match "*\['\"\\ \t]*" $str]} {
3762 if {![string match "*\['\"\\]*" $str]} {
3765 if {![string match "*'*" $str]} {
3768 return "\"[string map {\" \\\" \\ \\\\} $str]\""
3771 proc shellarglist {l} {
3777 append str [shellquote $a]
3782 proc shelldequote {str} {
3787 if {![regexp -start $used -indices "\['\"\\\\ \t]" $str first]} {
3788 append ret [string range $str $used end]
3789 set used [string length $str]
3792 set first [lindex $first 0]
3793 set ch [string index $str $first]
3794 if {$first > $used} {
3795 append ret [string range $str $used [expr {$first - 1}]]
3798 if {$ch eq " " || $ch eq "\t"} break
3801 set first [string first "'" $str $used]
3803 error "unmatched single-quote"
3805 append ret [string range $str $used [expr {$first - 1}]]
3810 if {$used >= [string length $str]} {
3811 error "trailing backslash"
3813 append ret [string index $str $used]
3818 if {![regexp -start $used -indices "\[\"\\\\]" $str first]} {
3819 error "unmatched double-quote"
3821 set first [lindex $first 0]
3822 set ch [string index $str $first]
3823 if {$first > $used} {
3824 append ret [string range $str $used [expr {$first - 1}]]
3827 if {$ch eq "\""} break
3829 append ret [string index $str $used]
3833 return [list $used $ret]
3836 proc shellsplit {str} {
3839 set str [string trimleft $str]
3840 if {$str eq {}} break
3841 set dq [shelldequote $str]
3842 set n [lindex $dq 0]
3843 set word [lindex $dq 1]
3844 set str [string range $str $n end]
3850 # Code to implement multiple views
3852 proc newview {ishighlight} {
3853 global nextviewnum newviewname newishighlight
3854 global revtreeargs viewargscmd newviewopts curview
3856 set newishighlight $ishighlight
3858 if {[winfo exists $top]} {
3862 decode_view_opts $nextviewnum $revtreeargs
3863 set newviewname($nextviewnum) "[mc "View"] $nextviewnum"
3864 set newviewopts($nextviewnum,perm) 0
3865 set newviewopts($nextviewnum,cmd) $viewargscmd($curview)
3866 vieweditor $top $nextviewnum [mc "Gitk view definition"]
3869 set known_view_options {
3870 {perm b . {} {mc "Remember this view"}}
3871 {reflabel l + {} {mc "References (space separated list):"}}
3872 {refs t15 .. {} {mc "Branches & tags:"}}
3873 {allrefs b *. "--all" {mc "All refs"}}
3874 {branches b . "--branches" {mc "All (local) branches"}}
3875 {tags b . "--tags" {mc "All tags"}}
3876 {remotes b . "--remotes" {mc "All remote-tracking branches"}}
3877 {commitlbl l + {} {mc "Commit Info (regular expressions):"}}
3878 {author t15 .. "--author=*" {mc "Author:"}}
3879 {committer t15 . "--committer=*" {mc "Committer:"}}
3880 {loginfo t15 .. "--grep=*" {mc "Commit Message:"}}
3881 {allmatch b .. "--all-match" {mc "Matches all Commit Info criteria"}}
3882 {changes_l l + {} {mc "Changes to Files:"}}
3883 {pickaxe_s r0 . {} {mc "Fixed String"}}
3884 {pickaxe_t r1 . "--pickaxe-regex" {mc "Regular Expression"}}
3885 {pickaxe t15 .. "-S*" {mc "Search string:"}}
3886 {datelabel l + {} {mc "Commit Dates (\"2 weeks ago\", \"2009-03-17 15:27:38\", \"March 17, 2009 15:27:38\"):"}}
3887 {since t15 .. {"--since=*" "--after=*"} {mc "Since:"}}
3888 {until t15 . {"--until=*" "--before=*"} {mc "Until:"}}
3889 {limit_lbl l + {} {mc "Limit and/or skip a number of revisions (positive integer):"}}
3890 {limit t10 *. "--max-count=*" {mc "Number to show:"}}
3891 {skip t10 . "--skip=*" {mc "Number to skip:"}}
3892 {misc_lbl l + {} {mc "Miscellaneous options:"}}
3893 {dorder b *. {"--date-order" "-d"} {mc "Strictly sort by date"}}
3894 {lright b . "--left-right" {mc "Mark branch sides"}}
3895 {first b . "--first-parent" {mc "Limit to first parent"}}
3896 {smplhst b . "--simplify-by-decoration" {mc "Simple history"}}
3897 {args t50 *. {} {mc "Additional arguments to git log:"}}
3898 {allpaths path + {} {mc "Enter files and directories to include, one per line:"}}
3899 {cmd t50= + {} {mc "Command to generate more commits to include:"}}
3902 # Convert $newviewopts($n, ...) into args for git log.
3903 proc encode_view_opts {n} {
3904 global known_view_options newviewopts
3907 foreach opt $known_view_options {
3908 set patterns [lindex $opt 3]
3909 if {$patterns eq {}} continue
3910 set pattern [lindex $patterns 0]
3912 if {[lindex $opt 1] eq "b"} {
3913 set val $newviewopts($n,[lindex $opt 0])
3915 lappend rargs $pattern
3917 } elseif {[regexp {^r(\d+)$} [lindex $opt 1] type value]} {
3918 regexp {^(.*_)} [lindex $opt 0] uselessvar button_id
3919 set val $newviewopts($n,$button_id)
3920 if {$val eq $value} {
3921 lappend rargs $pattern
3924 set val $newviewopts($n,[lindex $opt 0])
3925 set val [string trim $val]
3927 set pfix [string range $pattern 0 end-1]
3928 lappend rargs $pfix$val
3932 set rargs [concat $rargs [shellsplit $newviewopts($n,refs)]]
3933 return [concat $rargs [shellsplit $newviewopts($n,args)]]
3936 # Fill $newviewopts($n, ...) based on args for git log.
3937 proc decode_view_opts {n view_args} {
3938 global known_view_options newviewopts
3940 foreach opt $known_view_options {
3941 set id [lindex $opt 0]
3942 if {[lindex $opt 1] eq "b"} {
3945 } elseif {[regexp {^r(\d+)$} [lindex $opt 1]]} {
3947 regexp {^(.*_)} $id uselessvar id
3953 set newviewopts($n,$id) $val
3957 foreach arg $view_args {
3958 if {[regexp -- {^-([0-9]+)$} $arg arg cnt]
3959 && ![info exists found(limit)]} {
3960 set newviewopts($n,limit) $cnt
3965 foreach opt $known_view_options {
3966 set id [lindex $opt 0]
3967 if {[info exists found($id)]} continue
3968 foreach pattern [lindex $opt 3] {
3969 if {![string match $pattern $arg]} continue
3970 if {[lindex $opt 1] eq "b"} {
3973 } elseif {[regexp {^r(\d+)$} [lindex $opt 1] match num]} {
3975 regexp {^(.*_)} $id uselessvar id
3979 set size [string length $pattern]
3980 set val [string range $arg [expr {$size-1}] end]
3982 set newviewopts($n,$id) $val
3986 if {[info exists val]} break
3988 if {[info exists val]} continue
3989 if {[regexp {^-} $arg]} {
3992 lappend refargs $arg
3995 set newviewopts($n,refs) [shellarglist $refargs]
3996 set newviewopts($n,args) [shellarglist $oargs]
3999 proc edit_or_newview {} {
4011 global viewname viewperm newviewname newviewopts
4012 global viewargs viewargscmd
4014 set top .gitkvedit-$curview
4015 if {[winfo exists $top]} {
4019 decode_view_opts $curview $viewargs($curview)
4020 set newviewname($curview) $viewname($curview)
4021 set newviewopts($curview,perm) $viewperm($curview)
4022 set newviewopts($curview,cmd) $viewargscmd($curview)
4023 vieweditor $top $curview "[mc "Gitk: edit view"] $viewname($curview)"
4026 proc vieweditor {top n title} {
4027 global newviewname newviewopts viewfiles bgcolor
4028 global known_view_options NS
4031 wm title $top [concat $title [mc "-- criteria for selecting revisions"]]
4032 make_transient $top .
4035 ${NS}::frame $top.nfr
4036 ${NS}::label $top.nl -text [mc "View Name"]
4037 ${NS}::entry $top.name -width 20 -textvariable newviewname($n)
4038 pack $top.nfr -in $top -fill x -pady 5 -padx 3
4039 pack $top.nl -in $top.nfr -side left -padx {0 5}
4040 pack $top.name -in $top.nfr -side left -padx {0 25}
4046 foreach opt $known_view_options {
4047 set id [lindex $opt 0]
4048 set type [lindex $opt 1]
4049 set flags [lindex $opt 2]
4050 set title [eval [lindex $opt 4]]
4053 if {$flags eq "+" || $flags eq "*"} {
4054 set cframe $top.fr$cnt
4056 ${NS}::frame $cframe
4057 pack $cframe -in $top -fill x -pady 3 -padx 3
4058 set cexpand [expr {$flags eq "*"}]
4059 } elseif {$flags eq ".." || $flags eq "*."} {
4060 set cframe $top.fr$cnt
4062 ${NS}::frame $cframe
4063 pack $cframe -in $top -fill x -pady 3 -padx [list 15 3]
4064 set cexpand [expr {$flags eq "*."}]
4070 ${NS}::label $cframe.l_$id -text $title
4071 pack $cframe.l_$id -in $cframe -side left -pady [list 3 0] -anchor w
4072 } elseif {$type eq "b"} {
4073 ${NS}::checkbutton $cframe.c_$id -text $title -variable newviewopts($n,$id)
4074 pack $cframe.c_$id -in $cframe -side left \
4075 -padx [list $lxpad 0] -expand $cexpand -anchor w
4076 } elseif {[regexp {^r(\d+)$} $type type sz]} {
4077 regexp {^(.*_)} $id uselessvar button_id
4078 ${NS}::radiobutton $cframe.c_$id -text $title -variable newviewopts($n,$button_id) -value $sz
4079 pack $cframe.c_$id -in $cframe -side left \
4080 -padx [list $lxpad 0] -expand $cexpand -anchor w
4081 } elseif {[regexp {^t(\d+)$} $type type sz]} {
4082 ${NS}::label $cframe.l_$id -text $title
4083 ${NS}::entry $cframe.e_$id -width $sz -background $bgcolor \
4084 -textvariable newviewopts($n,$id)
4085 pack $cframe.l_$id -in $cframe -side left -padx [list $lxpad 0]
4086 pack $cframe.e_$id -in $cframe -side left -expand 1 -fill x
4087 } elseif {[regexp {^t(\d+)=$} $type type sz]} {
4088 ${NS}::label $cframe.l_$id -text $title
4089 ${NS}::entry $cframe.e_$id -width $sz -background $bgcolor \
4090 -textvariable newviewopts($n,$id)
4091 pack $cframe.l_$id -in $cframe -side top -pady [list 3 0] -anchor w
4092 pack $cframe.e_$id -in $cframe -side top -fill x
4093 } elseif {$type eq "path"} {
4094 ${NS}::label $top.l -text $title
4095 pack $top.l -in $top -side top -pady [list 3 0] -anchor w -padx 3
4096 text $top.t -width 40 -height 5 -background $bgcolor
4097 if {[info exists viewfiles($n)]} {
4098 foreach f $viewfiles($n) {
4099 $top.t insert end $f
4100 $top.t insert end "\n"
4102 $top.t delete {end - 1c} end
4103 $top.t mark set insert 0.0
4105 pack $top.t -in $top -side top -pady [list 0 5] -fill both -expand 1 -padx 3
4109 ${NS}::frame $top.buts
4110 ${NS}::button $top.buts.ok -text [mc "OK"] -command [list newviewok $top $n]
4111 ${NS}::button $top.buts.apply -text [mc "Apply (F5)"] -command [list newviewok $top $n 1]
4112 ${NS}::button $top.buts.can -text [mc "Cancel"] -command [list destroy $top]
4113 bind $top <Control-Return> [list newviewok $top $n]
4114 bind $top <F5> [list newviewok $top $n 1]
4115 bind $top <Escape> [list destroy $top]
4116 grid $top.buts.ok $top.buts.apply $top.buts.can
4117 grid columnconfigure $top.buts 0 -weight 1 -uniform a
4118 grid columnconfigure $top.buts 1 -weight 1 -uniform a
4119 grid columnconfigure $top.buts 2 -weight 1 -uniform a
4120 pack $top.buts -in $top -side top -fill x
4124 proc doviewmenu {m first cmd op argv} {
4125 set nmenu [$m index end]
4126 for {set i $first} {$i <= $nmenu} {incr i} {
4127 if {[$m entrycget $i -command] eq $cmd} {
4128 eval $m $op $i $argv
4134 proc allviewmenus {n op args} {
4137 doviewmenu .bar.view 5 [list showview $n] $op $args
4138 # doviewmenu $viewhlmenu 1 [list addvhighlight $n] $op $args
4141 proc newviewok {top n {apply 0}} {
4142 global nextviewnum newviewperm newviewname newishighlight
4143 global viewname viewfiles viewperm selectedview curview
4144 global viewargs viewargscmd newviewopts viewhlmenu
4147 set newargs [encode_view_opts $n]
4149 error_popup "[mc "Error in commit selection arguments:"] $err" $top
4153 foreach f [split [$top.t get 0.0 end] "\n"] {
4154 set ft [string trim $f]
4159 if {![info exists viewfiles($n)]} {
4160 # creating a new view
4162 set viewname($n) $newviewname($n)
4163 set viewperm($n) $newviewopts($n,perm)
4164 set viewfiles($n) $files
4165 set viewargs($n) $newargs
4166 set viewargscmd($n) $newviewopts($n,cmd)
4168 if {!$newishighlight} {
4171 run addvhighlight $n
4174 # editing an existing view
4175 set viewperm($n) $newviewopts($n,perm)
4176 if {$newviewname($n) ne $viewname($n)} {
4177 set viewname($n) $newviewname($n)
4178 doviewmenu .bar.view 5 [list showview $n] \
4179 entryconf [list -label $viewname($n)]
4180 # doviewmenu $viewhlmenu 1 [list addvhighlight $n] \
4181 # entryconf [list -label $viewname($n) -value $viewname($n)]
4183 if {$files ne $viewfiles($n) || $newargs ne $viewargs($n) || \
4184 $newviewopts($n,cmd) ne $viewargscmd($n)} {
4185 set viewfiles($n) $files
4186 set viewargs($n) $newargs
4187 set viewargscmd($n) $newviewopts($n,cmd)
4188 if {$curview == $n} {
4194 catch {destroy $top}
4198 global curview viewperm hlview selectedhlview
4200 if {$curview == 0} return
4201 if {[info exists hlview] && $hlview == $curview} {
4202 set selectedhlview [mc "None"]
4205 allviewmenus $curview delete
4206 set viewperm($curview) 0
4210 proc addviewmenu {n} {
4211 global viewname viewhlmenu
4213 .bar.view add radiobutton -label $viewname($n) \
4214 -command [list showview $n] -variable selectedview -value $n
4215 #$viewhlmenu add radiobutton -label $viewname($n) \
4216 # -command [list addvhighlight $n] -variable selectedhlview
4220 global curview cached_commitrow ordertok
4221 global displayorder parentlist rowidlist rowisopt rowfinal
4222 global colormap rowtextx nextcolor canvxmax
4223 global numcommits viewcomplete
4224 global selectedline currentid canv canvy0
4226 global pending_select mainheadid
4229 global hlview selectedhlview commitinterest
4231 if {$n == $curview} return
4233 set ymax [lindex [$canv cget -scrollregion] 3]
4234 set span [$canv yview]
4235 set ytop [expr {[lindex $span 0] * $ymax}]
4236 set ybot [expr {[lindex $span 1] * $ymax}]
4237 set yscreen [expr {($ybot - $ytop) / 2}]
4238 if {$selectedline ne {}} {
4239 set selid $currentid
4240 set y [yc $selectedline]
4241 if {$ytop < $y && $y < $ybot} {
4242 set yscreen [expr {$y - $ytop}]
4244 } elseif {[info exists pending_select]} {
4245 set selid $pending_select
4246 unset pending_select
4250 catch {unset treediffs}
4252 if {[info exists hlview] && $hlview == $n} {
4254 set selectedhlview [mc "None"]
4256 catch {unset commitinterest}
4257 catch {unset cached_commitrow}
4258 catch {unset ordertok}
4262 .bar.view entryconf [mca "Edit view..."] -state [expr {$n == 0? "disabled": "normal"}]
4263 .bar.view entryconf [mca "Delete view"] -state [expr {$n == 0? "disabled": "normal"}]
4266 if {![info exists viewcomplete($n)]} {
4276 set numcommits $commitidx($n)
4278 catch {unset colormap}
4279 catch {unset rowtextx}
4281 set canvxmax [$canv cget -width]
4287 if {$selid ne {} && [commitinview $selid $n]} {
4288 set row [rowofcommit $selid]
4289 # try to get the selected row in the same position on the screen
4290 set ymax [lindex [$canv cget -scrollregion] 3]
4291 set ytop [expr {[yc $row] - $yscreen}]
4295 set yf [expr {$ytop * 1.0 / $ymax}]
4297 allcanvs yview moveto $yf
4301 } elseif {!$viewcomplete($n)} {
4302 reset_pending_select $selid
4304 reset_pending_select {}
4306 if {[commitinview $pending_select $curview]} {
4307 selectline [rowofcommit $pending_select] 1
4309 set row [first_real_row]
4310 if {$row < $numcommits} {
4315 if {!$viewcomplete($n)} {
4316 if {$numcommits == 0} {
4317 show_status [mc "Reading commits..."]
4319 } elseif {$numcommits == 0} {
4320 show_status [mc "No commits selected"]
4324 # Stuff relating to the highlighting facility
4326 proc ishighlighted {id} {
4327 global vhighlights fhighlights nhighlights rhighlights
4329 if {[info exists nhighlights($id)] && $nhighlights($id) > 0} {
4330 return $nhighlights($id)
4332 if {[info exists vhighlights($id)] && $vhighlights($id) > 0} {
4333 return $vhighlights($id)
4335 if {[info exists fhighlights($id)] && $fhighlights($id) > 0} {
4336 return $fhighlights($id)
4338 if {[info exists rhighlights($id)] && $rhighlights($id) > 0} {
4339 return $rhighlights($id)
4344 proc bolden {id font} {
4345 global canv linehtag currentid boldids need_redisplay markedid
4347 # need_redisplay = 1 means the display is stale and about to be redrawn
4348 if {$need_redisplay} return
4350 $canv itemconf $linehtag($id) -font $font
4351 if {[info exists currentid] && $id eq $currentid} {
4353 set t [eval $canv create rect [$canv bbox $linehtag($id)] \
4354 -outline {{}} -tags secsel \
4355 -fill [$canv cget -selectbackground]]
4358 if {[info exists markedid] && $id eq $markedid} {
4363 proc bolden_name {id font} {
4364 global canv2 linentag currentid boldnameids need_redisplay
4366 if {$need_redisplay} return
4367 lappend boldnameids $id
4368 $canv2 itemconf $linentag($id) -font $font
4369 if {[info exists currentid] && $id eq $currentid} {
4370 $canv2 delete secsel
4371 set t [eval $canv2 create rect [$canv2 bbox $linentag($id)] \
4372 -outline {{}} -tags secsel \
4373 -fill [$canv2 cget -selectbackground]]
4382 foreach id $boldids {
4383 if {![ishighlighted $id]} {
4386 lappend stillbold $id
4389 set boldids $stillbold
4392 proc addvhighlight {n} {
4393 global hlview viewcomplete curview vhl_done commitidx
4395 if {[info exists hlview]} {
4399 if {$n != $curview && ![info exists viewcomplete($n)]} {
4402 set vhl_done $commitidx($hlview)
4403 if {$vhl_done > 0} {
4408 proc delvhighlight {} {
4409 global hlview vhighlights
4411 if {![info exists hlview]} return
4413 catch {unset vhighlights}
4417 proc vhighlightmore {} {
4418 global hlview vhl_done commitidx vhighlights curview
4420 set max $commitidx($hlview)
4421 set vr [visiblerows]
4422 set r0 [lindex $vr 0]
4423 set r1 [lindex $vr 1]
4424 for {set i $vhl_done} {$i < $max} {incr i} {
4425 set id [commitonrow $i $hlview]
4426 if {[commitinview $id $curview]} {
4427 set row [rowofcommit $id]
4428 if {$r0 <= $row && $row <= $r1} {
4429 if {![highlighted $row]} {
4430 bolden $id mainfontbold
4432 set vhighlights($id) 1
4440 proc askvhighlight {row id} {
4441 global hlview vhighlights iddrawn
4443 if {[commitinview $id $hlview]} {
4444 if {[info exists iddrawn($id)] && ![ishighlighted $id]} {
4445 bolden $id mainfontbold
4447 set vhighlights($id) 1
4449 set vhighlights($id) 0
4453 proc hfiles_change {} {
4454 global highlight_files filehighlight fhighlights fh_serial
4455 global highlight_paths
4457 if {[info exists filehighlight]} {
4458 # delete previous highlights
4459 catch {close $filehighlight}
4461 catch {unset fhighlights}
4463 unhighlight_filelist
4465 set highlight_paths {}
4466 after cancel do_file_hl $fh_serial
4468 if {$highlight_files ne {}} {
4469 after 300 do_file_hl $fh_serial
4473 proc gdttype_change {name ix op} {
4474 global gdttype highlight_files findstring findpattern
4477 if {$findstring ne {}} {
4478 if {$gdttype eq [mc "containing:"]} {
4479 if {$highlight_files ne {}} {
4480 set highlight_files {}
4485 if {$findpattern ne {}} {
4489 set highlight_files $findstring
4494 # enable/disable findtype/findloc menus too
4497 proc find_change {name ix op} {
4498 global gdttype findstring highlight_files
4501 if {$gdttype eq [mc "containing:"]} {
4504 if {$highlight_files ne $findstring} {
4505 set highlight_files $findstring
4512 proc findcom_change args {
4513 global nhighlights boldnameids
4514 global findpattern findtype findstring gdttype
4517 # delete previous highlights, if any
4518 foreach id $boldnameids {
4519 bolden_name $id mainfont
4522 catch {unset nhighlights}
4525 if {$gdttype ne [mc "containing:"] || $findstring eq {}} {
4527 } elseif {$findtype eq [mc "Regexp"]} {
4528 set findpattern $findstring
4530 set e [string map {"*" "\\*" "?" "\\?" "\[" "\\\[" "\\" "\\\\"} \
4532 set findpattern "*$e*"
4536 proc makepatterns {l} {
4539 set ee [string map {"*" "\\*" "?" "\\?" "\[" "\\\[" "\\" "\\\\"} $e]
4540 if {[string index $ee end] eq "/"} {
4550 proc do_file_hl {serial} {
4551 global highlight_files filehighlight highlight_paths gdttype fhl_list
4553 if {$gdttype eq [mc "touching paths:"]} {
4554 if {[catch {set paths [shellsplit $highlight_files]}]} return
4555 set highlight_paths [makepatterns $paths]
4557 set gdtargs [concat -- $paths]
4558 } elseif {$gdttype eq [mc "adding/removing string:"]} {
4559 set gdtargs [list "-S$highlight_files"]
4561 # must be "containing:", i.e. we're searching commit info
4564 set cmd [concat | git diff-tree -r -s --stdin $gdtargs]
4565 set filehighlight [open $cmd r+]
4566 fconfigure $filehighlight -blocking 0
4567 filerun $filehighlight readfhighlight
4573 proc flushhighlights {} {
4574 global filehighlight fhl_list
4576 if {[info exists filehighlight]} {
4578 puts $filehighlight ""
4579 flush $filehighlight
4583 proc askfilehighlight {row id} {
4584 global filehighlight fhighlights fhl_list
4586 lappend fhl_list $id
4587 set fhighlights($id) -1
4588 puts $filehighlight $id
4591 proc readfhighlight {} {
4592 global filehighlight fhighlights curview iddrawn
4593 global fhl_list find_dirn
4595 if {![info exists filehighlight]} {
4599 while {[incr nr] <= 100 && [gets $filehighlight line] >= 0} {
4600 set line [string trim $line]
4601 set i [lsearch -exact $fhl_list $line]
4602 if {$i < 0} continue
4603 for {set j 0} {$j < $i} {incr j} {
4604 set id [lindex $fhl_list $j]
4605 set fhighlights($id) 0
4607 set fhl_list [lrange $fhl_list [expr {$i+1}] end]
4608 if {$line eq {}} continue
4609 if {![commitinview $line $curview]} continue
4610 if {[info exists iddrawn($line)] && ![ishighlighted $line]} {
4611 bolden $line mainfontbold
4613 set fhighlights($line) 1
4615 if {[eof $filehighlight]} {
4617 puts "oops, git diff-tree died"
4618 catch {close $filehighlight}
4622 if {[info exists find_dirn]} {
4628 proc doesmatch {f} {
4629 global findtype findpattern
4631 if {$findtype eq [mc "Regexp"]} {
4632 return [regexp $findpattern $f]
4633 } elseif {$findtype eq [mc "IgnCase"]} {
4634 return [string match -nocase $findpattern $f]
4636 return [string match $findpattern $f]
4640 proc askfindhighlight {row id} {
4641 global nhighlights commitinfo iddrawn
4643 global markingmatches
4645 if {![info exists commitinfo($id)]} {
4648 set info $commitinfo($id)
4650 set fldtypes [list [mc Headline] [mc Author] [mc Date] [mc Committer] [mc CDate] [mc Comments]]
4651 foreach f $info ty $fldtypes {
4652 if {($findloc eq [mc "All fields"] || $findloc eq $ty) &&
4654 if {$ty eq [mc "Author"]} {
4661 if {$isbold && [info exists iddrawn($id)]} {
4662 if {![ishighlighted $id]} {
4663 bolden $id mainfontbold
4665 bolden_name $id mainfontbold
4668 if {$markingmatches} {
4669 markrowmatches $row $id
4672 set nhighlights($id) $isbold
4675 proc markrowmatches {row id} {
4676 global canv canv2 linehtag linentag commitinfo findloc
4678 set headline [lindex $commitinfo($id) 0]
4679 set author [lindex $commitinfo($id) 1]
4680 $canv delete match$row
4681 $canv2 delete match$row
4682 if {$findloc eq [mc "All fields"] || $findloc eq [mc "Headline"]} {
4683 set m [findmatches $headline]
4685 markmatches $canv $row $headline $linehtag($id) $m \
4686 [$canv itemcget $linehtag($id) -font] $row
4689 if {$findloc eq [mc "All fields"] || $findloc eq [mc "Author"]} {
4690 set m [findmatches $author]
4692 markmatches $canv2 $row $author $linentag($id) $m \
4693 [$canv2 itemcget $linentag($id) -font] $row
4698 proc vrel_change {name ix op} {
4699 global highlight_related
4702 if {$highlight_related ne [mc "None"]} {
4707 # prepare for testing whether commits are descendents or ancestors of a
4708 proc rhighlight_sel {a} {
4709 global descendent desc_todo ancestor anc_todo
4710 global highlight_related
4712 catch {unset descendent}
4713 set desc_todo [list $a]
4714 catch {unset ancestor}
4715 set anc_todo [list $a]
4716 if {$highlight_related ne [mc "None"]} {
4722 proc rhighlight_none {} {
4725 catch {unset rhighlights}
4729 proc is_descendent {a} {
4730 global curview children descendent desc_todo
4733 set la [rowofcommit $a]
4737 for {set i 0} {$i < [llength $todo]} {incr i} {
4738 set do [lindex $todo $i]
4739 if {[rowofcommit $do] < $la} {
4740 lappend leftover $do
4743 foreach nk $children($v,$do) {
4744 if {![info exists descendent($nk)]} {
4745 set descendent($nk) 1
4753 set desc_todo [concat $leftover [lrange $todo [expr {$i+1}] end]]
4757 set descendent($a) 0
4758 set desc_todo $leftover
4761 proc is_ancestor {a} {
4762 global curview parents ancestor anc_todo
4765 set la [rowofcommit $a]
4769 for {set i 0} {$i < [llength $todo]} {incr i} {
4770 set do [lindex $todo $i]
4771 if {![commitinview $do $v] || [rowofcommit $do] > $la} {
4772 lappend leftover $do
4775 foreach np $parents($v,$do) {
4776 if {![info exists ancestor($np)]} {
4785 set anc_todo [concat $leftover [lrange $todo [expr {$i+1}] end]]
4790 set anc_todo $leftover
4793 proc askrelhighlight {row id} {
4794 global descendent highlight_related iddrawn rhighlights
4795 global selectedline ancestor
4797 if {$selectedline eq {}} return
4799 if {$highlight_related eq [mc "Descendant"] ||
4800 $highlight_related eq [mc "Not descendant"]} {
4801 if {![info exists descendent($id)]} {
4804 if {$descendent($id) == ($highlight_related eq [mc "Descendant"])} {
4807 } elseif {$highlight_related eq [mc "Ancestor"] ||
4808 $highlight_related eq [mc "Not ancestor"]} {
4809 if {![info exists ancestor($id)]} {
4812 if {$ancestor($id) == ($highlight_related eq [mc "Ancestor"])} {
4816 if {[info exists iddrawn($id)]} {
4817 if {$isbold && ![ishighlighted $id]} {
4818 bolden $id mainfontbold
4821 set rhighlights($id) $isbold
4824 # Graph layout functions
4826 proc shortids {ids} {
4829 if {[llength $id] > 1} {
4830 lappend res [shortids $id]
4831 } elseif {[regexp {^[0-9a-f]{40}$} $id]} {
4832 lappend res [string range $id 0 7]
4843 for {set mask 1} {$mask <= $n} {incr mask $mask} {
4844 if {($n & $mask) != 0} {
4845 set ret [concat $ret $o]
4847 set o [concat $o $o]
4852 proc ordertoken {id} {
4853 global ordertok curview varcid varcstart varctok curview parents children
4854 global nullid nullid2
4856 if {[info exists ordertok($id)]} {
4857 return $ordertok($id)
4862 if {[info exists varcid($curview,$id)]} {
4863 set a $varcid($curview,$id)
4864 set p [lindex $varcstart($curview) $a]
4866 set p [lindex $children($curview,$id) 0]
4868 if {[info exists ordertok($p)]} {
4869 set tok $ordertok($p)
4872 set id [first_real_child $curview,$p]
4875 set tok [lindex $varctok($curview) $varcid($curview,$p)]
4878 if {[llength $parents($curview,$id)] == 1} {
4879 lappend todo [list $p {}]
4881 set j [lsearch -exact $parents($curview,$id) $p]
4883 puts "oops didn't find [shortids $p] in parents of [shortids $id]"
4885 lappend todo [list $p [strrep $j]]
4888 for {set i [llength $todo]} {[incr i -1] >= 0} {} {
4889 set p [lindex $todo $i 0]
4890 append tok [lindex $todo $i 1]
4891 set ordertok($p) $tok
4893 set ordertok($origid) $tok
4897 # Work out where id should go in idlist so that order-token
4898 # values increase from left to right
4899 proc idcol {idlist id {i 0}} {
4900 set t [ordertoken $id]
4904 if {$i >= [llength $idlist] || $t < [ordertoken [lindex $idlist $i]]} {
4905 if {$i > [llength $idlist]} {
4906 set i [llength $idlist]
4908 while {[incr i -1] >= 0 && $t < [ordertoken [lindex $idlist $i]]} {}
4911 if {$t > [ordertoken [lindex $idlist $i]]} {
4912 while {[incr i] < [llength $idlist] &&
4913 $t >= [ordertoken [lindex $idlist $i]]} {}
4919 proc initlayout {} {
4920 global rowidlist rowisopt rowfinal displayorder parentlist
4921 global numcommits canvxmax canv
4923 global colormap rowtextx
4932 set canvxmax [$canv cget -width]
4933 catch {unset colormap}
4934 catch {unset rowtextx}
4938 proc setcanvscroll {} {
4939 global canv canv2 canv3 numcommits linespc canvxmax canvy0
4940 global lastscrollset lastscrollrows
4942 set ymax [expr {$canvy0 + ($numcommits - 0.5) * $linespc + 2}]
4943 $canv conf -scrollregion [list 0 0 $canvxmax $ymax]
4944 $canv2 conf -scrollregion [list 0 0 0 $ymax]
4945 $canv3 conf -scrollregion [list 0 0 0 $ymax]
4946 set lastscrollset [clock clicks -milliseconds]
4947 set lastscrollrows $numcommits
4950 proc visiblerows {} {
4951 global canv numcommits linespc
4953 set ymax [lindex [$canv cget -scrollregion] 3]
4954 if {$ymax eq {} || $ymax == 0} return
4956 set y0 [expr {int([lindex $f 0] * $ymax)}]
4957 set r0 [expr {int(($y0 - 3) / $linespc) - 1}]
4961 set y1 [expr {int([lindex $f 1] * $ymax)}]
4962 set r1 [expr {int(($y1 - 3) / $linespc) + 1}]
4963 if {$r1 >= $numcommits} {
4964 set r1 [expr {$numcommits - 1}]
4966 return [list $r0 $r1]
4969 proc layoutmore {} {
4970 global commitidx viewcomplete curview
4971 global numcommits pending_select curview
4972 global lastscrollset lastscrollrows
4974 if {$lastscrollrows < 100 || $viewcomplete($curview) ||
4975 [clock clicks -milliseconds] - $lastscrollset > 500} {
4978 if {[info exists pending_select] &&
4979 [commitinview $pending_select $curview]} {
4981 selectline [rowofcommit $pending_select] 1
4986 # With path limiting, we mightn't get the actual HEAD commit,
4987 # so ask git rev-list what is the first ancestor of HEAD that
4988 # touches a file in the path limit.
4989 proc get_viewmainhead {view} {
4990 global viewmainheadid vfilelimit viewinstances mainheadid
4993 set rfd [open [concat | git rev-list -1 $mainheadid \
4994 -- $vfilelimit($view)] r]
4995 set j [reg_instance $rfd]
4996 lappend viewinstances($view) $j
4997 fconfigure $rfd -blocking 0
4998 filerun $rfd [list getviewhead $rfd $j $view]
4999 set viewmainheadid($curview) {}
5003 # git rev-list should give us just 1 line to use as viewmainheadid($view)
5004 proc getviewhead {fd inst view} {
5005 global viewmainheadid commfd curview viewinstances showlocalchanges
5008 if {[gets $fd line] < 0} {
5012 } elseif {[string length $line] == 40 && [string is xdigit $line]} {
5015 set viewmainheadid($view) $id
5018 set i [lsearch -exact $viewinstances($view) $inst]
5020 set viewinstances($view) [lreplace $viewinstances($view) $i $i]
5022 if {$showlocalchanges && $id ne {} && $view == $curview} {
5028 proc doshowlocalchanges {} {
5029 global curview viewmainheadid
5031 if {$viewmainheadid($curview) eq {}} return
5032 if {[commitinview $viewmainheadid($curview) $curview]} {
5035 interestedin $viewmainheadid($curview) dodiffindex
5039 proc dohidelocalchanges {} {
5040 global nullid nullid2 lserial curview
5042 if {[commitinview $nullid $curview]} {
5043 removefakerow $nullid
5045 if {[commitinview $nullid2 $curview]} {
5046 removefakerow $nullid2
5051 # spawn off a process to do git diff-index --cached HEAD
5052 proc dodiffindex {} {
5053 global lserial showlocalchanges vfilelimit curview
5056 if {!$showlocalchanges || !$isworktree} return
5058 set cmd "|git diff-index --cached HEAD"
5059 if {$vfilelimit($curview) ne {}} {
5060 set cmd [concat $cmd -- $vfilelimit($curview)]
5062 set fd [open $cmd r]
5063 fconfigure $fd -blocking 0
5064 set i [reg_instance $fd]
5065 filerun $fd [list readdiffindex $fd $lserial $i]
5068 proc readdiffindex {fd serial inst} {
5069 global viewmainheadid nullid nullid2 curview commitinfo commitdata lserial
5073 if {[gets $fd line] < 0} {
5079 # we only need to see one line and we don't really care what it says...
5082 if {$serial != $lserial} {
5086 # now see if there are any local changes not checked in to the index
5087 set cmd "|git diff-files"
5088 if {$vfilelimit($curview) ne {}} {
5089 set cmd [concat $cmd -- $vfilelimit($curview)]
5091 set fd [open $cmd r]
5092 fconfigure $fd -blocking 0
5093 set i [reg_instance $fd]
5094 filerun $fd [list readdifffiles $fd $serial $i]
5096 if {$isdiff && ![commitinview $nullid2 $curview]} {
5097 # add the line for the changes in the index to the graph
5098 set hl [mc "Local changes checked in to index but not committed"]
5099 set commitinfo($nullid2) [list $hl {} {} {} {} " $hl\n"]
5100 set commitdata($nullid2) "\n $hl\n"
5101 if {[commitinview $nullid $curview]} {
5102 removefakerow $nullid
5104 insertfakerow $nullid2 $viewmainheadid($curview)
5105 } elseif {!$isdiff && [commitinview $nullid2 $curview]} {
5106 if {[commitinview $nullid $curview]} {
5107 removefakerow $nullid
5109 removefakerow $nullid2
5114 proc readdifffiles {fd serial inst} {
5115 global viewmainheadid nullid nullid2 curview
5116 global commitinfo commitdata lserial
5119 if {[gets $fd line] < 0} {
5125 # we only need to see one line and we don't really care what it says...
5128 if {$serial != $lserial} {
5132 if {$isdiff && ![commitinview $nullid $curview]} {
5133 # add the line for the local diff to the graph
5134 set hl [mc "Local uncommitted changes, not checked in to index"]
5135 set commitinfo($nullid) [list $hl {} {} {} {} " $hl\n"]
5136 set commitdata($nullid) "\n $hl\n"
5137 if {[commitinview $nullid2 $curview]} {
5140 set p $viewmainheadid($curview)
5142 insertfakerow $nullid $p
5143 } elseif {!$isdiff && [commitinview $nullid $curview]} {
5144 removefakerow $nullid
5149 proc nextuse {id row} {
5150 global curview children
5152 if {[info exists children($curview,$id)]} {
5153 foreach kid $children($curview,$id) {
5154 if {![commitinview $kid $curview]} {
5157 if {[rowofcommit $kid] > $row} {
5158 return [rowofcommit $kid]
5162 if {[commitinview $id $curview]} {
5163 return [rowofcommit $id]
5168 proc prevuse {id row} {
5169 global curview children
5172 if {[info exists children($curview,$id)]} {
5173 foreach kid $children($curview,$id) {
5174 if {![commitinview $kid $curview]} break
5175 if {[rowofcommit $kid] < $row} {
5176 set ret [rowofcommit $kid]
5183 proc make_idlist {row} {
5184 global displayorder parentlist uparrowlen downarrowlen mingaplen
5185 global commitidx curview children
5187 set r [expr {$row - $mingaplen - $downarrowlen - 1}]
5191 set ra [expr {$row - $downarrowlen}]
5195 set rb [expr {$row + $uparrowlen}]
5196 if {$rb > $commitidx($curview)} {
5197 set rb $commitidx($curview)
5199 make_disporder $r [expr {$rb + 1}]
5201 for {} {$r < $ra} {incr r} {
5202 set nextid [lindex $displayorder [expr {$r + 1}]]
5203 foreach p [lindex $parentlist $r] {
5204 if {$p eq $nextid} continue
5205 set rn [nextuse $p $r]
5207 $rn <= $r + $downarrowlen + $mingaplen + $uparrowlen} {
5208 lappend ids [list [ordertoken $p] $p]
5212 for {} {$r < $row} {incr r} {
5213 set nextid [lindex $displayorder [expr {$r + 1}]]
5214 foreach p [lindex $parentlist $r] {
5215 if {$p eq $nextid} continue
5216 set rn [nextuse $p $r]
5217 if {$rn < 0 || $rn >= $row} {
5218 lappend ids [list [ordertoken $p] $p]
5222 set id [lindex $displayorder $row]
5223 lappend ids [list [ordertoken $id] $id]
5225 foreach p [lindex $parentlist $r] {
5226 set firstkid [lindex $children($curview,$p) 0]
5227 if {[rowofcommit $firstkid] < $row} {
5228 lappend ids [list [ordertoken $p] $p]
5232 set id [lindex $displayorder $r]
5234 set firstkid [lindex $children($curview,$id) 0]
5235 if {$firstkid ne {} && [rowofcommit $firstkid] < $row} {
5236 lappend ids [list [ordertoken $id] $id]
5241 foreach idx [lsort -unique $ids] {
5242 lappend idlist [lindex $idx 1]
5247 proc rowsequal {a b} {
5248 while {[set i [lsearch -exact $a {}]] >= 0} {
5249 set a [lreplace $a $i $i]
5251 while {[set i [lsearch -exact $b {}]] >= 0} {
5252 set b [lreplace $b $i $i]
5254 return [expr {$a eq $b}]
5257 proc makeupline {id row rend col} {
5258 global rowidlist uparrowlen downarrowlen mingaplen
5260 for {set r $rend} {1} {set r $rstart} {
5261 set rstart [prevuse $id $r]
5262 if {$rstart < 0} return
5263 if {$rstart < $row} break
5265 if {$rstart + $uparrowlen + $mingaplen + $downarrowlen < $rend} {
5266 set rstart [expr {$rend - $uparrowlen - 1}]
5268 for {set r $rstart} {[incr r] <= $row} {} {
5269 set idlist [lindex $rowidlist $r]
5270 if {$idlist ne {} && [lsearch -exact $idlist $id] < 0} {
5271 set col [idcol $idlist $id $col]
5272 lset rowidlist $r [linsert $idlist $col $id]
5278 proc layoutrows {row endrow} {
5279 global rowidlist rowisopt rowfinal displayorder
5280 global uparrowlen downarrowlen maxwidth mingaplen
5281 global children parentlist
5282 global commitidx viewcomplete curview
5284 make_disporder [expr {$row - 1}] [expr {$endrow + $uparrowlen}]
5287 set rm1 [expr {$row - 1}]
5288 foreach id [lindex $rowidlist $rm1] {
5293 set final [lindex $rowfinal $rm1]
5295 for {} {$row < $endrow} {incr row} {
5296 set rm1 [expr {$row - 1}]
5297 if {$rm1 < 0 || $idlist eq {}} {
5298 set idlist [make_idlist $row]
5301 set id [lindex $displayorder $rm1]
5302 set col [lsearch -exact $idlist $id]
5303 set idlist [lreplace $idlist $col $col]
5304 foreach p [lindex $parentlist $rm1] {
5305 if {[lsearch -exact $idlist $p] < 0} {
5306 set col [idcol $idlist $p $col]
5307 set idlist [linsert $idlist $col $p]
5308 # if not the first child, we have to insert a line going up
5309 if {$id ne [lindex $children($curview,$p) 0]} {
5310 makeupline $p $rm1 $row $col
5314 set id [lindex $displayorder $row]
5315 if {$row > $downarrowlen} {
5316 set termrow [expr {$row - $downarrowlen - 1}]
5317 foreach p [lindex $parentlist $termrow] {
5318 set i [lsearch -exact $idlist $p]
5319 if {$i < 0} continue
5320 set nr [nextuse $p $termrow]
5321 if {$nr < 0 || $nr >= $row + $mingaplen + $uparrowlen} {
5322 set idlist [lreplace $idlist $i $i]
5326 set col [lsearch -exact $idlist $id]
5328 set col [idcol $idlist $id]
5329 set idlist [linsert $idlist $col $id]
5330 if {$children($curview,$id) ne {}} {
5331 makeupline $id $rm1 $row $col
5334 set r [expr {$row + $uparrowlen - 1}]
5335 if {$r < $commitidx($curview)} {
5337 foreach p [lindex $parentlist $r] {
5338 if {[lsearch -exact $idlist $p] >= 0} continue
5339 set fk [lindex $children($curview,$p) 0]
5340 if {[rowofcommit $fk] < $row} {
5341 set x [idcol $idlist $p $x]
5342 set idlist [linsert $idlist $x $p]
5345 if {[incr r] < $commitidx($curview)} {
5346 set p [lindex $displayorder $r]
5347 if {[lsearch -exact $idlist $p] < 0} {
5348 set fk [lindex $children($curview,$p) 0]
5349 if {$fk ne {} && [rowofcommit $fk] < $row} {
5350 set x [idcol $idlist $p $x]
5351 set idlist [linsert $idlist $x $p]
5357 if {$final && !$viewcomplete($curview) &&
5358 $row + $uparrowlen + $mingaplen + $downarrowlen
5359 >= $commitidx($curview)} {
5362 set l [llength $rowidlist]
5364 lappend rowidlist $idlist
5366 lappend rowfinal $final
5367 } elseif {$row < $l} {
5368 if {![rowsequal $idlist [lindex $rowidlist $row]]} {
5369 lset rowidlist $row $idlist
5372 lset rowfinal $row $final
5374 set pad [ntimes [expr {$row - $l}] {}]
5375 set rowidlist [concat $rowidlist $pad]
5376 lappend rowidlist $idlist
5377 set rowfinal [concat $rowfinal $pad]
5378 lappend rowfinal $final
5379 set rowisopt [concat $rowisopt [ntimes [expr {$row - $l + 1}] 0]]
5385 proc changedrow {row} {
5386 global displayorder iddrawn rowisopt need_redisplay
5388 set l [llength $rowisopt]
5390 lset rowisopt $row 0
5391 if {$row + 1 < $l} {
5392 lset rowisopt [expr {$row + 1}] 0
5393 if {$row + 2 < $l} {
5394 lset rowisopt [expr {$row + 2}] 0
5398 set id [lindex $displayorder $row]
5399 if {[info exists iddrawn($id)]} {
5400 set need_redisplay 1
5404 proc insert_pad {row col npad} {
5407 set pad [ntimes $npad {}]
5408 set idlist [lindex $rowidlist $row]
5409 set bef [lrange $idlist 0 [expr {$col - 1}]]
5410 set aft [lrange $idlist $col end]
5411 set i [lsearch -exact $aft {}]
5413 set aft [lreplace $aft $i $i]
5415 lset rowidlist $row [concat $bef $pad $aft]
5419 proc optimize_rows {row col endrow} {
5420 global rowidlist rowisopt displayorder curview children
5425 for {} {$row < $endrow} {incr row; set col 0} {
5426 if {[lindex $rowisopt $row]} continue
5428 set y0 [expr {$row - 1}]
5429 set ym [expr {$row - 2}]
5430 set idlist [lindex $rowidlist $row]
5431 set previdlist [lindex $rowidlist $y0]
5432 if {$idlist eq {} || $previdlist eq {}} continue
5434 set pprevidlist [lindex $rowidlist $ym]
5435 if {$pprevidlist eq {}} continue
5441 for {} {$col < [llength $idlist]} {incr col} {
5442 set id [lindex $idlist $col]
5443 if {[lindex $previdlist $col] eq $id} continue
5448 set x0 [lsearch -exact $previdlist $id]
5449 if {$x0 < 0} continue
5450 set z [expr {$x0 - $col}]
5454 set xm [lsearch -exact $pprevidlist $id]
5456 set z0 [expr {$xm - $x0}]
5460 # if row y0 is the first child of $id then it's not an arrow
5461 if {[lindex $children($curview,$id) 0] ne
5462 [lindex $displayorder $y0]} {
5466 if {!$isarrow && $id ne [lindex $displayorder $row] &&
5467 [lsearch -exact [lindex $rowidlist [expr {$row+1}]] $id] < 0} {
5470 # Looking at lines from this row to the previous row,
5471 # make them go straight up if they end in an arrow on
5472 # the previous row; otherwise make them go straight up
5474 if {$z < -1 || ($z < 0 && $isarrow)} {
5475 # Line currently goes left too much;
5476 # insert pads in the previous row, then optimize it
5477 set npad [expr {-1 - $z + $isarrow}]
5478 insert_pad $y0 $x0 $npad
5480 optimize_rows $y0 $x0 $row
5482 set previdlist [lindex $rowidlist $y0]
5483 set x0 [lsearch -exact $previdlist $id]
5484 set z [expr {$x0 - $col}]
5486 set pprevidlist [lindex $rowidlist $ym]
5487 set xm [lsearch -exact $pprevidlist $id]
5488 set z0 [expr {$xm - $x0}]
5490 } elseif {$z > 1 || ($z > 0 && $isarrow)} {
5491 # Line currently goes right too much;
5492 # insert pads in this line
5493 set npad [expr {$z - 1 + $isarrow}]
5494 insert_pad $row $col $npad
5495 set idlist [lindex $rowidlist $row]
5497 set z [expr {$x0 - $col}]
5500 if {$z0 eq {} && !$isarrow && $ym >= 0} {
5501 # this line links to its first child on row $row-2
5502 set id [lindex $displayorder $ym]
5503 set xc [lsearch -exact $pprevidlist $id]
5505 set z0 [expr {$xc - $x0}]
5508 # avoid lines jigging left then immediately right
5509 if {$z0 ne {} && $z < 0 && $z0 > 0} {
5510 insert_pad $y0 $x0 1
5512 optimize_rows $y0 $x0 $row
5513 set previdlist [lindex $rowidlist $y0]
5517 # Find the first column that doesn't have a line going right
5518 for {set col [llength $idlist]} {[incr col -1] >= 0} {} {
5519 set id [lindex $idlist $col]
5520 if {$id eq {}} break
5521 set x0 [lsearch -exact $previdlist $id]
5523 # check if this is the link to the first child
5524 set kid [lindex $displayorder $y0]
5525 if {[lindex $children($curview,$id) 0] eq $kid} {
5526 # it is, work out offset to child
5527 set x0 [lsearch -exact $previdlist $kid]
5530 if {$x0 <= $col} break
5532 # Insert a pad at that column as long as it has a line and
5533 # isn't the last column
5534 if {$x0 >= 0 && [incr col] < [llength $idlist]} {
5535 set idlist [linsert $idlist $col {}]
5536 lset rowidlist $row $idlist
5544 global canvx0 linespc
5545 return [expr {$canvx0 + $col * $linespc}]
5549 global canvy0 linespc
5550 return [expr {$canvy0 + $row * $linespc}]
5553 proc linewidth {id} {
5554 global thickerline lthickness
5557 if {[info exists thickerline] && $id eq $thickerline} {
5558 set wid [expr {2 * $lthickness}]
5563 proc rowranges {id} {
5564 global curview children uparrowlen downarrowlen
5567 set kids $children($curview,$id)
5573 foreach child $kids {
5574 if {![commitinview $child $curview]} break
5575 set row [rowofcommit $child]
5576 if {![info exists prev]} {
5577 lappend ret [expr {$row + 1}]
5579 if {$row <= $prevrow} {
5580 puts "oops children of [shortids $id] out of order [shortids $child] $row <= [shortids $prev] $prevrow"
5582 # see if the line extends the whole way from prevrow to row
5583 if {$row > $prevrow + $uparrowlen + $downarrowlen &&
5584 [lsearch -exact [lindex $rowidlist \
5585 [expr {int(($row + $prevrow) / 2)}]] $id] < 0} {
5586 # it doesn't, see where it ends
5587 set r [expr {$prevrow + $downarrowlen}]
5588 if {[lsearch -exact [lindex $rowidlist $r] $id] < 0} {
5589 while {[incr r -1] > $prevrow &&
5590 [lsearch -exact [lindex $rowidlist $r] $id] < 0} {}
5592 while {[incr r] <= $row &&
5593 [lsearch -exact [lindex $rowidlist $r] $id] >= 0} {}
5597 # see where it starts up again
5598 set r [expr {$row - $uparrowlen}]
5599 if {[lsearch -exact [lindex $rowidlist $r] $id] < 0} {
5600 while {[incr r] < $row &&
5601 [lsearch -exact [lindex $rowidlist $r] $id] < 0} {}
5603 while {[incr r -1] >= $prevrow &&
5604 [lsearch -exact [lindex $rowidlist $r] $id] >= 0} {}
5610 if {$child eq $id} {
5619 proc drawlineseg {id row endrow arrowlow} {
5620 global rowidlist displayorder iddrawn linesegs
5621 global canv colormap linespc curview maxlinelen parentlist
5623 set cols [list [lsearch -exact [lindex $rowidlist $row] $id]]
5624 set le [expr {$row + 1}]
5627 set c [lsearch -exact [lindex $rowidlist $le] $id]
5633 set x [lindex $displayorder $le]
5638 if {[info exists iddrawn($x)] || $le == $endrow} {
5639 set c [lsearch -exact [lindex $rowidlist [expr {$le+1}]] $id]
5655 if {[info exists linesegs($id)]} {
5656 set lines $linesegs($id)
5658 set r0 [lindex $li 0]
5660 if {$r0 == $le && [lindex $li 1] - $row <= $maxlinelen} {
5670 set li [lindex $lines [expr {$i-1}]]
5671 set r1 [lindex $li 1]
5672 if {$r1 == $row && $le - [lindex $li 0] <= $maxlinelen} {
5677 set x [lindex $cols [expr {$le - $row}]]
5678 set xp [lindex $cols [expr {$le - 1 - $row}]]
5679 set dir [expr {$xp - $x}]
5681 set ith [lindex $lines $i 2]
5682 set coords [$canv coords $ith]
5683 set ah [$canv itemcget $ith -arrow]
5684 set arrowhigh [expr {$ah eq "first" || $ah eq "both"}]
5685 set x2 [lindex $cols [expr {$le + 1 - $row}]]
5686 if {$x2 ne {} && $x - $x2 == $dir} {
5687 set coords [lrange $coords 0 end-2]
5690 set coords [list [xc $le $x] [yc $le]]
5693 set itl [lindex $lines [expr {$i-1}] 2]
5694 set al [$canv itemcget $itl -arrow]
5695 set arrowlow [expr {$al eq "last" || $al eq "both"}]
5696 } elseif {$arrowlow} {
5697 if {[lsearch -exact [lindex $rowidlist [expr {$row-1}]] $id] >= 0 ||
5698 [lsearch -exact [lindex $parentlist [expr {$row-1}]] $id] >= 0} {
5702 set arrow [lindex {none first last both} [expr {$arrowhigh + 2*$arrowlow}]]
5703 for {set y $le} {[incr y -1] > $row} {} {
5705 set xp [lindex $cols [expr {$y - 1 - $row}]]
5706 set ndir [expr {$xp - $x}]
5707 if {$dir != $ndir || $xp < 0} {
5708 lappend coords [xc $y $x] [yc $y]
5714 # join parent line to first child
5715 set ch [lindex $displayorder $row]
5716 set xc [lsearch -exact [lindex $rowidlist $row] $ch]
5718 puts "oops: drawlineseg: child $ch not on row $row"
5719 } elseif {$xc != $x} {
5720 if {($arrowhigh && $le == $row + 1) || $dir == 0} {
5721 set d [expr {int(0.5 * $linespc)}]
5724 set x2 [expr {$x1 - $d}]
5726 set x2 [expr {$x1 + $d}]
5729 set y1 [expr {$y2 + $d}]
5730 lappend coords $x1 $y1 $x2 $y2
5731 } elseif {$xc < $x - 1} {
5732 lappend coords [xc $row [expr {$x-1}]] [yc $row]
5733 } elseif {$xc > $x + 1} {
5734 lappend coords [xc $row [expr {$x+1}]] [yc $row]
5738 lappend coords [xc $row $x] [yc $row]
5740 set xn [xc $row $xp]
5742 lappend coords $xn $yn
5746 set t [$canv create line $coords -width [linewidth $id] \
5747 -fill $colormap($id) -tags lines.$id -arrow $arrow]
5750 set lines [linsert $lines $i [list $row $le $t]]
5752 $canv coords $ith $coords
5753 if {$arrow ne $ah} {
5754 $canv itemconf $ith -arrow $arrow
5756 lset lines $i 0 $row
5759 set xo [lsearch -exact [lindex $rowidlist [expr {$row - 1}]] $id]
5760 set ndir [expr {$xo - $xp}]
5761 set clow [$canv coords $itl]
5762 if {$dir == $ndir} {
5763 set clow [lrange $clow 2 end]
5765 set coords [concat $coords $clow]
5767 lset lines [expr {$i-1}] 1 $le
5769 # coalesce two pieces
5771 set b [lindex $lines [expr {$i-1}] 0]
5772 set e [lindex $lines $i 1]
5773 set lines [lreplace $lines [expr {$i-1}] $i [list $b $e $itl]]
5775 $canv coords $itl $coords
5776 if {$arrow ne $al} {
5777 $canv itemconf $itl -arrow $arrow
5781 set linesegs($id) $lines
5785 proc drawparentlinks {id row} {
5786 global rowidlist canv colormap curview parentlist
5787 global idpos linespc
5789 set rowids [lindex $rowidlist $row]
5790 set col [lsearch -exact $rowids $id]
5791 if {$col < 0} return
5792 set olds [lindex $parentlist $row]
5793 set row2 [expr {$row + 1}]
5794 set x [xc $row $col]
5797 set d [expr {int(0.5 * $linespc)}]
5798 set ymid [expr {$y + $d}]
5799 set ids [lindex $rowidlist $row2]
5800 # rmx = right-most X coord used
5803 set i [lsearch -exact $ids $p]
5805 puts "oops, parent $p of $id not in list"
5808 set x2 [xc $row2 $i]
5812 set j [lsearch -exact $rowids $p]
5814 # drawlineseg will do this one for us
5818 # should handle duplicated parents here...
5819 set coords [list $x $y]
5821 # if attaching to a vertical segment, draw a smaller
5822 # slant for visual distinctness
5825 lappend coords [expr {$x2 + $d}] $y $x2 $ymid
5827 lappend coords [expr {$x2 - $d}] $y $x2 $ymid
5829 } elseif {$i < $col && $i < $j} {
5830 # segment slants towards us already
5831 lappend coords [xc $row $j] $y
5833 if {$i < $col - 1} {
5834 lappend coords [expr {$x2 + $linespc}] $y
5835 } elseif {$i > $col + 1} {
5836 lappend coords [expr {$x2 - $linespc}] $y
5838 lappend coords $x2 $y2
5841 lappend coords $x2 $y2
5843 set t [$canv create line $coords -width [linewidth $p] \
5844 -fill $colormap($p) -tags lines.$p]
5848 if {$rmx > [lindex $idpos($id) 1]} {
5849 lset idpos($id) 1 $rmx
5854 proc drawlines {id} {
5857 $canv itemconf lines.$id -width [linewidth $id]
5860 proc drawcmittext {id row col} {
5861 global linespc canv canv2 canv3 fgcolor curview
5862 global cmitlisted commitinfo rowidlist parentlist
5863 global rowtextx idpos idtags idheads idotherrefs
5864 global linehtag linentag linedtag selectedline
5865 global canvxmax boldids boldnameids fgcolor markedid
5866 global mainheadid nullid nullid2 circleitem circlecolors ctxbut
5868 # listed is 0 for boundary, 1 for normal, 2 for negative, 3 for left, 4 for right
5869 set listed $cmitlisted($curview,$id)
5870 if {$id eq $nullid} {
5872 } elseif {$id eq $nullid2} {
5874 } elseif {$id eq $mainheadid} {
5877 set ofill [lindex $circlecolors $listed]
5879 set x [xc $row $col]
5881 set orad [expr {$linespc / 3}]
5883 set t [$canv create oval [expr {$x - $orad}] [expr {$y - $orad}] \
5884 [expr {$x + $orad - 1}] [expr {$y + $orad - 1}] \
5885 -fill $ofill -outline $fgcolor -width 1 -tags circle]
5886 } elseif {$listed == 3} {
5887 # triangle pointing left for left-side commits
5888 set t [$canv create polygon \
5889 [expr {$x - $orad}] $y \
5890 [expr {$x + $orad - 1}] [expr {$y - $orad}] \
5891 [expr {$x + $orad - 1}] [expr {$y + $orad - 1}] \
5892 -fill $ofill -outline $fgcolor -width 1 -tags circle]
5894 # triangle pointing right for right-side commits
5895 set t [$canv create polygon \
5896 [expr {$x + $orad - 1}] $y \
5897 [expr {$x - $orad}] [expr {$y - $orad}] \
5898 [expr {$x - $orad}] [expr {$y + $orad - 1}] \
5899 -fill $ofill -outline $fgcolor -width 1 -tags circle]
5901 set circleitem($row) $t
5903 $canv bind $t <1> {selcanvline {} %x %y}
5904 set rmx [llength [lindex $rowidlist $row]]
5905 set olds [lindex $parentlist $row]
5907 set nextids [lindex $rowidlist [expr {$row + 1}]]
5909 set i [lsearch -exact $nextids $p]
5915 set xt [xc $row $rmx]
5916 set rowtextx($row) $xt
5917 set idpos($id) [list $x $xt $y]
5918 if {[info exists idtags($id)] || [info exists idheads($id)]
5919 || [info exists idotherrefs($id)]} {
5920 set xt [drawtags $id $x $xt $y]
5922 set headline [lindex $commitinfo($id) 0]
5923 set name [lindex $commitinfo($id) 1]
5924 set date [lindex $commitinfo($id) 2]
5925 set date [formatdate $date]
5928 set isbold [ishighlighted $id]
5931 set font mainfontbold
5933 lappend boldnameids $id
5934 set nfont mainfontbold
5937 set linehtag($id) [$canv create text $xt $y -anchor w -fill $fgcolor \
5938 -text $headline -font $font -tags text]
5939 $canv bind $linehtag($id) $ctxbut "rowmenu %X %Y $id"
5940 set linentag($id) [$canv2 create text 3 $y -anchor w -fill $fgcolor \
5941 -text $name -font $nfont -tags text]
5942 set linedtag($id) [$canv3 create text 3 $y -anchor w -fill $fgcolor \
5943 -text $date -font mainfont -tags text]
5944 if {$selectedline == $row} {
5947 if {[info exists markedid] && $markedid eq $id} {
5950 set xr [expr {$xt + [font measure $font $headline]}]
5951 if {$xr > $canvxmax} {
5957 proc drawcmitrow {row} {
5958 global displayorder rowidlist nrows_drawn
5959 global iddrawn markingmatches
5960 global commitinfo numcommits
5961 global filehighlight fhighlights findpattern nhighlights
5962 global hlview vhighlights
5963 global highlight_related rhighlights
5965 if {$row >= $numcommits} return
5967 set id [lindex $displayorder $row]
5968 if {[info exists hlview] && ![info exists vhighlights($id)]} {
5969 askvhighlight $row $id
5971 if {[info exists filehighlight] && ![info exists fhighlights($id)]} {
5972 askfilehighlight $row $id
5974 if {$findpattern ne {} && ![info exists nhighlights($id)]} {
5975 askfindhighlight $row $id
5977 if {$highlight_related ne [mc "None"] && ![info exists rhighlights($id)]} {
5978 askrelhighlight $row $id
5980 if {![info exists iddrawn($id)]} {
5981 set col [lsearch -exact [lindex $rowidlist $row] $id]
5983 puts "oops, row $row id $id not in list"
5986 if {![info exists commitinfo($id)]} {
5990 drawcmittext $id $row $col
5994 if {$markingmatches} {
5995 markrowmatches $row $id
5999 proc drawcommits {row {endrow {}}} {
6000 global numcommits iddrawn displayorder curview need_redisplay
6001 global parentlist rowidlist rowfinal uparrowlen downarrowlen nrows_drawn
6006 if {$endrow eq {}} {
6009 if {$endrow >= $numcommits} {
6010 set endrow [expr {$numcommits - 1}]
6013 set rl1 [expr {$row - $downarrowlen - 3}]
6017 set ro1 [expr {$row - 3}]
6021 set r2 [expr {$endrow + $uparrowlen + 3}]
6022 if {$r2 > $numcommits} {
6025 for {set r $rl1} {$r < $r2} {incr r} {
6026 if {[lindex $rowidlist $r] ne {} && [lindex $rowfinal $r]} {
6030 set rl1 [expr {$r + 1}]
6036 optimize_rows $ro1 0 $r2
6037 if {$need_redisplay || $nrows_drawn > 2000} {
6041 # make the lines join to already-drawn rows either side
6042 set r [expr {$row - 1}]
6043 if {$r < 0 || ![info exists iddrawn([lindex $displayorder $r])]} {
6046 set er [expr {$endrow + 1}]
6047 if {$er >= $numcommits ||
6048 ![info exists iddrawn([lindex $displayorder $er])]} {
6051 for {} {$r <= $er} {incr r} {
6052 set id [lindex $displayorder $r]
6053 set wasdrawn [info exists iddrawn($id)]
6055 if {$r == $er} break
6056 set nextid [lindex $displayorder [expr {$r + 1}]]
6057 if {$wasdrawn && [info exists iddrawn($nextid)]} continue
6058 drawparentlinks $id $r
6060 set rowids [lindex $rowidlist $r]
6061 foreach lid $rowids {
6062 if {$lid eq {}} continue
6063 if {[info exists lineend($lid)] && $lineend($lid) > $r} continue
6065 # see if this is the first child of any of its parents
6066 foreach p [lindex $parentlist $r] {
6067 if {[lsearch -exact $rowids $p] < 0} {
6068 # make this line extend up to the child
6069 set lineend($p) [drawlineseg $p $r $er 0]
6073 set lineend($lid) [drawlineseg $lid $r $er 1]
6079 proc undolayout {row} {
6080 global uparrowlen mingaplen downarrowlen
6081 global rowidlist rowisopt rowfinal need_redisplay
6083 set r [expr {$row - ($uparrowlen + $mingaplen + $downarrowlen)}]
6087 if {[llength $rowidlist] > $r} {
6089 set rowidlist [lrange $rowidlist 0 $r]
6090 set rowfinal [lrange $rowfinal 0 $r]
6091 set rowisopt [lrange $rowisopt 0 $r]
6092 set need_redisplay 1
6097 proc drawvisible {} {
6098 global canv linespc curview vrowmod selectedline targetrow targetid
6099 global need_redisplay cscroll numcommits
6101 set fs [$canv yview]
6102 set ymax [lindex [$canv cget -scrollregion] 3]
6103 if {$ymax eq {} || $ymax == 0 || $numcommits == 0} return
6104 set f0 [lindex $fs 0]
6105 set f1 [lindex $fs 1]
6106 set y0 [expr {int($f0 * $ymax)}]
6107 set y1 [expr {int($f1 * $ymax)}]
6109 if {[info exists targetid]} {
6110 if {[commitinview $targetid $curview]} {
6111 set r [rowofcommit $targetid]
6112 if {$r != $targetrow} {
6113 # Fix up the scrollregion and change the scrolling position
6114 # now that our target row has moved.
6115 set diff [expr {($r - $targetrow) * $linespc}]
6118 set ymax [lindex [$canv cget -scrollregion] 3]
6121 set f0 [expr {$y0 / $ymax}]
6122 set f1 [expr {$y1 / $ymax}]
6123 allcanvs yview moveto $f0
6124 $cscroll set $f0 $f1
6125 set need_redisplay 1
6132 set row [expr {int(($y0 - 3) / $linespc) - 1}]
6133 set endrow [expr {int(($y1 - 3) / $linespc) + 1}]
6134 if {$endrow >= $vrowmod($curview)} {
6135 update_arcrows $curview
6137 if {$selectedline ne {} &&
6138 $row <= $selectedline && $selectedline <= $endrow} {
6139 set targetrow $selectedline
6140 } elseif {[info exists targetid]} {
6141 set targetrow [expr {int(($row + $endrow) / 2)}]
6143 if {[info exists targetrow]} {
6144 if {$targetrow >= $numcommits} {
6145 set targetrow [expr {$numcommits - 1}]
6147 set targetid [commitonrow $targetrow]
6149 drawcommits $row $endrow
6152 proc clear_display {} {
6153 global iddrawn linesegs need_redisplay nrows_drawn
6154 global vhighlights fhighlights nhighlights rhighlights
6155 global linehtag linentag linedtag boldids boldnameids
6158 catch {unset iddrawn}
6159 catch {unset linesegs}
6160 catch {unset linehtag}
6161 catch {unset linentag}
6162 catch {unset linedtag}
6165 catch {unset vhighlights}
6166 catch {unset fhighlights}
6167 catch {unset nhighlights}
6168 catch {unset rhighlights}
6169 set need_redisplay 0
6173 proc findcrossings {id} {
6174 global rowidlist parentlist numcommits displayorder
6178 foreach {s e} [rowranges $id] {
6179 if {$e >= $numcommits} {
6180 set e [expr {$numcommits - 1}]
6182 if {$e <= $s} continue
6183 for {set row $e} {[incr row -1] >= $s} {} {
6184 set x [lsearch -exact [lindex $rowidlist $row] $id]
6186 set olds [lindex $parentlist $row]
6187 set kid [lindex $displayorder $row]
6188 set kidx [lsearch -exact [lindex $rowidlist $row] $kid]
6189 if {$kidx < 0} continue
6190 set nextrow [lindex $rowidlist [expr {$row + 1}]]
6192 set px [lsearch -exact $nextrow $p]
6193 if {$px < 0} continue
6194 if {($kidx < $x && $x < $px) || ($px < $x && $x < $kidx)} {
6195 if {[lsearch -exact $ccross $p] >= 0} continue
6196 if {$x == $px + ($kidx < $px? -1: 1)} {
6198 } elseif {[lsearch -exact $cross $p] < 0} {
6205 return [concat $ccross {{}} $cross]
6208 proc assigncolor {id} {
6209 global colormap colors nextcolor
6210 global parents children children curview
6212 if {[info exists colormap($id)]} return
6213 set ncolors [llength $colors]
6214 if {[info exists children($curview,$id)]} {
6215 set kids $children($curview,$id)
6219 if {[llength $kids] == 1} {
6220 set child [lindex $kids 0]
6221 if {[info exists colormap($child)]
6222 && [llength $parents($curview,$child)] == 1} {
6223 set colormap($id) $colormap($child)
6229 foreach x [findcrossings $id] {
6231 # delimiter between corner crossings and other crossings
6232 if {[llength $badcolors] >= $ncolors - 1} break
6233 set origbad $badcolors
6235 if {[info exists colormap($x)]
6236 && [lsearch -exact $badcolors $colormap($x)] < 0} {
6237 lappend badcolors $colormap($x)
6240 if {[llength $badcolors] >= $ncolors} {
6241 set badcolors $origbad
6243 set origbad $badcolors
6244 if {[llength $badcolors] < $ncolors - 1} {
6245 foreach child $kids {
6246 if {[info exists colormap($child)]
6247 && [lsearch -exact $badcolors $colormap($child)] < 0} {
6248 lappend badcolors $colormap($child)
6250 foreach p $parents($curview,$child) {
6251 if {[info exists colormap($p)]
6252 && [lsearch -exact $badcolors $colormap($p)] < 0} {
6253 lappend badcolors $colormap($p)
6257 if {[llength $badcolors] >= $ncolors} {
6258 set badcolors $origbad
6261 for {set i 0} {$i <= $ncolors} {incr i} {
6262 set c [lindex $colors $nextcolor]
6263 if {[incr nextcolor] >= $ncolors} {
6266 if {[lsearch -exact $badcolors $c]} break
6268 set colormap($id) $c
6271 proc bindline {t id} {
6274 $canv bind $t <Enter> "lineenter %x %y $id"
6275 $canv bind $t <Motion> "linemotion %x %y $id"
6276 $canv bind $t <Leave> "lineleave $id"
6277 $canv bind $t <Button-1> "lineclick %x %y $id 1"
6280 proc drawtags {id x xt y1} {
6281 global idtags idheads idotherrefs mainhead
6282 global linespc lthickness
6283 global canv rowtextx curview fgcolor bgcolor ctxbut
6288 if {[info exists idtags($id)]} {
6289 set marks $idtags($id)
6290 set ntags [llength $marks]
6292 if {[info exists idheads($id)]} {
6293 set marks [concat $marks $idheads($id)]
6294 set nheads [llength $idheads($id)]
6296 if {[info exists idotherrefs($id)]} {
6297 set marks [concat $marks $idotherrefs($id)]
6303 set delta [expr {int(0.5 * ($linespc - $lthickness))}]
6304 set yt [expr {$y1 - 0.5 * $linespc}]
6305 set yb [expr {$yt + $linespc - 1}]
6309 foreach tag $marks {
6311 if {$i >= $ntags && $i < $ntags + $nheads && $tag eq $mainhead} {
6312 set wid [font measure mainfontbold $tag]
6314 set wid [font measure mainfont $tag]
6318 set xt [expr {$xt + $delta + $wid + $lthickness + $linespc}]
6320 set t [$canv create line $x $y1 [lindex $xvals end] $y1 \
6321 -width $lthickness -fill black -tags tag.$id]
6323 foreach tag $marks x $xvals wid $wvals {
6324 set tag_quoted [string map {% %%} $tag]
6325 set xl [expr {$x + $delta}]
6326 set xr [expr {$x + $delta + $wid + $lthickness}]
6328 if {[incr ntags -1] >= 0} {
6330 set t [$canv create polygon $x [expr {$yt + $delta}] $xl $yt \
6331 $xr $yt $xr $yb $xl $yb $x [expr {$yb - $delta}] \
6332 -width 1 -outline black -fill yellow -tags tag.$id]
6333 $canv bind $t <1> [list showtag $tag_quoted 1]
6334 set rowtextx([rowofcommit $id]) [expr {$xr + $linespc}]
6336 # draw a head or other ref
6337 if {[incr nheads -1] >= 0} {
6339 if {$tag eq $mainhead} {
6340 set font mainfontbold
6345 set xl [expr {$xl - $delta/2}]
6346 $canv create polygon $x $yt $xr $yt $xr $yb $x $yb \
6347 -width 1 -outline black -fill $col -tags tag.$id
6348 if {[regexp {^(remotes/.*/|remotes/)} $tag match remoteprefix]} {
6349 set rwid [font measure mainfont $remoteprefix]
6350 set xi [expr {$x + 1}]
6351 set yti [expr {$yt + 1}]
6352 set xri [expr {$x + $rwid}]
6353 $canv create polygon $xi $yti $xri $yti $xri $yb $xi $yb \
6354 -width 0 -fill "#ffddaa" -tags tag.$id
6357 set t [$canv create text $xl $y1 -anchor w -text $tag -fill $fgcolor \
6358 -font $font -tags [list tag.$id text]]
6360 $canv bind $t <1> [list showtag $tag_quoted 1]
6361 } elseif {$nheads >= 0} {
6362 $canv bind $t $ctxbut [list headmenu %X %Y $id $tag_quoted]
6368 proc xcoord {i level ln} {
6369 global canvx0 xspc1 xspc2
6371 set x [expr {$canvx0 + $i * $xspc1($ln)}]
6372 if {$i > 0 && $i == $level} {
6373 set x [expr {$x + 0.5 * ($xspc2 - $xspc1($ln))}]
6374 } elseif {$i > $level} {
6375 set x [expr {$x + $xspc2 - $xspc1($ln)}]
6380 proc show_status {msg} {
6384 $canv create text 3 3 -anchor nw -text $msg -font mainfont \
6385 -tags text -fill $fgcolor
6388 # Don't change the text pane cursor if it is currently the hand cursor,
6389 # showing that we are over a sha1 ID link.
6390 proc settextcursor {c} {
6391 global ctext curtextcursor
6393 if {[$ctext cget -cursor] == $curtextcursor} {
6394 $ctext config -cursor $c
6396 set curtextcursor $c
6399 proc nowbusy {what {name {}}} {
6400 global isbusy busyname statusw
6402 if {[array names isbusy] eq {}} {
6403 . config -cursor watch
6407 set busyname($what) $name
6409 $statusw conf -text $name
6413 proc notbusy {what} {
6414 global isbusy maincursor textcursor busyname statusw
6418 if {$busyname($what) ne {} &&
6419 [$statusw cget -text] eq $busyname($what)} {
6420 $statusw conf -text {}
6423 if {[array names isbusy] eq {}} {
6424 . config -cursor $maincursor
6425 settextcursor $textcursor
6429 proc findmatches {f} {
6430 global findtype findstring
6431 if {$findtype == [mc "Regexp"]} {
6432 set matches [regexp -indices -all -inline $findstring $f]
6435 if {$findtype == [mc "IgnCase"]} {
6436 set f [string tolower $f]
6437 set fs [string tolower $fs]
6441 set l [string length $fs]
6442 while {[set j [string first $fs $f $i]] >= 0} {
6443 lappend matches [list $j [expr {$j+$l-1}]]
6444 set i [expr {$j + $l}]
6450 proc dofind {{dirn 1} {wrap 1}} {
6451 global findstring findstartline findcurline selectedline numcommits
6452 global gdttype filehighlight fh_serial find_dirn findallowwrap
6454 if {[info exists find_dirn]} {
6455 if {$find_dirn == $dirn} return
6459 if {$findstring eq {} || $numcommits == 0} return
6460 if {$selectedline eq {}} {
6461 set findstartline [lindex [visiblerows] [expr {$dirn < 0}]]
6463 set findstartline $selectedline
6465 set findcurline $findstartline
6466 nowbusy finding [mc "Searching"]
6467 if {$gdttype ne [mc "containing:"] && ![info exists filehighlight]} {
6468 after cancel do_file_hl $fh_serial
6469 do_file_hl $fh_serial
6472 set findallowwrap $wrap
6476 proc stopfinding {} {
6477 global find_dirn findcurline fprogcoord
6479 if {[info exists find_dirn]} {
6490 global commitdata commitinfo numcommits findpattern findloc
6491 global findstartline findcurline findallowwrap
6492 global find_dirn gdttype fhighlights fprogcoord
6493 global curview varcorder vrownum varccommits vrowmod
6495 if {![info exists find_dirn]} {
6498 set fldtypes [list [mc "Headline"] [mc "Author"] [mc "Date"] [mc "Committer"] [mc "CDate"] [mc "Comments"]]
6501 if {$find_dirn > 0} {
6503 if {$l >= $numcommits} {
6506 if {$l <= $findstartline} {
6507 set lim [expr {$findstartline + 1}]
6510 set moretodo $findallowwrap
6517 if {$l >= $findstartline} {
6518 set lim [expr {$findstartline - 1}]
6521 set moretodo $findallowwrap
6524 set n [expr {($lim - $l) * $find_dirn}]
6529 if {$l + ($find_dirn > 0? $n: 1) > $vrowmod($curview)} {
6530 update_arcrows $curview
6534 set ai [bsearch $vrownum($curview) $l]
6535 set a [lindex $varcorder($curview) $ai]
6536 set arow [lindex $vrownum($curview) $ai]
6537 set ids [lindex $varccommits($curview,$a)]
6538 set arowend [expr {$arow + [llength $ids]}]
6539 if {$gdttype eq [mc "containing:"]} {
6540 for {} {$n > 0} {incr n -1; incr l $find_dirn} {
6541 if {$l < $arow || $l >= $arowend} {
6543 set a [lindex $varcorder($curview) $ai]
6544 set arow [lindex $vrownum($curview) $ai]
6545 set ids [lindex $varccommits($curview,$a)]
6546 set arowend [expr {$arow + [llength $ids]}]
6548 set id [lindex $ids [expr {$l - $arow}]]
6549 # shouldn't happen unless git log doesn't give all the commits...
6550 if {![info exists commitdata($id)] ||
6551 ![doesmatch $commitdata($id)]} {
6554 if {![info exists commitinfo($id)]} {
6557 set info $commitinfo($id)
6558 foreach f $info ty $fldtypes {
6559 if {($findloc eq [mc "All fields"] || $findloc eq $ty) &&
6568 for {} {$n > 0} {incr n -1; incr l $find_dirn} {
6569 if {$l < $arow || $l >= $arowend} {
6571 set a [lindex $varcorder($curview) $ai]
6572 set arow [lindex $vrownum($curview) $ai]
6573 set ids [lindex $varccommits($curview,$a)]
6574 set arowend [expr {$arow + [llength $ids]}]
6576 set id [lindex $ids [expr {$l - $arow}]]
6577 if {![info exists fhighlights($id)]} {
6578 # this sets fhighlights($id) to -1
6579 askfilehighlight $l $id
6581 if {$fhighlights($id) > 0} {
6585 if {$fhighlights($id) < 0} {
6588 set findcurline [expr {$l - $find_dirn}]
6593 if {$found || ($domore && !$moretodo)} {
6609 set findcurline [expr {$l - $find_dirn}]
6611 set n [expr {($findcurline - $findstartline) * $find_dirn - 1}]
6615 set fprogcoord [expr {$n * 1.0 / $numcommits}]
6620 proc findselectline {l} {
6621 global findloc commentend ctext findcurline markingmatches gdttype
6623 set markingmatches [expr {$gdttype eq [mc "containing:"]}]
6626 if {$markingmatches &&
6627 ($findloc eq [mc "All fields"] || $findloc eq [mc "Comments"])} {
6628 # highlight the matches in the comments
6629 set f [$ctext get 1.0 $commentend]
6630 set matches [findmatches $f]
6631 foreach match $matches {
6632 set start [lindex $match 0]
6633 set end [expr {[lindex $match 1] + 1}]
6634 $ctext tag add found "1.0 + $start c" "1.0 + $end c"
6640 # mark the bits of a headline or author that match a find string
6641 proc markmatches {canv l str tag matches font row} {
6644 set bbox [$canv bbox $tag]
6645 set x0 [lindex $bbox 0]
6646 set y0 [lindex $bbox 1]
6647 set y1 [lindex $bbox 3]
6648 foreach match $matches {
6649 set start [lindex $match 0]
6650 set end [lindex $match 1]
6651 if {$start > $end} continue
6652 set xoff [font measure $font [string range $str 0 [expr {$start-1}]]]
6653 set xlen [font measure $font [string range $str 0 [expr {$end}]]]
6654 set t [$canv create rect [expr {$x0+$xoff}] $y0 \
6655 [expr {$x0+$xlen+2}] $y1 \
6656 -outline {} -tags [list match$l matches] -fill yellow]
6658 if {$row == $selectedline} {
6659 $canv raise $t secsel
6664 proc unmarkmatches {} {
6665 global markingmatches
6667 allcanvs delete matches
6668 set markingmatches 0
6672 proc selcanvline {w x y} {
6673 global canv canvy0 ctext linespc
6675 set ymax [lindex [$canv cget -scrollregion] 3]
6676 if {$ymax == {}} return
6677 set yfrac [lindex [$canv yview] 0]
6678 set y [expr {$y + $yfrac * $ymax}]
6679 set l [expr {int(($y - $canvy0) / $linespc + 0.5)}]
6684 set xmax [lindex [$canv cget -scrollregion] 2]
6685 set xleft [expr {[lindex [$canv xview] 0] * $xmax}]
6686 if {![info exists rowtextx($l)] || $xleft + $x < $rowtextx($l)} return
6692 proc commit_descriptor {p} {
6694 if {![info exists commitinfo($p)]} {
6698 if {[llength $commitinfo($p)] > 1} {
6699 set l [lindex $commitinfo($p) 0]
6704 # append some text to the ctext widget, and make any SHA1 ID
6705 # that we know about be a clickable link.
6706 proc appendwithlinks {text tags} {
6707 global ctext linknum curview
6709 set start [$ctext index "end - 1c"]
6710 $ctext insert end $text $tags
6711 set links [regexp -indices -all -inline {\m[0-9a-f]{6,40}\M} $text]
6715 set linkid [string range $text $s $e]
6717 $ctext tag delete link$linknum
6718 $ctext tag add link$linknum "$start + $s c" "$start + $e c"
6719 setlink $linkid link$linknum
6724 proc setlink {id lk} {
6725 global curview ctext pendinglinks
6728 if {[string length $id] < 40} {
6729 set matches [longid $id]
6730 if {[llength $matches] > 0} {
6731 if {[llength $matches] > 1} return
6733 set id [lindex $matches 0]
6736 set known [commitinview $id $curview]
6739 $ctext tag conf $lk -foreground blue -underline 1
6740 $ctext tag bind $lk <1> [list selbyid $id]
6741 $ctext tag bind $lk <Enter> {linkcursor %W 1}
6742 $ctext tag bind $lk <Leave> {linkcursor %W -1}
6744 lappend pendinglinks($id) $lk
6745 interestedin $id {makelink %P}
6749 proc appendshortlink {id {pre {}} {post {}}} {
6750 global ctext linknum
6752 $ctext insert end $pre
6753 $ctext tag delete link$linknum
6754 $ctext insert end [string range $id 0 7] link$linknum
6755 $ctext insert end $post
6756 setlink $id link$linknum
6760 proc makelink {id} {
6763 if {![info exists pendinglinks($id)]} return
6764 foreach lk $pendinglinks($id) {
6767 unset pendinglinks($id)
6770 proc linkcursor {w inc} {
6771 global linkentercount curtextcursor
6773 if {[incr linkentercount $inc] > 0} {
6774 $w configure -cursor hand2
6776 $w configure -cursor $curtextcursor
6777 if {$linkentercount < 0} {
6778 set linkentercount 0
6783 proc viewnextline {dir} {
6787 set ymax [lindex [$canv cget -scrollregion] 3]
6788 set wnow [$canv yview]
6789 set wtop [expr {[lindex $wnow 0] * $ymax}]
6790 set newtop [expr {$wtop + $dir * $linespc}]
6793 } elseif {$newtop > $ymax} {
6796 allcanvs yview moveto [expr {$newtop * 1.0 / $ymax}]
6799 # add a list of tag or branch names at position pos
6800 # returns the number of names inserted
6801 proc appendrefs {pos ids var} {
6802 global ctext linknum curview $var maxrefs
6804 if {[catch {$ctext index $pos}]} {
6807 $ctext conf -state normal
6808 $ctext delete $pos "$pos lineend"
6811 foreach tag [set $var\($id\)] {
6812 lappend tags [list $tag $id]
6815 if {[llength $tags] > $maxrefs} {
6816 $ctext insert $pos "[mc "many"] ([llength $tags])"
6818 set tags [lsort -index 0 -decreasing $tags]
6821 set id [lindex $ti 1]
6824 $ctext tag delete $lk
6825 $ctext insert $pos $sep
6826 $ctext insert $pos [lindex $ti 0] $lk
6831 $ctext conf -state disabled
6832 return [llength $tags]
6835 # called when we have finished computing the nearby tags
6836 proc dispneartags {delay} {
6837 global selectedline currentid showneartags tagphase
6839 if {$selectedline eq {} || !$showneartags} return
6840 after cancel dispnexttag
6842 after 200 dispnexttag
6845 after idle dispnexttag
6850 proc dispnexttag {} {
6851 global selectedline currentid showneartags tagphase ctext
6853 if {$selectedline eq {} || !$showneartags} return
6854 switch -- $tagphase {
6856 set dtags [desctags $currentid]
6858 appendrefs precedes $dtags idtags
6862 set atags [anctags $currentid]
6864 appendrefs follows $atags idtags
6868 set dheads [descheads $currentid]
6869 if {$dheads ne {}} {
6870 if {[appendrefs branch $dheads idheads] > 1
6871 && [$ctext get "branch -3c"] eq "h"} {
6872 # turn "Branch" into "Branches"
6873 $ctext conf -state normal
6874 $ctext insert "branch -2c" "es"
6875 $ctext conf -state disabled
6880 if {[incr tagphase] <= 2} {
6881 after idle dispnexttag
6885 proc make_secsel {id} {
6886 global linehtag linentag linedtag canv canv2 canv3
6888 if {![info exists linehtag($id)]} return
6890 set t [eval $canv create rect [$canv bbox $linehtag($id)] -outline {{}} \
6891 -tags secsel -fill [$canv cget -selectbackground]]
6893 $canv2 delete secsel
6894 set t [eval $canv2 create rect [$canv2 bbox $linentag($id)] -outline {{}} \
6895 -tags secsel -fill [$canv2 cget -selectbackground]]
6897 $canv3 delete secsel
6898 set t [eval $canv3 create rect [$canv3 bbox $linedtag($id)] -outline {{}} \
6899 -tags secsel -fill [$canv3 cget -selectbackground]]
6903 proc make_idmark {id} {
6904 global linehtag canv fgcolor
6906 if {![info exists linehtag($id)]} return
6908 set t [eval $canv create rect [$canv bbox $linehtag($id)] \
6909 -tags markid -outline $fgcolor]
6913 proc selectline {l isnew {desired_loc {}}} {
6914 global canv ctext commitinfo selectedline
6915 global canvy0 linespc parents children curview
6916 global currentid sha1entry
6917 global commentend idtags linknum
6918 global mergemax numcommits pending_select
6919 global cmitmode showneartags allcommits
6920 global targetrow targetid lastscrollrows
6921 global autoselect autosellen jump_to_here
6923 catch {unset pending_select}
6928 if {$l < 0 || $l >= $numcommits} return
6929 set id [commitonrow $l]
6934 if {$lastscrollrows < $numcommits} {
6938 set y [expr {$canvy0 + $l * $linespc}]
6939 set ymax [lindex [$canv cget -scrollregion] 3]
6940 set ytop [expr {$y - $linespc - 1}]
6941 set ybot [expr {$y + $linespc + 1}]
6942 set wnow [$canv yview]
6943 set wtop [expr {[lindex $wnow 0] * $ymax}]
6944 set wbot [expr {[lindex $wnow 1] * $ymax}]
6945 set wh [expr {$wbot - $wtop}]
6947 if {$ytop < $wtop} {
6948 if {$ybot < $wtop} {
6949 set newtop [expr {$y - $wh / 2.0}]
6952 if {$newtop > $wtop - $linespc} {
6953 set newtop [expr {$wtop - $linespc}]
6956 } elseif {$ybot > $wbot} {
6957 if {$ytop > $wbot} {
6958 set newtop [expr {$y - $wh / 2.0}]
6960 set newtop [expr {$ybot - $wh}]
6961 if {$newtop < $wtop + $linespc} {
6962 set newtop [expr {$wtop + $linespc}]
6966 if {$newtop != $wtop} {
6970 allcanvs yview moveto [expr {$newtop * 1.0 / $ymax}]
6977 addtohistory [list selbyid $id 0] savecmitpos
6980 $sha1entry delete 0 end
6981 $sha1entry insert 0 $id
6983 $sha1entry selection range 0 $autosellen
6987 $ctext conf -state normal
6990 if {![info exists commitinfo($id)]} {
6993 set info $commitinfo($id)
6994 set date [formatdate [lindex $info 2]]
6995 $ctext insert end "[mc "Author"]: [lindex $info 1] $date\n"
6996 set date [formatdate [lindex $info 4]]
6997 $ctext insert end "[mc "Committer"]: [lindex $info 3] $date\n"
6998 if {[info exists idtags($id)]} {
6999 $ctext insert end [mc "Tags:"]
7000 foreach tag $idtags($id) {
7001 $ctext insert end " $tag"
7003 $ctext insert end "\n"
7007 set olds $parents($curview,$id)
7008 if {[llength $olds] > 1} {
7011 if {$np >= $mergemax} {
7016 $ctext insert end "[mc "Parent"]: " $tag
7017 appendwithlinks [commit_descriptor $p] {}
7022 append headers "[mc "Parent"]: [commit_descriptor $p]"
7026 foreach c $children($curview,$id) {
7027 append headers "[mc "Child"]: [commit_descriptor $c]"
7030 # make anything that looks like a SHA1 ID be a clickable link
7031 appendwithlinks $headers {}
7032 if {$showneartags} {
7033 if {![info exists allcommits]} {
7036 $ctext insert end "[mc "Branch"]: "
7037 $ctext mark set branch "end -1c"
7038 $ctext mark gravity branch left
7039 $ctext insert end "\n[mc "Follows"]: "
7040 $ctext mark set follows "end -1c"
7041 $ctext mark gravity follows left
7042 $ctext insert end "\n[mc "Precedes"]: "
7043 $ctext mark set precedes "end -1c"
7044 $ctext mark gravity precedes left
7045 $ctext insert end "\n"
7048 $ctext insert end "\n"
7049 set comment [lindex $info 5]
7050 if {[string first "\r" $comment] >= 0} {
7051 set comment [string map {"\r" "\n "} $comment]
7053 appendwithlinks $comment {comment}
7055 $ctext tag remove found 1.0 end
7056 $ctext conf -state disabled
7057 set commentend [$ctext index "end - 1c"]
7059 set jump_to_here $desired_loc
7060 init_flist [mc "Comments"]
7061 if {$cmitmode eq "tree"} {
7063 } elseif {[llength $olds] <= 1} {
7070 proc selfirstline {} {
7075 proc sellastline {} {
7078 set l [expr {$numcommits - 1}]
7082 proc selnextline {dir} {
7085 if {$selectedline eq {}} return
7086 set l [expr {$selectedline + $dir}]
7091 proc selnextpage {dir} {
7092 global canv linespc selectedline numcommits
7094 set lpp [expr {([winfo height $canv] - 2) / $linespc}]
7098 allcanvs yview scroll [expr {$dir * $lpp}] units
7100 if {$selectedline eq {}} return
7101 set l [expr {$selectedline + $dir * $lpp}]
7104 } elseif {$l >= $numcommits} {
7105 set l [expr $numcommits - 1]
7111 proc unselectline {} {
7112 global selectedline currentid
7115 catch {unset currentid}
7116 allcanvs delete secsel
7120 proc reselectline {} {
7123 if {$selectedline ne {}} {
7124 selectline $selectedline 0
7128 proc addtohistory {cmd {saveproc {}}} {
7129 global history historyindex curview
7133 set elt [list $curview $cmd $saveproc {}]
7134 if {$historyindex > 0
7135 && [lindex $history [expr {$historyindex - 1}]] == $elt} {
7139 if {$historyindex < [llength $history]} {
7140 set history [lreplace $history $historyindex end $elt]
7142 lappend history $elt
7145 if {$historyindex > 1} {
7146 .tf.bar.leftbut conf -state normal
7148 .tf.bar.leftbut conf -state disabled
7150 .tf.bar.rightbut conf -state disabled
7153 # save the scrolling position of the diff display pane
7154 proc save_position {} {
7155 global historyindex history
7157 if {$historyindex < 1} return
7158 set hi [expr {$historyindex - 1}]
7159 set fn [lindex $history $hi 2]
7161 lset history $hi 3 [eval $fn]
7165 proc unset_posvars {} {
7168 if {[info exists last_posvars]} {
7169 foreach {var val} $last_posvars {
7178 global curview last_posvars
7180 set view [lindex $elt 0]
7181 set cmd [lindex $elt 1]
7182 set pv [lindex $elt 3]
7183 if {$curview != $view} {
7187 foreach {var val} $pv {
7191 set last_posvars $pv
7196 global history historyindex
7199 if {$historyindex > 1} {
7201 incr historyindex -1
7202 godo [lindex $history [expr {$historyindex - 1}]]
7203 .tf.bar.rightbut conf -state normal
7205 if {$historyindex <= 1} {
7206 .tf.bar.leftbut conf -state disabled
7211 global history historyindex
7214 if {$historyindex < [llength $history]} {
7216 set cmd [lindex $history $historyindex]
7219 .tf.bar.leftbut conf -state normal
7221 if {$historyindex >= [llength $history]} {
7222 .tf.bar.rightbut conf -state disabled
7227 global treefilelist treeidlist diffids diffmergeid treepending
7228 global nullid nullid2
7231 catch {unset diffmergeid}
7232 if {![info exists treefilelist($id)]} {
7233 if {![info exists treepending]} {
7234 if {$id eq $nullid} {
7235 set cmd [list | git ls-files]
7236 } elseif {$id eq $nullid2} {
7237 set cmd [list | git ls-files --stage -t]
7239 set cmd [list | git ls-tree -r $id]
7241 if {[catch {set gtf [open $cmd r]}]} {
7245 set treefilelist($id) {}
7246 set treeidlist($id) {}
7247 fconfigure $gtf -blocking 0 -encoding binary
7248 filerun $gtf [list gettreeline $gtf $id]
7255 proc gettreeline {gtf id} {
7256 global treefilelist treeidlist treepending cmitmode diffids nullid nullid2
7259 while {[incr nl] <= 1000 && [gets $gtf line] >= 0} {
7260 if {$diffids eq $nullid} {
7263 set i [string first "\t" $line]
7264 if {$i < 0} continue
7265 set fname [string range $line [expr {$i+1}] end]
7266 set line [string range $line 0 [expr {$i-1}]]
7267 if {$diffids ne $nullid2 && [lindex $line 1] ne "blob"} continue
7268 set sha1 [lindex $line 2]
7269 lappend treeidlist($id) $sha1
7271 if {[string index $fname 0] eq "\""} {
7272 set fname [lindex $fname 0]
7274 set fname [encoding convertfrom $fname]
7275 lappend treefilelist($id) $fname
7278 return [expr {$nl >= 1000? 2: 1}]
7282 if {$cmitmode ne "tree"} {
7283 if {![info exists diffmergeid]} {
7284 gettreediffs $diffids
7286 } elseif {$id ne $diffids} {
7295 global treefilelist treeidlist diffids nullid nullid2
7296 global ctext_file_names ctext_file_lines
7297 global ctext commentend
7299 set i [lsearch -exact $treefilelist($diffids) $f]
7301 puts "oops, $f not in list for id $diffids"
7304 if {$diffids eq $nullid} {
7305 if {[catch {set bf [open $f r]} err]} {
7306 puts "oops, can't read $f: $err"
7310 set blob [lindex $treeidlist($diffids) $i]
7311 if {[catch {set bf [open [concat | git cat-file blob $blob] r]} err]} {
7312 puts "oops, error reading blob $blob: $err"
7316 fconfigure $bf -blocking 0 -encoding [get_path_encoding $f]
7317 filerun $bf [list getblobline $bf $diffids]
7318 $ctext config -state normal
7319 clear_ctext $commentend
7320 lappend ctext_file_names $f
7321 lappend ctext_file_lines [lindex [split $commentend "."] 0]
7322 $ctext insert end "\n"
7323 $ctext insert end "$f\n" filesep
7324 $ctext config -state disabled
7325 $ctext yview $commentend
7329 proc getblobline {bf id} {
7330 global diffids cmitmode ctext
7332 if {$id ne $diffids || $cmitmode ne "tree"} {
7336 $ctext config -state normal
7338 while {[incr nl] <= 1000 && [gets $bf line] >= 0} {
7339 $ctext insert end "$line\n"
7342 global jump_to_here ctext_file_names commentend
7344 # delete last newline
7345 $ctext delete "end - 2c" "end - 1c"
7347 if {$jump_to_here ne {} &&
7348 [lindex $jump_to_here 0] eq [lindex $ctext_file_names 0]} {
7349 set lnum [expr {[lindex $jump_to_here 1] +
7350 [lindex [split $commentend .] 0]}]
7351 mark_ctext_line $lnum
7353 $ctext config -state disabled
7356 $ctext config -state disabled
7357 return [expr {$nl >= 1000? 2: 1}]
7360 proc mark_ctext_line {lnum} {
7361 global ctext markbgcolor
7363 $ctext tag delete omark
7364 $ctext tag add omark $lnum.0 "$lnum.0 + 1 line"
7365 $ctext tag conf omark -background $markbgcolor
7369 proc mergediff {id} {
7371 global diffids treediffs
7372 global parents curview
7376 set treediffs($id) {}
7377 set np [llength $parents($curview,$id)]
7382 proc startdiff {ids} {
7383 global treediffs diffids treepending diffmergeid nullid nullid2
7387 catch {unset diffmergeid}
7388 if {![info exists treediffs($ids)] ||
7389 [lsearch -exact $ids $nullid] >= 0 ||
7390 [lsearch -exact $ids $nullid2] >= 0} {
7391 if {![info exists treepending]} {
7399 # If the filename (name) is under any of the passed filter paths
7400 # then return true to include the file in the listing.
7401 proc path_filter {filter name} {
7402 set worktree [gitworktree]
7404 set fq_p [file normalize $p]
7405 set fq_n [file normalize [file join $worktree $name]]
7406 if {[string match [file normalize $fq_p]* $fq_n]} {
7413 proc addtocflist {ids} {
7416 add_flist $treediffs($ids)
7420 proc diffcmd {ids flags} {
7421 global nullid nullid2
7423 set i [lsearch -exact $ids $nullid]
7424 set j [lsearch -exact $ids $nullid2]
7426 if {[llength $ids] > 1 && $j < 0} {
7427 # comparing working directory with some specific revision
7428 set cmd [concat | git diff-index $flags]
7430 lappend cmd -R [lindex $ids 1]
7432 lappend cmd [lindex $ids 0]
7435 # comparing working directory with index
7436 set cmd [concat | git diff-files $flags]
7441 } elseif {$j >= 0} {
7442 set cmd [concat | git diff-index --cached $flags]
7443 if {[llength $ids] > 1} {
7444 # comparing index with specific revision
7446 lappend cmd -R [lindex $ids 1]
7448 lappend cmd [lindex $ids 0]
7451 # comparing index with HEAD
7455 set cmd [concat | git diff-tree -r $flags $ids]
7460 proc gettreediffs {ids} {
7461 global treediff treepending
7463 if {[catch {set gdtf [open [diffcmd $ids {--no-commit-id}] r]}]} return
7465 set treepending $ids
7467 fconfigure $gdtf -blocking 0 -encoding binary
7468 filerun $gdtf [list gettreediffline $gdtf $ids]
7471 proc gettreediffline {gdtf ids} {
7472 global treediff treediffs treepending diffids diffmergeid
7473 global cmitmode vfilelimit curview limitdiffs perfile_attrs
7478 if {$perfile_attrs} {
7479 # cache_gitattr is slow, and even slower on win32 where we
7480 # have to invoke it for only about 30 paths at a time
7482 if {[tk windowingsystem] == "win32"} {
7486 while {[incr nr] <= $max && [gets $gdtf line] >= 0} {
7487 set i [string first "\t" $line]
7489 set file [string range $line [expr {$i+1}] end]
7490 if {[string index $file 0] eq "\""} {
7491 set file [lindex $file 0]
7493 set file [encoding convertfrom $file]
7494 if {$file ne [lindex $treediff end]} {
7495 lappend treediff $file
7496 lappend sublist $file
7500 if {$perfile_attrs} {
7501 cache_gitattr encoding $sublist
7504 return [expr {$nr >= $max? 2: 1}]
7507 if {$limitdiffs && $vfilelimit($curview) ne {}} {
7509 foreach f $treediff {
7510 if {[path_filter $vfilelimit($curview) $f]} {
7514 set treediffs($ids) $flist
7516 set treediffs($ids) $treediff
7519 if {$cmitmode eq "tree" && [llength $diffids] == 1} {
7521 } elseif {$ids != $diffids} {
7522 if {![info exists diffmergeid]} {
7523 gettreediffs $diffids
7531 # empty string or positive integer
7532 proc diffcontextvalidate {v} {
7533 return [regexp {^(|[1-9][0-9]*)$} $v]
7536 proc diffcontextchange {n1 n2 op} {
7537 global diffcontextstring diffcontext
7539 if {[string is integer -strict $diffcontextstring]} {
7540 if {$diffcontextstring >= 0} {
7541 set diffcontext $diffcontextstring
7547 proc changeignorespace {} {
7551 proc changeworddiff {name ix op} {
7555 proc getblobdiffs {ids} {
7556 global blobdifffd diffids env
7557 global diffinhdr treediffs
7561 global limitdiffs vfilelimit curview
7562 global diffencoding targetline diffnparents
7563 global git_version currdiffsubmod
7566 if {[package vcompare $git_version "1.6.1"] >= 0} {
7567 set textconv "--textconv"
7570 if {[package vcompare $git_version "1.6.6"] >= 0} {
7571 set submodule "--submodule"
7573 set cmd [diffcmd $ids "-p $textconv $submodule -C --cc --no-commit-id -U$diffcontext"]
7577 if {$worddiff ne [mc "Line diff"]} {
7578 append cmd " --word-diff=porcelain"
7580 if {$limitdiffs && $vfilelimit($curview) ne {}} {
7581 set cmd [concat $cmd -- $vfilelimit($curview)]
7583 if {[catch {set bdf [open $cmd r]} err]} {
7584 error_popup [mc "Error getting diffs: %s" $err]
7590 set diffencoding [get_path_encoding {}]
7591 fconfigure $bdf -blocking 0 -encoding binary -eofchar {}
7592 set blobdifffd($ids) $bdf
7593 set currdiffsubmod ""
7594 filerun $bdf [list getblobdiffline $bdf $diffids]
7597 proc savecmitpos {} {
7598 global ctext cmitmode
7600 if {$cmitmode eq "tree"} {
7603 return [list target_scrollpos [$ctext index @0,0]]
7606 proc savectextpos {} {
7609 return [list target_scrollpos [$ctext index @0,0]]
7612 proc maybe_scroll_ctext {ateof} {
7613 global ctext target_scrollpos
7615 if {![info exists target_scrollpos]} return
7617 set nlines [expr {[winfo height $ctext]
7618 / [font metrics textfont -linespace]}]
7619 if {[$ctext compare "$target_scrollpos + $nlines lines" <= end]} return
7621 $ctext yview $target_scrollpos
7622 unset target_scrollpos
7625 proc setinlist {var i val} {
7628 while {[llength [set $var]] < $i} {
7631 if {[llength [set $var]] == $i} {
7638 proc makediffhdr {fname ids} {
7639 global ctext curdiffstart treediffs diffencoding
7640 global ctext_file_names jump_to_here targetline diffline
7642 set fname [encoding convertfrom $fname]
7643 set diffencoding [get_path_encoding $fname]
7644 set i [lsearch -exact $treediffs($ids) $fname]
7646 setinlist difffilestart $i $curdiffstart
7648 lset ctext_file_names end $fname
7649 set l [expr {(78 - [string length $fname]) / 2}]
7650 set pad [string range "----------------------------------------" 1 $l]
7651 $ctext insert $curdiffstart "$pad $fname $pad" filesep
7653 if {$jump_to_here ne {} && [lindex $jump_to_here 0] eq $fname} {
7654 set targetline [lindex $jump_to_here 1]
7659 proc getblobdiffline {bdf ids} {
7660 global diffids blobdifffd ctext curdiffstart
7661 global diffnexthead diffnextnote difffilestart
7662 global ctext_file_names ctext_file_lines
7663 global diffinhdr treediffs mergemax diffnparents
7664 global diffencoding jump_to_here targetline diffline currdiffsubmod
7668 $ctext conf -state normal
7669 while {[incr nr] <= 1000 && [gets $bdf line] >= 0} {
7670 if {$ids != $diffids || $bdf != $blobdifffd($ids)} {
7674 if {![string compare -length 5 "diff " $line]} {
7675 if {![regexp {^diff (--cc|--git) } $line m type]} {
7676 set line [encoding convertfrom $line]
7677 $ctext insert end "$line\n" hunksep
7680 # start of a new file
7682 $ctext insert end "\n"
7683 set curdiffstart [$ctext index "end - 1c"]
7684 lappend ctext_file_names ""
7685 lappend ctext_file_lines [lindex [split $curdiffstart "."] 0]
7686 $ctext insert end "\n" filesep
7688 if {$type eq "--cc"} {
7689 # start of a new file in a merge diff
7690 set fname [string range $line 10 end]
7691 if {[lsearch -exact $treediffs($ids) $fname] < 0} {
7692 lappend treediffs($ids) $fname
7693 add_flist [list $fname]
7697 set line [string range $line 11 end]
7698 # If the name hasn't changed the length will be odd,
7699 # the middle char will be a space, and the two bits either
7700 # side will be a/name and b/name, or "a/name" and "b/name".
7701 # If the name has changed we'll get "rename from" and
7702 # "rename to" or "copy from" and "copy to" lines following
7703 # this, and we'll use them to get the filenames.
7704 # This complexity is necessary because spaces in the
7705 # filename(s) don't get escaped.
7706 set l [string length $line]
7707 set i [expr {$l / 2}]
7708 if {!(($l & 1) && [string index $line $i] eq " " &&
7709 [string range $line 2 [expr {$i - 1}]] eq \
7710 [string range $line [expr {$i + 3}] end])} {
7713 # unescape if quoted and chop off the a/ from the front
7714 if {[string index $line 0] eq "\""} {
7715 set fname [string range [lindex $line 0] 2 end]
7717 set fname [string range $line 2 [expr {$i - 1}]]
7720 makediffhdr $fname $ids
7722 } elseif {![string compare -length 16 "* Unmerged path " $line]} {
7723 set fname [encoding convertfrom [string range $line 16 end]]
7724 $ctext insert end "\n"
7725 set curdiffstart [$ctext index "end - 1c"]
7726 lappend ctext_file_names $fname
7727 lappend ctext_file_lines [lindex [split $curdiffstart "."] 0]
7728 $ctext insert end "$line\n" filesep
7729 set i [lsearch -exact $treediffs($ids) $fname]
7731 setinlist difffilestart $i $curdiffstart
7734 } elseif {![string compare -length 2 "@@" $line]} {
7735 regexp {^@@+} $line ats
7736 set line [encoding convertfrom $diffencoding $line]
7737 $ctext insert end "$line\n" hunksep
7738 if {[regexp { \+(\d+),\d+ @@} $line m nl]} {
7741 set diffnparents [expr {[string length $ats] - 1}]
7744 } elseif {![string compare -length 10 "Submodule " $line]} {
7745 # start of a new submodule
7746 if {[regexp -indices "\[0-9a-f\]+\\.\\." $line nameend]} {
7747 set fname [string range $line 10 [expr [lindex $nameend 0] - 2]]
7749 set fname [string range $line 10 [expr [string first "contains " $line] - 2]]
7751 if {$currdiffsubmod != $fname} {
7752 $ctext insert end "\n"; # Add newline after commit message
7754 set curdiffstart [$ctext index "end - 1c"]
7755 lappend ctext_file_names ""
7756 if {$currdiffsubmod != $fname} {
7757 lappend ctext_file_lines $fname
7758 makediffhdr $fname $ids
7759 set currdiffsubmod $fname
7760 $ctext insert end "\n$line\n" filesep
7762 $ctext insert end "$line\n" filesep
7764 } elseif {![string compare -length 3 " >" $line]} {
7765 set $currdiffsubmod ""
7766 set line [encoding convertfrom $diffencoding $line]
7767 $ctext insert end "$line\n" dresult
7768 } elseif {![string compare -length 3 " <" $line]} {
7769 set $currdiffsubmod ""
7770 set line [encoding convertfrom $diffencoding $line]
7771 $ctext insert end "$line\n" d0
7772 } elseif {$diffinhdr} {
7773 if {![string compare -length 12 "rename from " $line]} {
7774 set fname [string range $line [expr 6 + [string first " from " $line] ] end]
7775 if {[string index $fname 0] eq "\""} {
7776 set fname [lindex $fname 0]
7778 set fname [encoding convertfrom $fname]
7779 set i [lsearch -exact $treediffs($ids) $fname]
7781 setinlist difffilestart $i $curdiffstart
7783 } elseif {![string compare -length 10 $line "rename to "] ||
7784 ![string compare -length 8 $line "copy to "]} {
7785 set fname [string range $line [expr 4 + [string first " to " $line] ] end]
7786 if {[string index $fname 0] eq "\""} {
7787 set fname [lindex $fname 0]
7789 makediffhdr $fname $ids
7790 } elseif {[string compare -length 3 $line "---"] == 0} {
7793 } elseif {[string compare -length 3 $line "+++"] == 0} {
7797 $ctext insert end "$line\n" filesep
7800 set line [string map {\x1A ^Z} \
7801 [encoding convertfrom $diffencoding $line]]
7802 # parse the prefix - one ' ', '-' or '+' for each parent
7803 set prefix [string range $line 0 [expr {$diffnparents - 1}]]
7804 set tag [expr {$diffnparents > 1? "m": "d"}]
7805 set dowords [expr {$worddiff ne [mc "Line diff"] && $diffnparents == 1}]
7806 set words_pre_markup ""
7807 set words_post_markup ""
7808 if {[string trim $prefix " -+"] eq {}} {
7809 # prefix only has " ", "-" and "+" in it: normal diff line
7810 set num [string first "-" $prefix]
7812 set line [string range $line 1 end]
7815 # removed line, first parent with line is $num
7816 if {$num >= $mergemax} {
7819 if {$dowords && $worddiff eq [mc "Markup words"]} {
7820 $ctext insert end "\[-$line-\]" $tag$num
7822 $ctext insert end "$line" $tag$num
7825 $ctext insert end "\n" $tag$num
7829 if {[string first "+" $prefix] >= 0} {
7831 lappend tags ${tag}result
7832 if {$diffnparents > 1} {
7833 set num [string first " " $prefix]
7835 if {$num >= $mergemax} {
7841 set words_pre_markup "{+"
7842 set words_post_markup "+}"
7844 if {$targetline ne {}} {
7845 if {$diffline == $targetline} {
7846 set seehere [$ctext index "end - 1 chars"]
7852 if {$dowords && $worddiff eq [mc "Markup words"]} {
7853 $ctext insert end "$words_pre_markup$line$words_post_markup" $tags
7855 $ctext insert end "$line" $tags
7858 $ctext insert end "\n" $tags
7861 } elseif {$dowords && $prefix eq "~"} {
7862 $ctext insert end "\n" {}
7864 # "\ No newline at end of file",
7865 # or something else we don't recognize
7866 $ctext insert end "$line\n" hunksep
7870 if {[info exists seehere]} {
7871 mark_ctext_line [lindex [split $seehere .] 0]
7873 maybe_scroll_ctext [eof $bdf]
7874 $ctext conf -state disabled
7879 return [expr {$nr >= 1000? 2: 1}]
7882 proc changediffdisp {} {
7883 global ctext diffelide
7885 $ctext tag conf d0 -elide [lindex $diffelide 0]
7886 $ctext tag conf dresult -elide [lindex $diffelide 1]
7889 proc highlightfile {loc cline} {
7890 global ctext cflist cflist_top
7893 $cflist tag remove highlight $cflist_top.0 "$cflist_top.0 lineend"
7894 $cflist tag add highlight $cline.0 "$cline.0 lineend"
7895 $cflist see $cline.0
7896 set cflist_top $cline
7900 global difffilestart ctext cmitmode
7902 if {$cmitmode eq "tree"} return
7905 set here [$ctext index @0,0]
7906 foreach loc $difffilestart {
7907 if {[$ctext compare $loc >= $here]} {
7908 highlightfile $prev $prevline
7914 highlightfile $prev $prevline
7918 global difffilestart ctext cmitmode
7920 if {$cmitmode eq "tree"} return
7921 set here [$ctext index @0,0]
7923 foreach loc $difffilestart {
7925 if {[$ctext compare $loc > $here]} {
7926 highlightfile $loc $line
7932 proc clear_ctext {{first 1.0}} {
7933 global ctext smarktop smarkbot
7934 global ctext_file_names ctext_file_lines
7937 set l [lindex [split $first .] 0]
7938 if {![info exists smarktop] || [$ctext compare $first < $smarktop.0]} {
7941 if {![info exists smarkbot] || [$ctext compare $first < $smarkbot.0]} {
7944 $ctext delete $first end
7945 if {$first eq "1.0"} {
7946 catch {unset pendinglinks}
7948 set ctext_file_names {}
7949 set ctext_file_lines {}
7952 proc settabs {{firstab {}}} {
7953 global firsttabstop tabstop ctext have_tk85
7955 if {$firstab ne {} && $have_tk85} {
7956 set firsttabstop $firstab
7958 set w [font measure textfont "0"]
7959 if {$firsttabstop != 0} {
7960 $ctext conf -tabs [list [expr {($firsttabstop + $tabstop) * $w}] \
7961 [expr {($firsttabstop + 2 * $tabstop) * $w}]]
7962 } elseif {$have_tk85 || $tabstop != 8} {
7963 $ctext conf -tabs [expr {$tabstop * $w}]
7965 $ctext conf -tabs {}
7969 proc incrsearch {name ix op} {
7970 global ctext searchstring searchdirn
7972 $ctext tag remove found 1.0 end
7973 if {[catch {$ctext index anchor}]} {
7974 # no anchor set, use start of selection, or of visible area
7975 set sel [$ctext tag ranges sel]
7977 $ctext mark set anchor [lindex $sel 0]
7978 } elseif {$searchdirn eq "-forwards"} {
7979 $ctext mark set anchor @0,0
7981 $ctext mark set anchor @0,[winfo height $ctext]
7984 if {$searchstring ne {}} {
7985 set here [$ctext search $searchdirn -- $searchstring anchor]
7994 global sstring ctext searchstring searchdirn
7997 $sstring icursor end
7998 set searchdirn -forwards
7999 if {$searchstring ne {}} {
8000 set sel [$ctext tag ranges sel]
8002 set start "[lindex $sel 0] + 1c"
8003 } elseif {[catch {set start [$ctext index anchor]}]} {
8006 set match [$ctext search -count mlen -- $searchstring $start]
8007 $ctext tag remove sel 1.0 end
8013 set mend "$match + $mlen c"
8014 $ctext tag add sel $match $mend
8015 $ctext mark unset anchor
8019 proc dosearchback {} {
8020 global sstring ctext searchstring searchdirn
8023 $sstring icursor end
8024 set searchdirn -backwards
8025 if {$searchstring ne {}} {
8026 set sel [$ctext tag ranges sel]
8028 set start [lindex $sel 0]
8029 } elseif {[catch {set start [$ctext index anchor]}]} {
8030 set start @0,[winfo height $ctext]
8032 set match [$ctext search -backwards -count ml -- $searchstring $start]
8033 $ctext tag remove sel 1.0 end
8039 set mend "$match + $ml c"
8040 $ctext tag add sel $match $mend
8041 $ctext mark unset anchor
8045 proc searchmark {first last} {
8046 global ctext searchstring
8050 set match [$ctext search -count mlen -- $searchstring $mend $last.end]
8051 if {$match eq {}} break
8052 set mend "$match + $mlen c"
8053 $ctext tag add found $match $mend
8057 proc searchmarkvisible {doall} {
8058 global ctext smarktop smarkbot
8060 set topline [lindex [split [$ctext index @0,0] .] 0]
8061 set botline [lindex [split [$ctext index @0,[winfo height $ctext]] .] 0]
8062 if {$doall || $botline < $smarktop || $topline > $smarkbot} {
8063 # no overlap with previous
8064 searchmark $topline $botline
8065 set smarktop $topline
8066 set smarkbot $botline
8068 if {$topline < $smarktop} {
8069 searchmark $topline [expr {$smarktop-1}]
8070 set smarktop $topline
8072 if {$botline > $smarkbot} {
8073 searchmark [expr {$smarkbot+1}] $botline
8074 set smarkbot $botline
8079 proc scrolltext {f0 f1} {
8082 .bleft.bottom.sb set $f0 $f1
8083 if {$searchstring ne {}} {
8089 global linespc charspc canvx0 canvy0
8090 global xspc1 xspc2 lthickness
8092 set linespc [font metrics mainfont -linespace]
8093 set charspc [font measure mainfont "m"]
8094 set canvy0 [expr {int(3 + 0.5 * $linespc)}]
8095 set canvx0 [expr {int(3 + 0.5 * $linespc)}]
8096 set lthickness [expr {int($linespc / 9) + 1}]
8097 set xspc1(0) $linespc
8105 set ymax [lindex [$canv cget -scrollregion] 3]
8106 if {$ymax eq {} || $ymax == 0} return
8107 set span [$canv yview]
8110 allcanvs yview moveto [lindex $span 0]
8112 if {$selectedline ne {}} {
8113 selectline $selectedline 0
8114 allcanvs yview moveto [lindex $span 0]
8118 proc parsefont {f n} {
8121 set fontattr($f,family) [lindex $n 0]
8123 if {$s eq {} || $s == 0} {
8126 set s [expr {int(-$s / [winfo fpixels . 1p] + 0.5)}]
8128 set fontattr($f,size) $s
8129 set fontattr($f,weight) normal
8130 set fontattr($f,slant) roman
8131 foreach style [lrange $n 2 end] {
8134 "bold" {set fontattr($f,weight) $style}
8136 "italic" {set fontattr($f,slant) $style}
8141 proc fontflags {f {isbold 0}} {
8144 return [list -family $fontattr($f,family) -size $fontattr($f,size) \
8145 -weight [expr {$isbold? "bold": $fontattr($f,weight)}] \
8146 -slant $fontattr($f,slant)]
8152 set n [list $fontattr($f,family) $fontattr($f,size)]
8153 if {$fontattr($f,weight) eq "bold"} {
8156 if {$fontattr($f,slant) eq "italic"} {
8162 proc incrfont {inc} {
8163 global mainfont textfont ctext canv cflist showrefstop
8164 global stopped entries fontattr
8167 set s $fontattr(mainfont,size)
8172 set fontattr(mainfont,size) $s
8173 font config mainfont -size $s
8174 font config mainfontbold -size $s
8175 set mainfont [fontname mainfont]
8176 set s $fontattr(textfont,size)
8181 set fontattr(textfont,size) $s
8182 font config textfont -size $s
8183 font config textfontbold -size $s
8184 set textfont [fontname textfont]
8191 global sha1entry sha1string
8192 if {[string length $sha1string] == 40} {
8193 $sha1entry delete 0 end
8197 proc sha1change {n1 n2 op} {
8198 global sha1string currentid sha1but
8199 if {$sha1string == {}
8200 || ([info exists currentid] && $sha1string == $currentid)} {
8205 if {[$sha1but cget -state] == $state} return
8206 if {$state == "normal"} {
8207 $sha1but conf -state normal -relief raised -text "[mc "Goto:"] "
8209 $sha1but conf -state disabled -relief flat -text "[mc "SHA1 ID:"] "
8213 proc gotocommit {} {
8214 global sha1string tagids headids curview varcid
8216 if {$sha1string == {}
8217 || ([info exists currentid] && $sha1string == $currentid)} return
8218 if {[info exists tagids($sha1string)]} {
8219 set id $tagids($sha1string)
8220 } elseif {[info exists headids($sha1string)]} {
8221 set id $headids($sha1string)
8223 set id [string tolower $sha1string]
8224 if {[regexp {^[0-9a-f]{4,39}$} $id]} {
8225 set matches [longid $id]
8226 if {$matches ne {}} {
8227 if {[llength $matches] > 1} {
8228 error_popup [mc "Short SHA1 id %s is ambiguous" $id]
8231 set id [lindex $matches 0]
8234 if {[catch {set id [exec git rev-parse --verify $sha1string]}]} {
8235 error_popup [mc "Revision %s is not known" $sha1string]
8240 if {[commitinview $id $curview]} {
8241 selectline [rowofcommit $id] 1
8244 if {[regexp {^[0-9a-fA-F]{4,}$} $sha1string]} {
8245 set msg [mc "SHA1 id %s is not known" $sha1string]
8247 set msg [mc "Revision %s is not in the current view" $sha1string]
8252 proc lineenter {x y id} {
8253 global hoverx hovery hoverid hovertimer
8254 global commitinfo canv
8256 if {![info exists commitinfo($id)] && ![getcommit $id]} return
8260 if {[info exists hovertimer]} {
8261 after cancel $hovertimer
8263 set hovertimer [after 500 linehover]
8267 proc linemotion {x y id} {
8268 global hoverx hovery hoverid hovertimer
8270 if {[info exists hoverid] && $id == $hoverid} {
8273 if {[info exists hovertimer]} {
8274 after cancel $hovertimer
8276 set hovertimer [after 500 linehover]
8280 proc lineleave {id} {
8281 global hoverid hovertimer canv
8283 if {[info exists hoverid] && $id == $hoverid} {
8285 if {[info exists hovertimer]} {
8286 after cancel $hovertimer
8294 global hoverx hovery hoverid hovertimer
8295 global canv linespc lthickness
8298 set text [lindex $commitinfo($hoverid) 0]
8299 set ymax [lindex [$canv cget -scrollregion] 3]
8300 if {$ymax == {}} return
8301 set yfrac [lindex [$canv yview] 0]
8302 set x [expr {$hoverx + 2 * $linespc}]
8303 set y [expr {$hovery + $yfrac * $ymax - $linespc / 2}]
8304 set x0 [expr {$x - 2 * $lthickness}]
8305 set y0 [expr {$y - 2 * $lthickness}]
8306 set x1 [expr {$x + [font measure mainfont $text] + 2 * $lthickness}]
8307 set y1 [expr {$y + $linespc + 2 * $lthickness}]
8308 set t [$canv create rectangle $x0 $y0 $x1 $y1 \
8309 -fill \#ffff80 -outline black -width 1 -tags hover]
8311 set t [$canv create text $x $y -anchor nw -text $text -tags hover \
8316 proc clickisonarrow {id y} {
8319 set ranges [rowranges $id]
8320 set thresh [expr {2 * $lthickness + 6}]
8321 set n [expr {[llength $ranges] - 1}]
8322 for {set i 1} {$i < $n} {incr i} {
8323 set row [lindex $ranges $i]
8324 if {abs([yc $row] - $y) < $thresh} {
8331 proc arrowjump {id n y} {
8334 # 1 <-> 2, 3 <-> 4, etc...
8335 set n [expr {(($n - 1) ^ 1) + 1}]
8336 set row [lindex [rowranges $id] $n]
8338 set ymax [lindex [$canv cget -scrollregion] 3]
8339 if {$ymax eq {} || $ymax <= 0} return
8340 set view [$canv yview]
8341 set yspan [expr {[lindex $view 1] - [lindex $view 0]}]
8342 set yfrac [expr {$yt / $ymax - $yspan / 2}]
8346 allcanvs yview moveto $yfrac
8349 proc lineclick {x y id isnew} {
8350 global ctext commitinfo children canv thickerline curview
8352 if {![info exists commitinfo($id)] && ![getcommit $id]} return
8357 # draw this line thicker than normal
8361 set ymax [lindex [$canv cget -scrollregion] 3]
8362 if {$ymax eq {}} return
8363 set yfrac [lindex [$canv yview] 0]
8364 set y [expr {$y + $yfrac * $ymax}]
8366 set dirn [clickisonarrow $id $y]
8368 arrowjump $id $dirn $y
8373 addtohistory [list lineclick $x $y $id 0] savectextpos
8375 # fill the details pane with info about this line
8376 $ctext conf -state normal
8379 $ctext insert end "[mc "Parent"]:\t"
8380 $ctext insert end $id link0
8382 set info $commitinfo($id)
8383 $ctext insert end "\n\t[lindex $info 0]\n"
8384 $ctext insert end "\t[mc "Author"]:\t[lindex $info 1]\n"
8385 set date [formatdate [lindex $info 2]]
8386 $ctext insert end "\t[mc "Date"]:\t$date\n"
8387 set kids $children($curview,$id)
8389 $ctext insert end "\n[mc "Children"]:"
8391 foreach child $kids {
8393 if {![info exists commitinfo($child)] && ![getcommit $child]} continue
8394 set info $commitinfo($child)
8395 $ctext insert end "\n\t"
8396 $ctext insert end $child link$i
8397 setlink $child link$i
8398 $ctext insert end "\n\t[lindex $info 0]"
8399 $ctext insert end "\n\t[mc "Author"]:\t[lindex $info 1]"
8400 set date [formatdate [lindex $info 2]]
8401 $ctext insert end "\n\t[mc "Date"]:\t$date\n"
8404 maybe_scroll_ctext 1
8405 $ctext conf -state disabled
8409 proc normalline {} {
8411 if {[info exists thickerline]} {
8418 proc selbyid {id {isnew 1}} {
8420 if {[commitinview $id $curview]} {
8421 selectline [rowofcommit $id] $isnew
8427 if {![info exists startmstime]} {
8428 set startmstime [clock clicks -milliseconds]
8430 return [format "%.3f" [expr {([clock click -milliseconds] - $startmstime) / 1000.0}]]
8433 proc rowmenu {x y id} {
8434 global rowctxmenu selectedline rowmenuid curview
8435 global nullid nullid2 fakerowmenu mainhead markedid
8439 if {$selectedline eq {} || [rowofcommit $id] eq $selectedline} {
8444 if {$id ne $nullid && $id ne $nullid2} {
8445 set menu $rowctxmenu
8446 if {$mainhead ne {}} {
8447 $menu entryconfigure 7 -label [mc "Reset %s branch to here" $mainhead] -state normal
8449 $menu entryconfigure 7 -label [mc "Detached head: can't reset" $mainhead] -state disabled
8451 if {[info exists markedid] && $markedid ne $id} {
8452 $menu entryconfigure 9 -state normal
8453 $menu entryconfigure 10 -state normal
8454 $menu entryconfigure 11 -state normal
8456 $menu entryconfigure 9 -state disabled
8457 $menu entryconfigure 10 -state disabled
8458 $menu entryconfigure 11 -state disabled
8461 set menu $fakerowmenu
8463 $menu entryconfigure [mca "Diff this -> selected"] -state $state
8464 $menu entryconfigure [mca "Diff selected -> this"] -state $state
8465 $menu entryconfigure [mca "Make patch"] -state $state
8466 tk_popup $menu $x $y
8470 global rowmenuid markedid canv
8472 set markedid $rowmenuid
8473 make_idmark $markedid
8479 if {[info exists markedid]} {
8484 proc replace_by_kids {l r} {
8485 global curview children
8487 set id [commitonrow $r]
8488 set l [lreplace $l 0 0]
8489 foreach kid $children($curview,$id) {
8490 lappend l [rowofcommit $kid]
8492 return [lsort -integer -decreasing -unique $l]
8495 proc find_common_desc {} {
8496 global markedid rowmenuid curview children
8498 if {![info exists markedid]} return
8499 if {![commitinview $markedid $curview] ||
8500 ![commitinview $rowmenuid $curview]} return
8501 #set t1 [clock clicks -milliseconds]
8502 set l1 [list [rowofcommit $markedid]]
8503 set l2 [list [rowofcommit $rowmenuid]]
8505 set r1 [lindex $l1 0]
8506 set r2 [lindex $l2 0]
8507 if {$r1 eq {} || $r2 eq {}} break
8513 set l1 [replace_by_kids $l1 $r1]
8515 set l2 [replace_by_kids $l2 $r2]
8518 #set t2 [clock clicks -milliseconds]
8519 #puts "took [expr {$t2-$t1}]ms"
8522 proc compare_commits {} {
8523 global markedid rowmenuid curview children
8525 if {![info exists markedid]} return
8526 if {![commitinview $markedid $curview]} return
8527 addtohistory [list do_cmp_commits $markedid $rowmenuid]
8528 do_cmp_commits $markedid $rowmenuid
8531 proc getpatchid {id} {
8534 if {![info exists patchids($id)]} {
8535 set cmd [diffcmd [list $id] {-p --root}]
8536 # trim off the initial "|"
8537 set cmd [lrange $cmd 1 end]
8539 set x [eval exec $cmd | git patch-id]
8540 set patchids($id) [lindex $x 0]
8542 set patchids($id) "error"
8545 return $patchids($id)
8548 proc do_cmp_commits {a b} {
8549 global ctext curview parents children patchids commitinfo
8551 $ctext conf -state normal
8554 for {set i 0} {$i < 100} {incr i} {
8557 if {[llength $parents($curview,$a)] > 1} {
8558 appendshortlink $a [mc "Skipping merge commit "] "\n"
8561 set patcha [getpatchid $a]
8563 if {[llength $parents($curview,$b)] > 1} {
8564 appendshortlink $b [mc "Skipping merge commit "] "\n"
8567 set patchb [getpatchid $b]
8569 if {!$skipa && !$skipb} {
8570 set heada [lindex $commitinfo($a) 0]
8571 set headb [lindex $commitinfo($b) 0]
8572 if {$patcha eq "error"} {
8573 appendshortlink $a [mc "Error getting patch ID for "] \
8574 [mc " - stopping\n"]
8577 if {$patchb eq "error"} {
8578 appendshortlink $b [mc "Error getting patch ID for "] \
8579 [mc " - stopping\n"]
8582 if {$patcha eq $patchb} {
8583 if {$heada eq $headb} {
8584 appendshortlink $a [mc "Commit "]
8585 appendshortlink $b " == " " $heada\n"
8587 appendshortlink $a [mc "Commit "] " $heada\n"
8588 appendshortlink $b [mc " is the same patch as\n "] \
8594 $ctext insert end "\n"
8595 appendshortlink $a [mc "Commit "] " $heada\n"
8596 appendshortlink $b [mc " differs from\n "] \
8598 $ctext insert end [mc "Diff of commits:\n\n"]
8599 $ctext conf -state disabled
8606 set kids [real_children $curview,$a]
8607 if {[llength $kids] != 1} {
8608 $ctext insert end "\n"
8609 appendshortlink $a [mc "Commit "] \
8610 [mc " has %s children - stopping\n" [llength $kids]]
8613 set a [lindex $kids 0]
8616 set kids [real_children $curview,$b]
8617 if {[llength $kids] != 1} {
8618 appendshortlink $b [mc "Commit "] \
8619 [mc " has %s children - stopping\n" [llength $kids]]
8622 set b [lindex $kids 0]
8625 $ctext conf -state disabled
8628 proc diffcommits {a b} {
8629 global diffcontext diffids blobdifffd diffinhdr currdiffsubmod
8631 set tmpdir [gitknewtmpdir]
8632 set fna [file join $tmpdir "commit-[string range $a 0 7]"]
8633 set fnb [file join $tmpdir "commit-[string range $b 0 7]"]
8635 exec git diff-tree -p --pretty $a >$fna
8636 exec git diff-tree -p --pretty $b >$fnb
8638 error_popup [mc "Error writing commit to file: %s" $err]
8642 set fd [open "| diff -U$diffcontext $fna $fnb" r]
8644 error_popup [mc "Error diffing commits: %s" $err]
8647 set diffids [list commits $a $b]
8648 set blobdifffd($diffids) $fd
8650 set currdiffsubmod ""
8651 filerun $fd [list getblobdiffline $fd $diffids]
8654 proc diffvssel {dirn} {
8655 global rowmenuid selectedline
8657 if {$selectedline eq {}} return
8659 set oldid [commitonrow $selectedline]
8660 set newid $rowmenuid
8662 set oldid $rowmenuid
8663 set newid [commitonrow $selectedline]
8665 addtohistory [list doseldiff $oldid $newid] savectextpos
8666 doseldiff $oldid $newid
8669 proc doseldiff {oldid newid} {
8673 $ctext conf -state normal
8675 init_flist [mc "Top"]
8676 $ctext insert end "[mc "From"] "
8677 $ctext insert end $oldid link0
8678 setlink $oldid link0
8679 $ctext insert end "\n "
8680 $ctext insert end [lindex $commitinfo($oldid) 0]
8681 $ctext insert end "\n\n[mc "To"] "
8682 $ctext insert end $newid link1
8683 setlink $newid link1
8684 $ctext insert end "\n "
8685 $ctext insert end [lindex $commitinfo($newid) 0]
8686 $ctext insert end "\n"
8687 $ctext conf -state disabled
8688 $ctext tag remove found 1.0 end
8689 startdiff [list $oldid $newid]
8693 global rowmenuid currentid commitinfo patchtop patchnum NS
8695 if {![info exists currentid]} return
8696 set oldid $currentid
8697 set oldhead [lindex $commitinfo($oldid) 0]
8698 set newid $rowmenuid
8699 set newhead [lindex $commitinfo($newid) 0]
8702 catch {destroy $top}
8704 make_transient $top .
8705 ${NS}::label $top.title -text [mc "Generate patch"]
8706 grid $top.title - -pady 10
8707 ${NS}::label $top.from -text [mc "From:"]
8708 ${NS}::entry $top.fromsha1 -width 40
8709 $top.fromsha1 insert 0 $oldid
8710 $top.fromsha1 conf -state readonly
8711 grid $top.from $top.fromsha1 -sticky w
8712 ${NS}::entry $top.fromhead -width 60
8713 $top.fromhead insert 0 $oldhead
8714 $top.fromhead conf -state readonly
8715 grid x $top.fromhead -sticky w
8716 ${NS}::label $top.to -text [mc "To:"]
8717 ${NS}::entry $top.tosha1 -width 40
8718 $top.tosha1 insert 0 $newid
8719 $top.tosha1 conf -state readonly
8720 grid $top.to $top.tosha1 -sticky w
8721 ${NS}::entry $top.tohead -width 60
8722 $top.tohead insert 0 $newhead
8723 $top.tohead conf -state readonly
8724 grid x $top.tohead -sticky w
8725 ${NS}::button $top.rev -text [mc "Reverse"] -command mkpatchrev
8726 grid $top.rev x -pady 10 -padx 5
8727 ${NS}::label $top.flab -text [mc "Output file:"]
8728 ${NS}::entry $top.fname -width 60
8729 $top.fname insert 0 [file normalize "patch$patchnum.patch"]
8731 grid $top.flab $top.fname -sticky w
8732 ${NS}::frame $top.buts
8733 ${NS}::button $top.buts.gen -text [mc "Generate"] -command mkpatchgo
8734 ${NS}::button $top.buts.can -text [mc "Cancel"] -command mkpatchcan
8735 bind $top <Key-Return> mkpatchgo
8736 bind $top <Key-Escape> mkpatchcan
8737 grid $top.buts.gen $top.buts.can
8738 grid columnconfigure $top.buts 0 -weight 1 -uniform a
8739 grid columnconfigure $top.buts 1 -weight 1 -uniform a
8740 grid $top.buts - -pady 10 -sticky ew
8744 proc mkpatchrev {} {
8747 set oldid [$patchtop.fromsha1 get]
8748 set oldhead [$patchtop.fromhead get]
8749 set newid [$patchtop.tosha1 get]
8750 set newhead [$patchtop.tohead get]
8751 foreach e [list fromsha1 fromhead tosha1 tohead] \
8752 v [list $newid $newhead $oldid $oldhead] {
8753 $patchtop.$e conf -state normal
8754 $patchtop.$e delete 0 end
8755 $patchtop.$e insert 0 $v
8756 $patchtop.$e conf -state readonly
8761 global patchtop nullid nullid2
8763 set oldid [$patchtop.fromsha1 get]
8764 set newid [$patchtop.tosha1 get]
8765 set fname [$patchtop.fname get]
8766 set cmd [diffcmd [list $oldid $newid] -p]
8767 # trim off the initial "|"
8768 set cmd [lrange $cmd 1 end]
8769 lappend cmd >$fname &
8770 if {[catch {eval exec $cmd} err]} {
8771 error_popup "[mc "Error creating patch:"] $err" $patchtop
8773 catch {destroy $patchtop}
8777 proc mkpatchcan {} {
8780 catch {destroy $patchtop}
8785 global rowmenuid mktagtop commitinfo NS
8789 catch {destroy $top}
8791 make_transient $top .
8792 ${NS}::label $top.title -text [mc "Create tag"]
8793 grid $top.title - -pady 10
8794 ${NS}::label $top.id -text [mc "ID:"]
8795 ${NS}::entry $top.sha1 -width 40
8796 $top.sha1 insert 0 $rowmenuid
8797 $top.sha1 conf -state readonly
8798 grid $top.id $top.sha1 -sticky w
8799 ${NS}::entry $top.head -width 60
8800 $top.head insert 0 [lindex $commitinfo($rowmenuid) 0]
8801 $top.head conf -state readonly
8802 grid x $top.head -sticky w
8803 ${NS}::label $top.tlab -text [mc "Tag name:"]
8804 ${NS}::entry $top.tag -width 60
8805 grid $top.tlab $top.tag -sticky w
8806 ${NS}::label $top.op -text [mc "Tag message is optional"]
8807 grid $top.op -columnspan 2 -sticky we
8808 ${NS}::label $top.mlab -text [mc "Tag message:"]
8809 ${NS}::entry $top.msg -width 60
8810 grid $top.mlab $top.msg -sticky w
8811 ${NS}::frame $top.buts
8812 ${NS}::button $top.buts.gen -text [mc "Create"] -command mktaggo
8813 ${NS}::button $top.buts.can -text [mc "Cancel"] -command mktagcan
8814 bind $top <Key-Return> mktaggo
8815 bind $top <Key-Escape> mktagcan
8816 grid $top.buts.gen $top.buts.can
8817 grid columnconfigure $top.buts 0 -weight 1 -uniform a
8818 grid columnconfigure $top.buts 1 -weight 1 -uniform a
8819 grid $top.buts - -pady 10 -sticky ew
8824 global mktagtop env tagids idtags
8826 set id [$mktagtop.sha1 get]
8827 set tag [$mktagtop.tag get]
8828 set msg [$mktagtop.msg get]
8830 error_popup [mc "No tag name specified"] $mktagtop
8833 if {[info exists tagids($tag)]} {
8834 error_popup [mc "Tag \"%s\" already exists" $tag] $mktagtop
8839 exec git tag -a -m $msg $tag $id
8841 exec git tag $tag $id
8844 error_popup "[mc "Error creating tag:"] $err" $mktagtop
8848 set tagids($tag) $id
8849 lappend idtags($id) $tag
8857 proc redrawtags {id} {
8858 global canv linehtag idpos currentid curview cmitlisted markedid
8859 global canvxmax iddrawn circleitem mainheadid circlecolors
8861 if {![commitinview $id $curview]} return
8862 if {![info exists iddrawn($id)]} return
8863 set row [rowofcommit $id]
8864 if {$id eq $mainheadid} {
8867 set ofill [lindex $circlecolors $cmitlisted($curview,$id)]
8869 $canv itemconf $circleitem($row) -fill $ofill
8870 $canv delete tag.$id
8871 set xt [eval drawtags $id $idpos($id)]
8872 $canv coords $linehtag($id) $xt [lindex $idpos($id) 2]
8873 set text [$canv itemcget $linehtag($id) -text]
8874 set font [$canv itemcget $linehtag($id) -font]
8875 set xr [expr {$xt + [font measure $font $text]}]
8876 if {$xr > $canvxmax} {
8880 if {[info exists currentid] && $currentid == $id} {
8883 if {[info exists markedid] && $markedid eq $id} {
8891 catch {destroy $mktagtop}
8896 if {![domktag]} return
8900 proc writecommit {} {
8901 global rowmenuid wrcomtop commitinfo wrcomcmd NS
8903 set top .writecommit
8905 catch {destroy $top}
8907 make_transient $top .
8908 ${NS}::label $top.title -text [mc "Write commit to file"]
8909 grid $top.title - -pady 10
8910 ${NS}::label $top.id -text [mc "ID:"]
8911 ${NS}::entry $top.sha1 -width 40
8912 $top.sha1 insert 0 $rowmenuid
8913 $top.sha1 conf -state readonly
8914 grid $top.id $top.sha1 -sticky w
8915 ${NS}::entry $top.head -width 60
8916 $top.head insert 0 [lindex $commitinfo($rowmenuid) 0]
8917 $top.head conf -state readonly
8918 grid x $top.head -sticky w
8919 ${NS}::label $top.clab -text [mc "Command:"]
8920 ${NS}::entry $top.cmd -width 60 -textvariable wrcomcmd
8921 grid $top.clab $top.cmd -sticky w -pady 10
8922 ${NS}::label $top.flab -text [mc "Output file:"]
8923 ${NS}::entry $top.fname -width 60
8924 $top.fname insert 0 [file normalize "commit-[string range $rowmenuid 0 6]"]
8925 grid $top.flab $top.fname -sticky w
8926 ${NS}::frame $top.buts
8927 ${NS}::button $top.buts.gen -text [mc "Write"] -command wrcomgo
8928 ${NS}::button $top.buts.can -text [mc "Cancel"] -command wrcomcan
8929 bind $top <Key-Return> wrcomgo
8930 bind $top <Key-Escape> wrcomcan
8931 grid $top.buts.gen $top.buts.can
8932 grid columnconfigure $top.buts 0 -weight 1 -uniform a
8933 grid columnconfigure $top.buts 1 -weight 1 -uniform a
8934 grid $top.buts - -pady 10 -sticky ew
8941 set id [$wrcomtop.sha1 get]
8942 set cmd "echo $id | [$wrcomtop.cmd get]"
8943 set fname [$wrcomtop.fname get]
8944 if {[catch {exec sh -c $cmd >$fname &} err]} {
8945 error_popup "[mc "Error writing commit:"] $err" $wrcomtop
8947 catch {destroy $wrcomtop}
8954 catch {destroy $wrcomtop}
8959 global rowmenuid mkbrtop NS
8962 catch {destroy $top}
8964 make_transient $top .
8965 ${NS}::label $top.title -text [mc "Create new branch"]
8966 grid $top.title - -pady 10
8967 ${NS}::label $top.id -text [mc "ID:"]
8968 ${NS}::entry $top.sha1 -width 40
8969 $top.sha1 insert 0 $rowmenuid
8970 $top.sha1 conf -state readonly
8971 grid $top.id $top.sha1 -sticky w
8972 ${NS}::label $top.nlab -text [mc "Name:"]
8973 ${NS}::entry $top.name -width 40
8974 grid $top.nlab $top.name -sticky w
8975 ${NS}::frame $top.buts
8976 ${NS}::button $top.buts.go -text [mc "Create"] -command [list mkbrgo $top]
8977 ${NS}::button $top.buts.can -text [mc "Cancel"] -command "catch {destroy $top}"
8978 bind $top <Key-Return> [list mkbrgo $top]
8979 bind $top <Key-Escape> "catch {destroy $top}"
8980 grid $top.buts.go $top.buts.can
8981 grid columnconfigure $top.buts 0 -weight 1 -uniform a
8982 grid columnconfigure $top.buts 1 -weight 1 -uniform a
8983 grid $top.buts - -pady 10 -sticky ew
8988 global headids idheads
8990 set name [$top.name get]
8991 set id [$top.sha1 get]
8995 error_popup [mc "Please specify a name for the new branch"] $top
8998 if {[info exists headids($name)]} {
8999 if {![confirm_popup [mc \
9000 "Branch '%s' already exists. Overwrite?" $name] $top]} {
9003 set old_id $headids($name)
9006 catch {destroy $top}
9007 lappend cmdargs $name $id
9011 eval exec git branch $cmdargs
9017 if {$old_id ne {}} {
9023 set headids($name) $id
9024 lappend idheads($id) $name
9033 proc exec_citool {tool_args {baseid {}}} {
9034 global commitinfo env
9036 set save_env [array get env GIT_AUTHOR_*]
9038 if {$baseid ne {}} {
9039 if {![info exists commitinfo($baseid)]} {
9042 set author [lindex $commitinfo($baseid) 1]
9043 set date [lindex $commitinfo($baseid) 2]
9044 if {[regexp {^\s*(\S.*\S|\S)\s*<(.*)>\s*$} \
9045 $author author name email]
9047 set env(GIT_AUTHOR_NAME) $name
9048 set env(GIT_AUTHOR_EMAIL) $email
9049 set env(GIT_AUTHOR_DATE) $date
9053 eval exec git citool $tool_args &
9055 array unset env GIT_AUTHOR_*
9056 array set env $save_env
9059 proc cherrypick {} {
9060 global rowmenuid curview
9061 global mainhead mainheadid
9063 set oldhead [exec git rev-parse HEAD]
9064 set dheads [descheads $rowmenuid]
9065 if {$dheads ne {} && [lsearch -exact $dheads $oldhead] >= 0} {
9066 set ok [confirm_popup [mc "Commit %s is already\
9067 included in branch %s -- really re-apply it?" \
9068 [string range $rowmenuid 0 7] $mainhead]]
9071 nowbusy cherrypick [mc "Cherry-picking"]
9073 # Unfortunately git-cherry-pick writes stuff to stderr even when
9074 # no error occurs, and exec takes that as an indication of error...
9075 if {[catch {exec sh -c "git cherry-pick -r $rowmenuid 2>&1"} err]} {
9078 {Entry '(.*)' (would be overwritten by merge|not uptodate)} \
9080 error_popup [mc "Cherry-pick failed because of local changes\
9081 to file '%s'.\nPlease commit, reset or stash\
9082 your changes and try again." $fname]
9083 } elseif {[regexp -line \
9084 {^(CONFLICT \(.*\):|Automatic cherry-pick failed|error: could not apply)} \
9086 if {[confirm_popup [mc "Cherry-pick failed because of merge\
9087 conflict.\nDo you wish to run git citool to\
9089 # Force citool to read MERGE_MSG
9090 file delete [file join [gitdir] "GITGUI_MSG"]
9091 exec_citool {} $rowmenuid
9099 set newhead [exec git rev-parse HEAD]
9100 if {$newhead eq $oldhead} {
9102 error_popup [mc "No changes committed"]
9105 addnewchild $newhead $oldhead
9106 if {[commitinview $oldhead $curview]} {
9107 # XXX this isn't right if we have a path limit...
9108 insertrow $newhead $oldhead $curview
9109 if {$mainhead ne {}} {
9110 movehead $newhead $mainhead
9111 movedhead $newhead $mainhead
9113 set mainheadid $newhead
9122 global mainhead rowmenuid confirm_ok resettype NS
9125 set w ".confirmreset"
9128 wm title $w [mc "Confirm reset"]
9129 ${NS}::label $w.m -text \
9130 [mc "Reset branch %s to %s?" $mainhead [string range $rowmenuid 0 7]]
9131 pack $w.m -side top -fill x -padx 20 -pady 20
9132 ${NS}::labelframe $w.f -text [mc "Reset type:"]
9134 ${NS}::radiobutton $w.f.soft -value soft -variable resettype \
9135 -text [mc "Soft: Leave working tree and index untouched"]
9136 grid $w.f.soft -sticky w
9137 ${NS}::radiobutton $w.f.mixed -value mixed -variable resettype \
9138 -text [mc "Mixed: Leave working tree untouched, reset index"]
9139 grid $w.f.mixed -sticky w
9140 ${NS}::radiobutton $w.f.hard -value hard -variable resettype \
9141 -text [mc "Hard: Reset working tree and index\n(discard ALL local changes)"]
9142 grid $w.f.hard -sticky w
9143 pack $w.f -side top -fill x -padx 4
9144 ${NS}::button $w.ok -text [mc OK] -command "set confirm_ok 1; destroy $w"
9145 pack $w.ok -side left -fill x -padx 20 -pady 20
9146 ${NS}::button $w.cancel -text [mc Cancel] -command "destroy $w"
9147 bind $w <Key-Escape> [list destroy $w]
9148 pack $w.cancel -side right -fill x -padx 20 -pady 20
9149 bind $w <Visibility> "grab $w; focus $w"
9151 if {!$confirm_ok} return
9152 if {[catch {set fd [open \
9153 [list | git reset --$resettype $rowmenuid 2>@1] r]} err]} {
9157 filerun $fd [list readresetstat $fd]
9158 nowbusy reset [mc "Resetting"]
9163 proc readresetstat {fd} {
9164 global mainhead mainheadid showlocalchanges rprogcoord
9166 if {[gets $fd line] >= 0} {
9167 if {[regexp {([0-9]+)% \(([0-9]+)/([0-9]+)\)} $line match p m n]} {
9168 set rprogcoord [expr {1.0 * $m / $n}]
9176 if {[catch {close $fd} err]} {
9179 set oldhead $mainheadid
9180 set newhead [exec git rev-parse HEAD]
9181 if {$newhead ne $oldhead} {
9182 movehead $newhead $mainhead
9183 movedhead $newhead $mainhead
9184 set mainheadid $newhead
9188 if {$showlocalchanges} {
9194 # context menu for a head
9195 proc headmenu {x y id head} {
9196 global headmenuid headmenuhead headctxmenu mainhead
9200 set headmenuhead $head
9202 if {[string match "remotes/*" $head]} {
9205 if {$head eq $mainhead} {
9208 $headctxmenu entryconfigure 0 -state $state
9209 $headctxmenu entryconfigure 1 -state $state
9210 tk_popup $headctxmenu $x $y
9214 global headmenuid headmenuhead headids
9215 global showlocalchanges
9217 # check the tree is clean first??
9218 nowbusy checkout [mc "Checking out"]
9222 set fd [open [list | git checkout $headmenuhead 2>@1] r]
9226 if {$showlocalchanges} {
9230 filerun $fd [list readcheckoutstat $fd $headmenuhead $headmenuid]
9234 proc readcheckoutstat {fd newhead newheadid} {
9235 global mainhead mainheadid headids showlocalchanges progresscoords
9236 global viewmainheadid curview
9238 if {[gets $fd line] >= 0} {
9239 if {[regexp {([0-9]+)% \(([0-9]+)/([0-9]+)\)} $line match p m n]} {
9240 set progresscoords [list 0 [expr {1.0 * $m / $n}]]
9245 set progresscoords {0 0}
9248 if {[catch {close $fd} err]} {
9251 set oldmainid $mainheadid
9252 set mainhead $newhead
9253 set mainheadid $newheadid
9254 set viewmainheadid($curview) $newheadid
9255 redrawtags $oldmainid
9256 redrawtags $newheadid
9258 if {$showlocalchanges} {
9264 global headmenuid headmenuhead mainhead
9267 set head $headmenuhead
9269 # this check shouldn't be needed any more...
9270 if {$head eq $mainhead} {
9271 error_popup [mc "Cannot delete the currently checked-out branch"]
9274 set dheads [descheads $id]
9275 if {[llength $dheads] == 1 && $idheads($dheads) eq $head} {
9276 # the stuff on this branch isn't on any other branch
9277 if {![confirm_popup [mc "The commits on branch %s aren't on any other\
9278 branch.\nReally delete branch %s?" $head $head]]} return
9282 if {[catch {exec git branch -D $head} err]} {
9287 removehead $id $head
9288 removedhead $id $head
9295 # Display a list of tags and heads
9297 global showrefstop bgcolor fgcolor selectbgcolor NS
9298 global bglist fglist reflistfilter reflist maincursor
9301 set showrefstop $top
9302 if {[winfo exists $top]} {
9308 wm title $top [mc "Tags and heads: %s" [file tail [pwd]]]
9309 make_transient $top .
9310 text $top.list -background $bgcolor -foreground $fgcolor \
9311 -selectbackground $selectbgcolor -font mainfont \
9312 -xscrollcommand "$top.xsb set" -yscrollcommand "$top.ysb set" \
9313 -width 30 -height 20 -cursor $maincursor \
9314 -spacing1 1 -spacing3 1 -state disabled
9315 $top.list tag configure highlight -background $selectbgcolor
9316 lappend bglist $top.list
9317 lappend fglist $top.list
9318 ${NS}::scrollbar $top.ysb -command "$top.list yview" -orient vertical
9319 ${NS}::scrollbar $top.xsb -command "$top.list xview" -orient horizontal
9320 grid $top.list $top.ysb -sticky nsew
9321 grid $top.xsb x -sticky ew
9323 ${NS}::label $top.f.l -text "[mc "Filter"]: "
9324 ${NS}::entry $top.f.e -width 20 -textvariable reflistfilter
9325 set reflistfilter "*"
9326 trace add variable reflistfilter write reflistfilter_change
9327 pack $top.f.e -side right -fill x -expand 1
9328 pack $top.f.l -side left
9329 grid $top.f - -sticky ew -pady 2
9330 ${NS}::button $top.close -command [list destroy $top] -text [mc "Close"]
9331 bind $top <Key-Escape> [list destroy $top]
9333 grid columnconfigure $top 0 -weight 1
9334 grid rowconfigure $top 0 -weight 1
9335 bind $top.list <1> {break}
9336 bind $top.list <B1-Motion> {break}
9337 bind $top.list <ButtonRelease-1> {sel_reflist %W %x %y; break}
9342 proc sel_reflist {w x y} {
9343 global showrefstop reflist headids tagids otherrefids
9345 if {![winfo exists $showrefstop]} return
9346 set l [lindex [split [$w index "@$x,$y"] "."] 0]
9347 set ref [lindex $reflist [expr {$l-1}]]
9348 set n [lindex $ref 0]
9349 switch -- [lindex $ref 1] {
9350 "H" {selbyid $headids($n)}
9351 "T" {selbyid $tagids($n)}
9352 "o" {selbyid $otherrefids($n)}
9354 $showrefstop.list tag add highlight $l.0 "$l.0 lineend"
9357 proc unsel_reflist {} {
9360 if {![info exists showrefstop] || ![winfo exists $showrefstop]} return
9361 $showrefstop.list tag remove highlight 0.0 end
9364 proc reflistfilter_change {n1 n2 op} {
9365 global reflistfilter
9367 after cancel refill_reflist
9368 after 200 refill_reflist
9371 proc refill_reflist {} {
9372 global reflist reflistfilter showrefstop headids tagids otherrefids
9375 if {![info exists showrefstop] || ![winfo exists $showrefstop]} return
9377 foreach n [array names headids] {
9378 if {[string match $reflistfilter $n]} {
9379 if {[commitinview $headids($n) $curview]} {
9380 lappend refs [list $n H]
9382 interestedin $headids($n) {run refill_reflist}
9386 foreach n [array names tagids] {
9387 if {[string match $reflistfilter $n]} {
9388 if {[commitinview $tagids($n) $curview]} {
9389 lappend refs [list $n T]
9391 interestedin $tagids($n) {run refill_reflist}
9395 foreach n [array names otherrefids] {
9396 if {[string match $reflistfilter $n]} {
9397 if {[commitinview $otherrefids($n) $curview]} {
9398 lappend refs [list $n o]
9400 interestedin $otherrefids($n) {run refill_reflist}
9404 set refs [lsort -index 0 $refs]
9405 if {$refs eq $reflist} return
9407 # Update the contents of $showrefstop.list according to the
9408 # differences between $reflist (old) and $refs (new)
9409 $showrefstop.list conf -state normal
9410 $showrefstop.list insert end "\n"
9413 while {$i < [llength $reflist] || $j < [llength $refs]} {
9414 if {$i < [llength $reflist]} {
9415 if {$j < [llength $refs]} {
9416 set cmp [string compare [lindex $reflist $i 0] \
9417 [lindex $refs $j 0]]
9419 set cmp [string compare [lindex $reflist $i 1] \
9420 [lindex $refs $j 1]]
9430 $showrefstop.list delete "[expr {$j+1}].0" "[expr {$j+2}].0"
9438 set l [expr {$j + 1}]
9439 $showrefstop.list image create $l.0 -align baseline \
9440 -image reficon-[lindex $refs $j 1] -padx 2
9441 $showrefstop.list insert $l.1 "[lindex $refs $j 0]\n"
9447 # delete last newline
9448 $showrefstop.list delete end-2c end-1c
9449 $showrefstop.list conf -state disabled
9452 # Stuff for finding nearby tags
9453 proc getallcommits {} {
9454 global allcommits nextarc seeds allccache allcwait cachedarcs allcupdate
9455 global idheads idtags idotherrefs allparents tagobjid
9457 if {![info exists allcommits]} {
9463 set allccache [file join [gitdir] "gitk.cache"]
9465 set f [open $allccache r]
9474 set cmd [list | git rev-list --parents]
9475 set allcupdate [expr {$seeds ne {}}]
9479 set refs [concat [array names idheads] [array names idtags] \
9480 [array names idotherrefs]]
9483 foreach name [array names tagobjid] {
9484 lappend tagobjs $tagobjid($name)
9486 foreach id [lsort -unique $refs] {
9487 if {![info exists allparents($id)] &&
9488 [lsearch -exact $tagobjs $id] < 0} {
9499 set cmd [limit_arg_length [concat $cmd $ids]]
9500 set fd [open $cmd r]
9501 fconfigure $fd -blocking 0
9504 filerun $fd [list getallclines $fd]
9510 # The maximum command line length for the CreateProcess function is 32767 characters, see
9511 # http://blogs.msdn.com/oldnewthing/archive/2003/12/10/56028.aspx
9512 # Be a little conservative in case Tcl adds some more stuff to the command line we do not
9513 # know about and truncate the command line at a SHA1-boundary below 32000 characters.
9514 proc limit_arg_length {cmd} {
9515 if {[tk windowingsystem] == "win32" &&
9516 [string length $cmd] > 32000} {
9517 set ndx [string last " " $cmd 32000]
9519 return [string range $cmd 0 $ndx]
9525 # Since most commits have 1 parent and 1 child, we group strings of
9526 # such commits into "arcs" joining branch/merge points (BMPs), which
9527 # are commits that either don't have 1 parent or don't have 1 child.
9529 # arcnos(id) - incoming arcs for BMP, arc we're on for other nodes
9530 # arcout(id) - outgoing arcs for BMP
9531 # arcids(a) - list of IDs on arc including end but not start
9532 # arcstart(a) - BMP ID at start of arc
9533 # arcend(a) - BMP ID at end of arc
9534 # growing(a) - arc a is still growing
9535 # arctags(a) - IDs out of arcids (excluding end) that have tags
9536 # archeads(a) - IDs out of arcids (excluding end) that have heads
9537 # The start of an arc is at the descendent end, so "incoming" means
9538 # coming from descendents, and "outgoing" means going towards ancestors.
9540 proc getallclines {fd} {
9541 global allparents allchildren idtags idheads nextarc
9542 global arcnos arcids arctags arcout arcend arcstart archeads growing
9543 global seeds allcommits cachedarcs allcupdate
9546 while {[incr nid] <= 1000 && [gets $fd line] >= 0} {
9547 set id [lindex $line 0]
9548 if {[info exists allparents($id)]} {
9553 set olds [lrange $line 1 end]
9554 set allparents($id) $olds
9555 if {![info exists allchildren($id)]} {
9556 set allchildren($id) {}
9561 if {[llength $olds] == 1 && [llength $a] == 1} {
9562 lappend arcids($a) $id
9563 if {[info exists idtags($id)]} {
9564 lappend arctags($a) $id
9566 if {[info exists idheads($id)]} {
9567 lappend archeads($a) $id
9569 if {[info exists allparents($olds)]} {
9570 # seen parent already
9571 if {![info exists arcout($olds)]} {
9574 lappend arcids($a) $olds
9575 set arcend($a) $olds
9578 lappend allchildren($olds) $id
9579 lappend arcnos($olds) $a
9583 foreach a $arcnos($id) {
9584 lappend arcids($a) $id
9591 lappend allchildren($p) $id
9592 set a [incr nextarc]
9593 set arcstart($a) $id
9600 if {[info exists allparents($p)]} {
9601 # seen it already, may need to make a new branch
9602 if {![info exists arcout($p)]} {
9605 lappend arcids($a) $p
9609 lappend arcnos($p) $a
9614 global cached_dheads cached_dtags cached_atags
9615 catch {unset cached_dheads}
9616 catch {unset cached_dtags}
9617 catch {unset cached_atags}
9620 return [expr {$nid >= 1000? 2: 1}]
9624 fconfigure $fd -blocking 1
9627 # got an error reading the list of commits
9628 # if we were updating, try rereading the whole thing again
9634 error_popup "[mc "Error reading commit topology information;\
9635 branch and preceding/following tag information\
9636 will be incomplete."]\n($err)"
9639 if {[incr allcommits -1] == 0} {
9649 proc recalcarc {a} {
9650 global arctags archeads arcids idtags idheads
9654 foreach id [lrange $arcids($a) 0 end-1] {
9655 if {[info exists idtags($id)]} {
9658 if {[info exists idheads($id)]} {
9663 set archeads($a) $ah
9667 global arcnos arcids nextarc arctags archeads idtags idheads
9668 global arcstart arcend arcout allparents growing
9671 if {[llength $a] != 1} {
9672 puts "oops splitarc called but [llength $a] arcs already"
9676 set i [lsearch -exact $arcids($a) $p]
9678 puts "oops splitarc $p not in arc $a"
9681 set na [incr nextarc]
9682 if {[info exists arcend($a)]} {
9683 set arcend($na) $arcend($a)
9685 set l [lindex $allparents([lindex $arcids($a) end]) 0]
9686 set j [lsearch -exact $arcnos($l) $a]
9687 set arcnos($l) [lreplace $arcnos($l) $j $j $na]
9689 set tail [lrange $arcids($a) [expr {$i+1}] end]
9690 set arcids($a) [lrange $arcids($a) 0 $i]
9692 set arcstart($na) $p
9694 set arcids($na) $tail
9695 if {[info exists growing($a)]} {
9701 if {[llength $arcnos($id)] == 1} {
9704 set j [lsearch -exact $arcnos($id) $a]
9705 set arcnos($id) [lreplace $arcnos($id) $j $j $na]
9709 # reconstruct tags and heads lists
9710 if {$arctags($a) ne {} || $archeads($a) ne {}} {
9715 set archeads($na) {}
9719 # Update things for a new commit added that is a child of one
9720 # existing commit. Used when cherry-picking.
9721 proc addnewchild {id p} {
9722 global allparents allchildren idtags nextarc
9723 global arcnos arcids arctags arcout arcend arcstart archeads growing
9724 global seeds allcommits
9726 if {![info exists allcommits] || ![info exists arcnos($p)]} return
9727 set allparents($id) [list $p]
9728 set allchildren($id) {}
9731 lappend allchildren($p) $id
9732 set a [incr nextarc]
9733 set arcstart($a) $id
9736 set arcids($a) [list $p]
9738 if {![info exists arcout($p)]} {
9741 lappend arcnos($p) $a
9742 set arcout($id) [list $a]
9745 # This implements a cache for the topology information.
9746 # The cache saves, for each arc, the start and end of the arc,
9747 # the ids on the arc, and the outgoing arcs from the end.
9748 proc readcache {f} {
9749 global arcnos arcids arcout arcstart arcend arctags archeads nextarc
9750 global idtags idheads allparents cachedarcs possible_seeds seeds growing
9755 if {$lim - $a > 500} {
9756 set lim [expr {$a + 500}]
9760 # finish reading the cache and setting up arctags, etc.
9762 if {$line ne "1"} {error "bad final version"}
9764 foreach id [array names idtags] {
9765 if {[info exists arcnos($id)] && [llength $arcnos($id)] == 1 &&
9766 [llength $allparents($id)] == 1} {
9767 set a [lindex $arcnos($id) 0]
9768 if {$arctags($a) eq {}} {
9773 foreach id [array names idheads] {
9774 if {[info exists arcnos($id)] && [llength $arcnos($id)] == 1 &&
9775 [llength $allparents($id)] == 1} {
9776 set a [lindex $arcnos($id) 0]
9777 if {$archeads($a) eq {}} {
9782 foreach id [lsort -unique $possible_seeds] {
9783 if {$arcnos($id) eq {}} {
9789 while {[incr a] <= $lim} {
9791 if {[llength $line] != 3} {error "bad line"}
9792 set s [lindex $line 0]
9794 lappend arcout($s) $a
9795 if {![info exists arcnos($s)]} {
9796 lappend possible_seeds $s
9799 set e [lindex $line 1]
9804 if {![info exists arcout($e)]} {
9808 set arcids($a) [lindex $line 2]
9809 foreach id $arcids($a) {
9810 lappend allparents($s) $id
9812 lappend arcnos($id) $a
9814 if {![info exists allparents($s)]} {
9815 set allparents($s) {}
9820 set nextarc [expr {$a - 1}]
9833 global nextarc cachedarcs possible_seeds
9837 if {[llength $line] != 2 || [lindex $line 0] ne "1"} {error "bad version"}
9838 # make sure it's an integer
9839 set cachedarcs [expr {int([lindex $line 1])}]
9840 if {$cachedarcs < 0} {error "bad number of arcs"}
9842 set possible_seeds {}
9850 proc dropcache {err} {
9851 global allcwait nextarc cachedarcs seeds
9853 #puts "dropping cache ($err)"
9854 foreach v {arcnos arcout arcids arcstart arcend growing \
9855 arctags archeads allparents allchildren} {
9866 proc writecache {f} {
9867 global cachearc cachedarcs allccache
9868 global arcstart arcend arcnos arcids arcout
9872 if {$lim - $a > 1000} {
9873 set lim [expr {$a + 1000}]
9876 while {[incr a] <= $lim} {
9877 if {[info exists arcend($a)]} {
9878 puts $f [list $arcstart($a) $arcend($a) $arcids($a)]
9880 puts $f [list $arcstart($a) {} $arcids($a)]
9885 catch {file delete $allccache}
9886 #puts "writing cache failed ($err)"
9889 set cachearc [expr {$a - 1}]
9890 if {$a > $cachedarcs} {
9899 global nextarc cachedarcs cachearc allccache
9901 if {$nextarc == $cachedarcs} return
9903 set cachedarcs $nextarc
9905 set f [open $allccache w]
9906 puts $f [list 1 $cachedarcs]
9911 # Returns 1 if a is an ancestor of b, -1 if b is an ancestor of a,
9912 # or 0 if neither is true.
9913 proc anc_or_desc {a b} {
9914 global arcout arcstart arcend arcnos cached_isanc
9916 if {$arcnos($a) eq $arcnos($b)} {
9917 # Both are on the same arc(s); either both are the same BMP,
9918 # or if one is not a BMP, the other is also not a BMP or is
9919 # the BMP at end of the arc (and it only has 1 incoming arc).
9920 # Or both can be BMPs with no incoming arcs.
9921 if {$a eq $b || $arcnos($a) eq {}} {
9924 # assert {[llength $arcnos($a)] == 1}
9925 set arc [lindex $arcnos($a) 0]
9926 set i [lsearch -exact $arcids($arc) $a]
9927 set j [lsearch -exact $arcids($arc) $b]
9928 if {$i < 0 || $i > $j} {
9935 if {![info exists arcout($a)]} {
9936 set arc [lindex $arcnos($a) 0]
9937 if {[info exists arcend($arc)]} {
9938 set aend $arcend($arc)
9942 set a $arcstart($arc)
9946 if {![info exists arcout($b)]} {
9947 set arc [lindex $arcnos($b) 0]
9948 if {[info exists arcend($arc)]} {
9949 set bend $arcend($arc)
9953 set b $arcstart($arc)
9963 if {[info exists cached_isanc($a,$bend)]} {
9964 if {$cached_isanc($a,$bend)} {
9968 if {[info exists cached_isanc($b,$aend)]} {
9969 if {$cached_isanc($b,$aend)} {
9972 if {[info exists cached_isanc($a,$bend)]} {
9977 set todo [list $a $b]
9980 for {set i 0} {$i < [llength $todo]} {incr i} {
9981 set x [lindex $todo $i]
9982 if {$anc($x) eq {}} {
9985 foreach arc $arcnos($x) {
9986 set xd $arcstart($arc)
9988 set cached_isanc($a,$bend) 1
9989 set cached_isanc($b,$aend) 0
9991 } elseif {$xd eq $aend} {
9992 set cached_isanc($b,$aend) 1
9993 set cached_isanc($a,$bend) 0
9996 if {![info exists anc($xd)]} {
9997 set anc($xd) $anc($x)
9999 } elseif {$anc($xd) ne $anc($x)} {
10004 set cached_isanc($a,$bend) 0
10005 set cached_isanc($b,$aend) 0
10009 # This identifies whether $desc has an ancestor that is
10010 # a growing tip of the graph and which is not an ancestor of $anc
10011 # and returns 0 if so and 1 if not.
10012 # If we subsequently discover a tag on such a growing tip, and that
10013 # turns out to be a descendent of $anc (which it could, since we
10014 # don't necessarily see children before parents), then $desc
10015 # isn't a good choice to display as a descendent tag of
10016 # $anc (since it is the descendent of another tag which is
10017 # a descendent of $anc). Similarly, $anc isn't a good choice to
10018 # display as a ancestor tag of $desc.
10020 proc is_certain {desc anc} {
10021 global arcnos arcout arcstart arcend growing problems
10024 if {[llength $arcnos($anc)] == 1} {
10025 # tags on the same arc are certain
10026 if {$arcnos($desc) eq $arcnos($anc)} {
10029 if {![info exists arcout($anc)]} {
10030 # if $anc is partway along an arc, use the start of the arc instead
10031 set a [lindex $arcnos($anc) 0]
10032 set anc $arcstart($a)
10035 if {[llength $arcnos($desc)] > 1 || [info exists arcout($desc)]} {
10038 set a [lindex $arcnos($desc) 0]
10044 set anclist [list $x]
10048 for {set i 0} {$i < [llength $anclist] && ($nnh > 0 || $ngrowanc > 0)} {incr i} {
10049 set x [lindex $anclist $i]
10054 foreach a $arcout($x) {
10055 if {[info exists growing($a)]} {
10056 if {![info exists growanc($x)] && $dl($x)} {
10062 if {[info exists dl($y)]} {
10066 if {![info exists done($y)]} {
10069 if {[info exists growanc($x)]} {
10073 for {set k 0} {$k < [llength $xl]} {incr k} {
10074 set z [lindex $xl $k]
10075 foreach c $arcout($z) {
10076 if {[info exists arcend($c)]} {
10078 if {[info exists dl($v)] && $dl($v)} {
10080 if {![info exists done($v)]} {
10083 if {[info exists growanc($v)]} {
10093 } elseif {$y eq $anc || !$dl($x)} {
10104 foreach x [array names growanc] {
10113 proc validate_arctags {a} {
10114 global arctags idtags
10117 set na $arctags($a)
10118 foreach id $arctags($a) {
10120 if {![info exists idtags($id)]} {
10121 set na [lreplace $na $i $i]
10125 set arctags($a) $na
10128 proc validate_archeads {a} {
10129 global archeads idheads
10132 set na $archeads($a)
10133 foreach id $archeads($a) {
10135 if {![info exists idheads($id)]} {
10136 set na [lreplace $na $i $i]
10140 set archeads($a) $na
10143 # Return the list of IDs that have tags that are descendents of id,
10144 # ignoring IDs that are descendents of IDs already reported.
10145 proc desctags {id} {
10146 global arcnos arcstart arcids arctags idtags allparents
10147 global growing cached_dtags
10149 if {![info exists allparents($id)]} {
10152 set t1 [clock clicks -milliseconds]
10154 if {[llength $arcnos($id)] == 1 && [llength $allparents($id)] == 1} {
10155 # part-way along an arc; check that arc first
10156 set a [lindex $arcnos($id) 0]
10157 if {$arctags($a) ne {}} {
10158 validate_arctags $a
10159 set i [lsearch -exact $arcids($a) $id]
10161 foreach t $arctags($a) {
10162 set j [lsearch -exact $arcids($a) $t]
10163 if {$j >= $i} break
10170 set id $arcstart($a)
10171 if {[info exists idtags($id)]} {
10175 if {[info exists cached_dtags($id)]} {
10176 return $cached_dtags($id)
10180 set todo [list $id]
10183 for {set i 0} {$i < [llength $todo] && $nc > 0} {incr i} {
10184 set id [lindex $todo $i]
10186 set ta [info exists hastaggedancestor($id)]
10190 # ignore tags on starting node
10191 if {!$ta && $i > 0} {
10192 if {[info exists idtags($id)]} {
10193 set tagloc($id) $id
10195 } elseif {[info exists cached_dtags($id)]} {
10196 set tagloc($id) $cached_dtags($id)
10200 foreach a $arcnos($id) {
10201 set d $arcstart($a)
10202 if {!$ta && $arctags($a) ne {}} {
10203 validate_arctags $a
10204 if {$arctags($a) ne {}} {
10205 lappend tagloc($id) [lindex $arctags($a) end]
10208 if {$ta || $arctags($a) ne {}} {
10209 set tomark [list $d]
10210 for {set j 0} {$j < [llength $tomark]} {incr j} {
10211 set dd [lindex $tomark $j]
10212 if {![info exists hastaggedancestor($dd)]} {
10213 if {[info exists done($dd)]} {
10214 foreach b $arcnos($dd) {
10215 lappend tomark $arcstart($b)
10217 if {[info exists tagloc($dd)]} {
10220 } elseif {[info exists queued($dd)]} {
10223 set hastaggedancestor($dd) 1
10227 if {![info exists queued($d)]} {
10230 if {![info exists hastaggedancestor($d)]} {
10237 foreach id [array names tagloc] {
10238 if {![info exists hastaggedancestor($id)]} {
10239 foreach t $tagloc($id) {
10240 if {[lsearch -exact $tags $t] < 0} {
10246 set t2 [clock clicks -milliseconds]
10249 # remove tags that are descendents of other tags
10250 for {set i 0} {$i < [llength $tags]} {incr i} {
10251 set a [lindex $tags $i]
10252 for {set j 0} {$j < $i} {incr j} {
10253 set b [lindex $tags $j]
10254 set r [anc_or_desc $a $b]
10256 set tags [lreplace $tags $j $j]
10259 } elseif {$r == -1} {
10260 set tags [lreplace $tags $i $i]
10267 if {[array names growing] ne {}} {
10268 # graph isn't finished, need to check if any tag could get
10269 # eclipsed by another tag coming later. Simply ignore any
10270 # tags that could later get eclipsed.
10273 if {[is_certain $t $origid]} {
10277 if {$tags eq $ctags} {
10278 set cached_dtags($origid) $tags
10283 set cached_dtags($origid) $tags
10285 set t3 [clock clicks -milliseconds]
10286 if {0 && $t3 - $t1 >= 100} {
10287 puts "iterating descendents ($loopix/[llength $todo] nodes) took\
10288 [expr {$t2-$t1}]+[expr {$t3-$t2}]ms, $nc candidates left"
10293 proc anctags {id} {
10294 global arcnos arcids arcout arcend arctags idtags allparents
10295 global growing cached_atags
10297 if {![info exists allparents($id)]} {
10300 set t1 [clock clicks -milliseconds]
10302 if {[llength $arcnos($id)] == 1 && [llength $allparents($id)] == 1} {
10303 # part-way along an arc; check that arc first
10304 set a [lindex $arcnos($id) 0]
10305 if {$arctags($a) ne {}} {
10306 validate_arctags $a
10307 set i [lsearch -exact $arcids($a) $id]
10308 foreach t $arctags($a) {
10309 set j [lsearch -exact $arcids($a) $t]
10315 if {![info exists arcend($a)]} {
10319 if {[info exists idtags($id)]} {
10323 if {[info exists cached_atags($id)]} {
10324 return $cached_atags($id)
10328 set todo [list $id]
10332 for {set i 0} {$i < [llength $todo] && $nc > 0} {incr i} {
10333 set id [lindex $todo $i]
10335 set td [info exists hastaggeddescendent($id)]
10339 # ignore tags on starting node
10340 if {!$td && $i > 0} {
10341 if {[info exists idtags($id)]} {
10342 set tagloc($id) $id
10344 } elseif {[info exists cached_atags($id)]} {
10345 set tagloc($id) $cached_atags($id)
10349 foreach a $arcout($id) {
10350 if {!$td && $arctags($a) ne {}} {
10351 validate_arctags $a
10352 if {$arctags($a) ne {}} {
10353 lappend tagloc($id) [lindex $arctags($a) 0]
10356 if {![info exists arcend($a)]} continue
10358 if {$td || $arctags($a) ne {}} {
10359 set tomark [list $d]
10360 for {set j 0} {$j < [llength $tomark]} {incr j} {
10361 set dd [lindex $tomark $j]
10362 if {![info exists hastaggeddescendent($dd)]} {
10363 if {[info exists done($dd)]} {
10364 foreach b $arcout($dd) {
10365 if {[info exists arcend($b)]} {
10366 lappend tomark $arcend($b)
10369 if {[info exists tagloc($dd)]} {
10372 } elseif {[info exists queued($dd)]} {
10375 set hastaggeddescendent($dd) 1
10379 if {![info exists queued($d)]} {
10382 if {![info exists hastaggeddescendent($d)]} {
10388 set t2 [clock clicks -milliseconds]
10391 foreach id [array names tagloc] {
10392 if {![info exists hastaggeddescendent($id)]} {
10393 foreach t $tagloc($id) {
10394 if {[lsearch -exact $tags $t] < 0} {
10401 # remove tags that are ancestors of other tags
10402 for {set i 0} {$i < [llength $tags]} {incr i} {
10403 set a [lindex $tags $i]
10404 for {set j 0} {$j < $i} {incr j} {
10405 set b [lindex $tags $j]
10406 set r [anc_or_desc $a $b]
10408 set tags [lreplace $tags $j $j]
10411 } elseif {$r == 1} {
10412 set tags [lreplace $tags $i $i]
10419 if {[array names growing] ne {}} {
10420 # graph isn't finished, need to check if any tag could get
10421 # eclipsed by another tag coming later. Simply ignore any
10422 # tags that could later get eclipsed.
10425 if {[is_certain $origid $t]} {
10429 if {$tags eq $ctags} {
10430 set cached_atags($origid) $tags
10435 set cached_atags($origid) $tags
10437 set t3 [clock clicks -milliseconds]
10438 if {0 && $t3 - $t1 >= 100} {
10439 puts "iterating ancestors ($loopix/[llength $todo] nodes) took\
10440 [expr {$t2-$t1}]+[expr {$t3-$t2}]ms, $nc candidates left"
10445 # Return the list of IDs that have heads that are descendents of id,
10446 # including id itself if it has a head.
10447 proc descheads {id} {
10448 global arcnos arcstart arcids archeads idheads cached_dheads
10451 if {![info exists allparents($id)]} {
10455 if {[llength $arcnos($id)] == 1 && [llength $allparents($id)] == 1} {
10456 # part-way along an arc; check it first
10457 set a [lindex $arcnos($id) 0]
10458 if {$archeads($a) ne {}} {
10459 validate_archeads $a
10460 set i [lsearch -exact $arcids($a) $id]
10461 foreach t $archeads($a) {
10462 set j [lsearch -exact $arcids($a) $t]
10467 set id $arcstart($a)
10470 set todo [list $id]
10473 for {set i 0} {$i < [llength $todo]} {incr i} {
10474 set id [lindex $todo $i]
10475 if {[info exists cached_dheads($id)]} {
10476 set ret [concat $ret $cached_dheads($id)]
10478 if {[info exists idheads($id)]} {
10481 foreach a $arcnos($id) {
10482 if {$archeads($a) ne {}} {
10483 validate_archeads $a
10484 if {$archeads($a) ne {}} {
10485 set ret [concat $ret $archeads($a)]
10488 set d $arcstart($a)
10489 if {![info exists seen($d)]} {
10496 set ret [lsort -unique $ret]
10497 set cached_dheads($origid) $ret
10498 return [concat $ret $aret]
10501 proc addedtag {id} {
10502 global arcnos arcout cached_dtags cached_atags
10504 if {![info exists arcnos($id)]} return
10505 if {![info exists arcout($id)]} {
10506 recalcarc [lindex $arcnos($id) 0]
10508 catch {unset cached_dtags}
10509 catch {unset cached_atags}
10512 proc addedhead {hid head} {
10513 global arcnos arcout cached_dheads
10515 if {![info exists arcnos($hid)]} return
10516 if {![info exists arcout($hid)]} {
10517 recalcarc [lindex $arcnos($hid) 0]
10519 catch {unset cached_dheads}
10522 proc removedhead {hid head} {
10523 global cached_dheads
10525 catch {unset cached_dheads}
10528 proc movedhead {hid head} {
10529 global arcnos arcout cached_dheads
10531 if {![info exists arcnos($hid)]} return
10532 if {![info exists arcout($hid)]} {
10533 recalcarc [lindex $arcnos($hid) 0]
10535 catch {unset cached_dheads}
10538 proc changedrefs {} {
10539 global cached_dheads cached_dtags cached_atags
10540 global arctags archeads arcnos arcout idheads idtags
10542 foreach id [concat [array names idheads] [array names idtags]] {
10543 if {[info exists arcnos($id)] && ![info exists arcout($id)]} {
10544 set a [lindex $arcnos($id) 0]
10545 if {![info exists donearc($a)]} {
10551 catch {unset cached_dtags}
10552 catch {unset cached_atags}
10553 catch {unset cached_dheads}
10556 proc rereadrefs {} {
10557 global idtags idheads idotherrefs mainheadid
10559 set refids [concat [array names idtags] \
10560 [array names idheads] [array names idotherrefs]]
10561 foreach id $refids {
10562 if {![info exists ref($id)]} {
10563 set ref($id) [listrefs $id]
10566 set oldmainhead $mainheadid
10569 set refids [lsort -unique [concat $refids [array names idtags] \
10570 [array names idheads] [array names idotherrefs]]]
10571 foreach id $refids {
10572 set v [listrefs $id]
10573 if {![info exists ref($id)] || $ref($id) != $v} {
10577 if {$oldmainhead ne $mainheadid} {
10578 redrawtags $oldmainhead
10579 redrawtags $mainheadid
10584 proc listrefs {id} {
10585 global idtags idheads idotherrefs
10588 if {[info exists idtags($id)]} {
10592 if {[info exists idheads($id)]} {
10593 set y $idheads($id)
10596 if {[info exists idotherrefs($id)]} {
10597 set z $idotherrefs($id)
10599 return [list $x $y $z]
10602 proc showtag {tag isnew} {
10603 global ctext tagcontents tagids linknum tagobjid
10606 addtohistory [list showtag $tag 0] savectextpos
10608 $ctext conf -state normal
10612 if {![info exists tagcontents($tag)]} {
10614 set tagcontents($tag) [exec git cat-file tag $tag]
10617 if {[info exists tagcontents($tag)]} {
10618 set text $tagcontents($tag)
10620 set text "[mc "Tag"]: $tag\n[mc "Id"]: $tagids($tag)"
10622 appendwithlinks $text {}
10623 maybe_scroll_ctext 1
10624 $ctext conf -state disabled
10636 if {[info exists gitktmpdir]} {
10637 catch {file delete -force $gitktmpdir}
10641 proc mkfontdisp {font top which} {
10642 global fontattr fontpref $font NS use_ttk
10644 set fontpref($font) [set $font]
10645 ${NS}::button $top.${font}but -text $which \
10646 -command [list choosefont $font $which]
10647 ${NS}::label $top.$font -relief flat -font $font \
10648 -text $fontattr($font,family) -justify left
10649 grid x $top.${font}but $top.$font -sticky w
10652 proc choosefont {font which} {
10653 global fontparam fontlist fonttop fontattr
10656 set fontparam(which) $which
10657 set fontparam(font) $font
10658 set fontparam(family) [font actual $font -family]
10659 set fontparam(size) $fontattr($font,size)
10660 set fontparam(weight) $fontattr($font,weight)
10661 set fontparam(slant) $fontattr($font,slant)
10664 if {![winfo exists $top]} {
10666 eval font config sample [font actual $font]
10668 make_transient $top $prefstop
10669 wm title $top [mc "Gitk font chooser"]
10670 ${NS}::label $top.l -textvariable fontparam(which)
10671 pack $top.l -side top
10672 set fontlist [lsort [font families]]
10673 ${NS}::frame $top.f
10674 listbox $top.f.fam -listvariable fontlist \
10675 -yscrollcommand [list $top.f.sb set]
10676 bind $top.f.fam <<ListboxSelect>> selfontfam
10677 ${NS}::scrollbar $top.f.sb -command [list $top.f.fam yview]
10678 pack $top.f.sb -side right -fill y
10679 pack $top.f.fam -side left -fill both -expand 1
10680 pack $top.f -side top -fill both -expand 1
10681 ${NS}::frame $top.g
10682 spinbox $top.g.size -from 4 -to 40 -width 4 \
10683 -textvariable fontparam(size) \
10684 -validatecommand {string is integer -strict %s}
10685 checkbutton $top.g.bold -padx 5 \
10686 -font {{Times New Roman} 12 bold} -text [mc "B"] -indicatoron 0 \
10687 -variable fontparam(weight) -onvalue bold -offvalue normal
10688 checkbutton $top.g.ital -padx 5 \
10689 -font {{Times New Roman} 12 italic} -text [mc "I"] -indicatoron 0 \
10690 -variable fontparam(slant) -onvalue italic -offvalue roman
10691 pack $top.g.size $top.g.bold $top.g.ital -side left
10692 pack $top.g -side top
10693 canvas $top.c -width 150 -height 50 -border 2 -relief sunk \
10695 $top.c create text 100 25 -anchor center -text $which -font sample \
10696 -fill black -tags text
10697 bind $top.c <Configure> [list centertext $top.c]
10698 pack $top.c -side top -fill x
10699 ${NS}::frame $top.buts
10700 ${NS}::button $top.buts.ok -text [mc "OK"] -command fontok -default active
10701 ${NS}::button $top.buts.can -text [mc "Cancel"] -command fontcan -default normal
10702 bind $top <Key-Return> fontok
10703 bind $top <Key-Escape> fontcan
10704 grid $top.buts.ok $top.buts.can
10705 grid columnconfigure $top.buts 0 -weight 1 -uniform a
10706 grid columnconfigure $top.buts 1 -weight 1 -uniform a
10707 pack $top.buts -side bottom -fill x
10708 trace add variable fontparam write chg_fontparam
10711 $top.c itemconf text -text $which
10713 set i [lsearch -exact $fontlist $fontparam(family)]
10715 $top.f.fam selection set $i
10720 proc centertext {w} {
10721 $w coords text [expr {[winfo width $w] / 2}] [expr {[winfo height $w] / 2}]
10725 global fontparam fontpref prefstop
10727 set f $fontparam(font)
10728 set fontpref($f) [list $fontparam(family) $fontparam(size)]
10729 if {$fontparam(weight) eq "bold"} {
10730 lappend fontpref($f) "bold"
10732 if {$fontparam(slant) eq "italic"} {
10733 lappend fontpref($f) "italic"
10736 $w conf -text $fontparam(family) -font $fontpref($f)
10742 global fonttop fontparam
10744 if {[info exists fonttop]} {
10745 catch {destroy $fonttop}
10746 catch {font delete sample}
10752 if {[package vsatisfies [package provide Tk] 8.6]} {
10753 # In Tk 8.6 we have a native font chooser dialog. Overwrite the above
10754 # function to make use of it.
10755 proc choosefont {font which} {
10756 tk fontchooser configure -title $which -font $font \
10757 -command [list on_choosefont $font $which]
10758 tk fontchooser show
10760 proc on_choosefont {font which newfont} {
10762 puts stderr "$font $newfont"
10763 array set f [font actual $newfont]
10764 set fontparam(which) $which
10765 set fontparam(font) $font
10766 set fontparam(family) $f(-family)
10767 set fontparam(size) $f(-size)
10768 set fontparam(weight) $f(-weight)
10769 set fontparam(slant) $f(-slant)
10774 proc selfontfam {} {
10775 global fonttop fontparam
10777 set i [$fonttop.f.fam curselection]
10779 set fontparam(family) [$fonttop.f.fam get $i]
10783 proc chg_fontparam {v sub op} {
10786 font config sample -$sub $fontparam($sub)
10790 global maxwidth maxgraphpct use_ttk NS
10791 global oldprefs prefstop showneartags showlocalchanges
10792 global uicolor bgcolor fgcolor ctext diffcolors selectbgcolor markbgcolor
10793 global tabstop limitdiffs autoselect autosellen extdifftool perfile_attrs
10794 global hideremotes want_ttk have_ttk
10798 if {[winfo exists $top]} {
10802 foreach v {maxwidth maxgraphpct showneartags showlocalchanges \
10803 limitdiffs tabstop perfile_attrs hideremotes want_ttk} {
10804 set oldprefs($v) [set $v]
10807 wm title $top [mc "Gitk preferences"]
10808 make_transient $top .
10809 ${NS}::label $top.ldisp -text [mc "Commit list display options"]
10810 grid $top.ldisp - -sticky w -pady 10
10811 ${NS}::label $top.spacer -text " "
10812 ${NS}::label $top.maxwidthl -text [mc "Maximum graph width (lines)"]
10813 spinbox $top.maxwidth -from 0 -to 100 -width 4 -textvariable maxwidth
10814 grid $top.spacer $top.maxwidthl $top.maxwidth -sticky w
10815 ${NS}::label $top.maxpctl -text [mc "Maximum graph width (% of pane)"]
10816 spinbox $top.maxpct -from 1 -to 100 -width 4 -textvariable maxgraphpct
10817 grid x $top.maxpctl $top.maxpct -sticky w
10818 ${NS}::checkbutton $top.showlocal -text [mc "Show local changes"] \
10819 -variable showlocalchanges
10820 grid x $top.showlocal -sticky w
10821 ${NS}::checkbutton $top.autoselect -text [mc "Auto-select SHA1 (length)"] \
10822 -variable autoselect
10823 spinbox $top.autosellen -from 1 -to 40 -width 4 -textvariable autosellen
10824 grid x $top.autoselect $top.autosellen -sticky w
10825 ${NS}::checkbutton $top.hideremotes -text [mc "Hide remote refs"] \
10826 -variable hideremotes
10827 grid x $top.hideremotes -sticky w
10829 ${NS}::label $top.ddisp -text [mc "Diff display options"]
10830 grid $top.ddisp - -sticky w -pady 10
10831 ${NS}::label $top.tabstopl -text [mc "Tab spacing"]
10832 spinbox $top.tabstop -from 1 -to 20 -width 4 -textvariable tabstop
10833 grid x $top.tabstopl $top.tabstop -sticky w
10834 ${NS}::checkbutton $top.ntag -text [mc "Display nearby tags"] \
10835 -variable showneartags
10836 grid x $top.ntag -sticky w
10837 ${NS}::checkbutton $top.ldiff -text [mc "Limit diffs to listed paths"] \
10838 -variable limitdiffs
10839 grid x $top.ldiff -sticky w
10840 ${NS}::checkbutton $top.lattr -text [mc "Support per-file encodings"] \
10841 -variable perfile_attrs
10842 grid x $top.lattr -sticky w
10844 ${NS}::entry $top.extdifft -textvariable extdifftool
10845 ${NS}::frame $top.extdifff
10846 ${NS}::label $top.extdifff.l -text [mc "External diff tool" ]
10847 ${NS}::button $top.extdifff.b -text [mc "Choose..."] -command choose_extdiff
10848 pack $top.extdifff.l $top.extdifff.b -side left
10849 pack configure $top.extdifff.l -padx 10
10850 grid x $top.extdifff $top.extdifft -sticky ew
10852 ${NS}::label $top.lgen -text [mc "General options"]
10853 grid $top.lgen - -sticky w -pady 10
10854 ${NS}::checkbutton $top.want_ttk -variable want_ttk \
10855 -text [mc "Use themed widgets"]
10857 ${NS}::label $top.ttk_note -text [mc "(change requires restart)"]
10859 ${NS}::label $top.ttk_note -text [mc "(currently unavailable)"]
10861 grid x $top.want_ttk $top.ttk_note -sticky w
10863 ${NS}::label $top.cdisp -text [mc "Colors: press to choose"]
10864 grid $top.cdisp - -sticky w -pady 10
10865 label $top.ui -padx 40 -relief sunk -background $uicolor
10866 ${NS}::button $top.uibut -text [mc "Interface"] \
10867 -command [list choosecolor uicolor {} $top.ui [mc "interface"] setui]
10868 grid x $top.uibut $top.ui -sticky w
10869 label $top.bg -padx 40 -relief sunk -background $bgcolor
10870 ${NS}::button $top.bgbut -text [mc "Background"] \
10871 -command [list choosecolor bgcolor {} $top.bg [mc "background"] setbg]
10872 grid x $top.bgbut $top.bg -sticky w
10873 label $top.fg -padx 40 -relief sunk -background $fgcolor
10874 ${NS}::button $top.fgbut -text [mc "Foreground"] \
10875 -command [list choosecolor fgcolor {} $top.fg [mc "foreground"] setfg]
10876 grid x $top.fgbut $top.fg -sticky w
10877 label $top.diffold -padx 40 -relief sunk -background [lindex $diffcolors 0]
10878 ${NS}::button $top.diffoldbut -text [mc "Diff: old lines"] \
10879 -command [list choosecolor diffcolors 0 $top.diffold [mc "diff old lines"] \
10880 [list $ctext tag conf d0 -foreground]]
10881 grid x $top.diffoldbut $top.diffold -sticky w
10882 label $top.diffnew -padx 40 -relief sunk -background [lindex $diffcolors 1]
10883 ${NS}::button $top.diffnewbut -text [mc "Diff: new lines"] \
10884 -command [list choosecolor diffcolors 1 $top.diffnew [mc "diff new lines"] \
10885 [list $ctext tag conf dresult -foreground]]
10886 grid x $top.diffnewbut $top.diffnew -sticky w
10887 label $top.hunksep -padx 40 -relief sunk -background [lindex $diffcolors 2]
10888 ${NS}::button $top.hunksepbut -text [mc "Diff: hunk header"] \
10889 -command [list choosecolor diffcolors 2 $top.hunksep \
10890 [mc "diff hunk header"] \
10891 [list $ctext tag conf hunksep -foreground]]
10892 grid x $top.hunksepbut $top.hunksep -sticky w
10893 label $top.markbgsep -padx 40 -relief sunk -background $markbgcolor
10894 ${NS}::button $top.markbgbut -text [mc "Marked line bg"] \
10895 -command [list choosecolor markbgcolor {} $top.markbgsep \
10896 [mc "marked line background"] \
10897 [list $ctext tag conf omark -background]]
10898 grid x $top.markbgbut $top.markbgsep -sticky w
10899 label $top.selbgsep -padx 40 -relief sunk -background $selectbgcolor
10900 ${NS}::button $top.selbgbut -text [mc "Select bg"] \
10901 -command [list choosecolor selectbgcolor {} $top.selbgsep [mc "background"] setselbg]
10902 grid x $top.selbgbut $top.selbgsep -sticky w
10904 ${NS}::label $top.cfont -text [mc "Fonts: press to choose"]
10905 grid $top.cfont - -sticky w -pady 10
10906 mkfontdisp mainfont $top [mc "Main font"]
10907 mkfontdisp textfont $top [mc "Diff display font"]
10908 mkfontdisp uifont $top [mc "User interface font"]
10910 ${NS}::frame $top.buts
10911 ${NS}::button $top.buts.ok -text [mc "OK"] -command prefsok -default active
10912 ${NS}::button $top.buts.can -text [mc "Cancel"] -command prefscan -default normal
10913 bind $top <Key-Return> prefsok
10914 bind $top <Key-Escape> prefscan
10915 grid $top.buts.ok $top.buts.can
10916 grid columnconfigure $top.buts 0 -weight 1 -uniform a
10917 grid columnconfigure $top.buts 1 -weight 1 -uniform a
10918 grid $top.buts - - -pady 10 -sticky ew
10919 grid columnconfigure $top 2 -weight 1
10920 bind $top <Visibility> "focus $top.buts.ok"
10923 proc choose_extdiff {} {
10926 set prog [tk_getOpenFile -title [mc "External diff tool"] -multiple false]
10928 set extdifftool $prog
10932 proc choosecolor {v vi w x cmd} {
10935 set c [tk_chooseColor -initialcolor [lindex [set $v] $vi] \
10936 -title [mc "Gitk: choose color for %s" $x]]
10937 if {$c eq {}} return
10938 $w conf -background $c
10943 proc setselbg {c} {
10944 global bglist cflist
10945 foreach w $bglist {
10946 $w configure -selectbackground $c
10948 $cflist tag configure highlight \
10949 -background [$cflist cget -selectbackground]
10950 allcanvs itemconf secsel -fill $c
10953 # This sets the background color and the color scheme for the whole UI.
10954 # For some reason, tk_setPalette chooses a nasty dark red for selectColor
10955 # if we don't specify one ourselves, which makes the checkbuttons and
10956 # radiobuttons look bad. This chooses white for selectColor if the
10957 # background color is light, or black if it is dark.
10959 if {[tk windowingsystem] eq "win32"} { return }
10960 set bg [winfo rgb . $c]
10962 if {[lindex $bg 0] + 1.5 * [lindex $bg 1] + 0.5 * [lindex $bg 2] > 100000} {
10965 tk_setPalette background $c selectColor $selc
10971 foreach w $bglist {
10972 $w conf -background $c
10979 foreach w $fglist {
10980 $w conf -foreground $c
10982 allcanvs itemconf text -fill $c
10983 $canv itemconf circle -outline $c
10984 $canv itemconf markid -outline $c
10988 global oldprefs prefstop
10990 foreach v {maxwidth maxgraphpct showneartags showlocalchanges \
10991 limitdiffs tabstop perfile_attrs hideremotes want_ttk} {
10993 set $v $oldprefs($v)
10995 catch {destroy $prefstop}
11001 global maxwidth maxgraphpct
11002 global oldprefs prefstop showneartags showlocalchanges
11003 global fontpref mainfont textfont uifont
11004 global limitdiffs treediffs perfile_attrs
11007 catch {destroy $prefstop}
11011 if {$mainfont ne $fontpref(mainfont)} {
11012 set mainfont $fontpref(mainfont)
11013 parsefont mainfont $mainfont
11014 eval font configure mainfont [fontflags mainfont]
11015 eval font configure mainfontbold [fontflags mainfont 1]
11019 if {$textfont ne $fontpref(textfont)} {
11020 set textfont $fontpref(textfont)
11021 parsefont textfont $textfont
11022 eval font configure textfont [fontflags textfont]
11023 eval font configure textfontbold [fontflags textfont 1]
11025 if {$uifont ne $fontpref(uifont)} {
11026 set uifont $fontpref(uifont)
11027 parsefont uifont $uifont
11028 eval font configure uifont [fontflags uifont]
11031 if {$showlocalchanges != $oldprefs(showlocalchanges)} {
11032 if {$showlocalchanges} {
11038 if {$limitdiffs != $oldprefs(limitdiffs) ||
11039 ($perfile_attrs && !$oldprefs(perfile_attrs))} {
11040 # treediffs elements are limited by path;
11041 # won't have encodings cached if perfile_attrs was just turned on
11042 catch {unset treediffs}
11044 if {$fontchanged || $maxwidth != $oldprefs(maxwidth)
11045 || $maxgraphpct != $oldprefs(maxgraphpct)} {
11047 } elseif {$showneartags != $oldprefs(showneartags) ||
11048 $limitdiffs != $oldprefs(limitdiffs)} {
11051 if {$hideremotes != $oldprefs(hideremotes)} {
11056 proc formatdate {d} {
11057 global datetimeformat
11059 set d [clock format $d -format $datetimeformat]
11064 # This list of encoding names and aliases is distilled from
11065 # http://www.iana.org/assignments/character-sets.
11066 # Not all of them are supported by Tcl.
11067 set encoding_aliases {
11068 { ANSI_X3.4-1968 iso-ir-6 ANSI_X3.4-1986 ISO_646.irv:1991 ASCII
11069 ISO646-US US-ASCII us IBM367 cp367 csASCII }
11070 { ISO-10646-UTF-1 csISO10646UTF1 }
11071 { ISO_646.basic:1983 ref csISO646basic1983 }
11072 { INVARIANT csINVARIANT }
11073 { ISO_646.irv:1983 iso-ir-2 irv csISO2IntlRefVersion }
11074 { BS_4730 iso-ir-4 ISO646-GB gb uk csISO4UnitedKingdom }
11075 { NATS-SEFI iso-ir-8-1 csNATSSEFI }
11076 { NATS-SEFI-ADD iso-ir-8-2 csNATSSEFIADD }
11077 { NATS-DANO iso-ir-9-1 csNATSDANO }
11078 { NATS-DANO-ADD iso-ir-9-2 csNATSDANOADD }
11079 { SEN_850200_B iso-ir-10 FI ISO646-FI ISO646-SE se csISO10Swedish }
11080 { SEN_850200_C iso-ir-11 ISO646-SE2 se2 csISO11SwedishForNames }
11081 { KS_C_5601-1987 iso-ir-149 KS_C_5601-1989 KSC_5601 korean csKSC56011987 }
11082 { ISO-2022-KR csISO2022KR }
11084 { ISO-2022-JP csISO2022JP }
11085 { ISO-2022-JP-2 csISO2022JP2 }
11086 { JIS_C6220-1969-jp JIS_C6220-1969 iso-ir-13 katakana x0201-7
11087 csISO13JISC6220jp }
11088 { JIS_C6220-1969-ro iso-ir-14 jp ISO646-JP csISO14JISC6220ro }
11089 { IT iso-ir-15 ISO646-IT csISO15Italian }
11090 { PT iso-ir-16 ISO646-PT csISO16Portuguese }
11091 { ES iso-ir-17 ISO646-ES csISO17Spanish }
11092 { greek7-old iso-ir-18 csISO18Greek7Old }
11093 { latin-greek iso-ir-19 csISO19LatinGreek }
11094 { DIN_66003 iso-ir-21 de ISO646-DE csISO21German }
11095 { NF_Z_62-010_(1973) iso-ir-25 ISO646-FR1 csISO25French }
11096 { Latin-greek-1 iso-ir-27 csISO27LatinGreek1 }
11097 { ISO_5427 iso-ir-37 csISO5427Cyrillic }
11098 { JIS_C6226-1978 iso-ir-42 csISO42JISC62261978 }
11099 { BS_viewdata iso-ir-47 csISO47BSViewdata }
11100 { INIS iso-ir-49 csISO49INIS }
11101 { INIS-8 iso-ir-50 csISO50INIS8 }
11102 { INIS-cyrillic iso-ir-51 csISO51INISCyrillic }
11103 { ISO_5427:1981 iso-ir-54 ISO5427Cyrillic1981 }
11104 { ISO_5428:1980 iso-ir-55 csISO5428Greek }
11105 { GB_1988-80 iso-ir-57 cn ISO646-CN csISO57GB1988 }
11106 { GB_2312-80 iso-ir-58 chinese csISO58GB231280 }
11107 { NS_4551-1 iso-ir-60 ISO646-NO no csISO60DanishNorwegian
11108 csISO60Norwegian1 }
11109 { NS_4551-2 ISO646-NO2 iso-ir-61 no2 csISO61Norwegian2 }
11110 { NF_Z_62-010 iso-ir-69 ISO646-FR fr csISO69French }
11111 { videotex-suppl iso-ir-70 csISO70VideotexSupp1 }
11112 { PT2 iso-ir-84 ISO646-PT2 csISO84Portuguese2 }
11113 { ES2 iso-ir-85 ISO646-ES2 csISO85Spanish2 }
11114 { MSZ_7795.3 iso-ir-86 ISO646-HU hu csISO86Hungarian }
11115 { JIS_C6226-1983 iso-ir-87 x0208 JIS_X0208-1983 csISO87JISX0208 }
11116 { greek7 iso-ir-88 csISO88Greek7 }
11117 { ASMO_449 ISO_9036 arabic7 iso-ir-89 csISO89ASMO449 }
11118 { iso-ir-90 csISO90 }
11119 { JIS_C6229-1984-a iso-ir-91 jp-ocr-a csISO91JISC62291984a }
11120 { JIS_C6229-1984-b iso-ir-92 ISO646-JP-OCR-B jp-ocr-b
11121 csISO92JISC62991984b }
11122 { JIS_C6229-1984-b-add iso-ir-93 jp-ocr-b-add csISO93JIS62291984badd }
11123 { JIS_C6229-1984-hand iso-ir-94 jp-ocr-hand csISO94JIS62291984hand }
11124 { JIS_C6229-1984-hand-add iso-ir-95 jp-ocr-hand-add
11125 csISO95JIS62291984handadd }
11126 { JIS_C6229-1984-kana iso-ir-96 csISO96JISC62291984kana }
11127 { ISO_2033-1983 iso-ir-98 e13b csISO2033 }
11128 { ANSI_X3.110-1983 iso-ir-99 CSA_T500-1983 NAPLPS csISO99NAPLPS }
11129 { ISO_8859-1:1987 iso-ir-100 ISO_8859-1 ISO-8859-1 latin1 l1 IBM819
11130 CP819 csISOLatin1 }
11131 { ISO_8859-2:1987 iso-ir-101 ISO_8859-2 ISO-8859-2 latin2 l2 csISOLatin2 }
11132 { T.61-7bit iso-ir-102 csISO102T617bit }
11133 { T.61-8bit T.61 iso-ir-103 csISO103T618bit }
11134 { ISO_8859-3:1988 iso-ir-109 ISO_8859-3 ISO-8859-3 latin3 l3 csISOLatin3 }
11135 { ISO_8859-4:1988 iso-ir-110 ISO_8859-4 ISO-8859-4 latin4 l4 csISOLatin4 }
11136 { ECMA-cyrillic iso-ir-111 KOI8-E csISO111ECMACyrillic }
11137 { CSA_Z243.4-1985-1 iso-ir-121 ISO646-CA csa7-1 ca csISO121Canadian1 }
11138 { CSA_Z243.4-1985-2 iso-ir-122 ISO646-CA2 csa7-2 csISO122Canadian2 }
11139 { CSA_Z243.4-1985-gr iso-ir-123 csISO123CSAZ24341985gr }
11140 { ISO_8859-6:1987 iso-ir-127 ISO_8859-6 ISO-8859-6 ECMA-114 ASMO-708
11141 arabic csISOLatinArabic }
11142 { ISO_8859-6-E csISO88596E ISO-8859-6-E }
11143 { ISO_8859-6-I csISO88596I ISO-8859-6-I }
11144 { ISO_8859-7:1987 iso-ir-126 ISO_8859-7 ISO-8859-7 ELOT_928 ECMA-118
11145 greek greek8 csISOLatinGreek }
11146 { T.101-G2 iso-ir-128 csISO128T101G2 }
11147 { ISO_8859-8:1988 iso-ir-138 ISO_8859-8 ISO-8859-8 hebrew
11149 { ISO_8859-8-E csISO88598E ISO-8859-8-E }
11150 { ISO_8859-8-I csISO88598I ISO-8859-8-I }
11151 { CSN_369103 iso-ir-139 csISO139CSN369103 }
11152 { JUS_I.B1.002 iso-ir-141 ISO646-YU js yu csISO141JUSIB1002 }
11153 { ISO_6937-2-add iso-ir-142 csISOTextComm }
11154 { IEC_P27-1 iso-ir-143 csISO143IECP271 }
11155 { ISO_8859-5:1988 iso-ir-144 ISO_8859-5 ISO-8859-5 cyrillic
11156 csISOLatinCyrillic }
11157 { JUS_I.B1.003-serb iso-ir-146 serbian csISO146Serbian }
11158 { JUS_I.B1.003-mac macedonian iso-ir-147 csISO147Macedonian }
11159 { ISO_8859-9:1989 iso-ir-148 ISO_8859-9 ISO-8859-9 latin5 l5 csISOLatin5 }
11160 { greek-ccitt iso-ir-150 csISO150 csISO150GreekCCITT }
11161 { NC_NC00-10:81 cuba iso-ir-151 ISO646-CU csISO151Cuba }
11162 { ISO_6937-2-25 iso-ir-152 csISO6937Add }
11163 { GOST_19768-74 ST_SEV_358-88 iso-ir-153 csISO153GOST1976874 }
11164 { ISO_8859-supp iso-ir-154 latin1-2-5 csISO8859Supp }
11165 { ISO_10367-box iso-ir-155 csISO10367Box }
11166 { ISO-8859-10 iso-ir-157 l6 ISO_8859-10:1992 csISOLatin6 latin6 }
11167 { latin-lap lap iso-ir-158 csISO158Lap }
11168 { JIS_X0212-1990 x0212 iso-ir-159 csISO159JISX02121990 }
11169 { DS_2089 DS2089 ISO646-DK dk csISO646Danish }
11172 { JIS_X0201 X0201 csHalfWidthKatakana }
11173 { KSC5636 ISO646-KR csKSC5636 }
11174 { ISO-10646-UCS-2 csUnicode }
11175 { ISO-10646-UCS-4 csUCS4 }
11176 { DEC-MCS dec csDECMCS }
11177 { hp-roman8 roman8 r8 csHPRoman8 }
11178 { macintosh mac csMacintosh }
11179 { IBM037 cp037 ebcdic-cp-us ebcdic-cp-ca ebcdic-cp-wt ebcdic-cp-nl
11181 { IBM038 EBCDIC-INT cp038 csIBM038 }
11182 { IBM273 CP273 csIBM273 }
11183 { IBM274 EBCDIC-BE CP274 csIBM274 }
11184 { IBM275 EBCDIC-BR cp275 csIBM275 }
11185 { IBM277 EBCDIC-CP-DK EBCDIC-CP-NO csIBM277 }
11186 { IBM278 CP278 ebcdic-cp-fi ebcdic-cp-se csIBM278 }
11187 { IBM280 CP280 ebcdic-cp-it csIBM280 }
11188 { IBM281 EBCDIC-JP-E cp281 csIBM281 }
11189 { IBM284 CP284 ebcdic-cp-es csIBM284 }
11190 { IBM285 CP285 ebcdic-cp-gb csIBM285 }
11191 { IBM290 cp290 EBCDIC-JP-kana csIBM290 }
11192 { IBM297 cp297 ebcdic-cp-fr csIBM297 }
11193 { IBM420 cp420 ebcdic-cp-ar1 csIBM420 }
11194 { IBM423 cp423 ebcdic-cp-gr csIBM423 }
11195 { IBM424 cp424 ebcdic-cp-he csIBM424 }
11196 { IBM437 cp437 437 csPC8CodePage437 }
11197 { IBM500 CP500 ebcdic-cp-be ebcdic-cp-ch csIBM500 }
11198 { IBM775 cp775 csPC775Baltic }
11199 { IBM850 cp850 850 csPC850Multilingual }
11200 { IBM851 cp851 851 csIBM851 }
11201 { IBM852 cp852 852 csPCp852 }
11202 { IBM855 cp855 855 csIBM855 }
11203 { IBM857 cp857 857 csIBM857 }
11204 { IBM860 cp860 860 csIBM860 }
11205 { IBM861 cp861 861 cp-is csIBM861 }
11206 { IBM862 cp862 862 csPC862LatinHebrew }
11207 { IBM863 cp863 863 csIBM863 }
11208 { IBM864 cp864 csIBM864 }
11209 { IBM865 cp865 865 csIBM865 }
11210 { IBM866 cp866 866 csIBM866 }
11211 { IBM868 CP868 cp-ar csIBM868 }
11212 { IBM869 cp869 869 cp-gr csIBM869 }
11213 { IBM870 CP870 ebcdic-cp-roece ebcdic-cp-yu csIBM870 }
11214 { IBM871 CP871 ebcdic-cp-is csIBM871 }
11215 { IBM880 cp880 EBCDIC-Cyrillic csIBM880 }
11216 { IBM891 cp891 csIBM891 }
11217 { IBM903 cp903 csIBM903 }
11218 { IBM904 cp904 904 csIBBM904 }
11219 { IBM905 CP905 ebcdic-cp-tr csIBM905 }
11220 { IBM918 CP918 ebcdic-cp-ar2 csIBM918 }
11221 { IBM1026 CP1026 csIBM1026 }
11222 { EBCDIC-AT-DE csIBMEBCDICATDE }
11223 { EBCDIC-AT-DE-A csEBCDICATDEA }
11224 { EBCDIC-CA-FR csEBCDICCAFR }
11225 { EBCDIC-DK-NO csEBCDICDKNO }
11226 { EBCDIC-DK-NO-A csEBCDICDKNOA }
11227 { EBCDIC-FI-SE csEBCDICFISE }
11228 { EBCDIC-FI-SE-A csEBCDICFISEA }
11229 { EBCDIC-FR csEBCDICFR }
11230 { EBCDIC-IT csEBCDICIT }
11231 { EBCDIC-PT csEBCDICPT }
11232 { EBCDIC-ES csEBCDICES }
11233 { EBCDIC-ES-A csEBCDICESA }
11234 { EBCDIC-ES-S csEBCDICESS }
11235 { EBCDIC-UK csEBCDICUK }
11236 { EBCDIC-US csEBCDICUS }
11237 { UNKNOWN-8BIT csUnknown8BiT }
11238 { MNEMONIC csMnemonic }
11240 { VISCII csVISCII }
11243 { IBM00858 CCSID00858 CP00858 PC-Multilingual-850+euro }
11244 { IBM00924 CCSID00924 CP00924 ebcdic-Latin9--euro }
11245 { IBM01140 CCSID01140 CP01140 ebcdic-us-37+euro }
11246 { IBM01141 CCSID01141 CP01141 ebcdic-de-273+euro }
11247 { IBM01142 CCSID01142 CP01142 ebcdic-dk-277+euro ebcdic-no-277+euro }
11248 { IBM01143 CCSID01143 CP01143 ebcdic-fi-278+euro ebcdic-se-278+euro }
11249 { IBM01144 CCSID01144 CP01144 ebcdic-it-280+euro }
11250 { IBM01145 CCSID01145 CP01145 ebcdic-es-284+euro }
11251 { IBM01146 CCSID01146 CP01146 ebcdic-gb-285+euro }
11252 { IBM01147 CCSID01147 CP01147 ebcdic-fr-297+euro }
11253 { IBM01148 CCSID01148 CP01148 ebcdic-international-500+euro }
11254 { IBM01149 CCSID01149 CP01149 ebcdic-is-871+euro }
11255 { IBM1047 IBM-1047 }
11256 { PTCP154 csPTCP154 PT154 CP154 Cyrillic-Asian }
11257 { Amiga-1251 Ami1251 Amiga1251 Ami-1251 }
11258 { UNICODE-1-1 csUnicode11 }
11259 { CESU-8 csCESU-8 }
11260 { BOCU-1 csBOCU-1 }
11261 { UNICODE-1-1-UTF-7 csUnicode11UTF7 }
11262 { ISO-8859-14 iso-ir-199 ISO_8859-14:1998 ISO_8859-14 latin8 iso-celtic
11264 { ISO-8859-15 ISO_8859-15 Latin-9 }
11265 { ISO-8859-16 iso-ir-226 ISO_8859-16:2001 ISO_8859-16 latin10 l10 }
11266 { GBK CP936 MS936 windows-936 }
11267 { JIS_Encoding csJISEncoding }
11268 { Shift_JIS MS_Kanji csShiftJIS ShiftJIS Shift-JIS }
11269 { Extended_UNIX_Code_Packed_Format_for_Japanese csEUCPkdFmtJapanese
11271 { Extended_UNIX_Code_Fixed_Width_for_Japanese csEUCFixWidJapanese }
11272 { ISO-10646-UCS-Basic csUnicodeASCII }
11273 { ISO-10646-Unicode-Latin1 csUnicodeLatin1 ISO-10646 }
11274 { ISO-Unicode-IBM-1261 csUnicodeIBM1261 }
11275 { ISO-Unicode-IBM-1268 csUnicodeIBM1268 }
11276 { ISO-Unicode-IBM-1276 csUnicodeIBM1276 }
11277 { ISO-Unicode-IBM-1264 csUnicodeIBM1264 }
11278 { ISO-Unicode-IBM-1265 csUnicodeIBM1265 }
11279 { ISO-8859-1-Windows-3.0-Latin-1 csWindows30Latin1 }
11280 { ISO-8859-1-Windows-3.1-Latin-1 csWindows31Latin1 }
11281 { ISO-8859-2-Windows-Latin-2 csWindows31Latin2 }
11282 { ISO-8859-9-Windows-Latin-5 csWindows31Latin5 }
11283 { Adobe-Standard-Encoding csAdobeStandardEncoding }
11284 { Ventura-US csVenturaUS }
11285 { Ventura-International csVenturaInternational }
11286 { PC8-Danish-Norwegian csPC8DanishNorwegian }
11287 { PC8-Turkish csPC8Turkish }
11288 { IBM-Symbols csIBMSymbols }
11289 { IBM-Thai csIBMThai }
11290 { HP-Legal csHPLegal }
11291 { HP-Pi-font csHPPiFont }
11292 { HP-Math8 csHPMath8 }
11293 { Adobe-Symbol-Encoding csHPPSMath }
11294 { HP-DeskTop csHPDesktop }
11295 { Ventura-Math csVenturaMath }
11296 { Microsoft-Publishing csMicrosoftPublishing }
11297 { Windows-31J csWindows31J }
11298 { GB2312 csGB2312 }
11302 proc tcl_encoding {enc} {
11303 global encoding_aliases tcl_encoding_cache
11304 if {[info exists tcl_encoding_cache($enc)]} {
11305 return $tcl_encoding_cache($enc)
11307 set names [encoding names]
11308 set lcnames [string tolower $names]
11309 set enc [string tolower $enc]
11310 set i [lsearch -exact $lcnames $enc]
11312 # look for "isonnn" instead of "iso-nnn" or "iso_nnn"
11313 if {[regsub {^(iso|cp|ibm|jis)[-_]} $enc {\1} encx]} {
11314 set i [lsearch -exact $lcnames $encx]
11318 foreach l $encoding_aliases {
11319 set ll [string tolower $l]
11320 if {[lsearch -exact $ll $enc] < 0} continue
11321 # look through the aliases for one that tcl knows about
11323 set i [lsearch -exact $lcnames $e]
11325 if {[regsub {^(iso|cp|ibm|jis)[-_]} $e {\1} ex]} {
11326 set i [lsearch -exact $lcnames $ex]
11336 set tclenc [lindex $names $i]
11338 set tcl_encoding_cache($enc) $tclenc
11342 proc gitattr {path attr default} {
11343 global path_attr_cache
11344 if {[info exists path_attr_cache($attr,$path)]} {
11345 set r $path_attr_cache($attr,$path)
11347 set r "unspecified"
11348 if {![catch {set line [exec git check-attr $attr -- $path]}]} {
11349 regexp "(.*): $attr: (.*)" $line m f r
11351 set path_attr_cache($attr,$path) $r
11353 if {$r eq "unspecified"} {
11359 proc cache_gitattr {attr pathlist} {
11360 global path_attr_cache
11362 foreach path $pathlist {
11363 if {![info exists path_attr_cache($attr,$path)]} {
11364 lappend newlist $path
11368 if {[tk windowingsystem] == "win32"} {
11369 # windows has a 32k limit on the arguments to a command...
11372 while {$newlist ne {}} {
11373 set head [lrange $newlist 0 [expr {$lim - 1}]]
11374 set newlist [lrange $newlist $lim end]
11375 if {![catch {set rlist [eval exec git check-attr $attr -- $head]}]} {
11376 foreach row [split $rlist "\n"] {
11377 if {[regexp "(.*): $attr: (.*)" $row m path value]} {
11378 if {[string index $path 0] eq "\""} {
11379 set path [encoding convertfrom [lindex $path 0]]
11381 set path_attr_cache($attr,$path) $value
11388 proc get_path_encoding {path} {
11389 global gui_encoding perfile_attrs
11390 set tcl_enc $gui_encoding
11391 if {$path ne {} && $perfile_attrs} {
11392 set enc2 [tcl_encoding [gitattr $path encoding $tcl_enc]]
11400 # First check that Tcl/Tk is recent enough
11401 if {[catch {package require Tk 8.4} err]} {
11402 show_error {} . "Sorry, gitk cannot run with this version of Tcl/Tk.\n\
11403 Gitk requires at least Tcl/Tk 8.4." list
11408 set wrcomcmd "git diff-tree --stdin -p --pretty"
11412 set gitencoding [exec git config --get i18n.commitencoding]
11415 set gitencoding [exec git config --get i18n.logoutputencoding]
11417 if {$gitencoding == ""} {
11418 set gitencoding "utf-8"
11420 set tclencoding [tcl_encoding $gitencoding]
11421 if {$tclencoding == {}} {
11422 puts stderr "Warning: encoding $gitencoding is not supported by Tcl/Tk"
11425 set gui_encoding [encoding system]
11427 set enc [exec git config --get gui.encoding]
11429 set tclenc [tcl_encoding $enc]
11430 if {$tclenc ne {}} {
11431 set gui_encoding $tclenc
11433 puts stderr "Warning: encoding $enc is not supported by Tcl/Tk"
11438 if {[tk windowingsystem] eq "aqua"} {
11439 set mainfont {{Lucida Grande} 9}
11440 set textfont {Monaco 9}
11441 set uifont {{Lucida Grande} 9 bold}
11443 set mainfont {Helvetica 9}
11444 set textfont {Courier 9}
11445 set uifont {Helvetica 9 bold}
11448 set findmergefiles 0
11456 set cmitmode "patch"
11457 set wrapcomment "none"
11462 set showlocalchanges 1
11464 set datetimeformat "%Y-%m-%d %H:%M:%S"
11467 set perfile_attrs 0
11470 if {[tk windowingsystem] eq "aqua"} {
11471 set extdifftool "opendiff"
11473 set extdifftool "meld"
11476 set colors {green red blue magenta darkgrey brown orange}
11477 if {[tk windowingsystem] eq "win32"} {
11478 set uicolor SystemButtonFace
11479 set bgcolor SystemWindow
11480 set fgcolor SystemButtonText
11481 set selectbgcolor SystemHighlight
11486 set selectbgcolor gray85
11488 set diffcolors {red "#00a000" blue}
11492 set markbgcolor "#e0e0ff"
11494 set circlecolors {white blue gray blue blue}
11496 # button for popping up context menus
11497 if {[tk windowingsystem] eq "aqua"} {
11498 set ctxbut <Button-2>
11500 set ctxbut <Button-3>
11503 ## For msgcat loading, first locate the installation location.
11504 if { [info exists ::env(GITK_MSGSDIR)] } {
11505 ## Msgsdir was manually set in the environment.
11506 set gitk_msgsdir $::env(GITK_MSGSDIR)
11508 ## Let's guess the prefix from argv0.
11509 set gitk_prefix [file dirname [file dirname [file normalize $argv0]]]
11510 set gitk_libdir [file join $gitk_prefix share gitk lib]
11511 set gitk_msgsdir [file join $gitk_libdir msgs]
11515 ## Internationalization (i18n) through msgcat and gettext. See
11516 ## http://www.gnu.org/software/gettext/manual/html_node/Tcl.html
11517 package require msgcat
11518 namespace import ::msgcat::mc
11519 ## And eventually load the actual message catalog
11520 ::msgcat::mcload $gitk_msgsdir
11522 catch {source ~/.gitk}
11524 parsefont mainfont $mainfont
11525 eval font create mainfont [fontflags mainfont]
11526 eval font create mainfontbold [fontflags mainfont 1]
11528 parsefont textfont $textfont
11529 eval font create textfont [fontflags textfont]
11530 eval font create textfontbold [fontflags textfont 1]
11532 parsefont uifont $uifont
11533 eval font create uifont [fontflags uifont]
11539 # check that we can find a .git directory somewhere...
11540 if {[catch {set gitdir [gitdir]}]} {
11541 show_error {} . [mc "Cannot find a git repository here."]
11544 if {![file isdirectory $gitdir]} {
11545 show_error {} . [mc "Cannot find the git directory \"%s\"." $gitdir]
11550 set selectheadid {}
11553 set cmdline_files {}
11555 set revtreeargscmd {}
11556 foreach arg $argv {
11557 switch -glob -- $arg {
11560 set cmdline_files [lrange $argv [expr {$i + 1}] end]
11563 "--select-commit=*" {
11564 set selecthead [string range $arg 16 end]
11567 set revtreeargscmd [string range $arg 10 end]
11570 lappend revtreeargs $arg
11576 if {$selecthead eq "HEAD"} {
11580 if {$i >= [llength $argv] && $revtreeargs ne {}} {
11581 # no -- on command line, but some arguments (other than --argscmd)
11583 set f [eval exec git rev-parse --no-revs --no-flags $revtreeargs]
11584 set cmdline_files [split $f "\n"]
11585 set n [llength $cmdline_files]
11586 set revtreeargs [lrange $revtreeargs 0 end-$n]
11587 # Unfortunately git rev-parse doesn't produce an error when
11588 # something is both a revision and a filename. To be consistent
11589 # with git log and git rev-list, check revtreeargs for filenames.
11590 foreach arg $revtreeargs {
11591 if {[file exists $arg]} {
11592 show_error {} . [mc "Ambiguous argument '%s': both revision\
11593 and filename" $arg]
11598 # unfortunately we get both stdout and stderr in $err,
11599 # so look for "fatal:".
11600 set i [string first "fatal:" $err]
11602 set err [string range $err [expr {$i + 6}] end]
11604 show_error {} . "[mc "Bad arguments to gitk:"]\n$err"
11609 set nullid "0000000000000000000000000000000000000000"
11610 set nullid2 "0000000000000000000000000000000000000001"
11611 set nullfile "/dev/null"
11613 set have_tk85 [expr {[package vcompare $tk_version "8.5"] >= 0}]
11614 if {![info exists have_ttk]} {
11615 set have_ttk [llength [info commands ::ttk::style]]
11617 set use_ttk [expr {$have_ttk && $want_ttk}]
11618 set NS [expr {$use_ttk ? "ttk" : ""}]
11620 regexp {^git version ([\d.]*\d)} [exec git version] _ git_version
11623 if {[package vcompare $git_version "1.6.6.2"] >= 0} {
11624 set show_notes "--show-notes"
11632 set highlight_paths {}
11634 set searchdirn -forwards
11637 set diffelide {0 0}
11638 set markingmatches 0
11639 set linkentercount 0
11640 set need_redisplay 0
11647 set selectedhlview [mc "None"]
11648 set highlight_related [mc "None"]
11649 set highlight_files {}
11650 set viewfiles(0) {}
11653 set viewargscmd(0) {}
11655 set selectedline {}
11663 set isworktree [expr {[exec git rev-parse --is-inside-work-tree] == "true"}]
11667 image create photo gitlogo -width 16 -height 16
11669 image create photo gitlogominus -width 4 -height 2
11670 gitlogominus put #C00000 -to 0 0 4 2
11671 gitlogo copy gitlogominus -to 1 5
11672 gitlogo copy gitlogominus -to 6 5
11673 gitlogo copy gitlogominus -to 11 5
11674 image delete gitlogominus
11676 image create photo gitlogoplus -width 4 -height 4
11677 gitlogoplus put #008000 -to 1 0 3 4
11678 gitlogoplus put #008000 -to 0 1 4 3
11679 gitlogo copy gitlogoplus -to 1 9
11680 gitlogo copy gitlogoplus -to 6 9
11681 gitlogo copy gitlogoplus -to 11 9
11682 image delete gitlogoplus
11684 image create photo gitlogo32 -width 32 -height 32
11685 gitlogo32 copy gitlogo -zoom 2 2
11687 wm iconphoto . -default gitlogo gitlogo32
11689 # wait for the window to become visible
11690 tkwait visibility .
11691 wm title . "[file tail $argv0]: [file tail [pwd]]"
11695 if {$cmdline_files ne {} || $revtreeargs ne {} || $revtreeargscmd ne {}} {
11696 # create a view for the files/dirs specified on the command line
11700 set viewname(1) [mc "Command line"]
11701 set viewfiles(1) $cmdline_files
11702 set viewargs(1) $revtreeargs
11703 set viewargscmd(1) $revtreeargscmd
11707 .bar.view entryconf [mca "Edit view..."] -state normal
11708 .bar.view entryconf [mca "Delete view"] -state normal
11711 if {[info exists permviews]} {
11712 foreach v $permviews {
11715 set viewname($n) [lindex $v 0]
11716 set viewfiles($n) [lindex $v 1]
11717 set viewargs($n) [lindex $v 2]
11718 set viewargscmd($n) [lindex $v 3]
11724 if {[tk windowingsystem] eq "win32"} {
11732 # indent-tabs-mode: t