2 # Tcl ignores the next line -*- tcl -*- \
5 # Copyright (C) 2005-2006 Paul Mackerras. All rights reserved.
6 # This program is free software; it may be used, copied, modified
7 # and distributed under the terms of the GNU General Public Licence,
8 # either version 2, or (at your option) any later version.
12 if {[info exists env
(GIT_DIR
)]} {
15 return [exec git rev-parse
--git-dir]
19 # A simple scheduler for compute-intensive stuff.
20 # The aim is to make sure that event handlers for GUI actions can
21 # run at least every 50-100 ms. Unfortunately fileevent handlers are
22 # run before X event handlers, so reading from a fast source can
23 # make the GUI completely unresponsive.
28 if {[info exists isonrunq
($script)]} return
32 lappend runq
[list
{} $script]
33 set isonrunq
($script) 1
36 proc filerun
{fd
script} {
37 fileevent
$fd readable
[list filereadable
$fd $script]
40 proc filereadable
{fd
script} {
43 fileevent
$fd readable
{}
47 lappend runq
[list
$fd $script]
53 set tstart
[clock clicks
-milliseconds]
56 set fd
[lindex
$runq 0 0]
57 set script [lindex
$runq 0 1]
58 set repeat
[eval $script]
59 set t1
[clock clicks
-milliseconds]
60 set t
[expr {$t1 - $t0}]
61 set runq
[lrange
$runq 1 end
]
62 if {$repeat ne
{} && $repeat} {
63 if {$fd eq
{} ||
$repeat == 2} {
64 # script returns 1 if it wants to be readded
65 # file readers return 2 if they could do more straight away
66 lappend runq
[list
$fd $script]
68 fileevent
$fd readable
[list filereadable
$fd $script]
70 } elseif
{$fd eq
{}} {
71 unset isonrunq
($script)
74 if {$t1 - $tstart >= 80} break
81 # Start off a git rev-list process and arrange to read its output
82 proc start_rev_list
{view
} {
84 global commfd leftover tclencoding datemode
85 global viewargs viewfiles commitidx
86 global lookingforhead showlocalchanges
88 set startmsecs
[clock clicks
-milliseconds]
89 set commitidx
($view) 0
90 set order
"--topo-order"
92 set order
"--date-order"
95 set fd
[open
[concat | git log
--no-color -z --pretty=raw
$order --parents \
96 --boundary $viewargs($view) "--" $viewfiles($view)] r
]
98 error_popup
"Error executing git rev-list: $err"
101 set commfd
($view) $fd
102 set leftover
($view) {}
103 set lookingforhead
$showlocalchanges
104 fconfigure
$fd -blocking 0 -translation lf
-eofchar {}
105 if {$tclencoding != {}} {
106 fconfigure
$fd -encoding $tclencoding
108 filerun
$fd [list getcommitlines
$fd $view]
112 proc stop_rev_list
{} {
113 global commfd curview
115 if {![info exists commfd
($curview)]} return
116 set fd
$commfd($curview)
122 unset commfd
($curview)
126 global phase canv mainfont curview
130 start_rev_list
$curview
131 show_status
"Reading commits..."
134 proc getcommitlines
{fd view
} {
136 global leftover commfd
137 global displayorder commitidx commitrow commitdata
138 global parentlist children curview hlview
139 global vparentlist vdisporder vcmitlisted
141 set stuff
[read $fd 500000]
142 # git log doesn't terminate the last commit with a null...
143 if {$stuff == {} && $leftover($view) ne
{} && [eof
$fd]} {
153 # set it blocking so we wait for the process to terminate
154 fconfigure
$fd -blocking 1
155 if {[catch
{close
$fd} err
]} {
157 if {$view != $curview} {
158 set fv
" for the \"$viewname($view)\" view"
160 if {[string range
$err 0 4] == "usage"} {
161 set err
"Gitk: error reading commits$fv:\
162 bad arguments to git rev-list."
163 if {$viewname($view) eq
"Command line"} {
165 " (Note: arguments to gitk are passed to git rev-list\
166 to allow selection of commits to be displayed.)"
169 set err
"Error reading commits$fv: $err"
173 if {$view == $curview} {
174 run chewcommits
$view
181 set i
[string first
"\0" $stuff $start]
183 append leftover
($view) [string range
$stuff $start end
]
187 set cmit
$leftover($view)
188 append cmit
[string range
$stuff 0 [expr {$i - 1}]]
189 set leftover
($view) {}
191 set cmit
[string range
$stuff $start [expr {$i - 1}]]
193 set start
[expr {$i + 1}]
194 set j
[string first
"\n" $cmit]
197 if {$j >= 0 && [string match
"commit *" $cmit]} {
198 set ids
[string range
$cmit 7 [expr {$j - 1}]]
199 if {[string match
{[-<>]*} $ids]} {
200 switch
-- [string index
$ids 0] {
205 set ids
[string range
$ids 1 end
]
209 if {[string length
$id] != 40} {
217 if {[string length
$shortcmit] > 80} {
218 set shortcmit
"[string range $shortcmit 0 80]..."
220 error_popup
"Can't parse git log output: {$shortcmit}"
223 set id
[lindex
$ids 0]
225 set olds
[lrange
$ids 1 end
]
228 if {$i == 0 ||
[lsearch
-exact $olds $p] >= $i} {
229 lappend children
($view,$p) $id
236 if {![info exists children
($view,$id)]} {
237 set children
($view,$id) {}
239 set commitdata
($id) [string range
$cmit [expr {$j + 1}] end
]
240 set commitrow
($view,$id) $commitidx($view)
241 incr commitidx
($view)
242 if {$view == $curview} {
243 lappend parentlist
$olds
244 lappend displayorder
$id
245 lappend commitlisted
$listed
247 lappend vparentlist
($view) $olds
248 lappend vdisporder
($view) $id
249 lappend vcmitlisted
($view) $listed
254 run chewcommits
$view
259 proc chewcommits
{view
} {
260 global curview hlview commfd
261 global selectedline pending_select
264 if {$view == $curview} {
265 set allread
[expr {![info exists commfd
($view)]}]
266 set tlimit
[expr {[clock clicks
-milliseconds] + 50}]
267 set more [layoutmore
$tlimit $allread]
268 if {$allread && !$more} {
269 global displayorder commitidx phase
270 global numcommits startmsecs
272 if {[info exists pending_select
]} {
273 set row
[first_real_row
]
276 if {$commitidx($curview) > 0} {
277 #set ms [expr {[clock clicks -milliseconds] - $startmsecs}]
278 #puts "overall $ms ms for $numcommits commits"
280 show_status
"No commits selected"
286 if {[info exists hlview
] && $view == $hlview} {
292 proc readcommit
{id
} {
293 if {[catch
{set contents
[exec git cat-file commit
$id]}]} return
294 parsecommit
$id $contents 0
297 proc updatecommits
{} {
298 global viewdata curview phase displayorder
299 global children commitrow selectedline thickerline showneartags
306 foreach id
$displayorder {
307 catch
{unset children
($n,$id)}
308 catch
{unset commitrow
($n,$id)}
311 catch
{unset selectedline
}
312 catch
{unset thickerline
}
313 catch
{unset viewdata
($n)}
322 proc parsecommit
{id contents listed
} {
323 global commitinfo cdate
332 set hdrend
[string first
"\n\n" $contents]
334 # should never happen...
335 set hdrend
[string length
$contents]
337 set header
[string range
$contents 0 [expr {$hdrend - 1}]]
338 set comment
[string range
$contents [expr {$hdrend + 2}] end
]
339 foreach line
[split $header "\n"] {
340 set tag
[lindex
$line 0]
341 if {$tag == "author"} {
342 set audate
[lindex
$line end-1
]
343 set auname
[lrange
$line 1 end-2
]
344 } elseif
{$tag == "committer"} {
345 set comdate
[lindex
$line end-1
]
346 set comname
[lrange
$line 1 end-2
]
350 # take the first non-blank line of the comment as the headline
351 set headline
[string trimleft
$comment]
352 set i
[string first
"\n" $headline]
354 set headline
[string range
$headline 0 $i]
356 set headline
[string trimright
$headline]
357 set i
[string first
"\r" $headline]
359 set headline
[string trimright
[string range
$headline 0 $i]]
362 # git rev-list indents the comment by 4 spaces;
363 # if we got this via git cat-file, add the indentation
365 foreach line
[split $comment "\n"] {
366 append newcomment
" "
367 append newcomment
$line
368 append newcomment
"\n"
370 set comment
$newcomment
372 if {$comdate != {}} {
373 set cdate
($id) $comdate
375 set commitinfo
($id) [list
$headline $auname $audate \
376 $comname $comdate $comment]
379 proc getcommit
{id
} {
380 global commitdata commitinfo
382 if {[info exists commitdata
($id)]} {
383 parsecommit
$id $commitdata($id) 1
386 if {![info exists commitinfo
($id)]} {
387 set commitinfo
($id) {"No commit information available"}
394 global tagids idtags headids idheads tagobjid
395 global otherrefids idotherrefs mainhead mainheadid
397 foreach v
{tagids idtags headids idheads otherrefids idotherrefs
} {
400 set refd
[open
[list | git show-ref
-d] r
]
401 while {[gets
$refd line
] >= 0} {
402 if {[string index
$line 40] ne
" "} continue
403 set id
[string range
$line 0 39]
404 set ref
[string range
$line 41 end
]
405 if {![string match
"refs/*" $ref]} continue
406 set name
[string range
$ref 5 end
]
407 if {[string match
"remotes/*" $name]} {
408 if {![string match
"*/HEAD" $name]} {
409 set headids
($name) $id
410 lappend idheads
($id) $name
412 } elseif
{[string match
"heads/*" $name]} {
413 set name
[string range
$name 6 end
]
414 set headids
($name) $id
415 lappend idheads
($id) $name
416 } elseif
{[string match
"tags/*" $name]} {
417 # this lets refs/tags/foo^{} overwrite refs/tags/foo,
418 # which is what we want since the former is the commit ID
419 set name
[string range
$name 5 end
]
420 if {[string match
"*^{}" $name]} {
421 set name
[string range
$name 0 end-3
]
423 set tagobjid
($name) $id
425 set tagids
($name) $id
426 lappend idtags
($id) $name
428 set otherrefids
($name) $id
429 lappend idotherrefs
($id) $name
436 set thehead
[exec git symbolic-ref HEAD
]
437 if {[string match
"refs/heads/*" $thehead]} {
438 set mainhead
[string range
$thehead 11 end
]
439 if {[info exists headids
($mainhead)]} {
440 set mainheadid
$headids($mainhead)
446 # skip over fake commits
447 proc first_real_row
{} {
448 global nullid nullid2 displayorder numcommits
450 for {set row
0} {$row < $numcommits} {incr row
} {
451 set id
[lindex
$displayorder $row]
452 if {$id ne
$nullid && $id ne
$nullid2} {
459 # update things for a head moved to a child of its previous location
460 proc movehead
{id name
} {
461 global headids idheads
463 removehead
$headids($name) $name
464 set headids
($name) $id
465 lappend idheads
($id) $name
468 # update things when a head has been removed
469 proc removehead
{id name
} {
470 global headids idheads
472 if {$idheads($id) eq
$name} {
475 set i
[lsearch
-exact $idheads($id) $name]
477 set idheads
($id) [lreplace
$idheads($id) $i $i]
483 proc show_error
{w top msg
} {
484 message
$w.m
-text $msg -justify center
-aspect 400
485 pack
$w.m
-side top
-fill x
-padx 20 -pady 20
486 button
$w.ok
-text OK
-command "destroy $top"
487 pack
$w.ok
-side bottom
-fill x
488 bind $top <Visibility
> "grab $top; focus $top"
489 bind $top <Key-Return
> "destroy $top"
493 proc error_popup msg
{
497 show_error
$w $w $msg
500 proc confirm_popup msg
{
506 message
$w.m
-text $msg -justify center
-aspect 400
507 pack
$w.m
-side top
-fill x
-padx 20 -pady 20
508 button
$w.ok
-text OK
-command "set confirm_ok 1; destroy $w"
509 pack
$w.ok
-side left
-fill x
510 button
$w.cancel
-text Cancel
-command "destroy $w"
511 pack
$w.cancel
-side right
-fill x
512 bind $w <Visibility
> "grab $w; focus $w"
518 global canv canv2 canv3 linespc charspc ctext cflist
519 global textfont mainfont uifont tabstop
520 global findtype findtypemenu findloc findstring fstring geometry
521 global entries sha1entry sha1string sha1but
522 global diffcontextstring diffcontext
523 global maincursor textcursor curtextcursor
524 global rowctxmenu fakerowmenu mergemax wrapcomment
525 global highlight_files gdttype
526 global searchstring sstring
527 global bgcolor fgcolor bglist fglist diffcolors selectbgcolor
531 .bar add cascade
-label "File" -menu .bar.
file
532 .bar configure
-font $uifont
534 .bar.
file add
command -label "Update" -command updatecommits
535 .bar.
file add
command -label "Reread references" -command rereadrefs
536 .bar.
file add
command -label "List references" -command showrefs
537 .bar.
file add
command -label "Quit" -command doquit
538 .bar.
file configure
-font $uifont
540 .bar add cascade
-label "Edit" -menu .bar.edit
541 .bar.edit add
command -label "Preferences" -command doprefs
542 .bar.edit configure
-font $uifont
544 menu .bar.view
-font $uifont
545 .bar add cascade
-label "View" -menu .bar.view
546 .bar.view add
command -label "New view..." -command {newview
0}
547 .bar.view add
command -label "Edit view..." -command editview \
549 .bar.view add
command -label "Delete view" -command delview
-state disabled
550 .bar.view add separator
551 .bar.view add radiobutton
-label "All files" -command {showview
0} \
552 -variable selectedview
-value 0
555 .bar add cascade
-label "Help" -menu .bar.
help
556 .bar.
help add
command -label "About gitk" -command about
557 .bar.
help add
command -label "Key bindings" -command keys
558 .bar.
help configure
-font $uifont
559 . configure
-menu .bar
561 # the gui has upper and lower half, parts of a paned window.
562 panedwindow .ctop
-orient vertical
564 # possibly use assumed geometry
565 if {![info exists geometry
(pwsash0
)]} {
566 set geometry
(topheight
) [expr {15 * $linespc}]
567 set geometry
(topwidth
) [expr {80 * $charspc}]
568 set geometry
(botheight
) [expr {15 * $linespc}]
569 set geometry
(botwidth
) [expr {50 * $charspc}]
570 set geometry
(pwsash0
) "[expr {40 * $charspc}] 2"
571 set geometry
(pwsash1
) "[expr {60 * $charspc}] 2"
574 # the upper half will have a paned window, a scroll bar to the right, and some stuff below
575 frame .tf
-height $geometry(topheight
) -width $geometry(topwidth
)
577 panedwindow .tf.histframe.pwclist
-orient horizontal
-sashpad 0 -handlesize 4
579 # create three canvases
580 set cscroll .tf.histframe.csb
581 set canv .tf.histframe.pwclist.canv
583 -selectbackground $selectbgcolor \
584 -background $bgcolor -bd 0 \
585 -yscrollincr $linespc -yscrollcommand "scrollcanv $cscroll"
586 .tf.histframe.pwclist add
$canv
587 set canv2 .tf.histframe.pwclist.canv2
589 -selectbackground $selectbgcolor \
590 -background $bgcolor -bd 0 -yscrollincr $linespc
591 .tf.histframe.pwclist add
$canv2
592 set canv3 .tf.histframe.pwclist.canv3
594 -selectbackground $selectbgcolor \
595 -background $bgcolor -bd 0 -yscrollincr $linespc
596 .tf.histframe.pwclist add
$canv3
597 eval .tf.histframe.pwclist sash place
0 $geometry(pwsash0
)
598 eval .tf.histframe.pwclist sash place
1 $geometry(pwsash1
)
600 # a scroll bar to rule them
601 scrollbar
$cscroll -command {allcanvs yview
} -highlightthickness 0
602 pack
$cscroll -side right
-fill y
603 bind .tf.histframe.pwclist
<Configure
> {resizeclistpanes
%W
%w
}
604 lappend bglist
$canv $canv2 $canv3
605 pack .tf.histframe.pwclist
-fill both
-expand 1 -side left
607 # we have two button bars at bottom of top frame. Bar 1
609 frame .tf.lbar
-height 15
611 set sha1entry .tf.bar.sha1
612 set entries
$sha1entry
613 set sha1but .tf.bar.sha1label
614 button
$sha1but -text "SHA1 ID: " -state disabled
-relief flat \
615 -command gotocommit
-width 8 -font $uifont
616 $sha1but conf
-disabledforeground [$sha1but cget
-foreground]
617 pack .tf.bar.sha1label
-side left
618 entry
$sha1entry -width 40 -font $textfont -textvariable sha1string
619 trace add variable sha1string
write sha1change
620 pack
$sha1entry -side left
-pady 2
622 image create bitmap bm-left
-data {
623 #define left_width 16
624 #define left_height 16
625 static unsigned char left_bits
[] = {
626 0x00, 0x00, 0xc0, 0x01, 0xe0, 0x00, 0x70, 0x00, 0x38, 0x00, 0x1c, 0x00,
627 0x0e, 0x00, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0x0e, 0x00, 0x1c, 0x00,
628 0x38, 0x00, 0x70, 0x00, 0xe0, 0x00, 0xc0, 0x01};
630 image create bitmap bm-right
-data {
631 #define right_width 16
632 #define right_height 16
633 static unsigned char right_bits
[] = {
634 0x00, 0x00, 0xc0, 0x01, 0x80, 0x03, 0x00, 0x07, 0x00, 0x0e, 0x00, 0x1c,
635 0x00, 0x38, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0x00, 0x38, 0x00, 0x1c,
636 0x00, 0x0e, 0x00, 0x07, 0x80, 0x03, 0xc0, 0x01};
638 button .tf.bar.leftbut
-image bm-left
-command goback \
639 -state disabled
-width 26
640 pack .tf.bar.leftbut
-side left
-fill y
641 button .tf.bar.rightbut
-image bm-right
-command goforw \
642 -state disabled
-width 26
643 pack .tf.bar.rightbut
-side left
-fill y
645 button .tf.bar.findbut
-text "Find" -command dofind
-font $uifont
646 pack .tf.bar.findbut
-side left
648 set fstring .tf.bar.findstring
649 lappend entries
$fstring
650 entry
$fstring -width 30 -font $textfont -textvariable findstring
651 trace add variable findstring
write find_change
652 pack
$fstring -side left
-expand 1 -fill x
-in .tf.bar
654 set findtypemenu
[tk_optionMenu .tf.bar.findtype \
655 findtype Exact IgnCase Regexp
]
656 trace add variable findtype
write find_change
657 .tf.bar.findtype configure
-font $uifont
658 .tf.bar.findtype.menu configure
-font $uifont
659 set findloc
"All fields"
660 tk_optionMenu .tf.bar.findloc findloc
"All fields" Headline \
661 Comments Author Committer
662 trace add variable findloc
write find_change
663 .tf.bar.findloc configure
-font $uifont
664 .tf.bar.findloc.menu configure
-font $uifont
665 pack .tf.bar.findloc
-side right
666 pack .tf.bar.findtype
-side right
668 # build up the bottom bar of upper window
669 label .tf.lbar.flabel
-text "Highlight: Commits " \
671 pack .tf.lbar.flabel
-side left
-fill y
672 set gdttype
"touching paths:"
673 set gm
[tk_optionMenu .tf.lbar.gdttype gdttype
"touching paths:" \
674 "adding/removing string:"]
675 trace add variable gdttype
write hfiles_change
676 $gm conf
-font $uifont
677 .tf.lbar.gdttype conf
-font $uifont
678 pack .tf.lbar.gdttype
-side left
-fill y
679 entry .tf.lbar.fent
-width 25 -font $textfont \
680 -textvariable highlight_files
681 trace add variable highlight_files
write hfiles_change
682 lappend entries .tf.lbar.fent
683 pack .tf.lbar.fent
-side left
-fill x
-expand 1
684 label .tf.lbar.vlabel
-text " OR in view" -font $uifont
685 pack .tf.lbar.vlabel
-side left
-fill y
686 global viewhlmenu selectedhlview
687 set viewhlmenu
[tk_optionMenu .tf.lbar.vhl selectedhlview None
]
688 $viewhlmenu entryconf None
-command delvhighlight
689 $viewhlmenu conf
-font $uifont
690 .tf.lbar.vhl conf
-font $uifont
691 pack .tf.lbar.vhl
-side left
-fill y
692 label .tf.lbar.rlabel
-text " OR " -font $uifont
693 pack .tf.lbar.rlabel
-side left
-fill y
694 global highlight_related
695 set m
[tk_optionMenu .tf.lbar.relm highlight_related None \
696 "Descendent" "Not descendent" "Ancestor" "Not ancestor"]
697 $m conf
-font $uifont
698 .tf.lbar.relm conf
-font $uifont
699 trace add variable highlight_related
write vrel_change
700 pack .tf.lbar.relm
-side left
-fill y
702 # Finish putting the upper half of the viewer together
703 pack .tf.lbar
-in .tf
-side bottom
-fill x
704 pack .tf.bar
-in .tf
-side bottom
-fill x
705 pack .tf.histframe
-fill both
-side top
-expand 1
707 .ctop paneconfigure .tf
-height $geometry(topheight
)
708 .ctop paneconfigure .tf
-width $geometry(topwidth
)
710 # now build up the bottom
711 panedwindow .pwbottom
-orient horizontal
713 # lower left, a text box over search bar, scroll bar to the right
714 # if we know window height, then that will set the lower text height, otherwise
715 # we set lower text height which will drive window height
716 if {[info exists geometry
(main
)]} {
717 frame .bleft
-width $geometry(botwidth
)
719 frame .bleft
-width $geometry(botwidth
) -height $geometry(botheight
)
724 button .bleft.top.search
-text "Search" -command dosearch \
726 pack .bleft.top.search
-side left
-padx 5
727 set sstring .bleft.top.sstring
728 entry
$sstring -width 20 -font $textfont -textvariable searchstring
729 lappend entries
$sstring
730 trace add variable searchstring
write incrsearch
731 pack
$sstring -side left
-expand 1 -fill x
732 radiobutton .bleft.mid.
diff -text "Diff" \
733 -command changediffdisp
-variable diffelide
-value {0 0}
734 radiobutton .bleft.mid.old
-text "Old version" \
735 -command changediffdisp
-variable diffelide
-value {0 1}
736 radiobutton .bleft.mid.new
-text "New version" \
737 -command changediffdisp
-variable diffelide
-value {1 0}
738 label .bleft.mid.labeldiffcontext
-text " Lines of context: " \
740 pack .bleft.mid.
diff .bleft.mid.old .bleft.mid.new
-side left
741 spinbox .bleft.mid.diffcontext
-width 5 -font $textfont \
742 -from 1 -increment 1 -to 10000000 \
743 -validate all
-validatecommand "diffcontextvalidate %P" \
744 -textvariable diffcontextstring
745 .bleft.mid.diffcontext
set $diffcontext
746 trace add variable diffcontextstring
write diffcontextchange
747 lappend entries .bleft.mid.diffcontext
748 pack .bleft.mid.labeldiffcontext .bleft.mid.diffcontext
-side left
749 set ctext .bleft.ctext
750 text
$ctext -background $bgcolor -foreground $fgcolor \
751 -tabs "[expr {$tabstop * $charspc}]" \
752 -state disabled
-font $textfont \
753 -yscrollcommand scrolltext
-wrap none
754 scrollbar .bleft.sb
-command "$ctext yview"
755 pack .bleft.top
-side top
-fill x
756 pack .bleft.mid
-side top
-fill x
757 pack .bleft.sb
-side right
-fill y
758 pack
$ctext -side left
-fill both
-expand 1
759 lappend bglist
$ctext
760 lappend fglist
$ctext
762 $ctext tag conf comment
-wrap $wrapcomment
763 $ctext tag conf filesep
-font [concat
$textfont bold
] -back "#aaaaaa"
764 $ctext tag conf hunksep
-fore [lindex
$diffcolors 2]
765 $ctext tag conf d0
-fore [lindex
$diffcolors 0]
766 $ctext tag conf d1
-fore [lindex
$diffcolors 1]
767 $ctext tag conf m0
-fore red
768 $ctext tag conf m1
-fore blue
769 $ctext tag conf m2
-fore green
770 $ctext tag conf m3
-fore purple
771 $ctext tag conf
m4 -fore brown
772 $ctext tag conf m5
-fore "#009090"
773 $ctext tag conf m6
-fore magenta
774 $ctext tag conf m7
-fore "#808000"
775 $ctext tag conf m8
-fore "#009000"
776 $ctext tag conf m9
-fore "#ff0080"
777 $ctext tag conf m10
-fore cyan
778 $ctext tag conf m11
-fore "#b07070"
779 $ctext tag conf m12
-fore "#70b0f0"
780 $ctext tag conf m13
-fore "#70f0b0"
781 $ctext tag conf m14
-fore "#f0b070"
782 $ctext tag conf m15
-fore "#ff70b0"
783 $ctext tag conf mmax
-fore darkgrey
785 $ctext tag conf mresult
-font [concat
$textfont bold
]
786 $ctext tag conf msep
-font [concat
$textfont bold
]
787 $ctext tag conf found
-back yellow
790 .pwbottom paneconfigure .bleft
-width $geometry(botwidth
)
795 radiobutton .bright.mode.
patch -text "Patch" \
796 -command reselectline
-variable cmitmode
-value "patch"
797 .bright.mode.
patch configure
-font $uifont
798 radiobutton .bright.mode.tree
-text "Tree" \
799 -command reselectline
-variable cmitmode
-value "tree"
800 .bright.mode.tree configure
-font $uifont
801 grid .bright.mode.
patch .bright.mode.tree
-sticky ew
802 pack .bright.mode
-side top
-fill x
803 set cflist .bright.cfiles
804 set indent
[font measure
$mainfont "nn"]
806 -selectbackground $selectbgcolor \
807 -background $bgcolor -foreground $fgcolor \
809 -tabs [list
$indent [expr {2 * $indent}]] \
810 -yscrollcommand ".bright.sb set" \
811 -cursor [. cget
-cursor] \
812 -spacing1 1 -spacing3 1
813 lappend bglist
$cflist
814 lappend fglist
$cflist
815 scrollbar .bright.sb
-command "$cflist yview"
816 pack .bright.sb
-side right
-fill y
817 pack
$cflist -side left
-fill both
-expand 1
818 $cflist tag configure highlight \
819 -background [$cflist cget
-selectbackground]
820 $cflist tag configure bold
-font [concat
$mainfont bold
]
822 .pwbottom add .bright
825 # restore window position if known
826 if {[info exists geometry
(main
)]} {
827 wm geometry .
"$geometry(main)"
830 if {[tk windowingsystem
] eq
{aqua
}} {
836 bind .pwbottom
<Configure
> {resizecdetpanes
%W
%w
}
837 pack .ctop
-fill both
-expand 1
838 bindall
<1> {selcanvline
%W
%x
%y
}
839 #bindall <B1-Motion> {selcanvline %W %x %y}
840 if {[tk windowingsystem
] == "win32"} {
841 bind .
<MouseWheel
> { windows_mousewheel_redirector
%W
%X
%Y
%D
}
842 bind $ctext <MouseWheel
> { windows_mousewheel_redirector
%W
%X
%Y
%D
; break }
844 bindall
<ButtonRelease-4
> "allcanvs yview scroll -5 units"
845 bindall
<ButtonRelease-5
> "allcanvs yview scroll 5 units"
846 if {[tk windowingsystem
] eq
"aqua"} {
847 bindall
<MouseWheel
> {
848 set delta
[expr {- (%D
)}]
849 allcanvs yview scroll
$delta units
853 bindall
<2> "canvscan mark %W %x %y"
854 bindall
<B2-Motion
> "canvscan dragto %W %x %y"
855 bindkey
<Home
> selfirstline
856 bindkey
<End
> sellastline
857 bind .
<Key-Up
> "selnextline -1"
858 bind .
<Key-Down
> "selnextline 1"
859 bind .
<Shift-Key-Up
> "next_highlight -1"
860 bind .
<Shift-Key-Down
> "next_highlight 1"
861 bindkey
<Key-Right
> "goforw"
862 bindkey
<Key-Left
> "goback"
863 bind .
<Key-Prior
> "selnextpage -1"
864 bind .
<Key-Next
> "selnextpage 1"
865 bind .
<$M1B-Home> "allcanvs yview moveto 0.0"
866 bind .
<$M1B-End> "allcanvs yview moveto 1.0"
867 bind .
<$M1B-Key-Up> "allcanvs yview scroll -1 units"
868 bind .
<$M1B-Key-Down> "allcanvs yview scroll 1 units"
869 bind .
<$M1B-Key-Prior> "allcanvs yview scroll -1 pages"
870 bind .
<$M1B-Key-Next> "allcanvs yview scroll 1 pages"
871 bindkey
<Key-Delete
> "$ctext yview scroll -1 pages"
872 bindkey
<Key-BackSpace
> "$ctext yview scroll -1 pages"
873 bindkey
<Key-space
> "$ctext yview scroll 1 pages"
874 bindkey p
"selnextline -1"
875 bindkey n
"selnextline 1"
878 bindkey i
"selnextline -1"
879 bindkey k
"selnextline 1"
882 bindkey b
"$ctext yview scroll -1 pages"
883 bindkey d
"$ctext yview scroll 18 units"
884 bindkey u
"$ctext yview scroll -18 units"
885 bindkey
/ {findnext
1}
886 bindkey
<Key-Return
> {findnext
0}
889 bindkey
<F5
> updatecommits
890 bind .
<$M1B-q> doquit
891 bind .
<$M1B-f> dofind
892 bind .
<$M1B-g> {findnext
0}
893 bind .
<$M1B-r> dosearchback
894 bind .
<$M1B-s> dosearch
895 bind .
<$M1B-equal> {incrfont
1}
896 bind .
<$M1B-KP_Add> {incrfont
1}
897 bind .
<$M1B-minus> {incrfont
-1}
898 bind .
<$M1B-KP_Subtract> {incrfont
-1}
899 wm protocol . WM_DELETE_WINDOW doquit
900 bind .
<Button-1
> "click %W"
901 bind $fstring <Key-Return
> dofind
902 bind $sha1entry <Key-Return
> gotocommit
903 bind $sha1entry <<PasteSelection>> clearsha1
904 bind $cflist <1> {sel_flist %W %x %y; break}
905 bind $cflist <B1-Motion> {sel_flist %W %x %y; break}
906 bind $cflist <ButtonRelease-1> {treeclick %W %x %y}
907 bind $cflist <Button-3> {pop_flist_menu %W %X %Y %x %y}
909 set maincursor [. cget -cursor]
910 set textcursor [$ctext cget -cursor]
911 set curtextcursor $textcursor
913 set rowctxmenu .rowctxmenu
914 menu $rowctxmenu -tearoff 0
915 $rowctxmenu add command -label "Diff this -> selected" \
916 -command {diffvssel 0}
917 $rowctxmenu add command -label "Diff selected -> this" \
918 -command {diffvssel 1}
919 $rowctxmenu add command -label "Make patch" -command mkpatch
920 $rowctxmenu add command -label "Create tag" -command mktag
921 $rowctxmenu add command -label "Write commit to file" -command writecommit
922 $rowctxmenu add command -label "Create new branch" -command mkbranch
923 $rowctxmenu add command -label "Cherry-pick this commit" \
925 $rowctxmenu add command -label "Reset HEAD branch to here" \
928 set fakerowmenu .fakerowmenu
929 menu $fakerowmenu -tearoff 0
930 $fakerowmenu add command -label "Diff this -> selected" \
931 -command {diffvssel 0}
932 $fakerowmenu add command -label "Diff selected -> this" \
933 -command {diffvssel 1}
934 $fakerowmenu add command -label "Make patch" -command mkpatch
935 # $fakerowmenu add command -label "Commit" -command {mkcommit 0}
936 # $fakerowmenu add command -label "Commit all" -command {mkcommit 1}
937 # $fakerowmenu add command -label "Revert local changes" -command revertlocal
939 set headctxmenu .headctxmenu
940 menu $headctxmenu -tearoff 0
941 $headctxmenu add command -label "Check out this branch" \
943 $headctxmenu add command -label "Remove this branch" \
947 set flist_menu .flistctxmenu
948 menu $flist_menu -tearoff 0
949 $flist_menu add command -label "Highlight this too" \
950 -command {flist_hl 0}
951 $flist_menu add command -label "Highlight this only" \
952 -command {flist_hl 1}
955 # Windows sends all mouse wheel events to the current focused window, not
956 # the one where the mouse hovers, so bind those events here and redirect
957 # to the correct window
958 proc windows_mousewheel_redirector {W X Y D} {
959 global canv canv2 canv3
960 set w [winfo containing -displayof $W $X $Y]
962 set u [expr {$D < 0 ? 5 : -5}]
963 if {$w == $canv || $w == $canv2 || $w == $canv3} {
964 allcanvs yview scroll $u units
967 $w yview scroll $u units
973 # mouse-2 makes all windows scan vertically, but only the one
974 # the cursor is in scans horizontally
975 proc canvscan {op w x y} {
976 global canv canv2 canv3
977 foreach c [list $canv $canv2 $canv3] {
986 proc scrollcanv {cscroll f0 f1} {
992 # when we make a key binding for the toplevel, make sure
993 # it doesn't get triggered when that key is pressed in the
994 # find string entry widget.
995 proc bindkey {ev script} {
998 set escript [bind Entry $ev]
999 if {$escript == {}} {
1000 set escript [bind Entry <Key>]
1002 foreach e $entries {
1003 bind $e $ev "$escript; break"
1007 # set the focus back to the toplevel for any click outside
1010 global ctext entries
1011 foreach e [concat $entries $ctext] {
1012 if {$w == $e} return
1017 proc savestuff {w} {
1018 global canv canv2 canv3 ctext cflist mainfont textfont uifont tabstop
1019 global stuffsaved findmergefiles maxgraphpct
1020 global maxwidth showneartags showlocalchanges
1021 global viewname viewfiles viewargs viewperm nextviewnum
1022 global cmitmode wrapcomment datetimeformat
1023 global colors bgcolor fgcolor diffcolors diffcontext selectbgcolor
1025 if {$stuffsaved} return
1026 if {![winfo viewable .]} return
1028 set f [open "~/.gitk-new" w]
1029 puts $f [list set mainfont $mainfont]
1030 puts $f [list set textfont $textfont]
1031 puts $f [list set uifont $uifont]
1032 puts $f [list set tabstop $tabstop]
1033 puts $f [list set findmergefiles $findmergefiles]
1034 puts $f [list set maxgraphpct $maxgraphpct]
1035 puts $f [list set maxwidth $maxwidth]
1036 puts $f [list set cmitmode $cmitmode]
1037 puts $f [list set wrapcomment $wrapcomment]
1038 puts $f [list set showneartags $showneartags]
1039 puts $f [list set showlocalchanges $showlocalchanges]
1040 puts $f [list set datetimeformat $datetimeformat]
1041 puts $f [list set bgcolor $bgcolor]
1042 puts $f [list set fgcolor $fgcolor]
1043 puts $f [list set colors $colors]
1044 puts $f [list set diffcolors $diffcolors]
1045 puts $f [list set diffcontext $diffcontext]
1046 puts $f [list set selectbgcolor $selectbgcolor]
1048 puts $f "set geometry(main) [wm geometry .]"
1049 puts $f "set geometry(topwidth) [winfo width .tf]"
1050 puts $f "set geometry(topheight) [winfo height .tf]"
1051 puts $f "set geometry(pwsash0) \"[.tf.histframe.pwclist sash coord 0]\""
1052 puts $f "set geometry(pwsash1) \"[.tf.histframe.pwclist sash coord 1]\""
1053 puts $f "set geometry(botwidth) [winfo width .bleft]"
1054 puts $f "set geometry(botheight) [winfo height .bleft]"
1056 puts -nonewline $f "set permviews {"
1057 for {set v 0} {$v < $nextviewnum} {incr v} {
1058 if {$viewperm($v)} {
1059 puts $f "{[list $viewname($v) $viewfiles($v) $viewargs($v)]}"
1064 file rename -force "~/.gitk-new" "~/.gitk"
1069 proc resizeclistpanes {win w} {
1071 if {[info exists oldwidth($win)]} {
1072 set s0 [$win sash coord 0]
1073 set s1 [$win sash coord 1]
1075 set sash0 [expr {int($w/2 - 2)}]
1076 set sash1 [expr {int($w*5/6 - 2)}]
1078 set factor [expr {1.0 * $w / $oldwidth($win)}]
1079 set sash0 [expr {int($factor * [lindex $s0 0])}]
1080 set sash1 [expr {int($factor * [lindex $s1 0])}]
1084 if {$sash1 < $sash0 + 20} {
1085 set sash1 [expr {$sash0 + 20}]
1087 if {$sash1 > $w - 10} {
1088 set sash1 [expr {$w - 10}]
1089 if {$sash0 > $sash1 - 20} {
1090 set sash0 [expr {$sash1 - 20}]
1094 $win sash place 0 $sash0 [lindex $s0 1]
1095 $win sash place 1 $sash1 [lindex $s1 1]
1097 set oldwidth($win) $w
1100 proc resizecdetpanes {win w} {
1102 if {[info exists oldwidth($win)]} {
1103 set s0 [$win sash coord 0]
1105 set sash0 [expr {int($w*3/4 - 2)}]
1107 set factor [expr {1.0 * $w / $oldwidth($win)}]
1108 set sash0 [expr {int($factor * [lindex $s0 0])}]
1112 if {$sash0 > $w - 15} {
1113 set sash0 [expr {$w - 15}]
1116 $win sash place 0 $sash0 [lindex $s0 1]
1118 set oldwidth($win) $w
1121 proc allcanvs args {
1122 global canv canv2 canv3
1128 proc bindall {event action} {
1129 global canv canv2 canv3
1130 bind $canv $event $action
1131 bind $canv2 $event $action
1132 bind $canv3 $event $action
1138 if {[winfo exists $w]} {
1143 wm title $w "About gitk"
1144 message $w.m -text {
1145 Gitk - a commit viewer for git
1147 Copyright © 2005-2006 Paul Mackerras
1149 Use and redistribute under the terms of the GNU General Public License} \
1150 -justify center -aspect 400 -border 2 -bg white -relief groove
1151 pack $w.m -side top -fill x -padx 2 -pady 2
1152 $w.m configure -font $uifont
1153 button $w.ok -text Close -command "destroy $w" -default active
1154 pack $w.ok -side bottom
1155 $w.ok configure -font $uifont
1156 bind $w <Visibility> "focus $w.ok"
1157 bind $w <Key-Escape> "destroy $w"
1158 bind $w <Key-Return> "destroy $w"
1164 if {[winfo exists $w]} {
1168 if {[tk windowingsystem] eq {aqua}} {
1174 wm title $w "Gitk key bindings"
1175 message $w.m -text "
1179 <Home> Move to first commit
1180 <End> Move to last commit
1181 <Up>, p, i Move up one commit
1182 <Down>, n, k Move down one commit
1183 <Left>, z, j Go back in history list
1184 <Right>, x, l Go forward in history list
1185 <PageUp> Move up one page in commit list
1186 <PageDown> Move down one page in commit list
1187 <$M1T-Home> Scroll to top of commit list
1188 <$M1T-End> Scroll to bottom of commit list
1189 <$M1T-Up> Scroll commit list up one line
1190 <$M1T-Down> Scroll commit list down one line
1191 <$M1T-PageUp> Scroll commit list up one page
1192 <$M1T-PageDown> Scroll commit list down one page
1193 <Shift-Up> Move to previous highlighted line
1194 <Shift-Down> Move to next highlighted line
1195 <Delete>, b Scroll diff view up one page
1196 <Backspace> Scroll diff view up one page
1197 <Space> Scroll diff view down one page
1198 u Scroll diff view up 18 lines
1199 d Scroll diff view down 18 lines
1201 <$M1T-G> Move to next find hit
1202 <Return> Move to next find hit
1203 / Move to next find hit, or redo find
1204 ? Move to previous find hit
1205 f Scroll diff view to next file
1206 <$M1T-S> Search for next hit in diff view
1207 <$M1T-R> Search for previous hit in diff view
1208 <$M1T-KP+> Increase font size
1209 <$M1T-plus> Increase font size
1210 <$M1T-KP-> Decrease font size
1211 <$M1T-minus> Decrease font size
1214 -justify left -bg white -border 2 -relief groove
1215 pack $w.m -side top -fill both -padx 2 -pady 2
1216 $w.m configure -font $uifont
1217 button $w.ok -text Close -command "destroy $w" -default active
1218 pack $w.ok -side bottom
1219 $w.ok configure -font $uifont
1220 bind $w <Visibility> "focus $w.ok"
1221 bind $w <Key-Escape> "destroy $w"
1222 bind $w <Key-Return> "destroy $w"
1225 # Procedures for manipulating the file list window at the
1226 # bottom right of the overall window.
1228 proc treeview {w l openlevs} {
1229 global treecontents treediropen treeheight treeparent treeindex
1239 set treecontents() {}
1240 $w conf -state normal
1242 while {[string range $f 0 $prefixend] ne $prefix} {
1243 if {$lev <= $openlevs} {
1244 $w mark set e:$treeindex($prefix) "end -1c"
1245 $w mark gravity e:$treeindex($prefix) left
1247 set treeheight($prefix) $ht
1248 incr ht [lindex $htstack end]
1249 set htstack [lreplace $htstack end end]
1250 set prefixend [lindex $prefendstack end]
1251 set prefendstack [lreplace $prefendstack end end]
1252 set prefix [string range $prefix 0 $prefixend]
1255 set tail [string range $f [expr {$prefixend+1}] end]
1256 while {[set slash [string first "/" $tail]] >= 0} {
1259 lappend prefendstack $prefixend
1260 incr prefixend [expr {$slash + 1}]
1261 set d [string range $tail 0 $slash]
1262 lappend treecontents($prefix) $d
1263 set oldprefix $prefix
1265 set treecontents($prefix) {}
1266 set treeindex($prefix) [incr ix]
1267 set treeparent($prefix) $oldprefix
1268 set tail [string range $tail [expr {$slash+1}] end]
1269 if {$lev <= $openlevs} {
1271 set treediropen($prefix) [expr {$lev < $openlevs}]
1272 set bm [expr {$lev == $openlevs? "tri-rt": "tri-dn"}]
1273 $w mark set d:$ix "end -1c"
1274 $w mark gravity d:$ix left
1276 for {set i 0} {$i < $lev} {incr i} {append str "\t"}
1278 $w image create end -align center -image $bm -padx 1 \
1280 $w insert end $d [highlight_tag $prefix]
1281 $w mark set s:$ix "end -1c"
1282 $w mark gravity s:$ix left
1287 if {$lev <= $openlevs} {
1290 for {set i 0} {$i < $lev} {incr i} {append str "\t"}
1292 $w insert end $tail [highlight_tag $f]
1294 lappend treecontents($prefix) $tail
1297 while {$htstack ne {}} {
1298 set treeheight($prefix) $ht
1299 incr ht [lindex $htstack end]
1300 set htstack [lreplace $htstack end end]
1301 set prefixend [lindex $prefendstack end]
1302 set prefendstack [lreplace $prefendstack end end]
1303 set prefix [string range $prefix 0 $prefixend]
1305 $w conf -state disabled
1308 proc linetoelt {l} {
1309 global treeheight treecontents
1314 foreach e $treecontents($prefix) {
1319 if {[string index $e end] eq "/"} {
1320 set n $treeheight($prefix$e)
1332 proc highlight_tree {y prefix} {
1333 global treeheight treecontents cflist
1335 foreach e $treecontents($prefix) {
1337 if {[highlight_tag $path] ne {}} {
1338 $cflist tag add bold $y.0 "$y.0 lineend"
1341 if {[string index $e end] eq "/" && $treeheight($path) > 1} {
1342 set y [highlight_tree $y $path]
1348 proc treeclosedir {w dir} {
1349 global treediropen treeheight treeparent treeindex
1351 set ix $treeindex($dir)
1352 $w conf -state normal
1353 $w delete s:$ix e:$ix
1354 set treediropen($dir) 0
1355 $w image configure a:$ix -image tri-rt
1356 $w conf -state disabled
1357 set n [expr {1 - $treeheight($dir)}]
1358 while {$dir ne {}} {
1359 incr treeheight($dir) $n
1360 set dir $treeparent($dir)
1364 proc treeopendir {w dir} {
1365 global treediropen treeheight treeparent treecontents treeindex
1367 set ix $treeindex($dir)
1368 $w conf -state normal
1369 $w image configure a:$ix -image tri-dn
1370 $w mark set e:$ix s:$ix
1371 $w mark gravity e:$ix right
1374 set n [llength $treecontents($dir)]
1375 for {set x $dir} {$x ne {}} {set x $treeparent($x)} {
1378 incr treeheight($x) $n
1380 foreach e $treecontents($dir) {
1382 if {[string index $e end] eq "/"} {
1383 set iy $treeindex($de)
1384 $w mark set d:$iy e:$ix
1385 $w mark gravity d:$iy left
1386 $w insert e:$ix $str
1387 set treediropen($de) 0
1388 $w image create e:$ix -align center -image tri-rt -padx 1 \
1390 $w insert e:$ix $e [highlight_tag $de]
1391 $w mark set s:$iy e:$ix
1392 $w mark gravity s:$iy left
1393 set treeheight($de) 1
1395 $w insert e:$ix $str
1396 $w insert e:$ix $e [highlight_tag $de]
1399 $w mark gravity e:$ix left
1400 $w conf -state disabled
1401 set treediropen($dir) 1
1402 set top [lindex [split [$w index @0,0] .] 0]
1403 set ht [$w cget -height]
1404 set l [lindex [split [$w index s:$ix] .] 0]
1407 } elseif {$l + $n + 1 > $top + $ht} {
1408 set top [expr {$l + $n + 2 - $ht}]
1416 proc treeclick {w x y} {
1417 global treediropen cmitmode ctext cflist cflist_top
1419 if {$cmitmode ne "tree"} return
1420 if {![info exists cflist_top]} return
1421 set l [lindex [split [$w index "@$x,$y"] "."] 0]
1422 $cflist tag remove highlight $cflist_top.0 "$cflist_top.0 lineend"
1423 $cflist tag add highlight $l.0 "$l.0 lineend"
1429 set e [linetoelt $l]
1430 if {[string index $e end] ne "/"} {
1432 } elseif {$treediropen($e)} {
1439 proc setfilelist {id} {
1440 global treefilelist cflist
1442 treeview $cflist $treefilelist($id) 0
1445 image create bitmap tri-rt -background black -foreground blue -data {
1446 #define tri-rt_width 13
1447 #define tri-rt_height 13
1448 static unsigned char tri-rt_bits[] = {
1449 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x30, 0x00, 0x70, 0x00, 0xf0, 0x00,
1450 0xf0, 0x01, 0xf0, 0x00, 0x70, 0x00, 0x30, 0x00, 0x10, 0x00, 0x00, 0x00,
1453 #define tri-rt-mask_width 13
1454 #define tri-rt-mask_height 13
1455 static unsigned char tri-rt-mask_bits[] = {
1456 0x08, 0x00, 0x18, 0x00, 0x38, 0x00, 0x78, 0x00, 0xf8, 0x00, 0xf8, 0x01,
1457 0xf8, 0x03, 0xf8, 0x01, 0xf8, 0x00, 0x78, 0x00, 0x38, 0x00, 0x18, 0x00,
1460 image create bitmap tri-dn -background black -foreground blue -data {
1461 #define tri-dn_width 13
1462 #define tri-dn_height 13
1463 static unsigned char tri-dn_bits[] = {
1464 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x07, 0xf8, 0x03,
1465 0xf0, 0x01, 0xe0, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1468 #define tri-dn-mask_width 13
1469 #define tri-dn-mask_height 13
1470 static unsigned char tri-dn-mask_bits[] = {
1471 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x1f, 0xfe, 0x0f, 0xfc, 0x07,
1472 0xf8, 0x03, 0xf0, 0x01, 0xe0, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
1476 image create bitmap reficon-T -background black -foreground yellow -data {
1477 #define tagicon_width 13
1478 #define tagicon_height 9
1479 static unsigned char tagicon_bits[] = {
1480 0x00, 0x00, 0x00, 0x00, 0xf0, 0x07, 0xf8, 0x07,
1481 0xfc, 0x07, 0xf8, 0x07, 0xf0, 0x07, 0x00, 0x00, 0x00, 0x00};
1483 #define tagicon-mask_width 13
1484 #define tagicon-mask_height 9
1485 static unsigned char tagicon-mask_bits[] = {
1486 0x00, 0x00, 0xf0, 0x0f, 0xf8, 0x0f, 0xfc, 0x0f,
1487 0xfe, 0x0f, 0xfc, 0x0f, 0xf8, 0x0f, 0xf0, 0x0f, 0x00, 0x00};
1490 #define headicon_width 13
1491 #define headicon_height 9
1492 static unsigned char headicon_bits[] = {
1493 0x00, 0x00, 0x00, 0x00, 0xf8, 0x07, 0xf8, 0x07,
1494 0xf8, 0x07, 0xf8, 0x07, 0xf8, 0x07, 0x00, 0x00, 0x00, 0x00};
1497 #define headicon-mask_width 13
1498 #define headicon-mask_height 9
1499 static unsigned char headicon-mask_bits[] = {
1500 0x00, 0x00, 0xfc, 0x0f, 0xfc, 0x0f, 0xfc, 0x0f,
1501 0xfc, 0x0f, 0xfc, 0x0f, 0xfc, 0x0f, 0xfc, 0x0f, 0x00, 0x00};
1503 image create bitmap reficon-H -background black -foreground green \
1504 -data $rectdata -maskdata $rectmask
1505 image create bitmap reficon-o -background black -foreground "#ddddff" \
1506 -data $rectdata -maskdata $rectmask
1508 proc init_flist {first} {
1509 global cflist cflist_top selectedline difffilestart
1511 $cflist conf -state normal
1512 $cflist delete 0.0 end
1514 $cflist insert end $first
1516 $cflist tag add highlight 1.0 "1.0 lineend"
1518 catch {unset cflist_top}
1520 $cflist conf -state disabled
1521 set difffilestart {}
1524 proc highlight_tag {f} {
1525 global highlight_paths
1527 foreach p $highlight_paths {
1528 if {[string match $p $f]} {
1535 proc highlight_filelist {} {
1536 global cmitmode cflist
1538 $cflist conf -state normal
1539 if {$cmitmode ne "tree"} {
1540 set end [lindex [split [$cflist index end] .] 0]
1541 for {set l 2} {$l < $end} {incr l} {
1542 set line [$cflist get $l.0 "$l.0 lineend"]
1543 if {[highlight_tag $line] ne {}} {
1544 $cflist tag add bold $l.0 "$l.0 lineend"
1550 $cflist conf -state disabled
1553 proc unhighlight_filelist {} {
1556 $cflist conf -state normal
1557 $cflist tag remove bold 1.0 end
1558 $cflist conf -state disabled
1561 proc add_flist {fl} {
1564 $cflist conf -state normal
1566 $cflist insert end "\n"
1567 $cflist insert end $f [highlight_tag $f]
1569 $cflist conf -state disabled
1572 proc sel_flist {w x y} {
1573 global ctext difffilestart cflist cflist_top cmitmode
1575 if {$cmitmode eq "tree"} return
1576 if {![info exists cflist_top]} return
1577 set l [lindex [split [$w index "@$x,$y"] "."] 0]
1578 $cflist tag remove highlight $cflist_top.0 "$cflist_top.0 lineend"
1579 $cflist tag add highlight $l.0 "$l.0 lineend"
1584 catch {$ctext yview [lindex $difffilestart [expr {$l - 2}]]}
1588 proc pop_flist_menu {w X Y x y} {
1589 global ctext cflist cmitmode flist_menu flist_menu_file
1590 global treediffs diffids
1592 set l [lindex [split [$w index "@$x,$y"] "."] 0]
1594 if {$cmitmode eq "tree"} {
1595 set e [linetoelt $l]
1596 if {[string index $e end] eq "/"} return
1598 set e [lindex $treediffs($diffids) [expr {$l-2}]]
1600 set flist_menu_file $e
1601 tk_popup $flist_menu $X $Y
1604 proc flist_hl {only} {
1605 global flist_menu_file highlight_files
1607 set x [shellquote $flist_menu_file]
1608 if {$only || $highlight_files eq {}} {
1609 set highlight_files $x
1611 append highlight_files " " $x
1615 # Functions for adding and removing shell-type quoting
1617 proc shellquote {str} {
1618 if {![string match "*\['\"\\ \t]*" $str]} {
1621 if {![string match "*\['\"\\]*" $str]} {
1624 if {![string match "*'*" $str]} {
1627 return "\"[string map {\" \\\" \\ \\\\} $str]\""
1630 proc shellarglist {l} {
1636 append str [shellquote $a]
1641 proc shelldequote {str} {
1646 if {![regexp -start $used -indices "\['\"\\\\ \t]" $str first]} {
1647 append ret [string range $str $used end]
1648 set used [string length $str]
1651 set first [lindex $first 0]
1652 set ch [string index $str $first]
1653 if {$first > $used} {
1654 append ret [string range $str $used [expr {$first - 1}]]
1657 if {$ch eq " " || $ch eq "\t"} break
1660 set first [string first "'" $str $used]
1662 error "unmatched single-quote"
1664 append ret [string range $str $used [expr {$first - 1}]]
1669 if {$used >= [string length $str]} {
1670 error "trailing backslash"
1672 append ret [string index $str $used]
1677 if {![regexp -start $used -indices "\[\"\\\\]" $str first]} {
1678 error "unmatched double-quote"
1680 set first [lindex $first 0]
1681 set ch [string index $str $first]
1682 if {$first > $used} {
1683 append ret [string range $str $used [expr {$first - 1}]]
1686 if {$ch eq "\""} break
1688 append ret [string index $str $used]
1692 return [list $used $ret]
1695 proc shellsplit {str} {
1698 set str [string trimleft $str]
1699 if {$str eq {}} break
1700 set dq [shelldequote $str]
1701 set n [lindex $dq 0]
1702 set word [lindex $dq 1]
1703 set str [string range $str $n end]
1709 # Code to implement multiple views
1711 proc newview {ishighlight} {
1712 global nextviewnum newviewname newviewperm uifont newishighlight
1713 global newviewargs revtreeargs
1715 set newishighlight $ishighlight
1717 if {[winfo exists $top]} {
1721 set newviewname($nextviewnum) "View $nextviewnum"
1722 set newviewperm($nextviewnum) 0
1723 set newviewargs($nextviewnum) [shellarglist $revtreeargs]
1724 vieweditor $top $nextviewnum "Gitk view definition"
1729 global viewname viewperm newviewname newviewperm
1730 global viewargs newviewargs
1732 set top .gitkvedit-$curview
1733 if {[winfo exists $top]} {
1737 set newviewname($curview) $viewname($curview)
1738 set newviewperm($curview) $viewperm($curview)
1739 set newviewargs($curview) [shellarglist $viewargs($curview)]
1740 vieweditor $top $curview "Gitk: edit view $viewname($curview)"
1743 proc vieweditor {top n title} {
1744 global newviewname newviewperm viewfiles
1748 wm title $top $title
1749 label $top.nl -text "Name" -font $uifont
1750 entry $top.name -width 20 -textvariable newviewname($n) -font $uifont
1751 grid $top.nl $top.name -sticky w -pady 5
1752 checkbutton $top.perm -text "Remember this view" -variable newviewperm($n) \
1754 grid $top.perm - -pady 5 -sticky w
1755 message $top.al -aspect 1000 -font $uifont \
1756 -text "Commits to include (arguments to git rev-list):"
1757 grid $top.al - -sticky w -pady 5
1758 entry $top.args -width 50 -textvariable newviewargs($n) \
1759 -background white -font $uifont
1760 grid $top.args - -sticky ew -padx 5
1761 message $top.l -aspect 1000 -font $uifont \
1762 -text "Enter files and directories to include, one per line:"
1763 grid $top.l - -sticky w
1764 text $top.t -width 40 -height 10 -background white -font $uifont
1765 if {[info exists viewfiles($n)]} {
1766 foreach f $viewfiles($n) {
1767 $top.t insert end $f
1768 $top.t insert end "\n"
1770 $top.t delete {end - 1c} end
1771 $top.t mark set insert 0.0
1773 grid $top.t - -sticky ew -padx 5
1775 button $top.buts.ok -text "OK" -command [list newviewok $top $n] \
1777 button $top.buts.can -text "Cancel" -command [list destroy $top] \
1779 grid $top.buts.ok $top.buts.can
1780 grid columnconfigure $top.buts 0 -weight 1 -uniform a
1781 grid columnconfigure $top.buts 1 -weight 1 -uniform a
1782 grid $top.buts - -pady 10 -sticky ew
1786 proc doviewmenu {m first cmd op argv} {
1787 set nmenu [$m index end]
1788 for {set i $first} {$i <= $nmenu} {incr i} {
1789 if {[$m entrycget $i -command] eq $cmd} {
1790 eval $m $op $i $argv
1796 proc allviewmenus {n op args} {
1799 doviewmenu .bar.view 5 [list showview $n] $op $args
1800 doviewmenu $viewhlmenu 1 [list addvhighlight $n] $op $args
1803 proc newviewok {top n} {
1804 global nextviewnum newviewperm newviewname newishighlight
1805 global viewname viewfiles viewperm selectedview curview
1806 global viewargs newviewargs viewhlmenu
1809 set newargs [shellsplit $newviewargs($n)]
1811 error_popup "Error in commit selection arguments: $err"
1817 foreach f [split [$top.t get 0.0 end] "\n"] {
1818 set ft [string trim $f]
1823 if {![info exists viewfiles($n)]} {
1824 # creating a new view
1826 set viewname($n) $newviewname($n)
1827 set viewperm($n) $newviewperm($n)
1828 set viewfiles($n) $files
1829 set viewargs($n) $newargs
1831 if {!$newishighlight} {
1834 run addvhighlight $n
1837 # editing an existing view
1838 set viewperm($n) $newviewperm($n)
1839 if {$newviewname($n) ne $viewname($n)} {
1840 set viewname($n) $newviewname($n)
1841 doviewmenu .bar.view 5 [list showview $n] \
1842 entryconf [list -label $viewname($n)]
1843 doviewmenu $viewhlmenu 1 [list addvhighlight $n] \
1844 entryconf [list -label $viewname($n) -value $viewname($n)]
1846 if {$files ne $viewfiles($n) || $newargs ne $viewargs($n)} {
1847 set viewfiles($n) $files
1848 set viewargs($n) $newargs
1849 if {$curview == $n} {
1854 catch {destroy $top}
1858 global curview viewdata viewperm hlview selectedhlview
1860 if {$curview == 0} return
1861 if {[info exists hlview] && $hlview == $curview} {
1862 set selectedhlview None
1865 allviewmenus $curview delete
1866 set viewdata($curview) {}
1867 set viewperm($curview) 0
1871 proc addviewmenu {n} {
1872 global viewname viewhlmenu
1874 .bar.view add radiobutton -label $viewname($n) \
1875 -command [list showview $n] -variable selectedview -value $n
1876 $viewhlmenu add radiobutton -label $viewname($n) \
1877 -command [list addvhighlight $n] -variable selectedhlview
1880 proc flatten {var} {
1884 foreach i [array names $var] {
1885 lappend ret $i [set $var\($i\)]
1890 proc unflatten {var l} {
1900 global curview viewdata viewfiles
1901 global displayorder parentlist rowidlist rowoffsets
1902 global colormap rowtextx commitrow nextcolor canvxmax
1903 global numcommits rowrangelist commitlisted idrowranges rowchk
1904 global selectedline currentid canv canvy0
1906 global pending_select phase
1907 global commitidx rowlaidout rowoptim
1909 global selectedview selectfirst
1910 global vparentlist vdisporder vcmitlisted
1911 global hlview selectedhlview
1913 if {$n == $curview} return
1915 if {[info exists selectedline]} {
1916 set selid $currentid
1917 set y [yc $selectedline]
1918 set ymax [lindex [$canv cget -scrollregion] 3]
1919 set span [$canv yview]
1920 set ytop [expr {[lindex $span 0] * $ymax}]
1921 set ybot [expr {[lindex $span 1] * $ymax}]
1922 if {$ytop < $y && $y < $ybot} {
1923 set yscreen [expr {$y - $ytop}]
1925 set yscreen [expr {($ybot - $ytop) / 2}]
1927 } elseif {[info exists pending_select]} {
1928 set selid $pending_select
1929 unset pending_select
1933 if {$curview >= 0} {
1934 set vparentlist($curview) $parentlist
1935 set vdisporder($curview) $displayorder
1936 set vcmitlisted($curview) $commitlisted
1938 set viewdata($curview) \
1939 [list $phase $rowidlist $rowoffsets $rowrangelist \
1940 [flatten idrowranges] [flatten idinlist] \
1941 $rowlaidout $rowoptim $numcommits]
1942 } elseif {![info exists viewdata($curview)]
1943 || [lindex $viewdata($curview) 0] ne {}} {
1944 set viewdata($curview) \
1945 [list {} $rowidlist $rowoffsets $rowrangelist]
1948 catch {unset treediffs}
1950 if {[info exists hlview] && $hlview == $n} {
1952 set selectedhlview None
1957 .bar.view entryconf Edit* -state [expr {$n == 0? "disabled": "normal"}]
1958 .bar.view entryconf Delete* -state [expr {$n == 0? "disabled": "normal"}]
1960 if {![info exists viewdata($n)]} {
1962 set pending_select $selid
1969 set phase [lindex $v 0]
1970 set displayorder $vdisporder($n)
1971 set parentlist $vparentlist($n)
1972 set commitlisted $vcmitlisted($n)
1973 set rowidlist [lindex $v 1]
1974 set rowoffsets [lindex $v 2]
1975 set rowrangelist [lindex $v 3]
1977 set numcommits [llength $displayorder]
1978 catch {unset idrowranges}
1980 unflatten idrowranges [lindex $v 4]
1981 unflatten idinlist [lindex $v 5]
1982 set rowlaidout [lindex $v 6]
1983 set rowoptim [lindex $v 7]
1984 set numcommits [lindex $v 8]
1985 catch {unset rowchk}
1988 catch {unset colormap}
1989 catch {unset rowtextx}
1991 set canvxmax [$canv cget -width]
1998 if {$selid ne {} && [info exists commitrow($n,$selid)]} {
1999 set row $commitrow($n,$selid)
2000 # try to get the selected row in the same position on the screen
2001 set ymax [lindex [$canv cget -scrollregion] 3]
2002 set ytop [expr {[yc $row] - $yscreen}]
2006 set yf [expr {$ytop * 1.0 / $ymax}]
2008 allcanvs yview moveto $yf
2012 } elseif {$selid ne {}} {
2013 set pending_select $selid
2015 set row [first_real_row]
2016 if {$row < $numcommits} {
2023 if {$phase eq "getcommits"} {
2024 show_status "Reading commits..."
2027 } elseif {$numcommits == 0} {
2028 show_status "No commits selected"
2033 # Stuff relating to the highlighting facility
2035 proc ishighlighted {row} {
2036 global vhighlights fhighlights nhighlights rhighlights
2038 if {[info exists nhighlights($row)] && $nhighlights($row) > 0} {
2039 return $nhighlights($row)
2041 if {[info exists vhighlights($row)] && $vhighlights($row) > 0} {
2042 return $vhighlights($row)
2044 if {[info exists fhighlights($row)] && $fhighlights($row) > 0} {
2045 return $fhighlights($row)
2047 if {[info exists rhighlights($row)] && $rhighlights($row) > 0} {
2048 return $rhighlights($row)
2053 proc bolden {row font} {
2054 global canv linehtag selectedline boldrows
2056 lappend boldrows $row
2057 $canv itemconf $linehtag($row) -font $font
2058 if {[info exists selectedline] && $row == $selectedline} {
2060 set t [eval $canv create rect [$canv bbox $linehtag($row)] \
2061 -outline {{}} -tags secsel \
2062 -fill [$canv cget -selectbackground]]
2067 proc bolden_name {row font} {
2068 global canv2 linentag selectedline boldnamerows
2070 lappend boldnamerows $row
2071 $canv2 itemconf $linentag($row) -font $font
2072 if {[info exists selectedline] && $row == $selectedline} {
2073 $canv2 delete secsel
2074 set t [eval $canv2 create rect [$canv2 bbox $linentag($row)] \
2075 -outline {{}} -tags secsel \
2076 -fill [$canv2 cget -selectbackground]]
2082 global mainfont boldrows
2085 foreach row $boldrows {
2086 if {![ishighlighted $row]} {
2087 bolden $row $mainfont
2089 lappend stillbold $row
2092 set boldrows $stillbold
2095 proc addvhighlight {n} {
2096 global hlview curview viewdata vhl_done vhighlights commitidx
2098 if {[info exists hlview]} {
2102 if {$n != $curview && ![info exists viewdata($n)]} {
2103 set viewdata($n) [list getcommits {{}} {{}} {} {} {} 0 0 0 {}]
2104 set vparentlist($n) {}
2105 set vdisporder($n) {}
2106 set vcmitlisted($n) {}
2109 set vhl_done $commitidx($hlview)
2110 if {$vhl_done > 0} {
2115 proc delvhighlight {} {
2116 global hlview vhighlights
2118 if {![info exists hlview]} return
2120 catch {unset vhighlights}
2124 proc vhighlightmore {} {
2125 global hlview vhl_done commitidx vhighlights
2126 global displayorder vdisporder curview mainfont
2128 set font [concat $mainfont bold]
2129 set max $commitidx($hlview)
2130 if {$hlview == $curview} {
2131 set disp $displayorder
2133 set disp $vdisporder($hlview)
2135 set vr [visiblerows]
2136 set r0 [lindex $vr 0]
2137 set r1 [lindex $vr 1]
2138 for {set i $vhl_done} {$i < $max} {incr i} {
2139 set id [lindex $disp $i]
2140 if {[info exists commitrow($curview,$id)]} {
2141 set row $commitrow($curview,$id)
2142 if {$r0 <= $row && $row <= $r1} {
2143 if {![highlighted $row]} {
2146 set vhighlights($row) 1
2153 proc askvhighlight {row id} {
2154 global hlview vhighlights commitrow iddrawn mainfont
2156 if {[info exists commitrow($hlview,$id)]} {
2157 if {[info exists iddrawn($id)] && ![ishighlighted $row]} {
2158 bolden $row [concat $mainfont bold]
2160 set vhighlights($row) 1
2162 set vhighlights($row) 0
2166 proc hfiles_change {name ix op} {
2167 global highlight_files filehighlight fhighlights fh_serial
2168 global mainfont highlight_paths
2170 if {[info exists filehighlight]} {
2171 # delete previous highlights
2172 catch {close $filehighlight}
2174 catch {unset fhighlights}
2176 unhighlight_filelist
2178 set highlight_paths {}
2179 after cancel do_file_hl $fh_serial
2181 if {$highlight_files ne {}} {
2182 after 300 do_file_hl $fh_serial
2186 proc makepatterns {l} {
2189 set ee [string map {"*" "\\*" "?" "\\?" "\[" "\\\[" "\\" "\\\\"} $e]
2190 if {[string index $ee end] eq "/"} {
2200 proc do_file_hl {serial} {
2201 global highlight_files filehighlight highlight_paths gdttype fhl_list
2203 if {$gdttype eq "touching paths:"} {
2204 if {[catch {set paths [shellsplit $highlight_files]}]} return
2205 set highlight_paths [makepatterns $paths]
2207 set gdtargs [concat -- $paths]
2209 set gdtargs [list "-S$highlight_files"]
2211 set cmd [concat | git diff-tree -r -s --stdin $gdtargs]
2212 set filehighlight [open $cmd r+]
2213 fconfigure $filehighlight -blocking 0
2214 filerun $filehighlight readfhighlight
2220 proc flushhighlights {} {
2221 global filehighlight fhl_list
2223 if {[info exists filehighlight]} {
2225 puts $filehighlight ""
2226 flush $filehighlight
2230 proc askfilehighlight {row id} {
2231 global filehighlight fhighlights fhl_list
2233 lappend fhl_list $id
2234 set fhighlights($row) -1
2235 puts $filehighlight $id
2238 proc readfhighlight {} {
2239 global filehighlight fhighlights commitrow curview mainfont iddrawn
2242 if {![info exists filehighlight]} {
2246 while {[incr nr] <= 100 && [gets $filehighlight line] >= 0} {
2247 set line [string trim $line]
2248 set i [lsearch -exact $fhl_list $line]
2249 if {$i < 0} continue
2250 for {set j 0} {$j < $i} {incr j} {
2251 set id [lindex $fhl_list $j]
2252 if {[info exists commitrow($curview,$id)]} {
2253 set fhighlights($commitrow($curview,$id)) 0
2256 set fhl_list [lrange $fhl_list [expr {$i+1}] end]
2257 if {$line eq {}} continue
2258 if {![info exists commitrow($curview,$line)]} continue
2259 set row $commitrow($curview,$line)
2260 if {[info exists iddrawn($line)] && ![ishighlighted $row]} {
2261 bolden $row [concat $mainfont bold]
2263 set fhighlights($row) 1
2265 if {[eof $filehighlight]} {
2267 puts "oops, git diff-tree died"
2268 catch {close $filehighlight}
2276 proc find_change {name ix op} {
2277 global nhighlights mainfont boldnamerows
2278 global findstring findpattern findtype
2280 # delete previous highlights, if any
2281 foreach row $boldnamerows {
2282 bolden_name $row $mainfont
2285 catch {unset nhighlights}
2288 if {$findtype ne "Regexp"} {
2289 set e [string map {"*" "\\*" "?" "\\?" "\[" "\\\[" "\\" "\\\\"} \
2291 set findpattern "*$e*"
2296 proc doesmatch {f} {
2297 global findtype findstring findpattern
2299 if {$findtype eq "Regexp"} {
2300 return [regexp $findstring $f]
2301 } elseif {$findtype eq "IgnCase"} {
2302 return [string match -nocase $findpattern $f]
2304 return [string match $findpattern $f]
2308 proc askfindhighlight {row id} {
2309 global nhighlights commitinfo iddrawn mainfont
2311 global markingmatches
2313 if {![info exists commitinfo($id)]} {
2316 set info $commitinfo($id)
2318 set fldtypes {Headline Author Date Committer CDate Comments}
2319 foreach f $info ty $fldtypes {
2320 if {($findloc eq "All fields" || $findloc eq $ty) &&
2322 if {$ty eq "Author"} {
2329 if {$isbold && [info exists iddrawn($id)]} {
2330 set f [concat $mainfont bold]
2331 if {![ishighlighted $row]} {
2337 if {$markingmatches} {
2338 markrowmatches $row $id
2341 set nhighlights($row) $isbold
2344 proc markrowmatches {row id} {
2345 global canv canv2 linehtag linentag commitinfo findloc
2347 set headline [lindex $commitinfo($id) 0]
2348 set author [lindex $commitinfo($id) 1]
2349 $canv delete match$row
2350 $canv2 delete match$row
2351 if {$findloc eq "All fields" || $findloc eq "Headline"} {
2352 set m [findmatches $headline]
2354 markmatches $canv $row $headline $linehtag($row) $m \
2355 [$canv itemcget $linehtag($row) -font] $row
2358 if {$findloc eq "All fields" || $findloc eq "Author"} {
2359 set m [findmatches $author]
2361 markmatches $canv2 $row $author $linentag($row) $m \
2362 [$canv2 itemcget $linentag($row) -font] $row
2367 proc vrel_change {name ix op} {
2368 global highlight_related
2371 if {$highlight_related ne "None"} {
2376 # prepare for testing whether commits are descendents or ancestors of a
2377 proc rhighlight_sel {a} {
2378 global descendent desc_todo ancestor anc_todo
2379 global highlight_related rhighlights
2381 catch {unset descendent}
2382 set desc_todo [list $a]
2383 catch {unset ancestor}
2384 set anc_todo [list $a]
2385 if {$highlight_related ne "None"} {
2391 proc rhighlight_none {} {
2394 catch {unset rhighlights}
2398 proc is_descendent {a} {
2399 global curview children commitrow descendent desc_todo
2402 set la $commitrow($v,$a)
2406 for {set i 0} {$i < [llength $todo]} {incr i} {
2407 set do [lindex $todo $i]
2408 if {$commitrow($v,$do) < $la} {
2409 lappend leftover $do
2412 foreach nk $children($v,$do) {
2413 if {![info exists descendent($nk)]} {
2414 set descendent($nk) 1
2422 set desc_todo [concat $leftover [lrange $todo [expr {$i+1}] end]]
2426 set descendent($a) 0
2427 set desc_todo $leftover
2430 proc is_ancestor {a} {
2431 global curview parentlist commitrow ancestor anc_todo
2434 set la $commitrow($v,$a)
2438 for {set i 0} {$i < [llength $todo]} {incr i} {
2439 set do [lindex $todo $i]
2440 if {![info exists commitrow($v,$do)] || $commitrow($v,$do) > $la} {
2441 lappend leftover $do
2444 foreach np [lindex $parentlist $commitrow($v,$do)] {
2445 if {![info exists ancestor($np)]} {
2454 set anc_todo [concat $leftover [lrange $todo [expr {$i+1}] end]]
2459 set anc_todo $leftover
2462 proc askrelhighlight {row id} {
2463 global descendent highlight_related iddrawn mainfont rhighlights
2464 global selectedline ancestor
2466 if {![info exists selectedline]} return
2468 if {$highlight_related eq "Descendent" ||
2469 $highlight_related eq "Not descendent"} {
2470 if {![info exists descendent($id)]} {
2473 if {$descendent($id) == ($highlight_related eq "Descendent")} {
2476 } elseif {$highlight_related eq "Ancestor" ||
2477 $highlight_related eq "Not ancestor"} {
2478 if {![info exists ancestor($id)]} {
2481 if {$ancestor($id) == ($highlight_related eq "Ancestor")} {
2485 if {[info exists iddrawn($id)]} {
2486 if {$isbold && ![ishighlighted $row]} {
2487 bolden $row [concat $mainfont bold]
2490 set rhighlights($row) $isbold
2493 proc next_hlcont {} {
2494 global fhl_row fhl_dirn displayorder numcommits
2495 global vhighlights fhighlights nhighlights rhighlights
2496 global hlview filehighlight findstring highlight_related
2498 if {![info exists fhl_dirn] || $fhl_dirn == 0} return
2501 if {$row < 0 || $row >= $numcommits} {
2506 set id [lindex $displayorder $row]
2507 if {[info exists hlview]} {
2508 if {![info exists vhighlights($row)]} {
2509 askvhighlight $row $id
2511 if {$vhighlights($row) > 0} break
2513 if {$findstring ne {}} {
2514 if {![info exists nhighlights($row)]} {
2515 askfindhighlight $row $id
2517 if {$nhighlights($row) > 0} break
2519 if {$highlight_related ne "None"} {
2520 if {![info exists rhighlights($row)]} {
2521 askrelhighlight $row $id
2523 if {$rhighlights($row) > 0} break
2525 if {[info exists filehighlight]} {
2526 if {![info exists fhighlights($row)]} {
2527 # ask for a few more while we're at it...
2529 for {set n 0} {$n < 100} {incr n} {
2530 if {![info exists fhighlights($r)]} {
2531 askfilehighlight $r [lindex $displayorder $r]
2534 if {$r < 0 || $r >= $numcommits} break
2538 if {$fhighlights($row) < 0} {
2542 if {$fhighlights($row) > 0} break
2550 proc next_highlight {dirn} {
2551 global selectedline fhl_row fhl_dirn
2552 global hlview filehighlight findstring highlight_related
2554 if {![info exists selectedline]} return
2555 if {!([info exists hlview] || $findstring ne {} ||
2556 $highlight_related ne "None" || [info exists filehighlight])} return
2557 set fhl_row [expr {$selectedline + $dirn}]
2562 proc cancel_next_highlight {} {
2568 # Graph layout functions
2570 proc shortids {ids} {
2573 if {[llength $id] > 1} {
2574 lappend res [shortids $id]
2575 } elseif {[regexp {^[0-9a-f]{40}$} $id]} {
2576 lappend res [string range $id 0 7]
2584 proc incrange {l x o} {
2587 set e [lindex $l $x]
2589 lset l $x [expr {$e + $o}]
2598 for {} {$n > 0} {incr n -1} {
2604 proc usedinrange {id l1 l2} {
2605 global children commitrow curview
2607 if {[info exists commitrow($curview,$id)]} {
2608 set r $commitrow($curview,$id)
2609 if {$l1 <= $r && $r <= $l2} {
2610 return [expr {$r - $l1 + 1}]
2613 set kids $children($curview,$id)
2615 set r $commitrow($curview,$c)
2616 if {$l1 <= $r && $r <= $l2} {
2617 return [expr {$r - $l1 + 1}]
2623 proc sanity {row {full 0}} {
2624 global rowidlist rowoffsets
2627 set ids [lindex $rowidlist $row]
2630 if {$id eq {}} continue
2631 if {$col < [llength $ids] - 1 &&
2632 [lsearch -exact -start [expr {$col+1}] $ids $id] >= 0} {
2633 puts "oops: [shortids $id] repeated in row $row col $col: {[shortids [lindex $rowidlist $row]]}"
2635 set o [lindex $rowoffsets $row $col]
2641 if {[lindex $rowidlist $y $x] != $id} {
2642 puts "oops: rowoffsets wrong at row [expr {$y+1}] col [expr {$x-$o}]"
2643 puts " id=[shortids $id] check started at row $row"
2644 for {set i $row} {$i >= $y} {incr i -1} {
2645 puts " row $i ids={[shortids [lindex $rowidlist $i]]} offs={[lindex $rowoffsets $i]}"
2650 set o [lindex $rowoffsets $y $x]
2655 proc makeuparrow {oid x y z} {
2656 global rowidlist rowoffsets uparrowlen idrowranges displayorder
2658 for {set i 1} {$i < $uparrowlen && $y > 1} {incr i} {
2661 set off0 [lindex $rowoffsets $y]
2662 for {set x0 $x} {1} {incr x0} {
2663 if {$x0 >= [llength $off0]} {
2664 set x0 [llength [lindex $rowoffsets [expr {$y-1}]]]
2667 set z [lindex $off0 $x0]
2673 set z [expr {$x0 - $x}]
2674 lset rowidlist $y [linsert [lindex $rowidlist $y] $x $oid]
2675 lset rowoffsets $y [linsert [lindex $rowoffsets $y] $x $z]
2677 set tmp [lreplace [lindex $rowoffsets $y] $x $x {}]
2678 lset rowoffsets $y [incrange $tmp [expr {$x+1}] -1]
2679 lappend idrowranges($oid) [lindex $displayorder $y]
2682 proc initlayout {} {
2683 global rowidlist rowoffsets displayorder commitlisted
2684 global rowlaidout rowoptim
2685 global idinlist rowchk rowrangelist idrowranges
2686 global numcommits canvxmax canv
2689 global colormap rowtextx
2700 catch {unset idinlist}
2701 catch {unset rowchk}
2704 set canvxmax [$canv cget -width]
2705 catch {unset colormap}
2706 catch {unset rowtextx}
2707 catch {unset idrowranges}
2711 proc setcanvscroll {} {
2712 global canv canv2 canv3 numcommits linespc canvxmax canvy0
2714 set ymax [expr {$canvy0 + ($numcommits - 0.5) * $linespc + 2}]
2715 $canv conf -scrollregion [list 0 0 $canvxmax $ymax]
2716 $canv2 conf -scrollregion [list 0 0 0 $ymax]
2717 $canv3 conf -scrollregion [list 0 0 0 $ymax]
2720 proc visiblerows {} {
2721 global canv numcommits linespc
2723 set ymax [lindex [$canv cget -scrollregion] 3]
2724 if {$ymax eq {} || $ymax == 0} return
2726 set y0 [expr {int([lindex $f 0] * $ymax)}]
2727 set r0 [expr {int(($y0 - 3) / $linespc) - 1}]
2731 set y1 [expr {int([lindex $f 1] * $ymax)}]
2732 set r1 [expr {int(($y1 - 3) / $linespc) + 1}]
2733 if {$r1 >= $numcommits} {
2734 set r1 [expr {$numcommits - 1}]
2736 return [list $r0 $r1]
2739 proc layoutmore {tmax allread} {
2740 global rowlaidout rowoptim commitidx numcommits optim_delay
2741 global uparrowlen curview rowidlist idinlist
2744 set showdelay $optim_delay
2745 set optdelay [expr {$uparrowlen + 1}]
2747 if {$rowoptim - $showdelay > $numcommits} {
2748 showstuff [expr {$rowoptim - $showdelay}] $showlast
2749 } elseif {$rowlaidout - $optdelay > $rowoptim} {
2750 set nr [expr {$rowlaidout - $optdelay - $rowoptim}]
2754 optimize_rows $rowoptim 0 [expr {$rowoptim + $nr}]
2756 } elseif {$commitidx($curview) > $rowlaidout} {
2757 set nr [expr {$commitidx($curview) - $rowlaidout}]
2758 # may need to increase this threshold if uparrowlen or
2759 # mingaplen are increased...
2764 set rowlaidout [layoutrows $row [expr {$row + $nr}] $allread]
2765 if {$rowlaidout == $row} {
2768 } elseif {$allread} {
2770 set nrows $commitidx($curview)
2771 if {[lindex $rowidlist $nrows] ne {} ||
2772 [array names idinlist] ne {}} {
2774 set rowlaidout $commitidx($curview)
2775 } elseif {$rowoptim == $nrows} {
2778 if {$numcommits == $nrows} {
2785 if {$tmax ne {} && [clock clicks -milliseconds] >= $tmax} {
2791 proc showstuff {canshow last} {
2792 global numcommits commitrow pending_select selectedline curview
2793 global lookingforhead mainheadid displayorder selectfirst
2794 global lastscrollset commitinterest
2796 if {$numcommits == 0} {
2798 set phase "incrdraw"
2801 for {set l $numcommits} {$l < $canshow} {incr l} {
2802 set id [lindex $displayorder $l]
2803 if {[info exists commitinterest($id)]} {
2804 foreach script $commitinterest($id) {
2805 eval [string map [list "%I" $id] $script]
2807 unset commitinterest($id)
2811 set prev $numcommits
2812 set numcommits $canshow
2813 set t [clock clicks -milliseconds]
2814 if {$prev < 100 || $last || $t - $lastscrollset > 500} {
2815 set lastscrollset $t
2818 set rows [visiblerows]
2819 set r1 [lindex $rows 1]
2820 if {$r1 >= $canshow} {
2821 set r1 [expr {$canshow - 1}]
2826 if {[info exists pending_select] &&
2827 [info exists commitrow($curview,$pending_select)] &&
2828 $commitrow($curview,$pending_select) < $numcommits} {
2829 selectline $commitrow($curview,$pending_select) 1
2832 if {[info exists selectedline] || [info exists pending_select]} {
2835 set l [first_real_row]
2840 if {$lookingforhead && [info exists commitrow($curview,$mainheadid)]
2841 && ($last || $commitrow($curview,$mainheadid) < $numcommits - 1)} {
2842 set lookingforhead 0
2847 proc doshowlocalchanges {} {
2848 global lookingforhead curview mainheadid phase commitrow
2850 if {[info exists commitrow($curview,$mainheadid)] &&
2851 ($phase eq {} || $commitrow($curview,$mainheadid) < $numcommits - 1)} {
2853 } elseif {$phase ne {}} {
2854 set lookingforhead 1
2858 proc dohidelocalchanges {} {
2859 global lookingforhead localfrow localirow lserial
2861 set lookingforhead 0
2862 if {$localfrow >= 0} {
2863 removerow $localfrow
2865 if {$localirow > 0} {
2869 if {$localirow >= 0} {
2870 removerow $localirow
2876 # spawn off a process to do git diff-index --cached HEAD
2877 proc dodiffindex {} {
2878 global localirow localfrow lserial
2883 set fd [open "|git diff-index --cached HEAD" r]
2884 fconfigure $fd -blocking 0
2885 filerun $fd [list readdiffindex $fd $lserial]
2888 proc readdiffindex {fd serial} {
2889 global localirow commitrow mainheadid nullid2 curview
2890 global commitinfo commitdata lserial
2893 if {[gets $fd line] < 0} {
2899 # we only need to see one line and we don't really care what it says...
2902 # now see if there are any local changes not checked in to the index
2903 if {$serial == $lserial} {
2904 set fd [open "|git diff-files" r]
2905 fconfigure $fd -blocking 0
2906 filerun $fd [list readdifffiles $fd $serial]
2909 if {$isdiff && $serial == $lserial && $localirow == -1} {
2910 # add the line for the changes in the index to the graph
2911 set localirow $commitrow($curview,$mainheadid)
2912 set hl "Local changes checked in to index but not committed"
2913 set commitinfo($nullid2) [list $hl {} {} {} {} " $hl\n"]
2914 set commitdata($nullid2) "\n $hl\n"
2915 insertrow $localirow $nullid2
2920 proc readdifffiles {fd serial} {
2921 global localirow localfrow commitrow mainheadid nullid curview
2922 global commitinfo commitdata lserial
2925 if {[gets $fd line] < 0} {
2931 # we only need to see one line and we don't really care what it says...
2934 if {$isdiff && $serial == $lserial && $localfrow == -1} {
2935 # add the line for the local diff to the graph
2936 if {$localirow >= 0} {
2937 set localfrow $localirow
2940 set localfrow $commitrow($curview,$mainheadid)
2942 set hl "Local uncommitted changes, not checked in to index"
2943 set commitinfo($nullid) [list $hl {} {} {} {} " $hl\n"]
2944 set commitdata($nullid) "\n $hl\n"
2945 insertrow $localfrow $nullid
2950 proc layoutrows {row endrow last} {
2951 global rowidlist rowoffsets displayorder
2952 global uparrowlen downarrowlen maxwidth mingaplen
2953 global children parentlist
2955 global commitidx curview
2956 global idinlist rowchk rowrangelist
2958 set idlist [lindex $rowidlist $row]
2959 set offs [lindex $rowoffsets $row]
2960 while {$row < $endrow} {
2961 set id [lindex $displayorder $row]
2962 set nev [expr {[llength $idlist] - $maxwidth + 1}]
2963 foreach p [lindex $parentlist $row] {
2964 if {![info exists idinlist($p)] || !$idinlist($p)} {
2970 $row + $uparrowlen + $mingaplen >= $commitidx($curview)} break
2971 for {set x [llength $idlist]} {[incr x -1] >= 0} {} {
2972 set i [lindex $idlist $x]
2973 if {![info exists rowchk($i)] || $row >= $rowchk($i)} {
2974 set r [usedinrange $i [expr {$row - $downarrowlen}] \
2975 [expr {$row + $uparrowlen + $mingaplen}]]
2977 set idlist [lreplace $idlist $x $x]
2978 set offs [lreplace $offs $x $x]
2979 set offs [incrange $offs $x 1]
2981 set rm1 [expr {$row - 1}]
2982 lappend idrowranges($i) [lindex $displayorder $rm1]
2983 if {[incr nev -1] <= 0} break
2986 set rowchk($i) [expr {$row + $r}]
2989 lset rowidlist $row $idlist
2990 lset rowoffsets $row $offs
2994 foreach p [lindex $parentlist $row] {
2995 if {![info exists idinlist($p)]} {
2997 } elseif {!$idinlist($p)} {
3002 set col [lsearch -exact $idlist $id]
3004 set col [llength $idlist]
3006 lset rowidlist $row $idlist
3008 if {$children($curview,$id) ne {}} {
3009 set z [expr {[llength [lindex $rowidlist [expr {$row-1}]]] - $col}]
3013 lset rowoffsets $row $offs
3015 makeuparrow $id $col $row $z
3021 if {[info exists idrowranges($id)]} {
3022 set ranges $idrowranges($id)
3024 unset idrowranges($id)
3026 lappend rowrangelist $ranges
3028 set offs [ntimes [llength $idlist] 0]
3029 set l [llength $newolds]
3030 set idlist [eval lreplace \$idlist $col $col $newolds]
3033 set offs [lrange $offs 0 [expr {$col - 1}]]
3034 foreach x $newolds {
3039 set tmp [expr {[llength $idlist] - [llength $offs]}]
3041 set offs [concat $offs [ntimes $tmp $o]]
3046 foreach i $newolds {
3047 set idrowranges($i) $id
3050 foreach oid $oldolds {
3051 set idlist [linsert $idlist $col $oid]
3052 set offs [linsert $offs $col $o]
3053 makeuparrow $oid $col $row $o
3056 lappend rowidlist $idlist
3057 lappend rowoffsets $offs
3062 proc addextraid {id row} {
3063 global displayorder commitrow commitinfo
3064 global commitidx commitlisted
3065 global parentlist children curview
3067 incr commitidx($curview)
3068 lappend displayorder $id
3069 lappend commitlisted 0
3070 lappend parentlist {}
3071 set commitrow($curview,$id) $row
3073 if {![info exists commitinfo($id)]} {
3074 set commitinfo($id) {"No commit information available"}
3076 if {![info exists children($curview,$id)]} {
3077 set children($curview,$id) {}
3081 proc layouttail {} {
3082 global rowidlist rowoffsets idinlist commitidx curview
3083 global idrowranges rowrangelist
3085 set row $commitidx($curview)
3086 set idlist [lindex $rowidlist $row]
3087 while {$idlist ne {}} {
3088 set col [expr {[llength $idlist] - 1}]
3089 set id [lindex $idlist $col]
3091 catch {unset idinlist($id)}
3092 lappend idrowranges($id) $id
3093 lappend rowrangelist $idrowranges($id)
3094 unset idrowranges($id)
3096 set offs [ntimes $col 0]
3097 set idlist [lreplace $idlist $col $col]
3098 lappend rowidlist $idlist
3099 lappend rowoffsets $offs
3102 foreach id [array names idinlist] {
3105 lset rowidlist $row [list $id]
3106 lset rowoffsets $row 0
3107 makeuparrow $id 0 $row 0
3108 lappend idrowranges($id) $id
3109 lappend rowrangelist $idrowranges($id)
3110 unset idrowranges($id)
3112 lappend rowidlist {}
3113 lappend rowoffsets {}
3117 proc insert_pad {row col npad} {
3118 global rowidlist rowoffsets
3120 set pad [ntimes $npad {}]
3121 lset rowidlist $row [eval linsert [list [lindex $rowidlist $row]] $col $pad]
3122 set tmp [eval linsert [list [lindex $rowoffsets $row]] $col $pad]
3123 lset rowoffsets $row [incrange $tmp [expr {$col + $npad}] [expr {-$npad}]]
3126 proc optimize_rows {row col endrow} {
3127 global rowidlist rowoffsets displayorder
3129 for {} {$row < $endrow} {incr row} {
3130 set idlist [lindex $rowidlist $row]
3131 set offs [lindex $rowoffsets $row]
3133 for {} {$col < [llength $offs]} {incr col} {
3134 if {[lindex $idlist $col] eq {}} {
3138 set z [lindex $offs $col]
3139 if {$z eq {}} continue
3141 set x0 [expr {$col + $z}]
3142 set y0 [expr {$row - 1}]
3143 set z0 [lindex $rowoffsets $y0 $x0]
3145 set id [lindex $idlist $col]
3146 set ranges [rowranges $id]
3147 if {$ranges ne {} && $y0 > [lindex $ranges 0]} {
3151 # Looking at lines from this row to the previous row,
3152 # make them go straight up if they end in an arrow on
3153 # the previous row; otherwise make them go straight up
3155 if {$z < -1 || ($z < 0 && $isarrow)} {
3156 # Line currently goes left too much;
3157 # insert pads in the previous row, then optimize it
3158 set npad [expr {-1 - $z + $isarrow}]
3159 set offs [incrange $offs $col $npad]
3160 insert_pad $y0 $x0 $npad
3162 optimize_rows $y0 $x0 $row
3164 set z [lindex $offs $col]
3165 set x0 [expr {$col + $z}]
3166 set z0 [lindex $rowoffsets $y0 $x0]
3167 } elseif {$z > 1 || ($z > 0 && $isarrow)} {
3168 # Line currently goes right too much;
3169 # insert pads in this line and adjust the next's rowoffsets
3170 set npad [expr {$z - 1 + $isarrow}]
3171 set y1 [expr {$row + 1}]
3172 set offs2 [lindex $rowoffsets $y1]
3176 if {$z eq {} || $x1 + $z < $col} continue
3177 if {$x1 + $z > $col} {
3180 lset rowoffsets $y1 [incrange $offs2 $x1 $npad]
3183 set pad [ntimes $npad {}]
3184 set idlist [eval linsert \$idlist $col $pad]
3185 set tmp [eval linsert \$offs $col $pad]
3187 set offs [incrange $tmp $col [expr {-$npad}]]
3188 set z [lindex $offs $col]
3191 if {$z0 eq {} && !$isarrow} {
3192 # this line links to its first child on row $row-2
3193 set rm2 [expr {$row - 2}]
3194 set id [lindex $displayorder $rm2]
3195 set xc [lsearch -exact [lindex $rowidlist $rm2] $id]
3197 set z0 [expr {$xc - $x0}]
3200 # avoid lines jigging left then immediately right
3201 if {$z0 ne {} && $z < 0 && $z0 > 0} {
3202 insert_pad $y0 $x0 1
3203 set offs [incrange $offs $col 1]
3204 optimize_rows $y0 [expr {$x0 + 1}] $row
3209 # Find the first column that doesn't have a line going right
3210 for {set col [llength $idlist]} {[incr col -1] >= 0} {} {
3211 set o [lindex $offs $col]
3213 # check if this is the link to the first child
3214 set id [lindex $idlist $col]
3215 set ranges [rowranges $id]
3216 if {$ranges ne {} && $row == [lindex $ranges 0]} {
3217 # it is, work out offset to child
3218 set y0 [expr {$row - 1}]
3219 set id [lindex $displayorder $y0]
3220 set x0 [lsearch -exact [lindex $rowidlist $y0] $id]
3222 set o [expr {$x0 - $col}]
3226 if {$o eq {} || $o <= 0} break
3228 # Insert a pad at that column as long as it has a line and
3229 # isn't the last column, and adjust the next row' offsets
3230 if {$o ne {} && [incr col] < [llength $idlist]} {
3231 set y1 [expr {$row + 1}]
3232 set offs2 [lindex $rowoffsets $y1]
3236 if {$z eq {} || $x1 + $z < $col} continue
3237 lset rowoffsets $y1 [incrange $offs2 $x1 1]
3240 set idlist [linsert $idlist $col {}]
3241 set tmp [linsert $offs $col {}]
3243 set offs [incrange $tmp $col -1]
3246 lset rowidlist $row $idlist
3247 lset rowoffsets $row $offs
3253 global canvx0 linespc
3254 return [expr {$canvx0 + $col * $linespc}]
3258 global canvy0 linespc
3259 return [expr {$canvy0 + $row * $linespc}]
3262 proc linewidth {id} {
3263 global thickerline lthickness
3266 if {[info exists thickerline] && $id eq $thickerline} {
3267 set wid [expr {2 * $lthickness}]
3272 proc rowranges {id} {
3273 global phase idrowranges commitrow rowlaidout rowrangelist curview
3277 ([info exists commitrow($curview,$id)]
3278 && $commitrow($curview,$id) < $rowlaidout)} {
3279 set ranges [lindex $rowrangelist $commitrow($curview,$id)]
3280 } elseif {[info exists idrowranges($id)]} {
3281 set ranges $idrowranges($id)
3284 foreach rid $ranges {
3285 lappend linenos $commitrow($curview,$rid)
3287 if {$linenos ne {}} {
3288 lset linenos 0 [expr {[lindex $linenos 0] + 1}]
3293 # work around tk8.4 refusal to draw arrows on diagonal segments
3294 proc adjarrowhigh {coords} {
3297 set x0 [lindex $coords 0]
3298 set x1 [lindex $coords 2]
3300 set y0 [lindex $coords 1]
3301 set y1 [lindex $coords 3]
3302 if {$y0 - $y1 <= 2 * $linespc && $x1 == [lindex $coords 4]} {
3303 # we have a nearby vertical segment, just trim off the diag bit
3304 set coords [lrange $coords 2 end]
3306 set slope [expr {($x0 - $x1) / ($y0 - $y1)}]
3307 set xi [expr {$x0 - $slope * $linespc / 2}]
3308 set yi [expr {$y0 - $linespc / 2}]
3309 set coords [lreplace $coords 0 1 $xi $y0 $xi $yi]
3315 proc drawlineseg {id row endrow arrowlow} {
3316 global rowidlist displayorder iddrawn linesegs
3317 global canv colormap linespc curview maxlinelen
3319 set cols [list [lsearch -exact [lindex $rowidlist $row] $id]]
3320 set le [expr {$row + 1}]
3323 set c [lsearch -exact [lindex $rowidlist $le] $id]
3329 set x [lindex $displayorder $le]
3334 if {[info exists iddrawn($x)] || $le == $endrow} {
3335 set c [lsearch -exact [lindex $rowidlist [expr {$le+1}]] $id]
3351 if {[info exists linesegs($id)]} {
3352 set lines $linesegs($id)
3354 set r0 [lindex $li 0]
3356 if {$r0 == $le && [lindex $li 1] - $row <= $maxlinelen} {
3366 set li [lindex $lines [expr {$i-1}]]
3367 set r1 [lindex $li 1]
3368 if {$r1 == $row && $le - [lindex $li 0] <= $maxlinelen} {
3373 set x [lindex $cols [expr {$le - $row}]]
3374 set xp [lindex $cols [expr {$le - 1 - $row}]]
3375 set dir [expr {$xp - $x}]
3377 set ith [lindex $lines $i 2]
3378 set coords [$canv coords $ith]
3379 set ah [$canv itemcget $ith -arrow]
3380 set arrowhigh [expr {$ah eq "first" || $ah eq "both"}]
3381 set x2 [lindex $cols [expr {$le + 1 - $row}]]
3382 if {$x2 ne {} && $x - $x2 == $dir} {
3383 set coords [lrange $coords 0 end-2]
3386 set coords [list [xc $le $x] [yc $le]]
3389 set itl [lindex $lines [expr {$i-1}] 2]
3390 set al [$canv itemcget $itl -arrow]
3391 set arrowlow [expr {$al eq "last" || $al eq "both"}]
3392 } elseif {$arrowlow &&
3393 [lsearch -exact [lindex $rowidlist [expr {$row-1}]] $id] >= 0} {
3396 set arrow [lindex {none first last both} [expr {$arrowhigh + 2*$arrowlow}]]
3397 for {set y $le} {[incr y -1] > $row} {} {
3399 set xp [lindex $cols [expr {$y - 1 - $row}]]
3400 set ndir [expr {$xp - $x}]
3401 if {$dir != $ndir || $xp < 0} {
3402 lappend coords [xc $y $x] [yc $y]
3408 # join parent line to first child
3409 set ch [lindex $displayorder $row]
3410 set xc [lsearch -exact [lindex $rowidlist $row] $ch]
3412 puts "oops: drawlineseg: child $ch not on row $row"
3415 lappend coords [xc $row [expr {$x-1}]] [yc $row]
3416 } elseif {$xc > $x + 1} {
3417 lappend coords [xc $row [expr {$x+1}]] [yc $row]
3421 lappend coords [xc $row $x] [yc $row]
3423 set xn [xc $row $xp]
3425 # work around tk8.4 refusal to draw arrows on diagonal segments
3426 if {$arrowlow && $xn != [lindex $coords end-1]} {
3427 if {[llength $coords] < 4 ||
3428 [lindex $coords end-3] != [lindex $coords end-1] ||
3429 [lindex $coords end] - $yn > 2 * $linespc} {
3430 set xn [xc $row [expr {$xp - 0.5 * $dir}]]
3431 set yo [yc [expr {$row + 0.5}]]
3432 lappend coords $xn $yo $xn $yn
3435 lappend coords $xn $yn
3440 set coords [adjarrowhigh $coords]
3443 set t [$canv create line $coords -width [linewidth $id] \
3444 -fill $colormap($id) -tags lines.$id -arrow $arrow]
3447 set lines [linsert $lines $i [list $row $le $t]]
3449 $canv coords $ith $coords
3450 if {$arrow ne $ah} {
3451 $canv itemconf $ith -arrow $arrow
3453 lset lines $i 0 $row
3456 set xo [lsearch -exact [lindex $rowidlist [expr {$row - 1}]] $id]
3457 set ndir [expr {$xo - $xp}]
3458 set clow [$canv coords $itl]
3459 if {$dir == $ndir} {
3460 set clow [lrange $clow 2 end]
3462 set coords [concat $coords $clow]
3464 lset lines [expr {$i-1}] 1 $le
3466 set coords [adjarrowhigh $coords]
3469 # coalesce two pieces
3471 set b [lindex $lines [expr {$i-1}] 0]
3472 set e [lindex $lines $i 1]
3473 set lines [lreplace $lines [expr {$i-1}] $i [list $b $e $itl]]
3475 $canv coords $itl $coords
3476 if {$arrow ne $al} {
3477 $canv itemconf $itl -arrow $arrow
3481 set linesegs($id) $lines
3485 proc drawparentlinks {id row} {
3486 global rowidlist canv colormap curview parentlist
3489 set rowids [lindex $rowidlist $row]
3490 set col [lsearch -exact $rowids $id]
3491 if {$col < 0} return
3492 set olds [lindex $parentlist $row]
3493 set row2 [expr {$row + 1}]
3494 set x [xc $row $col]
3497 set ids [lindex $rowidlist $row2]
3498 # rmx = right-most X coord used
3501 set i [lsearch -exact $ids $p]
3503 puts "oops, parent $p of $id not in list"
3506 set x2 [xc $row2 $i]
3510 if {[lsearch -exact $rowids $p] < 0} {
3511 # drawlineseg will do this one for us
3515 # should handle duplicated parents here...
3516 set coords [list $x $y]
3517 if {$i < $col - 1} {
3518 lappend coords [xc $row [expr {$i + 1}]] $y
3519 } elseif {$i > $col + 1} {
3520 lappend coords [xc $row [expr {$i - 1}]] $y
3522 lappend coords $x2 $y2
3523 set t [$canv create line $coords -width [linewidth $p] \
3524 -fill $colormap($p) -tags lines.$p]
3528 if {$rmx > [lindex $idpos($id) 1]} {
3529 lset idpos($id) 1 $rmx
3534 proc drawlines {id} {
3537 $canv itemconf lines.$id -width [linewidth $id]
3540 proc drawcmittext {id row col} {
3541 global linespc canv canv2 canv3 canvy0 fgcolor curview
3542 global commitlisted commitinfo rowidlist parentlist
3543 global rowtextx idpos idtags idheads idotherrefs
3544 global linehtag linentag linedtag
3545 global mainfont canvxmax boldrows boldnamerows fgcolor nullid nullid2
3547 # listed is 0 for boundary, 1 for normal, 2 for left, 3 for right
3548 set listed [lindex $commitlisted $row]
3549 if {$id eq $nullid} {
3551 } elseif {$id eq $nullid2} {
3554 set ofill [expr {$listed != 0? "blue": "white"}]
3556 set x [xc $row $col]
3558 set orad [expr {$linespc / 3}]
3560 set t [$canv create oval [expr {$x - $orad}] [expr {$y - $orad}] \
3561 [expr {$x + $orad - 1}] [expr {$y + $orad - 1}] \
3562 -fill $ofill -outline $fgcolor -width 1 -tags circle]
3563 } elseif {$listed == 2} {
3564 # triangle pointing left for left-side commits
3565 set t [$canv create polygon \
3566 [expr {$x - $orad}] $y \
3567 [expr {$x + $orad - 1}] [expr {$y - $orad}] \
3568 [expr {$x + $orad - 1}] [expr {$y + $orad - 1}] \
3569 -fill $ofill -outline $fgcolor -width 1 -tags circle]
3571 # triangle pointing right for right-side commits
3572 set t [$canv create polygon \
3573 [expr {$x + $orad - 1}] $y \
3574 [expr {$x - $orad}] [expr {$y - $orad}] \
3575 [expr {$x - $orad}] [expr {$y + $orad - 1}] \
3576 -fill $ofill -outline $fgcolor -width 1 -tags circle]
3579 $canv bind $t <1> {selcanvline {} %x %y}
3580 set rmx [llength [lindex $rowidlist $row]]
3581 set olds [lindex $parentlist $row]
3583 set nextids [lindex $rowidlist [expr {$row + 1}]]
3585 set i [lsearch -exact $nextids $p]
3591 set xt [xc $row $rmx]
3592 set rowtextx($row) $xt
3593 set idpos($id) [list $x $xt $y]
3594 if {[info exists idtags($id)] || [info exists idheads($id)]
3595 || [info exists idotherrefs($id)]} {
3596 set xt [drawtags $id $x $xt $y]
3598 set headline [lindex $commitinfo($id) 0]
3599 set name [lindex $commitinfo($id) 1]
3600 set date [lindex $commitinfo($id) 2]
3601 set date [formatdate $date]
3604 set isbold [ishighlighted $row]
3606 lappend boldrows $row
3609 lappend boldnamerows $row
3613 set linehtag($row) [$canv create text $xt $y -anchor w -fill $fgcolor \
3614 -text $headline -font $font -tags text]
3615 $canv bind $linehtag($row) <Button-3> "rowmenu %X %Y $id"
3616 set linentag($row) [$canv2 create text 3 $y -anchor w -fill $fgcolor \
3617 -text $name -font $nfont -tags text]
3618 set linedtag($row) [$canv3 create text 3 $y -anchor w -fill $fgcolor \
3619 -text $date -font $mainfont -tags text]
3620 set xr [expr {$xt + [font measure $mainfont $headline]}]
3621 if {$xr > $canvxmax} {
3627 proc drawcmitrow {row} {
3628 global displayorder rowidlist
3629 global iddrawn markingmatches
3630 global commitinfo parentlist numcommits
3631 global filehighlight fhighlights findstring nhighlights
3632 global hlview vhighlights
3633 global highlight_related rhighlights
3635 if {$row >= $numcommits} return
3637 set id [lindex $displayorder $row]
3638 if {[info exists hlview] && ![info exists vhighlights($row)]} {
3639 askvhighlight $row $id
3641 if {[info exists filehighlight] && ![info exists fhighlights($row)]} {
3642 askfilehighlight $row $id
3644 if {$findstring ne {} && ![info exists nhighlights($row)]} {
3645 askfindhighlight $row $id
3647 if {$highlight_related ne "None" && ![info exists rhighlights($row)]} {
3648 askrelhighlight $row $id
3650 if {![info exists iddrawn($id)]} {
3651 set col [lsearch -exact [lindex $rowidlist $row] $id]
3653 puts "oops, row $row id $id not in list"
3656 if {![info exists commitinfo($id)]} {
3660 drawcmittext $id $row $col
3663 if {$markingmatches} {
3664 markrowmatches $row $id
3668 proc drawcommits {row {endrow {}}} {
3669 global numcommits iddrawn displayorder curview
3670 global parentlist rowidlist
3675 if {$endrow eq {}} {
3678 if {$endrow >= $numcommits} {
3679 set endrow [expr {$numcommits - 1}]
3682 # make the lines join to already-drawn rows either side
3683 set r [expr {$row - 1}]
3684 if {$r < 0 || ![info exists iddrawn([lindex $displayorder $r])]} {
3687 set er [expr {$endrow + 1}]
3688 if {$er >= $numcommits ||
3689 ![info exists iddrawn([lindex $displayorder $er])]} {
3692 for {} {$r <= $er} {incr r} {
3693 set id [lindex $displayorder $r]
3694 set wasdrawn [info exists iddrawn($id)]
3696 if {$r == $er} break
3697 set nextid [lindex $displayorder [expr {$r + 1}]]
3698 if {$wasdrawn && [info exists iddrawn($nextid)]} {
3699 catch {unset prevlines}
3702 drawparentlinks $id $r
3704 if {[info exists lineends($r)]} {
3705 foreach lid $lineends($r) {
3706 unset prevlines($lid)
3709 set rowids [lindex $rowidlist $r]
3710 foreach lid $rowids {
3711 if {$lid eq {}} continue
3713 # see if this is the first child of any of its parents
3714 foreach p [lindex $parentlist $r] {
3715 if {[lsearch -exact $rowids $p] < 0} {
3716 # make this line extend up to the child
3717 set le [drawlineseg $p $r $er 0]
3718 lappend lineends($le) $p
3722 } elseif {![info exists prevlines($lid)]} {
3723 set le [drawlineseg $lid $r $er 1]
3724 lappend lineends($le) $lid
3725 set prevlines($lid) 1
3731 proc drawfrac {f0 f1} {
3734 set ymax [lindex [$canv cget -scrollregion] 3]
3735 if {$ymax eq {} || $ymax == 0} return
3736 set y0 [expr {int($f0 * $ymax)}]
3737 set row [expr {int(($y0 - 3) / $linespc) - 1}]
3738 set y1 [expr {int($f1 * $ymax)}]
3739 set endrow [expr {int(($y1 - 3) / $linespc) + 1}]
3740 drawcommits $row $endrow
3743 proc drawvisible {} {
3745 eval drawfrac [$canv yview]
3748 proc clear_display {} {
3749 global iddrawn linesegs
3750 global vhighlights fhighlights nhighlights rhighlights
3753 catch {unset iddrawn}
3754 catch {unset linesegs}
3755 catch {unset vhighlights}
3756 catch {unset fhighlights}
3757 catch {unset nhighlights}
3758 catch {unset rhighlights}
3761 proc findcrossings {id} {
3762 global rowidlist parentlist numcommits rowoffsets displayorder
3766 foreach {s e} [rowranges $id] {
3767 if {$e >= $numcommits} {
3768 set e [expr {$numcommits - 1}]
3770 if {$e <= $s} continue
3771 set x [lsearch -exact [lindex $rowidlist $e] $id]
3773 puts "findcrossings: oops, no [shortids $id] in row $e"
3776 for {set row $e} {[incr row -1] >= $s} {} {
3777 set olds [lindex $parentlist $row]
3778 set kid [lindex $displayorder $row]
3779 set kidx [lsearch -exact [lindex $rowidlist $row] $kid]
3780 if {$kidx < 0} continue
3781 set nextrow [lindex $rowidlist [expr {$row + 1}]]
3783 set px [lsearch -exact $nextrow $p]
3784 if {$px < 0} continue
3785 if {($kidx < $x && $x < $px) || ($px < $x && $x < $kidx)} {
3786 if {[lsearch -exact $ccross $p] >= 0} continue
3787 if {$x == $px + ($kidx < $px? -1: 1)} {
3789 } elseif {[lsearch -exact $cross $p] < 0} {
3794 set inc [lindex $rowoffsets $row $x]
3795 if {$inc eq {}} break
3799 return [concat $ccross {{}} $cross]
3802 proc assigncolor {id} {
3803 global colormap colors nextcolor
3804 global commitrow parentlist children children curview
3806 if {[info exists colormap($id)]} return
3807 set ncolors [llength $colors]
3808 if {[info exists children($curview,$id)]} {
3809 set kids $children($curview,$id)
3813 if {[llength $kids] == 1} {
3814 set child [lindex $kids 0]
3815 if {[info exists colormap($child)]
3816 && [llength [lindex $parentlist $commitrow($curview,$child)]] == 1} {
3817 set colormap($id) $colormap($child)
3823 foreach x [findcrossings $id] {
3825 # delimiter between corner crossings and other crossings
3826 if {[llength $badcolors] >= $ncolors - 1} break
3827 set origbad $badcolors
3829 if {[info exists colormap($x)]
3830 && [lsearch -exact $badcolors $colormap($x)] < 0} {
3831 lappend badcolors $colormap($x)
3834 if {[llength $badcolors] >= $ncolors} {
3835 set badcolors $origbad
3837 set origbad $badcolors
3838 if {[llength $badcolors] < $ncolors - 1} {
3839 foreach child $kids {
3840 if {[info exists colormap($child)]
3841 && [lsearch -exact $badcolors $colormap($child)] < 0} {
3842 lappend badcolors $colormap($child)
3844 foreach p [lindex $parentlist $commitrow($curview,$child)] {
3845 if {[info exists colormap($p)]
3846 && [lsearch -exact $badcolors $colormap($p)] < 0} {
3847 lappend badcolors $colormap($p)
3851 if {[llength $badcolors] >= $ncolors} {
3852 set badcolors $origbad
3855 for {set i 0} {$i <= $ncolors} {incr i} {
3856 set c [lindex $colors $nextcolor]
3857 if {[incr nextcolor] >= $ncolors} {
3860 if {[lsearch -exact $badcolors $c]} break
3862 set colormap($id) $c
3865 proc bindline {t id} {
3868 $canv bind $t <Enter> "lineenter %x %y $id"
3869 $canv bind $t <Motion> "linemotion %x %y $id"
3870 $canv bind $t <Leave> "lineleave $id"
3871 $canv bind $t <Button-1> "lineclick %x %y $id 1"
3874 proc drawtags {id x xt y1} {
3875 global idtags idheads idotherrefs mainhead
3876 global linespc lthickness
3877 global canv mainfont commitrow rowtextx curview fgcolor bgcolor
3882 if {[info exists idtags($id)]} {
3883 set marks $idtags($id)
3884 set ntags [llength $marks]
3886 if {[info exists idheads($id)]} {
3887 set marks [concat $marks $idheads($id)]
3888 set nheads [llength $idheads($id)]
3890 if {[info exists idotherrefs($id)]} {
3891 set marks [concat $marks $idotherrefs($id)]
3897 set delta [expr {int(0.5 * ($linespc - $lthickness))}]
3898 set yt [expr {$y1 - 0.5 * $linespc}]
3899 set yb [expr {$yt + $linespc - 1}]
3903 foreach tag $marks {
3905 if {$i >= $ntags && $i < $ntags + $nheads && $tag eq $mainhead} {
3906 set wid [font measure [concat $mainfont bold] $tag]
3908 set wid [font measure $mainfont $tag]
3912 set xt [expr {$xt + $delta + $wid + $lthickness + $linespc}]
3914 set t [$canv create line $x $y1 [lindex $xvals end] $y1 \
3915 -width $lthickness -fill black -tags tag.$id]
3917 foreach tag $marks x $xvals wid $wvals {
3918 set xl [expr {$x + $delta}]
3919 set xr [expr {$x + $delta + $wid + $lthickness}]
3921 if {[incr ntags -1] >= 0} {
3923 set t [$canv create polygon $x [expr {$yt + $delta}] $xl $yt \
3924 $xr $yt $xr $yb $xl $yb $x [expr {$yb - $delta}] \
3925 -width 1 -outline black -fill yellow -tags tag.$id]
3926 $canv bind $t <1> [list showtag $tag 1]
3927 set rowtextx($commitrow($curview,$id)) [expr {$xr + $linespc}]
3929 # draw a head or other ref
3930 if {[incr nheads -1] >= 0} {
3932 if {$tag eq $mainhead} {
3938 set xl [expr {$xl - $delta/2}]
3939 $canv create polygon $x $yt $xr $yt $xr $yb $x $yb \
3940 -width 1 -outline black -fill $col -tags tag.$id
3941 if {[regexp {^(remotes/.*/|remotes/)} $tag match remoteprefix]} {
3942 set rwid [font measure $mainfont $remoteprefix]
3943 set xi [expr {$x + 1}]
3944 set yti [expr {$yt + 1}]
3945 set xri [expr {$x + $rwid}]
3946 $canv create polygon $xi $yti $xri $yti $xri $yb $xi $yb \
3947 -width 0 -fill "#ffddaa" -tags tag.$id
3950 set t [$canv create text $xl $y1 -anchor w -text $tag -fill $fgcolor \
3951 -font $font -tags [list tag.$id text]]
3953 $canv bind $t <1> [list showtag $tag 1]
3954 } elseif {$nheads >= 0} {
3955 $canv bind $t <Button-3> [list headmenu %X %Y $id $tag]
3961 proc xcoord {i level ln} {
3962 global canvx0 xspc1 xspc2
3964 set x [expr {$canvx0 + $i * $xspc1($ln)}]
3965 if {$i > 0 && $i == $level} {
3966 set x [expr {$x + 0.5 * ($xspc2 - $xspc1($ln))}]
3967 } elseif {$i > $level} {
3968 set x [expr {$x + $xspc2 - $xspc1($ln)}]
3973 proc show_status {msg} {
3974 global canv mainfont fgcolor
3977 $canv create text 3 3 -anchor nw -text $msg -font $mainfont \
3978 -tags text -fill $fgcolor
3981 # Insert a new commit as the child of the commit on row $row.
3982 # The new commit will be displayed on row $row and the commits
3983 # on that row and below will move down one row.
3984 proc insertrow {row newcmit} {
3985 global displayorder parentlist commitlisted children
3986 global commitrow curview rowidlist rowoffsets numcommits
3987 global rowrangelist rowlaidout rowoptim numcommits
3988 global selectedline rowchk commitidx
3990 if {$row >= $numcommits} {
3991 puts "oops, inserting new row $row but only have $numcommits rows"
3994 set p [lindex $displayorder $row]
3995 set displayorder [linsert $displayorder $row $newcmit]
3996 set parentlist [linsert $parentlist $row $p]
3997 set kids $children($curview,$p)
3998 lappend kids $newcmit
3999 set children($curview,$p) $kids
4000 set children($curview,$newcmit) {}
4001 set commitlisted [linsert $commitlisted $row 1]
4002 set l [llength $displayorder]
4003 for {set r $row} {$r < $l} {incr r} {
4004 set id [lindex $displayorder $r]
4005 set commitrow($curview,$id) $r
4007 incr commitidx($curview)
4009 set idlist [lindex $rowidlist $row]
4010 set offs [lindex $rowoffsets $row]
4013 if {$x eq {} || ($x eq $p && [llength $kids] == 1)} {
4019 if {[llength $kids] == 1} {
4020 set col [lsearch -exact $idlist $p]
4021 lset idlist $col $newcmit
4023 set col [llength $idlist]
4024 lappend idlist $newcmit
4026 lset rowoffsets $row $offs
4028 set rowidlist [linsert $rowidlist $row $idlist]
4029 set rowoffsets [linsert $rowoffsets [expr {$row+1}] $newoffs]
4031 set rowrangelist [linsert $rowrangelist $row {}]
4032 if {[llength $kids] > 1} {
4033 set rp1 [expr {$row + 1}]
4034 set ranges [lindex $rowrangelist $rp1]
4035 if {$ranges eq {}} {
4036 set ranges [list $newcmit $p]
4037 } elseif {[lindex $ranges end-1] eq $p} {
4038 lset ranges end-1 $newcmit
4040 lset rowrangelist $rp1 $ranges
4043 catch {unset rowchk}
4049 if {[info exists selectedline] && $selectedline >= $row} {
4055 # Remove a commit that was inserted with insertrow on row $row.
4056 proc removerow {row} {
4057 global displayorder parentlist commitlisted children
4058 global commitrow curview rowidlist rowoffsets numcommits
4059 global rowrangelist idrowranges rowlaidout rowoptim numcommits
4060 global linesegends selectedline rowchk commitidx
4062 if {$row >= $numcommits} {
4063 puts "oops, removing row $row but only have $numcommits rows"
4066 set rp1 [expr {$row + 1}]
4067 set id [lindex $displayorder $row]
4068 set p [lindex $parentlist $row]
4069 set displayorder [lreplace $displayorder $row $row]
4070 set parentlist [lreplace $parentlist $row $row]
4071 set commitlisted [lreplace $commitlisted $row $row]
4072 set kids $children($curview,$p)
4073 set i [lsearch -exact $kids $id]
4075 set kids [lreplace $kids $i $i]
4076 set children($curview,$p) $kids
4078 set l [llength $displayorder]
4079 for {set r $row} {$r < $l} {incr r} {
4080 set id [lindex $displayorder $r]
4081 set commitrow($curview,$id) $r
4083 incr commitidx($curview) -1
4085 set rowidlist [lreplace $rowidlist $row $row]
4086 set rowoffsets [lreplace $rowoffsets $rp1 $rp1]
4088 set offs [lindex $rowoffsets $row]
4089 set offs [lreplace $offs end end]
4090 lset rowoffsets $row $offs
4093 set rowrangelist [lreplace $rowrangelist $row $row]
4094 if {[llength $kids] > 0} {
4095 set ranges [lindex $rowrangelist $row]
4096 if {[lindex $ranges end-1] eq $id} {
4097 set ranges [lreplace $ranges end-1 end]
4098 lset rowrangelist $row $ranges
4102 catch {unset rowchk}
4108 if {[info exists selectedline] && $selectedline > $row} {
4109 incr selectedline -1
4114 # Don't change the text pane cursor if it is currently the hand cursor,
4115 # showing that we are over a sha1 ID link.
4116 proc settextcursor {c} {
4117 global ctext curtextcursor
4119 if {[$ctext cget -cursor] == $curtextcursor} {
4120 $ctext config -cursor $c
4122 set curtextcursor $c
4125 proc nowbusy {what} {
4128 if {[array names isbusy] eq {}} {
4129 . config -cursor watch
4135 proc notbusy {what} {
4136 global isbusy maincursor textcursor
4138 catch {unset isbusy($what)}
4139 if {[array names isbusy] eq {}} {
4140 . config -cursor $maincursor
4141 settextcursor $textcursor
4145 proc findmatches {f} {
4146 global findtype findstring
4147 if {$findtype == "Regexp"} {
4148 set matches [regexp -indices -all -inline $findstring $f]
4151 if {$findtype == "IgnCase"} {
4152 set f [string tolower $f]
4153 set fs [string tolower $fs]
4157 set l [string length $fs]
4158 while {[set j [string first $fs $f $i]] >= 0} {
4159 lappend matches [list $j [expr {$j+$l-1}]]
4160 set i [expr {$j + $l}]
4166 proc dofind {{rev 0}} {
4167 global findstring findstartline findcurline selectedline numcommits
4170 cancel_next_highlight
4172 if {$findstring eq {} || $numcommits == 0} return
4173 if {![info exists selectedline]} {
4174 set findstartline [lindex [visiblerows] $rev]
4176 set findstartline $selectedline
4178 set findcurline $findstartline
4183 if {$findcurline == 0} {
4184 set findcurline $numcommits
4191 proc findnext {restart} {
4193 if {![info exists findcurline]} {
4207 if {![info exists findcurline]} {
4216 global commitdata commitinfo numcommits findstring findpattern findloc
4217 global findstartline findcurline displayorder
4219 set fldtypes {Headline Author Date Committer CDate Comments}
4220 set l [expr {$findcurline + 1}]
4221 if {$l >= $numcommits} {
4224 if {$l <= $findstartline} {
4225 set lim [expr {$findstartline + 1}]
4229 if {$lim - $l > 500} {
4230 set lim [expr {$l + 500}]
4233 for {} {$l < $lim} {incr l} {
4234 set id [lindex $displayorder $l]
4235 # shouldn't happen unless git log doesn't give all the commits...
4236 if {![info exists commitdata($id)]} continue
4237 if {![doesmatch $commitdata($id)]} continue
4238 if {![info exists commitinfo($id)]} {
4241 set info $commitinfo($id)
4242 foreach f $info ty $fldtypes {
4243 if {($findloc eq "All fields" || $findloc eq $ty) &&
4251 if {$l == $findstartline + 1} {
4257 set findcurline [expr {$l - 1}]
4261 proc findmorerev {} {
4262 global commitdata commitinfo numcommits findstring findpattern findloc
4263 global findstartline findcurline displayorder
4265 set fldtypes {Headline Author Date Committer CDate Comments}
4271 if {$l >= $findstartline} {
4272 set lim [expr {$findstartline - 1}]
4276 if {$l - $lim > 500} {
4277 set lim [expr {$l - 500}]
4280 for {} {$l > $lim} {incr l -1} {
4281 set id [lindex $displayorder $l]
4282 if {![doesmatch $commitdata($id)]} continue
4283 if {![info exists commitinfo($id)]} {
4286 set info $commitinfo($id)
4287 foreach f $info ty $fldtypes {
4288 if {($findloc eq "All fields" || $findloc eq $ty) &&
4302 set findcurline [expr {$l + 1}]
4306 proc findselectline {l} {
4307 global findloc commentend ctext findcurline markingmatches
4309 set markingmatches 1
4312 if {$findloc == "All fields" || $findloc == "Comments"} {
4313 # highlight the matches in the comments
4314 set f [$ctext get 1.0 $commentend]
4315 set matches [findmatches $f]
4316 foreach match $matches {
4317 set start [lindex $match 0]
4318 set end [expr {[lindex $match 1] + 1}]
4319 $ctext tag add found "1.0 + $start c" "1.0 + $end c"
4325 # mark the bits of a headline or author that match a find string
4326 proc markmatches {canv l str tag matches font row} {
4329 set bbox [$canv bbox $tag]
4330 set x0 [lindex $bbox 0]
4331 set y0 [lindex $bbox 1]
4332 set y1 [lindex $bbox 3]
4333 foreach match $matches {
4334 set start [lindex $match 0]
4335 set end [lindex $match 1]
4336 if {$start > $end} continue
4337 set xoff [font measure $font [string range $str 0 [expr {$start-1}]]]
4338 set xlen [font measure $font [string range $str 0 [expr {$end}]]]
4339 set t [$canv create rect [expr {$x0+$xoff}] $y0 \
4340 [expr {$x0+$xlen+2}] $y1 \
4341 -outline {} -tags [list match$l matches] -fill yellow]
4343 if {[info exists selectedline] && $row == $selectedline} {
4344 $canv raise $t secsel
4349 proc unmarkmatches {} {
4350 global findids markingmatches findcurline
4352 allcanvs delete matches
4353 catch {unset findids}
4354 set markingmatches 0
4355 catch {unset findcurline}
4358 proc selcanvline {w x y} {
4359 global canv canvy0 ctext linespc
4361 set ymax [lindex [$canv cget -scrollregion] 3]
4362 if {$ymax == {}} return
4363 set yfrac [lindex [$canv yview] 0]
4364 set y [expr {$y + $yfrac * $ymax}]
4365 set l [expr {int(($y - $canvy0) / $linespc + 0.5)}]
4370 if {![info exists rowtextx($l)] || $x < $rowtextx($l)} return
4376 proc commit_descriptor {p} {
4378 if {![info exists commitinfo($p)]} {
4382 if {[llength $commitinfo($p)] > 1} {
4383 set l [lindex $commitinfo($p) 0]
4388 # append some text to the ctext widget, and make any SHA1 ID
4389 # that we know about be a clickable link.
4390 proc appendwithlinks {text tags} {
4391 global ctext commitrow linknum curview
4393 set start [$ctext index "end - 1c"]
4394 $ctext insert end $text $tags
4395 set links [regexp -indices -all -inline {[0-9a-f]{40}} $text]
4399 set linkid [string range $text $s $e]
4400 if {![info exists commitrow($curview,$linkid)]} continue
4402 $ctext tag add link "$start + $s c" "$start + $e c"
4403 $ctext tag add link$linknum "$start + $s c" "$start + $e c"
4404 $ctext tag bind link$linknum <1> \
4405 [list selectline $commitrow($curview,$linkid) 1]
4408 $ctext tag conf link -foreground blue -underline 1
4409 $ctext tag bind link <Enter> { %W configure -cursor hand2 }
4410 $ctext tag bind link <Leave> { %W configure -cursor $curtextcursor }
4413 proc viewnextline {dir} {
4417 set ymax [lindex [$canv cget -scrollregion] 3]
4418 set wnow [$canv yview]
4419 set wtop [expr {[lindex $wnow 0] * $ymax}]
4420 set newtop [expr {$wtop + $dir * $linespc}]
4423 } elseif {$newtop > $ymax} {
4426 allcanvs yview moveto [expr {$newtop * 1.0 / $ymax}]
4429 # add a list of tag or branch names at position pos
4430 # returns the number of names inserted
4431 proc appendrefs {pos ids var} {
4432 global ctext commitrow linknum curview $var maxrefs
4434 if {[catch {$ctext index $pos}]} {
4437 $ctext conf -state normal
4438 $ctext delete $pos "$pos lineend"
4441 foreach tag [set $var\($id\)] {
4442 lappend tags [list $tag $id]
4445 if {[llength $tags] > $maxrefs} {
4446 $ctext insert $pos "many ([llength $tags])"
4448 set tags [lsort -index 0 -decreasing $tags]
4451 set id [lindex $ti 1]
4454 $ctext tag delete $lk
4455 $ctext insert $pos $sep
4456 $ctext insert $pos [lindex $ti 0] $lk
4457 if {[info exists commitrow($curview,$id)]} {
4458 $ctext tag conf $lk -foreground blue
4459 $ctext tag bind $lk <1> \
4460 [list selectline $commitrow($curview,$id) 1]
4461 $ctext tag conf $lk -underline 1
4462 $ctext tag bind $lk <Enter> { %W configure -cursor hand2 }
4463 $ctext tag bind $lk <Leave> \
4464 { %W configure -cursor $curtextcursor }
4469 $ctext conf -state disabled
4470 return [llength $tags]
4473 # called when we have finished computing the nearby tags
4474 proc dispneartags {delay} {
4475 global selectedline currentid showneartags tagphase
4477 if {![info exists selectedline] || !$showneartags} return
4478 after cancel dispnexttag
4480 after 200 dispnexttag
4483 after idle dispnexttag
4488 proc dispnexttag {} {
4489 global selectedline currentid showneartags tagphase ctext
4491 if {![info exists selectedline] || !$showneartags} return
4492 switch -- $tagphase {
4494 set dtags [desctags $currentid]
4496 appendrefs precedes $dtags idtags
4500 set atags [anctags $currentid]
4502 appendrefs follows $atags idtags
4506 set dheads [descheads $currentid]
4507 if {$dheads ne {}} {
4508 if {[appendrefs branch $dheads idheads] > 1
4509 && [$ctext get "branch -3c"] eq "h"} {
4510 # turn "Branch" into "Branches"
4511 $ctext conf -state normal
4512 $ctext insert "branch -2c" "es"
4513 $ctext conf -state disabled
4518 if {[incr tagphase] <= 2} {
4519 after idle dispnexttag
4523 proc selectline {l isnew} {
4524 global canv canv2 canv3 ctext commitinfo selectedline
4525 global displayorder linehtag linentag linedtag
4526 global canvy0 linespc parentlist children curview
4527 global currentid sha1entry
4528 global commentend idtags linknum
4529 global mergemax numcommits pending_select
4530 global cmitmode showneartags allcommits
4532 catch {unset pending_select}
4535 cancel_next_highlight
4537 if {$l < 0 || $l >= $numcommits} return
4538 set y [expr {$canvy0 + $l * $linespc}]
4539 set ymax [lindex [$canv cget -scrollregion] 3]
4540 set ytop [expr {$y - $linespc - 1}]
4541 set ybot [expr {$y + $linespc + 1}]
4542 set wnow [$canv yview]
4543 set wtop [expr {[lindex $wnow 0] * $ymax}]
4544 set wbot [expr {[lindex $wnow 1] * $ymax}]
4545 set wh [expr {$wbot - $wtop}]
4547 if {$ytop < $wtop} {
4548 if {$ybot < $wtop} {
4549 set newtop [expr {$y - $wh / 2.0}]
4552 if {$newtop > $wtop - $linespc} {
4553 set newtop [expr {$wtop - $linespc}]
4556 } elseif {$ybot > $wbot} {
4557 if {$ytop > $wbot} {
4558 set newtop [expr {$y - $wh / 2.0}]
4560 set newtop [expr {$ybot - $wh}]
4561 if {$newtop < $wtop + $linespc} {
4562 set newtop [expr {$wtop + $linespc}]
4566 if {$newtop != $wtop} {
4570 allcanvs yview moveto [expr {$newtop * 1.0 / $ymax}]
4574 if {![info exists linehtag($l)]} return
4576 set t [eval $canv create rect [$canv bbox $linehtag($l)] -outline {{}} \
4577 -tags secsel -fill [$canv cget -selectbackground]]
4579 $canv2 delete secsel
4580 set t [eval $canv2 create rect [$canv2 bbox $linentag($l)] -outline {{}} \
4581 -tags secsel -fill [$canv2 cget -selectbackground]]
4583 $canv3 delete secsel
4584 set t [eval $canv3 create rect [$canv3 bbox $linedtag($l)] -outline {{}} \
4585 -tags secsel -fill [$canv3 cget -selectbackground]]
4589 addtohistory [list selectline $l 0]
4594 set id [lindex $displayorder $l]
4596 $sha1entry delete 0 end
4597 $sha1entry insert 0 $id
4598 $sha1entry selection from 0
4599 $sha1entry selection to end
4602 $ctext conf -state normal
4605 set info $commitinfo($id)
4606 set date [formatdate [lindex $info 2]]
4607 $ctext insert end "Author: [lindex $info 1] $date\n"
4608 set date [formatdate [lindex $info 4]]
4609 $ctext insert end "Committer: [lindex $info 3] $date\n"
4610 if {[info exists idtags($id)]} {
4611 $ctext insert end "Tags:"
4612 foreach tag $idtags($id) {
4613 $ctext insert end " $tag"
4615 $ctext insert end "\n"
4619 set olds [lindex $parentlist $l]
4620 if {[llength $olds] > 1} {
4623 if {$np >= $mergemax} {
4628 $ctext insert end "Parent: " $tag
4629 appendwithlinks [commit_descriptor $p] {}
4634 append headers "Parent: [commit_descriptor $p]"
4638 foreach c $children($curview,$id) {
4639 append headers "Child: [commit_descriptor $c]"
4642 # make anything that looks like a SHA1 ID be a clickable link
4643 appendwithlinks $headers {}
4644 if {$showneartags} {
4645 if {![info exists allcommits]} {
4648 $ctext insert end "Branch: "
4649 $ctext mark set branch "end -1c"
4650 $ctext mark gravity branch left
4651 $ctext insert end "\nFollows: "
4652 $ctext mark set follows "end -1c"
4653 $ctext mark gravity follows left
4654 $ctext insert end "\nPrecedes: "
4655 $ctext mark set precedes "end -1c"
4656 $ctext mark gravity precedes left
4657 $ctext insert end "\n"
4660 $ctext insert end "\n"
4661 set comment [lindex $info 5]
4662 if {[string first "\r" $comment] >= 0} {
4663 set comment [string map {"\r" "\n "} $comment]
4665 appendwithlinks $comment {comment}
4667 $ctext tag remove found 1.0 end
4668 $ctext conf -state disabled
4669 set commentend [$ctext index "end - 1c"]
4671 init_flist "Comments"
4672 if {$cmitmode eq "tree"} {
4674 } elseif {[llength $olds] <= 1} {
4681 proc selfirstline {} {
4686 proc sellastline {} {
4689 set l [expr {$numcommits - 1}]
4693 proc selnextline {dir} {
4696 if {![info exists selectedline]} return
4697 set l [expr {$selectedline + $dir}]
4702 proc selnextpage {dir} {
4703 global canv linespc selectedline numcommits
4705 set lpp [expr {([winfo height $canv] - 2) / $linespc}]
4709 allcanvs yview scroll [expr {$dir * $lpp}] units
4711 if {![info exists selectedline]} return
4712 set l [expr {$selectedline + $dir * $lpp}]
4715 } elseif {$l >= $numcommits} {
4716 set l [expr $numcommits - 1]
4722 proc unselectline {} {
4723 global selectedline currentid
4725 catch {unset selectedline}
4726 catch {unset currentid}
4727 allcanvs delete secsel
4729 cancel_next_highlight
4732 proc reselectline {} {
4735 if {[info exists selectedline]} {
4736 selectline $selectedline 0
4740 proc addtohistory {cmd} {
4741 global history historyindex curview
4743 set elt [list $curview $cmd]
4744 if {$historyindex > 0
4745 && [lindex $history [expr {$historyindex - 1}]] == $elt} {
4749 if {$historyindex < [llength $history]} {
4750 set history [lreplace $history $historyindex end $elt]
4752 lappend history $elt
4755 if {$historyindex > 1} {
4756 .tf.bar.leftbut conf -state normal
4758 .tf.bar.leftbut conf -state disabled
4760 .tf.bar.rightbut conf -state disabled
4766 set view [lindex $elt 0]
4767 set cmd [lindex $elt 1]
4768 if {$curview != $view} {
4775 global history historyindex
4778 if {$historyindex > 1} {
4779 incr historyindex -1
4780 godo [lindex $history [expr {$historyindex - 1}]]
4781 .tf.bar.rightbut conf -state normal
4783 if {$historyindex <= 1} {
4784 .tf.bar.leftbut conf -state disabled
4789 global history historyindex
4792 if {$historyindex < [llength $history]} {
4793 set cmd [lindex $history $historyindex]
4796 .tf.bar.leftbut conf -state normal
4798 if {$historyindex >= [llength $history]} {
4799 .tf.bar.rightbut conf -state disabled
4804 global treefilelist treeidlist diffids diffmergeid treepending
4805 global nullid nullid2
4808 catch {unset diffmergeid}
4809 if {![info exists treefilelist($id)]} {
4810 if {![info exists treepending]} {
4811 if {$id eq $nullid} {
4812 set cmd [list | git ls-files]
4813 } elseif {$id eq $nullid2} {
4814 set cmd [list | git ls-files --stage -t]
4816 set cmd [list | git ls-tree -r $id]
4818 if {[catch {set gtf [open $cmd r]}]} {
4822 set treefilelist($id) {}
4823 set treeidlist($id) {}
4824 fconfigure $gtf -blocking 0
4825 filerun $gtf [list gettreeline $gtf $id]
4832 proc gettreeline {gtf id} {
4833 global treefilelist treeidlist treepending cmitmode diffids nullid nullid2
4836 while {[incr nl] <= 1000 && [gets $gtf line] >= 0} {
4837 if {$diffids eq $nullid} {
4840 if {$diffids ne $nullid2 && [lindex $line 1] ne "blob"} continue
4841 set i [string first "\t" $line]
4842 if {$i < 0} continue
4843 set sha1 [lindex $line 2]
4844 set fname [string range $line [expr {$i+1}] end]
4845 if {[string index $fname 0] eq "\""} {
4846 set fname [lindex $fname 0]
4848 lappend treeidlist($id) $sha1
4850 lappend treefilelist($id) $fname
4853 return [expr {$nl >= 1000? 2: 1}]
4857 if {$cmitmode ne "tree"} {
4858 if {![info exists diffmergeid]} {
4859 gettreediffs $diffids
4861 } elseif {$id ne $diffids} {
4870 global treefilelist treeidlist diffids nullid nullid2
4871 global ctext commentend
4873 set i [lsearch -exact $treefilelist($diffids) $f]
4875 puts "oops, $f not in list for id $diffids"
4878 if {$diffids eq $nullid} {
4879 if {[catch {set bf [open $f r]} err]} {
4880 puts "oops, can't read $f: $err"
4884 set blob [lindex $treeidlist($diffids) $i]
4885 if {[catch {set bf [open [concat | git cat-file blob $blob] r]} err]} {
4886 puts "oops, error reading blob $blob: $err"
4890 fconfigure $bf -blocking 0
4891 filerun $bf [list getblobline $bf $diffids]
4892 $ctext config -state normal
4893 clear_ctext $commentend
4894 $ctext insert end "\n"
4895 $ctext insert end "$f\n" filesep
4896 $ctext config -state disabled
4897 $ctext yview $commentend
4900 proc getblobline {bf id} {
4901 global diffids cmitmode ctext
4903 if {$id ne $diffids || $cmitmode ne "tree"} {
4907 $ctext config -state normal
4909 while {[incr nl] <= 1000 && [gets $bf line] >= 0} {
4910 $ctext insert end "$line\n"
4913 # delete last newline
4914 $ctext delete "end - 2c" "end - 1c"
4918 $ctext config -state disabled
4919 return [expr {$nl >= 1000? 2: 1}]
4922 proc mergediff {id l} {
4923 global diffmergeid diffopts mdifffd
4929 # this doesn't seem to actually affect anything...
4930 set env(GIT_DIFF_OPTS) $diffopts
4931 set cmd [concat | git diff-tree --no-commit-id --cc $id]
4932 if {[catch {set mdf [open $cmd r]} err]} {
4933 error_popup "Error getting merge diffs: $err"
4936 fconfigure $mdf -blocking 0
4937 set mdifffd($id) $mdf
4938 set np [llength [lindex $parentlist $l]]
4939 filerun $mdf [list getmergediffline $mdf $id $np]
4942 proc getmergediffline {mdf id np} {
4943 global diffmergeid ctext cflist mergemax
4944 global difffilestart mdifffd
4946 $ctext conf -state normal
4948 while {[incr nr] <= 1000 && [gets $mdf line] >= 0} {
4949 if {![info exists diffmergeid] || $id != $diffmergeid
4950 || $mdf != $mdifffd($id)} {
4954 if {[regexp {^diff --cc (.*)} $line match fname]} {
4955 # start of a new file
4956 $ctext insert end "\n"
4957 set here [$ctext index "end - 1c"]
4958 lappend difffilestart $here
4959 add_flist [list $fname]
4960 set l [expr {(78 - [string length $fname]) / 2}]
4961 set pad [string range "----------------------------------------" 1 $l]
4962 $ctext insert end "$pad $fname $pad\n" filesep
4963 } elseif {[regexp {^@@} $line]} {
4964 $ctext insert end "$line\n" hunksep
4965 } elseif {[regexp {^[0-9a-f]{40}$} $line] || [regexp {^index} $line]} {
4968 # parse the prefix - one ' ', '-' or '+' for each parent
4973 for {set j 0} {$j < $np} {incr j} {
4974 set c [string range $line $j $j]
4977 } elseif {$c == "-"} {
4979 } elseif {$c == "+"} {
4988 if {!$isbad && $minuses ne {} && $pluses eq {}} {
4989 # line doesn't appear in result, parents in $minuses have the line
4990 set num [lindex $minuses 0]
4991 } elseif {!$isbad && $pluses ne {} && $minuses eq {}} {
4992 # line appears in result, parents in $pluses don't have the line
4993 lappend tags mresult
4994 set num [lindex $spaces 0]
4997 if {$num >= $mergemax} {
5002 $ctext insert end "$line\n" $tags
5005 $ctext conf -state disabled
5010 return [expr {$nr >= 1000? 2: 1}]
5013 proc startdiff {ids} {
5014 global treediffs diffids treepending diffmergeid nullid nullid2
5017 catch {unset diffmergeid}
5018 if {![info exists treediffs($ids)] ||
5019 [lsearch -exact $ids $nullid] >= 0 ||
5020 [lsearch -exact $ids $nullid2] >= 0} {
5021 if {![info exists treepending]} {
5029 proc addtocflist {ids} {
5030 global treediffs cflist
5031 add_flist $treediffs($ids)
5035 proc diffcmd {ids flags} {
5036 global nullid nullid2
5038 set i [lsearch -exact $ids $nullid]
5039 set j [lsearch -exact $ids $nullid2]
5041 if {[llength $ids] > 1 && $j < 0} {
5042 # comparing working directory with some specific revision
5043 set cmd [concat | git diff-index $flags]
5045 lappend cmd -R [lindex $ids 1]
5047 lappend cmd [lindex $ids 0]
5050 # comparing working directory with index
5051 set cmd [concat | git diff-files $flags]
5056 } elseif {$j >= 0} {
5057 set cmd [concat | git diff-index --cached $flags]
5058 if {[llength $ids] > 1} {
5059 # comparing index with specific revision
5061 lappend cmd -R [lindex $ids 1]
5063 lappend cmd [lindex $ids 0]
5066 # comparing index with HEAD
5070 set cmd [concat | git diff-tree -r $flags $ids]
5075 proc gettreediffs {ids} {
5076 global treediff treepending
5078 set treepending $ids
5080 if {[catch {set gdtf [open [diffcmd $ids {--no-commit-id}] r]}]} return
5081 fconfigure $gdtf -blocking 0
5082 filerun $gdtf [list gettreediffline $gdtf $ids]
5085 proc gettreediffline {gdtf ids} {
5086 global treediff treediffs treepending diffids diffmergeid
5090 while {[incr nr] <= 1000 && [gets $gdtf line] >= 0} {
5091 set i [string first "\t" $line]
5093 set file [string range $line [expr {$i+1}] end]
5094 if {[string index $file 0] eq "\""} {
5095 set file [lindex $file 0]
5097 lappend treediff $file
5101 return [expr {$nr >= 1000? 2: 1}]
5104 set treediffs($ids) $treediff
5106 if {$cmitmode eq "tree"} {
5108 } elseif {$ids != $diffids} {
5109 if {![info exists diffmergeid]} {
5110 gettreediffs $diffids
5118 # empty string or positive integer
5119 proc diffcontextvalidate {v} {
5120 return [regexp {^(|[1-9][0-9]*)$} $v]
5123 proc diffcontextchange {n1 n2 op} {
5124 global diffcontextstring diffcontext
5126 if {[string is integer -strict $diffcontextstring]} {
5127 if {$diffcontextstring > 0} {
5128 set diffcontext $diffcontextstring
5134 proc getblobdiffs {ids} {
5135 global diffopts blobdifffd diffids env
5136 global diffinhdr treediffs
5139 set env(GIT_DIFF_OPTS) $diffopts
5140 if {[catch {set bdf [open [diffcmd $ids "-p -C --no-commit-id -U$diffcontext"] r]} err]} {
5141 puts "error getting diffs: $err"
5145 fconfigure $bdf -blocking 0
5146 set blobdifffd($ids) $bdf
5147 filerun $bdf [list getblobdiffline $bdf $diffids]
5150 proc setinlist {var i val} {
5153 while {[llength [set $var]] < $i} {
5156 if {[llength [set $var]] == $i} {
5163 proc makediffhdr {fname ids} {
5164 global ctext curdiffstart treediffs
5166 set i [lsearch -exact $treediffs($ids) $fname]
5168 setinlist difffilestart $i $curdiffstart
5170 set l [expr {(78 - [string length $fname]) / 2}]
5171 set pad [string range "----------------------------------------" 1 $l]
5172 $ctext insert $curdiffstart "$pad $fname $pad" filesep
5175 proc getblobdiffline {bdf ids} {
5176 global diffids blobdifffd ctext curdiffstart
5177 global diffnexthead diffnextnote difffilestart
5178 global diffinhdr treediffs
5181 $ctext conf -state normal
5182 while {[incr nr] <= 1000 && [gets $bdf line] >= 0} {
5183 if {$ids != $diffids || $bdf != $blobdifffd($ids)} {
5187 if {![string compare -length 11 "diff --git " $line]} {
5188 # trim off "diff --git "
5189 set line [string range $line 11 end]
5191 # start of a new file
5192 $ctext insert end "\n"
5193 set curdiffstart [$ctext index "end - 1c"]
5194 $ctext insert end "\n" filesep
5195 # If the name hasn't changed the length will be odd,
5196 # the middle char will be a space, and the two bits either
5197 # side will be a/name and b/name, or "a/name" and "b/name".
5198 # If the name has changed we'll get "rename from" and
5199 # "rename to" or "copy from" and "copy to" lines following this,
5200 # and we'll use them to get the filenames.
5201 # This complexity is necessary because spaces in the filename(s)
5202 # don't get escaped.
5203 set l [string length $line]
5204 set i [expr {$l / 2}]
5205 if {!(($l & 1) && [string index $line $i] eq " " &&
5206 [string range $line 2 [expr {$i - 1}]] eq \
5207 [string range $line [expr {$i + 3}] end])} {
5210 # unescape if quoted and chop off the a/ from the front
5211 if {[string index $line 0] eq "\""} {
5212 set fname [string range [lindex $line 0] 2 end]
5214 set fname [string range $line 2 [expr {$i - 1}]]
5216 makediffhdr $fname $ids
5218 } elseif {[regexp {^@@ -([0-9]+)(,[0-9]+)? \+([0-9]+)(,[0-9]+)? @@(.*)} \
5219 $line match f1l f1c f2l f2c rest]} {
5220 $ctext insert end "$line\n" hunksep
5223 } elseif {$diffinhdr} {
5224 if {![string compare -length 12 "rename from " $line] ||
5225 ![string compare -length 10 "copy from " $line]} {
5226 set fname [string range $line [expr 6 + [string first " from " $line] ] end]
5227 if {[string index $fname 0] eq "\""} {
5228 set fname [lindex $fname 0]
5230 set i [lsearch -exact $treediffs($ids) $fname]
5232 setinlist difffilestart $i $curdiffstart
5234 } elseif {![string compare -length 10 $line "rename to "] ||
5235 ![string compare -length 8 $line "copy to "]} {
5236 set fname [string range $line [expr 4 + [string first " to " $line] ] end]
5237 if {[string index $fname 0] eq "\""} {
5238 set fname [lindex $fname 0]
5240 makediffhdr $fname $ids
5241 } elseif {[string compare -length 3 $line "---"] == 0} {
5244 } elseif {[string compare -length 3 $line "+++"] == 0} {
5248 $ctext insert end "$line\n" filesep
5251 set x [string range $line 0 0]
5252 if {$x == "-" || $x == "+"} {
5253 set tag [expr {$x == "+"}]
5254 $ctext insert end "$line\n" d$tag
5255 } elseif {$x == " "} {
5256 $ctext insert end "$line\n"
5258 # "\ No newline at end of file",
5259 # or something else we don't recognize
5260 $ctext insert end "$line\n" hunksep
5264 $ctext conf -state disabled
5269 return [expr {$nr >= 1000? 2: 1}]
5272 proc changediffdisp {} {
5273 global ctext diffelide
5275 $ctext tag conf d0 -elide [lindex $diffelide 0]
5276 $ctext tag conf d1 -elide [lindex $diffelide 1]
5280 global difffilestart ctext
5281 set prev [lindex $difffilestart 0]
5282 set here [$ctext index @0,0]
5283 foreach loc $difffilestart {
5284 if {[$ctext compare $loc >= $here]} {
5294 global difffilestart ctext
5295 set here [$ctext index @0,0]
5296 foreach loc $difffilestart {
5297 if {[$ctext compare $loc > $here]} {
5304 proc clear_ctext {{first 1.0}} {
5305 global ctext smarktop smarkbot
5307 set l [lindex [split $first .] 0]
5308 if {![info exists smarktop] || [$ctext compare $first < $smarktop.0]} {
5311 if {![info exists smarkbot] || [$ctext compare $first < $smarkbot.0]} {
5314 $ctext delete $first end
5317 proc incrsearch {name ix op} {
5318 global ctext searchstring searchdirn
5320 $ctext tag remove found 1.0 end
5321 if {[catch {$ctext index anchor}]} {
5322 # no anchor set, use start of selection, or of visible area
5323 set sel [$ctext tag ranges sel]
5325 $ctext mark set anchor [lindex $sel 0]
5326 } elseif {$searchdirn eq "-forwards"} {
5327 $ctext mark set anchor @0,0
5329 $ctext mark set anchor @0,[winfo height $ctext]
5332 if {$searchstring ne {}} {
5333 set here [$ctext search $searchdirn -- $searchstring anchor]
5342 global sstring ctext searchstring searchdirn
5345 $sstring icursor end
5346 set searchdirn -forwards
5347 if {$searchstring ne {}} {
5348 set sel [$ctext tag ranges sel]
5350 set start "[lindex $sel 0] + 1c"
5351 } elseif {[catch {set start [$ctext index anchor]}]} {
5354 set match [$ctext search -count mlen -- $searchstring $start]
5355 $ctext tag remove sel 1.0 end
5361 set mend "$match + $mlen c"
5362 $ctext tag add sel $match $mend
5363 $ctext mark unset anchor
5367 proc dosearchback {} {
5368 global sstring ctext searchstring searchdirn
5371 $sstring icursor end
5372 set searchdirn -backwards
5373 if {$searchstring ne {}} {
5374 set sel [$ctext tag ranges sel]
5376 set start [lindex $sel 0]
5377 } elseif {[catch {set start [$ctext index anchor]}]} {
5378 set start @0,[winfo height $ctext]
5380 set match [$ctext search -backwards -count ml -- $searchstring $start]
5381 $ctext tag remove sel 1.0 end
5387 set mend "$match + $ml c"
5388 $ctext tag add sel $match $mend
5389 $ctext mark unset anchor
5393 proc searchmark {first last} {
5394 global ctext searchstring
5398 set match [$ctext search -count mlen -- $searchstring $mend $last.end]
5399 if {$match eq {}} break
5400 set mend "$match + $mlen c"
5401 $ctext tag add found $match $mend
5405 proc searchmarkvisible {doall} {
5406 global ctext smarktop smarkbot
5408 set topline [lindex [split [$ctext index @0,0] .] 0]
5409 set botline [lindex [split [$ctext index @0,[winfo height $ctext]] .] 0]
5410 if {$doall || $botline < $smarktop || $topline > $smarkbot} {
5411 # no overlap with previous
5412 searchmark $topline $botline
5413 set smarktop $topline
5414 set smarkbot $botline
5416 if {$topline < $smarktop} {
5417 searchmark $topline [expr {$smarktop-1}]
5418 set smarktop $topline
5420 if {$botline > $smarkbot} {
5421 searchmark [expr {$smarkbot+1}] $botline
5422 set smarkbot $botline
5427 proc scrolltext {f0 f1} {
5430 .bleft.sb set $f0 $f1
5431 if {$searchstring ne {}} {
5437 global linespc charspc canvx0 canvy0 mainfont
5438 global xspc1 xspc2 lthickness
5440 set linespc [font metrics $mainfont -linespace]
5441 set charspc [font measure $mainfont "m"]
5442 set canvy0 [expr {int(3 + 0.5 * $linespc)}]
5443 set canvx0 [expr {int(3 + 0.5 * $linespc)}]
5444 set lthickness [expr {int($linespc / 9) + 1}]
5445 set xspc1(0) $linespc
5453 set ymax [lindex [$canv cget -scrollregion] 3]
5454 if {$ymax eq {} || $ymax == 0} return
5455 set span [$canv yview]
5458 allcanvs yview moveto [lindex $span 0]
5460 if {[info exists selectedline]} {
5461 selectline $selectedline 0
5462 allcanvs yview moveto [lindex $span 0]
5466 proc incrfont {inc} {
5467 global mainfont textfont ctext canv phase cflist showrefstop
5468 global charspc tabstop
5469 global stopped entries
5471 set mainfont [lreplace $mainfont 1 1 [expr {[lindex $mainfont 1] + $inc}]]
5472 set textfont [lreplace $textfont 1 1 [expr {[lindex $textfont 1] + $inc}]]
5474 $ctext conf -font $textfont -tabs "[expr {$tabstop * $charspc}]"
5475 $cflist conf -font $textfont
5476 $ctext tag conf filesep -font [concat $textfont bold]
5477 foreach e $entries {
5478 $e conf -font $mainfont
5480 if {$phase eq "getcommits"} {
5481 $canv itemconf textitems -font $mainfont
5483 if {[info exists showrefstop] && [winfo exists $showrefstop]} {
5484 $showrefstop.list conf -font $mainfont
5490 global sha1entry sha1string
5491 if {[string length $sha1string] == 40} {
5492 $sha1entry delete 0 end
5496 proc sha1change {n1 n2 op} {
5497 global sha1string currentid sha1but
5498 if {$sha1string == {}
5499 || ([info exists currentid] && $sha1string == $currentid)} {
5504 if {[$sha1but cget -state] == $state} return
5505 if {$state == "normal"} {
5506 $sha1but conf -state normal -relief raised -text "Goto: "
5508 $sha1but conf -state disabled -relief flat -text "SHA1 ID: "
5512 proc gotocommit {} {
5513 global sha1string currentid commitrow tagids headids
5514 global displayorder numcommits curview
5516 if {$sha1string == {}
5517 || ([info exists currentid] && $sha1string == $currentid)} return
5518 if {[info exists tagids($sha1string)]} {
5519 set id $tagids($sha1string)
5520 } elseif {[info exists headids($sha1string)]} {
5521 set id $headids($sha1string)
5523 set id [string tolower $sha1string]
5524 if {[regexp {^[0-9a-f]{4,39}$} $id]} {
5526 foreach i $displayorder {
5527 if {[string match $id* $i]} {
5531 if {$matches ne {}} {
5532 if {[llength $matches] > 1} {
5533 error_popup "Short SHA1 id $id is ambiguous"
5536 set id [lindex $matches 0]
5540 if {[info exists commitrow($curview,$id)]} {
5541 selectline $commitrow($curview,$id) 1
5544 if {[regexp {^[0-9a-fA-F]{4,}$} $sha1string]} {
5549 error_popup "$type $sha1string is not known"
5552 proc lineenter {x y id} {
5553 global hoverx hovery hoverid hovertimer
5554 global commitinfo canv
5556 if {![info exists commitinfo($id)] && ![getcommit $id]} return
5560 if {[info exists hovertimer]} {
5561 after cancel $hovertimer
5563 set hovertimer [after 500 linehover]
5567 proc linemotion {x y id} {
5568 global hoverx hovery hoverid hovertimer
5570 if {[info exists hoverid] && $id == $hoverid} {
5573 if {[info exists hovertimer]} {
5574 after cancel $hovertimer
5576 set hovertimer [after 500 linehover]
5580 proc lineleave {id} {
5581 global hoverid hovertimer canv
5583 if {[info exists hoverid] && $id == $hoverid} {
5585 if {[info exists hovertimer]} {
5586 after cancel $hovertimer
5594 global hoverx hovery hoverid hovertimer
5595 global canv linespc lthickness
5596 global commitinfo mainfont
5598 set text [lindex $commitinfo($hoverid) 0]
5599 set ymax [lindex [$canv cget -scrollregion] 3]
5600 if {$ymax == {}} return
5601 set yfrac [lindex [$canv yview] 0]
5602 set x [expr {$hoverx + 2 * $linespc}]
5603 set y [expr {$hovery + $yfrac * $ymax - $linespc / 2}]
5604 set x0 [expr {$x - 2 * $lthickness}]
5605 set y0 [expr {$y - 2 * $lthickness}]
5606 set x1 [expr {$x + [font measure $mainfont $text] + 2 * $lthickness}]
5607 set y1 [expr {$y + $linespc + 2 * $lthickness}]
5608 set t [$canv create rectangle $x0 $y0 $x1 $y1 \
5609 -fill \#ffff80 -outline black -width 1 -tags hover]
5611 set t [$canv create text $x $y -anchor nw -text $text -tags hover \
5616 proc clickisonarrow {id y} {
5619 set ranges [rowranges $id]
5620 set thresh [expr {2 * $lthickness + 6}]
5621 set n [expr {[llength $ranges] - 1}]
5622 for {set i 1} {$i < $n} {incr i} {
5623 set row [lindex $ranges $i]
5624 if {abs([yc $row] - $y) < $thresh} {
5631 proc arrowjump {id n y} {
5634 # 1 <-> 2, 3 <-> 4, etc...
5635 set n [expr {(($n - 1) ^ 1) + 1}]
5636 set row [lindex [rowranges $id] $n]
5638 set ymax [lindex [$canv cget -scrollregion] 3]
5639 if {$ymax eq {} || $ymax <= 0} return
5640 set view [$canv yview]
5641 set yspan [expr {[lindex $view 1] - [lindex $view 0]}]
5642 set yfrac [expr {$yt / $ymax - $yspan / 2}]
5646 allcanvs yview moveto $yfrac
5649 proc lineclick {x y id isnew} {
5650 global ctext commitinfo children canv thickerline curview
5652 if {![info exists commitinfo($id)] && ![getcommit $id]} return
5657 # draw this line thicker than normal
5661 set ymax [lindex [$canv cget -scrollregion] 3]
5662 if {$ymax eq {}} return
5663 set yfrac [lindex [$canv yview] 0]
5664 set y [expr {$y + $yfrac * $ymax}]
5666 set dirn [clickisonarrow $id $y]
5668 arrowjump $id $dirn $y
5673 addtohistory [list lineclick $x $y $id 0]
5675 # fill the details pane with info about this line
5676 $ctext conf -state normal
5678 $ctext tag conf link -foreground blue -underline 1
5679 $ctext tag bind link <Enter> { %W configure -cursor hand2 }
5680 $ctext tag bind link <Leave> { %W configure -cursor $curtextcursor }
5681 $ctext insert end "Parent:\t"
5682 $ctext insert end $id [list link link0]
5683 $ctext tag bind link0 <1> [list selbyid $id]
5684 set info $commitinfo($id)
5685 $ctext insert end "\n\t[lindex $info 0]\n"
5686 $ctext insert end "\tAuthor:\t[lindex $info 1]\n"
5687 set date [formatdate [lindex $info 2]]
5688 $ctext insert end "\tDate:\t$date\n"
5689 set kids $children($curview,$id)
5691 $ctext insert end "\nChildren:"
5693 foreach child $kids {
5695 if {![info exists commitinfo($child)] && ![getcommit $child]} continue
5696 set info $commitinfo($child)
5697 $ctext insert end "\n\t"
5698 $ctext insert end $child [list link link$i]
5699 $ctext tag bind link$i <1> [list selbyid $child]
5700 $ctext insert end "\n\t[lindex $info 0]"
5701 $ctext insert end "\n\tAuthor:\t[lindex $info 1]"
5702 set date [formatdate [lindex $info 2]]
5703 $ctext insert end "\n\tDate:\t$date\n"
5706 $ctext conf -state disabled
5710 proc normalline {} {
5712 if {[info exists thickerline]} {
5720 global commitrow curview
5721 if {[info exists commitrow($curview,$id)]} {
5722 selectline $commitrow($curview,$id) 1
5728 if {![info exists startmstime]} {
5729 set startmstime [clock clicks -milliseconds]
5731 return [format "%.3f" [expr {([clock click -milliseconds] - $startmstime) / 1000.0}]]
5734 proc rowmenu {x y id} {
5735 global rowctxmenu commitrow selectedline rowmenuid curview
5736 global nullid nullid2 fakerowmenu mainhead
5739 if {![info exists selectedline]
5740 || $commitrow($curview,$id) eq $selectedline} {
5745 if {$id ne $nullid && $id ne $nullid2} {
5746 set menu $rowctxmenu
5747 $menu entryconfigure 7 -label "Reset $mainhead branch to here"
5749 set menu $fakerowmenu
5751 $menu entryconfigure "Diff this*" -state $state
5752 $menu entryconfigure "Diff selected*" -state $state
5753 $menu entryconfigure "Make patch" -state $state
5754 tk_popup $menu $x $y
5757 proc diffvssel {dirn} {
5758 global rowmenuid selectedline displayorder
5760 if {![info exists selectedline]} return
5762 set oldid [lindex $displayorder $selectedline]
5763 set newid $rowmenuid
5765 set oldid $rowmenuid
5766 set newid [lindex $displayorder $selectedline]
5768 addtohistory [list doseldiff $oldid $newid]
5769 doseldiff $oldid $newid
5772 proc doseldiff {oldid newid} {
5776 $ctext conf -state normal
5779 $ctext insert end "From "
5780 $ctext tag conf link -foreground blue -underline 1
5781 $ctext tag bind link <Enter> { %W configure -cursor hand2 }
5782 $ctext tag bind link <Leave> { %W configure -cursor $curtextcursor }
5783 $ctext tag bind link0 <1> [list selbyid $oldid]
5784 $ctext insert end $oldid [list link link0]
5785 $ctext insert end "\n "
5786 $ctext insert end [lindex $commitinfo($oldid) 0]
5787 $ctext insert end "\n\nTo "
5788 $ctext tag bind link1 <1> [list selbyid $newid]
5789 $ctext insert end $newid [list link link1]
5790 $ctext insert end "\n "
5791 $ctext insert end [lindex $commitinfo($newid) 0]
5792 $ctext insert end "\n"
5793 $ctext conf -state disabled
5794 $ctext tag remove found 1.0 end
5795 startdiff [list $oldid $newid]
5799 global rowmenuid currentid commitinfo patchtop patchnum
5801 if {![info exists currentid]} return
5802 set oldid $currentid
5803 set oldhead [lindex $commitinfo($oldid) 0]
5804 set newid $rowmenuid
5805 set newhead [lindex $commitinfo($newid) 0]
5808 catch {destroy $top}
5810 label $top.title -text "Generate patch"
5811 grid $top.title - -pady 10
5812 label $top.from -text "From:"
5813 entry $top.fromsha1 -width 40 -relief flat
5814 $top.fromsha1 insert 0 $oldid
5815 $top.fromsha1 conf -state readonly
5816 grid $top.from $top.fromsha1 -sticky w
5817 entry $top.fromhead -width 60 -relief flat
5818 $top.fromhead insert 0 $oldhead
5819 $top.fromhead conf -state readonly
5820 grid x $top.fromhead -sticky w
5821 label $top.to -text "To:"
5822 entry $top.tosha1 -width 40 -relief flat
5823 $top.tosha1 insert 0 $newid
5824 $top.tosha1 conf -state readonly
5825 grid $top.to $top.tosha1 -sticky w
5826 entry $top.tohead -width 60 -relief flat
5827 $top.tohead insert 0 $newhead
5828 $top.tohead conf -state readonly
5829 grid x $top.tohead -sticky w
5830 button $top.rev -text "Reverse" -command mkpatchrev -padx 5
5831 grid $top.rev x -pady 10
5832 label $top.flab -text "Output file:"
5833 entry $top.fname -width 60
5834 $top.fname insert 0 [file normalize "patch$patchnum.patch"]
5836 grid $top.flab $top.fname -sticky w
5838 button $top.buts.gen -text "Generate" -command mkpatchgo
5839 button $top.buts.can -text "Cancel" -command mkpatchcan
5840 grid $top.buts.gen $top.buts.can
5841 grid columnconfigure $top.buts 0 -weight 1 -uniform a
5842 grid columnconfigure $top.buts 1 -weight 1 -uniform a
5843 grid $top.buts - -pady 10 -sticky ew
5847 proc mkpatchrev {} {
5850 set oldid [$patchtop.fromsha1 get]
5851 set oldhead [$patchtop.fromhead get]
5852 set newid [$patchtop.tosha1 get]
5853 set newhead [$patchtop.tohead get]
5854 foreach e [list fromsha1 fromhead tosha1 tohead] \
5855 v [list $newid $newhead $oldid $oldhead] {
5856 $patchtop.$e conf -state normal
5857 $patchtop.$e delete 0 end
5858 $patchtop.$e insert 0 $v
5859 $patchtop.$e conf -state readonly
5864 global patchtop nullid nullid2
5866 set oldid [$patchtop.fromsha1 get]
5867 set newid [$patchtop.tosha1 get]
5868 set fname [$patchtop.fname get]
5869 set cmd [diffcmd [list $oldid $newid] -p]
5870 lappend cmd >$fname &
5871 if {[catch {eval exec $cmd} err]} {
5872 error_popup "Error creating patch: $err"
5874 catch {destroy $patchtop}
5878 proc mkpatchcan {} {
5881 catch {destroy $patchtop}
5886 global rowmenuid mktagtop commitinfo
5890 catch {destroy $top}
5892 label $top.title -text "Create tag"
5893 grid $top.title - -pady 10
5894 label $top.id -text "ID:"
5895 entry $top.sha1 -width 40 -relief flat
5896 $top.sha1 insert 0 $rowmenuid
5897 $top.sha1 conf -state readonly
5898 grid $top.id $top.sha1 -sticky w
5899 entry $top.head -width 60 -relief flat
5900 $top.head insert 0 [lindex $commitinfo($rowmenuid) 0]
5901 $top.head conf -state readonly
5902 grid x $top.head -sticky w
5903 label $top.tlab -text "Tag name:"
5904 entry $top.tag -width 60
5905 grid $top.tlab $top.tag -sticky w
5907 button $top.buts.gen -text "Create" -command mktaggo
5908 button $top.buts.can -text "Cancel" -command mktagcan
5909 grid $top.buts.gen $top.buts.can
5910 grid columnconfigure $top.buts 0 -weight 1 -uniform a
5911 grid columnconfigure $top.buts 1 -weight 1 -uniform a
5912 grid $top.buts - -pady 10 -sticky ew
5917 global mktagtop env tagids idtags
5919 set id [$mktagtop.sha1 get]
5920 set tag [$mktagtop.tag get]
5922 error_popup "No tag name specified"
5925 if {[info exists tagids($tag)]} {
5926 error_popup "Tag \"$tag\" already exists"
5931 set fname [file join $dir "refs/tags" $tag]
5932 set f [open $fname w]
5936 error_popup "Error creating tag: $err"
5940 set tagids($tag) $id
5941 lappend idtags($id) $tag
5948 proc redrawtags {id} {
5949 global canv linehtag commitrow idpos selectedline curview
5950 global mainfont canvxmax iddrawn
5952 if {![info exists commitrow($curview,$id)]} return
5953 if {![info exists iddrawn($id)]} return
5954 drawcommits $commitrow($curview,$id)
5955 $canv delete tag.$id
5956 set xt [eval drawtags $id $idpos($id)]
5957 $canv coords $linehtag($commitrow($curview,$id)) $xt [lindex $idpos($id) 2]
5958 set text [$canv itemcget $linehtag($commitrow($curview,$id)) -text]
5959 set xr [expr {$xt + [font measure $mainfont $text]}]
5960 if {$xr > $canvxmax} {
5964 if {[info exists selectedline]
5965 && $selectedline == $commitrow($curview,$id)} {
5966 selectline $selectedline 0
5973 catch {destroy $mktagtop}
5982 proc writecommit {} {
5983 global rowmenuid wrcomtop commitinfo wrcomcmd
5985 set top .writecommit
5987 catch {destroy $top}
5989 label $top.title -text "Write commit to file"
5990 grid $top.title - -pady 10
5991 label $top.id -text "ID:"
5992 entry $top.sha1 -width 40 -relief flat
5993 $top.sha1 insert 0 $rowmenuid
5994 $top.sha1 conf -state readonly
5995 grid $top.id $top.sha1 -sticky w
5996 entry $top.head -width 60 -relief flat
5997 $top.head insert 0 [lindex $commitinfo($rowmenuid) 0]
5998 $top.head conf -state readonly
5999 grid x $top.head -sticky w
6000 label $top.clab -text "Command:"
6001 entry $top.cmd -width 60 -textvariable wrcomcmd
6002 grid $top.clab $top.cmd -sticky w -pady 10
6003 label $top.flab -text "Output file:"
6004 entry $top.fname -width 60
6005 $top.fname insert 0 [file normalize "commit-[string range $rowmenuid 0 6]"]
6006 grid $top.flab $top.fname -sticky w
6008 button $top.buts.gen -text "Write" -command wrcomgo
6009 button $top.buts.can -text "Cancel" -command wrcomcan
6010 grid $top.buts.gen $top.buts.can
6011 grid columnconfigure $top.buts 0 -weight 1 -uniform a
6012 grid columnconfigure $top.buts 1 -weight 1 -uniform a
6013 grid $top.buts - -pady 10 -sticky ew
6020 set id [$wrcomtop.sha1 get]
6021 set cmd "echo $id | [$wrcomtop.cmd get]"
6022 set fname [$wrcomtop.fname get]
6023 if {[catch {exec sh -c $cmd >$fname &} err]} {
6024 error_popup "Error writing commit: $err"
6026 catch {destroy $wrcomtop}
6033 catch {destroy $wrcomtop}
6038 global rowmenuid mkbrtop
6041 catch {destroy $top}
6043 label $top.title -text "Create new branch"
6044 grid $top.title - -pady 10
6045 label $top.id -text "ID:"
6046 entry $top.sha1 -width 40 -relief flat
6047 $top.sha1 insert 0 $rowmenuid
6048 $top.sha1 conf -state readonly
6049 grid $top.id $top.sha1 -sticky w
6050 label $top.nlab -text "Name:"
6051 entry $top.name -width 40
6052 grid $top.nlab $top.name -sticky w
6054 button $top.buts.go -text "Create" -command [list mkbrgo $top]
6055 button $top.buts.can -text "Cancel" -command "catch {destroy $top}"
6056 grid $top.buts.go $top.buts.can
6057 grid columnconfigure $top.buts 0 -weight 1 -uniform a
6058 grid columnconfigure $top.buts 1 -weight 1 -uniform a
6059 grid $top.buts - -pady 10 -sticky ew
6064 global headids idheads
6066 set name [$top.name get]
6067 set id [$top.sha1 get]
6069 error_popup "Please specify a name for the new branch"
6072 catch {destroy $top}
6076 exec git branch $name $id
6081 set headids($name) $id
6082 lappend idheads($id) $name
6091 proc cherrypick {} {
6092 global rowmenuid curview commitrow
6095 set oldhead [exec git rev-parse HEAD]
6096 set dheads [descheads $rowmenuid]
6097 if {$dheads ne {} && [lsearch -exact $dheads $oldhead] >= 0} {
6098 set ok [confirm_popup "Commit [string range $rowmenuid 0 7] is already\
6099 included in branch $mainhead -- really re-apply it?"]
6104 # Unfortunately git-cherry-pick writes stuff to stderr even when
6105 # no error occurs, and exec takes that as an indication of error...
6106 if {[catch {exec sh -c "git cherry-pick -r $rowmenuid 2>&1"} err]} {
6111 set newhead [exec git rev-parse HEAD]
6112 if {$newhead eq $oldhead} {
6114 error_popup "No changes committed"
6117 addnewchild $newhead $oldhead
6118 if {[info exists commitrow($curview,$oldhead)]} {
6119 insertrow $commitrow($curview,$oldhead) $newhead
6120 if {$mainhead ne {}} {
6121 movehead $newhead $mainhead
6122 movedhead $newhead $mainhead
6131 global mainheadid mainhead rowmenuid confirm_ok resettype
6132 global showlocalchanges
6135 set w ".confirmreset"
6138 wm title $w "Confirm reset"
6139 message $w.m -text \
6140 "Reset branch $mainhead to [string range $rowmenuid 0 7]?" \
6141 -justify center -aspect 1000
6142 pack $w.m -side top -fill x -padx 20 -pady 20
6143 frame $w.f -relief sunken -border 2
6144 message $w.f.rt -text "Reset type:" -aspect 1000
6145 grid $w.f.rt -sticky w
6147 radiobutton $w.f.soft -value soft -variable resettype -justify left \
6148 -text "Soft: Leave working tree and index untouched"
6149 grid $w.f.soft -sticky w
6150 radiobutton $w.f.mixed -value mixed -variable resettype -justify left \
6151 -text "Mixed: Leave working tree untouched, reset index"
6152 grid $w.f.mixed -sticky w
6153 radiobutton $w.f.hard -value hard -variable resettype -justify left \
6154 -text "Hard: Reset working tree and index\n(discard ALL local changes)"
6155 grid $w.f.hard -sticky w
6156 pack $w.f -side top -fill x
6157 button $w.ok -text OK -command "set confirm_ok 1; destroy $w"
6158 pack $w.ok -side left -fill x -padx 20 -pady 20
6159 button $w.cancel -text Cancel -command "destroy $w"
6160 pack $w.cancel -side right -fill x -padx 20 -pady 20
6161 bind $w <Visibility> "grab $w; focus $w"
6163 if {!$confirm_ok} return
6164 if {[catch {set fd [open \
6165 [list | sh -c "git reset --$resettype $rowmenuid 2>&1"] r]} err]} {
6169 set w ".resetprogress"
6170 filerun $fd [list readresetstat $fd $w]
6173 wm title $w "Reset progress"
6174 message $w.m -text "Reset in progress, please wait..." \
6175 -justify center -aspect 1000
6176 pack $w.m -side top -fill x -padx 20 -pady 5
6177 canvas $w.c -width 150 -height 20 -bg white
6178 $w.c create rect 0 0 0 20 -fill green -tags rect
6179 pack $w.c -side top -fill x -padx 20 -pady 5 -expand 1
6184 proc readresetstat {fd w} {
6185 global mainhead mainheadid showlocalchanges
6187 if {[gets $fd line] >= 0} {
6188 if {[regexp {([0-9]+)% \(([0-9]+)/([0-9]+)\)} $line match p m n]} {
6189 set x [expr {($m * 150) / $n}]
6190 $w.c coords rect 0 0 $x 20
6196 if {[catch {close $fd} err]} {
6199 set oldhead $mainheadid
6200 set newhead [exec git rev-parse HEAD]
6201 if {$newhead ne $oldhead} {
6202 movehead $newhead $mainhead
6203 movedhead $newhead $mainhead
6204 set mainheadid $newhead
6208 if {$showlocalchanges} {
6214 # context menu for a head
6215 proc headmenu {x y id head} {
6216 global headmenuid headmenuhead headctxmenu mainhead
6219 set headmenuhead $head
6221 if {$head eq $mainhead} {
6224 $headctxmenu entryconfigure 0 -state $state
6225 $headctxmenu entryconfigure 1 -state $state
6226 tk_popup $headctxmenu $x $y
6230 global headmenuid headmenuhead mainhead headids
6231 global showlocalchanges mainheadid
6233 # check the tree is clean first??
6234 set oldmainhead $mainhead
6239 exec git checkout -q $headmenuhead
6245 set mainhead $headmenuhead
6246 set mainheadid $headmenuid
6247 if {[info exists headids($oldmainhead)]} {
6248 redrawtags $headids($oldmainhead)
6250 redrawtags $headmenuid
6252 if {$showlocalchanges} {
6258 global headmenuid headmenuhead mainhead
6261 set head $headmenuhead
6263 # this check shouldn't be needed any more...
6264 if {$head eq $mainhead} {
6265 error_popup "Cannot delete the currently checked-out branch"
6268 set dheads [descheads $id]
6269 if {[llength $dheads] == 1 && $idheads($dheads) eq $head} {
6270 # the stuff on this branch isn't on any other branch
6271 if {![confirm_popup "The commits on branch $head aren't on any other\
6272 branch.\nReally delete branch $head?"]} return
6276 if {[catch {exec git branch -D $head} err]} {
6281 removehead $id $head
6282 removedhead $id $head
6289 # Display a list of tags and heads
6291 global showrefstop bgcolor fgcolor selectbgcolor mainfont
6292 global bglist fglist uifont reflistfilter reflist maincursor
6295 set showrefstop $top
6296 if {[winfo exists $top]} {
6302 wm title $top "Tags and heads: [file tail [pwd]]"
6303 text $top.list -background $bgcolor -foreground $fgcolor \
6304 -selectbackground $selectbgcolor -font $mainfont \
6305 -xscrollcommand "$top.xsb set" -yscrollcommand "$top.ysb set" \
6306 -width 30 -height 20 -cursor $maincursor \
6307 -spacing1 1 -spacing3 1 -state disabled
6308 $top.list tag configure highlight -background $selectbgcolor
6309 lappend bglist $top.list
6310 lappend fglist $top.list
6311 scrollbar $top.ysb -command "$top.list yview" -orient vertical
6312 scrollbar $top.xsb -command "$top.list xview" -orient horizontal
6313 grid $top.list $top.ysb -sticky nsew
6314 grid $top.xsb x -sticky ew
6316 label $top.f.l -text "Filter: " -font $uifont
6317 entry $top.f.e -width 20 -textvariable reflistfilter -font $uifont
6318 set reflistfilter "*"
6319 trace add variable reflistfilter write reflistfilter_change
6320 pack $top.f.e -side right -fill x -expand 1
6321 pack $top.f.l -side left
6322 grid $top.f - -sticky ew -pady 2
6323 button $top.close -command [list destroy $top] -text "Close" \
6326 grid columnconfigure $top 0 -weight 1
6327 grid rowconfigure $top 0 -weight 1
6328 bind $top.list <1> {break}
6329 bind $top.list <B1-Motion> {break}
6330 bind $top.list <ButtonRelease-1> {sel_reflist %W %x %y; break}
6335 proc sel_reflist {w x y} {
6336 global showrefstop reflist headids tagids otherrefids
6338 if {![winfo exists $showrefstop]} return
6339 set l [lindex [split [$w index "@$x,$y"] "."] 0]
6340 set ref [lindex $reflist [expr {$l-1}]]
6341 set n [lindex $ref 0]
6342 switch -- [lindex $ref 1] {
6343 "H" {selbyid $headids($n)}
6344 "T" {selbyid $tagids($n)}
6345 "o" {selbyid $otherrefids($n)}
6347 $showrefstop.list tag add highlight $l.0 "$l.0 lineend"
6350 proc unsel_reflist {} {
6353 if {![info exists showrefstop] || ![winfo exists $showrefstop]} return
6354 $showrefstop.list tag remove highlight 0.0 end
6357 proc reflistfilter_change {n1 n2 op} {
6358 global reflistfilter
6360 after cancel refill_reflist
6361 after 200 refill_reflist
6364 proc refill_reflist {} {
6365 global reflist reflistfilter showrefstop headids tagids otherrefids
6366 global commitrow curview commitinterest
6368 if {![info exists showrefstop] || ![winfo exists $showrefstop]} return
6370 foreach n [array names headids] {
6371 if {[string match $reflistfilter $n]} {
6372 if {[info exists commitrow($curview,$headids($n))]} {
6373 lappend refs [list $n H]
6375 set commitinterest($headids($n)) {run refill_reflist}
6379 foreach n [array names tagids] {
6380 if {[string match $reflistfilter $n]} {
6381 if {[info exists commitrow($curview,$tagids($n))]} {
6382 lappend refs [list $n T]
6384 set commitinterest($tagids($n)) {run refill_reflist}
6388 foreach n [array names otherrefids] {
6389 if {[string match $reflistfilter $n]} {
6390 if {[info exists commitrow($curview,$otherrefids($n))]} {
6391 lappend refs [list $n o]
6393 set commitinterest($otherrefids($n)) {run refill_reflist}
6397 set refs [lsort -index 0 $refs]
6398 if {$refs eq $reflist} return
6400 # Update the contents of $showrefstop.list according to the
6401 # differences between $reflist (old) and $refs (new)
6402 $showrefstop.list conf -state normal
6403 $showrefstop.list insert end "\n"
6406 while {$i < [llength $reflist] || $j < [llength $refs]} {
6407 if {$i < [llength $reflist]} {
6408 if {$j < [llength $refs]} {
6409 set cmp [string compare [lindex $reflist $i 0] \
6410 [lindex $refs $j 0]]
6412 set cmp [string compare [lindex $reflist $i 1] \
6413 [lindex $refs $j 1]]
6423 $showrefstop.list delete "[expr {$j+1}].0" "[expr {$j+2}].0"
6431 set l [expr {$j + 1}]
6432 $showrefstop.list image create $l.0 -align baseline \
6433 -image reficon-[lindex $refs $j 1] -padx 2
6434 $showrefstop.list insert $l.1 "[lindex $refs $j 0]\n"
6440 # delete last newline
6441 $showrefstop.list delete end-2c end-1c
6442 $showrefstop.list conf -state disabled
6445 # Stuff for finding nearby tags
6446 proc getallcommits {} {
6447 global allcommits allids nbmp nextarc seeds
6449 if {![info exists allcommits]} {
6457 set cmd [concat | git rev-list --all --parents]
6461 set fd [open $cmd r]
6462 fconfigure $fd -blocking 0
6465 filerun $fd [list getallclines $fd]
6468 # Since most commits have 1 parent and 1 child, we group strings of
6469 # such commits into "arcs" joining branch/merge points (BMPs), which
6470 # are commits that either don't have 1 parent or don't have 1 child.
6472 # arcnos(id) - incoming arcs for BMP, arc we're on for other nodes
6473 # arcout(id) - outgoing arcs for BMP
6474 # arcids(a) - list of IDs on arc including end but not start
6475 # arcstart(a) - BMP ID at start of arc
6476 # arcend(a) - BMP ID at end of arc
6477 # growing(a) - arc a is still growing
6478 # arctags(a) - IDs out of arcids (excluding end) that have tags
6479 # archeads(a) - IDs out of arcids (excluding end) that have heads
6480 # The start of an arc is at the descendent end, so "incoming" means
6481 # coming from descendents, and "outgoing" means going towards ancestors.
6483 proc getallclines {fd} {
6484 global allids allparents allchildren idtags idheads nextarc nbmp
6485 global arcnos arcids arctags arcout arcend arcstart archeads growing
6486 global seeds allcommits
6489 while {[incr nid] <= 1000 && [gets $fd line] >= 0} {
6490 set id [lindex $line 0]
6491 if {[info exists allparents($id)]} {
6496 set olds [lrange $line 1 end]
6497 set allparents($id) $olds
6498 if {![info exists allchildren($id)]} {
6499 set allchildren($id) {}
6504 if {[llength $olds] == 1 && [llength $a] == 1} {
6505 lappend arcids($a) $id
6506 if {[info exists idtags($id)]} {
6507 lappend arctags($a) $id
6509 if {[info exists idheads($id)]} {
6510 lappend archeads($a) $id
6512 if {[info exists allparents($olds)]} {
6513 # seen parent already
6514 if {![info exists arcout($olds)]} {
6517 lappend arcids($a) $olds
6518 set arcend($a) $olds
6521 lappend allchildren($olds) $id
6522 lappend arcnos($olds) $a
6527 foreach a $arcnos($id) {
6528 lappend arcids($a) $id
6535 lappend allchildren($p) $id
6536 set a [incr nextarc]
6537 set arcstart($a) $id
6544 if {[info exists allparents($p)]} {
6545 # seen it already, may need to make a new branch
6546 if {![info exists arcout($p)]} {
6549 lappend arcids($a) $p
6553 lappend arcnos($p) $a
6558 global cached_dheads cached_dtags cached_atags
6559 catch {unset cached_dheads}
6560 catch {unset cached_dtags}
6561 catch {unset cached_atags}
6564 return [expr {$nid >= 1000? 2: 1}]
6567 if {[incr allcommits -1] == 0} {
6574 proc recalcarc {a} {
6575 global arctags archeads arcids idtags idheads
6579 foreach id [lrange $arcids($a) 0 end-1] {
6580 if {[info exists idtags($id)]} {
6583 if {[info exists idheads($id)]} {
6588 set archeads($a) $ah
6592 global arcnos arcids nextarc nbmp arctags archeads idtags idheads
6593 global arcstart arcend arcout allparents growing
6596 if {[llength $a] != 1} {
6597 puts "oops splitarc called but [llength $a] arcs already"
6601 set i [lsearch -exact $arcids($a) $p]
6603 puts "oops splitarc $p not in arc $a"
6606 set na [incr nextarc]
6607 if {[info exists arcend($a)]} {
6608 set arcend($na) $arcend($a)
6610 set l [lindex $allparents([lindex $arcids($a) end]) 0]
6611 set j [lsearch -exact $arcnos($l) $a]
6612 set arcnos($l) [lreplace $arcnos($l) $j $j $na]
6614 set tail [lrange $arcids($a) [expr {$i+1}] end]
6615 set arcids($a) [lrange $arcids($a) 0 $i]
6617 set arcstart($na) $p
6619 set arcids($na) $tail
6620 if {[info exists growing($a)]} {
6627 if {[llength $arcnos($id)] == 1} {
6630 set j [lsearch -exact $arcnos($id) $a]
6631 set arcnos($id) [lreplace $arcnos($id) $j $j $na]
6635 # reconstruct tags and heads lists
6636 if {$arctags($a) ne {} || $archeads($a) ne {}} {
6641 set archeads($na) {}
6645 # Update things for a new commit added that is a child of one
6646 # existing commit. Used when cherry-picking.
6647 proc addnewchild {id p} {
6648 global allids allparents allchildren idtags nextarc nbmp
6649 global arcnos arcids arctags arcout arcend arcstart archeads growing
6650 global seeds allcommits
6652 if {![info exists allcommits]} return
6654 set allparents($id) [list $p]
6655 set allchildren($id) {}
6659 lappend allchildren($p) $id
6660 set a [incr nextarc]
6661 set arcstart($a) $id
6664 set arcids($a) [list $p]
6666 if {![info exists arcout($p)]} {
6669 lappend arcnos($p) $a
6670 set arcout($id) [list $a]
6673 # Returns 1 if a is an ancestor of b, -1 if b is an ancestor of a,
6674 # or 0 if neither is true.
6675 proc anc_or_desc {a b} {
6676 global arcout arcstart arcend arcnos cached_isanc
6678 if {$arcnos($a) eq $arcnos($b)} {
6679 # Both are on the same arc(s); either both are the same BMP,
6680 # or if one is not a BMP, the other is also not a BMP or is
6681 # the BMP at end of the arc (and it only has 1 incoming arc).
6682 # Or both can be BMPs with no incoming arcs.
6683 if {$a eq $b || $arcnos($a) eq {}} {
6686 # assert {[llength $arcnos($a)] == 1}
6687 set arc [lindex $arcnos($a) 0]
6688 set i [lsearch -exact $arcids($arc) $a]
6689 set j [lsearch -exact $arcids($arc) $b]
6690 if {$i < 0 || $i > $j} {
6697 if {![info exists arcout($a)]} {
6698 set arc [lindex $arcnos($a) 0]
6699 if {[info exists arcend($arc)]} {
6700 set aend $arcend($arc)
6704 set a $arcstart($arc)
6708 if {![info exists arcout($b)]} {
6709 set arc [lindex $arcnos($b) 0]
6710 if {[info exists arcend($arc)]} {
6711 set bend $arcend($arc)
6715 set b $arcstart($arc)
6725 if {[info exists cached_isanc($a,$bend)]} {
6726 if {$cached_isanc($a,$bend)} {
6730 if {[info exists cached_isanc($b,$aend)]} {
6731 if {$cached_isanc($b,$aend)} {
6734 if {[info exists cached_isanc($a,$bend)]} {
6739 set todo [list $a $b]
6742 for {set i 0} {$i < [llength $todo]} {incr i} {
6743 set x [lindex $todo $i]
6744 if {$anc($x) eq {}} {
6747 foreach arc $arcnos($x) {
6748 set xd $arcstart($arc)
6750 set cached_isanc($a,$bend) 1
6751 set cached_isanc($b,$aend) 0
6753 } elseif {$xd eq $aend} {
6754 set cached_isanc($b,$aend) 1
6755 set cached_isanc($a,$bend) 0
6758 if {![info exists anc($xd)]} {
6759 set anc($xd) $anc($x)
6761 } elseif {$anc($xd) ne $anc($x)} {
6766 set cached_isanc($a,$bend) 0
6767 set cached_isanc($b,$aend) 0
6771 # This identifies whether $desc has an ancestor that is
6772 # a growing tip of the graph and which is not an ancestor of $anc
6773 # and returns 0 if so and 1 if not.
6774 # If we subsequently discover a tag on such a growing tip, and that
6775 # turns out to be a descendent of $anc (which it could, since we
6776 # don't necessarily see children before parents), then $desc
6777 # isn't a good choice to display as a descendent tag of
6778 # $anc (since it is the descendent of another tag which is
6779 # a descendent of $anc). Similarly, $anc isn't a good choice to
6780 # display as a ancestor tag of $desc.
6782 proc is_certain {desc anc} {
6783 global arcnos arcout arcstart arcend growing problems
6786 if {[llength $arcnos($anc)] == 1} {
6787 # tags on the same arc are certain
6788 if {$arcnos($desc) eq $arcnos($anc)} {
6791 if {![info exists arcout($anc)]} {
6792 # if $anc is partway along an arc, use the start of the arc instead
6793 set a [lindex $arcnos($anc) 0]
6794 set anc $arcstart($a)
6797 if {[llength $arcnos($desc)] > 1 || [info exists arcout($desc)]} {
6800 set a [lindex $arcnos($desc) 0]
6806 set anclist [list $x]
6810 for {set i 0} {$i < [llength $anclist] && ($nnh > 0 || $ngrowanc > 0)} {incr i} {
6811 set x [lindex $anclist $i]
6816 foreach a $arcout($x) {
6817 if {[info exists growing($a)]} {
6818 if {![info exists growanc($x)] && $dl($x)} {
6824 if {[info exists dl($y)]} {
6828 if {![info exists done($y)]} {
6831 if {[info exists growanc($x)]} {
6835 for {set k 0} {$k < [llength $xl]} {incr k} {
6836 set z [lindex $xl $k]
6837 foreach c $arcout($z) {
6838 if {[info exists arcend($c)]} {
6840 if {[info exists dl($v)] && $dl($v)} {
6842 if {![info exists done($v)]} {
6845 if {[info exists growanc($v)]} {
6855 } elseif {$y eq $anc || !$dl($x)} {
6866 foreach x [array names growanc] {
6875 proc validate_arctags {a} {
6876 global arctags idtags
6880 foreach id $arctags($a) {
6882 if {![info exists idtags($id)]} {
6883 set na [lreplace $na $i $i]
6890 proc validate_archeads {a} {
6891 global archeads idheads
6894 set na $archeads($a)
6895 foreach id $archeads($a) {
6897 if {![info exists idheads($id)]} {
6898 set na [lreplace $na $i $i]
6902 set archeads($a) $na
6905 # Return the list of IDs that have tags that are descendents of id,
6906 # ignoring IDs that are descendents of IDs already reported.
6907 proc desctags {id} {
6908 global arcnos arcstart arcids arctags idtags allparents
6909 global growing cached_dtags
6911 if {![info exists allparents($id)]} {
6914 set t1 [clock clicks -milliseconds]
6916 if {[llength $arcnos($id)] == 1 && [llength $allparents($id)] == 1} {
6917 # part-way along an arc; check that arc first
6918 set a [lindex $arcnos($id) 0]
6919 if {$arctags($a) ne {}} {
6921 set i [lsearch -exact $arcids($a) $id]
6923 foreach t $arctags($a) {
6924 set j [lsearch -exact $arcids($a) $t]
6932 set id $arcstart($a)
6933 if {[info exists idtags($id)]} {
6937 if {[info exists cached_dtags($id)]} {
6938 return $cached_dtags($id)
6945 for {set i 0} {$i < [llength $todo] && $nc > 0} {incr i} {
6946 set id [lindex $todo $i]
6948 set ta [info exists hastaggedancestor($id)]
6952 # ignore tags on starting node
6953 if {!$ta && $i > 0} {
6954 if {[info exists idtags($id)]} {
6957 } elseif {[info exists cached_dtags($id)]} {
6958 set tagloc($id) $cached_dtags($id)
6962 foreach a $arcnos($id) {
6964 if {!$ta && $arctags($a) ne {}} {
6966 if {$arctags($a) ne {}} {
6967 lappend tagloc($id) [lindex $arctags($a) end]
6970 if {$ta || $arctags($a) ne {}} {
6971 set tomark [list $d]
6972 for {set j 0} {$j < [llength $tomark]} {incr j} {
6973 set dd [lindex $tomark $j]
6974 if {![info exists hastaggedancestor($dd)]} {
6975 if {[info exists done($dd)]} {
6976 foreach b $arcnos($dd) {
6977 lappend tomark $arcstart($b)
6979 if {[info exists tagloc($dd)]} {
6982 } elseif {[info exists queued($dd)]} {
6985 set hastaggedancestor($dd) 1
6989 if {![info exists queued($d)]} {
6992 if {![info exists hastaggedancestor($d)]} {
6999 foreach id [array names tagloc] {
7000 if {![info exists hastaggedancestor($id)]} {
7001 foreach t $tagloc($id) {
7002 if {[lsearch -exact $tags $t] < 0} {
7008 set t2 [clock clicks -milliseconds]
7011 # remove tags that are descendents of other tags
7012 for {set i 0} {$i < [llength $tags]} {incr i} {
7013 set a [lindex $tags $i]
7014 for {set j 0} {$j < $i} {incr j} {
7015 set b [lindex $tags $j]
7016 set r [anc_or_desc $a $b]
7018 set tags [lreplace $tags $j $j]
7021 } elseif {$r == -1} {
7022 set tags [lreplace $tags $i $i]
7029 if {[array names growing] ne {}} {
7030 # graph isn't finished, need to check if any tag could get
7031 # eclipsed by another tag coming later. Simply ignore any
7032 # tags that could later get eclipsed.
7035 if {[is_certain $t $origid]} {
7039 if {$tags eq $ctags} {
7040 set cached_dtags($origid) $tags
7045 set cached_dtags($origid) $tags
7047 set t3 [clock clicks -milliseconds]
7048 if {0 && $t3 - $t1 >= 100} {
7049 puts "iterating descendents ($loopix/[llength $todo] nodes) took\
7050 [expr {$t2-$t1}]+[expr {$t3-$t2}]ms, $nc candidates left"
7056 global arcnos arcids arcout arcend arctags idtags allparents
7057 global growing cached_atags
7059 if {![info exists allparents($id)]} {
7062 set t1 [clock clicks -milliseconds]
7064 if {[llength $arcnos($id)] == 1 && [llength $allparents($id)] == 1} {
7065 # part-way along an arc; check that arc first
7066 set a [lindex $arcnos($id) 0]
7067 if {$arctags($a) ne {}} {
7069 set i [lsearch -exact $arcids($a) $id]
7070 foreach t $arctags($a) {
7071 set j [lsearch -exact $arcids($a) $t]
7077 if {![info exists arcend($a)]} {
7081 if {[info exists idtags($id)]} {
7085 if {[info exists cached_atags($id)]} {
7086 return $cached_atags($id)
7094 for {set i 0} {$i < [llength $todo] && $nc > 0} {incr i} {
7095 set id [lindex $todo $i]
7097 set td [info exists hastaggeddescendent($id)]
7101 # ignore tags on starting node
7102 if {!$td && $i > 0} {
7103 if {[info exists idtags($id)]} {
7106 } elseif {[info exists cached_atags($id)]} {
7107 set tagloc($id) $cached_atags($id)
7111 foreach a $arcout($id) {
7112 if {!$td && $arctags($a) ne {}} {
7114 if {$arctags($a) ne {}} {
7115 lappend tagloc($id) [lindex $arctags($a) 0]
7118 if {![info exists arcend($a)]} continue
7120 if {$td || $arctags($a) ne {}} {
7121 set tomark [list $d]
7122 for {set j 0} {$j < [llength $tomark]} {incr j} {
7123 set dd [lindex $tomark $j]
7124 if {![info exists hastaggeddescendent($dd)]} {
7125 if {[info exists done($dd)]} {
7126 foreach b $arcout($dd) {
7127 if {[info exists arcend($b)]} {
7128 lappend tomark $arcend($b)
7131 if {[info exists tagloc($dd)]} {
7134 } elseif {[info exists queued($dd)]} {
7137 set hastaggeddescendent($dd) 1
7141 if {![info exists queued($d)]} {
7144 if {![info exists hastaggeddescendent($d)]} {
7150 set t2 [clock clicks -milliseconds]
7153 foreach id [array names tagloc] {
7154 if {![info exists hastaggeddescendent($id)]} {
7155 foreach t $tagloc($id) {
7156 if {[lsearch -exact $tags $t] < 0} {
7163 # remove tags that are ancestors of other tags
7164 for {set i 0} {$i < [llength $tags]} {incr i} {
7165 set a [lindex $tags $i]
7166 for {set j 0} {$j < $i} {incr j} {
7167 set b [lindex $tags $j]
7168 set r [anc_or_desc $a $b]
7170 set tags [lreplace $tags $j $j]
7173 } elseif {$r == 1} {
7174 set tags [lreplace $tags $i $i]
7181 if {[array names growing] ne {}} {
7182 # graph isn't finished, need to check if any tag could get
7183 # eclipsed by another tag coming later. Simply ignore any
7184 # tags that could later get eclipsed.
7187 if {[is_certain $origid $t]} {
7191 if {$tags eq $ctags} {
7192 set cached_atags($origid) $tags
7197 set cached_atags($origid) $tags
7199 set t3 [clock clicks -milliseconds]
7200 if {0 && $t3 - $t1 >= 100} {
7201 puts "iterating ancestors ($loopix/[llength $todo] nodes) took\
7202 [expr {$t2-$t1}]+[expr {$t3-$t2}]ms, $nc candidates left"
7207 # Return the list of IDs that have heads that are descendents of id,
7208 # including id itself if it has a head.
7209 proc descheads {id} {
7210 global arcnos arcstart arcids archeads idheads cached_dheads
7213 if {![info exists allparents($id)]} {
7217 if {[llength $arcnos($id)] == 1 && [llength $allparents($id)] == 1} {
7218 # part-way along an arc; check it first
7219 set a [lindex $arcnos($id) 0]
7220 if {$archeads($a) ne {}} {
7221 validate_archeads $a
7222 set i [lsearch -exact $arcids($a) $id]
7223 foreach t $archeads($a) {
7224 set j [lsearch -exact $arcids($a) $t]
7229 set id $arcstart($a)
7235 for {set i 0} {$i < [llength $todo]} {incr i} {
7236 set id [lindex $todo $i]
7237 if {[info exists cached_dheads($id)]} {
7238 set ret [concat $ret $cached_dheads($id)]
7240 if {[info exists idheads($id)]} {
7243 foreach a $arcnos($id) {
7244 if {$archeads($a) ne {}} {
7245 validate_archeads $a
7246 if {$archeads($a) ne {}} {
7247 set ret [concat $ret $archeads($a)]
7251 if {![info exists seen($d)]} {
7258 set ret [lsort -unique $ret]
7259 set cached_dheads($origid) $ret
7260 return [concat $ret $aret]
7263 proc addedtag {id} {
7264 global arcnos arcout cached_dtags cached_atags
7266 if {![info exists arcnos($id)]} return
7267 if {![info exists arcout($id)]} {
7268 recalcarc [lindex $arcnos($id) 0]
7270 catch {unset cached_dtags}
7271 catch {unset cached_atags}
7274 proc addedhead {hid head} {
7275 global arcnos arcout cached_dheads
7277 if {![info exists arcnos($hid)]} return
7278 if {![info exists arcout($hid)]} {
7279 recalcarc [lindex $arcnos($hid) 0]
7281 catch {unset cached_dheads}
7284 proc removedhead {hid head} {
7285 global cached_dheads
7287 catch {unset cached_dheads}
7290 proc movedhead {hid head} {
7291 global arcnos arcout cached_dheads
7293 if {![info exists arcnos($hid)]} return
7294 if {![info exists arcout($hid)]} {
7295 recalcarc [lindex $arcnos($hid) 0]
7297 catch {unset cached_dheads}
7300 proc changedrefs {} {
7301 global cached_dheads cached_dtags cached_atags
7302 global arctags archeads arcnos arcout idheads idtags
7304 foreach id [concat [array names idheads] [array names idtags]] {
7305 if {[info exists arcnos($id)] && ![info exists arcout($id)]} {
7306 set a [lindex $arcnos($id) 0]
7307 if {![info exists donearc($a)]} {
7313 catch {unset cached_dtags}
7314 catch {unset cached_atags}
7315 catch {unset cached_dheads}
7318 proc rereadrefs {} {
7319 global idtags idheads idotherrefs mainhead
7321 set refids [concat [array names idtags] \
7322 [array names idheads] [array names idotherrefs]]
7323 foreach id $refids {
7324 if {![info exists ref($id)]} {
7325 set ref($id) [listrefs $id]
7328 set oldmainhead $mainhead
7331 set refids [lsort -unique [concat $refids [array names idtags] \
7332 [array names idheads] [array names idotherrefs]]]
7333 foreach id $refids {
7334 set v [listrefs $id]
7335 if {![info exists ref($id)] || $ref($id) != $v ||
7336 ($id eq $oldmainhead && $id ne $mainhead) ||
7337 ($id eq $mainhead && $id ne $oldmainhead)} {
7344 proc listrefs {id} {
7345 global idtags idheads idotherrefs
7348 if {[info exists idtags($id)]} {
7352 if {[info exists idheads($id)]} {
7356 if {[info exists idotherrefs($id)]} {
7357 set z $idotherrefs($id)
7359 return [list $x $y $z]
7362 proc showtag {tag isnew} {
7363 global ctext tagcontents tagids linknum tagobjid
7366 addtohistory [list showtag $tag 0]
7368 $ctext conf -state normal
7371 if {![info exists tagcontents($tag)]} {
7373 set tagcontents($tag) [exec git cat-file tag $tagobjid($tag)]
7376 if {[info exists tagcontents($tag)]} {
7377 set text $tagcontents($tag)
7379 set text "Tag: $tag\nId: $tagids($tag)"
7381 appendwithlinks $text {}
7382 $ctext conf -state disabled
7394 global maxwidth maxgraphpct diffopts
7395 global oldprefs prefstop showneartags showlocalchanges
7396 global bgcolor fgcolor ctext diffcolors selectbgcolor
7397 global uifont tabstop
7401 if {[winfo exists $top]} {
7405 foreach v {maxwidth maxgraphpct diffopts showneartags showlocalchanges} {
7406 set oldprefs($v) [set $v]
7409 wm title $top "Gitk preferences"
7410 label $top.ldisp -text "Commit list display options"
7411 $top.ldisp configure -font $uifont
7412 grid $top.ldisp - -sticky w -pady 10
7413 label $top.spacer -text " "
7414 label $top.maxwidthl -text "Maximum graph width (lines)" \
7416 spinbox $top.maxwidth -from 0 -to 100 -width 4 -textvariable maxwidth
7417 grid $top.spacer $top.maxwidthl $top.maxwidth -sticky w
7418 label $top.maxpctl -text "Maximum graph width (% of pane)" \
7420 spinbox $top.maxpct -from 1 -to 100 -width 4 -textvariable maxgraphpct
7421 grid x $top.maxpctl $top.maxpct -sticky w
7422 frame $top.showlocal
7423 label $top.showlocal.l -text "Show local changes" -font optionfont
7424 checkbutton $top.showlocal.b -variable showlocalchanges
7425 pack $top.showlocal.b $top.showlocal.l -side left
7426 grid x $top.showlocal -sticky w
7428 label $top.ddisp -text "Diff display options"
7429 $top.ddisp configure -font $uifont
7430 grid $top.ddisp - -sticky w -pady 10
7431 label $top.diffoptl -text "Options for diff program" \
7433 entry $top.diffopt -width 20 -textvariable diffopts
7434 grid x $top.diffoptl $top.diffopt -sticky w
7436 label $top.ntag.l -text "Display nearby tags" -font optionfont
7437 checkbutton $top.ntag.b -variable showneartags
7438 pack $top.ntag.b $top.ntag.l -side left
7439 grid x $top.ntag -sticky w
7440 label $top.tabstopl -text "tabstop" -font optionfont
7441 spinbox $top.tabstop -from 1 -to 20 -width 4 -textvariable tabstop
7442 grid x $top.tabstopl $top.tabstop -sticky w
7444 label $top.cdisp -text "Colors: press to choose"
7445 $top.cdisp configure -font $uifont
7446 grid $top.cdisp - -sticky w -pady 10
7447 label $top.bg -padx 40 -relief sunk -background $bgcolor
7448 button $top.bgbut -text "Background" -font optionfont \
7449 -command [list choosecolor bgcolor 0 $top.bg background setbg]
7450 grid x $top.bgbut $top.bg -sticky w
7451 label $top.fg -padx 40 -relief sunk -background $fgcolor
7452 button $top.fgbut -text "Foreground" -font optionfont \
7453 -command [list choosecolor fgcolor 0 $top.fg foreground setfg]
7454 grid x $top.fgbut $top.fg -sticky w
7455 label $top.diffold -padx 40 -relief sunk -background [lindex $diffcolors 0]
7456 button $top.diffoldbut -text "Diff: old lines" -font optionfont \
7457 -command [list choosecolor diffcolors 0 $top.diffold "diff old lines" \
7458 [list $ctext tag conf d0 -foreground]]
7459 grid x $top.diffoldbut $top.diffold -sticky w
7460 label $top.diffnew -padx 40 -relief sunk -background [lindex $diffcolors 1]
7461 button $top.diffnewbut -text "Diff: new lines" -font optionfont \
7462 -command [list choosecolor diffcolors 1 $top.diffnew "diff new lines" \
7463 [list $ctext tag conf d1 -foreground]]
7464 grid x $top.diffnewbut $top.diffnew -sticky w
7465 label $top.hunksep -padx 40 -relief sunk -background [lindex $diffcolors 2]
7466 button $top.hunksepbut -text "Diff: hunk header" -font optionfont \
7467 -command [list choosecolor diffcolors 2 $top.hunksep \
7468 "diff hunk header" \
7469 [list $ctext tag conf hunksep -foreground]]
7470 grid x $top.hunksepbut $top.hunksep -sticky w
7471 label $top.selbgsep -padx 40 -relief sunk -background $selectbgcolor
7472 button $top.selbgbut -text "Select bg" -font optionfont \
7473 -command [list choosecolor selectbgcolor 0 $top.selbgsep background setselbg]
7474 grid x $top.selbgbut $top.selbgsep -sticky w
7477 button $top.buts.ok -text "OK" -command prefsok -default active
7478 $top.buts.ok configure -font $uifont
7479 button $top.buts.can -text "Cancel" -command prefscan -default normal
7480 $top.buts.can configure -font $uifont
7481 grid $top.buts.ok $top.buts.can
7482 grid columnconfigure $top.buts 0 -weight 1 -uniform a
7483 grid columnconfigure $top.buts 1 -weight 1 -uniform a
7484 grid $top.buts - - -pady 10 -sticky ew
7485 bind $top <Visibility> "focus $top.buts.ok"
7488 proc choosecolor {v vi w x cmd} {
7491 set c [tk_chooseColor -initialcolor [lindex [set $v] $vi] \
7492 -title "Gitk: choose color for $x"]
7493 if {$c eq {}} return
7494 $w conf -background $c
7500 global bglist cflist
7502 $w configure -selectbackground $c
7504 $cflist tag configure highlight \
7505 -background [$cflist cget -selectbackground]
7506 allcanvs itemconf secsel -fill $c
7513 $w conf -background $c
7521 $w conf -foreground $c
7523 allcanvs itemconf text -fill $c
7524 $canv itemconf circle -outline $c
7528 global maxwidth maxgraphpct diffopts
7529 global oldprefs prefstop showneartags showlocalchanges
7531 foreach v {maxwidth maxgraphpct diffopts showneartags showlocalchanges} {
7532 set $v $oldprefs($v)
7534 catch {destroy $prefstop}
7539 global maxwidth maxgraphpct
7540 global oldprefs prefstop showneartags showlocalchanges
7541 global charspc ctext tabstop
7543 catch {destroy $prefstop}
7545 $ctext configure -tabs "[expr {$tabstop * $charspc}]"
7546 if {$showlocalchanges != $oldprefs(showlocalchanges)} {
7547 if {$showlocalchanges} {
7553 if {$maxwidth != $oldprefs(maxwidth)
7554 || $maxgraphpct != $oldprefs(maxgraphpct)} {
7556 } elseif {$showneartags != $oldprefs(showneartags)} {
7561 proc formatdate {d} {
7562 global datetimeformat
7564 set d [clock format $d -format $datetimeformat]
7569 # This list of encoding names and aliases is distilled from
7570 # http://www.iana.org/assignments/character-sets.
7571 # Not all of them are supported by Tcl.
7572 set encoding_aliases {
7573 { ANSI_X3.4-1968 iso-ir-6 ANSI_X3.4-1986 ISO_646.irv:1991 ASCII
7574 ISO646-US US-ASCII us IBM367 cp367 csASCII }
7575 { ISO-10646-UTF-1 csISO10646UTF1 }
7576 { ISO_646.basic:1983 ref csISO646basic1983 }
7577 { INVARIANT csINVARIANT }
7578 { ISO_646.irv:1983 iso-ir-2 irv csISO2IntlRefVersion }
7579 { BS_4730 iso-ir-4 ISO646-GB gb uk csISO4UnitedKingdom }
7580 { NATS-SEFI iso-ir-8-1 csNATSSEFI }
7581 { NATS-SEFI-ADD iso-ir-8-2 csNATSSEFIADD }
7582 { NATS-DANO iso-ir-9-1 csNATSDANO }
7583 { NATS-DANO-ADD iso-ir-9-2 csNATSDANOADD }
7584 { SEN_850200_B iso-ir-10 FI ISO646-FI ISO646-SE se csISO10Swedish }
7585 { SEN_850200_C iso-ir-11 ISO646-SE2 se2 csISO11SwedishForNames }
7586 { KS_C_5601-1987 iso-ir-149 KS_C_5601-1989 KSC_5601 korean csKSC56011987 }
7587 { ISO-2022-KR csISO2022KR }
7589 { ISO-2022-JP csISO2022JP }
7590 { ISO-2022-JP-2 csISO2022JP2 }
7591 { JIS_C6220-1969-jp JIS_C6220-1969 iso-ir-13 katakana x0201-7
7593 { JIS_C6220-1969-ro iso-ir-14 jp ISO646-JP csISO14JISC6220ro }
7594 { IT iso-ir-15 ISO646-IT csISO15Italian }
7595 { PT iso-ir-16 ISO646-PT csISO16Portuguese }
7596 { ES iso-ir-17 ISO646-ES csISO17Spanish }
7597 { greek7-old iso-ir-18 csISO18Greek7Old }
7598 { latin-greek iso-ir-19 csISO19LatinGreek }
7599 { DIN_66003 iso-ir-21 de ISO646-DE csISO21German }
7600 { NF_Z_62-010_(1973) iso-ir-25 ISO646-FR1 csISO25French }
7601 { Latin-greek-1 iso-ir-27 csISO27LatinGreek1 }
7602 { ISO_5427 iso-ir-37 csISO5427Cyrillic }
7603 { JIS_C6226-1978 iso-ir-42 csISO42JISC62261978 }
7604 { BS_viewdata iso-ir-47 csISO47BSViewdata }
7605 { INIS iso-ir-49 csISO49INIS }
7606 { INIS-8 iso-ir-50 csISO50INIS8 }
7607 { INIS-cyrillic iso-ir-51 csISO51INISCyrillic }
7608 { ISO_5427:1981 iso-ir-54 ISO5427Cyrillic1981 }
7609 { ISO_5428:1980 iso-ir-55 csISO5428Greek }
7610 { GB_1988-80 iso-ir-57 cn ISO646-CN csISO57GB1988 }
7611 { GB_2312-80 iso-ir-58 chinese csISO58GB231280 }
7612 { NS_4551-1 iso-ir-60 ISO646-NO no csISO60DanishNorwegian
7614 { NS_4551-2 ISO646-NO2 iso-ir-61 no2 csISO61Norwegian2 }
7615 { NF_Z_62-010 iso-ir-69 ISO646-FR fr csISO69French }
7616 { videotex-suppl iso-ir-70 csISO70VideotexSupp1 }
7617 { PT2 iso-ir-84 ISO646-PT2 csISO84Portuguese2 }
7618 { ES2 iso-ir-85 ISO646-ES2 csISO85Spanish2 }
7619 { MSZ_7795.3 iso-ir-86 ISO646-HU hu csISO86Hungarian }
7620 { JIS_C6226-1983 iso-ir-87 x0208 JIS_X0208-1983 csISO87JISX0208 }
7621 { greek7 iso-ir-88 csISO88Greek7 }
7622 { ASMO_449 ISO_9036 arabic7 iso-ir-89 csISO89ASMO449 }
7623 { iso-ir-90 csISO90 }
7624 { JIS_C6229-1984-a iso-ir-91 jp-ocr-a csISO91JISC62291984a }
7625 { JIS_C6229-1984-b iso-ir-92 ISO646-JP-OCR-B jp-ocr-b
7626 csISO92JISC62991984b }
7627 { JIS_C6229-1984-b-add iso-ir-93 jp-ocr-b-add csISO93JIS62291984badd }
7628 { JIS_C6229-1984-hand iso-ir-94 jp-ocr-hand csISO94JIS62291984hand }
7629 { JIS_C6229-1984-hand-add iso-ir-95 jp-ocr-hand-add
7630 csISO95JIS62291984handadd }
7631 { JIS_C6229-1984-kana iso-ir-96 csISO96JISC62291984kana }
7632 { ISO_2033-1983 iso-ir-98 e13b csISO2033 }
7633 { ANSI_X3.110-1983 iso-ir-99 CSA_T500-1983 NAPLPS csISO99NAPLPS }
7634 { ISO_8859-1:1987 iso-ir-100 ISO_8859-1 ISO-8859-1 latin1 l1 IBM819
7636 { ISO_8859-2:1987 iso-ir-101 ISO_8859-2 ISO-8859-2 latin2 l2 csISOLatin2 }
7637 { T.61-7bit iso-ir-102 csISO102T617bit }
7638 { T.61-8bit T.61 iso-ir-103 csISO103T618bit }
7639 { ISO_8859-3:1988 iso-ir-109 ISO_8859-3 ISO-8859-3 latin3 l3 csISOLatin3 }
7640 { ISO_8859-4:1988 iso-ir-110 ISO_8859-4 ISO-8859-4 latin4 l4 csISOLatin4 }
7641 { ECMA-cyrillic iso-ir-111 KOI8-E csISO111ECMACyrillic }
7642 { CSA_Z243.4-1985-1 iso-ir-121 ISO646-CA csa7-1 ca csISO121Canadian1 }
7643 { CSA_Z243.4-1985-2 iso-ir-122 ISO646-CA2 csa7-2 csISO122Canadian2 }
7644 { CSA_Z243.4-1985-gr iso-ir-123 csISO123CSAZ24341985gr }
7645 { ISO_8859-6:1987 iso-ir-127 ISO_8859-6 ISO-8859-6 ECMA-114 ASMO-708
7646 arabic csISOLatinArabic }
7647 { ISO_8859-6-E csISO88596E ISO-8859-6-E }
7648 { ISO_8859-6-I csISO88596I ISO-8859-6-I }
7649 { ISO_8859-7:1987 iso-ir-126 ISO_8859-7 ISO-8859-7 ELOT_928 ECMA-118
7650 greek greek8 csISOLatinGreek }
7651 { T.101-G2 iso-ir-128 csISO128T101G2 }
7652 { ISO_8859-8:1988 iso-ir-138 ISO_8859-8 ISO-8859-8 hebrew
7654 { ISO_8859-8-E csISO88598E ISO-8859-8-E }
7655 { ISO_8859-8-I csISO88598I ISO-8859-8-I }
7656 { CSN_369103 iso-ir-139 csISO139CSN369103 }
7657 { JUS_I.B1.002 iso-ir-141 ISO646-YU js yu csISO141JUSIB1002 }
7658 { ISO_6937-2-add iso-ir-142 csISOTextComm }
7659 { IEC_P27-1 iso-ir-143 csISO143IECP271 }
7660 { ISO_8859-5:1988 iso-ir-144 ISO_8859-5 ISO-8859-5 cyrillic
7661 csISOLatinCyrillic }
7662 { JUS_I.B1.003-serb iso-ir-146 serbian csISO146Serbian }
7663 { JUS_I.B1.003-mac macedonian iso-ir-147 csISO147Macedonian }
7664 { ISO_8859-9:1989 iso-ir-148 ISO_8859-9 ISO-8859-9 latin5 l5 csISOLatin5 }
7665 { greek-ccitt iso-ir-150 csISO150 csISO150GreekCCITT }
7666 { NC_NC00-10:81 cuba iso-ir-151 ISO646-CU csISO151Cuba }
7667 { ISO_6937-2-25 iso-ir-152 csISO6937Add }
7668 { GOST_19768-74 ST_SEV_358-88 iso-ir-153 csISO153GOST1976874 }
7669 { ISO_8859-supp iso-ir-154 latin1-2-5 csISO8859Supp }
7670 { ISO_10367-box iso-ir-155 csISO10367Box }
7671 { ISO-8859-10 iso-ir-157 l6 ISO_8859-10:1992 csISOLatin6 latin6 }
7672 { latin-lap lap iso-ir-158 csISO158Lap }
7673 { JIS_X0212-1990 x0212 iso-ir-159 csISO159JISX02121990 }
7674 { DS_2089 DS2089 ISO646-DK dk csISO646Danish }
7677 { JIS_X0201 X0201 csHalfWidthKatakana }
7678 { KSC5636 ISO646-KR csKSC5636 }
7679 { ISO-10646-UCS-2 csUnicode }
7680 { ISO-10646-UCS-4 csUCS4 }
7681 { DEC-MCS dec csDECMCS }
7682 { hp-roman8 roman8 r8 csHPRoman8 }
7683 { macintosh mac csMacintosh }
7684 { IBM037 cp037 ebcdic-cp-us ebcdic-cp-ca ebcdic-cp-wt ebcdic-cp-nl
7686 { IBM038 EBCDIC-INT cp038 csIBM038 }
7687 { IBM273 CP273 csIBM273 }
7688 { IBM274 EBCDIC-BE CP274 csIBM274 }
7689 { IBM275 EBCDIC-BR cp275 csIBM275 }
7690 { IBM277 EBCDIC-CP-DK EBCDIC-CP-NO csIBM277 }
7691 { IBM278 CP278 ebcdic-cp-fi ebcdic-cp-se csIBM278 }
7692 { IBM280 CP280 ebcdic-cp-it csIBM280 }
7693 { IBM281 EBCDIC-JP-E cp281 csIBM281 }
7694 { IBM284 CP284 ebcdic-cp-es csIBM284 }
7695 { IBM285 CP285 ebcdic-cp-gb csIBM285 }
7696 { IBM290 cp290 EBCDIC-JP-kana csIBM290 }
7697 { IBM297 cp297 ebcdic-cp-fr csIBM297 }
7698 { IBM420 cp420 ebcdic-cp-ar1 csIBM420 }
7699 { IBM423 cp423 ebcdic-cp-gr csIBM423 }
7700 { IBM424 cp424 ebcdic-cp-he csIBM424 }
7701 { IBM437 cp437 437 csPC8CodePage437 }
7702 { IBM500 CP500 ebcdic-cp-be ebcdic-cp-ch csIBM500 }
7703 { IBM775 cp775 csPC775Baltic }
7704 { IBM850 cp850 850 csPC850Multilingual }
7705 { IBM851 cp851 851 csIBM851 }
7706 { IBM852 cp852 852 csPCp852 }
7707 { IBM855 cp855 855 csIBM855 }
7708 { IBM857 cp857 857 csIBM857 }
7709 { IBM860 cp860 860 csIBM860 }
7710 { IBM861 cp861 861 cp-is csIBM861 }
7711 { IBM862 cp862 862 csPC862LatinHebrew }
7712 { IBM863 cp863 863 csIBM863 }
7713 { IBM864 cp864 csIBM864 }
7714 { IBM865 cp865 865 csIBM865 }
7715 { IBM866 cp866 866 csIBM866 }
7716 { IBM868 CP868 cp-ar csIBM868 }
7717 { IBM869 cp869 869 cp-gr csIBM869 }
7718 { IBM870 CP870 ebcdic-cp-roece ebcdic-cp-yu csIBM870 }
7719 { IBM871 CP871 ebcdic-cp-is csIBM871 }
7720 { IBM880 cp880 EBCDIC-Cyrillic csIBM880 }
7721 { IBM891 cp891 csIBM891 }
7722 { IBM903 cp903 csIBM903 }
7723 { IBM904 cp904 904 csIBBM904 }
7724 { IBM905 CP905 ebcdic-cp-tr csIBM905 }
7725 { IBM918 CP918 ebcdic-cp-ar2 csIBM918 }
7726 { IBM1026 CP1026 csIBM1026 }
7727 { EBCDIC-AT-DE csIBMEBCDICATDE }
7728 { EBCDIC-AT-DE-A csEBCDICATDEA }
7729 { EBCDIC-CA-FR csEBCDICCAFR }
7730 { EBCDIC-DK-NO csEBCDICDKNO }
7731 { EBCDIC-DK-NO-A csEBCDICDKNOA }
7732 { EBCDIC-FI-SE csEBCDICFISE }
7733 { EBCDIC-FI-SE-A csEBCDICFISEA }
7734 { EBCDIC-FR csEBCDICFR }
7735 { EBCDIC-IT csEBCDICIT }
7736 { EBCDIC-PT csEBCDICPT }
7737 { EBCDIC-ES csEBCDICES }
7738 { EBCDIC-ES-A csEBCDICESA }
7739 { EBCDIC-ES-S csEBCDICESS }
7740 { EBCDIC-UK csEBCDICUK }
7741 { EBCDIC-US csEBCDICUS }
7742 { UNKNOWN-8BIT csUnknown8BiT }
7743 { MNEMONIC csMnemonic }
7748 { IBM00858 CCSID00858 CP00858 PC-Multilingual-850+euro }
7749 { IBM00924 CCSID00924 CP00924 ebcdic-Latin9--euro }
7750 { IBM01140 CCSID01140 CP01140 ebcdic-us-37+euro }
7751 { IBM01141 CCSID01141 CP01141 ebcdic-de-273+euro }
7752 { IBM01142 CCSID01142 CP01142 ebcdic-dk-277+euro ebcdic-no-277+euro }
7753 { IBM01143 CCSID01143 CP01143 ebcdic-fi-278+euro ebcdic-se-278+euro }
7754 { IBM01144 CCSID01144 CP01144 ebcdic-it-280+euro }
7755 { IBM01145 CCSID01145 CP01145 ebcdic-es-284+euro }
7756 { IBM01146 CCSID01146 CP01146 ebcdic-gb-285+euro }
7757 { IBM01147 CCSID01147 CP01147 ebcdic-fr-297+euro }
7758 { IBM01148 CCSID01148 CP01148 ebcdic-international-500+euro }
7759 { IBM01149 CCSID01149 CP01149 ebcdic-is-871+euro }
7760 { IBM1047 IBM-1047 }
7761 { PTCP154 csPTCP154 PT154 CP154 Cyrillic-Asian }
7762 { Amiga-1251 Ami1251 Amiga1251 Ami-1251 }
7763 { UNICODE-1-1 csUnicode11 }
7766 { UNICODE-1-1-UTF-7 csUnicode11UTF7 }
7767 { ISO-8859-14 iso-ir-199 ISO_8859-14:1998 ISO_8859-14 latin8 iso-celtic
7769 { ISO-8859-15 ISO_8859-15 Latin-9 }
7770 { ISO-8859-16 iso-ir-226 ISO_8859-16:2001 ISO_8859-16 latin10 l10 }
7771 { GBK CP936 MS936 windows-936 }
7772 { JIS_Encoding csJISEncoding }
7773 { Shift_JIS MS_Kanji csShiftJIS }
7774 { Extended_UNIX_Code_Packed_Format_for_Japanese csEUCPkdFmtJapanese
7776 { Extended_UNIX_Code_Fixed_Width_for_Japanese csEUCFixWidJapanese }
7777 { ISO-10646-UCS-Basic csUnicodeASCII }
7778 { ISO-10646-Unicode-Latin1 csUnicodeLatin1 ISO-10646 }
7779 { ISO-Unicode-IBM-1261 csUnicodeIBM1261 }
7780 { ISO-Unicode-IBM-1268 csUnicodeIBM1268 }
7781 { ISO-Unicode-IBM-1276 csUnicodeIBM1276 }
7782 { ISO-Unicode-IBM-1264 csUnicodeIBM1264 }
7783 { ISO-Unicode-IBM-1265 csUnicodeIBM1265 }
7784 { ISO-8859-1-Windows-3.0-Latin-1 csWindows30Latin1 }
7785 { ISO-8859-1-Windows-3.1-Latin-1 csWindows31Latin1 }
7786 { ISO-8859-2-Windows-Latin-2 csWindows31Latin2 }
7787 { ISO-8859-9-Windows-Latin-5 csWindows31Latin5 }
7788 { Adobe-Standard-Encoding csAdobeStandardEncoding }
7789 { Ventura-US csVenturaUS }
7790 { Ventura-International csVenturaInternational }
7791 { PC8-Danish-Norwegian csPC8DanishNorwegian }
7792 { PC8-Turkish csPC8Turkish }
7793 { IBM-Symbols csIBMSymbols }
7794 { IBM-Thai csIBMThai }
7795 { HP-Legal csHPLegal }
7796 { HP-Pi-font csHPPiFont }
7797 { HP-Math8 csHPMath8 }
7798 { Adobe-Symbol-Encoding csHPPSMath }
7799 { HP-DeskTop csHPDesktop }
7800 { Ventura-Math csVenturaMath }
7801 { Microsoft-Publishing csMicrosoftPublishing }
7802 { Windows-31J csWindows31J }
7807 proc tcl_encoding {enc} {
7808 global encoding_aliases
7809 set names [encoding names]
7810 set lcnames [string tolower $names]
7811 set enc [string tolower $enc]
7812 set i [lsearch -exact $lcnames $enc]
7814 # look for "isonnn" instead of "iso-nnn" or "iso_nnn"
7815 if {[regsub {^iso[-_]} $enc iso encx]} {
7816 set i [lsearch -exact $lcnames $encx]
7820 foreach l $encoding_aliases {
7821 set ll [string tolower $l]
7822 if {[lsearch -exact $ll $enc] < 0} continue
7823 # look through the aliases for one that tcl knows about
7825 set i [lsearch -exact $lcnames $e]
7827 if {[regsub {^iso[-_]} $e iso ex]} {
7828 set i [lsearch -exact $lcnames $ex]
7837 return [lindex $names $i]
7844 set diffopts "-U 5 -p"
7845 set wrcomcmd "git diff-tree --stdin -p --pretty"
7849 set gitencoding [exec git config --get i18n.commitencoding]
7851 if {$gitencoding == ""} {
7852 set gitencoding "utf-8"
7854 set tclencoding [tcl_encoding $gitencoding]
7855 if {$tclencoding == {}} {
7856 puts stderr "Warning: encoding $gitencoding is not supported by Tcl/Tk"
7859 set mainfont {Helvetica 9}
7860 set textfont {Courier 9}
7861 set uifont {Helvetica 9 bold}
7863 set findmergefiles 0
7871 set cmitmode "patch"
7872 set wrapcomment "none"
7876 set showlocalchanges 1
7877 set datetimeformat "%Y-%m-%d %H:%M:%S"
7879 set colors {green red blue magenta darkgrey brown orange}
7882 set diffcolors {red "#00a000" blue}
7884 set selectbgcolor gray85
7886 catch {source ~/.gitk}
7888 font create optionfont -family sans-serif -size -12
7890 # check that we can find a .git directory somewhere...
7891 if {[catch {set gitdir [gitdir]}]} {
7892 show_error {} . "Cannot find a git repository here."
7895 if {![file isdirectory $gitdir]} {
7896 show_error {} . "Cannot find the git directory \"$gitdir\"."
7901 set cmdline_files {}
7906 "-d" { set datemode 1 }
7908 set cmdline_files [lrange $argv [expr {$i + 1}] end]
7912 lappend revtreeargs $arg
7918 if {$i >= [llength $argv] && $revtreeargs ne {}} {
7919 # no -- on command line, but some arguments (other than -d)
7921 set f [eval exec git rev-parse --no-revs --no-flags $revtreeargs]
7922 set cmdline_files [split $f "\n"]
7923 set n [llength $cmdline_files]
7924 set revtreeargs [lrange $revtreeargs 0 end-$n]
7925 # Unfortunately git rev-parse doesn't produce an error when
7926 # something is both a revision and a filename. To be consistent
7927 # with git log and git rev-list, check revtreeargs for filenames.
7928 foreach arg $revtreeargs {
7929 if {[file exists $arg]} {
7930 show_error {} . "Ambiguous argument '$arg': both revision\
7936 # unfortunately we get both stdout and stderr in $err,
7937 # so look for "fatal:".
7938 set i [string first "fatal:" $err]
7940 set err [string range $err [expr {$i + 6}] end]
7942 show_error {} . "Bad arguments to gitk:\n$err"
7947 set nullid "0000000000000000000000000000000000000000"
7948 set nullid2 "0000000000000000000000000000000000000001"
7956 set highlight_paths {}
7957 set searchdirn -forwards
7961 set markingmatches 0
7968 set selectedhlview None
7977 set lookingforhead 0
7983 # wait for the window to become visible
7985 wm title . "[file tail $argv0]: [file tail [pwd]]"
7988 if {$cmdline_files ne {} || $revtreeargs ne {}} {
7989 # create a view for the files/dirs specified on the command line
7993 set viewname(1) "Command line"
7994 set viewfiles(1) $cmdline_files
7995 set viewargs(1) $revtreeargs
7998 .bar.view entryconf Edit* -state normal
7999 .bar.view entryconf Delete* -state normal
8002 if {[info exists permviews]} {
8003 foreach v $permviews {
8006 set viewname($n) [lindex $v 0]
8007 set viewfiles($n) [lindex $v 1]
8008 set viewargs($n) [lindex $v 2]