2 # Tcl ignores the next line -*- tcl -*- \
5 # Copyright © 2005-2011 Paul Mackerras. All rights reserved.
6 # This program is free software; it may be used, copied, modified
7 # and distributed under the terms of the GNU General Public Licence,
8 # either version 2, or (at your option) any later version.
13 return [expr {[exec git rev-parse
--is-bare-repository] == "false" &&
14 [exec git rev-parse
--is-inside-git-dir] == "false"}]
19 set n
[file normalize
$gitdir]
20 if {[string match
"*/.git" $n]} {
21 set n
[string range
$n 0 end-5
]
28 if {[info exists _gitworktree
]} {
31 # v1.7.0 introduced --show-toplevel to return the canonical work-tree
32 if {[catch
{set _gitworktree
[exec git rev-parse
--show-toplevel]}]} {
33 # try to set work tree from environment, core.worktree or use
34 # cdup to obtain a relative path to the top of the worktree. If
35 # run from the top, the ./ prefix ensures normalize expands pwd.
36 if {[catch
{ set _gitworktree
$env(GIT_WORK_TREE
) }]} {
37 catch
{set _gitworktree
[exec git config
--get core.worktree
]}
38 if {$_gitworktree eq
""} {
39 set _gitworktree
[file normalize .
/[exec git rev-parse
--show-cdup]]
46 # A simple scheduler for compute-intensive stuff.
47 # The aim is to make sure that event handlers for GUI actions can
48 # run at least every 50-100 ms. Unfortunately fileevent handlers are
49 # run before X event handlers, so reading from a fast source can
50 # make the GUI completely unresponsive.
52 global isonrunq runq currunq
55 if {[info exists isonrunq
($script)]} return
56 if {$runq eq
{} && ![info exists currunq
]} {
59 lappend runq
[list
{} $script]
60 set isonrunq
($script) 1
63 proc filerun
{fd
script} {
64 fileevent
$fd readable
[list filereadable
$fd $script]
67 proc filereadable
{fd
script} {
70 fileevent
$fd readable
{}
71 if {$runq eq
{} && ![info exists currunq
]} {
74 lappend runq
[list
$fd $script]
80 for {set i
0} {$i < [llength
$runq]} {} {
81 if {[lindex
$runq $i 0] eq
$fd} {
82 set runq
[lreplace
$runq $i $i]
90 global isonrunq runq currunq
92 set tstart
[clock clicks
-milliseconds]
94 while {[llength
$runq] > 0} {
95 set fd
[lindex
$runq 0 0]
96 set script [lindex
$runq 0 1]
97 set currunq
[lindex
$runq 0]
98 set runq
[lrange
$runq 1 end
]
99 set repeat
[eval $script]
101 set t1
[clock clicks
-milliseconds]
102 set t
[expr {$t1 - $t0}]
103 if {$repeat ne
{} && $repeat} {
104 if {$fd eq
{} ||
$repeat == 2} {
105 # script returns 1 if it wants to be readded
106 # file readers return 2 if they could do more straight away
107 lappend runq
[list
$fd $script]
109 fileevent
$fd readable
[list filereadable
$fd $script]
111 } elseif
{$fd eq
{}} {
112 unset isonrunq
($script)
115 if {$t1 - $tstart >= 80} break
122 proc reg_instance
{fd
} {
123 global commfd leftover loginstance
125 set i
[incr loginstance
]
131 proc unmerged_files
{files
} {
134 # find the list of unmerged files
138 set fd
[open
"| git ls-files -u" r
]
140 show_error
{} .
"[mc "Couldn
't get list of unmerged files:"] $err"
143 while {[gets $fd line] >= 0} {
144 set i [string first "\t" $line]
146 set fname [string range $line [expr {$i+1}] end]
147 if {[lsearch -exact $mlist $fname] >= 0} continue
149 if {$files eq {} || [path_filter $files $fname]} {
157 proc parseviewargs {n arglist} {
158 global vdatemode vmergeonly vflags vdflags vrevs vfiltered vorigargs env
159 global worddiff git_version
167 set origargs $arglist
171 foreach arg $arglist {
178 switch -glob -- $arg {
182 # remove from origargs in case we hit an unknown option
183 set origargs [lreplace $origargs $i $i]
187 "--no-renames" - "--full-index" - "--binary" - "--abbrev=*" -
188 "--find-copies-harder" - "-l*" - "--ext-diff" - "--no-ext-diff" -
189 "--src-prefix=*" - "--dst-prefix=*" - "--no-prefix" -
190 "-O*" - "--text" - "--full-diff" - "--ignore-space-at-eol" -
191 "--ignore-space-change" - "-U*" - "--unified=*" {
192 # These request or affect diff output, which we don't want.
193 # Some could be used to set our defaults for diff display.
194 lappend diffargs
$arg
196 "--raw" - "--patch-with-raw" - "--patch-with-stat" -
197 "--name-only" - "--name-status" - "--color" -
198 "--log-size" - "--pretty=*" - "--decorate" - "--abbrev-commit" -
199 "--cc" - "-z" - "--header" - "--parents" - "--boundary" -
200 "--no-color" - "-g" - "--walk-reflogs" - "--no-walk" -
201 "--timestamp" - "relative-date" - "--date=*" - "--stdin" -
202 "--objects" - "--objects-edge" - "--reverse" {
203 # These cause our parsing of git log's output to fail, or else
204 # they're options we want to set ourselves, so ignore them.
206 "--color-words*" - "--word-diff=color" {
207 # These trigger a word diff in the console interface,
208 # so help the user by enabling our own support
209 if {[package vcompare
$git_version "1.7.2"] >= 0} {
210 set worddiff
[mc
"Color words"]
214 if {[package vcompare
$git_version "1.7.2"] >= 0} {
215 set worddiff
[mc
"Markup words"]
218 "--stat=*" - "--numstat" - "--shortstat" - "--summary" -
219 "--check" - "--exit-code" - "--quiet" - "--topo-order" -
220 "--full-history" - "--dense" - "--sparse" -
221 "--follow" - "--left-right" - "--encoding=*" {
222 # These are harmless, and some are even useful
225 "--diff-filter=*" - "--no-merges" - "--unpacked" -
226 "--max-count=*" - "--skip=*" - "--since=*" - "--after=*" -
227 "--until=*" - "--before=*" - "--max-age=*" - "--min-age=*" -
228 "--author=*" - "--committer=*" - "--grep=*" - "-[iE]" -
229 "--remove-empty" - "--first-parent" - "--cherry-pick" -
230 "-S*" - "--pickaxe-all" - "--pickaxe-regex" -
231 "--simplify-by-decoration" {
232 # These mean that we get a subset of the commits
237 # This appears to be the only one that has a value as a
238 # separate word following it
248 # git rev-parse doesn't understand --merge
249 lappend revargs
--gitk-symmetric-diff-marker MERGE_HEAD...HEAD
251 "--no-replace-objects" {
252 set env
(GIT_NO_REPLACE_OBJECTS
) "1"
255 # Other flag arguments including -<n>
256 if {[string is digit
-strict [string range
$arg 1 end
]]} {
259 # a flag argument that we don't recognize;
260 # that means we can't optimize
266 # Non-flag arguments specify commits or ranges of commits
267 if {[string match
"*...*" $arg]} {
268 lappend revargs
--gitk-symmetric-diff-marker
274 set vdflags
($n) $diffargs
275 set vflags
($n) $glflags
276 set vrevs
($n) $revargs
277 set vfiltered
($n) $filtered
278 set vorigargs
($n) $origargs
282 proc parseviewrevs
{view revs
} {
283 global vposids vnegids
288 if {[catch
{set ids
[eval exec git rev-parse
$revs]} err
]} {
289 # we get stdout followed by stderr in $err
290 # for an unknown rev, git rev-parse echoes it and then errors out
291 set errlines
[split $err "\n"]
293 for {set l
0} {$l < [llength
$errlines]} {incr l
} {
294 set line
[lindex
$errlines $l]
295 if {!([string length
$line] == 40 && [string is xdigit
$line])} {
296 if {[string match
"fatal:*" $line]} {
297 if {[string match
"fatal: ambiguous argument*" $line]
299 if {[llength
$badrev] == 1} {
300 set err
"unknown revision $badrev"
302 set err
"unknown revisions: [join $badrev ", "]"
305 set err
[join [lrange
$errlines $l end
] "\n"]
312 error_popup
"[mc "Error parsing revisions
:"] $err"
319 foreach id
[split $ids "\n"] {
320 if {$id eq
"--gitk-symmetric-diff-marker"} {
322 } elseif
{[string match
"^*" $id]} {
329 lappend neg
[string range
$id 1 end
]
334 lset ret end
$id...
[lindex
$ret end
]
340 set vposids
($view) $pos
341 set vnegids
($view) $neg
345 # Start off a git log process and arrange to read its output
346 proc start_rev_list
{view
} {
347 global startmsecs commitidx viewcomplete curview
349 global viewargs viewargscmd viewfiles vfilelimit
350 global showlocalchanges
351 global viewactive viewinstances vmergeonly
352 global mainheadid viewmainheadid viewmainheadid_orig
353 global vcanopt vflags vrevs vorigargs
356 set startmsecs
[clock clicks
-milliseconds]
357 set commitidx
($view) 0
358 # these are set this way for the error exits
359 set viewcomplete
($view) 1
360 set viewactive
($view) 0
363 set args
$viewargs($view)
364 if {$viewargscmd($view) ne
{}} {
366 set str
[exec sh
-c $viewargscmd($view)]
368 error_popup
"[mc "Error executing
--argscmd command:"] $err"
371 set args
[concat
$args [split $str "\n"]]
373 set vcanopt
($view) [parseviewargs
$view $args]
375 set files
$viewfiles($view)
376 if {$vmergeonly($view)} {
377 set files
[unmerged_files
$files]
380 if {$nr_unmerged == 0} {
381 error_popup
[mc
"No files selected: --merge specified but\
382 no files are unmerged."]
384 error_popup
[mc
"No files selected: --merge specified but\
385 no unmerged files are within file limit."]
390 set vfilelimit
($view) $files
392 if {$vcanopt($view)} {
393 set revs
[parseviewrevs
$view $vrevs($view)]
397 set args
[concat
$vflags($view) $revs]
399 set args
$vorigargs($view)
403 set fd
[open
[concat | git log
--no-color -z --pretty=raw
$show_notes \
404 --parents --boundary $args "--" $files] r
]
406 error_popup
"[mc "Error executing git log
:"] $err"
409 set i
[reg_instance
$fd]
410 set viewinstances
($view) [list
$i]
411 set viewmainheadid
($view) $mainheadid
412 set viewmainheadid_orig
($view) $mainheadid
413 if {$files ne
{} && $mainheadid ne
{}} {
414 get_viewmainhead
$view
416 if {$showlocalchanges && $viewmainheadid($view) ne
{}} {
417 interestedin
$viewmainheadid($view) dodiffindex
419 fconfigure
$fd -blocking 0 -translation lf
-eofchar {}
420 if {$tclencoding != {}} {
421 fconfigure
$fd -encoding $tclencoding
423 filerun
$fd [list getcommitlines
$fd $i $view 0]
424 nowbusy
$view [mc
"Reading"]
425 set viewcomplete
($view) 0
426 set viewactive
($view) 1
430 proc stop_instance
{inst
} {
431 global commfd leftover
433 set fd
$commfd($inst)
437 if {$
::tcl_platform
(platform
) eq
{windows
}} {
446 unset leftover
($inst)
449 proc stop_backends
{} {
452 foreach inst
[array names commfd
] {
457 proc stop_rev_list
{view
} {
460 foreach inst
$viewinstances($view) {
463 set viewinstances
($view) {}
466 proc reset_pending_select
{selid
} {
467 global pending_select mainheadid selectheadid
470 set pending_select
$selid
471 } elseif
{$selectheadid ne
{}} {
472 set pending_select
$selectheadid
474 set pending_select
$mainheadid
478 proc getcommits
{selid
} {
479 global canv curview need_redisplay viewactive
482 if {[start_rev_list
$curview]} {
483 reset_pending_select
$selid
484 show_status
[mc
"Reading commits..."]
487 show_status
[mc
"No commits selected"]
491 proc updatecommits
{} {
492 global curview vcanopt vorigargs vfilelimit viewinstances
493 global viewactive viewcomplete tclencoding
494 global startmsecs showneartags showlocalchanges
495 global mainheadid viewmainheadid viewmainheadid_orig pending_select
497 global varcid vposids vnegids vflags vrevs
500 set hasworktree
[hasworktree
]
503 if {$mainheadid ne
$viewmainheadid_orig($view)} {
504 if {$showlocalchanges} {
507 set viewmainheadid
($view) $mainheadid
508 set viewmainheadid_orig
($view) $mainheadid
509 if {$vfilelimit($view) ne
{}} {
510 get_viewmainhead
$view
513 if {$showlocalchanges} {
516 if {$vcanopt($view)} {
517 set oldpos
$vposids($view)
518 set oldneg
$vnegids($view)
519 set revs
[parseviewrevs
$view $vrevs($view)]
523 # note: getting the delta when negative refs change is hard,
524 # and could require multiple git log invocations, so in that
525 # case we ask git log for all the commits (not just the delta)
526 if {$oldneg eq
$vnegids($view)} {
529 # take out positive refs that we asked for before or
530 # that we have already seen
532 if {[string length
$rev] == 40} {
533 if {[lsearch
-exact $oldpos $rev] < 0
534 && ![info exists varcid
($view,$rev)]} {
539 lappend
$newrevs $rev
542 if {$npos == 0} return
544 set vposids
($view) [lsort
-unique [concat
$oldpos $vposids($view)]]
546 set args
[concat
$vflags($view) $revs --not $oldpos]
548 set args
$vorigargs($view)
551 set fd
[open
[concat | git log
--no-color -z --pretty=raw
$show_notes \
552 --parents --boundary $args "--" $vfilelimit($view)] r
]
554 error_popup
"[mc "Error executing git log
:"] $err"
557 if {$viewactive($view) == 0} {
558 set startmsecs
[clock clicks
-milliseconds]
560 set i
[reg_instance
$fd]
561 lappend viewinstances
($view) $i
562 fconfigure
$fd -blocking 0 -translation lf
-eofchar {}
563 if {$tclencoding != {}} {
564 fconfigure
$fd -encoding $tclencoding
566 filerun
$fd [list getcommitlines
$fd $i $view 1]
567 incr viewactive
($view)
568 set viewcomplete
($view) 0
569 reset_pending_select
{}
570 nowbusy
$view [mc
"Reading"]
576 proc reloadcommits
{} {
577 global curview viewcomplete selectedline currentid thickerline
578 global showneartags treediffs commitinterest cached_commitrow
582 if {$selectedline ne
{}} {
586 if {!$viewcomplete($curview)} {
587 stop_rev_list
$curview
591 catch
{unset currentid
}
592 catch
{unset thickerline
}
593 catch
{unset treediffs
}
600 catch
{unset commitinterest
}
601 catch
{unset cached_commitrow
}
602 catch
{unset targetid
}
608 # This makes a string representation of a positive integer which
609 # sorts as a string in numerical order
612 return [format
"%x" $n]
613 } elseif
{$n < 256} {
614 return [format
"x%.2x" $n]
615 } elseif
{$n < 65536} {
616 return [format
"y%.4x" $n]
618 return [format
"z%.8x" $n]
621 # Procedures used in reordering commits from git log (without
622 # --topo-order) into the order for display.
624 proc varcinit
{view
} {
625 global varcstart vupptr vdownptr vleftptr vbackptr varctok varcrow
626 global vtokmod varcmod vrowmod varcix vlastins
628 set varcstart
($view) {{}}
629 set vupptr
($view) {0}
630 set vdownptr
($view) {0}
631 set vleftptr
($view) {0}
632 set vbackptr
($view) {0}
633 set varctok
($view) {{}}
634 set varcrow
($view) {{}}
635 set vtokmod
($view) {}
638 set varcix
($view) {{}}
639 set vlastins
($view) {0}
642 proc resetvarcs
{view
} {
643 global varcid varccommits parents children vseedcount ordertok
646 foreach vid
[array names varcid
$view,*] {
651 foreach vid
[array names vshortids
$view,*] {
652 unset vshortids
($vid)
654 # some commits might have children but haven't been seen yet
655 foreach vid
[array names children
$view,*] {
658 foreach va
[array names varccommits
$view,*] {
659 unset varccommits
($va)
661 foreach vd
[array names vseedcount
$view,*] {
662 unset vseedcount
($vd)
664 catch
{unset ordertok
}
667 # returns a list of the commits with no children
669 global vdownptr vleftptr varcstart
672 set a
[lindex
$vdownptr($v) 0]
674 lappend ret
[lindex
$varcstart($v) $a]
675 set a
[lindex
$vleftptr($v) $a]
680 proc newvarc
{view id
} {
681 global varcid varctok parents children vdatemode
682 global vupptr vdownptr vleftptr vbackptr varcrow varcix varcstart
683 global commitdata commitinfo vseedcount varccommits vlastins
685 set a
[llength
$varctok($view)]
687 if {[llength
$children($vid)] == 0 ||
$vdatemode($view)} {
688 if {![info exists commitinfo
($id)]} {
689 parsecommit
$id $commitdata($id) 1
691 set cdate
[lindex
[lindex
$commitinfo($id) 4] 0]
692 if {![string is integer
-strict $cdate]} {
695 if {![info exists vseedcount
($view,$cdate)]} {
696 set vseedcount
($view,$cdate) -1
698 set c
[incr vseedcount
($view,$cdate)]
699 set cdate
[expr {$cdate ^
0xffffffff}]
700 set tok
"s[strrep $cdate][strrep $c]"
705 if {[llength
$children($vid)] > 0} {
706 set kid
[lindex
$children($vid) end
]
707 set k
$varcid($view,$kid)
708 if {[string compare
[lindex
$varctok($view) $k] $tok] > 0} {
711 set tok
[lindex
$varctok($view) $k]
715 set i
[lsearch
-exact $parents($view,$ki) $id]
716 set j
[expr {[llength
$parents($view,$ki)] - 1 - $i}]
717 append tok
[strrep
$j]
719 set c
[lindex
$vlastins($view) $ka]
720 if {$c == 0 ||
[string compare
$tok [lindex
$varctok($view) $c]] < 0} {
722 set b
[lindex
$vdownptr($view) $ka]
724 set b
[lindex
$vleftptr($view) $c]
726 while {$b != 0 && [string compare
$tok [lindex
$varctok($view) $b]] >= 0} {
728 set b
[lindex
$vleftptr($view) $c]
731 lset vdownptr
($view) $ka $a
732 lappend vbackptr
($view) 0
734 lset vleftptr
($view) $c $a
735 lappend vbackptr
($view) $c
737 lset vlastins
($view) $ka $a
738 lappend vupptr
($view) $ka
739 lappend vleftptr
($view) $b
741 lset vbackptr
($view) $b $a
743 lappend varctok
($view) $tok
744 lappend varcstart
($view) $id
745 lappend vdownptr
($view) 0
746 lappend varcrow
($view) {}
747 lappend varcix
($view) {}
748 set varccommits
($view,$a) {}
749 lappend vlastins
($view) 0
753 proc splitvarc
{p v
} {
754 global varcid varcstart varccommits varctok vtokmod
755 global vupptr vdownptr vleftptr vbackptr varcix varcrow vlastins
757 set oa
$varcid($v,$p)
758 set otok
[lindex
$varctok($v) $oa]
759 set ac
$varccommits($v,$oa)
760 set i
[lsearch
-exact $varccommits($v,$oa) $p]
762 set na
[llength
$varctok($v)]
763 # "%" sorts before "0"...
764 set tok
"$otok%[strrep $i]"
765 lappend varctok
($v) $tok
766 lappend varcrow
($v) {}
767 lappend varcix
($v) {}
768 set varccommits
($v,$oa) [lrange
$ac 0 [expr {$i - 1}]]
769 set varccommits
($v,$na) [lrange
$ac $i end
]
770 lappend varcstart
($v) $p
771 foreach id
$varccommits($v,$na) {
772 set varcid
($v,$id) $na
774 lappend vdownptr
($v) [lindex
$vdownptr($v) $oa]
775 lappend vlastins
($v) [lindex
$vlastins($v) $oa]
776 lset vdownptr
($v) $oa $na
777 lset vlastins
($v) $oa 0
778 lappend vupptr
($v) $oa
779 lappend vleftptr
($v) 0
780 lappend vbackptr
($v) 0
781 for {set b
[lindex
$vdownptr($v) $na]} {$b != 0} {set b
[lindex
$vleftptr($v) $b]} {
782 lset vupptr
($v) $b $na
784 if {[string compare
$otok $vtokmod($v)] <= 0} {
789 proc renumbervarc
{a v
} {
790 global parents children varctok varcstart varccommits
791 global vupptr vdownptr vleftptr vbackptr vlastins varcid vtokmod vdatemode
793 set t1
[clock clicks
-milliseconds]
799 if {[info exists isrelated
($a)]} {
801 set id
[lindex
$varccommits($v,$a) end
]
802 foreach p
$parents($v,$id) {
803 if {[info exists varcid
($v,$p)]} {
804 set isrelated
($varcid($v,$p)) 1
809 set b
[lindex
$vdownptr($v) $a]
812 set b
[lindex
$vleftptr($v) $a]
814 set a
[lindex
$vupptr($v) $a]
820 if {![info exists kidchanged
($a)]} continue
821 set id
[lindex
$varcstart($v) $a]
822 if {[llength
$children($v,$id)] > 1} {
823 set children
($v,$id) [lsort
-command [list vtokcmp
$v] \
826 set oldtok
[lindex
$varctok($v) $a]
827 if {!$vdatemode($v)} {
833 set kid
[last_real_child
$v,$id]
835 set k
$varcid($v,$kid)
836 if {[string compare
[lindex
$varctok($v) $k] $tok] > 0} {
839 set tok
[lindex
$varctok($v) $k]
843 set i
[lsearch
-exact $parents($v,$ki) $id]
844 set j
[expr {[llength
$parents($v,$ki)] - 1 - $i}]
845 append tok
[strrep
$j]
847 if {$tok eq
$oldtok} {
850 set id
[lindex
$varccommits($v,$a) end
]
851 foreach p
$parents($v,$id) {
852 if {[info exists varcid
($v,$p)]} {
853 set kidchanged
($varcid($v,$p)) 1
858 lset varctok
($v) $a $tok
859 set b
[lindex
$vupptr($v) $a]
861 if {[string compare
[lindex
$varctok($v) $ka] $vtokmod($v)] < 0} {
864 if {[string compare
[lindex
$varctok($v) $b] $vtokmod($v)] < 0} {
867 set c
[lindex
$vbackptr($v) $a]
868 set d
[lindex
$vleftptr($v) $a]
870 lset vdownptr
($v) $b $d
872 lset vleftptr
($v) $c $d
875 lset vbackptr
($v) $d $c
877 if {[lindex
$vlastins($v) $b] == $a} {
878 lset vlastins
($v) $b $c
880 lset vupptr
($v) $a $ka
881 set c
[lindex
$vlastins($v) $ka]
883 [string compare
$tok [lindex
$varctok($v) $c]] < 0} {
885 set b
[lindex
$vdownptr($v) $ka]
887 set b
[lindex
$vleftptr($v) $c]
890 [string compare
$tok [lindex
$varctok($v) $b]] >= 0} {
892 set b
[lindex
$vleftptr($v) $c]
895 lset vdownptr
($v) $ka $a
896 lset vbackptr
($v) $a 0
898 lset vleftptr
($v) $c $a
899 lset vbackptr
($v) $a $c
901 lset vleftptr
($v) $a $b
903 lset vbackptr
($v) $b $a
905 lset vlastins
($v) $ka $a
908 foreach id
[array names sortkids
] {
909 if {[llength
$children($v,$id)] > 1} {
910 set children
($v,$id) [lsort
-command [list vtokcmp
$v] \
914 set t2
[clock clicks
-milliseconds]
915 #puts "renumbervarc did [llength $todo] of $ntot arcs in [expr {$t2-$t1}]ms"
918 # Fix up the graph after we have found out that in view $v,
919 # $p (a commit that we have already seen) is actually the parent
920 # of the last commit in arc $a.
921 proc fix_reversal
{p a v
} {
922 global varcid varcstart varctok vupptr
924 set pa
$varcid($v,$p)
925 if {$p ne
[lindex
$varcstart($v) $pa]} {
927 set pa
$varcid($v,$p)
929 # seeds always need to be renumbered
930 if {[lindex
$vupptr($v) $pa] == 0 ||
931 [string compare
[lindex
$varctok($v) $a] \
932 [lindex
$varctok($v) $pa]] > 0} {
937 proc insertrow
{id p v
} {
938 global cmitlisted children parents varcid varctok vtokmod
939 global varccommits ordertok commitidx numcommits curview
940 global targetid targetrow vshortids
944 set cmitlisted
($vid) 1
945 set children
($vid) {}
946 set parents
($vid) [list
$p]
947 set a
[newvarc
$v $id]
949 lappend vshortids
($v,[string range
$id 0 3]) $id
950 if {[string compare
[lindex
$varctok($v) $a] $vtokmod($v)] < 0} {
953 lappend varccommits
($v,$a) $id
955 if {[llength
[lappend children
($vp) $id]] > 1} {
956 set children
($vp) [lsort
-command [list vtokcmp
$v] $children($vp)]
957 catch
{unset ordertok
}
959 fix_reversal
$p $a $v
961 if {$v == $curview} {
962 set numcommits
$commitidx($v)
964 if {[info exists targetid
]} {
965 if {![comes_before
$targetid $p]} {
972 proc insertfakerow
{id p
} {
973 global varcid varccommits parents children cmitlisted
974 global commitidx varctok vtokmod targetid targetrow curview numcommits
978 set i
[lsearch
-exact $varccommits($v,$a) $p]
980 puts
"oops: insertfakerow can't find [shortids $p] on arc $a"
983 set children
($v,$id) {}
984 set parents
($v,$id) [list
$p]
985 set varcid
($v,$id) $a
986 lappend children
($v,$p) $id
987 set cmitlisted
($v,$id) 1
988 set numcommits
[incr commitidx
($v)]
989 # note we deliberately don't update varcstart($v) even if $i == 0
990 set varccommits
($v,$a) [linsert
$varccommits($v,$a) $i $id]
992 if {[info exists targetid
]} {
993 if {![comes_before
$targetid $p]} {
1001 proc removefakerow
{id
} {
1002 global varcid varccommits parents children commitidx
1003 global varctok vtokmod cmitlisted currentid selectedline
1004 global targetid curview numcommits
1007 if {[llength
$parents($v,$id)] != 1} {
1008 puts
"oops: removefakerow [shortids $id] has [llength $parents($v,$id)] parents"
1011 set p
[lindex
$parents($v,$id) 0]
1012 set a
$varcid($v,$id)
1013 set i
[lsearch
-exact $varccommits($v,$a) $id]
1015 puts
"oops: removefakerow can't find [shortids $id] on arc $a"
1018 unset varcid
($v,$id)
1019 set varccommits
($v,$a) [lreplace
$varccommits($v,$a) $i $i]
1020 unset parents
($v,$id)
1021 unset children
($v,$id)
1022 unset cmitlisted
($v,$id)
1023 set numcommits
[incr commitidx
($v) -1]
1024 set j
[lsearch
-exact $children($v,$p) $id]
1026 set children
($v,$p) [lreplace
$children($v,$p) $j $j]
1029 if {[info exist currentid
] && $id eq
$currentid} {
1033 if {[info exists targetid
] && $targetid eq
$id} {
1040 proc real_children
{vp
} {
1041 global children nullid nullid2
1044 foreach id
$children($vp) {
1045 if {$id ne
$nullid && $id ne
$nullid2} {
1052 proc first_real_child
{vp
} {
1053 global children nullid nullid2
1055 foreach id
$children($vp) {
1056 if {$id ne
$nullid && $id ne
$nullid2} {
1063 proc last_real_child
{vp
} {
1064 global children nullid nullid2
1066 set kids
$children($vp)
1067 for {set i
[llength
$kids]} {[incr i
-1] >= 0} {} {
1068 set id
[lindex
$kids $i]
1069 if {$id ne
$nullid && $id ne
$nullid2} {
1076 proc vtokcmp
{v a b
} {
1077 global varctok varcid
1079 return [string compare
[lindex
$varctok($v) $varcid($v,$a)] \
1080 [lindex
$varctok($v) $varcid($v,$b)]]
1083 # This assumes that if lim is not given, the caller has checked that
1084 # arc a's token is less than $vtokmod($v)
1085 proc modify_arc
{v a
{lim
{}}} {
1086 global varctok vtokmod varcmod varcrow vupptr curview vrowmod varccommits
1089 set c
[string compare
[lindex
$varctok($v) $a] $vtokmod($v)]
1092 set r
[lindex
$varcrow($v) $a]
1093 if {$r ne
{} && $vrowmod($v) <= $r + $lim} return
1096 set vtokmod
($v) [lindex
$varctok($v) $a]
1098 if {$v == $curview} {
1099 while {$a != 0 && [lindex
$varcrow($v) $a] eq
{}} {
1100 set a
[lindex
$vupptr($v) $a]
1106 set lim
[llength
$varccommits($v,$a)]
1108 set r
[expr {[lindex
$varcrow($v) $a] + $lim}]
1115 proc update_arcrows
{v
} {
1116 global vtokmod varcmod vrowmod varcrow commitidx currentid selectedline
1117 global varcid vrownum varcorder varcix varccommits
1118 global vupptr vdownptr vleftptr varctok
1119 global displayorder parentlist curview cached_commitrow
1121 if {$vrowmod($v) == $commitidx($v)} return
1122 if {$v == $curview} {
1123 if {[llength
$displayorder] > $vrowmod($v)} {
1124 set displayorder
[lrange
$displayorder 0 [expr {$vrowmod($v) - 1}]]
1125 set parentlist
[lrange
$parentlist 0 [expr {$vrowmod($v) - 1}]]
1127 catch
{unset cached_commitrow
}
1129 set narctot
[expr {[llength
$varctok($v)] - 1}]
1131 while {$a != 0 && [lindex
$varcix($v) $a] eq
{}} {
1132 # go up the tree until we find something that has a row number,
1133 # or we get to a seed
1134 set a
[lindex
$vupptr($v) $a]
1137 set a
[lindex
$vdownptr($v) 0]
1140 set varcorder
($v) [list
$a]
1141 lset varcix
($v) $a 0
1142 lset varcrow
($v) $a 0
1146 set arcn
[lindex
$varcix($v) $a]
1147 if {[llength
$vrownum($v)] > $arcn + 1} {
1148 set vrownum
($v) [lrange
$vrownum($v) 0 $arcn]
1149 set varcorder
($v) [lrange
$varcorder($v) 0 $arcn]
1151 set row
[lindex
$varcrow($v) $a]
1155 incr row
[llength
$varccommits($v,$a)]
1156 # go down if possible
1157 set b
[lindex
$vdownptr($v) $a]
1159 # if not, go left, or go up until we can go left
1161 set b
[lindex
$vleftptr($v) $a]
1163 set a
[lindex
$vupptr($v) $a]
1169 lappend vrownum
($v) $row
1170 lappend varcorder
($v) $a
1171 lset varcix
($v) $a $arcn
1172 lset varcrow
($v) $a $row
1174 set vtokmod
($v) [lindex
$varctok($v) $p]
1176 set vrowmod
($v) $row
1177 if {[info exists currentid
]} {
1178 set selectedline
[rowofcommit
$currentid]
1182 # Test whether view $v contains commit $id
1183 proc commitinview
{id v
} {
1186 return [info exists varcid
($v,$id)]
1189 # Return the row number for commit $id in the current view
1190 proc rowofcommit
{id
} {
1191 global varcid varccommits varcrow curview cached_commitrow
1192 global varctok vtokmod
1195 if {![info exists varcid
($v,$id)]} {
1196 puts
"oops rowofcommit no arc for [shortids $id]"
1199 set a
$varcid($v,$id)
1200 if {[string compare
[lindex
$varctok($v) $a] $vtokmod($v)] >= 0} {
1203 if {[info exists cached_commitrow
($id)]} {
1204 return $cached_commitrow($id)
1206 set i
[lsearch
-exact $varccommits($v,$a) $id]
1208 puts
"oops didn't find commit [shortids $id] in arc $a"
1211 incr i
[lindex
$varcrow($v) $a]
1212 set cached_commitrow
($id) $i
1216 # Returns 1 if a is on an earlier row than b, otherwise 0
1217 proc comes_before
{a b
} {
1218 global varcid varctok curview
1221 if {$a eq
$b ||
![info exists varcid
($v,$a)] || \
1222 ![info exists varcid
($v,$b)]} {
1225 if {$varcid($v,$a) != $varcid($v,$b)} {
1226 return [expr {[string compare
[lindex
$varctok($v) $varcid($v,$a)] \
1227 [lindex
$varctok($v) $varcid($v,$b)]] < 0}]
1229 return [expr {[rowofcommit
$a] < [rowofcommit
$b]}]
1232 proc bsearch
{l elt
} {
1233 if {[llength
$l] == 0 ||
$elt <= [lindex
$l 0]} {
1238 while {$hi - $lo > 1} {
1239 set mid
[expr {int
(($lo + $hi) / 2)}]
1240 set t
[lindex
$l $mid]
1243 } elseif
{$elt > $t} {
1252 # Make sure rows $start..$end-1 are valid in displayorder and parentlist
1253 proc make_disporder
{start end
} {
1254 global vrownum curview commitidx displayorder parentlist
1255 global varccommits varcorder parents vrowmod varcrow
1256 global d_valid_start d_valid_end
1258 if {$end > $vrowmod($curview)} {
1259 update_arcrows
$curview
1261 set ai
[bsearch
$vrownum($curview) $start]
1262 set start
[lindex
$vrownum($curview) $ai]
1263 set narc
[llength
$vrownum($curview)]
1264 for {set r
$start} {$ai < $narc && $r < $end} {incr ai
} {
1265 set a
[lindex
$varcorder($curview) $ai]
1266 set l
[llength
$displayorder]
1267 set al
[llength
$varccommits($curview,$a)]
1268 if {$l < $r + $al} {
1270 set pad
[ntimes
[expr {$r - $l}] {}]
1271 set displayorder
[concat
$displayorder $pad]
1272 set parentlist
[concat
$parentlist $pad]
1273 } elseif
{$l > $r} {
1274 set displayorder
[lrange
$displayorder 0 [expr {$r - 1}]]
1275 set parentlist
[lrange
$parentlist 0 [expr {$r - 1}]]
1277 foreach id
$varccommits($curview,$a) {
1278 lappend displayorder
$id
1279 lappend parentlist
$parents($curview,$id)
1281 } elseif
{[lindex
$displayorder [expr {$r + $al - 1}]] eq
{}} {
1283 foreach id
$varccommits($curview,$a) {
1284 lset displayorder
$i $id
1285 lset parentlist
$i $parents($curview,$id)
1293 proc commitonrow
{row
} {
1296 set id
[lindex
$displayorder $row]
1298 make_disporder
$row [expr {$row + 1}]
1299 set id
[lindex
$displayorder $row]
1304 proc closevarcs
{v
} {
1305 global varctok varccommits varcid parents children
1306 global cmitlisted commitidx vtokmod
1308 set missing_parents
0
1310 set narcs
[llength
$varctok($v)]
1311 for {set a
1} {$a < $narcs} {incr a
} {
1312 set id
[lindex
$varccommits($v,$a) end
]
1313 foreach p
$parents($v,$id) {
1314 if {[info exists varcid
($v,$p)]} continue
1315 # add p as a new commit
1316 incr missing_parents
1317 set cmitlisted
($v,$p) 0
1318 set parents
($v,$p) {}
1319 if {[llength
$children($v,$p)] == 1 &&
1320 [llength
$parents($v,$id)] == 1} {
1323 set b
[newvarc
$v $p]
1325 set varcid
($v,$p) $b
1326 if {[string compare
[lindex
$varctok($v) $b] $vtokmod($v)] < 0} {
1329 lappend varccommits
($v,$b) $p
1331 set scripts
[check_interest
$p $scripts]
1334 if {$missing_parents > 0} {
1335 foreach s
$scripts {
1341 # Use $rwid as a substitute for $id, i.e. reparent $id's children to $rwid
1342 # Assumes we already have an arc for $rwid.
1343 proc rewrite_commit
{v id rwid
} {
1344 global children parents varcid varctok vtokmod varccommits
1346 foreach ch
$children($v,$id) {
1347 # make $rwid be $ch's parent in place of $id
1348 set i
[lsearch
-exact $parents($v,$ch) $id]
1350 puts
"oops rewrite_commit didn't find $id in parent list for $ch"
1352 set parents
($v,$ch) [lreplace
$parents($v,$ch) $i $i $rwid]
1353 # add $ch to $rwid's children and sort the list if necessary
1354 if {[llength
[lappend children
($v,$rwid) $ch]] > 1} {
1355 set children
($v,$rwid) [lsort
-command [list vtokcmp
$v] \
1356 $children($v,$rwid)]
1358 # fix the graph after joining $id to $rwid
1359 set a
$varcid($v,$ch)
1360 fix_reversal
$rwid $a $v
1361 # parentlist is wrong for the last element of arc $a
1362 # even if displayorder is right, hence the 3rd arg here
1363 modify_arc
$v $a [expr {[llength
$varccommits($v,$a)] - 1}]
1367 # Mechanism for registering a command to be executed when we come
1368 # across a particular commit. To handle the case when only the
1369 # prefix of the commit is known, the commitinterest array is now
1370 # indexed by the first 4 characters of the ID. Each element is a
1371 # list of id, cmd pairs.
1372 proc interestedin
{id cmd
} {
1373 global commitinterest
1375 lappend commitinterest
([string range
$id 0 3]) $id $cmd
1378 proc check_interest
{id scripts
} {
1379 global commitinterest
1381 set prefix
[string range
$id 0 3]
1382 if {[info exists commitinterest
($prefix)]} {
1384 foreach
{i
script} $commitinterest($prefix) {
1385 if {[string match
"$i*" $id]} {
1386 lappend scripts
[string map
[list
"%I" $id "%P" $i] $script]
1388 lappend newlist
$i $script
1391 if {$newlist ne
{}} {
1392 set commitinterest
($prefix) $newlist
1394 unset commitinterest
($prefix)
1400 proc getcommitlines
{fd inst view updating
} {
1401 global cmitlisted leftover
1402 global commitidx commitdata vdatemode
1403 global parents children curview hlview
1404 global idpending ordertok
1405 global varccommits varcid varctok vtokmod vfilelimit vshortids
1407 set stuff
[read $fd 500000]
1408 # git log doesn't terminate the last commit with a null...
1409 if {$stuff == {} && $leftover($inst) ne
{} && [eof
$fd]} {
1416 global commfd viewcomplete viewactive viewname
1417 global viewinstances
1419 set i
[lsearch
-exact $viewinstances($view) $inst]
1421 set viewinstances
($view) [lreplace
$viewinstances($view) $i $i]
1423 # set it blocking so we wait for the process to terminate
1424 fconfigure
$fd -blocking 1
1425 if {[catch
{close
$fd} err
]} {
1427 if {$view != $curview} {
1428 set fv
" for the \"$viewname($view)\" view"
1430 if {[string range
$err 0 4] == "usage"} {
1431 set err
"Gitk: error reading commits$fv:\
1432 bad arguments to git log."
1433 if {$viewname($view) eq
"Command line"} {
1435 " (Note: arguments to gitk are passed to git log\
1436 to allow selection of commits to be displayed.)"
1439 set err
"Error reading commits$fv: $err"
1443 if {[incr viewactive
($view) -1] <= 0} {
1444 set viewcomplete
($view) 1
1445 # Check if we have seen any ids listed as parents that haven't
1446 # appeared in the list
1450 if {$view == $curview} {
1459 set i
[string first
"\0" $stuff $start]
1461 append leftover
($inst) [string range
$stuff $start end
]
1465 set cmit
$leftover($inst)
1466 append cmit
[string range
$stuff 0 [expr {$i - 1}]]
1467 set leftover
($inst) {}
1469 set cmit
[string range
$stuff $start [expr {$i - 1}]]
1471 set start
[expr {$i + 1}]
1472 set j
[string first
"\n" $cmit]
1475 if {$j >= 0 && [string match
"commit *" $cmit]} {
1476 set ids
[string range
$cmit 7 [expr {$j - 1}]]
1477 if {[string match
{[-^
<>]*} $ids]} {
1478 switch
-- [string index
$ids 0] {
1484 set ids
[string range
$ids 1 end
]
1488 if {[string length
$id] != 40} {
1496 if {[string length
$shortcmit] > 80} {
1497 set shortcmit
"[string range $shortcmit 0 80]..."
1499 error_popup
"[mc "Can
't parse git log output:"] {$shortcmit}"
1502 set id [lindex $ids 0]
1505 lappend vshortids($view,[string range $id 0 3]) $id
1507 if {!$listed && $updating && ![info exists varcid($vid)] &&
1508 $vfilelimit($view) ne {}} {
1509 # git log doesn't rewrite parents
for unlisted commits
1510 # when doing path limiting, so work around that here
1511 # by working out the rewritten parent with git rev-list
1512 # and if we already know about it, using the rewritten
1513 # parent as a substitute parent for $id's children.
1515 set rwid
[exec git rev-list
--first-parent --max-count=1 \
1516 $id -- $vfilelimit($view)]
1518 if {$rwid ne
{} && [info exists varcid
($view,$rwid)]} {
1519 # use $rwid in place of $id
1520 rewrite_commit
$view $id $rwid
1527 if {[info exists varcid
($vid)]} {
1528 if {$cmitlisted($vid) ||
!$listed} continue
1532 set olds
[lrange
$ids 1 end
]
1536 set commitdata
($id) [string range
$cmit [expr {$j + 1}] end
]
1537 set cmitlisted
($vid) $listed
1538 set parents
($vid) $olds
1539 if {![info exists children
($vid)]} {
1540 set children
($vid) {}
1541 } elseif
{$a == 0 && [llength
$children($vid)] == 1} {
1542 set k
[lindex
$children($vid) 0]
1543 if {[llength
$parents($view,$k)] == 1 &&
1544 (!$vdatemode($view) ||
1545 $varcid($view,$k) == [llength
$varctok($view)] - 1)} {
1546 set a
$varcid($view,$k)
1551 set a
[newvarc
$view $id]
1553 if {[string compare
[lindex
$varctok($view) $a] $vtokmod($view)] < 0} {
1556 if {![info exists varcid
($vid)]} {
1558 lappend varccommits
($view,$a) $id
1559 incr commitidx
($view)
1564 if {$i == 0 ||
[lsearch
-exact $olds $p] >= $i} {
1566 if {[llength
[lappend children
($vp) $id]] > 1 &&
1567 [vtokcmp
$view [lindex
$children($vp) end-1
] $id] > 0} {
1568 set children
($vp) [lsort
-command [list vtokcmp
$view] \
1570 catch
{unset ordertok
}
1572 if {[info exists varcid
($view,$p)]} {
1573 fix_reversal
$p $a $view
1579 set scripts
[check_interest
$id $scripts]
1583 global numcommits hlview
1585 if {$view == $curview} {
1586 set numcommits
$commitidx($view)
1589 if {[info exists hlview
] && $view == $hlview} {
1590 # we never actually get here...
1593 foreach s
$scripts {
1600 proc chewcommits
{} {
1601 global curview hlview viewcomplete
1602 global pending_select
1605 if {$viewcomplete($curview)} {
1606 global commitidx varctok
1607 global numcommits startmsecs
1609 if {[info exists pending_select
]} {
1611 reset_pending_select
{}
1613 if {[commitinview
$pending_select $curview]} {
1614 selectline
[rowofcommit
$pending_select] 1
1616 set row
[first_real_row
]
1620 if {$commitidx($curview) > 0} {
1621 #set ms [expr {[clock clicks -milliseconds] - $startmsecs}]
1622 #puts "overall $ms ms for $numcommits commits"
1623 #puts "[llength $varctok($view)] arcs, $commitidx($view) commits"
1625 show_status
[mc
"No commits selected"]
1632 proc do_readcommit
{id
} {
1635 # Invoke git-log to handle automatic encoding conversion
1636 set fd
[open
[concat | git log
--no-color --pretty=raw
-1 $id] r
]
1637 # Read the results using i18n.logoutputencoding
1638 fconfigure
$fd -translation lf
-eofchar {}
1639 if {$tclencoding != {}} {
1640 fconfigure
$fd -encoding $tclencoding
1642 set contents
[read $fd]
1644 # Remove the heading line
1645 regsub
{^commit
[0-9a-f]+\n} $contents {} contents
1650 proc readcommit
{id
} {
1651 if {[catch
{set contents
[do_readcommit
$id]}]} return
1652 parsecommit
$id $contents 1
1655 proc parsecommit
{id contents listed
} {
1665 set hdrend
[string first
"\n\n" $contents]
1667 # should never happen...
1668 set hdrend
[string length
$contents]
1670 set header
[string range
$contents 0 [expr {$hdrend - 1}]]
1671 set comment
[string range
$contents [expr {$hdrend + 2}] end
]
1672 foreach line
[split $header "\n"] {
1673 set line
[split $line " "]
1674 set tag
[lindex
$line 0]
1675 if {$tag == "author"} {
1676 set audate
[lrange
$line end-1 end
]
1677 set auname
[join [lrange
$line 1 end-2
] " "]
1678 } elseif
{$tag == "committer"} {
1679 set comdate
[lrange
$line end-1 end
]
1680 set comname
[join [lrange
$line 1 end-2
] " "]
1684 # take the first non-blank line of the comment as the headline
1685 set headline
[string trimleft
$comment]
1686 set i
[string first
"\n" $headline]
1688 set headline
[string range
$headline 0 $i]
1690 set headline
[string trimright
$headline]
1691 set i
[string first
"\r" $headline]
1693 set headline
[string trimright
[string range
$headline 0 $i]]
1696 # git log indents the comment by 4 spaces;
1697 # if we got this via git cat-file, add the indentation
1699 foreach line
[split $comment "\n"] {
1700 append newcomment
" "
1701 append newcomment
$line
1702 append newcomment
"\n"
1704 set comment
$newcomment
1706 set hasnote
[string first
"\nNotes:\n" $contents]
1707 set commitinfo
($id) [list
$headline $auname $audate \
1708 $comname $comdate $comment $hasnote]
1711 proc getcommit
{id
} {
1712 global commitdata commitinfo
1714 if {[info exists commitdata
($id)]} {
1715 parsecommit
$id $commitdata($id) 1
1718 if {![info exists commitinfo
($id)]} {
1719 set commitinfo
($id) [list
[mc
"No commit information available"]]
1725 # Expand an abbreviated commit ID to a list of full 40-char IDs that match
1726 # and are present in the current view.
1727 # This is fairly slow...
1728 proc longid
{prefix
} {
1729 global varcid curview vshortids
1732 if {[string length
$prefix] >= 4} {
1733 set vshortid
$curview,[string range
$prefix 0 3]
1734 if {[info exists vshortids
($vshortid)]} {
1735 foreach id
$vshortids($vshortid) {
1736 if {[string match
"$prefix*" $id]} {
1737 if {[lsearch
-exact $ids $id] < 0} {
1739 if {[llength
$ids] >= 2} break
1745 foreach match
[array names varcid
"$curview,$prefix*"] {
1746 lappend ids
[lindex
[split $match ","] 1]
1747 if {[llength
$ids] >= 2} break
1754 global tagids idtags headids idheads tagobjid
1755 global otherrefids idotherrefs mainhead mainheadid
1756 global selecthead selectheadid
1759 foreach v
{tagids idtags headids idheads otherrefids idotherrefs
} {
1762 set refd
[open
[list | git show-ref
-d] r
]
1763 while {[gets
$refd line
] >= 0} {
1764 if {[string index
$line 40] ne
" "} continue
1765 set id
[string range
$line 0 39]
1766 set ref
[string range
$line 41 end
]
1767 if {![string match
"refs/*" $ref]} continue
1768 set name
[string range
$ref 5 end
]
1769 if {[string match
"remotes/*" $name]} {
1770 if {![string match
"*/HEAD" $name] && !$hideremotes} {
1771 set headids
($name) $id
1772 lappend idheads
($id) $name
1774 } elseif
{[string match
"heads/*" $name]} {
1775 set name
[string range
$name 6 end
]
1776 set headids
($name) $id
1777 lappend idheads
($id) $name
1778 } elseif
{[string match
"tags/*" $name]} {
1779 # this lets refs/tags/foo^{} overwrite refs/tags/foo,
1780 # which is what we want since the former is the commit ID
1781 set name
[string range
$name 5 end
]
1782 if {[string match
"*^{}" $name]} {
1783 set name
[string range
$name 0 end-3
]
1785 set tagobjid
($name) $id
1787 set tagids
($name) $id
1788 lappend idtags
($id) $name
1790 set otherrefids
($name) $id
1791 lappend idotherrefs
($id) $name
1798 set mainheadid
[exec git rev-parse HEAD
]
1799 set thehead
[exec git symbolic-ref HEAD
]
1800 if {[string match
"refs/heads/*" $thehead]} {
1801 set mainhead
[string range
$thehead 11 end
]
1805 if {$selecthead ne
{}} {
1807 set selectheadid
[exec git rev-parse
--verify $selecthead]
1812 # skip over fake commits
1813 proc first_real_row
{} {
1814 global nullid nullid2 numcommits
1816 for {set row
0} {$row < $numcommits} {incr row
} {
1817 set id
[commitonrow
$row]
1818 if {$id ne
$nullid && $id ne
$nullid2} {
1825 # update things for a head moved to a child of its previous location
1826 proc movehead
{id name
} {
1827 global headids idheads
1829 removehead
$headids($name) $name
1830 set headids
($name) $id
1831 lappend idheads
($id) $name
1834 # update things when a head has been removed
1835 proc removehead
{id name
} {
1836 global headids idheads
1838 if {$idheads($id) eq
$name} {
1841 set i
[lsearch
-exact $idheads($id) $name]
1843 set idheads
($id) [lreplace
$idheads($id) $i $i]
1846 unset headids
($name)
1849 proc ttk_toplevel
{w args
} {
1851 eval [linsert
$args 0 ::toplevel
$w]
1853 place
[ttk
::frame
$w._toplevel_background
] -x 0 -y 0 -relwidth 1 -relheight 1
1858 proc make_transient
{window origin
} {
1861 # In MacOS Tk 8.4 transient appears to work by setting
1862 # overrideredirect, which is utterly useless, since the
1863 # windows get no border, and are not even kept above
1865 if {!$have_tk85 && [tk windowingsystem
] eq
{aqua
}} return
1867 wm transient
$window $origin
1869 # Windows fails to place transient windows normally, so
1870 # schedule a callback to center them on the parent.
1871 if {[tk windowingsystem
] eq
{win32
}} {
1872 after idle
[list tk
::PlaceWindow
$window widget
$origin]
1876 proc show_error
{w top msg
{mc mc
}} {
1878 if {![info exists NS
]} {set NS
""}
1879 if {[wm state
$top] eq
"withdrawn"} { wm deiconify
$top }
1880 message
$w.m
-text $msg -justify center
-aspect 400
1881 pack
$w.m
-side top
-fill x
-padx 20 -pady 20
1882 ${NS}::button
$w.ok
-default active
-text [$mc OK
] -command "destroy $top"
1883 pack
$w.ok
-side bottom
-fill x
1884 bind $top <Visibility
> "grab $top; focus $top"
1885 bind $top <Key-Return
> "destroy $top"
1886 bind $top <Key-space
> "destroy $top"
1887 bind $top <Key-Escape
> "destroy $top"
1891 proc error_popup
{msg
{owner .
}} {
1892 if {[tk windowingsystem
] eq
"win32"} {
1893 tk_messageBox
-icon error
-type ok
-title [wm title .
] \
1894 -parent $owner -message $msg
1898 make_transient
$w $owner
1899 show_error
$w $w $msg
1903 proc confirm_popup
{msg
{owner .
}} {
1904 global confirm_ok NS
1908 make_transient
$w $owner
1909 message
$w.m
-text $msg -justify center
-aspect 400
1910 pack
$w.m
-side top
-fill x
-padx 20 -pady 20
1911 ${NS}::button
$w.ok
-text [mc OK
] -command "set confirm_ok 1; destroy $w"
1912 pack
$w.ok
-side left
-fill x
1913 ${NS}::button
$w.cancel
-text [mc Cancel
] -command "destroy $w"
1914 pack
$w.cancel
-side right
-fill x
1915 bind $w <Visibility
> "grab $w; focus $w"
1916 bind $w <Key-Return
> "set confirm_ok 1; destroy $w"
1917 bind $w <Key-space
> "set confirm_ok 1; destroy $w"
1918 bind $w <Key-Escape
> "destroy $w"
1919 tk
::PlaceWindow
$w widget
$owner
1924 proc setoptions
{} {
1925 if {[tk windowingsystem
] ne
"win32"} {
1926 option add
*Panedwindow.showHandle
1 startupFile
1927 option add
*Panedwindow.sashRelief raised startupFile
1928 if {[tk windowingsystem
] ne
"aqua"} {
1929 option add
*Menu.font uifont startupFile
1932 option add
*Menu.TearOff
0 startupFile
1934 option add
*Button.font uifont startupFile
1935 option add
*Checkbutton.font uifont startupFile
1936 option add
*Radiobutton.font uifont startupFile
1937 option add
*Menubutton.font uifont startupFile
1938 option add
*Label.font uifont startupFile
1939 option add
*Message.font uifont startupFile
1940 option add
*Entry.font textfont startupFile
1941 option add
*Text.font textfont startupFile
1942 option add
*Labelframe.font uifont startupFile
1943 option add
*Spinbox.font textfont startupFile
1944 option add
*Listbox.font mainfont startupFile
1947 # Make a menu and submenus.
1948 # m is the window name for the menu, items is the list of menu items to add.
1949 # Each item is a list {mc label type description options...}
1950 # mc is ignored; it's so we can put mc there to alert xgettext
1951 # label is the string that appears in the menu
1952 # type is cascade, command or radiobutton (should add checkbutton)
1953 # description depends on type; it's the sublist for cascade, the
1954 # command to invoke for command, or {variable value} for radiobutton
1955 proc makemenu
{m items
} {
1957 if {[tk windowingsystem
] eq
{aqua
}} {
1963 set name
[mc
[lindex
$i 1]]
1964 set type [lindex
$i 2]
1965 set thing
[lindex
$i 3]
1966 set params
[list
$type]
1968 set u
[string first
"&" [string map
{&& x
} $name]]
1969 lappend params
-label [string map
{&& & & {}} $name]
1971 lappend params
-underline $u
1976 set submenu
[string tolower
[string map
{& ""} [lindex
$i 1]]]
1977 lappend params
-menu $m.
$submenu
1980 lappend params
-command $thing
1983 lappend params
-variable [lindex
$thing 0] \
1984 -value [lindex
$thing 1]
1987 set tail [lrange
$i 4 end
]
1988 regsub
-all {\yMeta1\y
} $tail $Meta1 tail
1989 eval $m add
$params $tail
1990 if {$type eq
"cascade"} {
1991 makemenu
$m.
$submenu $thing
1996 # translate string and remove ampersands
1998 return [string map
{&& & & {}} [mc
$str]]
2001 proc makedroplist
{w varname args
} {
2005 foreach label
$args {
2006 set cx
[string length
$label]
2007 if {$cx > $width} {set width
$cx}
2009 set gm
[ttk
::combobox
$w -width $width -state readonly\
2010 -textvariable $varname -values $args]
2012 set gm
[eval [linsert
$args 0 tk_optionMenu
$w $varname]]
2017 proc makewindow
{} {
2018 global canv canv2 canv3 linespc charspc ctext cflist cscroll
2020 global findtype findtypemenu findloc findstring fstring geometry
2021 global entries sha1entry sha1string sha1but
2022 global diffcontextstring diffcontext
2024 global maincursor textcursor curtextcursor
2025 global rowctxmenu fakerowmenu mergemax wrapcomment
2026 global highlight_files gdttype
2027 global searchstring sstring
2028 global bgcolor fgcolor bglist fglist diffcolors selectbgcolor
2029 global headctxmenu progresscanv progressitem progresscoords statusw
2030 global fprogitem fprogcoord lastprogupdate progupdatepending
2031 global rprogitem rprogcoord rownumsel numcommits
2032 global have_tk85 use_ttk NS
2036 # The "mc" arguments here are purely so that xgettext
2037 # sees the following string as needing to be translated
2040 {mc
"Update" command updatecommits
-accelerator F5
}
2041 {mc
"Reload" command reloadcommits
-accelerator Shift-F5
}
2042 {mc
"Reread references" command rereadrefs
}
2043 {mc
"List references" command showrefs
-accelerator F2
}
2045 {mc
"Start git gui" command {exec git gui
&}}
2047 {mc
"Quit" command doquit
-accelerator Meta1-Q
}
2051 {mc
"Preferences" command doprefs
}
2055 {mc
"New view..." command {newview
0} -accelerator Shift-F4
}
2056 {mc
"Edit view..." command editview
-state disabled
-accelerator F4
}
2057 {mc
"Delete view" command delview
-state disabled
}
2059 {mc
"All files" radiobutton
{selectedview
0} -command {showview
0}}
2061 if {[tk windowingsystem
] ne
"aqua"} {
2064 {mc
"About gitk" command about
}
2065 {mc
"Key bindings" command keys
}
2067 set bar
[list
$file $edit $view $help]
2069 proc
::tk
::mac
::ShowPreferences
{} {doprefs
}
2070 proc
::tk
::mac
::Quit
{} {doquit
}
2071 lset
file end
[lreplace
[lindex
$file end
] end-1 end
]
2073 xx
"Apple" cascade
{
2074 {mc
"About gitk" command about
}
2079 {mc
"Key bindings" command keys
}
2081 set bar
[list
$apple $file $view $help]
2084 . configure
-menu .bar
2087 # cover the non-themed toplevel with a themed frame.
2088 place
[ttk
::frame ._main_background
] -x 0 -y 0 -relwidth 1 -relheight 1
2091 # the gui has upper and lower half, parts of a paned window.
2092 ${NS}::panedwindow .ctop
-orient vertical
2094 # possibly use assumed geometry
2095 if {![info exists geometry
(pwsash0
)]} {
2096 set geometry
(topheight
) [expr {15 * $linespc}]
2097 set geometry
(topwidth
) [expr {80 * $charspc}]
2098 set geometry
(botheight
) [expr {15 * $linespc}]
2099 set geometry
(botwidth
) [expr {50 * $charspc}]
2100 set geometry
(pwsash0
) [list
[expr {40 * $charspc}] 2]
2101 set geometry
(pwsash1
) [list
[expr {60 * $charspc}] 2]
2104 # the upper half will have a paned window, a scroll bar to the right, and some stuff below
2105 ${NS}::frame .tf
-height $geometry(topheight
) -width $geometry(topwidth
)
2106 ${NS}::frame .tf.histframe
2107 ${NS}::panedwindow .tf.histframe.pwclist
-orient horizontal
2109 .tf.histframe.pwclist configure
-sashpad 0 -handlesize 4
2112 # create three canvases
2113 set cscroll .tf.histframe.csb
2114 set canv .tf.histframe.pwclist.canv
2116 -selectbackground $selectbgcolor \
2117 -background $bgcolor -bd 0 \
2118 -yscrollincr $linespc -yscrollcommand "scrollcanv $cscroll"
2119 .tf.histframe.pwclist add
$canv
2120 set canv2 .tf.histframe.pwclist.canv2
2122 -selectbackground $selectbgcolor \
2123 -background $bgcolor -bd 0 -yscrollincr $linespc
2124 .tf.histframe.pwclist add
$canv2
2125 set canv3 .tf.histframe.pwclist.canv3
2127 -selectbackground $selectbgcolor \
2128 -background $bgcolor -bd 0 -yscrollincr $linespc
2129 .tf.histframe.pwclist add
$canv3
2131 bind .tf.histframe.pwclist
<Map
> {
2133 .tf.histframe.pwclist sashpos
1 [lindex $
::geometry
(pwsash1
) 0]
2134 .tf.histframe.pwclist sashpos
0 [lindex $
::geometry
(pwsash0
) 0]
2137 eval .tf.histframe.pwclist sash place
0 $geometry(pwsash0
)
2138 eval .tf.histframe.pwclist sash place
1 $geometry(pwsash1
)
2141 # a scroll bar to rule them
2142 ${NS}::scrollbar
$cscroll -command {allcanvs yview
}
2143 if {!$use_ttk} {$cscroll configure
-highlightthickness 0}
2144 pack
$cscroll -side right
-fill y
2145 bind .tf.histframe.pwclist
<Configure
> {resizeclistpanes
%W
%w
}
2146 lappend bglist
$canv $canv2 $canv3
2147 pack .tf.histframe.pwclist
-fill both
-expand 1 -side left
2149 # we have two button bars at bottom of top frame. Bar 1
2150 ${NS}::frame .tf.bar
2151 ${NS}::frame .tf.lbar
-height 15
2153 set sha1entry .tf.bar.sha1
2154 set entries
$sha1entry
2155 set sha1but .tf.bar.sha1label
2156 button
$sha1but -text "[mc "SHA1 ID
:"] " -state disabled
-relief flat \
2157 -command gotocommit
-width 8
2158 $sha1but conf
-disabledforeground [$sha1but cget
-foreground]
2159 pack .tf.bar.sha1label
-side left
2160 ${NS}::entry
$sha1entry -width 40 -font textfont
-textvariable sha1string
2161 trace add variable sha1string
write sha1change
2162 pack
$sha1entry -side left
-pady 2
2165 #define left_width 16
2166 #define left_height 16
2167 static unsigned char left_bits
[] = {
2168 0x00, 0x00, 0xc0, 0x01, 0xe0, 0x00, 0x70, 0x00, 0x38, 0x00, 0x1c, 0x00,
2169 0x0e, 0x00, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0x0e, 0x00, 0x1c, 0x00,
2170 0x38, 0x00, 0x70, 0x00, 0xe0, 0x00, 0xc0, 0x01};
2173 #define right_width 16
2174 #define right_height 16
2175 static unsigned char right_bits
[] = {
2176 0x00, 0x00, 0xc0, 0x01, 0x80, 0x03, 0x00, 0x07, 0x00, 0x0e, 0x00, 0x1c,
2177 0x00, 0x38, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0x00, 0x38, 0x00, 0x1c,
2178 0x00, 0x0e, 0x00, 0x07, 0x80, 0x03, 0xc0, 0x01};
2180 image create bitmap bm-left
-data $bm_left_data
2181 image create bitmap bm-left-gray
-data $bm_left_data -foreground "#999"
2182 image create bitmap bm-right
-data $bm_right_data
2183 image create bitmap bm-right-gray
-data $bm_right_data -foreground "#999"
2185 ${NS}::button .tf.bar.leftbut
-image [list bm-left disabled bm-left-gray
] \
2186 -command goback
-state disabled
-width 26
2187 pack .tf.bar.leftbut
-side left
-fill y
2188 ${NS}::button .tf.bar.rightbut
-image [list bm-right disabled bm-right-gray
] \
2189 -command goforw
-state disabled
-width 26
2190 pack .tf.bar.rightbut
-side left
-fill y
2192 ${NS}::label .tf.bar.rowlabel
-text [mc
"Row"]
2194 ${NS}::label .tf.bar.rownum
-width 7 -textvariable rownumsel \
2195 -relief sunken
-anchor e
2196 ${NS}::label .tf.bar.rowlabel2
-text "/"
2197 ${NS}::label .tf.bar.numcommits
-width 7 -textvariable numcommits \
2198 -relief sunken
-anchor e
2199 pack .tf.bar.rowlabel .tf.bar.rownum .tf.bar.rowlabel2 .tf.bar.numcommits \
2202 foreach w
{rownum numcommits
} {.tf.bar.
$w configure
-font textfont
}
2205 trace add variable selectedline
write selectedline_change
2207 # Status label and progress bar
2208 set statusw .tf.bar.status
2209 ${NS}::label
$statusw -width 15 -relief sunken
2210 pack
$statusw -side left
-padx 5
2212 set progresscanv
[ttk
::progressbar .tf.bar.progress
]
2214 set h
[expr {[font metrics uifont
-linespace] + 2}]
2215 set progresscanv .tf.bar.progress
2216 canvas
$progresscanv -relief sunken
-height $h -borderwidth 2
2217 set progressitem
[$progresscanv create rect
-1 0 0 $h -fill green
]
2218 set fprogitem
[$progresscanv create rect
-1 0 0 $h -fill yellow
]
2219 set rprogitem
[$progresscanv create rect
-1 0 0 $h -fill red
]
2221 pack
$progresscanv -side right
-expand 1 -fill x
-padx {0 2}
2222 set progresscoords
{0 0}
2225 bind $progresscanv <Configure
> adjustprogress
2226 set lastprogupdate
[clock clicks
-milliseconds]
2227 set progupdatepending
0
2229 # build up the bottom bar of upper window
2230 ${NS}::label .tf.lbar.flabel
-text "[mc "Find
"] "
2231 ${NS}::button .tf.lbar.fnext
-text [mc
"next"] -command {dofind
1 1}
2232 ${NS}::button .tf.lbar.fprev
-text [mc
"prev"] -command {dofind
-1 1}
2233 ${NS}::label .tf.lbar.flab2
-text " [mc "commit
"] "
2234 pack .tf.lbar.flabel .tf.lbar.fnext .tf.lbar.fprev .tf.lbar.flab2 \
2236 set gdttype
[mc
"containing:"]
2237 set gm
[makedroplist .tf.lbar.gdttype gdttype \
2238 [mc
"containing:"] \
2239 [mc
"touching paths:"] \
2240 [mc
"adding/removing string:"]]
2241 trace add variable gdttype
write gdttype_change
2242 pack .tf.lbar.gdttype
-side left
-fill y
2245 set fstring .tf.lbar.findstring
2246 lappend entries
$fstring
2247 ${NS}::entry
$fstring -width 30 -textvariable findstring
2248 trace add variable findstring
write find_change
2249 set findtype
[mc
"Exact"]
2250 set findtypemenu
[makedroplist .tf.lbar.findtype \
2251 findtype
[mc
"Exact"] [mc
"IgnCase"] [mc
"Regexp"]]
2252 trace add variable findtype
write findcom_change
2253 set findloc
[mc
"All fields"]
2254 makedroplist .tf.lbar.findloc findloc
[mc
"All fields"] [mc
"Headline"] \
2255 [mc
"Comments"] [mc
"Author"] [mc
"Committer"]
2256 trace add variable findloc
write find_change
2257 pack .tf.lbar.findloc
-side right
2258 pack .tf.lbar.findtype
-side right
2259 pack
$fstring -side left
-expand 1 -fill x
2261 # Finish putting the upper half of the viewer together
2262 pack .tf.lbar
-in .tf
-side bottom
-fill x
2263 pack .tf.bar
-in .tf
-side bottom
-fill x
2264 pack .tf.histframe
-fill both
-side top
-expand 1
2267 .ctop paneconfigure .tf
-height $geometry(topheight
)
2268 .ctop paneconfigure .tf
-width $geometry(topwidth
)
2271 # now build up the bottom
2272 ${NS}::panedwindow .pwbottom
-orient horizontal
2274 # lower left, a text box over search bar, scroll bar to the right
2275 # if we know window height, then that will set the lower text height, otherwise
2276 # we set lower text height which will drive window height
2277 if {[info exists geometry
(main
)]} {
2278 ${NS}::frame .bleft
-width $geometry(botwidth
)
2280 ${NS}::frame .bleft
-width $geometry(botwidth
) -height $geometry(botheight
)
2282 ${NS}::frame .bleft.top
2283 ${NS}::frame .bleft.mid
2284 ${NS}::frame .bleft.bottom
2286 ${NS}::button .bleft.top.search
-text [mc
"Search"] -command dosearch
2287 pack .bleft.top.search
-side left
-padx 5
2288 set sstring .bleft.top.sstring
2290 ${NS}::entry
$sstring -width 20 -textvariable searchstring
2291 lappend entries
$sstring
2292 trace add variable searchstring
write incrsearch
2293 pack
$sstring -side left
-expand 1 -fill x
2294 ${NS}::radiobutton .bleft.mid.
diff -text [mc
"Diff"] \
2295 -command changediffdisp
-variable diffelide
-value {0 0}
2296 ${NS}::radiobutton .bleft.mid.old
-text [mc
"Old version"] \
2297 -command changediffdisp
-variable diffelide
-value {0 1}
2298 ${NS}::radiobutton .bleft.mid.new
-text [mc
"New version"] \
2299 -command changediffdisp
-variable diffelide
-value {1 0}
2300 ${NS}::label .bleft.mid.labeldiffcontext
-text " [mc "Lines of context
"]: "
2301 pack .bleft.mid.
diff .bleft.mid.old .bleft.mid.new
-side left
2302 spinbox .bleft.mid.diffcontext
-width 5 \
2303 -from 0 -increment 1 -to 10000000 \
2304 -validate all
-validatecommand "diffcontextvalidate %P" \
2305 -textvariable diffcontextstring
2306 .bleft.mid.diffcontext
set $diffcontext
2307 trace add variable diffcontextstring
write diffcontextchange
2308 lappend entries .bleft.mid.diffcontext
2309 pack .bleft.mid.labeldiffcontext .bleft.mid.diffcontext
-side left
2310 ${NS}::checkbutton .bleft.mid.ignspace
-text [mc
"Ignore space change"] \
2311 -command changeignorespace
-variable ignorespace
2312 pack .bleft.mid.ignspace
-side left
-padx 5
2314 set worddiff
[mc
"Line diff"]
2315 if {[package vcompare
$git_version "1.7.2"] >= 0} {
2316 makedroplist .bleft.mid.worddiff worddiff
[mc
"Line diff"] \
2317 [mc
"Markup words"] [mc
"Color words"]
2318 trace add variable worddiff
write changeworddiff
2319 pack .bleft.mid.worddiff
-side left
-padx 5
2322 set ctext .bleft.bottom.ctext
2323 text
$ctext -background $bgcolor -foreground $fgcolor \
2324 -state disabled
-font textfont \
2325 -yscrollcommand scrolltext
-wrap none \
2326 -xscrollcommand ".bleft.bottom.sbhorizontal set"
2328 $ctext conf
-tabstyle wordprocessor
2330 ${NS}::scrollbar .bleft.bottom.sb
-command "$ctext yview"
2331 ${NS}::scrollbar .bleft.bottom.sbhorizontal
-command "$ctext xview" -orient h
2332 pack .bleft.top
-side top
-fill x
2333 pack .bleft.mid
-side top
-fill x
2334 grid
$ctext .bleft.bottom.sb
-sticky nsew
2335 grid .bleft.bottom.sbhorizontal
-sticky ew
2336 grid columnconfigure .bleft.bottom
0 -weight 1
2337 grid rowconfigure .bleft.bottom
0 -weight 1
2338 grid rowconfigure .bleft.bottom
1 -weight 0
2339 pack .bleft.bottom
-side top
-fill both
-expand 1
2340 lappend bglist
$ctext
2341 lappend fglist
$ctext
2343 $ctext tag conf comment
-wrap $wrapcomment
2344 $ctext tag conf filesep
-font textfontbold
-back "#aaaaaa"
2345 $ctext tag conf hunksep
-fore [lindex
$diffcolors 2]
2346 $ctext tag conf d0
-fore [lindex
$diffcolors 0]
2347 $ctext tag conf dresult
-fore [lindex
$diffcolors 1]
2348 $ctext tag conf m0
-fore red
2349 $ctext tag conf m1
-fore blue
2350 $ctext tag conf m2
-fore green
2351 $ctext tag conf m3
-fore purple
2352 $ctext tag conf
m4 -fore brown
2353 $ctext tag conf m5
-fore "#009090"
2354 $ctext tag conf m6
-fore magenta
2355 $ctext tag conf m7
-fore "#808000"
2356 $ctext tag conf m8
-fore "#009000"
2357 $ctext tag conf m9
-fore "#ff0080"
2358 $ctext tag conf m10
-fore cyan
2359 $ctext tag conf m11
-fore "#b07070"
2360 $ctext tag conf m12
-fore "#70b0f0"
2361 $ctext tag conf m13
-fore "#70f0b0"
2362 $ctext tag conf m14
-fore "#f0b070"
2363 $ctext tag conf m15
-fore "#ff70b0"
2364 $ctext tag conf mmax
-fore darkgrey
2366 $ctext tag conf mresult
-font textfontbold
2367 $ctext tag conf msep
-font textfontbold
2368 $ctext tag conf found
-back yellow
2369 $ctext tag conf currentsearchhit
-back orange
2371 .pwbottom add .bleft
2373 .pwbottom paneconfigure .bleft
-width $geometry(botwidth
)
2377 ${NS}::frame .bright
2378 ${NS}::frame .bright.mode
2379 ${NS}::radiobutton .bright.mode.
patch -text [mc
"Patch"] \
2380 -command reselectline
-variable cmitmode
-value "patch"
2381 ${NS}::radiobutton .bright.mode.tree
-text [mc
"Tree"] \
2382 -command reselectline
-variable cmitmode
-value "tree"
2383 grid .bright.mode.
patch .bright.mode.tree
-sticky ew
2384 pack .bright.mode
-side top
-fill x
2385 set cflist .bright.cfiles
2386 set indent
[font measure mainfont
"nn"]
2388 -selectbackground $selectbgcolor \
2389 -background $bgcolor -foreground $fgcolor \
2391 -tabs [list
$indent [expr {2 * $indent}]] \
2392 -yscrollcommand ".bright.sb set" \
2393 -cursor [. cget
-cursor] \
2394 -spacing1 1 -spacing3 1
2395 lappend bglist
$cflist
2396 lappend fglist
$cflist
2397 ${NS}::scrollbar .bright.sb
-command "$cflist yview"
2398 pack .bright.sb
-side right
-fill y
2399 pack
$cflist -side left
-fill both
-expand 1
2400 $cflist tag configure highlight \
2401 -background [$cflist cget
-selectbackground]
2402 $cflist tag configure bold
-font mainfontbold
2404 .pwbottom add .bright
2407 # restore window width & height if known
2408 if {[info exists geometry
(main
)]} {
2409 if {[scan
$geometry(main
) "%dx%d" w h
] >= 2} {
2410 if {$w > [winfo screenwidth .
]} {
2411 set w
[winfo screenwidth .
]
2413 if {$h > [winfo screenheight .
]} {
2414 set h
[winfo screenheight .
]
2416 wm geometry .
"${w}x$h"
2420 if {[info exists geometry
(state
)] && $geometry(state
) eq
"zoomed"} {
2421 wm state .
$geometry(state
)
2424 if {[tk windowingsystem
] eq
{aqua
}} {
2435 %W sashpos
0 $
::geometry
(topheight
)
2437 bind .pwbottom
<Map
> {
2439 %W sashpos
0 $
::geometry
(botwidth
)
2443 bind .pwbottom
<Configure
> {resizecdetpanes
%W
%w
}
2444 pack .ctop
-fill both
-expand 1
2445 bindall
<1> {selcanvline
%W
%x
%y
}
2446 #bindall <B1-Motion> {selcanvline %W %x %y}
2447 if {[tk windowingsystem
] == "win32"} {
2448 bind .
<MouseWheel
> { windows_mousewheel_redirector
%W
%X
%Y
%D
}
2449 bind $ctext <MouseWheel
> { windows_mousewheel_redirector
%W
%X
%Y
%D
; break }
2451 bindall
<ButtonRelease-4
> "allcanvs yview scroll -5 units"
2452 bindall
<ButtonRelease-5
> "allcanvs yview scroll 5 units"
2453 if {[tk windowingsystem
] eq
"aqua"} {
2454 bindall
<MouseWheel
> {
2455 set delta
[expr {- (%D
)}]
2456 allcanvs yview scroll
$delta units
2458 bindall
<Shift-MouseWheel
> {
2459 set delta
[expr {- (%D
)}]
2460 $canv xview scroll
$delta units
2464 bindall
<$
::BM
> "canvscan mark %W %x %y"
2465 bindall
<B$
::BM-Motion
> "canvscan dragto %W %x %y"
2466 bind all
<$M1B-Key-w> {destroy
[winfo toplevel
%W
]}
2467 bind .
<$M1B-Key-w> doquit
2468 bindkey
<Home
> selfirstline
2469 bindkey
<End
> sellastline
2470 bind .
<Key-Up
> "selnextline -1"
2471 bind .
<Key-Down
> "selnextline 1"
2472 bind .
<Shift-Key-Up
> "dofind -1 0"
2473 bind .
<Shift-Key-Down
> "dofind 1 0"
2474 bindkey
<Key-Right
> "goforw"
2475 bindkey
<Key-Left
> "goback"
2476 bind .
<Key-Prior
> "selnextpage -1"
2477 bind .
<Key-Next
> "selnextpage 1"
2478 bind .
<$M1B-Home> "allcanvs yview moveto 0.0"
2479 bind .
<$M1B-End> "allcanvs yview moveto 1.0"
2480 bind .
<$M1B-Key-Up> "allcanvs yview scroll -1 units"
2481 bind .
<$M1B-Key-Down> "allcanvs yview scroll 1 units"
2482 bind .
<$M1B-Key-Prior> "allcanvs yview scroll -1 pages"
2483 bind .
<$M1B-Key-Next> "allcanvs yview scroll 1 pages"
2484 bindkey
<Key-Delete
> "$ctext yview scroll -1 pages"
2485 bindkey
<Key-BackSpace
> "$ctext yview scroll -1 pages"
2486 bindkey
<Key-space
> "$ctext yview scroll 1 pages"
2487 bindkey p
"selnextline -1"
2488 bindkey n
"selnextline 1"
2491 bindkey k
"selnextline -1"
2492 bindkey j
"selnextline 1"
2496 bindkey d
"$ctext yview scroll 18 units"
2497 bindkey u
"$ctext yview scroll -18 units"
2498 bindkey
/ {focus
$fstring}
2499 bindkey
<Key-KP_Divide
> {focus
$fstring}
2500 bindkey
<Key-Return
> {dofind
1 1}
2501 bindkey ?
{dofind
-1 1}
2503 bind .
<F5
> updatecommits
2504 bind .
<Shift-F5
> reloadcommits
2505 bind .
<F2
> showrefs
2506 bindmodfunctionkey Shift
4 {newview
0}
2507 bind .
<F4
> edit_or_newview
2508 bind .
<$M1B-q> doquit
2509 bind .
<$M1B-f> {dofind
1 1}
2510 bind .
<$M1B-g> {dofind
1 0}
2511 bind .
<$M1B-r> dosearchback
2512 bind .
<$M1B-s> dosearch
2513 bind .
<$M1B-equal> {incrfont
1}
2514 bind .
<$M1B-plus> {incrfont
1}
2515 bind .
<$M1B-KP_Add> {incrfont
1}
2516 bind .
<$M1B-minus> {incrfont
-1}
2517 bind .
<$M1B-KP_Subtract> {incrfont
-1}
2518 wm protocol . WM_DELETE_WINDOW doquit
2519 bind .
<Destroy
> {stop_backends
}
2520 bind .
<Button-1
> "click %W"
2521 bind $fstring <Key-Return
> {dofind
1 1}
2522 bind $sha1entry <Key-Return
> {gotocommit
; break}
2523 bind $sha1entry <<PasteSelection>> clearsha1
2524 bind $cflist <1> {sel_flist %W %x %y; break}
2525 bind $cflist <B1-Motion> {sel_flist %W %x %y; break}
2526 bind $cflist <ButtonRelease-1> {treeclick %W %x %y}
2528 bind $cflist $ctxbut {pop_flist_menu %W %X %Y %x %y}
2529 bind $ctext $ctxbut {pop_diff_menu %W %X %Y %x %y}
2530 bind $ctext <Button-1> {focus %W}
2531 bind $ctext <<Selection>> rehighlight_search_results
2533 set maincursor [. cget -cursor]
2534 set textcursor [$ctext cget -cursor]
2535 set curtextcursor $textcursor
2537 set rowctxmenu .rowctxmenu
2538 makemenu $rowctxmenu {
2539 {mc "Diff this -> selected" command {diffvssel 0}}
2540 {mc "Diff selected -> this" command {diffvssel 1}}
2541 {mc "Make patch" command mkpatch}
2542 {mc "Create tag" command mktag}
2543 {mc "Write commit to file" command writecommit}
2544 {mc "Create new branch" command mkbranch}
2545 {mc "Cherry-pick this commit" command cherrypick}
2546 {mc "Reset HEAD branch to here" command resethead}
2547 {mc "Mark this commit" command markhere}
2548 {mc "Return to mark" command gotomark}
2549 {mc "Find descendant of this and mark" command find_common_desc}
2550 {mc "Compare with marked commit" command compare_commits}
2551 {mc "Diff this -> marked commit" command {diffvsmark 0}}
2552 {mc "Diff marked commit -> this" command {diffvsmark 1}}
2554 $rowctxmenu configure -tearoff 0
2556 set fakerowmenu .fakerowmenu
2557 makemenu $fakerowmenu {
2558 {mc "Diff this -> selected" command {diffvssel 0}}
2559 {mc "Diff selected -> this" command {diffvssel 1}}
2560 {mc "Make patch" command mkpatch}
2561 {mc "Diff this -> marked commit" command {diffvsmark 0}}
2562 {mc "Diff marked commit -> this" command {diffvsmark 1}}
2564 $fakerowmenu configure -tearoff 0
2566 set headctxmenu .headctxmenu
2567 makemenu $headctxmenu {
2568 {mc "Check out this branch" command cobranch}
2569 {mc "Remove this branch" command rmbranch}
2571 $headctxmenu configure -tearoff 0
2574 set flist_menu .flistctxmenu
2575 makemenu $flist_menu {
2576 {mc "Highlight this too" command {flist_hl 0}}
2577 {mc "Highlight this only" command {flist_hl 1}}
2578 {mc "External diff" command {external_diff}}
2579 {mc "Blame parent commit" command {external_blame 1}}
2581 $flist_menu configure -tearoff 0
2584 set diff_menu .diffctxmenu
2585 makemenu $diff_menu {
2586 {mc "Show origin of this line" command show_line_source}
2587 {mc "Run git gui blame on this line" command {external_blame_diff}}
2589 $diff_menu configure -tearoff 0
2592 # Windows sends all mouse wheel events to the current focused window, not
2593 # the one where the mouse hovers, so bind those events here and redirect
2594 # to the correct window
2595 proc windows_mousewheel_redirector {W X Y D} {
2596 global canv canv2 canv3
2597 set w [winfo containing -displayof $W $X $Y]
2599 set u [expr {$D < 0 ? 5 : -5}]
2600 if {$w == $canv || $w == $canv2 || $w == $canv3} {
2601 allcanvs yview scroll $u units
2604 $w yview scroll $u units
2610 # Update row number label when selectedline changes
2611 proc selectedline_change {n1 n2 op} {
2612 global selectedline rownumsel
2614 if {$selectedline eq {}} {
2617 set rownumsel [expr {$selectedline + 1}]
2621 # mouse-2 makes all windows scan vertically, but only the one
2622 # the cursor is in scans horizontally
2623 proc canvscan {op w x y} {
2624 global canv canv2 canv3
2625 foreach c [list $canv $canv2 $canv3] {
2634 proc scrollcanv {cscroll f0 f1} {
2635 $cscroll set $f0 $f1
2640 # when we make a key binding for the toplevel, make sure
2641 # it doesn't get triggered when that key is pressed in the
2642 # find string entry widget.
2643 proc bindkey {ev script} {
2646 set escript [bind Entry $ev]
2647 if {$escript == {}} {
2648 set escript [bind Entry <Key>]
2650 foreach e $entries {
2651 bind $e $ev "$escript; break"
2655 proc bindmodfunctionkey {mod n script} {
2656 bind . <$mod-F$n> $script
2657 catch { bind . <$mod-XF86_Switch_VT_$n> $script }
2660 # set the focus back to the toplevel for any click outside
2663 global ctext entries
2664 foreach e [concat $entries $ctext] {
2665 if {$w == $e} return
2670 # Adjust the progress bar for a change in requested extent or canvas size
2671 proc adjustprogress {} {
2672 global progresscanv progressitem progresscoords
2673 global fprogitem fprogcoord lastprogupdate progupdatepending
2674 global rprogitem rprogcoord use_ttk
2677 $progresscanv configure -value [expr {int($fprogcoord * 100)}]
2681 set w [expr {[winfo width $progresscanv] - 4}]
2682 set x0 [expr {$w * [lindex $progresscoords 0]}]
2683 set x1 [expr {$w * [lindex $progresscoords 1]}]
2684 set h [winfo height $progresscanv]
2685 $progresscanv coords $progressitem $x0 0 $x1 $h
2686 $progresscanv coords $fprogitem 0 0 [expr {$w * $fprogcoord}] $h
2687 $progresscanv coords $rprogitem 0 0 [expr {$w * $rprogcoord}] $h
2688 set now [clock clicks -milliseconds]
2689 if {$now >= $lastprogupdate + 100} {
2690 set progupdatepending 0
2692 } elseif {!$progupdatepending} {
2693 set progupdatepending 1
2694 after [expr {$lastprogupdate + 100 - $now}] doprogupdate
2698 proc doprogupdate {} {
2699 global lastprogupdate progupdatepending
2701 if {$progupdatepending} {
2702 set progupdatepending 0
2703 set lastprogupdate [clock clicks -milliseconds]
2708 proc savestuff {w} {
2709 global canv canv2 canv3 mainfont textfont uifont tabstop
2710 global stuffsaved findmergefiles maxgraphpct
2711 global maxwidth showneartags showlocalchanges
2712 global viewname viewfiles viewargs viewargscmd viewperm nextviewnum
2713 global cmitmode wrapcomment datetimeformat limitdiffs
2714 global colors uicolor bgcolor fgcolor diffcolors diffcontext selectbgcolor
2715 global autoselect autosellen extdifftool perfile_attrs markbgcolor use_ttk
2716 global hideremotes want_ttk
2718 if {$stuffsaved} return
2719 if {![winfo viewable .]} return
2721 if {[file exists ~/.gitk-new]} {file delete -force ~/.gitk-new}
2722 set f [open "~/.gitk-new" w]
2723 if {$::tcl_platform(platform) eq {windows}} {
2724 file attributes "~/.gitk-new" -hidden true
2726 puts $f [list set mainfont $mainfont]
2727 puts $f [list set textfont $textfont]
2728 puts $f [list set uifont $uifont]
2729 puts $f [list set tabstop $tabstop]
2730 puts $f [list set findmergefiles $findmergefiles]
2731 puts $f [list set maxgraphpct $maxgraphpct]
2732 puts $f [list set maxwidth $maxwidth]
2733 puts $f [list set cmitmode $cmitmode]
2734 puts $f [list set wrapcomment $wrapcomment]
2735 puts $f [list set autoselect $autoselect]
2736 puts $f [list set autosellen $autosellen]
2737 puts $f [list set showneartags $showneartags]
2738 puts $f [list set hideremotes $hideremotes]
2739 puts $f [list set showlocalchanges $showlocalchanges]
2740 puts $f [list set datetimeformat $datetimeformat]
2741 puts $f [list set limitdiffs $limitdiffs]
2742 puts $f [list set uicolor $uicolor]
2743 puts $f [list set want_ttk $want_ttk]
2744 puts $f [list set bgcolor $bgcolor]
2745 puts $f [list set fgcolor $fgcolor]
2746 puts $f [list set colors $colors]
2747 puts $f [list set diffcolors $diffcolors]
2748 puts $f [list set markbgcolor $markbgcolor]
2749 puts $f [list set diffcontext $diffcontext]
2750 puts $f [list set selectbgcolor $selectbgcolor]
2751 puts $f [list set extdifftool $extdifftool]
2752 puts $f [list set perfile_attrs $perfile_attrs]
2754 puts $f "set geometry(main) [wm geometry .]"
2755 puts $f "set geometry(state) [wm state .]"
2756 puts $f "set geometry(topwidth) [winfo width .tf]"
2757 puts $f "set geometry(topheight) [winfo height .tf]"
2759 puts $f "set geometry(pwsash0) \"[.tf.histframe.pwclist sashpos 0] 1\""
2760 puts $f "set geometry(pwsash1) \"[.tf.histframe.pwclist sashpos 1] 1\""
2762 puts $f "set geometry(pwsash0) \"[.tf.histframe.pwclist sash coord 0]\""
2763 puts $f "set geometry(pwsash1) \"[.tf.histframe.pwclist sash coord 1]\""
2765 puts $f "set geometry(botwidth) [winfo width .bleft]"
2766 puts $f "set geometry(botheight) [winfo height .bleft]"
2768 puts -nonewline $f "set permviews {"
2769 for {set v 0} {$v < $nextviewnum} {incr v} {
2770 if {$viewperm($v)} {
2771 puts $f "{[list $viewname($v) $viewfiles($v) $viewargs($v) $viewargscmd($v)]}"
2776 file rename -force "~/.gitk-new" "~/.gitk"
2781 proc resizeclistpanes {win w} {
2782 global oldwidth use_ttk
2783 if {[info exists oldwidth($win)]} {
2785 set s0 [$win sashpos 0]
2786 set s1 [$win sashpos 1]
2788 set s0 [$win sash coord 0]
2789 set s1 [$win sash coord 1]
2792 set sash0 [expr {int($w/2 - 2)}]
2793 set sash1 [expr {int($w*5/6 - 2)}]
2795 set factor [expr {1.0 * $w / $oldwidth($win)}]
2796 set sash0 [expr {int($factor * [lindex $s0 0])}]
2797 set sash1 [expr {int($factor * [lindex $s1 0])}]
2801 if {$sash1 < $sash0 + 20} {
2802 set sash1 [expr {$sash0 + 20}]
2804 if {$sash1 > $w - 10} {
2805 set sash1 [expr {$w - 10}]
2806 if {$sash0 > $sash1 - 20} {
2807 set sash0 [expr {$sash1 - 20}]
2812 $win sashpos 0 $sash0
2813 $win sashpos 1 $sash1
2815 $win sash place 0 $sash0 [lindex $s0 1]
2816 $win sash place 1 $sash1 [lindex $s1 1]
2819 set oldwidth($win) $w
2822 proc resizecdetpanes {win w} {
2823 global oldwidth use_ttk
2824 if {[info exists oldwidth($win)]} {
2826 set s0 [$win sashpos 0]
2828 set s0 [$win sash coord 0]
2831 set sash0 [expr {int($w*3/4 - 2)}]
2833 set factor [expr {1.0 * $w / $oldwidth($win)}]
2834 set sash0 [expr {int($factor * [lindex $s0 0])}]
2838 if {$sash0 > $w - 15} {
2839 set sash0 [expr {$w - 15}]
2843 $win sashpos 0 $sash0
2845 $win sash place 0 $sash0 [lindex $s0 1]
2848 set oldwidth($win) $w
2851 proc allcanvs args {
2852 global canv canv2 canv3
2858 proc bindall {event action} {
2859 global canv canv2 canv3
2860 bind $canv $event $action
2861 bind $canv2 $event $action
2862 bind $canv3 $event $action
2868 if {[winfo exists $w]} {
2873 wm title $w [mc "About gitk"]
2875 message $w.m -text [mc "
2876 Gitk - a commit viewer for git
2878 Copyright \u00a9 2005-2011 Paul Mackerras
2880 Use and redistribute under the terms of the GNU General Public License"] \
2881 -justify center -aspect 400 -border 2 -bg white -relief groove
2882 pack $w.m -side top -fill x -padx 2 -pady 2
2883 ${NS}::button $w.ok -text [mc "Close"] -command "destroy $w" -default active
2884 pack $w.ok -side bottom
2885 bind $w <Visibility> "focus $w.ok"
2886 bind $w <Key-Escape> "destroy $w"
2887 bind $w <Key-Return> "destroy $w"
2888 tk::PlaceWindow $w widget .
2894 if {[winfo exists $w]} {
2898 if {[tk windowingsystem] eq {aqua}} {
2904 wm title $w [mc "Gitk key bindings"]
2906 message $w.m -text "
2907 [mc "Gitk key bindings:"]
2909 [mc "<%s-Q> Quit" $M1T]
2910 [mc "<%s-W> Close window" $M1T]
2911 [mc "<Home> Move to first commit"]
2912 [mc "<End> Move to last commit"]
2913 [mc "<Up>, p, k Move up one commit"]
2914 [mc "<Down>, n, j Move down one commit"]
2915 [mc "<Left>, z, h Go back in history list"]
2916 [mc "<Right>, x, l Go forward in history list"]
2917 [mc "<PageUp> Move up one page in commit list"]
2918 [mc "<PageDown> Move down one page in commit list"]
2919 [mc "<%s-Home> Scroll to top of commit list" $M1T]
2920 [mc "<%s-End> Scroll to bottom of commit list" $M1T]
2921 [mc "<%s-Up> Scroll commit list up one line" $M1T]
2922 [mc "<%s-Down> Scroll commit list down one line" $M1T]
2923 [mc "<%s-PageUp> Scroll commit list up one page" $M1T]
2924 [mc "<%s-PageDown> Scroll commit list down one page" $M1T]
2925 [mc "<Shift-Up> Find backwards (upwards, later commits)"]
2926 [mc "<Shift-Down> Find forwards (downwards, earlier commits)"]
2927 [mc "<Delete>, b Scroll diff view up one page"]
2928 [mc "<Backspace> Scroll diff view up one page"]
2929 [mc "<Space> Scroll diff view down one page"]
2930 [mc "u Scroll diff view up 18 lines"]
2931 [mc "d Scroll diff view down 18 lines"]
2932 [mc "<%s-F> Find" $M1T]
2933 [mc "<%s-G> Move to next find hit" $M1T]
2934 [mc "<Return> Move to next find hit"]
2935 [mc "/ Focus the search box"]
2936 [mc "? Move to previous find hit"]
2937 [mc "f Scroll diff view to next file"]
2938 [mc "<%s-S> Search for next hit in diff view" $M1T]
2939 [mc "<%s-R> Search for previous hit in diff view" $M1T]
2940 [mc "<%s-KP+> Increase font size" $M1T]
2941 [mc "<%s-plus> Increase font size" $M1T]
2942 [mc "<%s-KP-> Decrease font size" $M1T]
2943 [mc "<%s-minus> Decrease font size" $M1T]
2946 -justify left -bg white -border 2 -relief groove
2947 pack $w.m -side top -fill both -padx 2 -pady 2
2948 ${NS}::button $w.ok -text [mc "Close"] -command "destroy $w" -default active
2949 bind $w <Key-Escape> [list destroy $w]
2950 pack $w.ok -side bottom
2951 bind $w <Visibility> "focus $w.ok"
2952 bind $w <Key-Escape> "destroy $w"
2953 bind $w <Key-Return> "destroy $w"
2956 # Procedures for manipulating the file list window at the
2957 # bottom right of the overall window.
2959 proc treeview {w l openlevs} {
2960 global treecontents treediropen treeheight treeparent treeindex
2970 set treecontents() {}
2971 $w conf -state normal
2973 while {[string range $f 0 $prefixend] ne $prefix} {
2974 if {$lev <= $openlevs} {
2975 $w mark set e:$treeindex($prefix) "end -1c"
2976 $w mark gravity e:$treeindex($prefix) left
2978 set treeheight($prefix) $ht
2979 incr ht [lindex $htstack end]
2980 set htstack [lreplace $htstack end end]
2981 set prefixend [lindex $prefendstack end]
2982 set prefendstack [lreplace $prefendstack end end]
2983 set prefix [string range $prefix 0 $prefixend]
2986 set tail [string range $f [expr {$prefixend+1}] end]
2987 while {[set slash [string first "/" $tail]] >= 0} {
2990 lappend prefendstack $prefixend
2991 incr prefixend [expr {$slash + 1}]
2992 set d [string range $tail 0 $slash]
2993 lappend treecontents($prefix) $d
2994 set oldprefix $prefix
2996 set treecontents($prefix) {}
2997 set treeindex($prefix) [incr ix]
2998 set treeparent($prefix) $oldprefix
2999 set tail [string range $tail [expr {$slash+1}] end]
3000 if {$lev <= $openlevs} {
3002 set treediropen($prefix) [expr {$lev < $openlevs}]
3003 set bm [expr {$lev == $openlevs? "tri-rt": "tri-dn"}]
3004 $w mark set d:$ix "end -1c"
3005 $w mark gravity d:$ix left
3007 for {set i 0} {$i < $lev} {incr i} {append str "\t"}
3009 $w image create end -align center -image $bm -padx 1 \
3011 $w insert end $d [highlight_tag $prefix]
3012 $w mark set s:$ix "end -1c"
3013 $w mark gravity s:$ix left
3018 if {$lev <= $openlevs} {
3021 for {set i 0} {$i < $lev} {incr i} {append str "\t"}
3023 $w insert end $tail [highlight_tag $f]
3025 lappend treecontents($prefix) $tail
3028 while {$htstack ne {}} {
3029 set treeheight($prefix) $ht
3030 incr ht [lindex $htstack end]
3031 set htstack [lreplace $htstack end end]
3032 set prefixend [lindex $prefendstack end]
3033 set prefendstack [lreplace $prefendstack end end]
3034 set prefix [string range $prefix 0 $prefixend]
3036 $w conf -state disabled
3039 proc linetoelt {l} {
3040 global treeheight treecontents
3045 foreach e $treecontents($prefix) {
3050 if {[string index $e end] eq "/"} {
3051 set n $treeheight($prefix$e)
3063 proc highlight_tree {y prefix} {
3064 global treeheight treecontents cflist
3066 foreach e $treecontents($prefix) {
3068 if {[highlight_tag $path] ne {}} {
3069 $cflist tag add bold $y.0 "$y.0 lineend"
3072 if {[string index $e end] eq "/" && $treeheight($path) > 1} {
3073 set y [highlight_tree $y $path]
3079 proc treeclosedir {w dir} {
3080 global treediropen treeheight treeparent treeindex
3082 set ix $treeindex($dir)
3083 $w conf -state normal
3084 $w delete s:$ix e:$ix
3085 set treediropen($dir) 0
3086 $w image configure a:$ix -image tri-rt
3087 $w conf -state disabled
3088 set n [expr {1 - $treeheight($dir)}]
3089 while {$dir ne {}} {
3090 incr treeheight($dir) $n
3091 set dir $treeparent($dir)
3095 proc treeopendir {w dir} {
3096 global treediropen treeheight treeparent treecontents treeindex
3098 set ix $treeindex($dir)
3099 $w conf -state normal
3100 $w image configure a:$ix -image tri-dn
3101 $w mark set e:$ix s:$ix
3102 $w mark gravity e:$ix right
3105 set n [llength $treecontents($dir)]
3106 for {set x $dir} {$x ne {}} {set x $treeparent($x)} {
3109 incr treeheight($x) $n
3111 foreach e $treecontents($dir) {
3113 if {[string index $e end] eq "/"} {
3114 set iy $treeindex($de)
3115 $w mark set d:$iy e:$ix
3116 $w mark gravity d:$iy left
3117 $w insert e:$ix $str
3118 set treediropen($de) 0
3119 $w image create e:$ix -align center -image tri-rt -padx 1 \
3121 $w insert e:$ix $e [highlight_tag $de]
3122 $w mark set s:$iy e:$ix
3123 $w mark gravity s:$iy left
3124 set treeheight($de) 1
3126 $w insert e:$ix $str
3127 $w insert e:$ix $e [highlight_tag $de]
3130 $w mark gravity e:$ix right
3131 $w conf -state disabled
3132 set treediropen($dir) 1
3133 set top [lindex [split [$w index @0,0] .] 0]
3134 set ht [$w cget -height]
3135 set l [lindex [split [$w index s:$ix] .] 0]
3138 } elseif {$l + $n + 1 > $top + $ht} {
3139 set top [expr {$l + $n + 2 - $ht}]
3147 proc treeclick {w x y} {
3148 global treediropen cmitmode ctext cflist cflist_top
3150 if {$cmitmode ne "tree"} return
3151 if {![info exists cflist_top]} return
3152 set l [lindex [split [$w index "@$x,$y"] "."] 0]
3153 $cflist tag remove highlight $cflist_top.0 "$cflist_top.0 lineend"
3154 $cflist tag add highlight $l.0 "$l.0 lineend"
3160 set e [linetoelt $l]
3161 if {[string index $e end] ne "/"} {
3163 } elseif {$treediropen($e)} {
3170 proc setfilelist {id} {
3171 global treefilelist cflist jump_to_here
3173 treeview $cflist $treefilelist($id) 0
3174 if {$jump_to_here ne {}} {
3175 set f [lindex $jump_to_here 0]
3176 if {[lsearch -exact $treefilelist($id) $f] >= 0} {
3182 image create bitmap tri-rt -background black -foreground blue -data {
3183 #define tri-rt_width 13
3184 #define tri-rt_height 13
3185 static unsigned char tri-rt_bits[] = {
3186 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x30, 0x00, 0x70, 0x00, 0xf0, 0x00,
3187 0xf0, 0x01, 0xf0, 0x00, 0x70, 0x00, 0x30, 0x00, 0x10, 0x00, 0x00, 0x00,
3190 #define tri-rt-mask_width 13
3191 #define tri-rt-mask_height 13
3192 static unsigned char tri-rt-mask_bits[] = {
3193 0x08, 0x00, 0x18, 0x00, 0x38, 0x00, 0x78, 0x00, 0xf8, 0x00, 0xf8, 0x01,
3194 0xf8, 0x03, 0xf8, 0x01, 0xf8, 0x00, 0x78, 0x00, 0x38, 0x00, 0x18, 0x00,
3197 image create bitmap tri-dn -background black -foreground blue -data {
3198 #define tri-dn_width 13
3199 #define tri-dn_height 13
3200 static unsigned char tri-dn_bits[] = {
3201 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x07, 0xf8, 0x03,
3202 0xf0, 0x01, 0xe0, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3205 #define tri-dn-mask_width 13
3206 #define tri-dn-mask_height 13
3207 static unsigned char tri-dn-mask_bits[] = {
3208 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x1f, 0xfe, 0x0f, 0xfc, 0x07,
3209 0xf8, 0x03, 0xf0, 0x01, 0xe0, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
3213 image create bitmap reficon-T -background black -foreground yellow -data {
3214 #define tagicon_width 13
3215 #define tagicon_height 9
3216 static unsigned char tagicon_bits[] = {
3217 0x00, 0x00, 0x00, 0x00, 0xf0, 0x07, 0xf8, 0x07,
3218 0xfc, 0x07, 0xf8, 0x07, 0xf0, 0x07, 0x00, 0x00, 0x00, 0x00};
3220 #define tagicon-mask_width 13
3221 #define tagicon-mask_height 9
3222 static unsigned char tagicon-mask_bits[] = {
3223 0x00, 0x00, 0xf0, 0x0f, 0xf8, 0x0f, 0xfc, 0x0f,
3224 0xfe, 0x0f, 0xfc, 0x0f, 0xf8, 0x0f, 0xf0, 0x0f, 0x00, 0x00};
3227 #define headicon_width 13
3228 #define headicon_height 9
3229 static unsigned char headicon_bits[] = {
3230 0x00, 0x00, 0x00, 0x00, 0xf8, 0x07, 0xf8, 0x07,
3231 0xf8, 0x07, 0xf8, 0x07, 0xf8, 0x07, 0x00, 0x00, 0x00, 0x00};
3234 #define headicon-mask_width 13
3235 #define headicon-mask_height 9
3236 static unsigned char headicon-mask_bits[] = {
3237 0x00, 0x00, 0xfc, 0x0f, 0xfc, 0x0f, 0xfc, 0x0f,
3238 0xfc, 0x0f, 0xfc, 0x0f, 0xfc, 0x0f, 0xfc, 0x0f, 0x00, 0x00};
3240 image create bitmap reficon-H -background black -foreground green \
3241 -data $rectdata -maskdata $rectmask
3242 image create bitmap reficon-o -background black -foreground "#ddddff" \
3243 -data $rectdata -maskdata $rectmask
3245 proc init_flist {first} {
3246 global cflist cflist_top difffilestart
3248 $cflist conf -state normal
3249 $cflist delete 0.0 end
3251 $cflist insert end $first
3253 $cflist tag add highlight 1.0 "1.0 lineend"
3255 catch {unset cflist_top}
3257 $cflist conf -state disabled
3258 set difffilestart {}
3261 proc highlight_tag {f} {
3262 global highlight_paths
3264 foreach p $highlight_paths {
3265 if {[string match $p $f]} {
3272 proc highlight_filelist {} {
3273 global cmitmode cflist
3275 $cflist conf -state normal
3276 if {$cmitmode ne "tree"} {
3277 set end [lindex [split [$cflist index end] .] 0]
3278 for {set l 2} {$l < $end} {incr l} {
3279 set line [$cflist get $l.0 "$l.0 lineend"]
3280 if {[highlight_tag $line] ne {}} {
3281 $cflist tag add bold $l.0 "$l.0 lineend"
3287 $cflist conf -state disabled
3290 proc unhighlight_filelist {} {
3293 $cflist conf -state normal
3294 $cflist tag remove bold 1.0 end
3295 $cflist conf -state disabled
3298 proc add_flist {fl} {
3301 $cflist conf -state normal
3303 $cflist insert end "\n"
3304 $cflist insert end $f [highlight_tag $f]
3306 $cflist conf -state disabled
3309 proc sel_flist {w x y} {
3310 global ctext difffilestart cflist cflist_top cmitmode
3312 if {$cmitmode eq "tree"} return
3313 if {![info exists cflist_top]} return
3314 set l [lindex [split [$w index "@$x,$y"] "."] 0]
3315 $cflist tag remove highlight $cflist_top.0 "$cflist_top.0 lineend"
3316 $cflist tag add highlight $l.0 "$l.0 lineend"
3321 catch {$ctext yview [lindex $difffilestart [expr {$l - 2}]]}
3323 suppress_highlighting_file_for_current_scrollpos
3326 proc pop_flist_menu {w X Y x y} {
3327 global ctext cflist cmitmode flist_menu flist_menu_file
3328 global treediffs diffids
3331 set l [lindex [split [$w index "@$x,$y"] "."] 0]
3333 if {$cmitmode eq "tree"} {
3334 set e [linetoelt $l]
3335 if {[string index $e end] eq "/"} return
3337 set e [lindex $treediffs($diffids) [expr {$l-2}]]
3339 set flist_menu_file $e
3340 set xdiffstate "normal"
3341 if {$cmitmode eq "tree"} {
3342 set xdiffstate "disabled"
3344 # Disable "External diff" item in tree mode
3345 $flist_menu entryconf 2 -state $xdiffstate
3346 tk_popup $flist_menu $X $Y
3349 proc find_ctext_fileinfo {line} {
3350 global ctext_file_names ctext_file_lines
3352 set ok [bsearch $ctext_file_lines $line]
3353 set tline [lindex $ctext_file_lines $ok]
3355 if {$ok >= [llength $ctext_file_lines] || $line < $tline} {
3358 return [list [lindex $ctext_file_names $ok] $tline]
3362 proc pop_diff_menu {w X Y x y} {
3363 global ctext diff_menu flist_menu_file
3364 global diff_menu_txtpos diff_menu_line
3365 global diff_menu_filebase
3367 set diff_menu_txtpos [split [$w index "@$x,$y"] "."]
3368 set diff_menu_line [lindex $diff_menu_txtpos 0]
3369 # don't pop up the menu on hunk-separator or file-separator lines
3370 if {[lsearch -glob [$ctext tag names $diff_menu_line.0] "*sep"] >= 0} {
3374 set f [find_ctext_fileinfo $diff_menu_line]
3375 if {$f eq {}} return
3376 set flist_menu_file [lindex $f 0]
3377 set diff_menu_filebase [lindex $f 1]
3378 tk_popup $diff_menu $X $Y
3381 proc flist_hl {only} {
3382 global flist_menu_file findstring gdttype
3384 set x [shellquote $flist_menu_file]
3385 if {$only || $findstring eq {} || $gdttype ne [mc "touching paths:"]} {
3388 append findstring " " $x
3390 set gdttype [mc "touching paths:"]
3393 proc gitknewtmpdir {} {
3394 global diffnum gitktmpdir gitdir
3396 if {![info exists gitktmpdir]} {
3397 set gitktmpdir [file join $gitdir [format ".gitk-tmp.%s" [pid]]]
3398 if {[catch {file mkdir $gitktmpdir} err]} {
3399 error_popup "[mc "Error creating temporary directory %s:" $gitktmpdir] $err"
3406 set diffdir [file join $gitktmpdir $diffnum]
3407 if {[catch {file mkdir $diffdir} err]} {
3408 error_popup "[mc "Error creating temporary directory %s:" $diffdir] $err"
3414 proc save_file_from_commit {filename output what} {
3417 if {[catch {exec git show $filename -- > $output} err]} {
3418 if {[string match "fatal: bad revision *" $err]} {
3421 error_popup "[mc "Error getting \"%s\" from %s:" $filename $what] $err"
3427 proc external_diff_get_one_file {diffid filename diffdir} {
3428 global nullid nullid2 nullfile
3431 if {$diffid == $nullid} {
3432 set difffile [file join $worktree $filename]
3433 if {[file exists $difffile]} {
3438 if {$diffid == $nullid2} {
3439 set difffile [file join $diffdir "\[index\] [file tail $filename]"]
3440 return [save_file_from_commit :$filename $difffile index]
3442 set difffile [file join $diffdir "\[$diffid\] [file tail $filename]"]
3443 return [save_file_from_commit $diffid:$filename $difffile \
3447 proc external_diff {} {
3448 global nullid nullid2
3449 global flist_menu_file
3453 if {[llength $diffids] == 1} {
3454 # no reference commit given
3455 set diffidto [lindex $diffids 0]
3456 if {$diffidto eq $nullid} {
3457 # diffing working copy with index
3458 set diffidfrom $nullid2
3459 } elseif {$diffidto eq $nullid2} {
3460 # diffing index with HEAD
3461 set diffidfrom "HEAD"
3463 # use first parent commit
3464 global parentlist selectedline
3465 set diffidfrom [lindex $parentlist $selectedline 0]
3468 set diffidfrom [lindex $diffids 0]
3469 set diffidto [lindex $diffids 1]
3472 # make sure that several diffs wont collide
3473 set diffdir [gitknewtmpdir]
3474 if {$diffdir eq {}} return
3476 # gather files to diff
3477 set difffromfile [external_diff_get_one_file $diffidfrom $flist_menu_file $diffdir]
3478 set difftofile [external_diff_get_one_file $diffidto $flist_menu_file $diffdir]
3480 if {$difffromfile ne {} && $difftofile ne {}} {
3481 set cmd [list [shellsplit $extdifftool] $difffromfile $difftofile]
3482 if {[catch {set fl [open |$cmd r]} err]} {
3483 file delete -force $diffdir
3484 error_popup "$extdifftool: [mc "command failed:"] $err"
3486 fconfigure $fl -blocking 0
3487 filerun $fl [list delete_at_eof $fl $diffdir]
3492 proc find_hunk_blamespec {base line} {
3495 # Find and parse the hunk header
3496 set s_lix [$ctext search -backwards -regexp ^@@ "$line.0 lineend" $base.0]
3497 if {$s_lix eq {}} return
3499 set s_line [$ctext get $s_lix "$s_lix + 1 lines"]
3500 if {![regexp {^@@@*(( -\d+(,\d+)?)+) \+(\d+)(,\d+)? @@} $s_line \
3501 s_line old_specs osz osz1 new_line nsz]} {
3505 # base lines for the parents
3506 set base_lines [list $new_line]
3507 foreach old_spec [lrange [split $old_specs " "] 1 end] {
3508 if {![regexp -- {-(\d+)(,\d+)?} $old_spec \
3509 old_spec old_line osz]} {
3512 lappend base_lines $old_line
3515 # Now scan the lines to determine offset within the hunk
3516 set max_parent [expr {[llength $base_lines]-2}]
3518 set s_lno [lindex [split $s_lix "."] 0]
3520 # Determine if the line is removed
3521 set chunk [$ctext get $line.0 "$line.1 + $max_parent chars"]
3522 if {[string match {[-+ ]*} $chunk]} {
3523 set removed_idx [string first "-" $chunk]
3524 # Choose a parent index
3525 if {$removed_idx >= 0} {
3526 set parent $removed_idx
3528 set unchanged_idx [string first " " $chunk]
3529 if {$unchanged_idx >= 0} {
3530 set parent $unchanged_idx
3532 # blame the current commit
3536 # then count other lines that belong to it
3537 for {set i $line} {[incr i -1] > $s_lno} {} {
3538 set chunk [$ctext get $i.0 "$i.1 + $max_parent chars"]
3539 # Determine if the line is removed
3540 set removed_idx [string first "-" $chunk]
3542 set code [string index $chunk $parent]
3543 if {$code eq "-" || ($removed_idx < 0 && $code ne "+")} {
3547 if {$removed_idx < 0} {
3557 incr dline [lindex $base_lines $parent]
3558 return [list $parent $dline]
3561 proc external_blame_diff {} {
3562 global currentid cmitmode
3563 global diff_menu_txtpos diff_menu_line
3564 global diff_menu_filebase flist_menu_file
3566 if {$cmitmode eq "tree"} {
3568 set line [expr {$diff_menu_line - $diff_menu_filebase}]
3570 set hinfo [find_hunk_blamespec $diff_menu_filebase $diff_menu_line]
3572 set parent_idx [lindex $hinfo 0]
3573 set line [lindex $hinfo 1]
3580 external_blame $parent_idx $line
3583 # Find the SHA1 ID of the blob for file $fname in the index
3585 proc index_sha1 {fname} {
3586 set f [open [list | git ls-files -s $fname] r]
3587 while {[gets $f line] >= 0} {
3588 set info [lindex [split $line "\t"] 0]
3589 set stage [lindex $info 2]
3590 if {$stage eq "0" || $stage eq "2"} {
3592 return [lindex $info 1]
3599 # Turn an absolute path into one relative to the current directory
3600 proc make_relative {f} {
3601 if {[file pathtype $f] eq "relative"} {
3604 set elts [file split $f]
3605 set here [file split [pwd]]
3610 if {$ei < $hi || $ei >= [llength $elts] || [lindex $elts $ei] ne $d} {
3617 set elts [concat $res [lrange $elts $ei end]]
3618 return [eval file join $elts]
3621 proc external_blame {parent_idx {line {}}} {
3622 global flist_menu_file cdup
3623 global nullid nullid2
3624 global parentlist selectedline currentid
3626 if {$parent_idx > 0} {
3627 set base_commit [lindex $parentlist $selectedline [expr {$parent_idx-1}]]
3629 set base_commit $currentid
3632 if {$base_commit eq {} || $base_commit eq $nullid || $base_commit eq $nullid2} {
3633 error_popup [mc "No such commit"]
3637 set cmdline [list git gui blame]
3638 if {$line ne {} && $line > 1} {
3639 lappend cmdline "--line=$line"
3641 set f [file join $cdup $flist_menu_file]
3642 # Unfortunately it seems git gui blame doesn't like
3643 # being given an absolute path...
3644 set f [make_relative $f]
3645 lappend cmdline $base_commit $f
3646 if {[catch {eval exec $cmdline &} err]} {
3647 error_popup "[mc "git gui blame: command failed:"] $err"
3651 proc show_line_source {} {
3652 global cmitmode currentid parents curview blamestuff blameinst
3653 global diff_menu_line diff_menu_filebase flist_menu_file
3654 global nullid nullid2 gitdir cdup
3657 if {$cmitmode eq "tree"} {
3659 set line [expr {$diff_menu_line - $diff_menu_filebase}]
3661 set h [find_hunk_blamespec $diff_menu_filebase $diff_menu_line]
3662 if {$h eq {}} return
3663 set pi [lindex $h 0]
3665 mark_ctext_line $diff_menu_line
3669 if {$currentid eq $nullid} {
3671 # must be a merge in progress...
3673 # get the last line from .git/MERGE_HEAD
3674 set f [open [file join $gitdir MERGE_HEAD] r]
3675 set id [lindex [split [read $f] "\n"] end-1]
3678 error_popup [mc "Couldn't read merge head: %s" $err]
3681 } elseif {$parents($curview,$currentid) eq $nullid2} {
3682 # need to do the blame from the index
3684 set from_index [index_sha1 $flist_menu_file]
3686 error_popup [mc "Error reading index: %s" $err]
3690 set id $parents($curview,$currentid)
3693 set id [lindex $parents($curview,$currentid) $pi]
3695 set line [lindex $h 1]
3698 if {$from_index ne {}} {
3699 lappend blameargs | git cat-file blob $from_index
3701 lappend blameargs | git blame -p -L$line,+1
3702 if {$from_index ne {}} {
3703 lappend blameargs --contents -
3705 lappend blameargs $id
3707 lappend blameargs -- [file join $cdup $flist_menu_file]
3709 set f [open $blameargs r]
3711 error_popup [mc "Couldn't start git blame: %s" $err]
3714 nowbusy blaming [mc "Searching"]
3715 fconfigure $f -blocking 0
3716 set i [reg_instance $f]
3717 set blamestuff($i) {}
3719 filerun $f [list read_line_source $f $i]
3722 proc stopblaming {} {
3725 if {[info exists blameinst]} {
3726 stop_instance $blameinst
3732 proc read_line_source {fd inst} {
3733 global blamestuff curview commfd blameinst nullid nullid2
3735 while {[gets $fd line] >= 0} {
3736 lappend blamestuff($inst) $line
3744 fconfigure $fd -blocking 1
3745 if {[catch {close $fd} err]} {
3746 error_popup [mc "Error running git blame: %s" $err]
3751 set line [split [lindex $blamestuff($inst) 0] " "]
3752 set id [lindex $line 0]
3753 set lnum [lindex $line 1]
3754 if {[string length $id] == 40 && [string is xdigit $id] &&
3755 [string is digit -strict $lnum]} {
3756 # look for "filename" line
3757 foreach l $blamestuff($inst) {
3758 if {[string match "filename *" $l]} {
3759 set fname [string range $l 9 end]
3765 # all looks good, select it
3766 if {$id eq $nullid} {
3767 # blame uses all-zeroes to mean not committed,
3768 # which would mean a change in the index
3771 if {[commitinview $id $curview]} {
3772 selectline [rowofcommit $id] 1 [list $fname $lnum]
3774 error_popup [mc "That line comes from commit %s, \
3775 which is not in this view" [shortids $id]]
3778 puts "oops couldn't parse git blame output"
3783 # delete $dir when we see eof on $f (presumably because the child has exited)
3784 proc delete_at_eof {f dir} {
3785 while {[gets $f line] >= 0} {}
3787 if {[catch {close $f} err]} {
3788 error_popup "[mc "External diff viewer failed:"] $err"
3790 file delete -force $dir
3796 # Functions for adding and removing shell-type quoting
3798 proc shellquote {str} {
3799 if {![string match "*\['\"\\ \t]*" $str]} {
3802 if {![string match "*\['\"\\]*" $str]} {
3805 if {![string match "*'*" $str]} {
3808 return "\"[string map {\" \\\" \\ \\\\} $str]\""
3811 proc shellarglist {l} {
3817 append str [shellquote $a]
3822 proc shelldequote {str} {
3827 if {![regexp -start $used -indices "\['\"\\\\ \t]" $str first]} {
3828 append ret [string range $str $used end]
3829 set used [string length $str]
3832 set first [lindex $first 0]
3833 set ch [string index $str $first]
3834 if {$first > $used} {
3835 append ret [string range $str $used [expr {$first - 1}]]
3838 if {$ch eq " " || $ch eq "\t"} break
3841 set first [string first "'" $str $used]
3843 error "unmatched single-quote"
3845 append ret [string range $str $used [expr {$first - 1}]]
3850 if {$used >= [string length $str]} {
3851 error "trailing backslash"
3853 append ret [string index $str $used]
3858 if {![regexp -start $used -indices "\[\"\\\\]" $str first]} {
3859 error "unmatched double-quote"
3861 set first [lindex $first 0]
3862 set ch [string index $str $first]
3863 if {$first > $used} {
3864 append ret [string range $str $used [expr {$first - 1}]]
3867 if {$ch eq "\""} break
3869 append ret [string index $str $used]
3873 return [list $used $ret]
3876 proc shellsplit {str} {
3879 set str [string trimleft $str]
3880 if {$str eq {}} break
3881 set dq [shelldequote $str]
3882 set n [lindex $dq 0]
3883 set word [lindex $dq 1]
3884 set str [string range $str $n end]
3890 # Code to implement multiple views
3892 proc newview {ishighlight} {
3893 global nextviewnum newviewname newishighlight
3894 global revtreeargs viewargscmd newviewopts curview
3896 set newishighlight $ishighlight
3898 if {[winfo exists $top]} {
3902 decode_view_opts $nextviewnum $revtreeargs
3903 set newviewname($nextviewnum) "[mc "View"] $nextviewnum"
3904 set newviewopts($nextviewnum,perm) 0
3905 set newviewopts($nextviewnum,cmd) $viewargscmd($curview)
3906 vieweditor $top $nextviewnum [mc "Gitk view definition"]
3909 set known_view_options {
3910 {perm b . {} {mc "Remember this view"}}
3911 {reflabel l + {} {mc "References (space separated list):"}}
3912 {refs t15 .. {} {mc "Branches & tags:"}}
3913 {allrefs b *. "--all" {mc "All refs"}}
3914 {branches b . "--branches" {mc "All (local) branches"}}
3915 {tags b . "--tags" {mc "All tags"}}
3916 {remotes b . "--remotes" {mc "All remote-tracking branches"}}
3917 {commitlbl l + {} {mc "Commit Info (regular expressions):"}}
3918 {author t15 .. "--author=*" {mc "Author:"}}
3919 {committer t15 . "--committer=*" {mc "Committer:"}}
3920 {loginfo t15 .. "--grep=*" {mc "Commit Message:"}}
3921 {allmatch b .. "--all-match" {mc "Matches all Commit Info criteria"}}
3922 {changes_l l + {} {mc "Changes to Files:"}}
3923 {pickaxe_s r0 . {} {mc "Fixed String"}}
3924 {pickaxe_t r1 . "--pickaxe-regex" {mc "Regular Expression"}}
3925 {pickaxe t15 .. "-S*" {mc "Search string:"}}
3926 {datelabel l + {} {mc "Commit Dates (\"2 weeks ago\", \"2009-03-17 15:27:38\", \"March 17, 2009 15:27:38\"):"}}
3927 {since t15 .. {"--since=*" "--after=*"} {mc "Since:"}}
3928 {until t15 . {"--until=*" "--before=*"} {mc "Until:"}}
3929 {limit_lbl l + {} {mc "Limit and/or skip a number of revisions (positive integer):"}}
3930 {limit t10 *. "--max-count=*" {mc "Number to show:"}}
3931 {skip t10 . "--skip=*" {mc "Number to skip:"}}
3932 {misc_lbl l + {} {mc "Miscellaneous options:"}}
3933 {dorder b *. {"--date-order" "-d"} {mc "Strictly sort by date"}}
3934 {lright b . "--left-right" {mc "Mark branch sides"}}
3935 {first b . "--first-parent" {mc "Limit to first parent"}}
3936 {smplhst b . "--simplify-by-decoration" {mc "Simple history"}}
3937 {args t50 *. {} {mc "Additional arguments to git log:"}}
3938 {allpaths path + {} {mc "Enter files and directories to include, one per line:"}}
3939 {cmd t50= + {} {mc "Command to generate more commits to include:"}}
3942 # Convert $newviewopts($n, ...) into args for git log.
3943 proc encode_view_opts {n} {
3944 global known_view_options newviewopts
3947 foreach opt $known_view_options {
3948 set patterns [lindex $opt 3]
3949 if {$patterns eq {}} continue
3950 set pattern [lindex $patterns 0]
3952 if {[lindex $opt 1] eq "b"} {
3953 set val $newviewopts($n,[lindex $opt 0])
3955 lappend rargs $pattern
3957 } elseif {[regexp {^r(\d+)$} [lindex $opt 1] type value]} {
3958 regexp {^(.*_)} [lindex $opt 0] uselessvar button_id
3959 set val $newviewopts($n,$button_id)
3960 if {$val eq $value} {
3961 lappend rargs $pattern
3964 set val $newviewopts($n,[lindex $opt 0])
3965 set val [string trim $val]
3967 set pfix [string range $pattern 0 end-1]
3968 lappend rargs $pfix$val
3972 set rargs [concat $rargs [shellsplit $newviewopts($n,refs)]]
3973 return [concat $rargs [shellsplit $newviewopts($n,args)]]
3976 # Fill $newviewopts($n, ...) based on args for git log.
3977 proc decode_view_opts {n view_args} {
3978 global known_view_options newviewopts
3980 foreach opt $known_view_options {
3981 set id [lindex $opt 0]
3982 if {[lindex $opt 1] eq "b"} {
3985 } elseif {[regexp {^r(\d+)$} [lindex $opt 1]]} {
3987 regexp {^(.*_)} $id uselessvar id
3993 set newviewopts($n,$id) $val
3997 foreach arg $view_args {
3998 if {[regexp -- {^-([0-9]+)$} $arg arg cnt]
3999 && ![info exists found(limit)]} {
4000 set newviewopts($n,limit) $cnt
4005 foreach opt $known_view_options {
4006 set id [lindex $opt 0]
4007 if {[info exists found($id)]} continue
4008 foreach pattern [lindex $opt 3] {
4009 if {![string match $pattern $arg]} continue
4010 if {[lindex $opt 1] eq "b"} {
4013 } elseif {[regexp {^r(\d+)$} [lindex $opt 1] match num]} {
4015 regexp {^(.*_)} $id uselessvar id
4019 set size [string length $pattern]
4020 set val [string range $arg [expr {$size-1}] end]
4022 set newviewopts($n,$id) $val
4026 if {[info exists val]} break
4028 if {[info exists val]} continue
4029 if {[regexp {^-} $arg]} {
4032 lappend refargs $arg
4035 set newviewopts($n,refs) [shellarglist $refargs]
4036 set newviewopts($n,args) [shellarglist $oargs]
4039 proc edit_or_newview {} {
4051 global viewname viewperm newviewname newviewopts
4052 global viewargs viewargscmd
4054 set top .gitkvedit-$curview
4055 if {[winfo exists $top]} {
4059 decode_view_opts $curview $viewargs($curview)
4060 set newviewname($curview) $viewname($curview)
4061 set newviewopts($curview,perm) $viewperm($curview)
4062 set newviewopts($curview,cmd) $viewargscmd($curview)
4063 vieweditor $top $curview "[mc "Gitk: edit view"] $viewname($curview)"
4066 proc vieweditor {top n title} {
4067 global newviewname newviewopts viewfiles bgcolor
4068 global known_view_options NS
4071 wm title $top [concat $title [mc "-- criteria for selecting revisions"]]
4072 make_transient $top .
4075 ${NS}::frame $top.nfr
4076 ${NS}::label $top.nl -text [mc "View Name"]
4077 ${NS}::entry $top.name -width 20 -textvariable newviewname($n)
4078 pack $top.nfr -in $top -fill x -pady 5 -padx 3
4079 pack $top.nl -in $top.nfr -side left -padx {0 5}
4080 pack $top.name -in $top.nfr -side left -padx {0 25}
4086 foreach opt $known_view_options {
4087 set id [lindex $opt 0]
4088 set type [lindex $opt 1]
4089 set flags [lindex $opt 2]
4090 set title [eval [lindex $opt 4]]
4093 if {$flags eq "+" || $flags eq "*"} {
4094 set cframe $top.fr$cnt
4096 ${NS}::frame $cframe
4097 pack $cframe -in $top -fill x -pady 3 -padx 3
4098 set cexpand [expr {$flags eq "*"}]
4099 } elseif {$flags eq ".." || $flags eq "*."} {
4100 set cframe $top.fr$cnt
4102 ${NS}::frame $cframe
4103 pack $cframe -in $top -fill x -pady 3 -padx [list 15 3]
4104 set cexpand [expr {$flags eq "*."}]
4110 ${NS}::label $cframe.l_$id -text $title
4111 pack $cframe.l_$id -in $cframe -side left -pady [list 3 0] -anchor w
4112 } elseif {$type eq "b"} {
4113 ${NS}::checkbutton $cframe.c_$id -text $title -variable newviewopts($n,$id)
4114 pack $cframe.c_$id -in $cframe -side left \
4115 -padx [list $lxpad 0] -expand $cexpand -anchor w
4116 } elseif {[regexp {^r(\d+)$} $type type sz]} {
4117 regexp {^(.*_)} $id uselessvar button_id
4118 ${NS}::radiobutton $cframe.c_$id -text $title -variable newviewopts($n,$button_id) -value $sz
4119 pack $cframe.c_$id -in $cframe -side left \
4120 -padx [list $lxpad 0] -expand $cexpand -anchor w
4121 } elseif {[regexp {^t(\d+)$} $type type sz]} {
4122 ${NS}::label $cframe.l_$id -text $title
4123 ${NS}::entry $cframe.e_$id -width $sz -background $bgcolor \
4124 -textvariable newviewopts($n,$id)
4125 pack $cframe.l_$id -in $cframe -side left -padx [list $lxpad 0]
4126 pack $cframe.e_$id -in $cframe -side left -expand 1 -fill x
4127 } elseif {[regexp {^t(\d+)=$} $type type sz]} {
4128 ${NS}::label $cframe.l_$id -text $title
4129 ${NS}::entry $cframe.e_$id -width $sz -background $bgcolor \
4130 -textvariable newviewopts($n,$id)
4131 pack $cframe.l_$id -in $cframe -side top -pady [list 3 0] -anchor w
4132 pack $cframe.e_$id -in $cframe -side top -fill x
4133 } elseif {$type eq "path"} {
4134 ${NS}::label $top.l -text $title
4135 pack $top.l -in $top -side top -pady [list 3 0] -anchor w -padx 3
4136 text $top.t -width 40 -height 5 -background $bgcolor
4137 if {[info exists viewfiles($n)]} {
4138 foreach f $viewfiles($n) {
4139 $top.t insert end $f
4140 $top.t insert end "\n"
4142 $top.t delete {end - 1c} end
4143 $top.t mark set insert 0.0
4145 pack $top.t -in $top -side top -pady [list 0 5] -fill both -expand 1 -padx 3
4149 ${NS}::frame $top.buts
4150 ${NS}::button $top.buts.ok -text [mc "OK"] -command [list newviewok $top $n]
4151 ${NS}::button $top.buts.apply -text [mc "Apply (F5)"] -command [list newviewok $top $n 1]
4152 ${NS}::button $top.buts.can -text [mc "Cancel"] -command [list destroy $top]
4153 bind $top <Control-Return> [list newviewok $top $n]
4154 bind $top <F5> [list newviewok $top $n 1]
4155 bind $top <Escape> [list destroy $top]
4156 grid $top.buts.ok $top.buts.apply $top.buts.can
4157 grid columnconfigure $top.buts 0 -weight 1 -uniform a
4158 grid columnconfigure $top.buts 1 -weight 1 -uniform a
4159 grid columnconfigure $top.buts 2 -weight 1 -uniform a
4160 pack $top.buts -in $top -side top -fill x
4164 proc doviewmenu {m first cmd op argv} {
4165 set nmenu [$m index end]
4166 for {set i $first} {$i <= $nmenu} {incr i} {
4167 if {[$m entrycget $i -command] eq $cmd} {
4168 eval $m $op $i $argv
4174 proc allviewmenus {n op args} {
4177 doviewmenu .bar.view 5 [list showview $n] $op $args
4178 # doviewmenu $viewhlmenu 1 [list addvhighlight $n] $op $args
4181 proc newviewok {top n {apply 0}} {
4182 global nextviewnum newviewperm newviewname newishighlight
4183 global viewname viewfiles viewperm selectedview curview
4184 global viewargs viewargscmd newviewopts viewhlmenu
4187 set newargs [encode_view_opts $n]
4189 error_popup "[mc "Error in commit selection arguments:"] $err" $top
4193 foreach f [split [$top.t get 0.0 end] "\n"] {
4194 set ft [string trim $f]
4199 if {![info exists viewfiles($n)]} {
4200 # creating a new view
4202 set viewname($n) $newviewname($n)
4203 set viewperm($n) $newviewopts($n,perm)
4204 set viewfiles($n) $files
4205 set viewargs($n) $newargs
4206 set viewargscmd($n) $newviewopts($n,cmd)
4208 if {!$newishighlight} {
4211 run addvhighlight $n
4214 # editing an existing view
4215 set viewperm($n) $newviewopts($n,perm)
4216 if {$newviewname($n) ne $viewname($n)} {
4217 set viewname($n) $newviewname($n)
4218 doviewmenu .bar.view 5 [list showview $n] \
4219 entryconf [list -label $viewname($n)]
4220 # doviewmenu $viewhlmenu 1 [list addvhighlight $n] \
4221 # entryconf [list -label $viewname($n) -value $viewname($n)]
4223 if {$files ne $viewfiles($n) || $newargs ne $viewargs($n) || \
4224 $newviewopts($n,cmd) ne $viewargscmd($n)} {
4225 set viewfiles($n) $files
4226 set viewargs($n) $newargs
4227 set viewargscmd($n) $newviewopts($n,cmd)
4228 if {$curview == $n} {
4234 catch {destroy $top}
4238 global curview viewperm hlview selectedhlview
4240 if {$curview == 0} return
4241 if {[info exists hlview] && $hlview == $curview} {
4242 set selectedhlview [mc "None"]
4245 allviewmenus $curview delete
4246 set viewperm($curview) 0
4250 proc addviewmenu {n} {
4251 global viewname viewhlmenu
4253 .bar.view add radiobutton -label $viewname($n) \
4254 -command [list showview $n] -variable selectedview -value $n
4255 #$viewhlmenu add radiobutton -label $viewname($n) \
4256 # -command [list addvhighlight $n] -variable selectedhlview
4260 global curview cached_commitrow ordertok
4261 global displayorder parentlist rowidlist rowisopt rowfinal
4262 global colormap rowtextx nextcolor canvxmax
4263 global numcommits viewcomplete
4264 global selectedline currentid canv canvy0
4266 global pending_select mainheadid
4269 global hlview selectedhlview commitinterest
4271 if {$n == $curview} return
4273 set ymax [lindex [$canv cget -scrollregion] 3]
4274 set span [$canv yview]
4275 set ytop [expr {[lindex $span 0] * $ymax}]
4276 set ybot [expr {[lindex $span 1] * $ymax}]
4277 set yscreen [expr {($ybot - $ytop) / 2}]
4278 if {$selectedline ne {}} {
4279 set selid $currentid
4280 set y [yc $selectedline]
4281 if {$ytop < $y && $y < $ybot} {
4282 set yscreen [expr {$y - $ytop}]
4284 } elseif {[info exists pending_select]} {
4285 set selid $pending_select
4286 unset pending_select
4290 catch {unset treediffs}
4292 if {[info exists hlview] && $hlview == $n} {
4294 set selectedhlview [mc "None"]
4296 catch {unset commitinterest}
4297 catch {unset cached_commitrow}
4298 catch {unset ordertok}
4302 .bar.view entryconf [mca "Edit view..."] -state [expr {$n == 0? "disabled": "normal"}]
4303 .bar.view entryconf [mca "Delete view"] -state [expr {$n == 0? "disabled": "normal"}]
4306 if {![info exists viewcomplete($n)]} {
4316 set numcommits $commitidx($n)
4318 catch {unset colormap}
4319 catch {unset rowtextx}
4321 set canvxmax [$canv cget -width]
4327 if {$selid ne {} && [commitinview $selid $n]} {
4328 set row [rowofcommit $selid]
4329 # try to get the selected row in the same position on the screen
4330 set ymax [lindex [$canv cget -scrollregion] 3]
4331 set ytop [expr {[yc $row] - $yscreen}]
4335 set yf [expr {$ytop * 1.0 / $ymax}]
4337 allcanvs yview moveto $yf
4341 } elseif {!$viewcomplete($n)} {
4342 reset_pending_select $selid
4344 reset_pending_select {}
4346 if {[commitinview $pending_select $curview]} {
4347 selectline [rowofcommit $pending_select] 1
4349 set row [first_real_row]
4350 if {$row < $numcommits} {
4355 if {!$viewcomplete($n)} {
4356 if {$numcommits == 0} {
4357 show_status [mc "Reading commits..."]
4359 } elseif {$numcommits == 0} {
4360 show_status [mc "No commits selected"]
4364 # Stuff relating to the highlighting facility
4366 proc ishighlighted {id} {
4367 global vhighlights fhighlights nhighlights rhighlights
4369 if {[info exists nhighlights($id)] && $nhighlights($id) > 0} {
4370 return $nhighlights($id)
4372 if {[info exists vhighlights($id)] && $vhighlights($id) > 0} {
4373 return $vhighlights($id)
4375 if {[info exists fhighlights($id)] && $fhighlights($id) > 0} {
4376 return $fhighlights($id)
4378 if {[info exists rhighlights($id)] && $rhighlights($id) > 0} {
4379 return $rhighlights($id)
4384 proc bolden {id font} {
4385 global canv linehtag currentid boldids need_redisplay markedid
4387 # need_redisplay = 1 means the display is stale and about to be redrawn
4388 if {$need_redisplay} return
4390 $canv itemconf $linehtag($id) -font $font
4391 if {[info exists currentid] && $id eq $currentid} {
4393 set t [eval $canv create rect [$canv bbox $linehtag($id)] \
4394 -outline {{}} -tags secsel \
4395 -fill [$canv cget -selectbackground]]
4398 if {[info exists markedid] && $id eq $markedid} {
4403 proc bolden_name {id font} {
4404 global canv2 linentag currentid boldnameids need_redisplay
4406 if {$need_redisplay} return
4407 lappend boldnameids $id
4408 $canv2 itemconf $linentag($id) -font $font
4409 if {[info exists currentid] && $id eq $currentid} {
4410 $canv2 delete secsel
4411 set t [eval $canv2 create rect [$canv2 bbox $linentag($id)] \
4412 -outline {{}} -tags secsel \
4413 -fill [$canv2 cget -selectbackground]]
4422 foreach id $boldids {
4423 if {![ishighlighted $id]} {
4426 lappend stillbold $id
4429 set boldids $stillbold
4432 proc addvhighlight {n} {
4433 global hlview viewcomplete curview vhl_done commitidx
4435 if {[info exists hlview]} {
4439 if {$n != $curview && ![info exists viewcomplete($n)]} {
4442 set vhl_done $commitidx($hlview)
4443 if {$vhl_done > 0} {
4448 proc delvhighlight {} {
4449 global hlview vhighlights
4451 if {![info exists hlview]} return
4453 catch {unset vhighlights}
4457 proc vhighlightmore {} {
4458 global hlview vhl_done commitidx vhighlights curview
4460 set max $commitidx($hlview)
4461 set vr [visiblerows]
4462 set r0 [lindex $vr 0]
4463 set r1 [lindex $vr 1]
4464 for {set i $vhl_done} {$i < $max} {incr i} {
4465 set id [commitonrow $i $hlview]
4466 if {[commitinview $id $curview]} {
4467 set row [rowofcommit $id]
4468 if {$r0 <= $row && $row <= $r1} {
4469 if {![highlighted $row]} {
4470 bolden $id mainfontbold
4472 set vhighlights($id) 1
4480 proc askvhighlight {row id} {
4481 global hlview vhighlights iddrawn
4483 if {[commitinview $id $hlview]} {
4484 if {[info exists iddrawn($id)] && ![ishighlighted $id]} {
4485 bolden $id mainfontbold
4487 set vhighlights($id) 1
4489 set vhighlights($id) 0
4493 proc hfiles_change {} {
4494 global highlight_files filehighlight fhighlights fh_serial
4495 global highlight_paths
4497 if {[info exists filehighlight]} {
4498 # delete previous highlights
4499 catch {close $filehighlight}
4501 catch {unset fhighlights}
4503 unhighlight_filelist
4505 set highlight_paths {}
4506 after cancel do_file_hl $fh_serial
4508 if {$highlight_files ne {}} {
4509 after 300 do_file_hl $fh_serial
4513 proc gdttype_change {name ix op} {
4514 global gdttype highlight_files findstring findpattern
4517 if {$findstring ne {}} {
4518 if {$gdttype eq [mc "containing:"]} {
4519 if {$highlight_files ne {}} {
4520 set highlight_files {}
4525 if {$findpattern ne {}} {
4529 set highlight_files $findstring
4534 # enable/disable findtype/findloc menus too
4537 proc find_change {name ix op} {
4538 global gdttype findstring highlight_files
4541 if {$gdttype eq [mc "containing:"]} {
4544 if {$highlight_files ne $findstring} {
4545 set highlight_files $findstring
4552 proc findcom_change args {
4553 global nhighlights boldnameids
4554 global findpattern findtype findstring gdttype
4557 # delete previous highlights, if any
4558 foreach id $boldnameids {
4559 bolden_name $id mainfont
4562 catch {unset nhighlights}
4565 if {$gdttype ne [mc "containing:"] || $findstring eq {}} {
4567 } elseif {$findtype eq [mc "Regexp"]} {
4568 set findpattern $findstring
4570 set e [string map {"*" "\\*" "?" "\\?" "\[" "\\\[" "\\" "\\\\"} \
4572 set findpattern "*$e*"
4576 proc makepatterns {l} {
4579 set ee [string map {"*" "\\*" "?" "\\?" "\[" "\\\[" "\\" "\\\\"} $e]
4580 if {[string index $ee end] eq "/"} {
4590 proc do_file_hl {serial} {
4591 global highlight_files filehighlight highlight_paths gdttype fhl_list
4592 global cdup findtype
4594 if {$gdttype eq [mc "touching paths:"]} {
4595 # If "exact" match then convert backslashes to forward slashes.
4596 # Most useful to support Windows-flavoured file paths.
4597 if {$findtype eq [mc "Exact"]} {
4598 set highlight_files [string map {"\\" "/"} $highlight_files]
4600 if {[catch {set paths [shellsplit $highlight_files]}]} return
4601 set highlight_paths [makepatterns $paths]
4603 set relative_paths {}
4604 foreach path $paths {
4605 lappend relative_paths [file join $cdup $path]
4607 set gdtargs [concat -- $relative_paths]
4608 } elseif {$gdttype eq [mc "adding/removing string:"]} {
4609 set gdtargs [list "-S$highlight_files"]
4611 # must be "containing:", i.e. we're searching commit info
4614 set cmd [concat | git diff-tree -r -s --stdin $gdtargs]
4615 set filehighlight [open $cmd r+]
4616 fconfigure $filehighlight -blocking 0
4617 filerun $filehighlight readfhighlight
4623 proc flushhighlights {} {
4624 global filehighlight fhl_list
4626 if {[info exists filehighlight]} {
4628 puts $filehighlight ""
4629 flush $filehighlight
4633 proc askfilehighlight {row id} {
4634 global filehighlight fhighlights fhl_list
4636 lappend fhl_list $id
4637 set fhighlights($id) -1
4638 puts $filehighlight $id
4641 proc readfhighlight {} {
4642 global filehighlight fhighlights curview iddrawn
4643 global fhl_list find_dirn
4645 if {![info exists filehighlight]} {
4649 while {[incr nr] <= 100 && [gets $filehighlight line] >= 0} {
4650 set line [string trim $line]
4651 set i [lsearch -exact $fhl_list $line]
4652 if {$i < 0} continue
4653 for {set j 0} {$j < $i} {incr j} {
4654 set id [lindex $fhl_list $j]
4655 set fhighlights($id) 0
4657 set fhl_list [lrange $fhl_list [expr {$i+1}] end]
4658 if {$line eq {}} continue
4659 if {![commitinview $line $curview]} continue
4660 if {[info exists iddrawn($line)] && ![ishighlighted $line]} {
4661 bolden $line mainfontbold
4663 set fhighlights($line) 1
4665 if {[eof $filehighlight]} {
4667 puts "oops, git diff-tree died"
4668 catch {close $filehighlight}
4672 if {[info exists find_dirn]} {
4678 proc doesmatch {f} {
4679 global findtype findpattern
4681 if {$findtype eq [mc "Regexp"]} {
4682 return [regexp $findpattern $f]
4683 } elseif {$findtype eq [mc "IgnCase"]} {
4684 return [string match -nocase $findpattern $f]
4686 return [string match $findpattern $f]
4690 proc askfindhighlight {row id} {
4691 global nhighlights commitinfo iddrawn
4693 global markingmatches
4695 if {![info exists commitinfo($id)]} {
4698 set info $commitinfo($id)
4700 set fldtypes [list [mc Headline] [mc Author] "" [mc Committer] "" [mc Comments]]
4701 foreach f $info ty $fldtypes {
4702 if {$ty eq ""} continue
4703 if {($findloc eq [mc "All fields"] || $findloc eq $ty) &&
4705 if {$ty eq [mc "Author"]} {
4712 if {$isbold && [info exists iddrawn($id)]} {
4713 if {![ishighlighted $id]} {
4714 bolden $id mainfontbold
4716 bolden_name $id mainfontbold
4719 if {$markingmatches} {
4720 markrowmatches $row $id
4723 set nhighlights($id) $isbold
4726 proc markrowmatches {row id} {
4727 global canv canv2 linehtag linentag commitinfo findloc
4729 set headline [lindex $commitinfo($id) 0]
4730 set author [lindex $commitinfo($id) 1]
4731 $canv delete match$row
4732 $canv2 delete match$row
4733 if {$findloc eq [mc "All fields"] || $findloc eq [mc "Headline"]} {
4734 set m [findmatches $headline]
4736 markmatches $canv $row $headline $linehtag($id) $m \
4737 [$canv itemcget $linehtag($id) -font] $row
4740 if {$findloc eq [mc "All fields"] || $findloc eq [mc "Author"]} {
4741 set m [findmatches $author]
4743 markmatches $canv2 $row $author $linentag($id) $m \
4744 [$canv2 itemcget $linentag($id) -font] $row
4749 proc vrel_change {name ix op} {
4750 global highlight_related
4753 if {$highlight_related ne [mc "None"]} {
4758 # prepare for testing whether commits are descendents or ancestors of a
4759 proc rhighlight_sel {a} {
4760 global descendent desc_todo ancestor anc_todo
4761 global highlight_related
4763 catch {unset descendent}
4764 set desc_todo [list $a]
4765 catch {unset ancestor}
4766 set anc_todo [list $a]
4767 if {$highlight_related ne [mc "None"]} {
4773 proc rhighlight_none {} {
4776 catch {unset rhighlights}
4780 proc is_descendent {a} {
4781 global curview children descendent desc_todo
4784 set la [rowofcommit $a]
4788 for {set i 0} {$i < [llength $todo]} {incr i} {
4789 set do [lindex $todo $i]
4790 if {[rowofcommit $do] < $la} {
4791 lappend leftover $do
4794 foreach nk $children($v,$do) {
4795 if {![info exists descendent($nk)]} {
4796 set descendent($nk) 1
4804 set desc_todo [concat $leftover [lrange $todo [expr {$i+1}] end]]
4808 set descendent($a) 0
4809 set desc_todo $leftover
4812 proc is_ancestor {a} {
4813 global curview parents ancestor anc_todo
4816 set la [rowofcommit $a]
4820 for {set i 0} {$i < [llength $todo]} {incr i} {
4821 set do [lindex $todo $i]
4822 if {![commitinview $do $v] || [rowofcommit $do] > $la} {
4823 lappend leftover $do
4826 foreach np $parents($v,$do) {
4827 if {![info exists ancestor($np)]} {
4836 set anc_todo [concat $leftover [lrange $todo [expr {$i+1}] end]]
4841 set anc_todo $leftover
4844 proc askrelhighlight {row id} {
4845 global descendent highlight_related iddrawn rhighlights
4846 global selectedline ancestor
4848 if {$selectedline eq {}} return
4850 if {$highlight_related eq [mc "Descendant"] ||
4851 $highlight_related eq [mc "Not descendant"]} {
4852 if {![info exists descendent($id)]} {
4855 if {$descendent($id) == ($highlight_related eq [mc "Descendant"])} {
4858 } elseif {$highlight_related eq [mc "Ancestor"] ||
4859 $highlight_related eq [mc "Not ancestor"]} {
4860 if {![info exists ancestor($id)]} {
4863 if {$ancestor($id) == ($highlight_related eq [mc "Ancestor"])} {
4867 if {[info exists iddrawn($id)]} {
4868 if {$isbold && ![ishighlighted $id]} {
4869 bolden $id mainfontbold
4872 set rhighlights($id) $isbold
4875 # Graph layout functions
4877 proc shortids {ids} {
4880 if {[llength $id] > 1} {
4881 lappend res [shortids $id]
4882 } elseif {[regexp {^[0-9a-f]{40}$} $id]} {
4883 lappend res [string range $id 0 7]
4894 for {set mask 1} {$mask <= $n} {incr mask $mask} {
4895 if {($n & $mask) != 0} {
4896 set ret [concat $ret $o]
4898 set o [concat $o $o]
4903 proc ordertoken {id} {
4904 global ordertok curview varcid varcstart varctok curview parents children
4905 global nullid nullid2
4907 if {[info exists ordertok($id)]} {
4908 return $ordertok($id)
4913 if {[info exists varcid($curview,$id)]} {
4914 set a $varcid($curview,$id)
4915 set p [lindex $varcstart($curview) $a]
4917 set p [lindex $children($curview,$id) 0]
4919 if {[info exists ordertok($p)]} {
4920 set tok $ordertok($p)
4923 set id [first_real_child $curview,$p]
4926 set tok [lindex $varctok($curview) $varcid($curview,$p)]
4929 if {[llength $parents($curview,$id)] == 1} {
4930 lappend todo [list $p {}]
4932 set j [lsearch -exact $parents($curview,$id) $p]
4934 puts "oops didn't find [shortids $p] in parents of [shortids $id]"
4936 lappend todo [list $p [strrep $j]]
4939 for {set i [llength $todo]} {[incr i -1] >= 0} {} {
4940 set p [lindex $todo $i 0]
4941 append tok [lindex $todo $i 1]
4942 set ordertok($p) $tok
4944 set ordertok($origid) $tok
4948 # Work out where id should go in idlist so that order-token
4949 # values increase from left to right
4950 proc idcol {idlist id {i 0}} {
4951 set t [ordertoken $id]
4955 if {$i >= [llength $idlist] || $t < [ordertoken [lindex $idlist $i]]} {
4956 if {$i > [llength $idlist]} {
4957 set i [llength $idlist]
4959 while {[incr i -1] >= 0 && $t < [ordertoken [lindex $idlist $i]]} {}
4962 if {$t > [ordertoken [lindex $idlist $i]]} {
4963 while {[incr i] < [llength $idlist] &&
4964 $t >= [ordertoken [lindex $idlist $i]]} {}
4970 proc initlayout {} {
4971 global rowidlist rowisopt rowfinal displayorder parentlist
4972 global numcommits canvxmax canv
4974 global colormap rowtextx
4983 set canvxmax [$canv cget -width]
4984 catch {unset colormap}
4985 catch {unset rowtextx}
4989 proc setcanvscroll {} {
4990 global canv canv2 canv3 numcommits linespc canvxmax canvy0
4991 global lastscrollset lastscrollrows
4993 set ymax [expr {$canvy0 + ($numcommits - 0.5) * $linespc + 2}]
4994 $canv conf -scrollregion [list 0 0 $canvxmax $ymax]
4995 $canv2 conf -scrollregion [list 0 0 0 $ymax]
4996 $canv3 conf -scrollregion [list 0 0 0 $ymax]
4997 set lastscrollset [clock clicks -milliseconds]
4998 set lastscrollrows $numcommits
5001 proc visiblerows {} {
5002 global canv numcommits linespc
5004 set ymax [lindex [$canv cget -scrollregion] 3]
5005 if {$ymax eq {} || $ymax == 0} return
5007 set y0 [expr {int([lindex $f 0] * $ymax)}]
5008 set r0 [expr {int(($y0 - 3) / $linespc) - 1}]
5012 set y1 [expr {int([lindex $f 1] * $ymax)}]
5013 set r1 [expr {int(($y1 - 3) / $linespc) + 1}]
5014 if {$r1 >= $numcommits} {
5015 set r1 [expr {$numcommits - 1}]
5017 return [list $r0 $r1]
5020 proc layoutmore {} {
5021 global commitidx viewcomplete curview
5022 global numcommits pending_select curview
5023 global lastscrollset lastscrollrows
5025 if {$lastscrollrows < 100 || $viewcomplete($curview) ||
5026 [clock clicks -milliseconds] - $lastscrollset > 500} {
5029 if {[info exists pending_select] &&
5030 [commitinview $pending_select $curview]} {
5032 selectline [rowofcommit $pending_select] 1
5037 # With path limiting, we mightn't get the actual HEAD commit,
5038 # so ask git rev-list what is the first ancestor of HEAD that
5039 # touches a file in the path limit.
5040 proc get_viewmainhead {view} {
5041 global viewmainheadid vfilelimit viewinstances mainheadid
5044 set rfd [open [concat | git rev-list -1 $mainheadid \
5045 -- $vfilelimit($view)] r]
5046 set j [reg_instance $rfd]
5047 lappend viewinstances($view) $j
5048 fconfigure $rfd -blocking 0
5049 filerun $rfd [list getviewhead $rfd $j $view]
5050 set viewmainheadid($curview) {}
5054 # git rev-list should give us just 1 line to use as viewmainheadid($view)
5055 proc getviewhead {fd inst view} {
5056 global viewmainheadid commfd curview viewinstances showlocalchanges
5059 if {[gets $fd line] < 0} {
5063 } elseif {[string length $line] == 40 && [string is xdigit $line]} {
5066 set viewmainheadid($view) $id
5069 set i [lsearch -exact $viewinstances($view) $inst]
5071 set viewinstances($view) [lreplace $viewinstances($view) $i $i]
5073 if {$showlocalchanges && $id ne {} && $view == $curview} {
5079 proc doshowlocalchanges {} {
5080 global curview viewmainheadid
5082 if {$viewmainheadid($curview) eq {}} return
5083 if {[commitinview $viewmainheadid($curview) $curview]} {
5086 interestedin $viewmainheadid($curview) dodiffindex
5090 proc dohidelocalchanges {} {
5091 global nullid nullid2 lserial curview
5093 if {[commitinview $nullid $curview]} {
5094 removefakerow $nullid
5096 if {[commitinview $nullid2 $curview]} {
5097 removefakerow $nullid2
5102 # spawn off a process to do git diff-index --cached HEAD
5103 proc dodiffindex {} {
5104 global lserial showlocalchanges vfilelimit curview
5107 if {!$showlocalchanges || !$hasworktree} return
5109 set cmd "|git diff-index --cached HEAD"
5110 if {$vfilelimit($curview) ne {}} {
5111 set cmd [concat $cmd -- $vfilelimit($curview)]
5113 set fd [open $cmd r]
5114 fconfigure $fd -blocking 0
5115 set i [reg_instance $fd]
5116 filerun $fd [list readdiffindex $fd $lserial $i]
5119 proc readdiffindex {fd serial inst} {
5120 global viewmainheadid nullid nullid2 curview commitinfo commitdata lserial
5124 if {[gets $fd line] < 0} {
5130 # we only need to see one line and we don't really care what it says...
5133 if {$serial != $lserial} {
5137 # now see if there are any local changes not checked in to the index
5138 set cmd "|git diff-files"
5139 if {$vfilelimit($curview) ne {}} {
5140 set cmd [concat $cmd -- $vfilelimit($curview)]
5142 set fd [open $cmd r]
5143 fconfigure $fd -blocking 0
5144 set i [reg_instance $fd]
5145 filerun $fd [list readdifffiles $fd $serial $i]
5147 if {$isdiff && ![commitinview $nullid2 $curview]} {
5148 # add the line for the changes in the index to the graph
5149 set hl [mc "Local changes checked in to index but not committed"]
5150 set commitinfo($nullid2) [list $hl {} {} {} {} " $hl\n"]
5151 set commitdata($nullid2) "\n $hl\n"
5152 if {[commitinview $nullid $curview]} {
5153 removefakerow $nullid
5155 insertfakerow $nullid2 $viewmainheadid($curview)
5156 } elseif {!$isdiff && [commitinview $nullid2 $curview]} {
5157 if {[commitinview $nullid $curview]} {
5158 removefakerow $nullid
5160 removefakerow $nullid2
5165 proc readdifffiles {fd serial inst} {
5166 global viewmainheadid nullid nullid2 curview
5167 global commitinfo commitdata lserial
5170 if {[gets $fd line] < 0} {
5176 # we only need to see one line and we don't really care what it says...
5179 if {$serial != $lserial} {
5183 if {$isdiff && ![commitinview $nullid $curview]} {
5184 # add the line for the local diff to the graph
5185 set hl [mc "Local uncommitted changes, not checked in to index"]
5186 set commitinfo($nullid) [list $hl {} {} {} {} " $hl\n"]
5187 set commitdata($nullid) "\n $hl\n"
5188 if {[commitinview $nullid2 $curview]} {
5191 set p $viewmainheadid($curview)
5193 insertfakerow $nullid $p
5194 } elseif {!$isdiff && [commitinview $nullid $curview]} {
5195 removefakerow $nullid
5200 proc nextuse {id row} {
5201 global curview children
5203 if {[info exists children($curview,$id)]} {
5204 foreach kid $children($curview,$id) {
5205 if {![commitinview $kid $curview]} {
5208 if {[rowofcommit $kid] > $row} {
5209 return [rowofcommit $kid]
5213 if {[commitinview $id $curview]} {
5214 return [rowofcommit $id]
5219 proc prevuse {id row} {
5220 global curview children
5223 if {[info exists children($curview,$id)]} {
5224 foreach kid $children($curview,$id) {
5225 if {![commitinview $kid $curview]} break
5226 if {[rowofcommit $kid] < $row} {
5227 set ret [rowofcommit $kid]
5234 proc make_idlist {row} {
5235 global displayorder parentlist uparrowlen downarrowlen mingaplen
5236 global commitidx curview children
5238 set r [expr {$row - $mingaplen - $downarrowlen - 1}]
5242 set ra [expr {$row - $downarrowlen}]
5246 set rb [expr {$row + $uparrowlen}]
5247 if {$rb > $commitidx($curview)} {
5248 set rb $commitidx($curview)
5250 make_disporder $r [expr {$rb + 1}]
5252 for {} {$r < $ra} {incr r} {
5253 set nextid [lindex $displayorder [expr {$r + 1}]]
5254 foreach p [lindex $parentlist $r] {
5255 if {$p eq $nextid} continue
5256 set rn [nextuse $p $r]
5258 $rn <= $r + $downarrowlen + $mingaplen + $uparrowlen} {
5259 lappend ids [list [ordertoken $p] $p]
5263 for {} {$r < $row} {incr r} {
5264 set nextid [lindex $displayorder [expr {$r + 1}]]
5265 foreach p [lindex $parentlist $r] {
5266 if {$p eq $nextid} continue
5267 set rn [nextuse $p $r]
5268 if {$rn < 0 || $rn >= $row} {
5269 lappend ids [list [ordertoken $p] $p]
5273 set id [lindex $displayorder $row]
5274 lappend ids [list [ordertoken $id] $id]
5276 foreach p [lindex $parentlist $r] {
5277 set firstkid [lindex $children($curview,$p) 0]
5278 if {[rowofcommit $firstkid] < $row} {
5279 lappend ids [list [ordertoken $p] $p]
5283 set id [lindex $displayorder $r]
5285 set firstkid [lindex $children($curview,$id) 0]
5286 if {$firstkid ne {} && [rowofcommit $firstkid] < $row} {
5287 lappend ids [list [ordertoken $id] $id]
5292 foreach idx [lsort -unique $ids] {
5293 lappend idlist [lindex $idx 1]
5298 proc rowsequal {a b} {
5299 while {[set i [lsearch -exact $a {}]] >= 0} {
5300 set a [lreplace $a $i $i]
5302 while {[set i [lsearch -exact $b {}]] >= 0} {
5303 set b [lreplace $b $i $i]
5305 return [expr {$a eq $b}]
5308 proc makeupline {id row rend col} {
5309 global rowidlist uparrowlen downarrowlen mingaplen
5311 for {set r $rend} {1} {set r $rstart} {
5312 set rstart [prevuse $id $r]
5313 if {$rstart < 0} return
5314 if {$rstart < $row} break
5316 if {$rstart + $uparrowlen + $mingaplen + $downarrowlen < $rend} {
5317 set rstart [expr {$rend - $uparrowlen - 1}]
5319 for {set r $rstart} {[incr r] <= $row} {} {
5320 set idlist [lindex $rowidlist $r]
5321 if {$idlist ne {} && [lsearch -exact $idlist $id] < 0} {
5322 set col [idcol $idlist $id $col]
5323 lset rowidlist $r [linsert $idlist $col $id]
5329 proc layoutrows {row endrow} {
5330 global rowidlist rowisopt rowfinal displayorder
5331 global uparrowlen downarrowlen maxwidth mingaplen
5332 global children parentlist
5333 global commitidx viewcomplete curview
5335 make_disporder [expr {$row - 1}] [expr {$endrow + $uparrowlen}]
5338 set rm1 [expr {$row - 1}]
5339 foreach id [lindex $rowidlist $rm1] {
5344 set final [lindex $rowfinal $rm1]
5346 for {} {$row < $endrow} {incr row} {
5347 set rm1 [expr {$row - 1}]
5348 if {$rm1 < 0 || $idlist eq {}} {
5349 set idlist [make_idlist $row]
5352 set id [lindex $displayorder $rm1]
5353 set col [lsearch -exact $idlist $id]
5354 set idlist [lreplace $idlist $col $col]
5355 foreach p [lindex $parentlist $rm1] {
5356 if {[lsearch -exact $idlist $p] < 0} {
5357 set col [idcol $idlist $p $col]
5358 set idlist [linsert $idlist $col $p]
5359 # if not the first child, we have to insert a line going up
5360 if {$id ne [lindex $children($curview,$p) 0]} {
5361 makeupline $p $rm1 $row $col
5365 set id [lindex $displayorder $row]
5366 if {$row > $downarrowlen} {
5367 set termrow [expr {$row - $downarrowlen - 1}]
5368 foreach p [lindex $parentlist $termrow] {
5369 set i [lsearch -exact $idlist $p]
5370 if {$i < 0} continue
5371 set nr [nextuse $p $termrow]
5372 if {$nr < 0 || $nr >= $row + $mingaplen + $uparrowlen} {
5373 set idlist [lreplace $idlist $i $i]
5377 set col [lsearch -exact $idlist $id]
5379 set col [idcol $idlist $id]
5380 set idlist [linsert $idlist $col $id]
5381 if {$children($curview,$id) ne {}} {
5382 makeupline $id $rm1 $row $col
5385 set r [expr {$row + $uparrowlen - 1}]
5386 if {$r < $commitidx($curview)} {
5388 foreach p [lindex $parentlist $r] {
5389 if {[lsearch -exact $idlist $p] >= 0} continue
5390 set fk [lindex $children($curview,$p) 0]
5391 if {[rowofcommit $fk] < $row} {
5392 set x [idcol $idlist $p $x]
5393 set idlist [linsert $idlist $x $p]
5396 if {[incr r] < $commitidx($curview)} {
5397 set p [lindex $displayorder $r]
5398 if {[lsearch -exact $idlist $p] < 0} {
5399 set fk [lindex $children($curview,$p) 0]
5400 if {$fk ne {} && [rowofcommit $fk] < $row} {
5401 set x [idcol $idlist $p $x]
5402 set idlist [linsert $idlist $x $p]
5408 if {$final && !$viewcomplete($curview) &&
5409 $row + $uparrowlen + $mingaplen + $downarrowlen
5410 >= $commitidx($curview)} {
5413 set l [llength $rowidlist]
5415 lappend rowidlist $idlist
5417 lappend rowfinal $final
5418 } elseif {$row < $l} {
5419 if {![rowsequal $idlist [lindex $rowidlist $row]]} {
5420 lset rowidlist $row $idlist
5423 lset rowfinal $row $final
5425 set pad [ntimes [expr {$row - $l}] {}]
5426 set rowidlist [concat $rowidlist $pad]
5427 lappend rowidlist $idlist
5428 set rowfinal [concat $rowfinal $pad]
5429 lappend rowfinal $final
5430 set rowisopt [concat $rowisopt [ntimes [expr {$row - $l + 1}] 0]]
5436 proc changedrow {row} {
5437 global displayorder iddrawn rowisopt need_redisplay
5439 set l [llength $rowisopt]
5441 lset rowisopt $row 0
5442 if {$row + 1 < $l} {
5443 lset rowisopt [expr {$row + 1}] 0
5444 if {$row + 2 < $l} {
5445 lset rowisopt [expr {$row + 2}] 0
5449 set id [lindex $displayorder $row]
5450 if {[info exists iddrawn($id)]} {
5451 set need_redisplay 1
5455 proc insert_pad {row col npad} {
5458 set pad [ntimes $npad {}]
5459 set idlist [lindex $rowidlist $row]
5460 set bef [lrange $idlist 0 [expr {$col - 1}]]
5461 set aft [lrange $idlist $col end]
5462 set i [lsearch -exact $aft {}]
5464 set aft [lreplace $aft $i $i]
5466 lset rowidlist $row [concat $bef $pad $aft]
5470 proc optimize_rows {row col endrow} {
5471 global rowidlist rowisopt displayorder curview children
5476 for {} {$row < $endrow} {incr row; set col 0} {
5477 if {[lindex $rowisopt $row]} continue
5479 set y0 [expr {$row - 1}]
5480 set ym [expr {$row - 2}]
5481 set idlist [lindex $rowidlist $row]
5482 set previdlist [lindex $rowidlist $y0]
5483 if {$idlist eq {} || $previdlist eq {}} continue
5485 set pprevidlist [lindex $rowidlist $ym]
5486 if {$pprevidlist eq {}} continue
5492 for {} {$col < [llength $idlist]} {incr col} {
5493 set id [lindex $idlist $col]
5494 if {[lindex $previdlist $col] eq $id} continue
5499 set x0 [lsearch -exact $previdlist $id]
5500 if {$x0 < 0} continue
5501 set z [expr {$x0 - $col}]
5505 set xm [lsearch -exact $pprevidlist $id]
5507 set z0 [expr {$xm - $x0}]
5511 # if row y0 is the first child of $id then it's not an arrow
5512 if {[lindex $children($curview,$id) 0] ne
5513 [lindex $displayorder $y0]} {
5517 if {!$isarrow && $id ne [lindex $displayorder $row] &&
5518 [lsearch -exact [lindex $rowidlist [expr {$row+1}]] $id] < 0} {
5521 # Looking at lines from this row to the previous row,
5522 # make them go straight up if they end in an arrow on
5523 # the previous row; otherwise make them go straight up
5525 if {$z < -1 || ($z < 0 && $isarrow)} {
5526 # Line currently goes left too much;
5527 # insert pads in the previous row, then optimize it
5528 set npad [expr {-1 - $z + $isarrow}]
5529 insert_pad $y0 $x0 $npad
5531 optimize_rows $y0 $x0 $row
5533 set previdlist [lindex $rowidlist $y0]
5534 set x0 [lsearch -exact $previdlist $id]
5535 set z [expr {$x0 - $col}]
5537 set pprevidlist [lindex $rowidlist $ym]
5538 set xm [lsearch -exact $pprevidlist $id]
5539 set z0 [expr {$xm - $x0}]
5541 } elseif {$z > 1 || ($z > 0 && $isarrow)} {
5542 # Line currently goes right too much;
5543 # insert pads in this line
5544 set npad [expr {$z - 1 + $isarrow}]
5545 insert_pad $row $col $npad
5546 set idlist [lindex $rowidlist $row]
5548 set z [expr {$x0 - $col}]
5551 if {$z0 eq {} && !$isarrow && $ym >= 0} {
5552 # this line links to its first child on row $row-2
5553 set id [lindex $displayorder $ym]
5554 set xc [lsearch -exact $pprevidlist $id]
5556 set z0 [expr {$xc - $x0}]
5559 # avoid lines jigging left then immediately right
5560 if {$z0 ne {} && $z < 0 && $z0 > 0} {
5561 insert_pad $y0 $x0 1
5563 optimize_rows $y0 $x0 $row
5564 set previdlist [lindex $rowidlist $y0]
5568 # Find the first column that doesn't have a line going right
5569 for {set col [llength $idlist]} {[incr col -1] >= 0} {} {
5570 set id [lindex $idlist $col]
5571 if {$id eq {}} break
5572 set x0 [lsearch -exact $previdlist $id]
5574 # check if this is the link to the first child
5575 set kid [lindex $displayorder $y0]
5576 if {[lindex $children($curview,$id) 0] eq $kid} {
5577 # it is, work out offset to child
5578 set x0 [lsearch -exact $previdlist $kid]
5581 if {$x0 <= $col} break
5583 # Insert a pad at that column as long as it has a line and
5584 # isn't the last column
5585 if {$x0 >= 0 && [incr col] < [llength $idlist]} {
5586 set idlist [linsert $idlist $col {}]
5587 lset rowidlist $row $idlist
5595 global canvx0 linespc
5596 return [expr {$canvx0 + $col * $linespc}]
5600 global canvy0 linespc
5601 return [expr {$canvy0 + $row * $linespc}]
5604 proc linewidth {id} {
5605 global thickerline lthickness
5608 if {[info exists thickerline] && $id eq $thickerline} {
5609 set wid [expr {2 * $lthickness}]
5614 proc rowranges {id} {
5615 global curview children uparrowlen downarrowlen
5618 set kids $children($curview,$id)
5624 foreach child $kids {
5625 if {![commitinview $child $curview]} break
5626 set row [rowofcommit $child]
5627 if {![info exists prev]} {
5628 lappend ret [expr {$row + 1}]
5630 if {$row <= $prevrow} {
5631 puts "oops children of [shortids $id] out of order [shortids $child] $row <= [shortids $prev] $prevrow"
5633 # see if the line extends the whole way from prevrow to row
5634 if {$row > $prevrow + $uparrowlen + $downarrowlen &&
5635 [lsearch -exact [lindex $rowidlist \
5636 [expr {int(($row + $prevrow) / 2)}]] $id] < 0} {
5637 # it doesn't, see where it ends
5638 set r [expr {$prevrow + $downarrowlen}]
5639 if {[lsearch -exact [lindex $rowidlist $r] $id] < 0} {
5640 while {[incr r -1] > $prevrow &&
5641 [lsearch -exact [lindex $rowidlist $r] $id] < 0} {}
5643 while {[incr r] <= $row &&
5644 [lsearch -exact [lindex $rowidlist $r] $id] >= 0} {}
5648 # see where it starts up again
5649 set r [expr {$row - $uparrowlen}]
5650 if {[lsearch -exact [lindex $rowidlist $r] $id] < 0} {
5651 while {[incr r] < $row &&
5652 [lsearch -exact [lindex $rowidlist $r] $id] < 0} {}
5654 while {[incr r -1] >= $prevrow &&
5655 [lsearch -exact [lindex $rowidlist $r] $id] >= 0} {}
5661 if {$child eq $id} {
5670 proc drawlineseg {id row endrow arrowlow} {
5671 global rowidlist displayorder iddrawn linesegs
5672 global canv colormap linespc curview maxlinelen parentlist
5674 set cols [list [lsearch -exact [lindex $rowidlist $row] $id]]
5675 set le [expr {$row + 1}]
5678 set c [lsearch -exact [lindex $rowidlist $le] $id]
5684 set x [lindex $displayorder $le]
5689 if {[info exists iddrawn($x)] || $le == $endrow} {
5690 set c [lsearch -exact [lindex $rowidlist [expr {$le+1}]] $id]
5706 if {[info exists linesegs($id)]} {
5707 set lines $linesegs($id)
5709 set r0 [lindex $li 0]
5711 if {$r0 == $le && [lindex $li 1] - $row <= $maxlinelen} {
5721 set li [lindex $lines [expr {$i-1}]]
5722 set r1 [lindex $li 1]
5723 if {$r1 == $row && $le - [lindex $li 0] <= $maxlinelen} {
5728 set x [lindex $cols [expr {$le - $row}]]
5729 set xp [lindex $cols [expr {$le - 1 - $row}]]
5730 set dir [expr {$xp - $x}]
5732 set ith [lindex $lines $i 2]
5733 set coords [$canv coords $ith]
5734 set ah [$canv itemcget $ith -arrow]
5735 set arrowhigh [expr {$ah eq "first" || $ah eq "both"}]
5736 set x2 [lindex $cols [expr {$le + 1 - $row}]]
5737 if {$x2 ne {} && $x - $x2 == $dir} {
5738 set coords [lrange $coords 0 end-2]
5741 set coords [list [xc $le $x] [yc $le]]
5744 set itl [lindex $lines [expr {$i-1}] 2]
5745 set al [$canv itemcget $itl -arrow]
5746 set arrowlow [expr {$al eq "last" || $al eq "both"}]
5747 } elseif {$arrowlow} {
5748 if {[lsearch -exact [lindex $rowidlist [expr {$row-1}]] $id] >= 0 ||
5749 [lsearch -exact [lindex $parentlist [expr {$row-1}]] $id] >= 0} {
5753 set arrow [lindex {none first last both} [expr {$arrowhigh + 2*$arrowlow}]]
5754 for {set y $le} {[incr y -1] > $row} {} {
5756 set xp [lindex $cols [expr {$y - 1 - $row}]]
5757 set ndir [expr {$xp - $x}]
5758 if {$dir != $ndir || $xp < 0} {
5759 lappend coords [xc $y $x] [yc $y]
5765 # join parent line to first child
5766 set ch [lindex $displayorder $row]
5767 set xc [lsearch -exact [lindex $rowidlist $row] $ch]
5769 puts "oops: drawlineseg: child $ch not on row $row"
5770 } elseif {$xc != $x} {
5771 if {($arrowhigh && $le == $row + 1) || $dir == 0} {
5772 set d [expr {int(0.5 * $linespc)}]
5775 set x2 [expr {$x1 - $d}]
5777 set x2 [expr {$x1 + $d}]
5780 set y1 [expr {$y2 + $d}]
5781 lappend coords $x1 $y1 $x2 $y2
5782 } elseif {$xc < $x - 1} {
5783 lappend coords [xc $row [expr {$x-1}]] [yc $row]
5784 } elseif {$xc > $x + 1} {
5785 lappend coords [xc $row [expr {$x+1}]] [yc $row]
5789 lappend coords [xc $row $x] [yc $row]
5791 set xn [xc $row $xp]
5793 lappend coords $xn $yn
5797 set t [$canv create line $coords -width [linewidth $id] \
5798 -fill $colormap($id) -tags lines.$id -arrow $arrow]
5801 set lines [linsert $lines $i [list $row $le $t]]
5803 $canv coords $ith $coords
5804 if {$arrow ne $ah} {
5805 $canv itemconf $ith -arrow $arrow
5807 lset lines $i 0 $row
5810 set xo [lsearch -exact [lindex $rowidlist [expr {$row - 1}]] $id]
5811 set ndir [expr {$xo - $xp}]
5812 set clow [$canv coords $itl]
5813 if {$dir == $ndir} {
5814 set clow [lrange $clow 2 end]
5816 set coords [concat $coords $clow]
5818 lset lines [expr {$i-1}] 1 $le
5820 # coalesce two pieces
5822 set b [lindex $lines [expr {$i-1}] 0]
5823 set e [lindex $lines $i 1]
5824 set lines [lreplace $lines [expr {$i-1}] $i [list $b $e $itl]]
5826 $canv coords $itl $coords
5827 if {$arrow ne $al} {
5828 $canv itemconf $itl -arrow $arrow
5832 set linesegs($id) $lines
5836 proc drawparentlinks {id row} {
5837 global rowidlist canv colormap curview parentlist
5838 global idpos linespc
5840 set rowids [lindex $rowidlist $row]
5841 set col [lsearch -exact $rowids $id]
5842 if {$col < 0} return
5843 set olds [lindex $parentlist $row]
5844 set row2 [expr {$row + 1}]
5845 set x [xc $row $col]
5848 set d [expr {int(0.5 * $linespc)}]
5849 set ymid [expr {$y + $d}]
5850 set ids [lindex $rowidlist $row2]
5851 # rmx = right-most X coord used
5854 set i [lsearch -exact $ids $p]
5856 puts "oops, parent $p of $id not in list"
5859 set x2 [xc $row2 $i]
5863 set j [lsearch -exact $rowids $p]
5865 # drawlineseg will do this one for us
5869 # should handle duplicated parents here...
5870 set coords [list $x $y]
5872 # if attaching to a vertical segment, draw a smaller
5873 # slant for visual distinctness
5876 lappend coords [expr {$x2 + $d}] $y $x2 $ymid
5878 lappend coords [expr {$x2 - $d}] $y $x2 $ymid
5880 } elseif {$i < $col && $i < $j} {
5881 # segment slants towards us already
5882 lappend coords [xc $row $j] $y
5884 if {$i < $col - 1} {
5885 lappend coords [expr {$x2 + $linespc}] $y
5886 } elseif {$i > $col + 1} {
5887 lappend coords [expr {$x2 - $linespc}] $y
5889 lappend coords $x2 $y2
5892 lappend coords $x2 $y2
5894 set t [$canv create line $coords -width [linewidth $p] \
5895 -fill $colormap($p) -tags lines.$p]
5899 if {$rmx > [lindex $idpos($id) 1]} {
5900 lset idpos($id) 1 $rmx
5905 proc drawlines {id} {
5908 $canv itemconf lines.$id -width [linewidth $id]
5911 proc drawcmittext {id row col} {
5912 global linespc canv canv2 canv3 fgcolor curview
5913 global cmitlisted commitinfo rowidlist parentlist
5914 global rowtextx idpos idtags idheads idotherrefs
5915 global linehtag linentag linedtag selectedline
5916 global canvxmax boldids boldnameids fgcolor markedid
5917 global mainheadid nullid nullid2 circleitem circlecolors ctxbut
5919 # listed is 0 for boundary, 1 for normal, 2 for negative, 3 for left, 4 for right
5920 set listed $cmitlisted($curview,$id)
5921 if {$id eq $nullid} {
5923 } elseif {$id eq $nullid2} {
5925 } elseif {$id eq $mainheadid} {
5928 set ofill [lindex $circlecolors $listed]
5930 set x [xc $row $col]
5932 set orad [expr {$linespc / 3}]
5934 set t [$canv create oval [expr {$x - $orad}] [expr {$y - $orad}] \
5935 [expr {$x + $orad - 1}] [expr {$y + $orad - 1}] \
5936 -fill $ofill -outline $fgcolor -width 1 -tags circle]
5937 } elseif {$listed == 3} {
5938 # triangle pointing left for left-side commits
5939 set t [$canv create polygon \
5940 [expr {$x - $orad}] $y \
5941 [expr {$x + $orad - 1}] [expr {$y - $orad}] \
5942 [expr {$x + $orad - 1}] [expr {$y + $orad - 1}] \
5943 -fill $ofill -outline $fgcolor -width 1 -tags circle]
5945 # triangle pointing right for right-side commits
5946 set t [$canv create polygon \
5947 [expr {$x + $orad - 1}] $y \
5948 [expr {$x - $orad}] [expr {$y - $orad}] \
5949 [expr {$x - $orad}] [expr {$y + $orad - 1}] \
5950 -fill $ofill -outline $fgcolor -width 1 -tags circle]
5952 set circleitem($row) $t
5954 $canv bind $t <1> {selcanvline {} %x %y}
5955 set rmx [llength [lindex $rowidlist $row]]
5956 set olds [lindex $parentlist $row]
5958 set nextids [lindex $rowidlist [expr {$row + 1}]]
5960 set i [lsearch -exact $nextids $p]
5966 set xt [xc $row $rmx]
5967 set rowtextx($row) $xt
5968 set idpos($id) [list $x $xt $y]
5969 if {[info exists idtags($id)] || [info exists idheads($id)]
5970 || [info exists idotherrefs($id)]} {
5971 set xt [drawtags $id $x $xt $y]
5973 if {[lindex $commitinfo($id) 6] > 0} {
5974 set xt [drawnotesign $xt $y]
5976 set headline [lindex $commitinfo($id) 0]
5977 set name [lindex $commitinfo($id) 1]
5978 set date [lindex $commitinfo($id) 2]
5979 set date [formatdate $date]
5982 set isbold [ishighlighted $id]
5985 set font mainfontbold
5987 lappend boldnameids $id
5988 set nfont mainfontbold
5991 set linehtag($id) [$canv create text $xt $y -anchor w -fill $fgcolor \
5992 -text $headline -font $font -tags text]
5993 $canv bind $linehtag($id) $ctxbut "rowmenu %X %Y $id"
5994 set linentag($id) [$canv2 create text 3 $y -anchor w -fill $fgcolor \
5995 -text $name -font $nfont -tags text]
5996 set linedtag($id) [$canv3 create text 3 $y -anchor w -fill $fgcolor \
5997 -text $date -font mainfont -tags text]
5998 if {$selectedline == $row} {
6001 if {[info exists markedid] && $markedid eq $id} {
6004 set xr [expr {$xt + [font measure $font $headline]}]
6005 if {$xr > $canvxmax} {
6011 proc drawcmitrow {row} {
6012 global displayorder rowidlist nrows_drawn
6013 global iddrawn markingmatches
6014 global commitinfo numcommits
6015 global filehighlight fhighlights findpattern nhighlights
6016 global hlview vhighlights
6017 global highlight_related rhighlights
6019 if {$row >= $numcommits} return
6021 set id [lindex $displayorder $row]
6022 if {[info exists hlview] && ![info exists vhighlights($id)]} {
6023 askvhighlight $row $id
6025 if {[info exists filehighlight] && ![info exists fhighlights($id)]} {
6026 askfilehighlight $row $id
6028 if {$findpattern ne {} && ![info exists nhighlights($id)]} {
6029 askfindhighlight $row $id
6031 if {$highlight_related ne [mc "None"] && ![info exists rhighlights($id)]} {
6032 askrelhighlight $row $id
6034 if {![info exists iddrawn($id)]} {
6035 set col [lsearch -exact [lindex $rowidlist $row] $id]
6037 puts "oops, row $row id $id not in list"
6040 if {![info exists commitinfo($id)]} {
6044 drawcmittext $id $row $col
6048 if {$markingmatches} {
6049 markrowmatches $row $id
6053 proc drawcommits {row {endrow {}}} {
6054 global numcommits iddrawn displayorder curview need_redisplay
6055 global parentlist rowidlist rowfinal uparrowlen downarrowlen nrows_drawn
6060 if {$endrow eq {}} {
6063 if {$endrow >= $numcommits} {
6064 set endrow [expr {$numcommits - 1}]
6067 set rl1 [expr {$row - $downarrowlen - 3}]
6071 set ro1 [expr {$row - 3}]
6075 set r2 [expr {$endrow + $uparrowlen + 3}]
6076 if {$r2 > $numcommits} {
6079 for {set r $rl1} {$r < $r2} {incr r} {
6080 if {[lindex $rowidlist $r] ne {} && [lindex $rowfinal $r]} {
6084 set rl1 [expr {$r + 1}]
6090 optimize_rows $ro1 0 $r2
6091 if {$need_redisplay || $nrows_drawn > 2000} {
6095 # make the lines join to already-drawn rows either side
6096 set r [expr {$row - 1}]
6097 if {$r < 0 || ![info exists iddrawn([lindex $displayorder $r])]} {
6100 set er [expr {$endrow + 1}]
6101 if {$er >= $numcommits ||
6102 ![info exists iddrawn([lindex $displayorder $er])]} {
6105 for {} {$r <= $er} {incr r} {
6106 set id [lindex $displayorder $r]
6107 set wasdrawn [info exists iddrawn($id)]
6109 if {$r == $er} break
6110 set nextid [lindex $displayorder [expr {$r + 1}]]
6111 if {$wasdrawn && [info exists iddrawn($nextid)]} continue
6112 drawparentlinks $id $r
6114 set rowids [lindex $rowidlist $r]
6115 foreach lid $rowids {
6116 if {$lid eq {}} continue
6117 if {[info exists lineend($lid)] && $lineend($lid) > $r} continue
6119 # see if this is the first child of any of its parents
6120 foreach p [lindex $parentlist $r] {
6121 if {[lsearch -exact $rowids $p] < 0} {
6122 # make this line extend up to the child
6123 set lineend($p) [drawlineseg $p $r $er 0]
6127 set lineend($lid) [drawlineseg $lid $r $er 1]
6133 proc undolayout {row} {
6134 global uparrowlen mingaplen downarrowlen
6135 global rowidlist rowisopt rowfinal need_redisplay
6137 set r [expr {$row - ($uparrowlen + $mingaplen + $downarrowlen)}]
6141 if {[llength $rowidlist] > $r} {
6143 set rowidlist [lrange $rowidlist 0 $r]
6144 set rowfinal [lrange $rowfinal 0 $r]
6145 set rowisopt [lrange $rowisopt 0 $r]
6146 set need_redisplay 1
6151 proc drawvisible {} {
6152 global canv linespc curview vrowmod selectedline targetrow targetid
6153 global need_redisplay cscroll numcommits
6155 set fs [$canv yview]
6156 set ymax [lindex [$canv cget -scrollregion] 3]
6157 if {$ymax eq {} || $ymax == 0 || $numcommits == 0} return
6158 set f0 [lindex $fs 0]
6159 set f1 [lindex $fs 1]
6160 set y0 [expr {int($f0 * $ymax)}]
6161 set y1 [expr {int($f1 * $ymax)}]
6163 if {[info exists targetid]} {
6164 if {[commitinview $targetid $curview]} {
6165 set r [rowofcommit $targetid]
6166 if {$r != $targetrow} {
6167 # Fix up the scrollregion and change the scrolling position
6168 # now that our target row has moved.
6169 set diff [expr {($r - $targetrow) * $linespc}]
6172 set ymax [lindex [$canv cget -scrollregion] 3]
6175 set f0 [expr {$y0 / $ymax}]
6176 set f1 [expr {$y1 / $ymax}]
6177 allcanvs yview moveto $f0
6178 $cscroll set $f0 $f1
6179 set need_redisplay 1
6186 set row [expr {int(($y0 - 3) / $linespc) - 1}]
6187 set endrow [expr {int(($y1 - 3) / $linespc) + 1}]
6188 if {$endrow >= $vrowmod($curview)} {
6189 update_arcrows $curview
6191 if {$selectedline ne {} &&
6192 $row <= $selectedline && $selectedline <= $endrow} {
6193 set targetrow $selectedline
6194 } elseif {[info exists targetid]} {
6195 set targetrow [expr {int(($row + $endrow) / 2)}]
6197 if {[info exists targetrow]} {
6198 if {$targetrow >= $numcommits} {
6199 set targetrow [expr {$numcommits - 1}]
6201 set targetid [commitonrow $targetrow]
6203 drawcommits $row $endrow
6206 proc clear_display {} {
6207 global iddrawn linesegs need_redisplay nrows_drawn
6208 global vhighlights fhighlights nhighlights rhighlights
6209 global linehtag linentag linedtag boldids boldnameids
6212 catch {unset iddrawn}
6213 catch {unset linesegs}
6214 catch {unset linehtag}
6215 catch {unset linentag}
6216 catch {unset linedtag}
6219 catch {unset vhighlights}
6220 catch {unset fhighlights}
6221 catch {unset nhighlights}
6222 catch {unset rhighlights}
6223 set need_redisplay 0
6227 proc findcrossings {id} {
6228 global rowidlist parentlist numcommits displayorder
6232 foreach {s e} [rowranges $id] {
6233 if {$e >= $numcommits} {
6234 set e [expr {$numcommits - 1}]
6236 if {$e <= $s} continue
6237 for {set row $e} {[incr row -1] >= $s} {} {
6238 set x [lsearch -exact [lindex $rowidlist $row] $id]
6240 set olds [lindex $parentlist $row]
6241 set kid [lindex $displayorder $row]
6242 set kidx [lsearch -exact [lindex $rowidlist $row] $kid]
6243 if {$kidx < 0} continue
6244 set nextrow [lindex $rowidlist [expr {$row + 1}]]
6246 set px [lsearch -exact $nextrow $p]
6247 if {$px < 0} continue
6248 if {($kidx < $x && $x < $px) || ($px < $x && $x < $kidx)} {
6249 if {[lsearch -exact $ccross $p] >= 0} continue
6250 if {$x == $px + ($kidx < $px? -1: 1)} {
6252 } elseif {[lsearch -exact $cross $p] < 0} {
6259 return [concat $ccross {{}} $cross]
6262 proc assigncolor {id} {
6263 global colormap colors nextcolor
6264 global parents children children curview
6266 if {[info exists colormap($id)]} return
6267 set ncolors [llength $colors]
6268 if {[info exists children($curview,$id)]} {
6269 set kids $children($curview,$id)
6273 if {[llength $kids] == 1} {
6274 set child [lindex $kids 0]
6275 if {[info exists colormap($child)]
6276 && [llength $parents($curview,$child)] == 1} {
6277 set colormap($id) $colormap($child)
6283 foreach x [findcrossings $id] {
6285 # delimiter between corner crossings and other crossings
6286 if {[llength $badcolors] >= $ncolors - 1} break
6287 set origbad $badcolors
6289 if {[info exists colormap($x)]
6290 && [lsearch -exact $badcolors $colormap($x)] < 0} {
6291 lappend badcolors $colormap($x)
6294 if {[llength $badcolors] >= $ncolors} {
6295 set badcolors $origbad
6297 set origbad $badcolors
6298 if {[llength $badcolors] < $ncolors - 1} {
6299 foreach child $kids {
6300 if {[info exists colormap($child)]
6301 && [lsearch -exact $badcolors $colormap($child)] < 0} {
6302 lappend badcolors $colormap($child)
6304 foreach p $parents($curview,$child) {
6305 if {[info exists colormap($p)]
6306 && [lsearch -exact $badcolors $colormap($p)] < 0} {
6307 lappend badcolors $colormap($p)
6311 if {[llength $badcolors] >= $ncolors} {
6312 set badcolors $origbad
6315 for {set i 0} {$i <= $ncolors} {incr i} {
6316 set c [lindex $colors $nextcolor]
6317 if {[incr nextcolor] >= $ncolors} {
6320 if {[lsearch -exact $badcolors $c]} break
6322 set colormap($id) $c
6325 proc bindline {t id} {
6328 $canv bind $t <Enter> "lineenter %x %y $id"
6329 $canv bind $t <Motion> "linemotion %x %y $id"
6330 $canv bind $t <Leave> "lineleave $id"
6331 $canv bind $t <Button-1> "lineclick %x %y $id 1"
6334 proc drawtags {id x xt y1} {
6335 global idtags idheads idotherrefs mainhead
6336 global linespc lthickness
6337 global canv rowtextx curview fgcolor bgcolor ctxbut
6342 if {[info exists idtags($id)]} {
6343 set marks $idtags($id)
6344 set ntags [llength $marks]
6346 if {[info exists idheads($id)]} {
6347 set marks [concat $marks $idheads($id)]
6348 set nheads [llength $idheads($id)]
6350 if {[info exists idotherrefs($id)]} {
6351 set marks [concat $marks $idotherrefs($id)]
6357 set delta [expr {int(0.5 * ($linespc - $lthickness))}]
6358 set yt [expr {$y1 - 0.5 * $linespc}]
6359 set yb [expr {$yt + $linespc - 1}]
6363 foreach tag $marks {
6365 if {$i >= $ntags && $i < $ntags + $nheads && $tag eq $mainhead} {
6366 set wid [font measure mainfontbold $tag]
6368 set wid [font measure mainfont $tag]
6372 set xt [expr {$xt + $delta + $wid + $lthickness + $linespc}]
6374 set t [$canv create line $x $y1 [lindex $xvals end] $y1 \
6375 -width $lthickness -fill black -tags tag.$id]
6377 foreach tag $marks x $xvals wid $wvals {
6378 set tag_quoted [string map {% %%} $tag]
6379 set xl [expr {$x + $delta}]
6380 set xr [expr {$x + $delta + $wid + $lthickness}]
6382 if {[incr ntags -1] >= 0} {
6384 set t [$canv create polygon $x [expr {$yt + $delta}] $xl $yt \
6385 $xr $yt $xr $yb $xl $yb $x [expr {$yb - $delta}] \
6386 -width 1 -outline black -fill yellow -tags tag.$id]
6387 $canv bind $t <1> [list showtag $tag_quoted 1]
6388 set rowtextx([rowofcommit $id]) [expr {$xr + $linespc}]
6390 # draw a head or other ref
6391 if {[incr nheads -1] >= 0} {
6393 if {$tag eq $mainhead} {
6394 set font mainfontbold
6399 set xl [expr {$xl - $delta/2}]
6400 $canv create polygon $x $yt $xr $yt $xr $yb $x $yb \
6401 -width 1 -outline black -fill $col -tags tag.$id
6402 if {[regexp {^(remotes/.*/|remotes/)} $tag match remoteprefix]} {
6403 set rwid [font measure mainfont $remoteprefix]
6404 set xi [expr {$x + 1}]
6405 set yti [expr {$yt + 1}]
6406 set xri [expr {$x + $rwid}]
6407 $canv create polygon $xi $yti $xri $yti $xri $yb $xi $yb \
6408 -width 0 -fill "#ffddaa" -tags tag.$id
6411 set t [$canv create text $xl $y1 -anchor w -text $tag -fill $fgcolor \
6412 -font $font -tags [list tag.$id text]]
6414 $canv bind $t <1> [list showtag $tag_quoted 1]
6415 } elseif {$nheads >= 0} {
6416 $canv bind $t $ctxbut [list headmenu %X %Y $id $tag_quoted]
6422 proc drawnotesign {xt y} {
6423 global linespc canv fgcolor
6425 set orad [expr {$linespc / 3}]
6426 set t [$canv create rectangle [expr {$xt - $orad}] [expr {$y - $orad}] \
6427 [expr {$xt + $orad - 1}] [expr {$y + $orad - 1}] \
6428 -fill yellow -outline $fgcolor -width 1 -tags circle]
6429 set xt [expr {$xt + $orad * 3}]
6433 proc xcoord {i level ln} {
6434 global canvx0 xspc1 xspc2
6436 set x [expr {$canvx0 + $i * $xspc1($ln)}]
6437 if {$i > 0 && $i == $level} {
6438 set x [expr {$x + 0.5 * ($xspc2 - $xspc1($ln))}]
6439 } elseif {$i > $level} {
6440 set x [expr {$x + $xspc2 - $xspc1($ln)}]
6445 proc show_status {msg} {
6449 $canv create text 3 3 -anchor nw -text $msg -font mainfont \
6450 -tags text -fill $fgcolor
6453 # Don't change the text pane cursor if it is currently the hand cursor,
6454 # showing that we are over a sha1 ID link.
6455 proc settextcursor {c} {
6456 global ctext curtextcursor
6458 if {[$ctext cget -cursor] == $curtextcursor} {
6459 $ctext config -cursor $c
6461 set curtextcursor $c
6464 proc nowbusy {what {name {}}} {
6465 global isbusy busyname statusw
6467 if {[array names isbusy] eq {}} {
6468 . config -cursor watch
6472 set busyname($what) $name
6474 $statusw conf -text $name
6478 proc notbusy {what} {
6479 global isbusy maincursor textcursor busyname statusw
6483 if {$busyname($what) ne {} &&
6484 [$statusw cget -text] eq $busyname($what)} {
6485 $statusw conf -text {}
6488 if {[array names isbusy] eq {}} {
6489 . config -cursor $maincursor
6490 settextcursor $textcursor
6494 proc findmatches {f} {
6495 global findtype findstring
6496 if {$findtype == [mc "Regexp"]} {
6497 set matches [regexp -indices -all -inline $findstring $f]
6500 if {$findtype == [mc "IgnCase"]} {
6501 set f [string tolower $f]
6502 set fs [string tolower $fs]
6506 set l [string length $fs]
6507 while {[set j [string first $fs $f $i]] >= 0} {
6508 lappend matches [list $j [expr {$j+$l-1}]]
6509 set i [expr {$j + $l}]
6515 proc dofind {{dirn 1} {wrap 1}} {
6516 global findstring findstartline findcurline selectedline numcommits
6517 global gdttype filehighlight fh_serial find_dirn findallowwrap
6519 if {[info exists find_dirn]} {
6520 if {$find_dirn == $dirn} return
6524 if {$findstring eq {} || $numcommits == 0} return
6525 if {$selectedline eq {}} {
6526 set findstartline [lindex [visiblerows] [expr {$dirn < 0}]]
6528 set findstartline $selectedline
6530 set findcurline $findstartline
6531 nowbusy finding [mc "Searching"]
6532 if {$gdttype ne [mc "containing:"] && ![info exists filehighlight]} {
6533 after cancel do_file_hl $fh_serial
6534 do_file_hl $fh_serial
6537 set findallowwrap $wrap
6541 proc stopfinding {} {
6542 global find_dirn findcurline fprogcoord
6544 if {[info exists find_dirn]} {
6555 global commitdata commitinfo numcommits findpattern findloc
6556 global findstartline findcurline findallowwrap
6557 global find_dirn gdttype fhighlights fprogcoord
6558 global curview varcorder vrownum varccommits vrowmod
6560 if {![info exists find_dirn]} {
6563 set fldtypes [list [mc "Headline"] [mc "Author"] "" [mc "Committer"] "" [mc "Comments"]]
6566 if {$find_dirn > 0} {
6568 if {$l >= $numcommits} {
6571 if {$l <= $findstartline} {
6572 set lim [expr {$findstartline + 1}]
6575 set moretodo $findallowwrap
6582 if {$l >= $findstartline} {
6583 set lim [expr {$findstartline - 1}]
6586 set moretodo $findallowwrap
6589 set n [expr {($lim - $l) * $find_dirn}]
6594 if {$l + ($find_dirn > 0? $n: 1) > $vrowmod($curview)} {
6595 update_arcrows $curview
6599 set ai [bsearch $vrownum($curview) $l]
6600 set a [lindex $varcorder($curview) $ai]
6601 set arow [lindex $vrownum($curview) $ai]
6602 set ids [lindex $varccommits($curview,$a)]
6603 set arowend [expr {$arow + [llength $ids]}]
6604 if {$gdttype eq [mc "containing:"]} {
6605 for {} {$n > 0} {incr n -1; incr l $find_dirn} {
6606 if {$l < $arow || $l >= $arowend} {
6608 set a [lindex $varcorder($curview) $ai]
6609 set arow [lindex $vrownum($curview) $ai]
6610 set ids [lindex $varccommits($curview,$a)]
6611 set arowend [expr {$arow + [llength $ids]}]
6613 set id [lindex $ids [expr {$l - $arow}]]
6614 # shouldn't happen unless git log doesn't give all the commits...
6615 if {![info exists commitdata($id)] ||
6616 ![doesmatch $commitdata($id)]} {
6619 if {![info exists commitinfo($id)]} {
6622 set info $commitinfo($id)
6623 foreach f $info ty $fldtypes {
6624 if {$ty eq ""} continue
6625 if {($findloc eq [mc "All fields"] || $findloc eq $ty) &&
6634 for {} {$n > 0} {incr n -1; incr l $find_dirn} {
6635 if {$l < $arow || $l >= $arowend} {
6637 set a [lindex $varcorder($curview) $ai]
6638 set arow [lindex $vrownum($curview) $ai]
6639 set ids [lindex $varccommits($curview,$a)]
6640 set arowend [expr {$arow + [llength $ids]}]
6642 set id [lindex $ids [expr {$l - $arow}]]
6643 if {![info exists fhighlights($id)]} {
6644 # this sets fhighlights($id) to -1
6645 askfilehighlight $l $id
6647 if {$fhighlights($id) > 0} {
6651 if {$fhighlights($id) < 0} {
6654 set findcurline [expr {$l - $find_dirn}]
6659 if {$found || ($domore && !$moretodo)} {
6675 set findcurline [expr {$l - $find_dirn}]
6677 set n [expr {($findcurline - $findstartline) * $find_dirn - 1}]
6681 set fprogcoord [expr {$n * 1.0 / $numcommits}]
6686 proc findselectline {l} {
6687 global findloc commentend ctext findcurline markingmatches gdttype
6689 set markingmatches [expr {$gdttype eq [mc "containing:"]}]
6692 if {$markingmatches &&
6693 ($findloc eq [mc "All fields"] || $findloc eq [mc "Comments"])} {
6694 # highlight the matches in the comments
6695 set f [$ctext get 1.0 $commentend]
6696 set matches [findmatches $f]
6697 foreach match $matches {
6698 set start [lindex $match 0]
6699 set end [expr {[lindex $match 1] + 1}]
6700 $ctext tag add found "1.0 + $start c" "1.0 + $end c"
6706 # mark the bits of a headline or author that match a find string
6707 proc markmatches {canv l str tag matches font row} {
6710 set bbox [$canv bbox $tag]
6711 set x0 [lindex $bbox 0]
6712 set y0 [lindex $bbox 1]
6713 set y1 [lindex $bbox 3]
6714 foreach match $matches {
6715 set start [lindex $match 0]
6716 set end [lindex $match 1]
6717 if {$start > $end} continue
6718 set xoff [font measure $font [string range $str 0 [expr {$start-1}]]]
6719 set xlen [font measure $font [string range $str 0 [expr {$end}]]]
6720 set t [$canv create rect [expr {$x0+$xoff}] $y0 \
6721 [expr {$x0+$xlen+2}] $y1 \
6722 -outline {} -tags [list match$l matches] -fill yellow]
6724 if {$row == $selectedline} {
6725 $canv raise $t secsel
6730 proc unmarkmatches {} {
6731 global markingmatches
6733 allcanvs delete matches
6734 set markingmatches 0
6738 proc selcanvline {w x y} {
6739 global canv canvy0 ctext linespc
6741 set ymax [lindex [$canv cget -scrollregion] 3]
6742 if {$ymax == {}} return
6743 set yfrac [lindex [$canv yview] 0]
6744 set y [expr {$y + $yfrac * $ymax}]
6745 set l [expr {int(($y - $canvy0) / $linespc + 0.5)}]
6750 set xmax [lindex [$canv cget -scrollregion] 2]
6751 set xleft [expr {[lindex [$canv xview] 0] * $xmax}]
6752 if {![info exists rowtextx($l)] || $xleft + $x < $rowtextx($l)} return
6758 proc commit_descriptor {p} {
6760 if {![info exists commitinfo($p)]} {
6764 if {[llength $commitinfo($p)] > 1} {
6765 set l [lindex $commitinfo($p) 0]
6770 # append some text to the ctext widget, and make any SHA1 ID
6771 # that we know about be a clickable link.
6772 proc appendwithlinks {text tags} {
6773 global ctext linknum curview
6775 set start [$ctext index "end - 1c"]
6776 $ctext insert end $text $tags
6777 set links [regexp -indices -all -inline {(?:\m|-g)[0-9a-f]{6,40}\M} $text]
6781 set linkid [string range $text $s $e]
6783 $ctext tag delete link$linknum
6784 $ctext tag add link$linknum "$start + $s c" "$start + $e c"
6785 setlink $linkid link$linknum
6790 proc setlink {id lk} {
6791 global curview ctext pendinglinks
6793 if {[string range $id 0 1] eq "-g"} {
6794 set id [string range $id 2 end]
6798 if {[string length $id] < 40} {
6799 set matches [longid $id]
6800 if {[llength $matches] > 0} {
6801 if {[llength $matches] > 1} return
6803 set id [lindex $matches 0]
6806 set known [commitinview $id $curview]
6809 $ctext tag conf $lk -foreground blue -underline 1
6810 $ctext tag bind $lk <1> [list selbyid $id]
6811 $ctext tag bind $lk <Enter> {linkcursor %W 1}
6812 $ctext tag bind $lk <Leave> {linkcursor %W -1}
6814 lappend pendinglinks($id) $lk
6815 interestedin $id {makelink %P}
6819 proc appendshortlink {id {pre {}} {post {}}} {
6820 global ctext linknum
6822 $ctext insert end $pre
6823 $ctext tag delete link$linknum
6824 $ctext insert end [string range $id 0 7] link$linknum
6825 $ctext insert end $post
6826 setlink $id link$linknum
6830 proc makelink {id} {
6833 if {![info exists pendinglinks($id)]} return
6834 foreach lk $pendinglinks($id) {
6837 unset pendinglinks($id)
6840 proc linkcursor {w inc} {
6841 global linkentercount curtextcursor
6843 if {[incr linkentercount $inc] > 0} {
6844 $w configure -cursor hand2
6846 $w configure -cursor $curtextcursor
6847 if {$linkentercount < 0} {
6848 set linkentercount 0
6853 proc viewnextline {dir} {
6857 set ymax [lindex [$canv cget -scrollregion] 3]
6858 set wnow [$canv yview]
6859 set wtop [expr {[lindex $wnow 0] * $ymax}]
6860 set newtop [expr {$wtop + $dir * $linespc}]
6863 } elseif {$newtop > $ymax} {
6866 allcanvs yview moveto [expr {$newtop * 1.0 / $ymax}]
6869 # add a list of tag or branch names at position pos
6870 # returns the number of names inserted
6871 proc appendrefs {pos ids var} {
6872 global ctext linknum curview $var maxrefs
6874 if {[catch {$ctext index $pos}]} {
6877 $ctext conf -state normal
6878 $ctext delete $pos "$pos lineend"
6881 foreach tag [set $var\($id\)] {
6882 lappend tags [list $tag $id]
6885 if {[llength $tags] > $maxrefs} {
6886 $ctext insert $pos "[mc "many"] ([llength $tags])"
6888 set tags [lsort -index 0 -decreasing $tags]
6891 set id [lindex $ti 1]
6894 $ctext tag delete $lk
6895 $ctext insert $pos $sep
6896 $ctext insert $pos [lindex $ti 0] $lk
6901 $ctext conf -state disabled
6902 return [llength $tags]
6905 # called when we have finished computing the nearby tags
6906 proc dispneartags {delay} {
6907 global selectedline currentid showneartags tagphase
6909 if {$selectedline eq {} || !$showneartags} return
6910 after cancel dispnexttag
6912 after 200 dispnexttag
6915 after idle dispnexttag
6920 proc dispnexttag {} {
6921 global selectedline currentid showneartags tagphase ctext
6923 if {$selectedline eq {} || !$showneartags} return
6924 switch -- $tagphase {
6926 set dtags [desctags $currentid]
6928 appendrefs precedes $dtags idtags
6932 set atags [anctags $currentid]
6934 appendrefs follows $atags idtags
6938 set dheads [descheads $currentid]
6939 if {$dheads ne {}} {
6940 if {[appendrefs branch $dheads idheads] > 1
6941 && [$ctext get "branch -3c"] eq "h"} {
6942 # turn "Branch" into "Branches"
6943 $ctext conf -state normal
6944 $ctext insert "branch -2c" "es"
6945 $ctext conf -state disabled
6950 if {[incr tagphase] <= 2} {
6951 after idle dispnexttag
6955 proc make_secsel {id} {
6956 global linehtag linentag linedtag canv canv2 canv3
6958 if {![info exists linehtag($id)]} return
6960 set t [eval $canv create rect [$canv bbox $linehtag($id)] -outline {{}} \
6961 -tags secsel -fill [$canv cget -selectbackground]]
6963 $canv2 delete secsel
6964 set t [eval $canv2 create rect [$canv2 bbox $linentag($id)] -outline {{}} \
6965 -tags secsel -fill [$canv2 cget -selectbackground]]
6967 $canv3 delete secsel
6968 set t [eval $canv3 create rect [$canv3 bbox $linedtag($id)] -outline {{}} \
6969 -tags secsel -fill [$canv3 cget -selectbackground]]
6973 proc make_idmark {id} {
6974 global linehtag canv fgcolor
6976 if {![info exists linehtag($id)]} return
6978 set t [eval $canv create rect [$canv bbox $linehtag($id)] \
6979 -tags markid -outline $fgcolor]
6983 proc selectline {l isnew {desired_loc {}}} {
6984 global canv ctext commitinfo selectedline
6985 global canvy0 linespc parents children curview
6986 global currentid sha1entry
6987 global commentend idtags linknum
6988 global mergemax numcommits pending_select
6989 global cmitmode showneartags allcommits
6990 global targetrow targetid lastscrollrows
6991 global autoselect autosellen jump_to_here
6993 catch {unset pending_select}
6998 if {$l < 0 || $l >= $numcommits} return
6999 set id [commitonrow $l]
7004 if {$lastscrollrows < $numcommits} {
7008 set y [expr {$canvy0 + $l * $linespc}]
7009 set ymax [lindex [$canv cget -scrollregion] 3]
7010 set ytop [expr {$y - $linespc - 1}]
7011 set ybot [expr {$y + $linespc + 1}]
7012 set wnow [$canv yview]
7013 set wtop [expr {[lindex $wnow 0] * $ymax}]
7014 set wbot [expr {[lindex $wnow 1] * $ymax}]
7015 set wh [expr {$wbot - $wtop}]
7017 if {$ytop < $wtop} {
7018 if {$ybot < $wtop} {
7019 set newtop [expr {$y - $wh / 2.0}]
7022 if {$newtop > $wtop - $linespc} {
7023 set newtop [expr {$wtop - $linespc}]
7026 } elseif {$ybot > $wbot} {
7027 if {$ytop > $wbot} {
7028 set newtop [expr {$y - $wh / 2.0}]
7030 set newtop [expr {$ybot - $wh}]
7031 if {$newtop < $wtop + $linespc} {
7032 set newtop [expr {$wtop + $linespc}]
7036 if {$newtop != $wtop} {
7040 allcanvs yview moveto [expr {$newtop * 1.0 / $ymax}]
7047 addtohistory [list selbyid $id 0] savecmitpos
7050 $sha1entry delete 0 end
7051 $sha1entry insert 0 $id
7053 $sha1entry selection range 0 $autosellen
7057 $ctext conf -state normal
7060 if {![info exists commitinfo($id)]} {
7063 set info $commitinfo($id)
7064 set date [formatdate [lindex $info 2]]
7065 $ctext insert end "[mc "Author"]: [lindex $info 1] $date\n"
7066 set date [formatdate [lindex $info 4]]
7067 $ctext insert end "[mc "Committer"]: [lindex $info 3] $date\n"
7068 if {[info exists idtags($id)]} {
7069 $ctext insert end [mc "Tags:"]
7070 foreach tag $idtags($id) {
7071 $ctext insert end " $tag"
7073 $ctext insert end "\n"
7077 set olds $parents($curview,$id)
7078 if {[llength $olds] > 1} {
7081 if {$np >= $mergemax} {
7086 $ctext insert end "[mc "Parent"]: " $tag
7087 appendwithlinks [commit_descriptor $p] {}
7092 append headers "[mc "Parent"]: [commit_descriptor $p]"
7096 foreach c $children($curview,$id) {
7097 append headers "[mc "Child"]: [commit_descriptor $c]"
7100 # make anything that looks like a SHA1 ID be a clickable link
7101 appendwithlinks $headers {}
7102 if {$showneartags} {
7103 if {![info exists allcommits]} {
7106 $ctext insert end "[mc "Branch"]: "
7107 $ctext mark set branch "end -1c"
7108 $ctext mark gravity branch left
7109 $ctext insert end "\n[mc "Follows"]: "
7110 $ctext mark set follows "end -1c"
7111 $ctext mark gravity follows left
7112 $ctext insert end "\n[mc "Precedes"]: "
7113 $ctext mark set precedes "end -1c"
7114 $ctext mark gravity precedes left
7115 $ctext insert end "\n"
7118 $ctext insert end "\n"
7119 set comment [lindex $info 5]
7120 if {[string first "\r" $comment] >= 0} {
7121 set comment [string map {"\r" "\n "} $comment]
7123 appendwithlinks $comment {comment}
7125 $ctext tag remove found 1.0 end
7126 $ctext conf -state disabled
7127 set commentend [$ctext index "end - 1c"]
7129 set jump_to_here $desired_loc
7130 init_flist [mc "Comments"]
7131 if {$cmitmode eq "tree"} {
7133 } elseif {[llength $olds] <= 1} {
7140 proc selfirstline {} {
7145 proc sellastline {} {
7148 set l [expr {$numcommits - 1}]
7152 proc selnextline {dir} {
7155 if {$selectedline eq {}} return
7156 set l [expr {$selectedline + $dir}]
7161 proc selnextpage {dir} {
7162 global canv linespc selectedline numcommits
7164 set lpp [expr {([winfo height $canv] - 2) / $linespc}]
7168 allcanvs yview scroll [expr {$dir * $lpp}] units
7170 if {$selectedline eq {}} return
7171 set l [expr {$selectedline + $dir * $lpp}]
7174 } elseif {$l >= $numcommits} {
7175 set l [expr $numcommits - 1]
7181 proc unselectline {} {
7182 global selectedline currentid
7185 catch {unset currentid}
7186 allcanvs delete secsel
7190 proc reselectline {} {
7193 if {$selectedline ne {}} {
7194 selectline $selectedline 0
7198 proc addtohistory {cmd {saveproc {}}} {
7199 global history historyindex curview
7203 set elt [list $curview $cmd $saveproc {}]
7204 if {$historyindex > 0
7205 && [lindex $history [expr {$historyindex - 1}]] == $elt} {
7209 if {$historyindex < [llength $history]} {
7210 set history [lreplace $history $historyindex end $elt]
7212 lappend history $elt
7215 if {$historyindex > 1} {
7216 .tf.bar.leftbut conf -state normal
7218 .tf.bar.leftbut conf -state disabled
7220 .tf.bar.rightbut conf -state disabled
7223 # save the scrolling position of the diff display pane
7224 proc save_position {} {
7225 global historyindex history
7227 if {$historyindex < 1} return
7228 set hi [expr {$historyindex - 1}]
7229 set fn [lindex $history $hi 2]
7231 lset history $hi 3 [eval $fn]
7235 proc unset_posvars {} {
7238 if {[info exists last_posvars]} {
7239 foreach {var val} $last_posvars {
7248 global curview last_posvars
7250 set view [lindex $elt 0]
7251 set cmd [lindex $elt 1]
7252 set pv [lindex $elt 3]
7253 if {$curview != $view} {
7257 foreach {var val} $pv {
7261 set last_posvars $pv
7266 global history historyindex
7269 if {$historyindex > 1} {
7271 incr historyindex -1
7272 godo [lindex $history [expr {$historyindex - 1}]]
7273 .tf.bar.rightbut conf -state normal
7275 if {$historyindex <= 1} {
7276 .tf.bar.leftbut conf -state disabled
7281 global history historyindex
7284 if {$historyindex < [llength $history]} {
7286 set cmd [lindex $history $historyindex]
7289 .tf.bar.leftbut conf -state normal
7291 if {$historyindex >= [llength $history]} {
7292 .tf.bar.rightbut conf -state disabled
7297 global treefilelist treeidlist diffids diffmergeid treepending
7298 global nullid nullid2
7301 catch {unset diffmergeid}
7302 if {![info exists treefilelist($id)]} {
7303 if {![info exists treepending]} {
7304 if {$id eq $nullid} {
7305 set cmd [list | git ls-files]
7306 } elseif {$id eq $nullid2} {
7307 set cmd [list | git ls-files --stage -t]
7309 set cmd [list | git ls-tree -r $id]
7311 if {[catch {set gtf [open $cmd r]}]} {
7315 set treefilelist($id) {}
7316 set treeidlist($id) {}
7317 fconfigure $gtf -blocking 0 -encoding binary
7318 filerun $gtf [list gettreeline $gtf $id]
7325 proc gettreeline {gtf id} {
7326 global treefilelist treeidlist treepending cmitmode diffids nullid nullid2
7329 while {[incr nl] <= 1000 && [gets $gtf line] >= 0} {
7330 if {$diffids eq $nullid} {
7333 set i [string first "\t" $line]
7334 if {$i < 0} continue
7335 set fname [string range $line [expr {$i+1}] end]
7336 set line [string range $line 0 [expr {$i-1}]]
7337 if {$diffids ne $nullid2 && [lindex $line 1] ne "blob"} continue
7338 set sha1 [lindex $line 2]
7339 lappend treeidlist($id) $sha1
7341 if {[string index $fname 0] eq "\""} {
7342 set fname [lindex $fname 0]
7344 set fname [encoding convertfrom $fname]
7345 lappend treefilelist($id) $fname
7348 return [expr {$nl >= 1000? 2: 1}]
7352 if {$cmitmode ne "tree"} {
7353 if {![info exists diffmergeid]} {
7354 gettreediffs $diffids
7356 } elseif {$id ne $diffids} {
7365 global treefilelist treeidlist diffids nullid nullid2
7366 global ctext_file_names ctext_file_lines
7367 global ctext commentend
7369 set i [lsearch -exact $treefilelist($diffids) $f]
7371 puts "oops, $f not in list for id $diffids"
7374 if {$diffids eq $nullid} {
7375 if {[catch {set bf [open $f r]} err]} {
7376 puts "oops, can't read $f: $err"
7380 set blob [lindex $treeidlist($diffids) $i]
7381 if {[catch {set bf [open [concat | git cat-file blob $blob] r]} err]} {
7382 puts "oops, error reading blob $blob: $err"
7386 fconfigure $bf -blocking 0 -encoding [get_path_encoding $f]
7387 filerun $bf [list getblobline $bf $diffids]
7388 $ctext config -state normal
7389 clear_ctext $commentend
7390 lappend ctext_file_names $f
7391 lappend ctext_file_lines [lindex [split $commentend "."] 0]
7392 $ctext insert end "\n"
7393 $ctext insert end "$f\n" filesep
7394 $ctext config -state disabled
7395 $ctext yview $commentend
7399 proc getblobline {bf id} {
7400 global diffids cmitmode ctext
7402 if {$id ne $diffids || $cmitmode ne "tree"} {
7406 $ctext config -state normal
7408 while {[incr nl] <= 1000 && [gets $bf line] >= 0} {
7409 $ctext insert end "$line\n"
7412 global jump_to_here ctext_file_names commentend
7414 # delete last newline
7415 $ctext delete "end - 2c" "end - 1c"
7417 if {$jump_to_here ne {} &&
7418 [lindex $jump_to_here 0] eq [lindex $ctext_file_names 0]} {
7419 set lnum [expr {[lindex $jump_to_here 1] +
7420 [lindex [split $commentend .] 0]}]
7421 mark_ctext_line $lnum
7423 $ctext config -state disabled
7426 $ctext config -state disabled
7427 return [expr {$nl >= 1000? 2: 1}]
7430 proc mark_ctext_line {lnum} {
7431 global ctext markbgcolor
7433 $ctext tag delete omark
7434 $ctext tag add omark $lnum.0 "$lnum.0 + 1 line"
7435 $ctext tag conf omark -background $markbgcolor
7439 proc mergediff {id} {
7441 global diffids treediffs
7442 global parents curview
7446 set treediffs($id) {}
7447 set np [llength $parents($curview,$id)]
7452 proc startdiff {ids} {
7453 global treediffs diffids treepending diffmergeid nullid nullid2
7457 catch {unset diffmergeid}
7458 if {![info exists treediffs($ids)] ||
7459 [lsearch -exact $ids $nullid] >= 0 ||
7460 [lsearch -exact $ids $nullid2] >= 0} {
7461 if {![info exists treepending]} {
7469 # If the filename (name) is under any of the passed filter paths
7470 # then return true to include the file in the listing.
7471 proc path_filter {filter name} {
7472 set worktree [gitworktree]
7474 set fq_p [file normalize $p]
7475 set fq_n [file normalize [file join $worktree $name]]
7476 if {[string match [file normalize $fq_p]* $fq_n]} {
7483 proc addtocflist {ids} {
7486 add_flist $treediffs($ids)
7490 proc diffcmd {ids flags} {
7491 global log_showroot nullid nullid2
7493 set i [lsearch -exact $ids $nullid]
7494 set j [lsearch -exact $ids $nullid2]
7496 if {[llength $ids] > 1 && $j < 0} {
7497 # comparing working directory with some specific revision
7498 set cmd [concat | git diff-index $flags]
7500 lappend cmd -R [lindex $ids 1]
7502 lappend cmd [lindex $ids 0]
7505 # comparing working directory with index
7506 set cmd [concat | git diff-files $flags]
7511 } elseif {$j >= 0} {
7512 set cmd [concat | git diff-index --cached $flags]
7513 if {[llength $ids] > 1} {
7514 # comparing index with specific revision
7516 lappend cmd -R [lindex $ids 1]
7518 lappend cmd [lindex $ids 0]
7521 # comparing index with HEAD
7525 if {$log_showroot} {
7526 lappend flags --root
7528 set cmd [concat | git diff-tree -r $flags $ids]
7533 proc gettreediffs {ids} {
7534 global treediff treepending
7536 if {[catch {set gdtf [open [diffcmd $ids {--no-commit-id}] r]}]} return
7538 set treepending $ids
7540 fconfigure $gdtf -blocking 0 -encoding binary
7541 filerun $gdtf [list gettreediffline $gdtf $ids]
7544 proc gettreediffline {gdtf ids} {
7545 global treediff treediffs treepending diffids diffmergeid
7546 global cmitmode vfilelimit curview limitdiffs perfile_attrs
7551 if {$perfile_attrs} {
7552 # cache_gitattr is slow, and even slower on win32 where we
7553 # have to invoke it for only about 30 paths at a time
7555 if {[tk windowingsystem] == "win32"} {
7559 while {[incr nr] <= $max && [gets $gdtf line] >= 0} {
7560 set i [string first "\t" $line]
7562 set file [string range $line [expr {$i+1}] end]
7563 if {[string index $file 0] eq "\""} {
7564 set file [lindex $file 0]
7566 set file [encoding convertfrom $file]
7567 if {$file ne [lindex $treediff end]} {
7568 lappend treediff $file
7569 lappend sublist $file
7573 if {$perfile_attrs} {
7574 cache_gitattr encoding $sublist
7577 return [expr {$nr >= $max? 2: 1}]
7580 if {$limitdiffs && $vfilelimit($curview) ne {}} {
7582 foreach f $treediff {
7583 if {[path_filter $vfilelimit($curview) $f]} {
7587 set treediffs($ids) $flist
7589 set treediffs($ids) $treediff
7592 if {$cmitmode eq "tree" && [llength $diffids] == 1} {
7594 } elseif {$ids != $diffids} {
7595 if {![info exists diffmergeid]} {
7596 gettreediffs $diffids
7604 # empty string or positive integer
7605 proc diffcontextvalidate {v} {
7606 return [regexp {^(|[1-9][0-9]*)$} $v]
7609 proc diffcontextchange {n1 n2 op} {
7610 global diffcontextstring diffcontext
7612 if {[string is integer -strict $diffcontextstring]} {
7613 if {$diffcontextstring >= 0} {
7614 set diffcontext $diffcontextstring
7620 proc changeignorespace {} {
7624 proc changeworddiff {name ix op} {
7628 proc getblobdiffs {ids} {
7629 global blobdifffd diffids env
7630 global diffinhdr treediffs
7634 global limitdiffs vfilelimit curview
7635 global diffencoding targetline diffnparents
7636 global git_version currdiffsubmod
7639 if {[package vcompare $git_version "1.6.1"] >= 0} {
7640 set textconv "--textconv"
7643 if {[package vcompare $git_version "1.6.6"] >= 0} {
7644 set submodule "--submodule"
7646 set cmd [diffcmd $ids "-p $textconv $submodule -C --cc --no-commit-id -U$diffcontext"]
7650 if {$worddiff ne [mc "Line diff"]} {
7651 append cmd " --word-diff=porcelain"
7653 if {$limitdiffs && $vfilelimit($curview) ne {}} {
7654 set cmd [concat $cmd -- $vfilelimit($curview)]
7656 if {[catch {set bdf [open $cmd r]} err]} {
7657 error_popup [mc "Error getting diffs: %s" $err]
7663 set diffencoding [get_path_encoding {}]
7664 fconfigure $bdf -blocking 0 -encoding binary -eofchar {}
7665 set blobdifffd($ids) $bdf
7666 set currdiffsubmod ""
7667 filerun $bdf [list getblobdiffline $bdf $diffids]
7670 proc savecmitpos {} {
7671 global ctext cmitmode
7673 if {$cmitmode eq "tree"} {
7676 return [list target_scrollpos [$ctext index @0,0]]
7679 proc savectextpos {} {
7682 return [list target_scrollpos [$ctext index @0,0]]
7685 proc maybe_scroll_ctext {ateof} {
7686 global ctext target_scrollpos
7688 if {![info exists target_scrollpos]} return
7690 set nlines [expr {[winfo height $ctext]
7691 / [font metrics textfont -linespace]}]
7692 if {[$ctext compare "$target_scrollpos + $nlines lines" <= end]} return
7694 $ctext yview $target_scrollpos
7695 unset target_scrollpos
7698 proc setinlist {var i val} {
7701 while {[llength [set $var]] < $i} {
7704 if {[llength [set $var]] == $i} {
7711 proc makediffhdr {fname ids} {
7712 global ctext curdiffstart treediffs diffencoding
7713 global ctext_file_names jump_to_here targetline diffline
7715 set fname [encoding convertfrom $fname]
7716 set diffencoding [get_path_encoding $fname]
7717 set i [lsearch -exact $treediffs($ids) $fname]
7719 setinlist difffilestart $i $curdiffstart
7721 lset ctext_file_names end $fname
7722 set l [expr {(78 - [string length $fname]) / 2}]
7723 set pad [string range "----------------------------------------" 1 $l]
7724 $ctext insert $curdiffstart "$pad $fname $pad" filesep
7726 if {$jump_to_here ne {} && [lindex $jump_to_here 0] eq $fname} {
7727 set targetline [lindex $jump_to_here 1]
7732 proc getblobdiffline {bdf ids} {
7733 global diffids blobdifffd ctext curdiffstart
7734 global diffnexthead diffnextnote difffilestart
7735 global ctext_file_names ctext_file_lines
7736 global diffinhdr treediffs mergemax diffnparents
7737 global diffencoding jump_to_here targetline diffline currdiffsubmod
7741 $ctext conf -state normal
7742 while {[incr nr] <= 1000 && [gets $bdf line] >= 0} {
7743 if {$ids != $diffids || $bdf != $blobdifffd($ids)} {
7747 if {![string compare -length 5 "diff " $line]} {
7748 if {![regexp {^diff (--cc|--git) } $line m type]} {
7749 set line [encoding convertfrom $line]
7750 $ctext insert end "$line\n" hunksep
7753 # start of a new file
7755 $ctext insert end "\n"
7756 set curdiffstart [$ctext index "end - 1c"]
7757 lappend ctext_file_names ""
7758 lappend ctext_file_lines [lindex [split $curdiffstart "."] 0]
7759 $ctext insert end "\n" filesep
7761 if {$type eq "--cc"} {
7762 # start of a new file in a merge diff
7763 set fname [string range $line 10 end]
7764 if {[lsearch -exact $treediffs($ids) $fname] < 0} {
7765 lappend treediffs($ids) $fname
7766 add_flist [list $fname]
7770 set line [string range $line 11 end]
7771 # If the name hasn't changed the length will be odd,
7772 # the middle char will be a space, and the two bits either
7773 # side will be a/name and b/name, or "a/name" and "b/name".
7774 # If the name has changed we'll get "rename from" and
7775 # "rename to" or "copy from" and "copy to" lines following
7776 # this, and we'll use them to get the filenames.
7777 # This complexity is necessary because spaces in the
7778 # filename(s) don't get escaped.
7779 set l [string length $line]
7780 set i [expr {$l / 2}]
7781 if {!(($l & 1) && [string index $line $i] eq " " &&
7782 [string range $line 2 [expr {$i - 1}]] eq \
7783 [string range $line [expr {$i + 3}] end])} {
7786 # unescape if quoted and chop off the a/ from the front
7787 if {[string index $line 0] eq "\""} {
7788 set fname [string range [lindex $line 0] 2 end]
7790 set fname [string range $line 2 [expr {$i - 1}]]
7793 makediffhdr $fname $ids
7795 } elseif {![string compare -length 16 "* Unmerged path " $line]} {
7796 set fname [encoding convertfrom [string range $line 16 end]]
7797 $ctext insert end "\n"
7798 set curdiffstart [$ctext index "end - 1c"]
7799 lappend ctext_file_names $fname
7800 lappend ctext_file_lines [lindex [split $curdiffstart "."] 0]
7801 $ctext insert end "$line\n" filesep
7802 set i [lsearch -exact $treediffs($ids) $fname]
7804 setinlist difffilestart $i $curdiffstart
7807 } elseif {![string compare -length 2 "@@" $line]} {
7808 regexp {^@@+} $line ats
7809 set line [encoding convertfrom $diffencoding $line]
7810 $ctext insert end "$line\n" hunksep
7811 if {[regexp { \+(\d+),\d+ @@} $line m nl]} {
7814 set diffnparents [expr {[string length $ats] - 1}]
7817 } elseif {![string compare -length 10 "Submodule " $line]} {
7818 # start of a new submodule
7819 if {[regexp -indices "\[0-9a-f\]+\\.\\." $line nameend]} {
7820 set fname [string range $line 10 [expr [lindex $nameend 0] - 2]]
7822 set fname [string range $line 10 [expr [string first "contains " $line] - 2]]
7824 if {$currdiffsubmod != $fname} {
7825 $ctext insert end "\n"; # Add newline after commit message
7827 set curdiffstart [$ctext index "end - 1c"]
7828 lappend ctext_file_names ""
7829 if {$currdiffsubmod != $fname} {
7830 lappend ctext_file_lines $fname
7831 makediffhdr $fname $ids
7832 set currdiffsubmod $fname
7833 $ctext insert end "\n$line\n" filesep
7835 $ctext insert end "$line\n" filesep
7837 } elseif {![string compare -length 3 " >" $line]} {
7838 set $currdiffsubmod ""
7839 set line [encoding convertfrom $diffencoding $line]
7840 $ctext insert end "$line\n" dresult
7841 } elseif {![string compare -length 3 " <" $line]} {
7842 set $currdiffsubmod ""
7843 set line [encoding convertfrom $diffencoding $line]
7844 $ctext insert end "$line\n" d0
7845 } elseif {$diffinhdr} {
7846 if {![string compare -length 12 "rename from " $line]} {
7847 set fname [string range $line [expr 6 + [string first " from " $line] ] end]
7848 if {[string index $fname 0] eq "\""} {
7849 set fname [lindex $fname 0]
7851 set fname [encoding convertfrom $fname]
7852 set i [lsearch -exact $treediffs($ids) $fname]
7854 setinlist difffilestart $i $curdiffstart
7856 } elseif {![string compare -length 10 $line "rename to "] ||
7857 ![string compare -length 8 $line "copy to "]} {
7858 set fname [string range $line [expr 4 + [string first " to " $line] ] end]
7859 if {[string index $fname 0] eq "\""} {
7860 set fname [lindex $fname 0]
7862 makediffhdr $fname $ids
7863 } elseif {[string compare -length 3 $line "---"] == 0} {
7866 } elseif {[string compare -length 3 $line "+++"] == 0} {
7870 $ctext insert end "$line\n" filesep
7873 set line [string map {\x1A ^Z} \
7874 [encoding convertfrom $diffencoding $line]]
7875 # parse the prefix - one ' ', '-' or '+' for each parent
7876 set prefix [string range $line 0 [expr {$diffnparents - 1}]]
7877 set tag [expr {$diffnparents > 1? "m": "d"}]
7878 set dowords [expr {$worddiff ne [mc "Line diff"] && $diffnparents == 1}]
7879 set words_pre_markup ""
7880 set words_post_markup ""
7881 if {[string trim $prefix " -+"] eq {}} {
7882 # prefix only has " ", "-" and "+" in it: normal diff line
7883 set num [string first "-" $prefix]
7885 set line [string range $line 1 end]
7888 # removed line, first parent with line is $num
7889 if {$num >= $mergemax} {
7892 if {$dowords && $worddiff eq [mc "Markup words"]} {
7893 $ctext insert end "\[-$line-\]" $tag$num
7895 $ctext insert end "$line" $tag$num
7898 $ctext insert end "\n" $tag$num
7902 if {[string first "+" $prefix] >= 0} {
7904 lappend tags ${tag}result
7905 if {$diffnparents > 1} {
7906 set num [string first " " $prefix]
7908 if {$num >= $mergemax} {
7914 set words_pre_markup "{+"
7915 set words_post_markup "+}"
7917 if {$targetline ne {}} {
7918 if {$diffline == $targetline} {
7919 set seehere [$ctext index "end - 1 chars"]
7925 if {$dowords && $worddiff eq [mc "Markup words"]} {
7926 $ctext insert end "$words_pre_markup$line$words_post_markup" $tags
7928 $ctext insert end "$line" $tags
7931 $ctext insert end "\n" $tags
7934 } elseif {$dowords && $prefix eq "~"} {
7935 $ctext insert end "\n" {}
7937 # "\ No newline at end of file",
7938 # or something else we don't recognize
7939 $ctext insert end "$line\n" hunksep
7943 if {[info exists seehere]} {
7944 mark_ctext_line [lindex [split $seehere .] 0]
7946 maybe_scroll_ctext [eof $bdf]
7947 $ctext conf -state disabled
7952 return [expr {$nr >= 1000? 2: 1}]
7955 proc changediffdisp {} {
7956 global ctext diffelide
7958 $ctext tag conf d0 -elide [lindex $diffelide 0]
7959 $ctext tag conf dresult -elide [lindex $diffelide 1]
7962 proc highlightfile {cline} {
7963 global cflist cflist_top
7965 $cflist tag remove highlight $cflist_top.0 "$cflist_top.0 lineend"
7966 $cflist tag add highlight $cline.0 "$cline.0 lineend"
7967 $cflist see $cline.0
7968 set cflist_top $cline
7971 proc highlightfile_for_scrollpos {topidx} {
7972 global difffilestart
7974 if {![info exists difffilestart]} return
7976 set top [lindex [split $topidx .] 0]
7977 if {$difffilestart eq {} || $top < [lindex $difffilestart 0]} {
7980 highlightfile [expr {[bsearch $difffilestart $top] + 2}]
7985 global difffilestart ctext cmitmode
7987 if {$cmitmode eq "tree"} return
7989 set here [$ctext index @0,0]
7990 foreach loc $difffilestart {
7991 if {[$ctext compare $loc >= $here]} {
8001 global difffilestart ctext cmitmode
8003 if {$cmitmode eq "tree"} return
8004 set here [$ctext index @0,0]
8005 foreach loc $difffilestart {
8006 if {[$ctext compare $loc > $here]} {
8013 proc clear_ctext {{first 1.0}} {
8014 global ctext smarktop smarkbot
8015 global ctext_file_names ctext_file_lines
8018 set l [lindex [split $first .] 0]
8019 if {![info exists smarktop] || [$ctext compare $first < $smarktop.0]} {
8022 if {![info exists smarkbot] || [$ctext compare $first < $smarkbot.0]} {
8025 $ctext delete $first end
8026 if {$first eq "1.0"} {
8027 catch {unset pendinglinks}
8029 set ctext_file_names {}
8030 set ctext_file_lines {}
8033 proc settabs {{firstab {}}} {
8034 global firsttabstop tabstop ctext have_tk85
8036 if {$firstab ne {} && $have_tk85} {
8037 set firsttabstop $firstab
8039 set w [font measure textfont "0"]
8040 if {$firsttabstop != 0} {
8041 $ctext conf -tabs [list [expr {($firsttabstop + $tabstop) * $w}] \
8042 [expr {($firsttabstop + 2 * $tabstop) * $w}]]
8043 } elseif {$have_tk85 || $tabstop != 8} {
8044 $ctext conf -tabs [expr {$tabstop * $w}]
8046 $ctext conf -tabs {}
8050 proc incrsearch {name ix op} {
8051 global ctext searchstring searchdirn
8053 if {[catch {$ctext index anchor}]} {
8054 # no anchor set, use start of selection, or of visible area
8055 set sel [$ctext tag ranges sel]
8057 $ctext mark set anchor [lindex $sel 0]
8058 } elseif {$searchdirn eq "-forwards"} {
8059 $ctext mark set anchor @0,0
8061 $ctext mark set anchor @0,[winfo height $ctext]
8064 if {$searchstring ne {}} {
8065 set here [$ctext search -count mlen $searchdirn -- $searchstring anchor]
8068 set mend "$here + $mlen c"
8069 $ctext tag remove sel 1.0 end
8070 $ctext tag add sel $here $mend
8071 suppress_highlighting_file_for_current_scrollpos
8072 highlightfile_for_scrollpos $here
8075 rehighlight_search_results
8079 global sstring ctext searchstring searchdirn
8082 $sstring icursor end
8083 set searchdirn -forwards
8084 if {$searchstring ne {}} {
8085 set sel [$ctext tag ranges sel]
8087 set start "[lindex $sel 0] + 1c"
8088 } elseif {[catch {set start [$ctext index anchor]}]} {
8091 set match [$ctext search -count mlen -- $searchstring $start]
8092 $ctext tag remove sel 1.0 end
8098 suppress_highlighting_file_for_current_scrollpos
8099 highlightfile_for_scrollpos $match
8100 set mend "$match + $mlen c"
8101 $ctext tag add sel $match $mend
8102 $ctext mark unset anchor
8103 rehighlight_search_results
8107 proc dosearchback {} {
8108 global sstring ctext searchstring searchdirn
8111 $sstring icursor end
8112 set searchdirn -backwards
8113 if {$searchstring ne {}} {
8114 set sel [$ctext tag ranges sel]
8116 set start [lindex $sel 0]
8117 } elseif {[catch {set start [$ctext index anchor]}]} {
8118 set start @0,[winfo height $ctext]
8120 set match [$ctext search -backwards -count ml -- $searchstring $start]
8121 $ctext tag remove sel 1.0 end
8127 suppress_highlighting_file_for_current_scrollpos
8128 highlightfile_for_scrollpos $match
8129 set mend "$match + $ml c"
8130 $ctext tag add sel $match $mend
8131 $ctext mark unset anchor
8132 rehighlight_search_results
8136 proc rehighlight_search_results {} {
8137 global ctext searchstring
8139 $ctext tag remove found 1.0 end
8140 $ctext tag remove currentsearchhit 1.0 end
8142 if {$searchstring ne {}} {
8147 proc searchmark {first last} {
8148 global ctext searchstring
8150 set sel [$ctext tag ranges sel]
8154 set match [$ctext search -count mlen -- $searchstring $mend $last.end]
8155 if {$match eq {}} break
8156 set mend "$match + $mlen c"
8157 if {$sel ne {} && [$ctext compare $match == [lindex $sel 0]]} {
8158 $ctext tag add currentsearchhit $match $mend
8160 $ctext tag add found $match $mend
8165 proc searchmarkvisible {doall} {
8166 global ctext smarktop smarkbot
8168 set topline [lindex [split [$ctext index @0,0] .] 0]
8169 set botline [lindex [split [$ctext index @0,[winfo height $ctext]] .] 0]
8170 if {$doall || $botline < $smarktop || $topline > $smarkbot} {
8171 # no overlap with previous
8172 searchmark $topline $botline
8173 set smarktop $topline
8174 set smarkbot $botline
8176 if {$topline < $smarktop} {
8177 searchmark $topline [expr {$smarktop-1}]
8178 set smarktop $topline
8180 if {$botline > $smarkbot} {
8181 searchmark [expr {$smarkbot+1}] $botline
8182 set smarkbot $botline
8187 proc suppress_highlighting_file_for_current_scrollpos {} {
8188 global ctext suppress_highlighting_file_for_this_scrollpos
8190 set suppress_highlighting_file_for_this_scrollpos [$ctext index @0,0]
8193 proc scrolltext {f0 f1} {
8194 global searchstring cmitmode ctext
8195 global suppress_highlighting_file_for_this_scrollpos
8197 if {$cmitmode ne "tree"} {
8198 set topidx [$ctext index @0,0]
8199 if {![info exists suppress_highlighting_file_for_this_scrollpos]
8200 || $topidx ne $suppress_highlighting_file_for_this_scrollpos} {
8201 highlightfile_for_scrollpos $topidx
8205 catch {unset suppress_highlighting_file_for_this_scrollpos}
8207 .bleft.bottom.sb set $f0 $f1
8208 if {$searchstring ne {}} {
8214 global linespc charspc canvx0 canvy0
8215 global xspc1 xspc2 lthickness
8217 set linespc [font metrics mainfont -linespace]
8218 set charspc [font measure mainfont "m"]
8219 set canvy0 [expr {int(3 + 0.5 * $linespc)}]
8220 set canvx0 [expr {int(3 + 0.5 * $linespc)}]
8221 set lthickness [expr {int($linespc / 9) + 1}]
8222 set xspc1(0) $linespc
8230 set ymax [lindex [$canv cget -scrollregion] 3]
8231 if {$ymax eq {} || $ymax == 0} return
8232 set span [$canv yview]
8235 allcanvs yview moveto [lindex $span 0]
8237 if {$selectedline ne {}} {
8238 selectline $selectedline 0
8239 allcanvs yview moveto [lindex $span 0]
8243 proc parsefont {f n} {
8246 set fontattr($f,family) [lindex $n 0]
8248 if {$s eq {} || $s == 0} {
8251 set s [expr {int(-$s / [winfo fpixels . 1p] + 0.5)}]
8253 set fontattr($f,size) $s
8254 set fontattr($f,weight) normal
8255 set fontattr($f,slant) roman
8256 foreach style [lrange $n 2 end] {
8259 "bold" {set fontattr($f,weight) $style}
8261 "italic" {set fontattr($f,slant) $style}
8266 proc fontflags {f {isbold 0}} {
8269 return [list -family $fontattr($f,family) -size $fontattr($f,size) \
8270 -weight [expr {$isbold? "bold": $fontattr($f,weight)}] \
8271 -slant $fontattr($f,slant)]
8277 set n [list $fontattr($f,family) $fontattr($f,size)]
8278 if {$fontattr($f,weight) eq "bold"} {
8281 if {$fontattr($f,slant) eq "italic"} {
8287 proc incrfont {inc} {
8288 global mainfont textfont ctext canv cflist showrefstop
8289 global stopped entries fontattr
8292 set s $fontattr(mainfont,size)
8297 set fontattr(mainfont,size) $s
8298 font config mainfont -size $s
8299 font config mainfontbold -size $s
8300 set mainfont [fontname mainfont]
8301 set s $fontattr(textfont,size)
8306 set fontattr(textfont,size) $s
8307 font config textfont -size $s
8308 font config textfontbold -size $s
8309 set textfont [fontname textfont]
8316 global sha1entry sha1string
8317 if {[string length $sha1string] == 40} {
8318 $sha1entry delete 0 end
8322 proc sha1change {n1 n2 op} {
8323 global sha1string currentid sha1but
8324 if {$sha1string == {}
8325 || ([info exists currentid] && $sha1string == $currentid)} {
8330 if {[$sha1but cget -state] == $state} return
8331 if {$state == "normal"} {
8332 $sha1but conf -state normal -relief raised -text "[mc "Goto:"] "
8334 $sha1but conf -state disabled -relief flat -text "[mc "SHA1 ID:"] "
8338 proc gotocommit {} {
8339 global sha1string tagids headids curview varcid
8341 if {$sha1string == {}
8342 || ([info exists currentid] && $sha1string == $currentid)} return
8343 if {[info exists tagids($sha1string)]} {
8344 set id $tagids($sha1string)
8345 } elseif {[info exists headids($sha1string)]} {
8346 set id $headids($sha1string)
8348 set id [string tolower $sha1string]
8349 if {[regexp {^[0-9a-f]{4,39}$} $id]} {
8350 set matches [longid $id]
8351 if {$matches ne {}} {
8352 if {[llength $matches] > 1} {
8353 error_popup [mc "Short SHA1 id %s is ambiguous" $id]
8356 set id [lindex $matches 0]
8359 if {[catch {set id [exec git rev-parse --verify $sha1string]}]} {
8360 error_popup [mc "Revision %s is not known" $sha1string]
8365 if {[commitinview $id $curview]} {
8366 selectline [rowofcommit $id] 1
8369 if {[regexp {^[0-9a-fA-F]{4,}$} $sha1string]} {
8370 set msg [mc "SHA1 id %s is not known" $sha1string]
8372 set msg [mc "Revision %s is not in the current view" $sha1string]
8377 proc lineenter {x y id} {
8378 global hoverx hovery hoverid hovertimer
8379 global commitinfo canv
8381 if {![info exists commitinfo($id)] && ![getcommit $id]} return
8385 if {[info exists hovertimer]} {
8386 after cancel $hovertimer
8388 set hovertimer [after 500 linehover]
8392 proc linemotion {x y id} {
8393 global hoverx hovery hoverid hovertimer
8395 if {[info exists hoverid] && $id == $hoverid} {
8398 if {[info exists hovertimer]} {
8399 after cancel $hovertimer
8401 set hovertimer [after 500 linehover]
8405 proc lineleave {id} {
8406 global hoverid hovertimer canv
8408 if {[info exists hoverid] && $id == $hoverid} {
8410 if {[info exists hovertimer]} {
8411 after cancel $hovertimer
8419 global hoverx hovery hoverid hovertimer
8420 global canv linespc lthickness
8423 set text [lindex $commitinfo($hoverid) 0]
8424 set ymax [lindex [$canv cget -scrollregion] 3]
8425 if {$ymax == {}} return
8426 set yfrac [lindex [$canv yview] 0]
8427 set x [expr {$hoverx + 2 * $linespc}]
8428 set y [expr {$hovery + $yfrac * $ymax - $linespc / 2}]
8429 set x0 [expr {$x - 2 * $lthickness}]
8430 set y0 [expr {$y - 2 * $lthickness}]
8431 set x1 [expr {$x + [font measure mainfont $text] + 2 * $lthickness}]
8432 set y1 [expr {$y + $linespc + 2 * $lthickness}]
8433 set t [$canv create rectangle $x0 $y0 $x1 $y1 \
8434 -fill \#ffff80 -outline black -width 1 -tags hover]
8436 set t [$canv create text $x $y -anchor nw -text $text -tags hover \
8441 proc clickisonarrow {id y} {
8444 set ranges [rowranges $id]
8445 set thresh [expr {2 * $lthickness + 6}]
8446 set n [expr {[llength $ranges] - 1}]
8447 for {set i 1} {$i < $n} {incr i} {
8448 set row [lindex $ranges $i]
8449 if {abs([yc $row] - $y) < $thresh} {
8456 proc arrowjump {id n y} {
8459 # 1 <-> 2, 3 <-> 4, etc...
8460 set n [expr {(($n - 1) ^ 1) + 1}]
8461 set row [lindex [rowranges $id] $n]
8463 set ymax [lindex [$canv cget -scrollregion] 3]
8464 if {$ymax eq {} || $ymax <= 0} return
8465 set view [$canv yview]
8466 set yspan [expr {[lindex $view 1] - [lindex $view 0]}]
8467 set yfrac [expr {$yt / $ymax - $yspan / 2}]
8471 allcanvs yview moveto $yfrac
8474 proc lineclick {x y id isnew} {
8475 global ctext commitinfo children canv thickerline curview
8477 if {![info exists commitinfo($id)] && ![getcommit $id]} return
8482 # draw this line thicker than normal
8486 set ymax [lindex [$canv cget -scrollregion] 3]
8487 if {$ymax eq {}} return
8488 set yfrac [lindex [$canv yview] 0]
8489 set y [expr {$y + $yfrac * $ymax}]
8491 set dirn [clickisonarrow $id $y]
8493 arrowjump $id $dirn $y
8498 addtohistory [list lineclick $x $y $id 0] savectextpos
8500 # fill the details pane with info about this line
8501 $ctext conf -state normal
8504 $ctext insert end "[mc "Parent"]:\t"
8505 $ctext insert end $id link0
8507 set info $commitinfo($id)
8508 $ctext insert end "\n\t[lindex $info 0]\n"
8509 $ctext insert end "\t[mc "Author"]:\t[lindex $info 1]\n"
8510 set date [formatdate [lindex $info 2]]
8511 $ctext insert end "\t[mc "Date"]:\t$date\n"
8512 set kids $children($curview,$id)
8514 $ctext insert end "\n[mc "Children"]:"
8516 foreach child $kids {
8518 if {![info exists commitinfo($child)] && ![getcommit $child]} continue
8519 set info $commitinfo($child)
8520 $ctext insert end "\n\t"
8521 $ctext insert end $child link$i
8522 setlink $child link$i
8523 $ctext insert end "\n\t[lindex $info 0]"
8524 $ctext insert end "\n\t[mc "Author"]:\t[lindex $info 1]"
8525 set date [formatdate [lindex $info 2]]
8526 $ctext insert end "\n\t[mc "Date"]:\t$date\n"
8529 maybe_scroll_ctext 1
8530 $ctext conf -state disabled
8534 proc normalline {} {
8536 if {[info exists thickerline]} {
8543 proc selbyid {id {isnew 1}} {
8545 if {[commitinview $id $curview]} {
8546 selectline [rowofcommit $id] $isnew
8552 if {![info exists startmstime]} {
8553 set startmstime [clock clicks -milliseconds]
8555 return [format "%.3f" [expr {([clock click -milliseconds] - $startmstime) / 1000.0}]]
8558 proc rowmenu {x y id} {
8559 global rowctxmenu selectedline rowmenuid curview
8560 global nullid nullid2 fakerowmenu mainhead markedid
8564 if {$selectedline eq {} || [rowofcommit $id] eq $selectedline} {
8569 if {[info exists markedid] && $markedid ne $id} {
8574 if {$id ne $nullid && $id ne $nullid2} {
8575 set menu $rowctxmenu
8576 if {$mainhead ne {}} {
8577 $menu entryconfigure 7 -label [mc "Reset %s branch to here" $mainhead] -state normal
8579 $menu entryconfigure 7 -label [mc "Detached head: can't reset" $mainhead] -state disabled
8581 $menu entryconfigure 9 -state $mstate
8582 $menu entryconfigure 10 -state $mstate
8583 $menu entryconfigure 11 -state $mstate
8585 set menu $fakerowmenu
8587 $menu entryconfigure [mca "Diff this -> selected"] -state $state
8588 $menu entryconfigure [mca "Diff selected -> this"] -state $state
8589 $menu entryconfigure [mca "Make patch"] -state $state
8590 $menu entryconfigure [mca "Diff this -> marked commit"] -state $mstate
8591 $menu entryconfigure [mca "Diff marked commit -> this"] -state $mstate
8592 tk_popup $menu $x $y
8596 global rowmenuid markedid canv
8598 set markedid $rowmenuid
8599 make_idmark $markedid
8605 if {[info exists markedid]} {
8610 proc replace_by_kids {l r} {
8611 global curview children
8613 set id [commitonrow $r]
8614 set l [lreplace $l 0 0]
8615 foreach kid $children($curview,$id) {
8616 lappend l [rowofcommit $kid]
8618 return [lsort -integer -decreasing -unique $l]
8621 proc find_common_desc {} {
8622 global markedid rowmenuid curview children
8624 if {![info exists markedid]} return
8625 if {![commitinview $markedid $curview] ||
8626 ![commitinview $rowmenuid $curview]} return
8627 #set t1 [clock clicks -milliseconds]
8628 set l1 [list [rowofcommit $markedid]]
8629 set l2 [list [rowofcommit $rowmenuid]]
8631 set r1 [lindex $l1 0]
8632 set r2 [lindex $l2 0]
8633 if {$r1 eq {} || $r2 eq {}} break
8639 set l1 [replace_by_kids $l1 $r1]
8641 set l2 [replace_by_kids $l2 $r2]
8644 #set t2 [clock clicks -milliseconds]
8645 #puts "took [expr {$t2-$t1}]ms"
8648 proc compare_commits {} {
8649 global markedid rowmenuid curview children
8651 if {![info exists markedid]} return
8652 if {![commitinview $markedid $curview]} return
8653 addtohistory [list do_cmp_commits $markedid $rowmenuid]
8654 do_cmp_commits $markedid $rowmenuid
8657 proc getpatchid {id} {
8660 if {![info exists patchids($id)]} {
8661 set cmd [diffcmd [list $id] {-p --root}]
8662 # trim off the initial "|"
8663 set cmd [lrange $cmd 1 end]
8665 set x [eval exec $cmd | git patch-id]
8666 set patchids($id) [lindex $x 0]
8668 set patchids($id) "error"
8671 return $patchids($id)
8674 proc do_cmp_commits {a b} {
8675 global ctext curview parents children patchids commitinfo
8677 $ctext conf -state normal
8680 for {set i 0} {$i < 100} {incr i} {
8683 if {[llength $parents($curview,$a)] > 1} {
8684 appendshortlink $a [mc "Skipping merge commit "] "\n"
8687 set patcha [getpatchid $a]
8689 if {[llength $parents($curview,$b)] > 1} {
8690 appendshortlink $b [mc "Skipping merge commit "] "\n"
8693 set patchb [getpatchid $b]
8695 if {!$skipa && !$skipb} {
8696 set heada [lindex $commitinfo($a) 0]
8697 set headb [lindex $commitinfo($b) 0]
8698 if {$patcha eq "error"} {
8699 appendshortlink $a [mc "Error getting patch ID for "] \
8700 [mc " - stopping\n"]
8703 if {$patchb eq "error"} {
8704 appendshortlink $b [mc "Error getting patch ID for "] \
8705 [mc " - stopping\n"]
8708 if {$patcha eq $patchb} {
8709 if {$heada eq $headb} {
8710 appendshortlink $a [mc "Commit "]
8711 appendshortlink $b " == " " $heada\n"
8713 appendshortlink $a [mc "Commit "] " $heada\n"
8714 appendshortlink $b [mc " is the same patch as\n "] \
8720 $ctext insert end "\n"
8721 appendshortlink $a [mc "Commit "] " $heada\n"
8722 appendshortlink $b [mc " differs from\n "] \
8724 $ctext insert end [mc "Diff of commits:\n\n"]
8725 $ctext conf -state disabled
8732 set kids [real_children $curview,$a]
8733 if {[llength $kids] != 1} {
8734 $ctext insert end "\n"
8735 appendshortlink $a [mc "Commit "] \
8736 [mc " has %s children - stopping\n" [llength $kids]]
8739 set a [lindex $kids 0]
8742 set kids [real_children $curview,$b]
8743 if {[llength $kids] != 1} {
8744 appendshortlink $b [mc "Commit "] \
8745 [mc " has %s children - stopping\n" [llength $kids]]
8748 set b [lindex $kids 0]
8751 $ctext conf -state disabled
8754 proc diffcommits {a b} {
8755 global diffcontext diffids blobdifffd diffinhdr currdiffsubmod
8757 set tmpdir [gitknewtmpdir]
8758 set fna [file join $tmpdir "commit-[string range $a 0 7]"]
8759 set fnb [file join $tmpdir "commit-[string range $b 0 7]"]
8761 exec git diff-tree -p --pretty $a >$fna
8762 exec git diff-tree -p --pretty $b >$fnb
8764 error_popup [mc "Error writing commit to file: %s" $err]
8768 set fd [open "| diff -U$diffcontext $fna $fnb" r]
8770 error_popup [mc "Error diffing commits: %s" $err]
8773 set diffids [list commits $a $b]
8774 set blobdifffd($diffids) $fd
8776 set currdiffsubmod ""
8777 filerun $fd [list getblobdiffline $fd $diffids]
8780 proc diffvssel {dirn} {
8781 global rowmenuid selectedline
8783 if {$selectedline eq {}} return
8785 set oldid [commitonrow $selectedline]
8786 set newid $rowmenuid
8788 set oldid $rowmenuid
8789 set newid [commitonrow $selectedline]
8791 addtohistory [list doseldiff $oldid $newid] savectextpos
8792 doseldiff $oldid $newid
8795 proc diffvsmark {dirn} {
8796 global rowmenuid markedid
8798 if {![info exists markedid]} return
8801 set newid $rowmenuid
8803 set oldid $rowmenuid
8806 addtohistory [list doseldiff $oldid $newid] savectextpos
8807 doseldiff $oldid $newid
8810 proc doseldiff {oldid newid} {
8814 $ctext conf -state normal
8816 init_flist [mc "Top"]
8817 $ctext insert end "[mc "From"] "
8818 $ctext insert end $oldid link0
8819 setlink $oldid link0
8820 $ctext insert end "\n "
8821 $ctext insert end [lindex $commitinfo($oldid) 0]
8822 $ctext insert end "\n\n[mc "To"] "
8823 $ctext insert end $newid link1
8824 setlink $newid link1
8825 $ctext insert end "\n "
8826 $ctext insert end [lindex $commitinfo($newid) 0]
8827 $ctext insert end "\n"
8828 $ctext conf -state disabled
8829 $ctext tag remove found 1.0 end
8830 startdiff [list $oldid $newid]
8834 global rowmenuid currentid commitinfo patchtop patchnum NS
8836 if {![info exists currentid]} return
8837 set oldid $currentid
8838 set oldhead [lindex $commitinfo($oldid) 0]
8839 set newid $rowmenuid
8840 set newhead [lindex $commitinfo($newid) 0]
8843 catch {destroy $top}
8845 make_transient $top .
8846 ${NS}::label $top.title -text [mc "Generate patch"]
8847 grid $top.title - -pady 10
8848 ${NS}::label $top.from -text [mc "From:"]
8849 ${NS}::entry $top.fromsha1 -width 40
8850 $top.fromsha1 insert 0 $oldid
8851 $top.fromsha1 conf -state readonly
8852 grid $top.from $top.fromsha1 -sticky w
8853 ${NS}::entry $top.fromhead -width 60
8854 $top.fromhead insert 0 $oldhead
8855 $top.fromhead conf -state readonly
8856 grid x $top.fromhead -sticky w
8857 ${NS}::label $top.to -text [mc "To:"]
8858 ${NS}::entry $top.tosha1 -width 40
8859 $top.tosha1 insert 0 $newid
8860 $top.tosha1 conf -state readonly
8861 grid $top.to $top.tosha1 -sticky w
8862 ${NS}::entry $top.tohead -width 60
8863 $top.tohead insert 0 $newhead
8864 $top.tohead conf -state readonly
8865 grid x $top.tohead -sticky w
8866 ${NS}::button $top.rev -text [mc "Reverse"] -command mkpatchrev
8867 grid $top.rev x -pady 10 -padx 5
8868 ${NS}::label $top.flab -text [mc "Output file:"]
8869 ${NS}::entry $top.fname -width 60
8870 $top.fname insert 0 [file normalize "patch$patchnum.patch"]
8872 grid $top.flab $top.fname -sticky w
8873 ${NS}::frame $top.buts
8874 ${NS}::button $top.buts.gen -text [mc "Generate"] -command mkpatchgo
8875 ${NS}::button $top.buts.can -text [mc "Cancel"] -command mkpatchcan
8876 bind $top <Key-Return> mkpatchgo
8877 bind $top <Key-Escape> mkpatchcan
8878 grid $top.buts.gen $top.buts.can
8879 grid columnconfigure $top.buts 0 -weight 1 -uniform a
8880 grid columnconfigure $top.buts 1 -weight 1 -uniform a
8881 grid $top.buts - -pady 10 -sticky ew
8885 proc mkpatchrev {} {
8888 set oldid [$patchtop.fromsha1 get]
8889 set oldhead [$patchtop.fromhead get]
8890 set newid [$patchtop.tosha1 get]
8891 set newhead [$patchtop.tohead get]
8892 foreach e [list fromsha1 fromhead tosha1 tohead] \
8893 v [list $newid $newhead $oldid $oldhead] {
8894 $patchtop.$e conf -state normal
8895 $patchtop.$e delete 0 end
8896 $patchtop.$e insert 0 $v
8897 $patchtop.$e conf -state readonly
8902 global patchtop nullid nullid2
8904 set oldid [$patchtop.fromsha1 get]
8905 set newid [$patchtop.tosha1 get]
8906 set fname [$patchtop.fname get]
8907 set cmd [diffcmd [list $oldid $newid] -p]
8908 # trim off the initial "|"
8909 set cmd [lrange $cmd 1 end]
8910 lappend cmd >$fname &
8911 if {[catch {eval exec $cmd} err]} {
8912 error_popup "[mc "Error creating patch:"] $err" $patchtop
8914 catch {destroy $patchtop}
8918 proc mkpatchcan {} {
8921 catch {destroy $patchtop}
8926 global rowmenuid mktagtop commitinfo NS
8930 catch {destroy $top}
8932 make_transient $top .
8933 ${NS}::label $top.title -text [mc "Create tag"]
8934 grid $top.title - -pady 10
8935 ${NS}::label $top.id -text [mc "ID:"]
8936 ${NS}::entry $top.sha1 -width 40
8937 $top.sha1 insert 0 $rowmenuid
8938 $top.sha1 conf -state readonly
8939 grid $top.id $top.sha1 -sticky w
8940 ${NS}::entry $top.head -width 60
8941 $top.head insert 0 [lindex $commitinfo($rowmenuid) 0]
8942 $top.head conf -state readonly
8943 grid x $top.head -sticky w
8944 ${NS}::label $top.tlab -text [mc "Tag name:"]
8945 ${NS}::entry $top.tag -width 60
8946 grid $top.tlab $top.tag -sticky w
8947 ${NS}::label $top.op -text [mc "Tag message is optional"]
8948 grid $top.op -columnspan 2 -sticky we
8949 ${NS}::label $top.mlab -text [mc "Tag message:"]
8950 ${NS}::entry $top.msg -width 60
8951 grid $top.mlab $top.msg -sticky w
8952 ${NS}::frame $top.buts
8953 ${NS}::button $top.buts.gen -text [mc "Create"] -command mktaggo
8954 ${NS}::button $top.buts.can -text [mc "Cancel"] -command mktagcan
8955 bind $top <Key-Return> mktaggo
8956 bind $top <Key-Escape> mktagcan
8957 grid $top.buts.gen $top.buts.can
8958 grid columnconfigure $top.buts 0 -weight 1 -uniform a
8959 grid columnconfigure $top.buts 1 -weight 1 -uniform a
8960 grid $top.buts - -pady 10 -sticky ew
8965 global mktagtop env tagids idtags
8967 set id [$mktagtop.sha1 get]
8968 set tag [$mktagtop.tag get]
8969 set msg [$mktagtop.msg get]
8971 error_popup [mc "No tag name specified"] $mktagtop
8974 if {[info exists tagids($tag)]} {
8975 error_popup [mc "Tag \"%s\" already exists" $tag] $mktagtop
8980 exec git tag -a -m $msg $tag $id
8982 exec git tag $tag $id
8985 error_popup "[mc "Error creating tag:"] $err" $mktagtop
8989 set tagids($tag) $id
8990 lappend idtags($id) $tag
8998 proc redrawtags {id} {
8999 global canv linehtag idpos currentid curview cmitlisted markedid
9000 global canvxmax iddrawn circleitem mainheadid circlecolors
9002 if {![commitinview $id $curview]} return
9003 if {![info exists iddrawn($id)]} return
9004 set row [rowofcommit $id]
9005 if {$id eq $mainheadid} {
9008 set ofill [lindex $circlecolors $cmitlisted($curview,$id)]
9010 $canv itemconf $circleitem($row) -fill $ofill
9011 $canv delete tag.$id
9012 set xt [eval drawtags $id $idpos($id)]
9013 $canv coords $linehtag($id) $xt [lindex $idpos($id) 2]
9014 set text [$canv itemcget $linehtag($id) -text]
9015 set font [$canv itemcget $linehtag($id) -font]
9016 set xr [expr {$xt + [font measure $font $text]}]
9017 if {$xr > $canvxmax} {
9021 if {[info exists currentid] && $currentid == $id} {
9024 if {[info exists markedid] && $markedid eq $id} {
9032 catch {destroy $mktagtop}
9037 if {![domktag]} return
9041 proc writecommit {} {
9042 global rowmenuid wrcomtop commitinfo wrcomcmd NS
9044 set top .writecommit
9046 catch {destroy $top}
9048 make_transient $top .
9049 ${NS}::label $top.title -text [mc "Write commit to file"]
9050 grid $top.title - -pady 10
9051 ${NS}::label $top.id -text [mc "ID:"]
9052 ${NS}::entry $top.sha1 -width 40
9053 $top.sha1 insert 0 $rowmenuid
9054 $top.sha1 conf -state readonly
9055 grid $top.id $top.sha1 -sticky w
9056 ${NS}::entry $top.head -width 60
9057 $top.head insert 0 [lindex $commitinfo($rowmenuid) 0]
9058 $top.head conf -state readonly
9059 grid x $top.head -sticky w
9060 ${NS}::label $top.clab -text [mc "Command:"]
9061 ${NS}::entry $top.cmd -width 60 -textvariable wrcomcmd
9062 grid $top.clab $top.cmd -sticky w -pady 10
9063 ${NS}::label $top.flab -text [mc "Output file:"]
9064 ${NS}::entry $top.fname -width 60
9065 $top.fname insert 0 [file normalize "commit-[string range $rowmenuid 0 6]"]
9066 grid $top.flab $top.fname -sticky w
9067 ${NS}::frame $top.buts
9068 ${NS}::button $top.buts.gen -text [mc "Write"] -command wrcomgo
9069 ${NS}::button $top.buts.can -text [mc "Cancel"] -command wrcomcan
9070 bind $top <Key-Return> wrcomgo
9071 bind $top <Key-Escape> wrcomcan
9072 grid $top.buts.gen $top.buts.can
9073 grid columnconfigure $top.buts 0 -weight 1 -uniform a
9074 grid columnconfigure $top.buts 1 -weight 1 -uniform a
9075 grid $top.buts - -pady 10 -sticky ew
9082 set id [$wrcomtop.sha1 get]
9083 set cmd "echo $id | [$wrcomtop.cmd get]"
9084 set fname [$wrcomtop.fname get]
9085 if {[catch {exec sh -c $cmd >$fname &} err]} {
9086 error_popup "[mc "Error writing commit:"] $err" $wrcomtop
9088 catch {destroy $wrcomtop}
9095 catch {destroy $wrcomtop}
9100 global rowmenuid mkbrtop NS
9103 catch {destroy $top}
9105 make_transient $top .
9106 ${NS}::label $top.title -text [mc "Create new branch"]
9107 grid $top.title - -pady 10
9108 ${NS}::label $top.id -text [mc "ID:"]
9109 ${NS}::entry $top.sha1 -width 40
9110 $top.sha1 insert 0 $rowmenuid
9111 $top.sha1 conf -state readonly
9112 grid $top.id $top.sha1 -sticky w
9113 ${NS}::label $top.nlab -text [mc "Name:"]
9114 ${NS}::entry $top.name -width 40
9115 grid $top.nlab $top.name -sticky w
9116 ${NS}::frame $top.buts
9117 ${NS}::button $top.buts.go -text [mc "Create"] -command [list mkbrgo $top]
9118 ${NS}::button $top.buts.can -text [mc "Cancel"] -command "catch {destroy $top}"
9119 bind $top <Key-Return> [list mkbrgo $top]
9120 bind $top <Key-Escape> "catch {destroy $top}"
9121 grid $top.buts.go $top.buts.can
9122 grid columnconfigure $top.buts 0 -weight 1 -uniform a
9123 grid columnconfigure $top.buts 1 -weight 1 -uniform a
9124 grid $top.buts - -pady 10 -sticky ew
9129 global headids idheads
9131 set name [$top.name get]
9132 set id [$top.sha1 get]
9136 error_popup [mc "Please specify a name for the new branch"] $top
9139 if {[info exists headids($name)]} {
9140 if {![confirm_popup [mc \
9141 "Branch '%s' already exists. Overwrite?" $name] $top]} {
9144 set old_id $headids($name)
9147 catch {destroy $top}
9148 lappend cmdargs $name $id
9152 eval exec git branch $cmdargs
9158 if {$old_id ne {}} {
9164 set headids($name) $id
9165 lappend idheads($id) $name
9174 proc exec_citool {tool_args {baseid {}}} {
9175 global commitinfo env
9177 set save_env [array get env GIT_AUTHOR_*]
9179 if {$baseid ne {}} {
9180 if {![info exists commitinfo($baseid)]} {
9183 set author [lindex $commitinfo($baseid) 1]
9184 set date [lindex $commitinfo($baseid) 2]
9185 if {[regexp {^\s*(\S.*\S|\S)\s*<(.*)>\s*$} \
9186 $author author name email]
9188 set env(GIT_AUTHOR_NAME) $name
9189 set env(GIT_AUTHOR_EMAIL) $email
9190 set env(GIT_AUTHOR_DATE) $date
9194 eval exec git citool $tool_args &
9196 array unset env GIT_AUTHOR_*
9197 array set env $save_env
9200 proc cherrypick {} {
9201 global rowmenuid curview
9202 global mainhead mainheadid
9205 set oldhead [exec git rev-parse HEAD]
9206 set dheads [descheads $rowmenuid]
9207 if {$dheads ne {} && [lsearch -exact $dheads $oldhead] >= 0} {
9208 set ok [confirm_popup [mc "Commit %s is already\
9209 included in branch %s -- really re-apply it?" \
9210 [string range $rowmenuid 0 7] $mainhead]]
9213 nowbusy cherrypick [mc "Cherry-picking"]
9215 # Unfortunately git-cherry-pick writes stuff to stderr even when
9216 # no error occurs, and exec takes that as an indication of error...
9217 if {[catch {exec sh -c "git cherry-pick -r $rowmenuid 2>&1"} err]} {
9220 {Entry '(.*)' (would be overwritten by merge|not uptodate)} \
9222 error_popup [mc "Cherry-pick failed because of local changes\
9223 to file '%s'.\nPlease commit, reset or stash\
9224 your changes and try again." $fname]
9225 } elseif {[regexp -line \
9226 {^(CONFLICT \(.*\):|Automatic cherry-pick failed|error: could not apply)} \
9228 if {[confirm_popup [mc "Cherry-pick failed because of merge\
9229 conflict.\nDo you wish to run git citool to\
9231 # Force citool to read MERGE_MSG
9232 file delete [file join $gitdir "GITGUI_MSG"]
9233 exec_citool {} $rowmenuid
9241 set newhead [exec git rev-parse HEAD]
9242 if {$newhead eq $oldhead} {
9244 error_popup [mc "No changes committed"]
9247 addnewchild $newhead $oldhead
9248 if {[commitinview $oldhead $curview]} {
9249 # XXX this isn't right if we have a path limit...
9250 insertrow $newhead $oldhead $curview
9251 if {$mainhead ne {}} {
9252 movehead $newhead $mainhead
9253 movedhead $newhead $mainhead
9255 set mainheadid $newhead
9264 global mainhead rowmenuid confirm_ok resettype NS
9267 set w ".confirmreset"
9270 wm title $w [mc "Confirm reset"]
9271 ${NS}::label $w.m -text \
9272 [mc "Reset branch %s to %s?" $mainhead [string range $rowmenuid 0 7]]
9273 pack $w.m -side top -fill x -padx 20 -pady 20
9274 ${NS}::labelframe $w.f -text [mc "Reset type:"]
9276 ${NS}::radiobutton $w.f.soft -value soft -variable resettype \
9277 -text [mc "Soft: Leave working tree and index untouched"]
9278 grid $w.f.soft -sticky w
9279 ${NS}::radiobutton $w.f.mixed -value mixed -variable resettype \
9280 -text [mc "Mixed: Leave working tree untouched, reset index"]
9281 grid $w.f.mixed -sticky w
9282 ${NS}::radiobutton $w.f.hard -value hard -variable resettype \
9283 -text [mc "Hard: Reset working tree and index\n(discard ALL local changes)"]
9284 grid $w.f.hard -sticky w
9285 pack $w.f -side top -fill x -padx 4
9286 ${NS}::button $w.ok -text [mc OK] -command "set confirm_ok 1; destroy $w"
9287 pack $w.ok -side left -fill x -padx 20 -pady 20
9288 ${NS}::button $w.cancel -text [mc Cancel] -command "destroy $w"
9289 bind $w <Key-Escape> [list destroy $w]
9290 pack $w.cancel -side right -fill x -padx 20 -pady 20
9291 bind $w <Visibility> "grab $w; focus $w"
9293 if {!$confirm_ok} return
9294 if {[catch {set fd [open \
9295 [list | git reset --$resettype $rowmenuid 2>@1] r]} err]} {
9299 filerun $fd [list readresetstat $fd]
9300 nowbusy reset [mc "Resetting"]
9305 proc readresetstat {fd} {
9306 global mainhead mainheadid showlocalchanges rprogcoord
9308 if {[gets $fd line] >= 0} {
9309 if {[regexp {([0-9]+)% \(([0-9]+)/([0-9]+)\)} $line match p m n]} {
9310 set rprogcoord [expr {1.0 * $m / $n}]
9318 if {[catch {close $fd} err]} {
9321 set oldhead $mainheadid
9322 set newhead [exec git rev-parse HEAD]
9323 if {$newhead ne $oldhead} {
9324 movehead $newhead $mainhead
9325 movedhead $newhead $mainhead
9326 set mainheadid $newhead
9330 if {$showlocalchanges} {
9336 # context menu for a head
9337 proc headmenu {x y id head} {
9338 global headmenuid headmenuhead headctxmenu mainhead
9342 set headmenuhead $head
9344 if {[string match "remotes/*" $head]} {
9347 if {$head eq $mainhead} {
9350 $headctxmenu entryconfigure 0 -state $state
9351 $headctxmenu entryconfigure 1 -state $state
9352 tk_popup $headctxmenu $x $y
9356 global headmenuid headmenuhead headids
9357 global showlocalchanges
9359 # check the tree is clean first??
9360 nowbusy checkout [mc "Checking out"]
9364 set fd [open [list | git checkout $headmenuhead 2>@1] r]
9368 if {$showlocalchanges} {
9372 filerun $fd [list readcheckoutstat $fd $headmenuhead $headmenuid]
9376 proc readcheckoutstat {fd newhead newheadid} {
9377 global mainhead mainheadid headids showlocalchanges progresscoords
9378 global viewmainheadid curview
9380 if {[gets $fd line] >= 0} {
9381 if {[regexp {([0-9]+)% \(([0-9]+)/([0-9]+)\)} $line match p m n]} {
9382 set progresscoords [list 0 [expr {1.0 * $m / $n}]]
9387 set progresscoords {0 0}
9390 if {[catch {close $fd} err]} {
9393 set oldmainid $mainheadid
9394 set mainhead $newhead
9395 set mainheadid $newheadid
9396 set viewmainheadid($curview) $newheadid
9397 redrawtags $oldmainid
9398 redrawtags $newheadid
9400 if {$showlocalchanges} {
9406 global headmenuid headmenuhead mainhead
9409 set head $headmenuhead
9411 # this check shouldn't be needed any more...
9412 if {$head eq $mainhead} {
9413 error_popup [mc "Cannot delete the currently checked-out branch"]
9416 set dheads [descheads $id]
9417 if {[llength $dheads] == 1 && $idheads($dheads) eq $head} {
9418 # the stuff on this branch isn't on any other branch
9419 if {![confirm_popup [mc "The commits on branch %s aren't on any other\
9420 branch.\nReally delete branch %s?" $head $head]]} return
9424 if {[catch {exec git branch -D $head} err]} {
9429 removehead $id $head
9430 removedhead $id $head
9437 # Display a list of tags and heads
9439 global showrefstop bgcolor fgcolor selectbgcolor NS
9440 global bglist fglist reflistfilter reflist maincursor
9443 set showrefstop $top
9444 if {[winfo exists $top]} {
9450 wm title $top [mc "Tags and heads: %s" [file tail [pwd]]]
9451 make_transient $top .
9452 text $top.list -background $bgcolor -foreground $fgcolor \
9453 -selectbackground $selectbgcolor -font mainfont \
9454 -xscrollcommand "$top.xsb set" -yscrollcommand "$top.ysb set" \
9455 -width 30 -height 20 -cursor $maincursor \
9456 -spacing1 1 -spacing3 1 -state disabled
9457 $top.list tag configure highlight -background $selectbgcolor
9458 lappend bglist $top.list
9459 lappend fglist $top.list
9460 ${NS}::scrollbar $top.ysb -command "$top.list yview" -orient vertical
9461 ${NS}::scrollbar $top.xsb -command "$top.list xview" -orient horizontal
9462 grid $top.list $top.ysb -sticky nsew
9463 grid $top.xsb x -sticky ew
9465 ${NS}::label $top.f.l -text "[mc "Filter"]: "
9466 ${NS}::entry $top.f.e -width 20 -textvariable reflistfilter
9467 set reflistfilter "*"
9468 trace add variable reflistfilter write reflistfilter_change
9469 pack $top.f.e -side right -fill x -expand 1
9470 pack $top.f.l -side left
9471 grid $top.f - -sticky ew -pady 2
9472 ${NS}::button $top.close -command [list destroy $top] -text [mc "Close"]
9473 bind $top <Key-Escape> [list destroy $top]
9475 grid columnconfigure $top 0 -weight 1
9476 grid rowconfigure $top 0 -weight 1
9477 bind $top.list <1> {break}
9478 bind $top.list <B1-Motion> {break}
9479 bind $top.list <ButtonRelease-1> {sel_reflist %W %x %y; break}
9484 proc sel_reflist {w x y} {
9485 global showrefstop reflist headids tagids otherrefids
9487 if {![winfo exists $showrefstop]} return
9488 set l [lindex [split [$w index "@$x,$y"] "."] 0]
9489 set ref [lindex $reflist [expr {$l-1}]]
9490 set n [lindex $ref 0]
9491 switch -- [lindex $ref 1] {
9492 "H" {selbyid $headids($n)}
9493 "T" {selbyid $tagids($n)}
9494 "o" {selbyid $otherrefids($n)}
9496 $showrefstop.list tag add highlight $l.0 "$l.0 lineend"
9499 proc unsel_reflist {} {
9502 if {![info exists showrefstop] || ![winfo exists $showrefstop]} return
9503 $showrefstop.list tag remove highlight 0.0 end
9506 proc reflistfilter_change {n1 n2 op} {
9507 global reflistfilter
9509 after cancel refill_reflist
9510 after 200 refill_reflist
9513 proc refill_reflist {} {
9514 global reflist reflistfilter showrefstop headids tagids otherrefids
9517 if {![info exists showrefstop] || ![winfo exists $showrefstop]} return
9519 foreach n [array names headids] {
9520 if {[string match $reflistfilter $n]} {
9521 if {[commitinview $headids($n) $curview]} {
9522 lappend refs [list $n H]
9524 interestedin $headids($n) {run refill_reflist}
9528 foreach n [array names tagids] {
9529 if {[string match $reflistfilter $n]} {
9530 if {[commitinview $tagids($n) $curview]} {
9531 lappend refs [list $n T]
9533 interestedin $tagids($n) {run refill_reflist}
9537 foreach n [array names otherrefids] {
9538 if {[string match $reflistfilter $n]} {
9539 if {[commitinview $otherrefids($n) $curview]} {
9540 lappend refs [list $n o]
9542 interestedin $otherrefids($n) {run refill_reflist}
9546 set refs [lsort -index 0 $refs]
9547 if {$refs eq $reflist} return
9549 # Update the contents of $showrefstop.list according to the
9550 # differences between $reflist (old) and $refs (new)
9551 $showrefstop.list conf -state normal
9552 $showrefstop.list insert end "\n"
9555 while {$i < [llength $reflist] || $j < [llength $refs]} {
9556 if {$i < [llength $reflist]} {
9557 if {$j < [llength $refs]} {
9558 set cmp [string compare [lindex $reflist $i 0] \
9559 [lindex $refs $j 0]]
9561 set cmp [string compare [lindex $reflist $i 1] \
9562 [lindex $refs $j 1]]
9572 $showrefstop.list delete "[expr {$j+1}].0" "[expr {$j+2}].0"
9580 set l [expr {$j + 1}]
9581 $showrefstop.list image create $l.0 -align baseline \
9582 -image reficon-[lindex $refs $j 1] -padx 2
9583 $showrefstop.list insert $l.1 "[lindex $refs $j 0]\n"
9589 # delete last newline
9590 $showrefstop.list delete end-2c end-1c
9591 $showrefstop.list conf -state disabled
9594 # Stuff for finding nearby tags
9595 proc getallcommits {} {
9596 global allcommits nextarc seeds allccache allcwait cachedarcs allcupdate
9597 global idheads idtags idotherrefs allparents tagobjid
9600 if {![info exists allcommits]} {
9606 set allccache [file join $gitdir "gitk.cache"]
9608 set f [open $allccache r]
9617 set cmd [list | git rev-list --parents]
9618 set allcupdate [expr {$seeds ne {}}]
9622 set refs [concat [array names idheads] [array names idtags] \
9623 [array names idotherrefs]]
9626 foreach name [array names tagobjid] {
9627 lappend tagobjs $tagobjid($name)
9629 foreach id [lsort -unique $refs] {
9630 if {![info exists allparents($id)] &&
9631 [lsearch -exact $tagobjs $id] < 0} {
9642 set fd [open [concat $cmd $ids] r]
9643 fconfigure $fd -blocking 0
9646 filerun $fd [list getallclines $fd]
9652 # Since most commits have 1 parent and 1 child, we group strings of
9653 # such commits into "arcs" joining branch/merge points (BMPs), which
9654 # are commits that either don't have 1 parent or don't have 1 child.
9656 # arcnos(id) - incoming arcs for BMP, arc we're on for other nodes
9657 # arcout(id) - outgoing arcs for BMP
9658 # arcids(a) - list of IDs on arc including end but not start
9659 # arcstart(a) - BMP ID at start of arc
9660 # arcend(a) - BMP ID at end of arc
9661 # growing(a) - arc a is still growing
9662 # arctags(a) - IDs out of arcids (excluding end) that have tags
9663 # archeads(a) - IDs out of arcids (excluding end) that have heads
9664 # The start of an arc is at the descendent end, so "incoming" means
9665 # coming from descendents, and "outgoing" means going towards ancestors.
9667 proc getallclines {fd} {
9668 global allparents allchildren idtags idheads nextarc
9669 global arcnos arcids arctags arcout arcend arcstart archeads growing
9670 global seeds allcommits cachedarcs allcupdate
9673 while {[incr nid] <= 1000 && [gets $fd line] >= 0} {
9674 set id [lindex $line 0]
9675 if {[info exists allparents($id)]} {
9680 set olds [lrange $line 1 end]
9681 set allparents($id) $olds
9682 if {![info exists allchildren($id)]} {
9683 set allchildren($id) {}
9688 if {[llength $olds] == 1 && [llength $a] == 1} {
9689 lappend arcids($a) $id
9690 if {[info exists idtags($id)]} {
9691 lappend arctags($a) $id
9693 if {[info exists idheads($id)]} {
9694 lappend archeads($a) $id
9696 if {[info exists allparents($olds)]} {
9697 # seen parent already
9698 if {![info exists arcout($olds)]} {
9701 lappend arcids($a) $olds
9702 set arcend($a) $olds
9705 lappend allchildren($olds) $id
9706 lappend arcnos($olds) $a
9710 foreach a $arcnos($id) {
9711 lappend arcids($a) $id
9718 lappend allchildren($p) $id
9719 set a [incr nextarc]
9720 set arcstart($a) $id
9727 if {[info exists allparents($p)]} {
9728 # seen it already, may need to make a new branch
9729 if {![info exists arcout($p)]} {
9732 lappend arcids($a) $p
9736 lappend arcnos($p) $a
9741 global cached_dheads cached_dtags cached_atags
9742 catch {unset cached_dheads}
9743 catch {unset cached_dtags}
9744 catch {unset cached_atags}
9747 return [expr {$nid >= 1000? 2: 1}]
9751 fconfigure $fd -blocking 1
9754 # got an error reading the list of commits
9755 # if we were updating, try rereading the whole thing again
9761 error_popup "[mc "Error reading commit topology information;\
9762 branch and preceding/following tag information\
9763 will be incomplete."]\n($err)"
9766 if {[incr allcommits -1] == 0} {
9776 proc recalcarc {a} {
9777 global arctags archeads arcids idtags idheads
9781 foreach id [lrange $arcids($a) 0 end-1] {
9782 if {[info exists idtags($id)]} {
9785 if {[info exists idheads($id)]} {
9790 set archeads($a) $ah
9794 global arcnos arcids nextarc arctags archeads idtags idheads
9795 global arcstart arcend arcout allparents growing
9798 if {[llength $a] != 1} {
9799 puts "oops splitarc called but [llength $a] arcs already"
9803 set i [lsearch -exact $arcids($a) $p]
9805 puts "oops splitarc $p not in arc $a"
9808 set na [incr nextarc]
9809 if {[info exists arcend($a)]} {
9810 set arcend($na) $arcend($a)
9812 set l [lindex $allparents([lindex $arcids($a) end]) 0]
9813 set j [lsearch -exact $arcnos($l) $a]
9814 set arcnos($l) [lreplace $arcnos($l) $j $j $na]
9816 set tail [lrange $arcids($a) [expr {$i+1}] end]
9817 set arcids($a) [lrange $arcids($a) 0 $i]
9819 set arcstart($na) $p
9821 set arcids($na) $tail
9822 if {[info exists growing($a)]} {
9828 if {[llength $arcnos($id)] == 1} {
9831 set j [lsearch -exact $arcnos($id) $a]
9832 set arcnos($id) [lreplace $arcnos($id) $j $j $na]
9836 # reconstruct tags and heads lists
9837 if {$arctags($a) ne {} || $archeads($a) ne {}} {
9842 set archeads($na) {}
9846 # Update things for a new commit added that is a child of one
9847 # existing commit. Used when cherry-picking.
9848 proc addnewchild {id p} {
9849 global allparents allchildren idtags nextarc
9850 global arcnos arcids arctags arcout arcend arcstart archeads growing
9851 global seeds allcommits
9853 if {![info exists allcommits] || ![info exists arcnos($p)]} return
9854 set allparents($id) [list $p]
9855 set allchildren($id) {}
9858 lappend allchildren($p) $id
9859 set a [incr nextarc]
9860 set arcstart($a) $id
9863 set arcids($a) [list $p]
9865 if {![info exists arcout($p)]} {
9868 lappend arcnos($p) $a
9869 set arcout($id) [list $a]
9872 # This implements a cache for the topology information.
9873 # The cache saves, for each arc, the start and end of the arc,
9874 # the ids on the arc, and the outgoing arcs from the end.
9875 proc readcache {f} {
9876 global arcnos arcids arcout arcstart arcend arctags archeads nextarc
9877 global idtags idheads allparents cachedarcs possible_seeds seeds growing
9882 if {$lim - $a > 500} {
9883 set lim [expr {$a + 500}]
9887 # finish reading the cache and setting up arctags, etc.
9889 if {$line ne "1"} {error "bad final version"}
9891 foreach id [array names idtags] {
9892 if {[info exists arcnos($id)] && [llength $arcnos($id)] == 1 &&
9893 [llength $allparents($id)] == 1} {
9894 set a [lindex $arcnos($id) 0]
9895 if {$arctags($a) eq {}} {
9900 foreach id [array names idheads] {
9901 if {[info exists arcnos($id)] && [llength $arcnos($id)] == 1 &&
9902 [llength $allparents($id)] == 1} {
9903 set a [lindex $arcnos($id) 0]
9904 if {$archeads($a) eq {}} {
9909 foreach id [lsort -unique $possible_seeds] {
9910 if {$arcnos($id) eq {}} {
9916 while {[incr a] <= $lim} {
9918 if {[llength $line] != 3} {error "bad line"}
9919 set s [lindex $line 0]
9921 lappend arcout($s) $a
9922 if {![info exists arcnos($s)]} {
9923 lappend possible_seeds $s
9926 set e [lindex $line 1]
9931 if {![info exists arcout($e)]} {
9935 set arcids($a) [lindex $line 2]
9936 foreach id $arcids($a) {
9937 lappend allparents($s) $id
9939 lappend arcnos($id) $a
9941 if {![info exists allparents($s)]} {
9942 set allparents($s) {}
9947 set nextarc [expr {$a - 1}]
9960 global nextarc cachedarcs possible_seeds
9964 if {[llength $line] != 2 || [lindex $line 0] ne "1"} {error "bad version"}
9965 # make sure it's an integer
9966 set cachedarcs [expr {int([lindex $line 1])}]
9967 if {$cachedarcs < 0} {error "bad number of arcs"}
9969 set possible_seeds {}
9977 proc dropcache {err} {
9978 global allcwait nextarc cachedarcs seeds
9980 #puts "dropping cache ($err)"
9981 foreach v {arcnos arcout arcids arcstart arcend growing \
9982 arctags archeads allparents allchildren} {
9993 proc writecache {f} {
9994 global cachearc cachedarcs allccache
9995 global arcstart arcend arcnos arcids arcout
9999 if {$lim - $a > 1000} {
10000 set lim [expr {$a + 1000}]
10003 while {[incr a] <= $lim} {
10004 if {[info exists arcend($a)]} {
10005 puts $f [list $arcstart($a) $arcend($a) $arcids($a)]
10007 puts $f [list $arcstart($a) {} $arcids($a)]
10012 catch {file delete $allccache}
10013 #puts "writing cache failed ($err)"
10016 set cachearc [expr {$a - 1}]
10017 if {$a > $cachedarcs} {
10025 proc savecache {} {
10026 global nextarc cachedarcs cachearc allccache
10028 if {$nextarc == $cachedarcs} return
10030 set cachedarcs $nextarc
10032 set f [open $allccache w]
10033 puts $f [list 1 $cachedarcs]
10038 # Returns 1 if a is an ancestor of b, -1 if b is an ancestor of a,
10039 # or 0 if neither is true.
10040 proc anc_or_desc {a b} {
10041 global arcout arcstart arcend arcnos cached_isanc
10043 if {$arcnos($a) eq $arcnos($b)} {
10044 # Both are on the same arc(s); either both are the same BMP,
10045 # or if one is not a BMP, the other is also not a BMP or is
10046 # the BMP at end of the arc (and it only has 1 incoming arc).
10047 # Or both can be BMPs with no incoming arcs.
10048 if {$a eq $b || $arcnos($a) eq {}} {
10051 # assert {[llength $arcnos($a)] == 1}
10052 set arc [lindex $arcnos($a) 0]
10053 set i [lsearch -exact $arcids($arc) $a]
10054 set j [lsearch -exact $arcids($arc) $b]
10055 if {$i < 0 || $i > $j} {
10062 if {![info exists arcout($a)]} {
10063 set arc [lindex $arcnos($a) 0]
10064 if {[info exists arcend($arc)]} {
10065 set aend $arcend($arc)
10069 set a $arcstart($arc)
10073 if {![info exists arcout($b)]} {
10074 set arc [lindex $arcnos($b) 0]
10075 if {[info exists arcend($arc)]} {
10076 set bend $arcend($arc)
10080 set b $arcstart($arc)
10090 if {[info exists cached_isanc($a,$bend)]} {
10091 if {$cached_isanc($a,$bend)} {
10095 if {[info exists cached_isanc($b,$aend)]} {
10096 if {$cached_isanc($b,$aend)} {
10099 if {[info exists cached_isanc($a,$bend)]} {
10104 set todo [list $a $b]
10107 for {set i 0} {$i < [llength $todo]} {incr i} {
10108 set x [lindex $todo $i]
10109 if {$anc($x) eq {}} {
10112 foreach arc $arcnos($x) {
10113 set xd $arcstart($arc)
10114 if {$xd eq $bend} {
10115 set cached_isanc($a,$bend) 1
10116 set cached_isanc($b,$aend) 0
10118 } elseif {$xd eq $aend} {
10119 set cached_isanc($b,$aend) 1
10120 set cached_isanc($a,$bend) 0
10123 if {![info exists anc($xd)]} {
10124 set anc($xd) $anc($x)
10126 } elseif {$anc($xd) ne $anc($x)} {
10131 set cached_isanc($a,$bend) 0
10132 set cached_isanc($b,$aend) 0
10136 # This identifies whether $desc has an ancestor that is
10137 # a growing tip of the graph and which is not an ancestor of $anc
10138 # and returns 0 if so and 1 if not.
10139 # If we subsequently discover a tag on such a growing tip, and that
10140 # turns out to be a descendent of $anc (which it could, since we
10141 # don't necessarily see children before parents), then $desc
10142 # isn't a good choice to display as a descendent tag of
10143 # $anc (since it is the descendent of another tag which is
10144 # a descendent of $anc). Similarly, $anc isn't a good choice to
10145 # display as a ancestor tag of $desc.
10147 proc is_certain {desc anc} {
10148 global arcnos arcout arcstart arcend growing problems
10151 if {[llength $arcnos($anc)] == 1} {
10152 # tags on the same arc are certain
10153 if {$arcnos($desc) eq $arcnos($anc)} {
10156 if {![info exists arcout($anc)]} {
10157 # if $anc is partway along an arc, use the start of the arc instead
10158 set a [lindex $arcnos($anc) 0]
10159 set anc $arcstart($a)
10162 if {[llength $arcnos($desc)] > 1 || [info exists arcout($desc)]} {
10165 set a [lindex $arcnos($desc) 0]
10171 set anclist [list $x]
10175 for {set i 0} {$i < [llength $anclist] && ($nnh > 0 || $ngrowanc > 0)} {incr i} {
10176 set x [lindex $anclist $i]
10181 foreach a $arcout($x) {
10182 if {[info exists growing($a)]} {
10183 if {![info exists growanc($x)] && $dl($x)} {
10189 if {[info exists dl($y)]} {
10193 if {![info exists done($y)]} {
10196 if {[info exists growanc($x)]} {
10200 for {set k 0} {$k < [llength $xl]} {incr k} {
10201 set z [lindex $xl $k]
10202 foreach c $arcout($z) {
10203 if {[info exists arcend($c)]} {
10205 if {[info exists dl($v)] && $dl($v)} {
10207 if {![info exists done($v)]} {
10210 if {[info exists growanc($v)]} {
10220 } elseif {$y eq $anc || !$dl($x)} {
10231 foreach x [array names growanc] {
10240 proc validate_arctags {a} {
10241 global arctags idtags
10244 set na $arctags($a)
10245 foreach id $arctags($a) {
10247 if {![info exists idtags($id)]} {
10248 set na [lreplace $na $i $i]
10252 set arctags($a) $na
10255 proc validate_archeads {a} {
10256 global archeads idheads
10259 set na $archeads($a)
10260 foreach id $archeads($a) {
10262 if {![info exists idheads($id)]} {
10263 set na [lreplace $na $i $i]
10267 set archeads($a) $na
10270 # Return the list of IDs that have tags that are descendents of id,
10271 # ignoring IDs that are descendents of IDs already reported.
10272 proc desctags {id} {
10273 global arcnos arcstart arcids arctags idtags allparents
10274 global growing cached_dtags
10276 if {![info exists allparents($id)]} {
10279 set t1 [clock clicks -milliseconds]
10281 if {[llength $arcnos($id)] == 1 && [llength $allparents($id)] == 1} {
10282 # part-way along an arc; check that arc first
10283 set a [lindex $arcnos($id) 0]
10284 if {$arctags($a) ne {}} {
10285 validate_arctags $a
10286 set i [lsearch -exact $arcids($a) $id]
10288 foreach t $arctags($a) {
10289 set j [lsearch -exact $arcids($a) $t]
10290 if {$j >= $i} break
10297 set id $arcstart($a)
10298 if {[info exists idtags($id)]} {
10302 if {[info exists cached_dtags($id)]} {
10303 return $cached_dtags($id)
10307 set todo [list $id]
10310 for {set i 0} {$i < [llength $todo] && $nc > 0} {incr i} {
10311 set id [lindex $todo $i]
10313 set ta [info exists hastaggedancestor($id)]
10317 # ignore tags on starting node
10318 if {!$ta && $i > 0} {
10319 if {[info exists idtags($id)]} {
10320 set tagloc($id) $id
10322 } elseif {[info exists cached_dtags($id)]} {
10323 set tagloc($id) $cached_dtags($id)
10327 foreach a $arcnos($id) {
10328 set d $arcstart($a)
10329 if {!$ta && $arctags($a) ne {}} {
10330 validate_arctags $a
10331 if {$arctags($a) ne {}} {
10332 lappend tagloc($id) [lindex $arctags($a) end]
10335 if {$ta || $arctags($a) ne {}} {
10336 set tomark [list $d]
10337 for {set j 0} {$j < [llength $tomark]} {incr j} {
10338 set dd [lindex $tomark $j]
10339 if {![info exists hastaggedancestor($dd)]} {
10340 if {[info exists done($dd)]} {
10341 foreach b $arcnos($dd) {
10342 lappend tomark $arcstart($b)
10344 if {[info exists tagloc($dd)]} {
10347 } elseif {[info exists queued($dd)]} {
10350 set hastaggedancestor($dd) 1
10354 if {![info exists queued($d)]} {
10357 if {![info exists hastaggedancestor($d)]} {
10364 foreach id [array names tagloc] {
10365 if {![info exists hastaggedancestor($id)]} {
10366 foreach t $tagloc($id) {
10367 if {[lsearch -exact $tags $t] < 0} {
10373 set t2 [clock clicks -milliseconds]
10376 # remove tags that are descendents of other tags
10377 for {set i 0} {$i < [llength $tags]} {incr i} {
10378 set a [lindex $tags $i]
10379 for {set j 0} {$j < $i} {incr j} {
10380 set b [lindex $tags $j]
10381 set r [anc_or_desc $a $b]
10383 set tags [lreplace $tags $j $j]
10386 } elseif {$r == -1} {
10387 set tags [lreplace $tags $i $i]
10394 if {[array names growing] ne {}} {
10395 # graph isn't finished, need to check if any tag could get
10396 # eclipsed by another tag coming later. Simply ignore any
10397 # tags that could later get eclipsed.
10400 if {[is_certain $t $origid]} {
10404 if {$tags eq $ctags} {
10405 set cached_dtags($origid) $tags
10410 set cached_dtags($origid) $tags
10412 set t3 [clock clicks -milliseconds]
10413 if {0 && $t3 - $t1 >= 100} {
10414 puts "iterating descendents ($loopix/[llength $todo] nodes) took\
10415 [expr {$t2-$t1}]+[expr {$t3-$t2}]ms, $nc candidates left"
10420 proc anctags {id} {
10421 global arcnos arcids arcout arcend arctags idtags allparents
10422 global growing cached_atags
10424 if {![info exists allparents($id)]} {
10427 set t1 [clock clicks -milliseconds]
10429 if {[llength $arcnos($id)] == 1 && [llength $allparents($id)] == 1} {
10430 # part-way along an arc; check that arc first
10431 set a [lindex $arcnos($id) 0]
10432 if {$arctags($a) ne {}} {
10433 validate_arctags $a
10434 set i [lsearch -exact $arcids($a) $id]
10435 foreach t $arctags($a) {
10436 set j [lsearch -exact $arcids($a) $t]
10442 if {![info exists arcend($a)]} {
10446 if {[info exists idtags($id)]} {
10450 if {[info exists cached_atags($id)]} {
10451 return $cached_atags($id)
10455 set todo [list $id]
10459 for {set i 0} {$i < [llength $todo] && $nc > 0} {incr i} {
10460 set id [lindex $todo $i]
10462 set td [info exists hastaggeddescendent($id)]
10466 # ignore tags on starting node
10467 if {!$td && $i > 0} {
10468 if {[info exists idtags($id)]} {
10469 set tagloc($id) $id
10471 } elseif {[info exists cached_atags($id)]} {
10472 set tagloc($id) $cached_atags($id)
10476 foreach a $arcout($id) {
10477 if {!$td && $arctags($a) ne {}} {
10478 validate_arctags $a
10479 if {$arctags($a) ne {}} {
10480 lappend tagloc($id) [lindex $arctags($a) 0]
10483 if {![info exists arcend($a)]} continue
10485 if {$td || $arctags($a) ne {}} {
10486 set tomark [list $d]
10487 for {set j 0} {$j < [llength $tomark]} {incr j} {
10488 set dd [lindex $tomark $j]
10489 if {![info exists hastaggeddescendent($dd)]} {
10490 if {[info exists done($dd)]} {
10491 foreach b $arcout($dd) {
10492 if {[info exists arcend($b)]} {
10493 lappend tomark $arcend($b)
10496 if {[info exists tagloc($dd)]} {
10499 } elseif {[info exists queued($dd)]} {
10502 set hastaggeddescendent($dd) 1
10506 if {![info exists queued($d)]} {
10509 if {![info exists hastaggeddescendent($d)]} {
10515 set t2 [clock clicks -milliseconds]
10518 foreach id [array names tagloc] {
10519 if {![info exists hastaggeddescendent($id)]} {
10520 foreach t $tagloc($id) {
10521 if {[lsearch -exact $tags $t] < 0} {
10528 # remove tags that are ancestors of other tags
10529 for {set i 0} {$i < [llength $tags]} {incr i} {
10530 set a [lindex $tags $i]
10531 for {set j 0} {$j < $i} {incr j} {
10532 set b [lindex $tags $j]
10533 set r [anc_or_desc $a $b]
10535 set tags [lreplace $tags $j $j]
10538 } elseif {$r == 1} {
10539 set tags [lreplace $tags $i $i]
10546 if {[array names growing] ne {}} {
10547 # graph isn't finished, need to check if any tag could get
10548 # eclipsed by another tag coming later. Simply ignore any
10549 # tags that could later get eclipsed.
10552 if {[is_certain $origid $t]} {
10556 if {$tags eq $ctags} {
10557 set cached_atags($origid) $tags
10562 set cached_atags($origid) $tags
10564 set t3 [clock clicks -milliseconds]
10565 if {0 && $t3 - $t1 >= 100} {
10566 puts "iterating ancestors ($loopix/[llength $todo] nodes) took\
10567 [expr {$t2-$t1}]+[expr {$t3-$t2}]ms, $nc candidates left"
10572 # Return the list of IDs that have heads that are descendents of id,
10573 # including id itself if it has a head.
10574 proc descheads {id} {
10575 global arcnos arcstart arcids archeads idheads cached_dheads
10578 if {![info exists allparents($id)]} {
10582 if {[llength $arcnos($id)] == 1 && [llength $allparents($id)] == 1} {
10583 # part-way along an arc; check it first
10584 set a [lindex $arcnos($id) 0]
10585 if {$archeads($a) ne {}} {
10586 validate_archeads $a
10587 set i [lsearch -exact $arcids($a) $id]
10588 foreach t $archeads($a) {
10589 set j [lsearch -exact $arcids($a) $t]
10594 set id $arcstart($a)
10597 set todo [list $id]
10600 for {set i 0} {$i < [llength $todo]} {incr i} {
10601 set id [lindex $todo $i]
10602 if {[info exists cached_dheads($id)]} {
10603 set ret [concat $ret $cached_dheads($id)]
10605 if {[info exists idheads($id)]} {
10608 foreach a $arcnos($id) {
10609 if {$archeads($a) ne {}} {
10610 validate_archeads $a
10611 if {$archeads($a) ne {}} {
10612 set ret [concat $ret $archeads($a)]
10615 set d $arcstart($a)
10616 if {![info exists seen($d)]} {
10623 set ret [lsort -unique $ret]
10624 set cached_dheads($origid) $ret
10625 return [concat $ret $aret]
10628 proc addedtag {id} {
10629 global arcnos arcout cached_dtags cached_atags
10631 if {![info exists arcnos($id)]} return
10632 if {![info exists arcout($id)]} {
10633 recalcarc [lindex $arcnos($id) 0]
10635 catch {unset cached_dtags}
10636 catch {unset cached_atags}
10639 proc addedhead {hid head} {
10640 global arcnos arcout cached_dheads
10642 if {![info exists arcnos($hid)]} return
10643 if {![info exists arcout($hid)]} {
10644 recalcarc [lindex $arcnos($hid) 0]
10646 catch {unset cached_dheads}
10649 proc removedhead {hid head} {
10650 global cached_dheads
10652 catch {unset cached_dheads}
10655 proc movedhead {hid head} {
10656 global arcnos arcout cached_dheads
10658 if {![info exists arcnos($hid)]} return
10659 if {![info exists arcout($hid)]} {
10660 recalcarc [lindex $arcnos($hid) 0]
10662 catch {unset cached_dheads}
10665 proc changedrefs {} {
10666 global cached_dheads cached_dtags cached_atags cached_tagcontent
10667 global arctags archeads arcnos arcout idheads idtags
10669 foreach id [concat [array names idheads] [array names idtags]] {
10670 if {[info exists arcnos($id)] && ![info exists arcout($id)]} {
10671 set a [lindex $arcnos($id) 0]
10672 if {![info exists donearc($a)]} {
10678 catch {unset cached_tagcontent}
10679 catch {unset cached_dtags}
10680 catch {unset cached_atags}
10681 catch {unset cached_dheads}
10684 proc rereadrefs {} {
10685 global idtags idheads idotherrefs mainheadid
10687 set refids [concat [array names idtags] \
10688 [array names idheads] [array names idotherrefs]]
10689 foreach id $refids {
10690 if {![info exists ref($id)]} {
10691 set ref($id) [listrefs $id]
10694 set oldmainhead $mainheadid
10697 set refids [lsort -unique [concat $refids [array names idtags] \
10698 [array names idheads] [array names idotherrefs]]]
10699 foreach id $refids {
10700 set v [listrefs $id]
10701 if {![info exists ref($id)] || $ref($id) != $v} {
10705 if {$oldmainhead ne $mainheadid} {
10706 redrawtags $oldmainhead
10707 redrawtags $mainheadid
10712 proc listrefs {id} {
10713 global idtags idheads idotherrefs
10716 if {[info exists idtags($id)]} {
10720 if {[info exists idheads($id)]} {
10721 set y $idheads($id)
10724 if {[info exists idotherrefs($id)]} {
10725 set z $idotherrefs($id)
10727 return [list $x $y $z]
10730 proc showtag {tag isnew} {
10731 global ctext cached_tagcontent tagids linknum tagobjid
10734 addtohistory [list showtag $tag 0] savectextpos
10736 $ctext conf -state normal
10740 if {![info exists cached_tagcontent($tag)]} {
10742 set cached_tagcontent($tag) [exec git cat-file tag $tag]
10745 if {[info exists cached_tagcontent($tag)]} {
10746 set text $cached_tagcontent($tag)
10748 set text "[mc "Tag"]: $tag\n[mc "Id"]: $tagids($tag)"
10750 appendwithlinks $text {}
10751 maybe_scroll_ctext 1
10752 $ctext conf -state disabled
10764 if {[info exists gitktmpdir]} {
10765 catch {file delete -force $gitktmpdir}
10769 proc mkfontdisp {font top which} {
10770 global fontattr fontpref $font NS use_ttk
10772 set fontpref($font) [set $font]
10773 ${NS}::button $top.${font}but -text $which \
10774 -command [list choosefont $font $which]
10775 ${NS}::label $top.$font -relief flat -font $font \
10776 -text $fontattr($font,family) -justify left
10777 grid x $top.${font}but $top.$font -sticky w
10780 proc choosefont {font which} {
10781 global fontparam fontlist fonttop fontattr
10784 set fontparam(which) $which
10785 set fontparam(font) $font
10786 set fontparam(family) [font actual $font -family]
10787 set fontparam(size) $fontattr($font,size)
10788 set fontparam(weight) $fontattr($font,weight)
10789 set fontparam(slant) $fontattr($font,slant)
10792 if {![winfo exists $top]} {
10794 eval font config sample [font actual $font]
10796 make_transient $top $prefstop
10797 wm title $top [mc "Gitk font chooser"]
10798 ${NS}::label $top.l -textvariable fontparam(which)
10799 pack $top.l -side top
10800 set fontlist [lsort [font families]]
10801 ${NS}::frame $top.f
10802 listbox $top.f.fam -listvariable fontlist \
10803 -yscrollcommand [list $top.f.sb set]
10804 bind $top.f.fam <<ListboxSelect>> selfontfam
10805 ${NS}::scrollbar $top.f.sb -command [list $top.f.fam yview]
10806 pack $top.f.sb -side right -fill y
10807 pack $top.f.fam -side left -fill both -expand 1
10808 pack $top.f -side top -fill both -expand 1
10809 ${NS}::frame $top.g
10810 spinbox $top.g.size -from 4 -to 40 -width 4 \
10811 -textvariable fontparam(size) \
10812 -validatecommand {string is integer -strict %s}
10813 checkbutton $top.g.bold -padx 5 \
10814 -font {{Times New Roman} 12 bold} -text [mc "B"] -indicatoron 0 \
10815 -variable fontparam(weight) -onvalue bold -offvalue normal
10816 checkbutton $top.g.ital -padx 5 \
10817 -font {{Times New Roman} 12 italic} -text [mc "I"] -indicatoron 0 \
10818 -variable fontparam(slant) -onvalue italic -offvalue roman
10819 pack $top.g.size $top.g.bold $top.g.ital -side left
10820 pack $top.g -side top
10821 canvas $top.c -width 150 -height 50 -border 2 -relief sunk \
10823 $top.c create text 100 25 -anchor center -text $which -font sample \
10824 -fill black -tags text
10825 bind $top.c <Configure> [list centertext $top.c]
10826 pack $top.c -side top -fill x
10827 ${NS}::frame $top.buts
10828 ${NS}::button $top.buts.ok -text [mc "OK"] -command fontok -default active
10829 ${NS}::button $top.buts.can -text [mc "Cancel"] -command fontcan -default normal
10830 bind $top <Key-Return> fontok
10831 bind $top <Key-Escape> fontcan
10832 grid $top.buts.ok $top.buts.can
10833 grid columnconfigure $top.buts 0 -weight 1 -uniform a
10834 grid columnconfigure $top.buts 1 -weight 1 -uniform a
10835 pack $top.buts -side bottom -fill x
10836 trace add variable fontparam write chg_fontparam
10839 $top.c itemconf text -text $which
10841 set i [lsearch -exact $fontlist $fontparam(family)]
10843 $top.f.fam selection set $i
10848 proc centertext {w} {
10849 $w coords text [expr {[winfo width $w] / 2}] [expr {[winfo height $w] / 2}]
10853 global fontparam fontpref prefstop
10855 set f $fontparam(font)
10856 set fontpref($f) [list $fontparam(family) $fontparam(size)]
10857 if {$fontparam(weight) eq "bold"} {
10858 lappend fontpref($f) "bold"
10860 if {$fontparam(slant) eq "italic"} {
10861 lappend fontpref($f) "italic"
10863 set w $prefstop.notebook.fonts.$f
10864 $w conf -text $fontparam(family) -font $fontpref($f)
10870 global fonttop fontparam
10872 if {[info exists fonttop]} {
10873 catch {destroy $fonttop}
10874 catch {font delete sample}
10880 if {[package vsatisfies [package provide Tk] 8.6]} {
10881 # In Tk 8.6 we have a native font chooser dialog. Overwrite the above
10882 # function to make use of it.
10883 proc choosefont {font which} {
10884 tk fontchooser configure -title $which -font $font \
10885 -command [list on_choosefont $font $which]
10886 tk fontchooser show
10888 proc on_choosefont {font which newfont} {
10890 puts stderr "$font $newfont"
10891 array set f [font actual $newfont]
10892 set fontparam(which) $which
10893 set fontparam(font) $font
10894 set fontparam(family) $f(-family)
10895 set fontparam(size) $f(-size)
10896 set fontparam(weight) $f(-weight)
10897 set fontparam(slant) $f(-slant)
10902 proc selfontfam {} {
10903 global fonttop fontparam
10905 set i [$fonttop.f.fam curselection]
10907 set fontparam(family) [$fonttop.f.fam get $i]
10911 proc chg_fontparam {v sub op} {
10914 font config sample -$sub $fontparam($sub)
10917 # Create a property sheet tab page
10918 proc create_prefs_page {w} {
10920 set parent [join [lrange [split $w .] 0 end-1] .]
10921 if {[winfo class $parent] eq "TNotebook"} {
10924 ${NS}::labelframe $w
10928 proc prefspage_general {notebook} {
10929 global NS maxwidth maxgraphpct showneartags showlocalchanges
10930 global tabstop limitdiffs autoselect autosellen extdifftool perfile_attrs
10931 global hideremotes want_ttk have_ttk
10933 set page [create_prefs_page $notebook.general]
10935 ${NS}::label $page.ldisp -text [mc "Commit list display options"]
10936 grid $page.ldisp - -sticky w -pady 10
10937 ${NS}::label $page.spacer -text " "
10938 ${NS}::label $page.maxwidthl -text [mc "Maximum graph width (lines)"]
10939 spinbox $page.maxwidth -from 0 -to 100 -width 4 -textvariable maxwidth
10940 grid $page.spacer $page.maxwidthl $page.maxwidth -sticky w
10941 ${NS}::label $page.maxpctl -text [mc "Maximum graph width (% of pane)"]
10942 spinbox $page.maxpct -from 1 -to 100 -width 4 -textvariable maxgraphpct
10943 grid x $page.maxpctl $page.maxpct -sticky w
10944 ${NS}::checkbutton $page.showlocal -text [mc "Show local changes"] \
10945 -variable showlocalchanges
10946 grid x $page.showlocal -sticky w
10947 ${NS}::checkbutton $page.autoselect -text [mc "Auto-select SHA1 (length)"] \
10948 -variable autoselect
10949 spinbox $page.autosellen -from 1 -to 40 -width 4 -textvariable autosellen
10950 grid x $page.autoselect $page.autosellen -sticky w
10951 ${NS}::checkbutton $page.hideremotes -text [mc "Hide remote refs"] \
10952 -variable hideremotes
10953 grid x $page.hideremotes -sticky w
10955 ${NS}::label $page.ddisp -text [mc "Diff display options"]
10956 grid $page.ddisp - -sticky w -pady 10
10957 ${NS}::label $page.tabstopl -text [mc "Tab spacing"]
10958 spinbox $page.tabstop -from 1 -to 20 -width 4 -textvariable tabstop
10959 grid x $page.tabstopl $page.tabstop -sticky w
10960 ${NS}::checkbutton $page.ntag -text [mc "Display nearby tags"] \
10961 -variable showneartags
10962 grid x $page.ntag -sticky w
10963 ${NS}::checkbutton $page.ldiff -text [mc "Limit diffs to listed paths"] \
10964 -variable limitdiffs
10965 grid x $page.ldiff -sticky w
10966 ${NS}::checkbutton $page.lattr -text [mc "Support per-file encodings"] \
10967 -variable perfile_attrs
10968 grid x $page.lattr -sticky w
10970 ${NS}::entry $page.extdifft -textvariable extdifftool
10971 ${NS}::frame $page.extdifff
10972 ${NS}::label $page.extdifff.l -text [mc "External diff tool" ]
10973 ${NS}::button $page.extdifff.b -text [mc "Choose..."] -command choose_extdiff
10974 pack $page.extdifff.l $page.extdifff.b -side left
10975 pack configure $page.extdifff.l -padx 10
10976 grid x $page.extdifff $page.extdifft -sticky ew
10978 ${NS}::label $page.lgen -text [mc "General options"]
10979 grid $page.lgen - -sticky w -pady 10
10980 ${NS}::checkbutton $page.want_ttk -variable want_ttk \
10981 -text [mc "Use themed widgets"]
10983 ${NS}::label $page.ttk_note -text [mc "(change requires restart)"]
10985 ${NS}::label $page.ttk_note -text [mc "(currently unavailable)"]
10987 grid x $page.want_ttk $page.ttk_note -sticky w
10991 proc prefspage_colors {notebook} {
10992 global NS uicolor bgcolor fgcolor ctext diffcolors selectbgcolor markbgcolor
10994 set page [create_prefs_page $notebook.colors]
10996 ${NS}::label $page.cdisp -text [mc "Colors: press to choose"]
10997 grid $page.cdisp - -sticky w -pady 10
10998 label $page.ui -padx 40 -relief sunk -background $uicolor
10999 ${NS}::button $page.uibut -text [mc "Interface"] \
11000 -command [list choosecolor uicolor {} $page.ui [mc "interface"] setui]
11001 grid x $page.uibut $page.ui -sticky w
11002 label $page.bg -padx 40 -relief sunk -background $bgcolor
11003 ${NS}::button $page.bgbut -text [mc "Background"] \
11004 -command [list choosecolor bgcolor {} $page.bg [mc "background"] setbg]
11005 grid x $page.bgbut $page.bg -sticky w
11006 label $page.fg -padx 40 -relief sunk -background $fgcolor
11007 ${NS}::button $page.fgbut -text [mc "Foreground"] \
11008 -command [list choosecolor fgcolor {} $page.fg [mc "foreground"] setfg]
11009 grid x $page.fgbut $page.fg -sticky w
11010 label $page.diffold -padx 40 -relief sunk -background [lindex $diffcolors 0]
11011 ${NS}::button $page.diffoldbut -text [mc "Diff: old lines"] \
11012 -command [list choosecolor diffcolors 0 $page.diffold [mc "diff old lines"] \
11013 [list $ctext tag conf d0 -foreground]]
11014 grid x $page.diffoldbut $page.diffold -sticky w
11015 label $page.diffnew -padx 40 -relief sunk -background [lindex $diffcolors 1]
11016 ${NS}::button $page.diffnewbut -text [mc "Diff: new lines"] \
11017 -command [list choosecolor diffcolors 1 $page.diffnew [mc "diff new lines"] \
11018 [list $ctext tag conf dresult -foreground]]
11019 grid x $page.diffnewbut $page.diffnew -sticky w
11020 label $page.hunksep -padx 40 -relief sunk -background [lindex $diffcolors 2]
11021 ${NS}::button $page.hunksepbut -text [mc "Diff: hunk header"] \
11022 -command [list choosecolor diffcolors 2 $page.hunksep \
11023 [mc "diff hunk header"] \
11024 [list $ctext tag conf hunksep -foreground]]
11025 grid x $page.hunksepbut $page.hunksep -sticky w
11026 label $page.markbgsep -padx 40 -relief sunk -background $markbgcolor
11027 ${NS}::button $page.markbgbut -text [mc "Marked line bg"] \
11028 -command [list choosecolor markbgcolor {} $page.markbgsep \
11029 [mc "marked line background"] \
11030 [list $ctext tag conf omark -background]]
11031 grid x $page.markbgbut $page.markbgsep -sticky w
11032 label $page.selbgsep -padx 40 -relief sunk -background $selectbgcolor
11033 ${NS}::button $page.selbgbut -text [mc "Select bg"] \
11034 -command [list choosecolor selectbgcolor {} $page.selbgsep [mc "background"] setselbg]
11035 grid x $page.selbgbut $page.selbgsep -sticky w
11039 proc prefspage_fonts {notebook} {
11041 set page [create_prefs_page $notebook.fonts]
11042 ${NS}::label $page.cfont -text [mc "Fonts: press to choose"]
11043 grid $page.cfont - -sticky w -pady 10
11044 mkfontdisp mainfont $page [mc "Main font"]
11045 mkfontdisp textfont $page [mc "Diff display font"]
11046 mkfontdisp uifont $page [mc "User interface font"]
11051 global maxwidth maxgraphpct use_ttk NS
11052 global oldprefs prefstop showneartags showlocalchanges
11053 global uicolor bgcolor fgcolor ctext diffcolors selectbgcolor markbgcolor
11054 global tabstop limitdiffs autoselect autosellen extdifftool perfile_attrs
11055 global hideremotes want_ttk have_ttk
11059 if {[winfo exists $top]} {
11063 foreach v {maxwidth maxgraphpct showneartags showlocalchanges \
11064 limitdiffs tabstop perfile_attrs hideremotes want_ttk} {
11065 set oldprefs($v) [set $v]
11068 wm title $top [mc "Gitk preferences"]
11069 make_transient $top .
11071 if {[set use_notebook [expr {$use_ttk && [info command ::ttk::notebook] ne ""}]]} {
11072 set notebook [ttk::notebook $top.notebook]
11074 set notebook [${NS}::frame $top.notebook -borderwidth 0 -relief flat]
11077 lappend pages [prefspage_general $notebook] [mc "General"]
11078 lappend pages [prefspage_colors $notebook] [mc "Colors"]
11079 lappend pages [prefspage_fonts $notebook] [mc "Fonts"]
11081 foreach {page title} $pages {
11082 if {$use_notebook} {
11083 $notebook add $page -text $title
11085 set btn [${NS}::button $notebook.b_[string map {. X} $page] \
11086 -text $title -command [list raise $page]]
11087 $page configure -text $title
11088 grid $btn -row 0 -column [incr col] -sticky w
11089 grid $page -row 1 -column 0 -sticky news -columnspan 100
11093 if {!$use_notebook} {
11094 grid columnconfigure $notebook 0 -weight 1
11095 grid rowconfigure $notebook 1 -weight 1
11096 raise [lindex $pages 0]
11099 grid $notebook -sticky news -padx 2 -pady 2
11100 grid rowconfigure $top 0 -weight 1
11101 grid columnconfigure $top 0 -weight 1
11103 ${NS}::frame $top.buts
11104 ${NS}::button $top.buts.ok -text [mc "OK"] -command prefsok -default active
11105 ${NS}::button $top.buts.can -text [mc "Cancel"] -command prefscan -default normal
11106 bind $top <Key-Return> prefsok
11107 bind $top <Key-Escape> prefscan
11108 grid $top.buts.ok $top.buts.can
11109 grid columnconfigure $top.buts 0 -weight 1 -uniform a
11110 grid columnconfigure $top.buts 1 -weight 1 -uniform a
11111 grid $top.buts - - -pady 10 -sticky ew
11112 grid columnconfigure $top 2 -weight 1
11113 bind $top <Visibility> [list focus $top.buts.ok]
11116 proc choose_extdiff {} {
11119 set prog [tk_getOpenFile -title [mc "External diff tool"] -multiple false]
11121 set extdifftool $prog
11125 proc choosecolor {v vi w x cmd} {
11128 set c [tk_chooseColor -initialcolor [lindex [set $v] $vi] \
11129 -title [mc "Gitk: choose color for %s" $x]]
11130 if {$c eq {}} return
11131 $w conf -background $c
11136 proc setselbg {c} {
11137 global bglist cflist
11138 foreach w $bglist {
11139 $w configure -selectbackground $c
11141 $cflist tag configure highlight \
11142 -background [$cflist cget -selectbackground]
11143 allcanvs itemconf secsel -fill $c
11146 # This sets the background color and the color scheme for the whole UI.
11147 # For some reason, tk_setPalette chooses a nasty dark red for selectColor
11148 # if we don't specify one ourselves, which makes the checkbuttons and
11149 # radiobuttons look bad. This chooses white for selectColor if the
11150 # background color is light, or black if it is dark.
11152 if {[tk windowingsystem] eq "win32"} { return }
11153 set bg [winfo rgb . $c]
11155 if {[lindex $bg 0] + 1.5 * [lindex $bg 1] + 0.5 * [lindex $bg 2] > 100000} {
11158 tk_setPalette background $c selectColor $selc
11164 foreach w $bglist {
11165 $w conf -background $c
11172 foreach w $fglist {
11173 $w conf -foreground $c
11175 allcanvs itemconf text -fill $c
11176 $canv itemconf circle -outline $c
11177 $canv itemconf markid -outline $c
11181 global oldprefs prefstop
11183 foreach v {maxwidth maxgraphpct showneartags showlocalchanges \
11184 limitdiffs tabstop perfile_attrs hideremotes want_ttk} {
11186 set $v $oldprefs($v)
11188 catch {destroy $prefstop}
11194 global maxwidth maxgraphpct
11195 global oldprefs prefstop showneartags showlocalchanges
11196 global fontpref mainfont textfont uifont
11197 global limitdiffs treediffs perfile_attrs
11200 catch {destroy $prefstop}
11204 if {$mainfont ne $fontpref(mainfont)} {
11205 set mainfont $fontpref(mainfont)
11206 parsefont mainfont $mainfont
11207 eval font configure mainfont [fontflags mainfont]
11208 eval font configure mainfontbold [fontflags mainfont 1]
11212 if {$textfont ne $fontpref(textfont)} {
11213 set textfont $fontpref(textfont)
11214 parsefont textfont $textfont
11215 eval font configure textfont [fontflags textfont]
11216 eval font configure textfontbold [fontflags textfont 1]
11218 if {$uifont ne $fontpref(uifont)} {
11219 set uifont $fontpref(uifont)
11220 parsefont uifont $uifont
11221 eval font configure uifont [fontflags uifont]
11224 if {$showlocalchanges != $oldprefs(showlocalchanges)} {
11225 if {$showlocalchanges} {
11231 if {$limitdiffs != $oldprefs(limitdiffs) ||
11232 ($perfile_attrs && !$oldprefs(perfile_attrs))} {
11233 # treediffs elements are limited by path;
11234 # won't have encodings cached if perfile_attrs was just turned on
11235 catch {unset treediffs}
11237 if {$fontchanged || $maxwidth != $oldprefs(maxwidth)
11238 || $maxgraphpct != $oldprefs(maxgraphpct)} {
11240 } elseif {$showneartags != $oldprefs(showneartags) ||
11241 $limitdiffs != $oldprefs(limitdiffs)} {
11244 if {$hideremotes != $oldprefs(hideremotes)} {
11249 proc formatdate {d} {
11250 global datetimeformat
11252 set d [clock format [lindex $d 0] -format $datetimeformat]
11257 # This list of encoding names and aliases is distilled from
11258 # http://www.iana.org/assignments/character-sets.
11259 # Not all of them are supported by Tcl.
11260 set encoding_aliases {
11261 { ANSI_X3.4-1968 iso-ir-6 ANSI_X3.4-1986 ISO_646.irv:1991 ASCII
11262 ISO646-US US-ASCII us IBM367 cp367 csASCII }
11263 { ISO-10646-UTF-1 csISO10646UTF1 }
11264 { ISO_646.basic:1983 ref csISO646basic1983 }
11265 { INVARIANT csINVARIANT }
11266 { ISO_646.irv:1983 iso-ir-2 irv csISO2IntlRefVersion }
11267 { BS_4730 iso-ir-4 ISO646-GB gb uk csISO4UnitedKingdom }
11268 { NATS-SEFI iso-ir-8-1 csNATSSEFI }
11269 { NATS-SEFI-ADD iso-ir-8-2 csNATSSEFIADD }
11270 { NATS-DANO iso-ir-9-1 csNATSDANO }
11271 { NATS-DANO-ADD iso-ir-9-2 csNATSDANOADD }
11272 { SEN_850200_B iso-ir-10 FI ISO646-FI ISO646-SE se csISO10Swedish }
11273 { SEN_850200_C iso-ir-11 ISO646-SE2 se2 csISO11SwedishForNames }
11274 { KS_C_5601-1987 iso-ir-149 KS_C_5601-1989 KSC_5601 korean csKSC56011987 }
11275 { ISO-2022-KR csISO2022KR }
11277 { ISO-2022-JP csISO2022JP }
11278 { ISO-2022-JP-2 csISO2022JP2 }
11279 { JIS_C6220-1969-jp JIS_C6220-1969 iso-ir-13 katakana x0201-7
11280 csISO13JISC6220jp }
11281 { JIS_C6220-1969-ro iso-ir-14 jp ISO646-JP csISO14JISC6220ro }
11282 { IT iso-ir-15 ISO646-IT csISO15Italian }
11283 { PT iso-ir-16 ISO646-PT csISO16Portuguese }
11284 { ES iso-ir-17 ISO646-ES csISO17Spanish }
11285 { greek7-old iso-ir-18 csISO18Greek7Old }
11286 { latin-greek iso-ir-19 csISO19LatinGreek }
11287 { DIN_66003 iso-ir-21 de ISO646-DE csISO21German }
11288 { NF_Z_62-010_(1973) iso-ir-25 ISO646-FR1 csISO25French }
11289 { Latin-greek-1 iso-ir-27 csISO27LatinGreek1 }
11290 { ISO_5427 iso-ir-37 csISO5427Cyrillic }
11291 { JIS_C6226-1978 iso-ir-42 csISO42JISC62261978 }
11292 { BS_viewdata iso-ir-47 csISO47BSViewdata }
11293 { INIS iso-ir-49 csISO49INIS }
11294 { INIS-8 iso-ir-50 csISO50INIS8 }
11295 { INIS-cyrillic iso-ir-51 csISO51INISCyrillic }
11296 { ISO_5427:1981 iso-ir-54 ISO5427Cyrillic1981 }
11297 { ISO_5428:1980 iso-ir-55 csISO5428Greek }
11298 { GB_1988-80 iso-ir-57 cn ISO646-CN csISO57GB1988 }
11299 { GB_2312-80 iso-ir-58 chinese csISO58GB231280 }
11300 { NS_4551-1 iso-ir-60 ISO646-NO no csISO60DanishNorwegian
11301 csISO60Norwegian1 }
11302 { NS_4551-2 ISO646-NO2 iso-ir-61 no2 csISO61Norwegian2 }
11303 { NF_Z_62-010 iso-ir-69 ISO646-FR fr csISO69French }
11304 { videotex-suppl iso-ir-70 csISO70VideotexSupp1 }
11305 { PT2 iso-ir-84 ISO646-PT2 csISO84Portuguese2 }
11306 { ES2 iso-ir-85 ISO646-ES2 csISO85Spanish2 }
11307 { MSZ_7795.3 iso-ir-86 ISO646-HU hu csISO86Hungarian }
11308 { JIS_C6226-1983 iso-ir-87 x0208 JIS_X0208-1983 csISO87JISX0208 }
11309 { greek7 iso-ir-88 csISO88Greek7 }
11310 { ASMO_449 ISO_9036 arabic7 iso-ir-89 csISO89ASMO449 }
11311 { iso-ir-90 csISO90 }
11312 { JIS_C6229-1984-a iso-ir-91 jp-ocr-a csISO91JISC62291984a }
11313 { JIS_C6229-1984-b iso-ir-92 ISO646-JP-OCR-B jp-ocr-b
11314 csISO92JISC62991984b }
11315 { JIS_C6229-1984-b-add iso-ir-93 jp-ocr-b-add csISO93JIS62291984badd }
11316 { JIS_C6229-1984-hand iso-ir-94 jp-ocr-hand csISO94JIS62291984hand }
11317 { JIS_C6229-1984-hand-add iso-ir-95 jp-ocr-hand-add
11318 csISO95JIS62291984handadd }
11319 { JIS_C6229-1984-kana iso-ir-96 csISO96JISC62291984kana }
11320 { ISO_2033-1983 iso-ir-98 e13b csISO2033 }
11321 { ANSI_X3.110-1983 iso-ir-99 CSA_T500-1983 NAPLPS csISO99NAPLPS }
11322 { ISO_8859-1:1987 iso-ir-100 ISO_8859-1 ISO-8859-1 latin1 l1 IBM819
11323 CP819 csISOLatin1 }
11324 { ISO_8859-2:1987 iso-ir-101 ISO_8859-2 ISO-8859-2 latin2 l2 csISOLatin2 }
11325 { T.61-7bit iso-ir-102 csISO102T617bit }
11326 { T.61-8bit T.61 iso-ir-103 csISO103T618bit }
11327 { ISO_8859-3:1988 iso-ir-109 ISO_8859-3 ISO-8859-3 latin3 l3 csISOLatin3 }
11328 { ISO_8859-4:1988 iso-ir-110 ISO_8859-4 ISO-8859-4 latin4 l4 csISOLatin4 }
11329 { ECMA-cyrillic iso-ir-111 KOI8-E csISO111ECMACyrillic }
11330 { CSA_Z243.4-1985-1 iso-ir-121 ISO646-CA csa7-1 ca csISO121Canadian1 }
11331 { CSA_Z243.4-1985-2 iso-ir-122 ISO646-CA2 csa7-2 csISO122Canadian2 }
11332 { CSA_Z243.4-1985-gr iso-ir-123 csISO123CSAZ24341985gr }
11333 { ISO_8859-6:1987 iso-ir-127 ISO_8859-6 ISO-8859-6 ECMA-114 ASMO-708
11334 arabic csISOLatinArabic }
11335 { ISO_8859-6-E csISO88596E ISO-8859-6-E }
11336 { ISO_8859-6-I csISO88596I ISO-8859-6-I }
11337 { ISO_8859-7:1987 iso-ir-126 ISO_8859-7 ISO-8859-7 ELOT_928 ECMA-118
11338 greek greek8 csISOLatinGreek }
11339 { T.101-G2 iso-ir-128 csISO128T101G2 }
11340 { ISO_8859-8:1988 iso-ir-138 ISO_8859-8 ISO-8859-8 hebrew
11342 { ISO_8859-8-E csISO88598E ISO-8859-8-E }
11343 { ISO_8859-8-I csISO88598I ISO-8859-8-I }
11344 { CSN_369103 iso-ir-139 csISO139CSN369103 }
11345 { JUS_I.B1.002 iso-ir-141 ISO646-YU js yu csISO141JUSIB1002 }
11346 { ISO_6937-2-add iso-ir-142 csISOTextComm }
11347 { IEC_P27-1 iso-ir-143 csISO143IECP271 }
11348 { ISO_8859-5:1988 iso-ir-144 ISO_8859-5 ISO-8859-5 cyrillic
11349 csISOLatinCyrillic }
11350 { JUS_I.B1.003-serb iso-ir-146 serbian csISO146Serbian }
11351 { JUS_I.B1.003-mac macedonian iso-ir-147 csISO147Macedonian }
11352 { ISO_8859-9:1989 iso-ir-148 ISO_8859-9 ISO-8859-9 latin5 l5 csISOLatin5 }
11353 { greek-ccitt iso-ir-150 csISO150 csISO150GreekCCITT }
11354 { NC_NC00-10:81 cuba iso-ir-151 ISO646-CU csISO151Cuba }
11355 { ISO_6937-2-25 iso-ir-152 csISO6937Add }
11356 { GOST_19768-74 ST_SEV_358-88 iso-ir-153 csISO153GOST1976874 }
11357 { ISO_8859-supp iso-ir-154 latin1-2-5 csISO8859Supp }
11358 { ISO_10367-box iso-ir-155 csISO10367Box }
11359 { ISO-8859-10 iso-ir-157 l6 ISO_8859-10:1992 csISOLatin6 latin6 }
11360 { latin-lap lap iso-ir-158 csISO158Lap }
11361 { JIS_X0212-1990 x0212 iso-ir-159 csISO159JISX02121990 }
11362 { DS_2089 DS2089 ISO646-DK dk csISO646Danish }
11365 { JIS_X0201 X0201 csHalfWidthKatakana }
11366 { KSC5636 ISO646-KR csKSC5636 }
11367 { ISO-10646-UCS-2 csUnicode }
11368 { ISO-10646-UCS-4 csUCS4 }
11369 { DEC-MCS dec csDECMCS }
11370 { hp-roman8 roman8 r8 csHPRoman8 }
11371 { macintosh mac csMacintosh }
11372 { IBM037 cp037 ebcdic-cp-us ebcdic-cp-ca ebcdic-cp-wt ebcdic-cp-nl
11374 { IBM038 EBCDIC-INT cp038 csIBM038 }
11375 { IBM273 CP273 csIBM273 }
11376 { IBM274 EBCDIC-BE CP274 csIBM274 }
11377 { IBM275 EBCDIC-BR cp275 csIBM275 }
11378 { IBM277 EBCDIC-CP-DK EBCDIC-CP-NO csIBM277 }
11379 { IBM278 CP278 ebcdic-cp-fi ebcdic-cp-se csIBM278 }
11380 { IBM280 CP280 ebcdic-cp-it csIBM280 }
11381 { IBM281 EBCDIC-JP-E cp281 csIBM281 }
11382 { IBM284 CP284 ebcdic-cp-es csIBM284 }
11383 { IBM285 CP285 ebcdic-cp-gb csIBM285 }
11384 { IBM290 cp290 EBCDIC-JP-kana csIBM290 }
11385 { IBM297 cp297 ebcdic-cp-fr csIBM297 }
11386 { IBM420 cp420 ebcdic-cp-ar1 csIBM420 }
11387 { IBM423 cp423 ebcdic-cp-gr csIBM423 }
11388 { IBM424 cp424 ebcdic-cp-he csIBM424 }
11389 { IBM437 cp437 437 csPC8CodePage437 }
11390 { IBM500 CP500 ebcdic-cp-be ebcdic-cp-ch csIBM500 }
11391 { IBM775 cp775 csPC775Baltic }
11392 { IBM850 cp850 850 csPC850Multilingual }
11393 { IBM851 cp851 851 csIBM851 }
11394 { IBM852 cp852 852 csPCp852 }
11395 { IBM855 cp855 855 csIBM855 }
11396 { IBM857 cp857 857 csIBM857 }
11397 { IBM860 cp860 860 csIBM860 }
11398 { IBM861 cp861 861 cp-is csIBM861 }
11399 { IBM862 cp862 862 csPC862LatinHebrew }
11400 { IBM863 cp863 863 csIBM863 }
11401 { IBM864 cp864 csIBM864 }
11402 { IBM865 cp865 865 csIBM865 }
11403 { IBM866 cp866 866 csIBM866 }
11404 { IBM868 CP868 cp-ar csIBM868 }
11405 { IBM869 cp869 869 cp-gr csIBM869 }
11406 { IBM870 CP870 ebcdic-cp-roece ebcdic-cp-yu csIBM870 }
11407 { IBM871 CP871 ebcdic-cp-is csIBM871 }
11408 { IBM880 cp880 EBCDIC-Cyrillic csIBM880 }
11409 { IBM891 cp891 csIBM891 }
11410 { IBM903 cp903 csIBM903 }
11411 { IBM904 cp904 904 csIBBM904 }
11412 { IBM905 CP905 ebcdic-cp-tr csIBM905 }
11413 { IBM918 CP918 ebcdic-cp-ar2 csIBM918 }
11414 { IBM1026 CP1026 csIBM1026 }
11415 { EBCDIC-AT-DE csIBMEBCDICATDE }
11416 { EBCDIC-AT-DE-A csEBCDICATDEA }
11417 { EBCDIC-CA-FR csEBCDICCAFR }
11418 { EBCDIC-DK-NO csEBCDICDKNO }
11419 { EBCDIC-DK-NO-A csEBCDICDKNOA }
11420 { EBCDIC-FI-SE csEBCDICFISE }
11421 { EBCDIC-FI-SE-A csEBCDICFISEA }
11422 { EBCDIC-FR csEBCDICFR }
11423 { EBCDIC-IT csEBCDICIT }
11424 { EBCDIC-PT csEBCDICPT }
11425 { EBCDIC-ES csEBCDICES }
11426 { EBCDIC-ES-A csEBCDICESA }
11427 { EBCDIC-ES-S csEBCDICESS }
11428 { EBCDIC-UK csEBCDICUK }
11429 { EBCDIC-US csEBCDICUS }
11430 { UNKNOWN-8BIT csUnknown8BiT }
11431 { MNEMONIC csMnemonic }
11433 { VISCII csVISCII }
11436 { IBM00858 CCSID00858 CP00858 PC-Multilingual-850+euro }
11437 { IBM00924 CCSID00924 CP00924 ebcdic-Latin9--euro }
11438 { IBM01140 CCSID01140 CP01140 ebcdic-us-37+euro }
11439 { IBM01141 CCSID01141 CP01141 ebcdic-de-273+euro }
11440 { IBM01142 CCSID01142 CP01142 ebcdic-dk-277+euro ebcdic-no-277+euro }
11441 { IBM01143 CCSID01143 CP01143 ebcdic-fi-278+euro ebcdic-se-278+euro }
11442 { IBM01144 CCSID01144 CP01144 ebcdic-it-280+euro }
11443 { IBM01145 CCSID01145 CP01145 ebcdic-es-284+euro }
11444 { IBM01146 CCSID01146 CP01146 ebcdic-gb-285+euro }
11445 { IBM01147 CCSID01147 CP01147 ebcdic-fr-297+euro }
11446 { IBM01148 CCSID01148 CP01148 ebcdic-international-500+euro }
11447 { IBM01149 CCSID01149 CP01149 ebcdic-is-871+euro }
11448 { IBM1047 IBM-1047 }
11449 { PTCP154 csPTCP154 PT154 CP154 Cyrillic-Asian }
11450 { Amiga-1251 Ami1251 Amiga1251 Ami-1251 }
11451 { UNICODE-1-1 csUnicode11 }
11452 { CESU-8 csCESU-8 }
11453 { BOCU-1 csBOCU-1 }
11454 { UNICODE-1-1-UTF-7 csUnicode11UTF7 }
11455 { ISO-8859-14 iso-ir-199 ISO_8859-14:1998 ISO_8859-14 latin8 iso-celtic
11457 { ISO-8859-15 ISO_8859-15 Latin-9 }
11458 { ISO-8859-16 iso-ir-226 ISO_8859-16:2001 ISO_8859-16 latin10 l10 }
11459 { GBK CP936 MS936 windows-936 }
11460 { JIS_Encoding csJISEncoding }
11461 { Shift_JIS MS_Kanji csShiftJIS ShiftJIS Shift-JIS }
11462 { Extended_UNIX_Code_Packed_Format_for_Japanese csEUCPkdFmtJapanese
11464 { Extended_UNIX_Code_Fixed_Width_for_Japanese csEUCFixWidJapanese }
11465 { ISO-10646-UCS-Basic csUnicodeASCII }
11466 { ISO-10646-Unicode-Latin1 csUnicodeLatin1 ISO-10646 }
11467 { ISO-Unicode-IBM-1261 csUnicodeIBM1261 }
11468 { ISO-Unicode-IBM-1268 csUnicodeIBM1268 }
11469 { ISO-Unicode-IBM-1276 csUnicodeIBM1276 }
11470 { ISO-Unicode-IBM-1264 csUnicodeIBM1264 }
11471 { ISO-Unicode-IBM-1265 csUnicodeIBM1265 }
11472 { ISO-8859-1-Windows-3.0-Latin-1 csWindows30Latin1 }
11473 { ISO-8859-1-Windows-3.1-Latin-1 csWindows31Latin1 }
11474 { ISO-8859-2-Windows-Latin-2 csWindows31Latin2 }
11475 { ISO-8859-9-Windows-Latin-5 csWindows31Latin5 }
11476 { Adobe-Standard-Encoding csAdobeStandardEncoding }
11477 { Ventura-US csVenturaUS }
11478 { Ventura-International csVenturaInternational }
11479 { PC8-Danish-Norwegian csPC8DanishNorwegian }
11480 { PC8-Turkish csPC8Turkish }
11481 { IBM-Symbols csIBMSymbols }
11482 { IBM-Thai csIBMThai }
11483 { HP-Legal csHPLegal }
11484 { HP-Pi-font csHPPiFont }
11485 { HP-Math8 csHPMath8 }
11486 { Adobe-Symbol-Encoding csHPPSMath }
11487 { HP-DeskTop csHPDesktop }
11488 { Ventura-Math csVenturaMath }
11489 { Microsoft-Publishing csMicrosoftPublishing }
11490 { Windows-31J csWindows31J }
11491 { GB2312 csGB2312 }
11495 proc tcl_encoding {enc} {
11496 global encoding_aliases tcl_encoding_cache
11497 if {[info exists tcl_encoding_cache($enc)]} {
11498 return $tcl_encoding_cache($enc)
11500 set names [encoding names]
11501 set lcnames [string tolower $names]
11502 set enc [string tolower $enc]
11503 set i [lsearch -exact $lcnames $enc]
11505 # look for "isonnn" instead of "iso-nnn" or "iso_nnn"
11506 if {[regsub {^(iso|cp|ibm|jis)[-_]} $enc {\1} encx]} {
11507 set i [lsearch -exact $lcnames $encx]
11511 foreach l $encoding_aliases {
11512 set ll [string tolower $l]
11513 if {[lsearch -exact $ll $enc] < 0} continue
11514 # look through the aliases for one that tcl knows about
11516 set i [lsearch -exact $lcnames $e]
11518 if {[regsub {^(iso|cp|ibm|jis)[-_]} $e {\1} ex]} {
11519 set i [lsearch -exact $lcnames $ex]
11529 set tclenc [lindex $names $i]
11531 set tcl_encoding_cache($enc) $tclenc
11535 proc gitattr {path attr default} {
11536 global path_attr_cache
11537 if {[info exists path_attr_cache($attr,$path)]} {
11538 set r $path_attr_cache($attr,$path)
11540 set r "unspecified"
11541 if {![catch {set line [exec git check-attr $attr -- $path]}]} {
11542 regexp "(.*): $attr: (.*)" $line m f r
11544 set path_attr_cache($attr,$path) $r
11546 if {$r eq "unspecified"} {
11552 proc cache_gitattr {attr pathlist} {
11553 global path_attr_cache
11555 foreach path $pathlist {
11556 if {![info exists path_attr_cache($attr,$path)]} {
11557 lappend newlist $path
11561 if {[tk windowingsystem] == "win32"} {
11562 # windows has a 32k limit on the arguments to a command...
11565 while {$newlist ne {}} {
11566 set head [lrange $newlist 0 [expr {$lim - 1}]]
11567 set newlist [lrange $newlist $lim end]
11568 if {![catch {set rlist [eval exec git check-attr $attr -- $head]}]} {
11569 foreach row [split $rlist "\n"] {
11570 if {[regexp "(.*): $attr: (.*)" $row m path value]} {
11571 if {[string index $path 0] eq "\""} {
11572 set path [encoding convertfrom [lindex $path 0]]
11574 set path_attr_cache($attr,$path) $value
11581 proc get_path_encoding {path} {
11582 global gui_encoding perfile_attrs
11583 set tcl_enc $gui_encoding
11584 if {$path ne {} && $perfile_attrs} {
11585 set enc2 [tcl_encoding [gitattr $path encoding $tcl_enc]]
11593 # First check that Tcl/Tk is recent enough
11594 if {[catch {package require Tk 8.4} err]} {
11595 show_error {} . "Sorry, gitk cannot run with this version of Tcl/Tk.\n\
11596 Gitk requires at least Tcl/Tk 8.4." list
11600 # Unset GIT_TRACE var if set
11601 if { [info exists ::env(GIT_TRACE)] } {
11602 unset ::env(GIT_TRACE)
11606 set wrcomcmd "git diff-tree --stdin -p --pretty"
11610 set gitencoding [exec git config --get i18n.commitencoding]
11613 set gitencoding [exec git config --get i18n.logoutputencoding]
11615 if {$gitencoding == ""} {
11616 set gitencoding "utf-8"
11618 set tclencoding [tcl_encoding $gitencoding]
11619 if {$tclencoding == {}} {
11620 puts stderr "Warning: encoding $gitencoding is not supported by Tcl/Tk"
11623 set gui_encoding [encoding system]
11625 set enc [exec git config --get gui.encoding]
11627 set tclenc [tcl_encoding $enc]
11628 if {$tclenc ne {}} {
11629 set gui_encoding $tclenc
11631 puts stderr "Warning: encoding $enc is not supported by Tcl/Tk"
11636 set log_showroot true
11638 set log_showroot [exec git config --bool --get log.showroot]
11641 if {[tk windowingsystem] eq "aqua"} {
11642 set mainfont {{Lucida Grande} 9}
11643 set textfont {Monaco 9}
11644 set uifont {{Lucida Grande} 9 bold}
11645 } elseif {![catch {::tk::pkgconfig get fontsystem} xft] && $xft eq "xft"} {
11647 set mainfont {sans 9}
11648 set textfont {monospace 9}
11649 set uifont {sans 9 bold}
11651 set mainfont {Helvetica 9}
11652 set textfont {Courier 9}
11653 set uifont {Helvetica 9 bold}
11656 set findmergefiles 0
11664 set cmitmode "patch"
11665 set wrapcomment "none"
11670 set showlocalchanges 1
11672 set datetimeformat "%Y-%m-%d %H:%M:%S"
11675 set perfile_attrs 0
11678 if {[tk windowingsystem] eq "aqua"} {
11679 set extdifftool "opendiff"
11681 set extdifftool "meld"
11684 set colors {green red blue magenta darkgrey brown orange}
11685 if {[tk windowingsystem] eq "win32"} {
11686 set uicolor SystemButtonFace
11687 set bgcolor SystemWindow
11688 set fgcolor SystemButtonText
11689 set selectbgcolor SystemHighlight
11694 set selectbgcolor gray85
11696 set diffcolors {red "#00a000" blue}
11700 set markbgcolor "#e0e0ff"
11702 set circlecolors {white blue gray blue blue}
11704 # button for popping up context menus
11705 if {[tk windowingsystem] eq "aqua"} {
11706 set ctxbut <Button-2>
11708 set ctxbut <Button-3>
11711 ## For msgcat loading, first locate the installation location.
11712 if { [info exists ::env(GITK_MSGSDIR)] } {
11713 ## Msgsdir was manually set in the environment.
11714 set gitk_msgsdir $::env(GITK_MSGSDIR)
11716 ## Let's guess the prefix from argv0.
11717 set gitk_prefix [file dirname [file dirname [file normalize $argv0]]]
11718 set gitk_libdir [file join $gitk_prefix share gitk lib]
11719 set gitk_msgsdir [file join $gitk_libdir msgs]
11723 ## Internationalization (i18n) through msgcat and gettext. See
11724 ## http://www.gnu.org/software/gettext/manual/html_node/Tcl.html
11725 package require msgcat
11726 namespace import ::msgcat::mc
11727 ## And eventually load the actual message catalog
11728 ::msgcat::mcload $gitk_msgsdir
11730 catch {source ~/.gitk}
11732 parsefont mainfont $mainfont
11733 eval font create mainfont [fontflags mainfont]
11734 eval font create mainfontbold [fontflags mainfont 1]
11736 parsefont textfont $textfont
11737 eval font create textfont [fontflags textfont]
11738 eval font create textfontbold [fontflags textfont 1]
11740 parsefont uifont $uifont
11741 eval font create uifont [fontflags uifont]
11747 # check that we can find a .git directory somewhere...
11748 if {[catch {set gitdir [exec git rev-parse --git-dir]}]} {
11749 show_error {} . [mc "Cannot find a git repository here."]
11754 set selectheadid {}
11757 set cmdline_files {}
11759 set revtreeargscmd {}
11760 foreach arg $argv {
11761 switch -glob -- $arg {
11764 set cmdline_files [lrange $argv [expr {$i + 1}] end]
11767 "--select-commit=*" {
11768 set selecthead [string range $arg 16 end]
11771 set revtreeargscmd [string range $arg 10 end]
11774 lappend revtreeargs $arg
11780 if {$selecthead eq "HEAD"} {
11784 if {$i >= [llength $argv] && $revtreeargs ne {}} {
11785 # no -- on command line, but some arguments (other than --argscmd)
11787 set f [eval exec git rev-parse --no-revs --no-flags $revtreeargs]
11788 set cmdline_files [split $f "\n"]
11789 set n [llength $cmdline_files]
11790 set revtreeargs [lrange $revtreeargs 0 end-$n]
11791 # Unfortunately git rev-parse doesn't produce an error when
11792 # something is both a revision and a filename. To be consistent
11793 # with git log and git rev-list, check revtreeargs for filenames.
11794 foreach arg $revtreeargs {
11795 if {[file exists $arg]} {
11796 show_error {} . [mc "Ambiguous argument '%s': both revision\
11797 and filename" $arg]
11802 # unfortunately we get both stdout and stderr in $err,
11803 # so look for "fatal:".
11804 set i [string first "fatal:" $err]
11806 set err [string range $err [expr {$i + 6}] end]
11808 show_error {} . "[mc "Bad arguments to gitk:"]\n$err"
11813 set nullid "0000000000000000000000000000000000000000"
11814 set nullid2 "0000000000000000000000000000000000000001"
11815 set nullfile "/dev/null"
11817 set have_tk85 [expr {[package vcompare $tk_version "8.5"] >= 0}]
11818 if {![info exists have_ttk]} {
11819 set have_ttk [llength [info commands ::ttk::style]]
11821 set use_ttk [expr {$have_ttk && $want_ttk}]
11822 set NS [expr {$use_ttk ? "ttk" : ""}]
11824 regexp {^git version ([\d.]*\d)} [exec git version] _ git_version
11827 if {[package vcompare $git_version "1.6.6.2"] >= 0} {
11828 set show_notes "--show-notes"
11838 set highlight_paths {}
11840 set searchdirn -forwards
11843 set diffelide {0 0}
11844 set markingmatches 0
11845 set linkentercount 0
11846 set need_redisplay 0
11853 set selectedhlview [mc "None"]
11854 set highlight_related [mc "None"]
11855 set highlight_files {}
11856 set viewfiles(0) {}
11859 set viewargscmd(0) {}
11861 set selectedline {}
11869 set hasworktree [hasworktree]
11871 if {[expr {[exec git rev-parse --is-inside-work-tree] == "true"}]} {
11872 set cdup [exec git rev-parse --show-cdup]
11874 set worktree [exec git rev-parse --show-toplevel]
11878 image create photo gitlogo -width 16 -height 16
11880 image create photo gitlogominus -width 4 -height 2
11881 gitlogominus put #C00000 -to 0 0 4 2
11882 gitlogo copy gitlogominus -to 1 5
11883 gitlogo copy gitlogominus -to 6 5
11884 gitlogo copy gitlogominus -to 11 5
11885 image delete gitlogominus
11887 image create photo gitlogoplus -width 4 -height 4
11888 gitlogoplus put #008000 -to 1 0 3 4
11889 gitlogoplus put #008000 -to 0 1 4 3
11890 gitlogo copy gitlogoplus -to 1 9
11891 gitlogo copy gitlogoplus -to 6 9
11892 gitlogo copy gitlogoplus -to 11 9
11893 image delete gitlogoplus
11895 image create photo gitlogo32 -width 32 -height 32
11896 gitlogo32 copy gitlogo -zoom 2 2
11898 wm iconphoto . -default gitlogo gitlogo32
11900 # wait for the window to become visible
11901 tkwait visibility .
11902 wm title . "$appname: [reponame]"
11906 if {$cmdline_files ne {} || $revtreeargs ne {} || $revtreeargscmd ne {}} {
11907 # create a view for the files/dirs specified on the command line
11911 set viewname(1) [mc "Command line"]
11912 set viewfiles(1) $cmdline_files
11913 set viewargs(1) $revtreeargs
11914 set viewargscmd(1) $revtreeargscmd
11918 .bar.view entryconf [mca "Edit view..."] -state normal
11919 .bar.view entryconf [mca "Delete view"] -state normal
11922 if {[info exists permviews]} {
11923 foreach v $permviews {
11926 set viewname($n) [lindex $v 0]
11927 set viewfiles($n) [lindex $v 1]
11928 set viewargs($n) [lindex $v 2]
11929 set viewargscmd($n) [lindex $v 3]
11935 if {[tk windowingsystem] eq "win32"} {
11943 # indent-tabs-mode: t