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 args $viewargs($view)
91 if {$viewfiles($view) ne {}} {
92 set args [concat $args "--" $viewfiles($view)]
94 set order "--topo-order"
96 set order "--date-order"
99 set fd [open [concat | git rev-list --header $order \
100 --parents --boundary --default HEAD $args] r]
102 puts stderr "Error executing git rev-list: $err"
105 set commfd($view) $fd
106 set leftover($view) {}
107 set lookingforhead $showlocalchanges
108 fconfigure $fd -blocking 0 -translation lf
109 if {$tclencoding != {}} {
110 fconfigure $fd -encoding $tclencoding
112 filerun $fd [list getcommitlines $fd $view]
116 proc stop_rev_list {} {
117 global commfd curview
119 if {![info exists commfd($curview)]} return
120 set fd $commfd($curview)
126 unset commfd($curview)
130 global phase canv mainfont curview
134 start_rev_list $curview
135 show_status "Reading commits..."
138 proc getcommitlines {fd view} {
140 global leftover commfd
141 global displayorder commitidx commitrow commitdata
142 global parentlist childlist children curview hlview
143 global vparentlist vchildlist vdisporder vcmitlisted
145 set stuff [read $fd 500000]
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]
198 set ids [string range $cmit 0 [expr {$j - 1}]]
199 if {[string range $ids 0 0] == "-"} {
201 set ids [string range $ids 1 end]
205 if {[string length $id] != 40} {
213 if {[string length $shortcmit] > 80} {
214 set shortcmit "[string range $shortcmit 0 80]..."
216 error_popup "Can't parse git rev-list output: {$shortcmit}"
219 set id [lindex $ids 0]
221 set olds [lrange $ids 1 end]
224 if {$i == 0 || [lsearch -exact $olds $p] >= $i} {
225 lappend children($view,$p) $id
232 if {![info exists children($view,$id)]} {
233 set children($view,$id) {}
235 set commitdata($id) [string range $cmit [expr {$j + 1}] end]
236 set commitrow($view,$id) $commitidx($view)
237 incr commitidx($view)
238 if {$view == $curview} {
239 lappend parentlist $olds
240 lappend childlist $children($view,$id)
241 lappend displayorder $id
242 lappend commitlisted $listed
244 lappend vparentlist($view) $olds
245 lappend vchildlist($view) $children($view,$id)
246 lappend vdisporder($view) $id
247 lappend vcmitlisted($view) $listed
252 run chewcommits $view
257 proc chewcommits {view} {
258 global curview hlview commfd
259 global selectedline pending_select
262 if {$view == $curview} {
263 set allread [expr {![info exists commfd($view)]}]
264 set tlimit [expr {[clock clicks -milliseconds] + 50}]
265 set more [layoutmore $tlimit $allread]
266 if {$allread && !$more} {
267 global displayorder nullid commitidx phase
268 global numcommits startmsecs
270 if {[info exists pending_select]} {
271 set row [expr {[lindex $displayorder 0] eq $nullid}]
274 if {$commitidx($curview) > 0} {
275 #set ms [expr {[clock clicks -milliseconds] - $startmsecs}]
276 #puts "overall $ms ms for $numcommits commits"
278 show_status "No commits selected"
284 if {[info exists hlview] && $view == $hlview} {
290 proc readcommit {id} {
291 if {[catch {set contents [exec git cat-file commit $id]}]} return
292 parsecommit $id $contents 0
295 proc updatecommits {} {
296 global viewdata curview phase displayorder
297 global children commitrow selectedline thickerline
304 foreach id $displayorder {
305 catch {unset children($n,$id)}
306 catch {unset commitrow($n,$id)}
309 catch {unset selectedline}
310 catch {unset thickerline}
311 catch {unset viewdata($n)}
318 proc parsecommit {id contents listed} {
319 global commitinfo cdate
328 set hdrend [string first "\n\n" $contents]
330 # should never happen...
331 set hdrend [string length $contents]
333 set header [string range $contents 0 [expr {$hdrend - 1}]]
334 set comment [string range $contents [expr {$hdrend + 2}] end]
335 foreach line [split $header "\n"] {
336 set tag [lindex $line 0]
337 if {$tag == "author"} {
338 set audate [lindex $line end-1]
339 set auname [lrange $line 1 end-2]
340 } elseif {$tag == "committer"} {
341 set comdate [lindex $line end-1]
342 set comname [lrange $line 1 end-2]
346 # take the first non-blank line of the comment as the headline
347 set headline [string trimleft $comment]
348 set i [string first "\n" $headline]
350 set headline [string range $headline 0 $i]
352 set headline [string trimright $headline]
353 set i [string first "\r" $headline]
355 set headline [string trimright [string range $headline 0 $i]]
358 # git rev-list indents the comment by 4 spaces;
359 # if we got this via git cat-file, add the indentation
361 foreach line [split $comment "\n"] {
362 append newcomment " "
363 append newcomment $line
364 append newcomment "\n"
366 set comment $newcomment
368 if {$comdate != {}} {
369 set cdate($id) $comdate
371 set commitinfo($id) [list $headline $auname $audate \
372 $comname $comdate $comment]
375 proc getcommit {id} {
376 global commitdata commitinfo
378 if {[info exists commitdata($id)]} {
379 parsecommit $id $commitdata($id) 1
382 if {![info exists commitinfo($id)]} {
383 set commitinfo($id) {"No commit information available"}
390 global tagids idtags headids idheads tagcontents
391 global otherrefids idotherrefs mainhead mainheadid
393 foreach v {tagids idtags headids idheads otherrefids idotherrefs} {
396 set refd [open [list | git show-ref] r]
397 while {0 <= [set n [gets $refd line]]} {
398 if {![regexp {^([0-9a-f]{40}) refs/([^^]*)$} $line \
402 if {[regexp {^remotes/.*/HEAD$} $path match]} {
405 if {![regexp {^(tags|heads)/(.*)$} $path match type name]} {
409 if {[regexp {^remotes/} $path match]} {
412 if {$type == "tags"} {
413 set tagids($name) $id
414 lappend idtags($id) $name
419 set commit [exec git rev-parse "$id^0"]
420 if {$commit != $id} {
421 set tagids($name) $commit
422 lappend idtags($commit) $name
426 set tagcontents($name) [exec git cat-file tag $id]
428 } elseif { $type == "heads" } {
429 set headids($name) $id
430 lappend idheads($id) $name
432 set otherrefids($name) $id
433 lappend idotherrefs($id) $name
440 set thehead [exec git symbolic-ref HEAD]
441 if {[string match "refs/heads/*" $thehead]} {
442 set mainhead [string range $thehead 11 end]
443 if {[info exists headids($mainhead)]} {
444 set mainheadid $headids($mainhead)
450 # update things for a head moved to a child of its previous location
451 proc movehead {id name} {
452 global headids idheads
454 removehead $headids($name) $name
455 set headids($name) $id
456 lappend idheads($id) $name
459 # update things when a head has been removed
460 proc removehead {id name} {
461 global headids idheads
463 if {$idheads($id) eq $name} {
466 set i [lsearch -exact $idheads($id) $name]
468 set idheads($id) [lreplace $idheads($id) $i $i]
474 proc show_error {w top msg} {
475 message $w.m -text $msg -justify center -aspect 400
476 pack $w.m -side top -fill x -padx 20 -pady 20
477 button $w.ok -text OK -command "destroy $top"
478 pack $w.ok -side bottom -fill x
479 bind $top <Visibility> "grab $top; focus $top"
480 bind $top <Key-Return> "destroy $top"
484 proc error_popup msg {
488 show_error $w $w $msg
491 proc confirm_popup msg {
497 message $w.m -text $msg -justify center -aspect 400
498 pack $w.m -side top -fill x -padx 20 -pady 20
499 button $w.ok -text OK -command "set confirm_ok 1; destroy $w"
500 pack $w.ok -side left -fill x
501 button $w.cancel -text Cancel -command "destroy $w"
502 pack $w.cancel -side right -fill x
503 bind $w <Visibility> "grab $w; focus $w"
509 global canv canv2 canv3 linespc charspc ctext cflist
510 global textfont mainfont uifont tabstop
511 global findtype findtypemenu findloc findstring fstring geometry
512 global entries sha1entry sha1string sha1but
513 global maincursor textcursor curtextcursor
514 global rowctxmenu fakerowmenu mergemax wrapcomment
515 global highlight_files gdttype
516 global searchstring sstring
517 global bgcolor fgcolor bglist fglist diffcolors selectbgcolor
521 .bar add cascade -label "File" -menu .bar.file
522 .bar configure -font $uifont
524 .bar.file add command -label "Update" -command updatecommits
525 .bar.file add command -label "Reread references" -command rereadrefs
526 .bar.file add command -label "Quit" -command doquit
527 .bar.file configure -font $uifont
529 .bar add cascade -label "Edit" -menu .bar.edit
530 .bar.edit add command -label "Preferences" -command doprefs
531 .bar.edit configure -font $uifont
533 menu .bar.view -font $uifont
534 .bar add cascade -label "View" -menu .bar.view
535 .bar.view add command -label "New view..." -command {newview 0}
536 .bar.view add command -label "Edit view..." -command editview \
538 .bar.view add command -label "Delete view" -command delview -state disabled
539 .bar.view add separator
540 .bar.view add radiobutton -label "All files" -command {showview 0} \
541 -variable selectedview -value 0
544 .bar add cascade -label "Help" -menu .bar.help
545 .bar.help add command -label "About gitk" -command about
546 .bar.help add command -label "Key bindings" -command keys
547 .bar.help configure -font $uifont
548 . configure -menu .bar
550 # the gui has upper and lower half, parts of a paned window.
551 panedwindow .ctop -orient vertical
553 # possibly use assumed geometry
554 if {![info exists geometry(pwsash0)]} {
555 set geometry(topheight) [expr {15 * $linespc}]
556 set geometry(topwidth) [expr {80 * $charspc}]
557 set geometry(botheight) [expr {15 * $linespc}]
558 set geometry(botwidth) [expr {50 * $charspc}]
559 set geometry(pwsash0) "[expr {40 * $charspc}] 2"
560 set geometry(pwsash1) "[expr {60 * $charspc}] 2"
563 # the upper half will have a paned window, a scroll bar to the right, and some stuff below
564 frame .tf -height $geometry(topheight) -width $geometry(topwidth)
566 panedwindow .tf.histframe.pwclist -orient horizontal -sashpad 0 -handlesize 4
568 # create three canvases
569 set cscroll .tf.histframe.csb
570 set canv .tf.histframe.pwclist.canv
572 -selectbackground $selectbgcolor \
573 -background $bgcolor -bd 0 \
574 -yscrollincr $linespc -yscrollcommand "scrollcanv $cscroll"
575 .tf.histframe.pwclist add $canv
576 set canv2 .tf.histframe.pwclist.canv2
578 -selectbackground $selectbgcolor \
579 -background $bgcolor -bd 0 -yscrollincr $linespc
580 .tf.histframe.pwclist add $canv2
581 set canv3 .tf.histframe.pwclist.canv3
583 -selectbackground $selectbgcolor \
584 -background $bgcolor -bd 0 -yscrollincr $linespc
585 .tf.histframe.pwclist add $canv3
586 eval .tf.histframe.pwclist sash place 0 $geometry(pwsash0)
587 eval .tf.histframe.pwclist sash place 1 $geometry(pwsash1)
589 # a scroll bar to rule them
590 scrollbar $cscroll -command {allcanvs yview} -highlightthickness 0
591 pack $cscroll -side right -fill y
592 bind .tf.histframe.pwclist <Configure> {resizeclistpanes %W %w}
593 lappend bglist $canv $canv2 $canv3
594 pack .tf.histframe.pwclist -fill both -expand 1 -side left
596 # we have two button bars at bottom of top frame. Bar 1
598 frame .tf.lbar -height 15
600 set sha1entry .tf.bar.sha1
601 set entries $sha1entry
602 set sha1but .tf.bar.sha1label
603 button $sha1but -text "SHA1 ID: " -state disabled -relief flat \
604 -command gotocommit -width 8 -font $uifont
605 $sha1but conf -disabledforeground [$sha1but cget -foreground]
606 pack .tf.bar.sha1label -side left
607 entry $sha1entry -width 40 -font $textfont -textvariable sha1string
608 trace add variable sha1string write sha1change
609 pack $sha1entry -side left -pady 2
611 image create bitmap bm-left -data {
612 #define left_width 16
613 #define left_height 16
614 static unsigned char left_bits[] = {
615 0x00, 0x00, 0xc0, 0x01, 0xe0, 0x00, 0x70, 0x00, 0x38, 0x00, 0x1c, 0x00,
616 0x0e, 0x00, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0x0e, 0x00, 0x1c, 0x00,
617 0x38, 0x00, 0x70, 0x00, 0xe0, 0x00, 0xc0, 0x01};
619 image create bitmap bm-right -data {
620 #define right_width 16
621 #define right_height 16
622 static unsigned char right_bits[] = {
623 0x00, 0x00, 0xc0, 0x01, 0x80, 0x03, 0x00, 0x07, 0x00, 0x0e, 0x00, 0x1c,
624 0x00, 0x38, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0x00, 0x38, 0x00, 0x1c,
625 0x00, 0x0e, 0x00, 0x07, 0x80, 0x03, 0xc0, 0x01};
627 button .tf.bar.leftbut -image bm-left -command goback \
628 -state disabled -width 26
629 pack .tf.bar.leftbut -side left -fill y
630 button .tf.bar.rightbut -image bm-right -command goforw \
631 -state disabled -width 26
632 pack .tf.bar.rightbut -side left -fill y
634 button .tf.bar.findbut -text "Find" -command dofind -font $uifont
635 pack .tf.bar.findbut -side left
637 set fstring .tf.bar.findstring
638 lappend entries $fstring
639 entry $fstring -width 30 -font $textfont -textvariable findstring
640 trace add variable findstring write find_change
641 pack $fstring -side left -expand 1 -fill x -in .tf.bar
643 set findtypemenu [tk_optionMenu .tf.bar.findtype \
644 findtype Exact IgnCase Regexp]
645 trace add variable findtype write find_change
646 .tf.bar.findtype configure -font $uifont
647 .tf.bar.findtype.menu configure -font $uifont
648 set findloc "All fields"
649 tk_optionMenu .tf.bar.findloc findloc "All fields" Headline \
650 Comments Author Committer
651 trace add variable findloc write find_change
652 .tf.bar.findloc configure -font $uifont
653 .tf.bar.findloc.menu configure -font $uifont
654 pack .tf.bar.findloc -side right
655 pack .tf.bar.findtype -side right
657 # build up the bottom bar of upper window
658 label .tf.lbar.flabel -text "Highlight: Commits " \
660 pack .tf.lbar.flabel -side left -fill y
661 set gdttype "touching paths:"
662 set gm [tk_optionMenu .tf.lbar.gdttype gdttype "touching paths:" \
663 "adding/removing string:"]
664 trace add variable gdttype write hfiles_change
665 $gm conf -font $uifont
666 .tf.lbar.gdttype conf -font $uifont
667 pack .tf.lbar.gdttype -side left -fill y
668 entry .tf.lbar.fent -width 25 -font $textfont \
669 -textvariable highlight_files
670 trace add variable highlight_files write hfiles_change
671 lappend entries .tf.lbar.fent
672 pack .tf.lbar.fent -side left -fill x -expand 1
673 label .tf.lbar.vlabel -text " OR in view" -font $uifont
674 pack .tf.lbar.vlabel -side left -fill y
675 global viewhlmenu selectedhlview
676 set viewhlmenu [tk_optionMenu .tf.lbar.vhl selectedhlview None]
677 $viewhlmenu entryconf None -command delvhighlight
678 $viewhlmenu conf -font $uifont
679 .tf.lbar.vhl conf -font $uifont
680 pack .tf.lbar.vhl -side left -fill y
681 label .tf.lbar.rlabel -text " OR " -font $uifont
682 pack .tf.lbar.rlabel -side left -fill y
683 global highlight_related
684 set m [tk_optionMenu .tf.lbar.relm highlight_related None \
685 "Descendent" "Not descendent" "Ancestor" "Not ancestor"]
686 $m conf -font $uifont
687 .tf.lbar.relm conf -font $uifont
688 trace add variable highlight_related write vrel_change
689 pack .tf.lbar.relm -side left -fill y
691 # Finish putting the upper half of the viewer together
692 pack .tf.lbar -in .tf -side bottom -fill x
693 pack .tf.bar -in .tf -side bottom -fill x
694 pack .tf.histframe -fill both -side top -expand 1
696 .ctop paneconfigure .tf -height $geometry(topheight)
697 .ctop paneconfigure .tf -width $geometry(topwidth)
699 # now build up the bottom
700 panedwindow .pwbottom -orient horizontal
702 # lower left, a text box over search bar, scroll bar to the right
703 # if we know window height, then that will set the lower text height, otherwise
704 # we set lower text height which will drive window height
705 if {[info exists geometry(main)]} {
706 frame .bleft -width $geometry(botwidth)
708 frame .bleft -width $geometry(botwidth) -height $geometry(botheight)
713 button .bleft.top.search -text "Search" -command dosearch \
715 pack .bleft.top.search -side left -padx 5
716 set sstring .bleft.top.sstring
717 entry $sstring -width 20 -font $textfont -textvariable searchstring
718 lappend entries $sstring
719 trace add variable searchstring write incrsearch
720 pack $sstring -side left -expand 1 -fill x
721 radiobutton .bleft.mid.diff -text "Diff" \
722 -command changediffdisp -variable diffelide -value {0 0}
723 radiobutton .bleft.mid.old -text "Old version" \
724 -command changediffdisp -variable diffelide -value {0 1}
725 radiobutton .bleft.mid.new -text "New version" \
726 -command changediffdisp -variable diffelide -value {1 0}
727 pack .bleft.mid.diff .bleft.mid.old .bleft.mid.new -side left
728 set ctext .bleft.ctext
729 text $ctext -background $bgcolor -foreground $fgcolor \
730 -tabs "[expr {$tabstop * $charspc}]" \
731 -state disabled -font $textfont \
732 -yscrollcommand scrolltext -wrap none
733 scrollbar .bleft.sb -command "$ctext yview"
734 pack .bleft.top -side top -fill x
735 pack .bleft.mid -side top -fill x
736 pack .bleft.sb -side right -fill y
737 pack $ctext -side left -fill both -expand 1
738 lappend bglist $ctext
739 lappend fglist $ctext
741 $ctext tag conf comment -wrap $wrapcomment
742 $ctext tag conf filesep -font [concat $textfont bold] -back "#aaaaaa"
743 $ctext tag conf hunksep -fore [lindex $diffcolors 2]
744 $ctext tag conf d0 -fore [lindex $diffcolors 0]
745 $ctext tag conf d1 -fore [lindex $diffcolors 1]
746 $ctext tag conf m0 -fore red
747 $ctext tag conf m1 -fore blue
748 $ctext tag conf m2 -fore green
749 $ctext tag conf m3 -fore purple
750 $ctext tag conf m4 -fore brown
751 $ctext tag conf m5 -fore "#009090"
752 $ctext tag conf m6 -fore magenta
753 $ctext tag conf m7 -fore "#808000"
754 $ctext tag conf m8 -fore "#009000"
755 $ctext tag conf m9 -fore "#ff0080"
756 $ctext tag conf m10 -fore cyan
757 $ctext tag conf m11 -fore "#b07070"
758 $ctext tag conf m12 -fore "#70b0f0"
759 $ctext tag conf m13 -fore "#70f0b0"
760 $ctext tag conf m14 -fore "#f0b070"
761 $ctext tag conf m15 -fore "#ff70b0"
762 $ctext tag conf mmax -fore darkgrey
764 $ctext tag conf mresult -font [concat $textfont bold]
765 $ctext tag conf msep -font [concat $textfont bold]
766 $ctext tag conf found -back yellow
769 .pwbottom paneconfigure .bleft -width $geometry(botwidth)
774 radiobutton .bright.mode.patch -text "Patch" \
775 -command reselectline -variable cmitmode -value "patch"
776 .bright.mode.patch configure -font $uifont
777 radiobutton .bright.mode.tree -text "Tree" \
778 -command reselectline -variable cmitmode -value "tree"
779 .bright.mode.tree configure -font $uifont
780 grid .bright.mode.patch .bright.mode.tree -sticky ew
781 pack .bright.mode -side top -fill x
782 set cflist .bright.cfiles
783 set indent [font measure $mainfont "nn"]
785 -selectbackground $selectbgcolor \
786 -background $bgcolor -foreground $fgcolor \
788 -tabs [list $indent [expr {2 * $indent}]] \
789 -yscrollcommand ".bright.sb set" \
790 -cursor [. cget -cursor] \
791 -spacing1 1 -spacing3 1
792 lappend bglist $cflist
793 lappend fglist $cflist
794 scrollbar .bright.sb -command "$cflist yview"
795 pack .bright.sb -side right -fill y
796 pack $cflist -side left -fill both -expand 1
797 $cflist tag configure highlight \
798 -background [$cflist cget -selectbackground]
799 $cflist tag configure bold -font [concat $mainfont bold]
801 .pwbottom add .bright
804 # restore window position if known
805 if {[info exists geometry(main)]} {
806 wm geometry . "$geometry(main)"
809 bind .pwbottom <Configure> {resizecdetpanes %W %w}
810 pack .ctop -fill both -expand 1
811 bindall <1> {selcanvline %W %x %y}
812 #bindall <B1-Motion> {selcanvline %W %x %y}
813 bindall <ButtonRelease-4> "allcanvs yview scroll -5 units"
814 bindall <ButtonRelease-5> "allcanvs yview scroll 5 units"
815 bindall <2> "canvscan mark %W %x %y"
816 bindall <B2-Motion> "canvscan dragto %W %x %y"
817 bindkey <Home> selfirstline
818 bindkey <End> sellastline
819 bind . <Key-Up> "selnextline -1"
820 bind . <Key-Down> "selnextline 1"
821 bind . <Shift-Key-Up> "next_highlight -1"
822 bind . <Shift-Key-Down> "next_highlight 1"
823 bindkey <Key-Right> "goforw"
824 bindkey <Key-Left> "goback"
825 bind . <Key-Prior> "selnextpage -1"
826 bind . <Key-Next> "selnextpage 1"
827 bind . <Control-Home> "allcanvs yview moveto 0.0"
828 bind . <Control-End> "allcanvs yview moveto 1.0"
829 bind . <Control-Key-Up> "allcanvs yview scroll -1 units"
830 bind . <Control-Key-Down> "allcanvs yview scroll 1 units"
831 bind . <Control-Key-Prior> "allcanvs yview scroll -1 pages"
832 bind . <Control-Key-Next> "allcanvs yview scroll 1 pages"
833 bindkey <Key-Delete> "$ctext yview scroll -1 pages"
834 bindkey <Key-BackSpace> "$ctext yview scroll -1 pages"
835 bindkey <Key-space> "$ctext yview scroll 1 pages"
836 bindkey p "selnextline -1"
837 bindkey n "selnextline 1"
840 bindkey i "selnextline -1"
841 bindkey k "selnextline 1"
844 bindkey b "$ctext yview scroll -1 pages"
845 bindkey d "$ctext yview scroll 18 units"
846 bindkey u "$ctext yview scroll -18 units"
847 bindkey / {findnext 1}
848 bindkey <Key-Return> {findnext 0}
851 bindkey <F5> updatecommits
852 bind . <Control-q> doquit
853 bind . <Control-f> dofind
854 bind . <Control-g> {findnext 0}
855 bind . <Control-r> dosearchback
856 bind . <Control-s> dosearch
857 bind . <Control-equal> {incrfont 1}
858 bind . <Control-KP_Add> {incrfont 1}
859 bind . <Control-minus> {incrfont -1}
860 bind . <Control-KP_Subtract> {incrfont -1}
861 wm protocol . WM_DELETE_WINDOW doquit
862 bind . <Button-1> "click %W"
863 bind $fstring <Key-Return> dofind
864 bind $sha1entry <Key-Return> gotocommit
865 bind $sha1entry <<PasteSelection>> clearsha1
866 bind $cflist <1> {sel_flist %W %x %y; break}
867 bind $cflist <B1-Motion> {sel_flist %W %x %y; break}
868 bind $cflist <ButtonRelease-1> {treeclick %W %x %y}
870 set maincursor [. cget -cursor]
871 set textcursor [$ctext cget -cursor]
872 set curtextcursor $textcursor
874 set rowctxmenu .rowctxmenu
875 menu $rowctxmenu -tearoff 0
876 $rowctxmenu add command -label "Diff this -> selected" \
877 -command {diffvssel 0}
878 $rowctxmenu add command -label "Diff selected -> this" \
879 -command {diffvssel 1}
880 $rowctxmenu add command -label "Make patch" -command mkpatch
881 $rowctxmenu add command -label "Create tag" -command mktag
882 $rowctxmenu add command -label "Write commit to file" -command writecommit
883 $rowctxmenu add command -label "Create new branch" -command mkbranch
884 $rowctxmenu add command -label "Cherry-pick this commit" \
887 set fakerowmenu .fakerowmenu
888 menu $fakerowmenu -tearoff 0
889 $fakerowmenu add command -label "Diff this -> selected" \
890 -command {diffvssel 0}
891 $fakerowmenu add command -label "Diff selected -> this" \
892 -command {diffvssel 1}
893 $fakerowmenu add command -label "Make patch" -command mkpatch
894 # $fakerowmenu add command -label "Commit" -command {mkcommit 0}
895 # $fakerowmenu add command -label "Commit all" -command {mkcommit 1}
896 # $fakerowmenu add command -label "Revert local changes" -command revertlocal
898 set headctxmenu .headctxmenu
899 menu $headctxmenu -tearoff 0
900 $headctxmenu add command -label "Check out this branch" \
902 $headctxmenu add command -label "Remove this branch" \
906 # mouse-2 makes all windows scan vertically, but only the one
907 # the cursor is in scans horizontally
908 proc canvscan {op w x y} {
909 global canv canv2 canv3
910 foreach c [list $canv $canv2 $canv3] {
919 proc scrollcanv {cscroll f0 f1} {
925 # when we make a key binding for the toplevel, make sure
926 # it doesn't get triggered when that key is pressed in the
927 # find string entry widget.
928 proc bindkey {ev script} {
931 set escript [bind Entry $ev]
932 if {$escript == {}} {
933 set escript [bind Entry <Key>]
936 bind $e $ev "$escript; break"
940 # set the focus back to the toplevel for any click outside
951 global canv canv2 canv3 ctext cflist mainfont textfont uifont tabstop
952 global stuffsaved findmergefiles maxgraphpct
953 global maxwidth showneartags showlocalchanges
954 global viewname viewfiles viewargs viewperm nextviewnum
955 global cmitmode wrapcomment
956 global colors bgcolor fgcolor diffcolors selectbgcolor
958 if {$stuffsaved} return
959 if {![winfo viewable .]} return
961 set f [open "~/.gitk-new" w]
962 puts $f [list set mainfont $mainfont]
963 puts $f [list set textfont $textfont]
964 puts $f [list set uifont $uifont]
965 puts $f [list set tabstop $tabstop]
966 puts $f [list set findmergefiles $findmergefiles]
967 puts $f [list set maxgraphpct $maxgraphpct]
968 puts $f [list set maxwidth $maxwidth]
969 puts $f [list set cmitmode $cmitmode]
970 puts $f [list set wrapcomment $wrapcomment]
971 puts $f [list set showneartags $showneartags]
972 puts $f [list set showlocalchanges $showlocalchanges]
973 puts $f [list set bgcolor $bgcolor]
974 puts $f [list set fgcolor $fgcolor]
975 puts $f [list set colors $colors]
976 puts $f [list set diffcolors $diffcolors]
977 puts $f [list set selectbgcolor $selectbgcolor]
979 puts $f "set geometry(main) [wm geometry .]"
980 puts $f "set geometry(topwidth) [winfo width .tf]"
981 puts $f "set geometry(topheight) [winfo height .tf]"
982 puts $f "set geometry(pwsash0) \"[.tf.histframe.pwclist sash coord 0]\""
983 puts $f "set geometry(pwsash1) \"[.tf.histframe.pwclist sash coord 1]\""
984 puts $f "set geometry(botwidth) [winfo width .bleft]"
985 puts $f "set geometry(botheight) [winfo height .bleft]"
987 puts -nonewline $f "set permviews {"
988 for {set v 0} {$v < $nextviewnum} {incr v} {
990 puts $f "{[list $viewname($v) $viewfiles($v) $viewargs($v)]}"
995 file rename -force "~/.gitk-new" "~/.gitk"
1000 proc resizeclistpanes {win w} {
1002 if {[info exists oldwidth($win)]} {
1003 set s0 [$win sash coord 0]
1004 set s1 [$win sash coord 1]
1006 set sash0 [expr {int($w/2 - 2)}]
1007 set sash1 [expr {int($w*5/6 - 2)}]
1009 set factor [expr {1.0 * $w / $oldwidth($win)}]
1010 set sash0 [expr {int($factor * [lindex $s0 0])}]
1011 set sash1 [expr {int($factor * [lindex $s1 0])}]
1015 if {$sash1 < $sash0 + 20} {
1016 set sash1 [expr {$sash0 + 20}]
1018 if {$sash1 > $w - 10} {
1019 set sash1 [expr {$w - 10}]
1020 if {$sash0 > $sash1 - 20} {
1021 set sash0 [expr {$sash1 - 20}]
1025 $win sash place 0 $sash0 [lindex $s0 1]
1026 $win sash place 1 $sash1 [lindex $s1 1]
1028 set oldwidth($win) $w
1031 proc resizecdetpanes {win w} {
1033 if {[info exists oldwidth($win)]} {
1034 set s0 [$win sash coord 0]
1036 set sash0 [expr {int($w*3/4 - 2)}]
1038 set factor [expr {1.0 * $w / $oldwidth($win)}]
1039 set sash0 [expr {int($factor * [lindex $s0 0])}]
1043 if {$sash0 > $w - 15} {
1044 set sash0 [expr {$w - 15}]
1047 $win sash place 0 $sash0 [lindex $s0 1]
1049 set oldwidth($win) $w
1052 proc allcanvs args {
1053 global canv canv2 canv3
1059 proc bindall {event action} {
1060 global canv canv2 canv3
1061 bind $canv $event $action
1062 bind $canv2 $event $action
1063 bind $canv3 $event $action
1069 if {[winfo exists $w]} {
1074 wm title $w "About gitk"
1075 message $w.m -text {
1076 Gitk - a commit viewer for git
1078 Copyright © 2005-2006 Paul Mackerras
1080 Use and redistribute under the terms of the GNU General Public License} \
1081 -justify center -aspect 400 -border 2 -bg white -relief groove
1082 pack $w.m -side top -fill x -padx 2 -pady 2
1083 $w.m configure -font $uifont
1084 button $w.ok -text Close -command "destroy $w" -default active
1085 pack $w.ok -side bottom
1086 $w.ok configure -font $uifont
1087 bind $w <Visibility> "focus $w.ok"
1088 bind $w <Key-Escape> "destroy $w"
1089 bind $w <Key-Return> "destroy $w"
1095 if {[winfo exists $w]} {
1100 wm title $w "Gitk key bindings"
1101 message $w.m -text {
1105 <Home> Move to first commit
1106 <End> Move to last commit
1107 <Up>, p, i Move up one commit
1108 <Down>, n, k Move down one commit
1109 <Left>, z, j Go back in history list
1110 <Right>, x, l Go forward in history list
1111 <PageUp> Move up one page in commit list
1112 <PageDown> Move down one page in commit list
1113 <Ctrl-Home> Scroll to top of commit list
1114 <Ctrl-End> Scroll to bottom of commit list
1115 <Ctrl-Up> Scroll commit list up one line
1116 <Ctrl-Down> Scroll commit list down one line
1117 <Ctrl-PageUp> Scroll commit list up one page
1118 <Ctrl-PageDown> Scroll commit list down one page
1119 <Shift-Up> Move to previous highlighted line
1120 <Shift-Down> Move to next highlighted line
1121 <Delete>, b Scroll diff view up one page
1122 <Backspace> Scroll diff view up one page
1123 <Space> Scroll diff view down one page
1124 u Scroll diff view up 18 lines
1125 d Scroll diff view down 18 lines
1127 <Ctrl-G> Move to next find hit
1128 <Return> Move to next find hit
1129 / Move to next find hit, or redo find
1130 ? Move to previous find hit
1131 f Scroll diff view to next file
1132 <Ctrl-S> Search for next hit in diff view
1133 <Ctrl-R> Search for previous hit in diff view
1134 <Ctrl-KP+> Increase font size
1135 <Ctrl-plus> Increase font size
1136 <Ctrl-KP-> Decrease font size
1137 <Ctrl-minus> Decrease font size
1140 -justify left -bg white -border 2 -relief groove
1141 pack $w.m -side top -fill both -padx 2 -pady 2
1142 $w.m configure -font $uifont
1143 button $w.ok -text Close -command "destroy $w" -default active
1144 pack $w.ok -side bottom
1145 $w.ok configure -font $uifont
1146 bind $w <Visibility> "focus $w.ok"
1147 bind $w <Key-Escape> "destroy $w"
1148 bind $w <Key-Return> "destroy $w"
1151 # Procedures for manipulating the file list window at the
1152 # bottom right of the overall window.
1154 proc treeview {w l openlevs} {
1155 global treecontents treediropen treeheight treeparent treeindex
1165 set treecontents() {}
1166 $w conf -state normal
1168 while {[string range $f 0 $prefixend] ne $prefix} {
1169 if {$lev <= $openlevs} {
1170 $w mark set e:$treeindex($prefix) "end -1c"
1171 $w mark gravity e:$treeindex($prefix) left
1173 set treeheight($prefix) $ht
1174 incr ht [lindex $htstack end]
1175 set htstack [lreplace $htstack end end]
1176 set prefixend [lindex $prefendstack end]
1177 set prefendstack [lreplace $prefendstack end end]
1178 set prefix [string range $prefix 0 $prefixend]
1181 set tail [string range $f [expr {$prefixend+1}] end]
1182 while {[set slash [string first "/" $tail]] >= 0} {
1185 lappend prefendstack $prefixend
1186 incr prefixend [expr {$slash + 1}]
1187 set d [string range $tail 0 $slash]
1188 lappend treecontents($prefix) $d
1189 set oldprefix $prefix
1191 set treecontents($prefix) {}
1192 set treeindex($prefix) [incr ix]
1193 set treeparent($prefix) $oldprefix
1194 set tail [string range $tail [expr {$slash+1}] end]
1195 if {$lev <= $openlevs} {
1197 set treediropen($prefix) [expr {$lev < $openlevs}]
1198 set bm [expr {$lev == $openlevs? "tri-rt": "tri-dn"}]
1199 $w mark set d:$ix "end -1c"
1200 $w mark gravity d:$ix left
1202 for {set i 0} {$i < $lev} {incr i} {append str "\t"}
1204 $w image create end -align center -image $bm -padx 1 \
1206 $w insert end $d [highlight_tag $prefix]
1207 $w mark set s:$ix "end -1c"
1208 $w mark gravity s:$ix left
1213 if {$lev <= $openlevs} {
1216 for {set i 0} {$i < $lev} {incr i} {append str "\t"}
1218 $w insert end $tail [highlight_tag $f]
1220 lappend treecontents($prefix) $tail
1223 while {$htstack ne {}} {
1224 set treeheight($prefix) $ht
1225 incr ht [lindex $htstack end]
1226 set htstack [lreplace $htstack end end]
1228 $w conf -state disabled
1231 proc linetoelt {l} {
1232 global treeheight treecontents
1237 foreach e $treecontents($prefix) {
1242 if {[string index $e end] eq "/"} {
1243 set n $treeheight($prefix$e)
1255 proc highlight_tree {y prefix} {
1256 global treeheight treecontents cflist
1258 foreach e $treecontents($prefix) {
1260 if {[highlight_tag $path] ne {}} {
1261 $cflist tag add bold $y.0 "$y.0 lineend"
1264 if {[string index $e end] eq "/" && $treeheight($path) > 1} {
1265 set y [highlight_tree $y $path]
1271 proc treeclosedir {w dir} {
1272 global treediropen treeheight treeparent treeindex
1274 set ix $treeindex($dir)
1275 $w conf -state normal
1276 $w delete s:$ix e:$ix
1277 set treediropen($dir) 0
1278 $w image configure a:$ix -image tri-rt
1279 $w conf -state disabled
1280 set n [expr {1 - $treeheight($dir)}]
1281 while {$dir ne {}} {
1282 incr treeheight($dir) $n
1283 set dir $treeparent($dir)
1287 proc treeopendir {w dir} {
1288 global treediropen treeheight treeparent treecontents treeindex
1290 set ix $treeindex($dir)
1291 $w conf -state normal
1292 $w image configure a:$ix -image tri-dn
1293 $w mark set e:$ix s:$ix
1294 $w mark gravity e:$ix right
1297 set n [llength $treecontents($dir)]
1298 for {set x $dir} {$x ne {}} {set x $treeparent($x)} {
1301 incr treeheight($x) $n
1303 foreach e $treecontents($dir) {
1305 if {[string index $e end] eq "/"} {
1306 set iy $treeindex($de)
1307 $w mark set d:$iy e:$ix
1308 $w mark gravity d:$iy left
1309 $w insert e:$ix $str
1310 set treediropen($de) 0
1311 $w image create e:$ix -align center -image tri-rt -padx 1 \
1313 $w insert e:$ix $e [highlight_tag $de]
1314 $w mark set s:$iy e:$ix
1315 $w mark gravity s:$iy left
1316 set treeheight($de) 1
1318 $w insert e:$ix $str
1319 $w insert e:$ix $e [highlight_tag $de]
1322 $w mark gravity e:$ix left
1323 $w conf -state disabled
1324 set treediropen($dir) 1
1325 set top [lindex [split [$w index @0,0] .] 0]
1326 set ht [$w cget -height]
1327 set l [lindex [split [$w index s:$ix] .] 0]
1330 } elseif {$l + $n + 1 > $top + $ht} {
1331 set top [expr {$l + $n + 2 - $ht}]
1339 proc treeclick {w x y} {
1340 global treediropen cmitmode ctext cflist cflist_top
1342 if {$cmitmode ne "tree"} return
1343 if {![info exists cflist_top]} return
1344 set l [lindex [split [$w index "@$x,$y"] "."] 0]
1345 $cflist tag remove highlight $cflist_top.0 "$cflist_top.0 lineend"
1346 $cflist tag add highlight $l.0 "$l.0 lineend"
1352 set e [linetoelt $l]
1353 if {[string index $e end] ne "/"} {
1355 } elseif {$treediropen($e)} {
1362 proc setfilelist {id} {
1363 global treefilelist cflist
1365 treeview $cflist $treefilelist($id) 0
1368 image create bitmap tri-rt -background black -foreground blue -data {
1369 #define tri-rt_width 13
1370 #define tri-rt_height 13
1371 static unsigned char tri-rt_bits[] = {
1372 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x30, 0x00, 0x70, 0x00, 0xf0, 0x00,
1373 0xf0, 0x01, 0xf0, 0x00, 0x70, 0x00, 0x30, 0x00, 0x10, 0x00, 0x00, 0x00,
1376 #define tri-rt-mask_width 13
1377 #define tri-rt-mask_height 13
1378 static unsigned char tri-rt-mask_bits[] = {
1379 0x08, 0x00, 0x18, 0x00, 0x38, 0x00, 0x78, 0x00, 0xf8, 0x00, 0xf8, 0x01,
1380 0xf8, 0x03, 0xf8, 0x01, 0xf8, 0x00, 0x78, 0x00, 0x38, 0x00, 0x18, 0x00,
1383 image create bitmap tri-dn -background black -foreground blue -data {
1384 #define tri-dn_width 13
1385 #define tri-dn_height 13
1386 static unsigned char tri-dn_bits[] = {
1387 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x07, 0xf8, 0x03,
1388 0xf0, 0x01, 0xe0, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1391 #define tri-dn-mask_width 13
1392 #define tri-dn-mask_height 13
1393 static unsigned char tri-dn-mask_bits[] = {
1394 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x1f, 0xfe, 0x0f, 0xfc, 0x07,
1395 0xf8, 0x03, 0xf0, 0x01, 0xe0, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
1399 proc init_flist {first} {
1400 global cflist cflist_top selectedline difffilestart
1402 $cflist conf -state normal
1403 $cflist delete 0.0 end
1405 $cflist insert end $first
1407 $cflist tag add highlight 1.0 "1.0 lineend"
1409 catch {unset cflist_top}
1411 $cflist conf -state disabled
1412 set difffilestart {}
1415 proc highlight_tag {f} {
1416 global highlight_paths
1418 foreach p $highlight_paths {
1419 if {[string match $p $f]} {
1426 proc highlight_filelist {} {
1427 global cmitmode cflist
1429 $cflist conf -state normal
1430 if {$cmitmode ne "tree"} {
1431 set end [lindex [split [$cflist index end] .] 0]
1432 for {set l 2} {$l < $end} {incr l} {
1433 set line [$cflist get $l.0 "$l.0 lineend"]
1434 if {[highlight_tag $line] ne {}} {
1435 $cflist tag add bold $l.0 "$l.0 lineend"
1441 $cflist conf -state disabled
1444 proc unhighlight_filelist {} {
1447 $cflist conf -state normal
1448 $cflist tag remove bold 1.0 end
1449 $cflist conf -state disabled
1452 proc add_flist {fl} {
1455 $cflist conf -state normal
1457 $cflist insert end "\n"
1458 $cflist insert end $f [highlight_tag $f]
1460 $cflist conf -state disabled
1463 proc sel_flist {w x y} {
1464 global ctext difffilestart cflist cflist_top cmitmode
1466 if {$cmitmode eq "tree"} return
1467 if {![info exists cflist_top]} return
1468 set l [lindex [split [$w index "@$x,$y"] "."] 0]
1469 $cflist tag remove highlight $cflist_top.0 "$cflist_top.0 lineend"
1470 $cflist tag add highlight $l.0 "$l.0 lineend"
1475 catch {$ctext yview [lindex $difffilestart [expr {$l - 2}]]}
1479 # Functions for adding and removing shell-type quoting
1481 proc shellquote {str} {
1482 if {![string match "*\['\"\\ \t]*" $str]} {
1485 if {![string match "*\['\"\\]*" $str]} {
1488 if {![string match "*'*" $str]} {
1491 return "\"[string map {\" \\\" \\ \\\\} $str]\""
1494 proc shellarglist {l} {
1500 append str [shellquote $a]
1505 proc shelldequote {str} {
1510 if {![regexp -start $used -indices "\['\"\\\\ \t]" $str first]} {
1511 append ret [string range $str $used end]
1512 set used [string length $str]
1515 set first [lindex $first 0]
1516 set ch [string index $str $first]
1517 if {$first > $used} {
1518 append ret [string range $str $used [expr {$first - 1}]]
1521 if {$ch eq " " || $ch eq "\t"} break
1524 set first [string first "'" $str $used]
1526 error "unmatched single-quote"
1528 append ret [string range $str $used [expr {$first - 1}]]
1533 if {$used >= [string length $str]} {
1534 error "trailing backslash"
1536 append ret [string index $str $used]
1541 if {![regexp -start $used -indices "\[\"\\\\]" $str first]} {
1542 error "unmatched double-quote"
1544 set first [lindex $first 0]
1545 set ch [string index $str $first]
1546 if {$first > $used} {
1547 append ret [string range $str $used [expr {$first - 1}]]
1550 if {$ch eq "\""} break
1552 append ret [string index $str $used]
1556 return [list $used $ret]
1559 proc shellsplit {str} {
1562 set str [string trimleft $str]
1563 if {$str eq {}} break
1564 set dq [shelldequote $str]
1565 set n [lindex $dq 0]
1566 set word [lindex $dq 1]
1567 set str [string range $str $n end]
1573 # Code to implement multiple views
1575 proc newview {ishighlight} {
1576 global nextviewnum newviewname newviewperm uifont newishighlight
1577 global newviewargs revtreeargs
1579 set newishighlight $ishighlight
1581 if {[winfo exists $top]} {
1585 set newviewname($nextviewnum) "View $nextviewnum"
1586 set newviewperm($nextviewnum) 0
1587 set newviewargs($nextviewnum) [shellarglist $revtreeargs]
1588 vieweditor $top $nextviewnum "Gitk view definition"
1593 global viewname viewperm newviewname newviewperm
1594 global viewargs newviewargs
1596 set top .gitkvedit-$curview
1597 if {[winfo exists $top]} {
1601 set newviewname($curview) $viewname($curview)
1602 set newviewperm($curview) $viewperm($curview)
1603 set newviewargs($curview) [shellarglist $viewargs($curview)]
1604 vieweditor $top $curview "Gitk: edit view $viewname($curview)"
1607 proc vieweditor {top n title} {
1608 global newviewname newviewperm viewfiles
1612 wm title $top $title
1613 label $top.nl -text "Name" -font $uifont
1614 entry $top.name -width 20 -textvariable newviewname($n) -font $uifont
1615 grid $top.nl $top.name -sticky w -pady 5
1616 checkbutton $top.perm -text "Remember this view" -variable newviewperm($n) \
1618 grid $top.perm - -pady 5 -sticky w
1619 message $top.al -aspect 1000 -font $uifont \
1620 -text "Commits to include (arguments to git rev-list):"
1621 grid $top.al - -sticky w -pady 5
1622 entry $top.args -width 50 -textvariable newviewargs($n) \
1623 -background white -font $uifont
1624 grid $top.args - -sticky ew -padx 5
1625 message $top.l -aspect 1000 -font $uifont \
1626 -text "Enter files and directories to include, one per line:"
1627 grid $top.l - -sticky w
1628 text $top.t -width 40 -height 10 -background white -font $uifont
1629 if {[info exists viewfiles($n)]} {
1630 foreach f $viewfiles($n) {
1631 $top.t insert end $f
1632 $top.t insert end "\n"
1634 $top.t delete {end - 1c} end
1635 $top.t mark set insert 0.0
1637 grid $top.t - -sticky ew -padx 5
1639 button $top.buts.ok -text "OK" -command [list newviewok $top $n] \
1641 button $top.buts.can -text "Cancel" -command [list destroy $top] \
1643 grid $top.buts.ok $top.buts.can
1644 grid columnconfigure $top.buts 0 -weight 1 -uniform a
1645 grid columnconfigure $top.buts 1 -weight 1 -uniform a
1646 grid $top.buts - -pady 10 -sticky ew
1650 proc doviewmenu {m first cmd op argv} {
1651 set nmenu [$m index end]
1652 for {set i $first} {$i <= $nmenu} {incr i} {
1653 if {[$m entrycget $i -command] eq $cmd} {
1654 eval $m $op $i $argv
1660 proc allviewmenus {n op args} {
1663 doviewmenu .bar.view 5 [list showview $n] $op $args
1664 doviewmenu $viewhlmenu 1 [list addvhighlight $n] $op $args
1667 proc newviewok {top n} {
1668 global nextviewnum newviewperm newviewname newishighlight
1669 global viewname viewfiles viewperm selectedview curview
1670 global viewargs newviewargs viewhlmenu
1673 set newargs [shellsplit $newviewargs($n)]
1675 error_popup "Error in commit selection arguments: $err"
1681 foreach f [split [$top.t get 0.0 end] "\n"] {
1682 set ft [string trim $f]
1687 if {![info exists viewfiles($n)]} {
1688 # creating a new view
1690 set viewname($n) $newviewname($n)
1691 set viewperm($n) $newviewperm($n)
1692 set viewfiles($n) $files
1693 set viewargs($n) $newargs
1695 if {!$newishighlight} {
1698 run addvhighlight $n
1701 # editing an existing view
1702 set viewperm($n) $newviewperm($n)
1703 if {$newviewname($n) ne $viewname($n)} {
1704 set viewname($n) $newviewname($n)
1705 doviewmenu .bar.view 5 [list showview $n] \
1706 entryconf [list -label $viewname($n)]
1707 doviewmenu $viewhlmenu 1 [list addvhighlight $n] \
1708 entryconf [list -label $viewname($n) -value $viewname($n)]
1710 if {$files ne $viewfiles($n) || $newargs ne $viewargs($n)} {
1711 set viewfiles($n) $files
1712 set viewargs($n) $newargs
1713 if {$curview == $n} {
1718 catch {destroy $top}
1722 global curview viewdata viewperm hlview selectedhlview
1724 if {$curview == 0} return
1725 if {[info exists hlview] && $hlview == $curview} {
1726 set selectedhlview None
1729 allviewmenus $curview delete
1730 set viewdata($curview) {}
1731 set viewperm($curview) 0
1735 proc addviewmenu {n} {
1736 global viewname viewhlmenu
1738 .bar.view add radiobutton -label $viewname($n) \
1739 -command [list showview $n] -variable selectedview -value $n
1740 $viewhlmenu add radiobutton -label $viewname($n) \
1741 -command [list addvhighlight $n] -variable selectedhlview
1744 proc flatten {var} {
1748 foreach i [array names $var] {
1749 lappend ret $i [set $var\($i\)]
1754 proc unflatten {var l} {
1764 global curview viewdata viewfiles
1765 global displayorder parentlist childlist rowidlist rowoffsets
1766 global colormap rowtextx commitrow nextcolor canvxmax
1767 global numcommits rowrangelist commitlisted idrowranges rowchk
1768 global selectedline currentid canv canvy0
1769 global matchinglines treediffs
1770 global pending_select phase
1771 global commitidx rowlaidout rowoptim
1773 global selectedview selectfirst
1774 global vparentlist vchildlist vdisporder vcmitlisted
1775 global hlview selectedhlview
1777 if {$n == $curview} return
1779 if {[info exists selectedline]} {
1780 set selid $currentid
1781 set y [yc $selectedline]
1782 set ymax [lindex [$canv cget -scrollregion] 3]
1783 set span [$canv yview]
1784 set ytop [expr {[lindex $span 0] * $ymax}]
1785 set ybot [expr {[lindex $span 1] * $ymax}]
1786 if {$ytop < $y && $y < $ybot} {
1787 set yscreen [expr {$y - $ytop}]
1789 set yscreen [expr {($ybot - $ytop) / 2}]
1791 } elseif {[info exists pending_select]} {
1792 set selid $pending_select
1793 unset pending_select
1798 if {$curview >= 0} {
1799 set vparentlist($curview) $parentlist
1800 set vchildlist($curview) $childlist
1801 set vdisporder($curview) $displayorder
1802 set vcmitlisted($curview) $commitlisted
1804 set viewdata($curview) \
1805 [list $phase $rowidlist $rowoffsets $rowrangelist \
1806 [flatten idrowranges] [flatten idinlist] \
1807 $rowlaidout $rowoptim $numcommits]
1808 } elseif {![info exists viewdata($curview)]
1809 || [lindex $viewdata($curview) 0] ne {}} {
1810 set viewdata($curview) \
1811 [list {} $rowidlist $rowoffsets $rowrangelist]
1814 catch {unset matchinglines}
1815 catch {unset treediffs}
1817 if {[info exists hlview] && $hlview == $n} {
1819 set selectedhlview None
1824 .bar.view entryconf Edit* -state [expr {$n == 0? "disabled": "normal"}]
1825 .bar.view entryconf Delete* -state [expr {$n == 0? "disabled": "normal"}]
1827 if {![info exists viewdata($n)]} {
1829 set pending_select $selid
1836 set phase [lindex $v 0]
1837 set displayorder $vdisporder($n)
1838 set parentlist $vparentlist($n)
1839 set childlist $vchildlist($n)
1840 set commitlisted $vcmitlisted($n)
1841 set rowidlist [lindex $v 1]
1842 set rowoffsets [lindex $v 2]
1843 set rowrangelist [lindex $v 3]
1845 set numcommits [llength $displayorder]
1846 catch {unset idrowranges}
1848 unflatten idrowranges [lindex $v 4]
1849 unflatten idinlist [lindex $v 5]
1850 set rowlaidout [lindex $v 6]
1851 set rowoptim [lindex $v 7]
1852 set numcommits [lindex $v 8]
1853 catch {unset rowchk}
1856 catch {unset colormap}
1857 catch {unset rowtextx}
1859 set canvxmax [$canv cget -width]
1866 if {$selid ne {} && [info exists commitrow($n,$selid)]} {
1867 set row $commitrow($n,$selid)
1868 # try to get the selected row in the same position on the screen
1869 set ymax [lindex [$canv cget -scrollregion] 3]
1870 set ytop [expr {[yc $row] - $yscreen}]
1874 set yf [expr {$ytop * 1.0 / $ymax}]
1876 allcanvs yview moveto $yf
1880 } elseif {$selid ne {}} {
1881 set pending_select $selid
1883 set row [expr {[lindex $displayorder 0] eq $nullid}]
1884 if {$row < $numcommits} {
1891 if {$phase eq "getcommits"} {
1892 show_status "Reading commits..."
1895 } elseif {$numcommits == 0} {
1896 show_status "No commits selected"
1900 # Stuff relating to the highlighting facility
1902 proc ishighlighted {row} {
1903 global vhighlights fhighlights nhighlights rhighlights
1905 if {[info exists nhighlights($row)] && $nhighlights($row) > 0} {
1906 return $nhighlights($row)
1908 if {[info exists vhighlights($row)] && $vhighlights($row) > 0} {
1909 return $vhighlights($row)
1911 if {[info exists fhighlights($row)] && $fhighlights($row) > 0} {
1912 return $fhighlights($row)
1914 if {[info exists rhighlights($row)] && $rhighlights($row) > 0} {
1915 return $rhighlights($row)
1920 proc bolden {row font} {
1921 global canv linehtag selectedline boldrows
1923 lappend boldrows $row
1924 $canv itemconf $linehtag($row) -font $font
1925 if {[info exists selectedline] && $row == $selectedline} {
1927 set t [eval $canv create rect [$canv bbox $linehtag($row)] \
1928 -outline {{}} -tags secsel \
1929 -fill [$canv cget -selectbackground]]
1934 proc bolden_name {row font} {
1935 global canv2 linentag selectedline boldnamerows
1937 lappend boldnamerows $row
1938 $canv2 itemconf $linentag($row) -font $font
1939 if {[info exists selectedline] && $row == $selectedline} {
1940 $canv2 delete secsel
1941 set t [eval $canv2 create rect [$canv2 bbox $linentag($row)] \
1942 -outline {{}} -tags secsel \
1943 -fill [$canv2 cget -selectbackground]]
1949 global mainfont boldrows
1952 foreach row $boldrows {
1953 if {![ishighlighted $row]} {
1954 bolden $row $mainfont
1956 lappend stillbold $row
1959 set boldrows $stillbold
1962 proc addvhighlight {n} {
1963 global hlview curview viewdata vhl_done vhighlights commitidx
1965 if {[info exists hlview]} {
1969 if {$n != $curview && ![info exists viewdata($n)]} {
1970 set viewdata($n) [list getcommits {{}} {{}} {} {} {} 0 0 0 {}]
1971 set vparentlist($n) {}
1972 set vchildlist($n) {}
1973 set vdisporder($n) {}
1974 set vcmitlisted($n) {}
1977 set vhl_done $commitidx($hlview)
1978 if {$vhl_done > 0} {
1983 proc delvhighlight {} {
1984 global hlview vhighlights
1986 if {![info exists hlview]} return
1988 catch {unset vhighlights}
1992 proc vhighlightmore {} {
1993 global hlview vhl_done commitidx vhighlights
1994 global displayorder vdisporder curview mainfont
1996 set font [concat $mainfont bold]
1997 set max $commitidx($hlview)
1998 if {$hlview == $curview} {
1999 set disp $displayorder
2001 set disp $vdisporder($hlview)
2003 set vr [visiblerows]
2004 set r0 [lindex $vr 0]
2005 set r1 [lindex $vr 1]
2006 for {set i $vhl_done} {$i < $max} {incr i} {
2007 set id [lindex $disp $i]
2008 if {[info exists commitrow($curview,$id)]} {
2009 set row $commitrow($curview,$id)
2010 if {$r0 <= $row && $row <= $r1} {
2011 if {![highlighted $row]} {
2014 set vhighlights($row) 1
2021 proc askvhighlight {row id} {
2022 global hlview vhighlights commitrow iddrawn mainfont
2024 if {[info exists commitrow($hlview,$id)]} {
2025 if {[info exists iddrawn($id)] && ![ishighlighted $row]} {
2026 bolden $row [concat $mainfont bold]
2028 set vhighlights($row) 1
2030 set vhighlights($row) 0
2034 proc hfiles_change {name ix op} {
2035 global highlight_files filehighlight fhighlights fh_serial
2036 global mainfont highlight_paths
2038 if {[info exists filehighlight]} {
2039 # delete previous highlights
2040 catch {close $filehighlight}
2042 catch {unset fhighlights}
2044 unhighlight_filelist
2046 set highlight_paths {}
2047 after cancel do_file_hl $fh_serial
2049 if {$highlight_files ne {}} {
2050 after 300 do_file_hl $fh_serial
2054 proc makepatterns {l} {
2057 set ee [string map {"*" "\\*" "?" "\\?" "\[" "\\\[" "\\" "\\\\"} $e]
2058 if {[string index $ee end] eq "/"} {
2068 proc do_file_hl {serial} {
2069 global highlight_files filehighlight highlight_paths gdttype fhl_list
2071 if {$gdttype eq "touching paths:"} {
2072 if {[catch {set paths [shellsplit $highlight_files]}]} return
2073 set highlight_paths [makepatterns $paths]
2075 set gdtargs [concat -- $paths]
2077 set gdtargs [list "-S$highlight_files"]
2079 set cmd [concat | git diff-tree -r -s --stdin $gdtargs]
2080 set filehighlight [open $cmd r+]
2081 fconfigure $filehighlight -blocking 0
2082 filerun $filehighlight readfhighlight
2088 proc flushhighlights {} {
2089 global filehighlight fhl_list
2091 if {[info exists filehighlight]} {
2093 puts $filehighlight ""
2094 flush $filehighlight
2098 proc askfilehighlight {row id} {
2099 global filehighlight fhighlights fhl_list
2101 lappend fhl_list $id
2102 set fhighlights($row) -1
2103 puts $filehighlight $id
2106 proc readfhighlight {} {
2107 global filehighlight fhighlights commitrow curview mainfont iddrawn
2110 if {![info exists filehighlight]} {
2114 while {[incr nr] <= 100 && [gets $filehighlight line] >= 0} {
2115 set line [string trim $line]
2116 set i [lsearch -exact $fhl_list $line]
2117 if {$i < 0} continue
2118 for {set j 0} {$j < $i} {incr j} {
2119 set id [lindex $fhl_list $j]
2120 if {[info exists commitrow($curview,$id)]} {
2121 set fhighlights($commitrow($curview,$id)) 0
2124 set fhl_list [lrange $fhl_list [expr {$i+1}] end]
2125 if {$line eq {}} continue
2126 if {![info exists commitrow($curview,$line)]} continue
2127 set row $commitrow($curview,$line)
2128 if {[info exists iddrawn($line)] && ![ishighlighted $row]} {
2129 bolden $row [concat $mainfont bold]
2131 set fhighlights($row) 1
2133 if {[eof $filehighlight]} {
2135 puts "oops, git diff-tree died"
2136 catch {close $filehighlight}
2144 proc find_change {name ix op} {
2145 global nhighlights mainfont boldnamerows
2146 global findstring findpattern findtype
2148 # delete previous highlights, if any
2149 foreach row $boldnamerows {
2150 bolden_name $row $mainfont
2153 catch {unset nhighlights}
2155 if {$findtype ne "Regexp"} {
2156 set e [string map {"*" "\\*" "?" "\\?" "\[" "\\\[" "\\" "\\\\"} \
2158 set findpattern "*$e*"
2163 proc askfindhighlight {row id} {
2164 global nhighlights commitinfo iddrawn mainfont
2165 global findstring findtype findloc findpattern
2167 if {![info exists commitinfo($id)]} {
2170 set info $commitinfo($id)
2172 set fldtypes {Headline Author Date Committer CDate Comments}
2173 foreach f $info ty $fldtypes {
2174 if {$findloc ne "All fields" && $findloc ne $ty} {
2177 if {$findtype eq "Regexp"} {
2178 set doesmatch [regexp $findstring $f]
2179 } elseif {$findtype eq "IgnCase"} {
2180 set doesmatch [string match -nocase $findpattern $f]
2182 set doesmatch [string match $findpattern $f]
2185 if {$ty eq "Author"} {
2192 if {[info exists iddrawn($id)]} {
2193 if {$isbold && ![ishighlighted $row]} {
2194 bolden $row [concat $mainfont bold]
2197 bolden_name $row [concat $mainfont bold]
2200 set nhighlights($row) $isbold
2203 proc vrel_change {name ix op} {
2204 global highlight_related
2207 if {$highlight_related ne "None"} {
2212 # prepare for testing whether commits are descendents or ancestors of a
2213 proc rhighlight_sel {a} {
2214 global descendent desc_todo ancestor anc_todo
2215 global highlight_related rhighlights
2217 catch {unset descendent}
2218 set desc_todo [list $a]
2219 catch {unset ancestor}
2220 set anc_todo [list $a]
2221 if {$highlight_related ne "None"} {
2227 proc rhighlight_none {} {
2230 catch {unset rhighlights}
2234 proc is_descendent {a} {
2235 global curview children commitrow descendent desc_todo
2238 set la $commitrow($v,$a)
2242 for {set i 0} {$i < [llength $todo]} {incr i} {
2243 set do [lindex $todo $i]
2244 if {$commitrow($v,$do) < $la} {
2245 lappend leftover $do
2248 foreach nk $children($v,$do) {
2249 if {![info exists descendent($nk)]} {
2250 set descendent($nk) 1
2258 set desc_todo [concat $leftover [lrange $todo [expr {$i+1}] end]]
2262 set descendent($a) 0
2263 set desc_todo $leftover
2266 proc is_ancestor {a} {
2267 global curview parentlist commitrow ancestor anc_todo
2270 set la $commitrow($v,$a)
2274 for {set i 0} {$i < [llength $todo]} {incr i} {
2275 set do [lindex $todo $i]
2276 if {![info exists commitrow($v,$do)] || $commitrow($v,$do) > $la} {
2277 lappend leftover $do
2280 foreach np [lindex $parentlist $commitrow($v,$do)] {
2281 if {![info exists ancestor($np)]} {
2290 set anc_todo [concat $leftover [lrange $todo [expr {$i+1}] end]]
2295 set anc_todo $leftover
2298 proc askrelhighlight {row id} {
2299 global descendent highlight_related iddrawn mainfont rhighlights
2300 global selectedline ancestor
2302 if {![info exists selectedline]} return
2304 if {$highlight_related eq "Descendent" ||
2305 $highlight_related eq "Not descendent"} {
2306 if {![info exists descendent($id)]} {
2309 if {$descendent($id) == ($highlight_related eq "Descendent")} {
2312 } elseif {$highlight_related eq "Ancestor" ||
2313 $highlight_related eq "Not ancestor"} {
2314 if {![info exists ancestor($id)]} {
2317 if {$ancestor($id) == ($highlight_related eq "Ancestor")} {
2321 if {[info exists iddrawn($id)]} {
2322 if {$isbold && ![ishighlighted $row]} {
2323 bolden $row [concat $mainfont bold]
2326 set rhighlights($row) $isbold
2329 proc next_hlcont {} {
2330 global fhl_row fhl_dirn displayorder numcommits
2331 global vhighlights fhighlights nhighlights rhighlights
2332 global hlview filehighlight findstring highlight_related
2334 if {![info exists fhl_dirn] || $fhl_dirn == 0} return
2337 if {$row < 0 || $row >= $numcommits} {
2342 set id [lindex $displayorder $row]
2343 if {[info exists hlview]} {
2344 if {![info exists vhighlights($row)]} {
2345 askvhighlight $row $id
2347 if {$vhighlights($row) > 0} break
2349 if {$findstring ne {}} {
2350 if {![info exists nhighlights($row)]} {
2351 askfindhighlight $row $id
2353 if {$nhighlights($row) > 0} break
2355 if {$highlight_related ne "None"} {
2356 if {![info exists rhighlights($row)]} {
2357 askrelhighlight $row $id
2359 if {$rhighlights($row) > 0} break
2361 if {[info exists filehighlight]} {
2362 if {![info exists fhighlights($row)]} {
2363 # ask for a few more while we're at it...
2365 for {set n 0} {$n < 100} {incr n} {
2366 if {![info exists fhighlights($r)]} {
2367 askfilehighlight $r [lindex $displayorder $r]
2370 if {$r < 0 || $r >= $numcommits} break
2374 if {$fhighlights($row) < 0} {
2378 if {$fhighlights($row) > 0} break
2386 proc next_highlight {dirn} {
2387 global selectedline fhl_row fhl_dirn
2388 global hlview filehighlight findstring highlight_related
2390 if {![info exists selectedline]} return
2391 if {!([info exists hlview] || $findstring ne {} ||
2392 $highlight_related ne "None" || [info exists filehighlight])} return
2393 set fhl_row [expr {$selectedline + $dirn}]
2398 proc cancel_next_highlight {} {
2404 # Graph layout functions
2406 proc shortids {ids} {
2409 if {[llength $id] > 1} {
2410 lappend res [shortids $id]
2411 } elseif {[regexp {^[0-9a-f]{40}$} $id]} {
2412 lappend res [string range $id 0 7]
2420 proc incrange {l x o} {
2423 set e [lindex $l $x]
2425 lset l $x [expr {$e + $o}]
2434 for {} {$n > 0} {incr n -1} {
2440 proc usedinrange {id l1 l2} {
2441 global children commitrow childlist curview
2443 if {[info exists commitrow($curview,$id)]} {
2444 set r $commitrow($curview,$id)
2445 if {$l1 <= $r && $r <= $l2} {
2446 return [expr {$r - $l1 + 1}]
2448 set kids [lindex $childlist $r]
2450 set kids $children($curview,$id)
2453 set r $commitrow($curview,$c)
2454 if {$l1 <= $r && $r <= $l2} {
2455 return [expr {$r - $l1 + 1}]
2461 proc sanity {row {full 0}} {
2462 global rowidlist rowoffsets
2465 set ids [lindex $rowidlist $row]
2468 if {$id eq {}} continue
2469 if {$col < [llength $ids] - 1 &&
2470 [lsearch -exact -start [expr {$col+1}] $ids $id] >= 0} {
2471 puts "oops: [shortids $id] repeated in row $row col $col: {[shortids [lindex $rowidlist $row]]}"
2473 set o [lindex $rowoffsets $row $col]
2479 if {[lindex $rowidlist $y $x] != $id} {
2480 puts "oops: rowoffsets wrong at row [expr {$y+1}] col [expr {$x-$o}]"
2481 puts " id=[shortids $id] check started at row $row"
2482 for {set i $row} {$i >= $y} {incr i -1} {
2483 puts " row $i ids={[shortids [lindex $rowidlist $i]]} offs={[lindex $rowoffsets $i]}"
2488 set o [lindex $rowoffsets $y $x]
2493 proc makeuparrow {oid x y z} {
2494 global rowidlist rowoffsets uparrowlen idrowranges displayorder
2496 for {set i 1} {$i < $uparrowlen && $y > 1} {incr i} {
2499 set off0 [lindex $rowoffsets $y]
2500 for {set x0 $x} {1} {incr x0} {
2501 if {$x0 >= [llength $off0]} {
2502 set x0 [llength [lindex $rowoffsets [expr {$y-1}]]]
2505 set z [lindex $off0 $x0]
2511 set z [expr {$x0 - $x}]
2512 lset rowidlist $y [linsert [lindex $rowidlist $y] $x $oid]
2513 lset rowoffsets $y [linsert [lindex $rowoffsets $y] $x $z]
2515 set tmp [lreplace [lindex $rowoffsets $y] $x $x {}]
2516 lset rowoffsets $y [incrange $tmp [expr {$x+1}] -1]
2517 lappend idrowranges($oid) [lindex $displayorder $y]
2520 proc initlayout {} {
2521 global rowidlist rowoffsets displayorder commitlisted
2522 global rowlaidout rowoptim
2523 global idinlist rowchk rowrangelist idrowranges
2524 global numcommits canvxmax canv
2526 global parentlist childlist children
2527 global colormap rowtextx
2539 catch {unset idinlist}
2540 catch {unset rowchk}
2543 set canvxmax [$canv cget -width]
2544 catch {unset colormap}
2545 catch {unset rowtextx}
2546 catch {unset idrowranges}
2550 proc setcanvscroll {} {
2551 global canv canv2 canv3 numcommits linespc canvxmax canvy0
2553 set ymax [expr {$canvy0 + ($numcommits - 0.5) * $linespc + 2}]
2554 $canv conf -scrollregion [list 0 0 $canvxmax $ymax]
2555 $canv2 conf -scrollregion [list 0 0 0 $ymax]
2556 $canv3 conf -scrollregion [list 0 0 0 $ymax]
2559 proc visiblerows {} {
2560 global canv numcommits linespc
2562 set ymax [lindex [$canv cget -scrollregion] 3]
2563 if {$ymax eq {} || $ymax == 0} return
2565 set y0 [expr {int([lindex $f 0] * $ymax)}]
2566 set r0 [expr {int(($y0 - 3) / $linespc) - 1}]
2570 set y1 [expr {int([lindex $f 1] * $ymax)}]
2571 set r1 [expr {int(($y1 - 3) / $linespc) + 1}]
2572 if {$r1 >= $numcommits} {
2573 set r1 [expr {$numcommits - 1}]
2575 return [list $r0 $r1]
2578 proc layoutmore {tmax allread} {
2579 global rowlaidout rowoptim commitidx numcommits optim_delay
2580 global uparrowlen curview rowidlist idinlist
2583 set showdelay $optim_delay
2584 set optdelay [expr {$uparrowlen + 1}]
2586 if {$rowoptim - $showdelay > $numcommits} {
2587 showstuff [expr {$rowoptim - $showdelay}] $showlast
2588 } elseif {$rowlaidout - $optdelay > $rowoptim} {
2589 set nr [expr {$rowlaidout - $optdelay - $rowoptim}]
2593 optimize_rows $rowoptim 0 [expr {$rowoptim + $nr}]
2595 } elseif {$commitidx($curview) > $rowlaidout} {
2596 set nr [expr {$commitidx($curview) - $rowlaidout}]
2597 # may need to increase this threshold if uparrowlen or
2598 # mingaplen are increased...
2603 set rowlaidout [layoutrows $row [expr {$row + $nr}] $allread]
2604 if {$rowlaidout == $row} {
2607 } elseif {$allread} {
2609 set nrows $commitidx($curview)
2610 if {[lindex $rowidlist $nrows] ne {} ||
2611 [array names idinlist] ne {}} {
2613 set rowlaidout $commitidx($curview)
2614 } elseif {$rowoptim == $nrows} {
2617 if {$numcommits == $nrows} {
2624 if {$tmax ne {} && [clock clicks -milliseconds] >= $tmax} {
2630 proc showstuff {canshow last} {
2631 global numcommits commitrow pending_select selectedline curview
2632 global lookingforhead mainheadid displayorder nullid selectfirst
2634 if {$numcommits == 0} {
2636 set phase "incrdraw"
2640 set numcommits $canshow
2642 set rows [visiblerows]
2643 set r1 [lindex $rows 1]
2644 if {$r1 >= $canshow} {
2645 set r1 [expr {$canshow - 1}]
2650 if {[info exists pending_select] &&
2651 [info exists commitrow($curview,$pending_select)] &&
2652 $commitrow($curview,$pending_select) < $numcommits} {
2653 selectline $commitrow($curview,$pending_select) 1
2656 if {[info exists selectedline] || [info exists pending_select]} {
2659 set l [expr {[lindex $displayorder 0] eq $nullid}]
2664 if {$lookingforhead && [info exists commitrow($curview,$mainheadid)]
2665 && ($last || $commitrow($curview,$mainheadid) < $numcommits - 1)} {
2666 set lookingforhead 0
2671 proc doshowlocalchanges {} {
2672 global lookingforhead curview mainheadid phase commitrow
2674 if {[info exists commitrow($curview,$mainheadid)] &&
2675 ($phase eq {} || $commitrow($curview,$mainheadid) < $numcommits - 1)} {
2677 } elseif {$phase ne {}} {
2678 set lookingforhead 1
2682 proc dohidelocalchanges {} {
2683 global lookingforhead localrow lserial
2685 set lookingforhead 0
2686 if {$localrow >= 0} {
2693 # spawn off a process to do git diff-index HEAD
2694 proc dodiffindex {} {
2695 global localrow lserial
2699 set fd [open "|git diff-index HEAD" r]
2700 fconfigure $fd -blocking 0
2701 filerun $fd [list readdiffindex $fd $lserial]
2704 proc readdiffindex {fd serial} {
2705 global localrow commitrow mainheadid nullid curview
2706 global commitinfo commitdata lserial
2708 if {[gets $fd line] < 0} {
2715 # we only need to see one line and we don't really care what it says...
2718 if {$serial == $lserial && $localrow == -1} {
2719 # add the line for the local diff to the graph
2720 set localrow $commitrow($curview,$mainheadid)
2721 set hl "Local uncommitted changes"
2722 set commitinfo($nullid) [list $hl {} {} {} {} " $hl\n"]
2723 set commitdata($nullid) "\n $hl\n"
2724 insertrow $localrow $nullid
2729 proc layoutrows {row endrow last} {
2730 global rowidlist rowoffsets displayorder
2731 global uparrowlen downarrowlen maxwidth mingaplen
2732 global childlist parentlist
2734 global commitidx curview
2735 global idinlist rowchk rowrangelist
2737 set idlist [lindex $rowidlist $row]
2738 set offs [lindex $rowoffsets $row]
2739 while {$row < $endrow} {
2740 set id [lindex $displayorder $row]
2743 foreach p [lindex $parentlist $row] {
2744 if {![info exists idinlist($p)]} {
2746 } elseif {!$idinlist($p)} {
2750 set nev [expr {[llength $idlist] + [llength $newolds]
2751 + [llength $oldolds] - $maxwidth + 1}]
2754 $row + $uparrowlen + $mingaplen >= $commitidx($curview)} break
2755 for {set x [llength $idlist]} {[incr x -1] >= 0} {} {
2756 set i [lindex $idlist $x]
2757 if {![info exists rowchk($i)] || $row >= $rowchk($i)} {
2758 set r [usedinrange $i [expr {$row - $downarrowlen}] \
2759 [expr {$row + $uparrowlen + $mingaplen}]]
2761 set idlist [lreplace $idlist $x $x]
2762 set offs [lreplace $offs $x $x]
2763 set offs [incrange $offs $x 1]
2765 set rm1 [expr {$row - 1}]
2766 lappend idrowranges($i) [lindex $displayorder $rm1]
2767 if {[incr nev -1] <= 0} break
2770 set rowchk($id) [expr {$row + $r}]
2773 lset rowidlist $row $idlist
2774 lset rowoffsets $row $offs
2776 set col [lsearch -exact $idlist $id]
2778 set col [llength $idlist]
2780 lset rowidlist $row $idlist
2782 if {[lindex $childlist $row] ne {}} {
2783 set z [expr {[llength [lindex $rowidlist [expr {$row-1}]]] - $col}]
2787 lset rowoffsets $row $offs
2789 makeuparrow $id $col $row $z
2795 if {[info exists idrowranges($id)]} {
2796 set ranges $idrowranges($id)
2798 unset idrowranges($id)
2800 lappend rowrangelist $ranges
2802 set offs [ntimes [llength $idlist] 0]
2803 set l [llength $newolds]
2804 set idlist [eval lreplace \$idlist $col $col $newolds]
2807 set offs [lrange $offs 0 [expr {$col - 1}]]
2808 foreach x $newolds {
2813 set tmp [expr {[llength $idlist] - [llength $offs]}]
2815 set offs [concat $offs [ntimes $tmp $o]]
2820 foreach i $newolds {
2822 set idrowranges($i) $id
2825 foreach oid $oldolds {
2826 set idinlist($oid) 1
2827 set idlist [linsert $idlist $col $oid]
2828 set offs [linsert $offs $col $o]
2829 makeuparrow $oid $col $row $o
2832 lappend rowidlist $idlist
2833 lappend rowoffsets $offs
2838 proc addextraid {id row} {
2839 global displayorder commitrow commitinfo
2840 global commitidx commitlisted
2841 global parentlist childlist children curview
2843 incr commitidx($curview)
2844 lappend displayorder $id
2845 lappend commitlisted 0
2846 lappend parentlist {}
2847 set commitrow($curview,$id) $row
2849 if {![info exists commitinfo($id)]} {
2850 set commitinfo($id) {"No commit information available"}
2852 if {![info exists children($curview,$id)]} {
2853 set children($curview,$id) {}
2855 lappend childlist $children($curview,$id)
2858 proc layouttail {} {
2859 global rowidlist rowoffsets idinlist commitidx curview
2860 global idrowranges rowrangelist
2862 set row $commitidx($curview)
2863 set idlist [lindex $rowidlist $row]
2864 while {$idlist ne {}} {
2865 set col [expr {[llength $idlist] - 1}]
2866 set id [lindex $idlist $col]
2869 lappend idrowranges($id) $row
2870 lappend rowrangelist $idrowranges($id)
2871 unset idrowranges($id)
2873 set offs [ntimes $col 0]
2874 set idlist [lreplace $idlist $col $col]
2875 lappend rowidlist $idlist
2876 lappend rowoffsets $offs
2879 foreach id [array names idinlist] {
2882 lset rowidlist $row [list $id]
2883 lset rowoffsets $row 0
2884 makeuparrow $id 0 $row 0
2885 lappend idrowranges($id) $row
2886 lappend rowrangelist $idrowranges($id)
2887 unset idrowranges($id)
2889 lappend rowidlist {}
2890 lappend rowoffsets {}
2894 proc insert_pad {row col npad} {
2895 global rowidlist rowoffsets
2897 set pad [ntimes $npad {}]
2898 lset rowidlist $row [eval linsert [list [lindex $rowidlist $row]] $col $pad]
2899 set tmp [eval linsert [list [lindex $rowoffsets $row]] $col $pad]
2900 lset rowoffsets $row [incrange $tmp [expr {$col + $npad}] [expr {-$npad}]]
2903 proc optimize_rows {row col endrow} {
2904 global rowidlist rowoffsets displayorder
2906 for {} {$row < $endrow} {incr row} {
2907 set idlist [lindex $rowidlist $row]
2908 set offs [lindex $rowoffsets $row]
2910 for {} {$col < [llength $offs]} {incr col} {
2911 if {[lindex $idlist $col] eq {}} {
2915 set z [lindex $offs $col]
2916 if {$z eq {}} continue
2918 set x0 [expr {$col + $z}]
2919 set y0 [expr {$row - 1}]
2920 set z0 [lindex $rowoffsets $y0 $x0]
2922 set id [lindex $idlist $col]
2923 set ranges [rowranges $id]
2924 if {$ranges ne {} && $y0 > [lindex $ranges 0]} {
2928 # Looking at lines from this row to the previous row,
2929 # make them go straight up if they end in an arrow on
2930 # the previous row; otherwise make them go straight up
2932 if {$z < -1 || ($z < 0 && $isarrow)} {
2933 # Line currently goes left too much;
2934 # insert pads in the previous row, then optimize it
2935 set npad [expr {-1 - $z + $isarrow}]
2936 set offs [incrange $offs $col $npad]
2937 insert_pad $y0 $x0 $npad
2939 optimize_rows $y0 $x0 $row
2941 set z [lindex $offs $col]
2942 set x0 [expr {$col + $z}]
2943 set z0 [lindex $rowoffsets $y0 $x0]
2944 } elseif {$z > 1 || ($z > 0 && $isarrow)} {
2945 # Line currently goes right too much;
2946 # insert pads in this line and adjust the next's rowoffsets
2947 set npad [expr {$z - 1 + $isarrow}]
2948 set y1 [expr {$row + 1}]
2949 set offs2 [lindex $rowoffsets $y1]
2953 if {$z eq {} || $x1 + $z < $col} continue
2954 if {$x1 + $z > $col} {
2957 lset rowoffsets $y1 [incrange $offs2 $x1 $npad]
2960 set pad [ntimes $npad {}]
2961 set idlist [eval linsert \$idlist $col $pad]
2962 set tmp [eval linsert \$offs $col $pad]
2964 set offs [incrange $tmp $col [expr {-$npad}]]
2965 set z [lindex $offs $col]
2968 if {$z0 eq {} && !$isarrow} {
2969 # this line links to its first child on row $row-2
2970 set rm2 [expr {$row - 2}]
2971 set id [lindex $displayorder $rm2]
2972 set xc [lsearch -exact [lindex $rowidlist $rm2] $id]
2974 set z0 [expr {$xc - $x0}]
2977 # avoid lines jigging left then immediately right
2978 if {$z0 ne {} && $z < 0 && $z0 > 0} {
2979 insert_pad $y0 $x0 1
2980 set offs [incrange $offs $col 1]
2981 optimize_rows $y0 [expr {$x0 + 1}] $row
2986 # Find the first column that doesn't have a line going right
2987 for {set col [llength $idlist]} {[incr col -1] >= 0} {} {
2988 set o [lindex $offs $col]
2990 # check if this is the link to the first child
2991 set id [lindex $idlist $col]
2992 set ranges [rowranges $id]
2993 if {$ranges ne {} && $row == [lindex $ranges 0]} {
2994 # it is, work out offset to child
2995 set y0 [expr {$row - 1}]
2996 set id [lindex $displayorder $y0]
2997 set x0 [lsearch -exact [lindex $rowidlist $y0] $id]
2999 set o [expr {$x0 - $col}]
3003 if {$o eq {} || $o <= 0} break
3005 # Insert a pad at that column as long as it has a line and
3006 # isn't the last column, and adjust the next row' offsets
3007 if {$o ne {} && [incr col] < [llength $idlist]} {
3008 set y1 [expr {$row + 1}]
3009 set offs2 [lindex $rowoffsets $y1]
3013 if {$z eq {} || $x1 + $z < $col} continue
3014 lset rowoffsets $y1 [incrange $offs2 $x1 1]
3017 set idlist [linsert $idlist $col {}]
3018 set tmp [linsert $offs $col {}]
3020 set offs [incrange $tmp $col -1]
3023 lset rowidlist $row $idlist
3024 lset rowoffsets $row $offs
3030 global canvx0 linespc
3031 return [expr {$canvx0 + $col * $linespc}]
3035 global canvy0 linespc
3036 return [expr {$canvy0 + $row * $linespc}]
3039 proc linewidth {id} {
3040 global thickerline lthickness
3043 if {[info exists thickerline] && $id eq $thickerline} {
3044 set wid [expr {2 * $lthickness}]
3049 proc rowranges {id} {
3050 global phase idrowranges commitrow rowlaidout rowrangelist curview
3054 ([info exists commitrow($curview,$id)]
3055 && $commitrow($curview,$id) < $rowlaidout)} {
3056 set ranges [lindex $rowrangelist $commitrow($curview,$id)]
3057 } elseif {[info exists idrowranges($id)]} {
3058 set ranges $idrowranges($id)
3061 foreach rid $ranges {
3062 lappend linenos $commitrow($curview,$rid)
3064 if {$linenos ne {}} {
3065 lset linenos 0 [expr {[lindex $linenos 0] + 1}]
3070 # work around tk8.4 refusal to draw arrows on diagonal segments
3071 proc adjarrowhigh {coords} {
3074 set x0 [lindex $coords 0]
3075 set x1 [lindex $coords 2]
3077 set y0 [lindex $coords 1]
3078 set y1 [lindex $coords 3]
3079 if {$y0 - $y1 <= 2 * $linespc && $x1 == [lindex $coords 4]} {
3080 # we have a nearby vertical segment, just trim off the diag bit
3081 set coords [lrange $coords 2 end]
3083 set slope [expr {($x0 - $x1) / ($y0 - $y1)}]
3084 set xi [expr {$x0 - $slope * $linespc / 2}]
3085 set yi [expr {$y0 - $linespc / 2}]
3086 set coords [lreplace $coords 0 1 $xi $y0 $xi $yi]
3092 proc drawlineseg {id row endrow arrowlow} {
3093 global rowidlist displayorder iddrawn linesegs
3094 global canv colormap linespc curview maxlinelen
3096 set cols [list [lsearch -exact [lindex $rowidlist $row] $id]]
3097 set le [expr {$row + 1}]
3100 set c [lsearch -exact [lindex $rowidlist $le] $id]
3106 set x [lindex $displayorder $le]
3111 if {[info exists iddrawn($x)] || $le == $endrow} {
3112 set c [lsearch -exact [lindex $rowidlist [expr {$le+1}]] $id]
3128 if {[info exists linesegs($id)]} {
3129 set lines $linesegs($id)
3131 set r0 [lindex $li 0]
3133 if {$r0 == $le && [lindex $li 1] - $row <= $maxlinelen} {
3143 set li [lindex $lines [expr {$i-1}]]
3144 set r1 [lindex $li 1]
3145 if {$r1 == $row && $le - [lindex $li 0] <= $maxlinelen} {
3150 set x [lindex $cols [expr {$le - $row}]]
3151 set xp [lindex $cols [expr {$le - 1 - $row}]]
3152 set dir [expr {$xp - $x}]
3154 set ith [lindex $lines $i 2]
3155 set coords [$canv coords $ith]
3156 set ah [$canv itemcget $ith -arrow]
3157 set arrowhigh [expr {$ah eq "first" || $ah eq "both"}]
3158 set x2 [lindex $cols [expr {$le + 1 - $row}]]
3159 if {$x2 ne {} && $x - $x2 == $dir} {
3160 set coords [lrange $coords 0 end-2]
3163 set coords [list [xc $le $x] [yc $le]]
3166 set itl [lindex $lines [expr {$i-1}] 2]
3167 set al [$canv itemcget $itl -arrow]
3168 set arrowlow [expr {$al eq "last" || $al eq "both"}]
3169 } elseif {$arrowlow &&
3170 [lsearch -exact [lindex $rowidlist [expr {$row-1}]] $id] >= 0} {
3173 set arrow [lindex {none first last both} [expr {$arrowhigh + 2*$arrowlow}]]
3174 for {set y $le} {[incr y -1] > $row} {} {
3176 set xp [lindex $cols [expr {$y - 1 - $row}]]
3177 set ndir [expr {$xp - $x}]
3178 if {$dir != $ndir || $xp < 0} {
3179 lappend coords [xc $y $x] [yc $y]
3185 # join parent line to first child
3186 set ch [lindex $displayorder $row]
3187 set xc [lsearch -exact [lindex $rowidlist $row] $ch]
3189 puts "oops: drawlineseg: child $ch not on row $row"
3192 lappend coords [xc $row [expr {$x-1}]] [yc $row]
3193 } elseif {$xc > $x + 1} {
3194 lappend coords [xc $row [expr {$x+1}]] [yc $row]
3198 lappend coords [xc $row $x] [yc $row]
3200 set xn [xc $row $xp]
3202 # work around tk8.4 refusal to draw arrows on diagonal segments
3203 if {$arrowlow && $xn != [lindex $coords end-1]} {
3204 if {[llength $coords] < 4 ||
3205 [lindex $coords end-3] != [lindex $coords end-1] ||
3206 [lindex $coords end] - $yn > 2 * $linespc} {
3207 set xn [xc $row [expr {$xp - 0.5 * $dir}]]
3208 set yo [yc [expr {$row + 0.5}]]
3209 lappend coords $xn $yo $xn $yn
3212 lappend coords $xn $yn
3217 set coords [adjarrowhigh $coords]
3220 set t [$canv create line $coords -width [linewidth $id] \
3221 -fill $colormap($id) -tags lines.$id -arrow $arrow]
3224 set lines [linsert $lines $i [list $row $le $t]]
3226 $canv coords $ith $coords
3227 if {$arrow ne $ah} {
3228 $canv itemconf $ith -arrow $arrow
3230 lset lines $i 0 $row
3233 set xo [lsearch -exact [lindex $rowidlist [expr {$row - 1}]] $id]
3234 set ndir [expr {$xo - $xp}]
3235 set clow [$canv coords $itl]
3236 if {$dir == $ndir} {
3237 set clow [lrange $clow 2 end]
3239 set coords [concat $coords $clow]
3241 lset lines [expr {$i-1}] 1 $le
3243 set coords [adjarrowhigh $coords]
3246 # coalesce two pieces
3248 set b [lindex $lines [expr {$i-1}] 0]
3249 set e [lindex $lines $i 1]
3250 set lines [lreplace $lines [expr {$i-1}] $i [list $b $e $itl]]
3252 $canv coords $itl $coords
3253 if {$arrow ne $al} {
3254 $canv itemconf $itl -arrow $arrow
3258 set linesegs($id) $lines
3262 proc drawparentlinks {id row} {
3263 global rowidlist canv colormap curview parentlist
3266 set rowids [lindex $rowidlist $row]
3267 set col [lsearch -exact $rowids $id]
3268 if {$col < 0} return
3269 set olds [lindex $parentlist $row]
3270 set row2 [expr {$row + 1}]
3271 set x [xc $row $col]
3274 set ids [lindex $rowidlist $row2]
3275 # rmx = right-most X coord used
3278 set i [lsearch -exact $ids $p]
3280 puts "oops, parent $p of $id not in list"
3283 set x2 [xc $row2 $i]
3287 if {[lsearch -exact $rowids $p] < 0} {
3288 # drawlineseg will do this one for us
3292 # should handle duplicated parents here...
3293 set coords [list $x $y]
3294 if {$i < $col - 1} {
3295 lappend coords [xc $row [expr {$i + 1}]] $y
3296 } elseif {$i > $col + 1} {
3297 lappend coords [xc $row [expr {$i - 1}]] $y
3299 lappend coords $x2 $y2
3300 set t [$canv create line $coords -width [linewidth $p] \
3301 -fill $colormap($p) -tags lines.$p]
3305 if {$rmx > [lindex $idpos($id) 1]} {
3306 lset idpos($id) 1 $rmx
3311 proc drawlines {id} {
3314 $canv itemconf lines.$id -width [linewidth $id]
3317 proc drawcmittext {id row col} {
3318 global linespc canv canv2 canv3 canvy0 fgcolor
3319 global commitlisted commitinfo rowidlist parentlist
3320 global rowtextx idpos idtags idheads idotherrefs
3321 global linehtag linentag linedtag
3322 global mainfont canvxmax boldrows boldnamerows fgcolor nullid
3324 if {$id eq $nullid} {
3327 set ofill [expr {[lindex $commitlisted $row]? "blue": "white"}]
3329 set x [xc $row $col]
3331 set orad [expr {$linespc / 3}]
3332 set t [$canv create oval [expr {$x - $orad}] [expr {$y - $orad}] \
3333 [expr {$x + $orad - 1}] [expr {$y + $orad - 1}] \
3334 -fill $ofill -outline $fgcolor -width 1 -tags circle]
3336 $canv bind $t <1> {selcanvline {} %x %y}
3337 set rmx [llength [lindex $rowidlist $row]]
3338 set olds [lindex $parentlist $row]
3340 set nextids [lindex $rowidlist [expr {$row + 1}]]
3342 set i [lsearch -exact $nextids $p]
3348 set xt [xc $row $rmx]
3349 set rowtextx($row) $xt
3350 set idpos($id) [list $x $xt $y]
3351 if {[info exists idtags($id)] || [info exists idheads($id)]
3352 || [info exists idotherrefs($id)]} {
3353 set xt [drawtags $id $x $xt $y]
3355 set headline [lindex $commitinfo($id) 0]
3356 set name [lindex $commitinfo($id) 1]
3357 set date [lindex $commitinfo($id) 2]
3358 set date [formatdate $date]
3361 set isbold [ishighlighted $row]
3363 lappend boldrows $row
3366 lappend boldnamerows $row
3370 set linehtag($row) [$canv create text $xt $y -anchor w -fill $fgcolor \
3371 -text $headline -font $font -tags text]
3372 $canv bind $linehtag($row) <Button-3> "rowmenu %X %Y $id"
3373 set linentag($row) [$canv2 create text 3 $y -anchor w -fill $fgcolor \
3374 -text $name -font $nfont -tags text]
3375 set linedtag($row) [$canv3 create text 3 $y -anchor w -fill $fgcolor \
3376 -text $date -font $mainfont -tags text]
3377 set xr [expr {$xt + [font measure $mainfont $headline]}]
3378 if {$xr > $canvxmax} {
3384 proc drawcmitrow {row} {
3385 global displayorder rowidlist
3387 global commitinfo parentlist numcommits
3388 global filehighlight fhighlights findstring nhighlights
3389 global hlview vhighlights
3390 global highlight_related rhighlights
3392 if {$row >= $numcommits} return
3394 set id [lindex $displayorder $row]
3395 if {[info exists hlview] && ![info exists vhighlights($row)]} {
3396 askvhighlight $row $id
3398 if {[info exists filehighlight] && ![info exists fhighlights($row)]} {
3399 askfilehighlight $row $id
3401 if {$findstring ne {} && ![info exists nhighlights($row)]} {
3402 askfindhighlight $row $id
3404 if {$highlight_related ne "None" && ![info exists rhighlights($row)]} {
3405 askrelhighlight $row $id
3407 if {[info exists iddrawn($id)]} return
3408 set col [lsearch -exact [lindex $rowidlist $row] $id]
3410 puts "oops, row $row id $id not in list"
3413 if {![info exists commitinfo($id)]} {
3417 drawcmittext $id $row $col
3421 proc drawcommits {row {endrow {}}} {
3422 global numcommits iddrawn displayorder curview
3423 global parentlist rowidlist
3428 if {$endrow eq {}} {
3431 if {$endrow >= $numcommits} {
3432 set endrow [expr {$numcommits - 1}]
3435 # make the lines join to already-drawn rows either side
3436 set r [expr {$row - 1}]
3437 if {$r < 0 || ![info exists iddrawn([lindex $displayorder $r])]} {
3440 set er [expr {$endrow + 1}]
3441 if {$er >= $numcommits ||
3442 ![info exists iddrawn([lindex $displayorder $er])]} {
3445 for {} {$r <= $er} {incr r} {
3446 set id [lindex $displayorder $r]
3447 set wasdrawn [info exists iddrawn($id)]
3451 if {$r == $er} break
3452 set nextid [lindex $displayorder [expr {$r + 1}]]
3453 if {$wasdrawn && [info exists iddrawn($nextid)]} {
3454 catch {unset prevlines}
3457 drawparentlinks $id $r
3459 if {[info exists lineends($r)]} {
3460 foreach lid $lineends($r) {
3461 unset prevlines($lid)
3464 set rowids [lindex $rowidlist $r]
3465 foreach lid $rowids {
3466 if {$lid eq {}} continue
3468 # see if this is the first child of any of its parents
3469 foreach p [lindex $parentlist $r] {
3470 if {[lsearch -exact $rowids $p] < 0} {
3471 # make this line extend up to the child
3472 set le [drawlineseg $p $r $er 0]
3473 lappend lineends($le) $p
3477 } elseif {![info exists prevlines($lid)]} {
3478 set le [drawlineseg $lid $r $er 1]
3479 lappend lineends($le) $lid
3480 set prevlines($lid) 1
3486 proc drawfrac {f0 f1} {
3489 set ymax [lindex [$canv cget -scrollregion] 3]
3490 if {$ymax eq {} || $ymax == 0} return
3491 set y0 [expr {int($f0 * $ymax)}]
3492 set row [expr {int(($y0 - 3) / $linespc) - 1}]
3493 set y1 [expr {int($f1 * $ymax)}]
3494 set endrow [expr {int(($y1 - 3) / $linespc) + 1}]
3495 drawcommits $row $endrow
3498 proc drawvisible {} {
3500 eval drawfrac [$canv yview]
3503 proc clear_display {} {
3504 global iddrawn linesegs
3505 global vhighlights fhighlights nhighlights rhighlights
3508 catch {unset iddrawn}
3509 catch {unset linesegs}
3510 catch {unset vhighlights}
3511 catch {unset fhighlights}
3512 catch {unset nhighlights}
3513 catch {unset rhighlights}
3516 proc findcrossings {id} {
3517 global rowidlist parentlist numcommits rowoffsets displayorder
3521 foreach {s e} [rowranges $id] {
3522 if {$e >= $numcommits} {
3523 set e [expr {$numcommits - 1}]
3525 if {$e <= $s} continue
3526 set x [lsearch -exact [lindex $rowidlist $e] $id]
3528 puts "findcrossings: oops, no [shortids $id] in row $e"
3531 for {set row $e} {[incr row -1] >= $s} {} {
3532 set olds [lindex $parentlist $row]
3533 set kid [lindex $displayorder $row]
3534 set kidx [lsearch -exact [lindex $rowidlist $row] $kid]
3535 if {$kidx < 0} continue
3536 set nextrow [lindex $rowidlist [expr {$row + 1}]]
3538 set px [lsearch -exact $nextrow $p]
3539 if {$px < 0} continue
3540 if {($kidx < $x && $x < $px) || ($px < $x && $x < $kidx)} {
3541 if {[lsearch -exact $ccross $p] >= 0} continue
3542 if {$x == $px + ($kidx < $px? -1: 1)} {
3544 } elseif {[lsearch -exact $cross $p] < 0} {
3549 set inc [lindex $rowoffsets $row $x]
3550 if {$inc eq {}} break
3554 return [concat $ccross {{}} $cross]
3557 proc assigncolor {id} {
3558 global colormap colors nextcolor
3559 global commitrow parentlist children children curview
3561 if {[info exists colormap($id)]} return
3562 set ncolors [llength $colors]
3563 if {[info exists children($curview,$id)]} {
3564 set kids $children($curview,$id)
3568 if {[llength $kids] == 1} {
3569 set child [lindex $kids 0]
3570 if {[info exists colormap($child)]
3571 && [llength [lindex $parentlist $commitrow($curview,$child)]] == 1} {
3572 set colormap($id) $colormap($child)
3578 foreach x [findcrossings $id] {
3580 # delimiter between corner crossings and other crossings
3581 if {[llength $badcolors] >= $ncolors - 1} break
3582 set origbad $badcolors
3584 if {[info exists colormap($x)]
3585 && [lsearch -exact $badcolors $colormap($x)] < 0} {
3586 lappend badcolors $colormap($x)
3589 if {[llength $badcolors] >= $ncolors} {
3590 set badcolors $origbad
3592 set origbad $badcolors
3593 if {[llength $badcolors] < $ncolors - 1} {
3594 foreach child $kids {
3595 if {[info exists colormap($child)]
3596 && [lsearch -exact $badcolors $colormap($child)] < 0} {
3597 lappend badcolors $colormap($child)
3599 foreach p [lindex $parentlist $commitrow($curview,$child)] {
3600 if {[info exists colormap($p)]
3601 && [lsearch -exact $badcolors $colormap($p)] < 0} {
3602 lappend badcolors $colormap($p)
3606 if {[llength $badcolors] >= $ncolors} {
3607 set badcolors $origbad
3610 for {set i 0} {$i <= $ncolors} {incr i} {
3611 set c [lindex $colors $nextcolor]
3612 if {[incr nextcolor] >= $ncolors} {
3615 if {[lsearch -exact $badcolors $c]} break
3617 set colormap($id) $c
3620 proc bindline {t id} {
3623 $canv bind $t <Enter> "lineenter %x %y $id"
3624 $canv bind $t <Motion> "linemotion %x %y $id"
3625 $canv bind $t <Leave> "lineleave $id"
3626 $canv bind $t <Button-1> "lineclick %x %y $id 1"
3629 proc drawtags {id x xt y1} {
3630 global idtags idheads idotherrefs mainhead
3631 global linespc lthickness
3632 global canv mainfont commitrow rowtextx curview fgcolor bgcolor
3637 if {[info exists idtags($id)]} {
3638 set marks $idtags($id)
3639 set ntags [llength $marks]
3641 if {[info exists idheads($id)]} {
3642 set marks [concat $marks $idheads($id)]
3643 set nheads [llength $idheads($id)]
3645 if {[info exists idotherrefs($id)]} {
3646 set marks [concat $marks $idotherrefs($id)]
3652 set delta [expr {int(0.5 * ($linespc - $lthickness))}]
3653 set yt [expr {$y1 - 0.5 * $linespc}]
3654 set yb [expr {$yt + $linespc - 1}]
3658 foreach tag $marks {
3660 if {$i >= $ntags && $i < $ntags + $nheads && $tag eq $mainhead} {
3661 set wid [font measure [concat $mainfont bold] $tag]
3663 set wid [font measure $mainfont $tag]
3667 set xt [expr {$xt + $delta + $wid + $lthickness + $linespc}]
3669 set t [$canv create line $x $y1 [lindex $xvals end] $y1 \
3670 -width $lthickness -fill black -tags tag.$id]
3672 foreach tag $marks x $xvals wid $wvals {
3673 set xl [expr {$x + $delta}]
3674 set xr [expr {$x + $delta + $wid + $lthickness}]
3676 if {[incr ntags -1] >= 0} {
3678 set t [$canv create polygon $x [expr {$yt + $delta}] $xl $yt \
3679 $xr $yt $xr $yb $xl $yb $x [expr {$yb - $delta}] \
3680 -width 1 -outline black -fill yellow -tags tag.$id]
3681 $canv bind $t <1> [list showtag $tag 1]
3682 set rowtextx($commitrow($curview,$id)) [expr {$xr + $linespc}]
3684 # draw a head or other ref
3685 if {[incr nheads -1] >= 0} {
3687 if {$tag eq $mainhead} {
3693 set xl [expr {$xl - $delta/2}]
3694 $canv create polygon $x $yt $xr $yt $xr $yb $x $yb \
3695 -width 1 -outline black -fill $col -tags tag.$id
3696 if {[regexp {^(remotes/.*/|remotes/)} $tag match remoteprefix]} {
3697 set rwid [font measure $mainfont $remoteprefix]
3698 set xi [expr {$x + 1}]
3699 set yti [expr {$yt + 1}]
3700 set xri [expr {$x + $rwid}]
3701 $canv create polygon $xi $yti $xri $yti $xri $yb $xi $yb \
3702 -width 0 -fill "#ffddaa" -tags tag.$id
3705 set t [$canv create text $xl $y1 -anchor w -text $tag -fill $fgcolor \
3706 -font $font -tags [list tag.$id text]]
3708 $canv bind $t <1> [list showtag $tag 1]
3709 } elseif {$nheads >= 0} {
3710 $canv bind $t <Button-3> [list headmenu %X %Y $id $tag]
3716 proc xcoord {i level ln} {
3717 global canvx0 xspc1 xspc2
3719 set x [expr {$canvx0 + $i * $xspc1($ln)}]
3720 if {$i > 0 && $i == $level} {
3721 set x [expr {$x + 0.5 * ($xspc2 - $xspc1($ln))}]
3722 } elseif {$i > $level} {
3723 set x [expr {$x + $xspc2 - $xspc1($ln)}]
3728 proc show_status {msg} {
3729 global canv mainfont fgcolor
3732 $canv create text 3 3 -anchor nw -text $msg -font $mainfont \
3733 -tags text -fill $fgcolor
3736 # Insert a new commit as the child of the commit on row $row.
3737 # The new commit will be displayed on row $row and the commits
3738 # on that row and below will move down one row.
3739 proc insertrow {row newcmit} {
3740 global displayorder parentlist childlist commitlisted children
3741 global commitrow curview rowidlist rowoffsets numcommits
3742 global rowrangelist rowlaidout rowoptim numcommits
3743 global selectedline rowchk commitidx
3745 if {$row >= $numcommits} {
3746 puts "oops, inserting new row $row but only have $numcommits rows"
3749 set p [lindex $displayorder $row]
3750 set displayorder [linsert $displayorder $row $newcmit]
3751 set parentlist [linsert $parentlist $row $p]
3752 set kids [lindex $childlist $row]
3753 lappend kids $newcmit
3754 lset childlist $row $kids
3755 set childlist [linsert $childlist $row {}]
3756 set children($curview,$p) $kids
3757 set commitlisted [linsert $commitlisted $row 1]
3758 set l [llength $displayorder]
3759 for {set r $row} {$r < $l} {incr r} {
3760 set id [lindex $displayorder $r]
3761 set commitrow($curview,$id) $r
3763 incr commitidx($curview)
3765 set idlist [lindex $rowidlist $row]
3766 set offs [lindex $rowoffsets $row]
3769 if {$x eq {} || ($x eq $p && [llength $kids] == 1)} {
3775 if {[llength $kids] == 1} {
3776 set col [lsearch -exact $idlist $p]
3777 lset idlist $col $newcmit
3779 set col [llength $idlist]
3780 lappend idlist $newcmit
3782 lset rowoffsets $row $offs
3784 set rowidlist [linsert $rowidlist $row $idlist]
3785 set rowoffsets [linsert $rowoffsets [expr {$row+1}] $newoffs]
3787 set rowrangelist [linsert $rowrangelist $row {}]
3788 if {[llength $kids] > 1} {
3789 set rp1 [expr {$row + 1}]
3790 set ranges [lindex $rowrangelist $rp1]
3791 if {$ranges eq {}} {
3792 set ranges [list $newcmit $p]
3793 } elseif {[lindex $ranges end-1] eq $p} {
3794 lset ranges end-1 $newcmit
3796 lset rowrangelist $rp1 $ranges
3799 catch {unset rowchk}
3805 if {[info exists selectedline] && $selectedline >= $row} {
3811 # Remove a commit that was inserted with insertrow on row $row.
3812 proc removerow {row} {
3813 global displayorder parentlist childlist commitlisted children
3814 global commitrow curview rowidlist rowoffsets numcommits
3815 global rowrangelist idrowranges rowlaidout rowoptim numcommits
3816 global linesegends selectedline rowchk commitidx
3818 if {$row >= $numcommits} {
3819 puts "oops, removing row $row but only have $numcommits rows"
3822 set rp1 [expr {$row + 1}]
3823 set id [lindex $displayorder $row]
3824 set p [lindex $parentlist $row]
3825 set displayorder [lreplace $displayorder $row $row]
3826 set parentlist [lreplace $parentlist $row $row]
3827 set childlist [lreplace $childlist $row $row]
3828 set commitlisted [lreplace $commitlisted $row $row]
3829 set kids [lindex $childlist $row]
3830 set i [lsearch -exact $kids $id]
3832 set kids [lreplace $kids $i $i]
3833 lset childlist $row $kids
3834 set children($curview,$p) $kids
3836 set l [llength $displayorder]
3837 for {set r $row} {$r < $l} {incr r} {
3838 set id [lindex $displayorder $r]
3839 set commitrow($curview,$id) $r
3841 incr commitidx($curview) -1
3843 set rowidlist [lreplace $rowidlist $row $row]
3844 set rowoffsets [lreplace $rowoffsets $rp1 $rp1]
3846 set offs [lindex $rowoffsets $row]
3847 set offs [lreplace $offs end end]
3848 lset rowoffsets $row $offs
3851 set rowrangelist [lreplace $rowrangelist $row $row]
3852 if {[llength $kids] > 0} {
3853 set ranges [lindex $rowrangelist $row]
3854 if {[lindex $ranges end-1] eq $id} {
3855 set ranges [lreplace $ranges end-1 end]
3856 lset rowrangelist $row $ranges
3860 catch {unset rowchk}
3866 if {[info exists selectedline] && $selectedline > $row} {
3867 incr selectedline -1
3872 # Don't change the text pane cursor if it is currently the hand cursor,
3873 # showing that we are over a sha1 ID link.
3874 proc settextcursor {c} {
3875 global ctext curtextcursor
3877 if {[$ctext cget -cursor] == $curtextcursor} {
3878 $ctext config -cursor $c
3880 set curtextcursor $c
3883 proc nowbusy {what} {
3886 if {[array names isbusy] eq {}} {
3887 . config -cursor watch
3893 proc notbusy {what} {
3894 global isbusy maincursor textcursor
3896 catch {unset isbusy($what)}
3897 if {[array names isbusy] eq {}} {
3898 . config -cursor $maincursor
3899 settextcursor $textcursor
3903 proc findmatches {f} {
3904 global findtype foundstring foundstrlen
3905 if {$findtype == "Regexp"} {
3906 set matches [regexp -indices -all -inline $foundstring $f]
3908 if {$findtype == "IgnCase"} {
3909 set str [string tolower $f]
3915 while {[set j [string first $foundstring $str $i]] >= 0} {
3916 lappend matches [list $j [expr {$j+$foundstrlen-1}]]
3917 set i [expr {$j + $foundstrlen}]
3924 global findtype findloc findstring markedmatches commitinfo
3925 global numcommits displayorder linehtag linentag linedtag
3926 global mainfont canv canv2 canv3 selectedline
3927 global matchinglines foundstring foundstrlen matchstring
3932 cancel_next_highlight
3934 set matchinglines {}
3935 if {$findtype == "IgnCase"} {
3936 set foundstring [string tolower $findstring]
3938 set foundstring $findstring
3940 set foundstrlen [string length $findstring]
3941 if {$foundstrlen == 0} return
3942 regsub -all {[*?\[\\]} $foundstring {\\&} matchstring
3943 set matchstring "*$matchstring*"
3944 if {![info exists selectedline]} {
3947 set oldsel $selectedline
3950 set fldtypes {Headline Author Date Committer CDate Comments}
3952 foreach id $displayorder {
3953 set d $commitdata($id)
3955 if {$findtype == "Regexp"} {
3956 set doesmatch [regexp $foundstring $d]
3957 } elseif {$findtype == "IgnCase"} {
3958 set doesmatch [string match -nocase $matchstring $d]
3960 set doesmatch [string match $matchstring $d]
3962 if {!$doesmatch} continue
3963 if {![info exists commitinfo($id)]} {
3966 set info $commitinfo($id)
3968 foreach f $info ty $fldtypes {
3969 if {$findloc != "All fields" && $findloc != $ty} {
3972 set matches [findmatches $f]
3973 if {$matches == {}} continue
3975 if {$ty == "Headline"} {
3977 markmatches $canv $l $f $linehtag($l) $matches $mainfont
3978 } elseif {$ty == "Author"} {
3980 markmatches $canv2 $l $f $linentag($l) $matches $mainfont
3981 } elseif {$ty == "Date"} {
3983 markmatches $canv3 $l $f $linedtag($l) $matches $mainfont
3987 lappend matchinglines $l
3988 if {!$didsel && $l > $oldsel} {
3994 if {$matchinglines == {}} {
3996 } elseif {!$didsel} {
3997 findselectline [lindex $matchinglines 0]
4001 proc findselectline {l} {
4002 global findloc commentend ctext
4004 if {$findloc == "All fields" || $findloc == "Comments"} {
4005 # highlight the matches in the comments
4006 set f [$ctext get 1.0 $commentend]
4007 set matches [findmatches $f]
4008 foreach match $matches {
4009 set start [lindex $match 0]
4010 set end [expr {[lindex $match 1] + 1}]
4011 $ctext tag add found "1.0 + $start c" "1.0 + $end c"
4016 proc findnext {restart} {
4017 global matchinglines selectedline
4018 if {![info exists matchinglines]} {
4024 if {![info exists selectedline]} return
4025 foreach l $matchinglines {
4026 if {$l > $selectedline} {
4035 global matchinglines selectedline
4036 if {![info exists matchinglines]} {
4040 if {![info exists selectedline]} return
4042 foreach l $matchinglines {
4043 if {$l >= $selectedline} break
4047 findselectline $prev
4053 proc stopfindproc {{done 0}} {
4054 global findprocpid findprocfile findids
4055 global ctext findoldcursor phase maincursor textcursor
4056 global findinprogress
4058 catch {unset findids}
4059 if {[info exists findprocpid]} {
4061 catch {exec kill $findprocpid}
4063 catch {close $findprocfile}
4066 catch {unset findinprogress}
4070 # mark a commit as matching by putting a yellow background
4071 # behind the headline
4072 proc markheadline {l id} {
4073 global canv mainfont linehtag
4076 set bbox [$canv bbox $linehtag($l)]
4077 set t [$canv create rect $bbox -outline {} -tags matches -fill yellow]
4081 # mark the bits of a headline, author or date that match a find string
4082 proc markmatches {canv l str tag matches font} {
4083 set bbox [$canv bbox $tag]
4084 set x0 [lindex $bbox 0]
4085 set y0 [lindex $bbox 1]
4086 set y1 [lindex $bbox 3]
4087 foreach match $matches {
4088 set start [lindex $match 0]
4089 set end [lindex $match 1]
4090 if {$start > $end} continue
4091 set xoff [font measure $font [string range $str 0 [expr {$start-1}]]]
4092 set xlen [font measure $font [string range $str 0 [expr {$end}]]]
4093 set t [$canv create rect [expr {$x0+$xoff}] $y0 \
4094 [expr {$x0+$xlen+2}] $y1 \
4095 -outline {} -tags matches -fill yellow]
4100 proc unmarkmatches {} {
4101 global matchinglines findids
4102 allcanvs delete matches
4103 catch {unset matchinglines}
4104 catch {unset findids}
4107 proc selcanvline {w x y} {
4108 global canv canvy0 ctext linespc
4110 set ymax [lindex [$canv cget -scrollregion] 3]
4111 if {$ymax == {}} return
4112 set yfrac [lindex [$canv yview] 0]
4113 set y [expr {$y + $yfrac * $ymax}]
4114 set l [expr {int(($y - $canvy0) / $linespc + 0.5)}]
4119 if {![info exists rowtextx($l)] || $x < $rowtextx($l)} return
4125 proc commit_descriptor {p} {
4127 if {![info exists commitinfo($p)]} {
4131 if {[llength $commitinfo($p)] > 1} {
4132 set l [lindex $commitinfo($p) 0]
4137 # append some text to the ctext widget, and make any SHA1 ID
4138 # that we know about be a clickable link.
4139 proc appendwithlinks {text tags} {
4140 global ctext commitrow linknum curview
4142 set start [$ctext index "end - 1c"]
4143 $ctext insert end $text $tags
4144 set links [regexp -indices -all -inline {[0-9a-f]{40}} $text]
4148 set linkid [string range $text $s $e]
4149 if {![info exists commitrow($curview,$linkid)]} continue
4151 $ctext tag add link "$start + $s c" "$start + $e c"
4152 $ctext tag add link$linknum "$start + $s c" "$start + $e c"
4153 $ctext tag bind link$linknum <1> \
4154 [list selectline $commitrow($curview,$linkid) 1]
4157 $ctext tag conf link -foreground blue -underline 1
4158 $ctext tag bind link <Enter> { %W configure -cursor hand2 }
4159 $ctext tag bind link <Leave> { %W configure -cursor $curtextcursor }
4162 proc viewnextline {dir} {
4166 set ymax [lindex [$canv cget -scrollregion] 3]
4167 set wnow [$canv yview]
4168 set wtop [expr {[lindex $wnow 0] * $ymax}]
4169 set newtop [expr {$wtop + $dir * $linespc}]
4172 } elseif {$newtop > $ymax} {
4175 allcanvs yview moveto [expr {$newtop * 1.0 / $ymax}]
4178 # add a list of tag or branch names at position pos
4179 # returns the number of names inserted
4180 proc appendrefs {pos ids var} {
4181 global ctext commitrow linknum curview $var maxrefs
4183 if {[catch {$ctext index $pos}]} {
4186 $ctext conf -state normal
4187 $ctext delete $pos "$pos lineend"
4190 foreach tag [set $var\($id\)] {
4191 lappend tags [list $tag $id]
4194 if {[llength $tags] > $maxrefs} {
4195 $ctext insert $pos "many ([llength $tags])"
4197 set tags [lsort -index 0 -decreasing $tags]
4200 set id [lindex $ti 1]
4203 $ctext tag delete $lk
4204 $ctext insert $pos $sep
4205 $ctext insert $pos [lindex $ti 0] $lk
4206 if {[info exists commitrow($curview,$id)]} {
4207 $ctext tag conf $lk -foreground blue
4208 $ctext tag bind $lk <1> \
4209 [list selectline $commitrow($curview,$id) 1]
4210 $ctext tag conf $lk -underline 1
4211 $ctext tag bind $lk <Enter> { %W configure -cursor hand2 }
4212 $ctext tag bind $lk <Leave> \
4213 { %W configure -cursor $curtextcursor }
4218 $ctext conf -state disabled
4219 return [llength $tags]
4222 # called when we have finished computing the nearby tags
4223 proc dispneartags {delay} {
4224 global selectedline currentid showneartags tagphase
4226 if {![info exists selectedline] || !$showneartags} return
4227 after cancel dispnexttag
4229 after 200 dispnexttag
4232 after idle dispnexttag
4237 proc dispnexttag {} {
4238 global selectedline currentid showneartags tagphase ctext
4240 if {![info exists selectedline] || !$showneartags} return
4241 switch -- $tagphase {
4243 set dtags [desctags $currentid]
4245 appendrefs precedes $dtags idtags
4249 set atags [anctags $currentid]
4251 appendrefs follows $atags idtags
4255 set dheads [descheads $currentid]
4256 if {$dheads ne {}} {
4257 if {[appendrefs branch $dheads idheads] > 1
4258 && [$ctext get "branch -3c"] eq "h"} {
4259 # turn "Branch" into "Branches"
4260 $ctext conf -state normal
4261 $ctext insert "branch -2c" "es"
4262 $ctext conf -state disabled
4267 if {[incr tagphase] <= 2} {
4268 after idle dispnexttag
4272 proc selectline {l isnew} {
4273 global canv canv2 canv3 ctext commitinfo selectedline
4274 global displayorder linehtag linentag linedtag
4275 global canvy0 linespc parentlist childlist
4276 global currentid sha1entry
4277 global commentend idtags linknum
4278 global mergemax numcommits pending_select
4279 global cmitmode showneartags allcommits
4281 catch {unset pending_select}
4284 cancel_next_highlight
4285 if {$l < 0 || $l >= $numcommits} return
4286 set y [expr {$canvy0 + $l * $linespc}]
4287 set ymax [lindex [$canv cget -scrollregion] 3]
4288 set ytop [expr {$y - $linespc - 1}]
4289 set ybot [expr {$y + $linespc + 1}]
4290 set wnow [$canv yview]
4291 set wtop [expr {[lindex $wnow 0] * $ymax}]
4292 set wbot [expr {[lindex $wnow 1] * $ymax}]
4293 set wh [expr {$wbot - $wtop}]
4295 if {$ytop < $wtop} {
4296 if {$ybot < $wtop} {
4297 set newtop [expr {$y - $wh / 2.0}]
4300 if {$newtop > $wtop - $linespc} {
4301 set newtop [expr {$wtop - $linespc}]
4304 } elseif {$ybot > $wbot} {
4305 if {$ytop > $wbot} {
4306 set newtop [expr {$y - $wh / 2.0}]
4308 set newtop [expr {$ybot - $wh}]
4309 if {$newtop < $wtop + $linespc} {
4310 set newtop [expr {$wtop + $linespc}]
4314 if {$newtop != $wtop} {
4318 allcanvs yview moveto [expr {$newtop * 1.0 / $ymax}]
4322 if {![info exists linehtag($l)]} return
4324 set t [eval $canv create rect [$canv bbox $linehtag($l)] -outline {{}} \
4325 -tags secsel -fill [$canv cget -selectbackground]]
4327 $canv2 delete secsel
4328 set t [eval $canv2 create rect [$canv2 bbox $linentag($l)] -outline {{}} \
4329 -tags secsel -fill [$canv2 cget -selectbackground]]
4331 $canv3 delete secsel
4332 set t [eval $canv3 create rect [$canv3 bbox $linedtag($l)] -outline {{}} \
4333 -tags secsel -fill [$canv3 cget -selectbackground]]
4337 addtohistory [list selectline $l 0]
4342 set id [lindex $displayorder $l]
4344 $sha1entry delete 0 end
4345 $sha1entry insert 0 $id
4346 $sha1entry selection from 0
4347 $sha1entry selection to end
4350 $ctext conf -state normal
4353 set info $commitinfo($id)
4354 set date [formatdate [lindex $info 2]]
4355 $ctext insert end "Author: [lindex $info 1] $date\n"
4356 set date [formatdate [lindex $info 4]]
4357 $ctext insert end "Committer: [lindex $info 3] $date\n"
4358 if {[info exists idtags($id)]} {
4359 $ctext insert end "Tags:"
4360 foreach tag $idtags($id) {
4361 $ctext insert end " $tag"
4363 $ctext insert end "\n"
4367 set olds [lindex $parentlist $l]
4368 if {[llength $olds] > 1} {
4371 if {$np >= $mergemax} {
4376 $ctext insert end "Parent: " $tag
4377 appendwithlinks [commit_descriptor $p] {}
4382 append headers "Parent: [commit_descriptor $p]"
4386 foreach c [lindex $childlist $l] {
4387 append headers "Child: [commit_descriptor $c]"
4390 # make anything that looks like a SHA1 ID be a clickable link
4391 appendwithlinks $headers {}
4392 if {$showneartags} {
4393 if {![info exists allcommits]} {
4396 $ctext insert end "Branch: "
4397 $ctext mark set branch "end -1c"
4398 $ctext mark gravity branch left
4399 $ctext insert end "\nFollows: "
4400 $ctext mark set follows "end -1c"
4401 $ctext mark gravity follows left
4402 $ctext insert end "\nPrecedes: "
4403 $ctext mark set precedes "end -1c"
4404 $ctext mark gravity precedes left
4405 $ctext insert end "\n"
4408 $ctext insert end "\n"
4409 set comment [lindex $info 5]
4410 if {[string first "\r" $comment] >= 0} {
4411 set comment [string map {"\r" "\n "} $comment]
4413 appendwithlinks $comment {comment}
4415 $ctext tag delete Comments
4416 $ctext tag remove found 1.0 end
4417 $ctext conf -state disabled
4418 set commentend [$ctext index "end - 1c"]
4420 init_flist "Comments"
4421 if {$cmitmode eq "tree"} {
4423 } elseif {[llength $olds] <= 1} {
4430 proc selfirstline {} {
4435 proc sellastline {} {
4438 set l [expr {$numcommits - 1}]
4442 proc selnextline {dir} {
4444 if {![info exists selectedline]} return
4445 set l [expr {$selectedline + $dir}]
4450 proc selnextpage {dir} {
4451 global canv linespc selectedline numcommits
4453 set lpp [expr {([winfo height $canv] - 2) / $linespc}]
4457 allcanvs yview scroll [expr {$dir * $lpp}] units
4459 if {![info exists selectedline]} return
4460 set l [expr {$selectedline + $dir * $lpp}]
4463 } elseif {$l >= $numcommits} {
4464 set l [expr $numcommits - 1]
4470 proc unselectline {} {
4471 global selectedline currentid
4473 catch {unset selectedline}
4474 catch {unset currentid}
4475 allcanvs delete secsel
4477 cancel_next_highlight
4480 proc reselectline {} {
4483 if {[info exists selectedline]} {
4484 selectline $selectedline 0
4488 proc addtohistory {cmd} {
4489 global history historyindex curview
4491 set elt [list $curview $cmd]
4492 if {$historyindex > 0
4493 && [lindex $history [expr {$historyindex - 1}]] == $elt} {
4497 if {$historyindex < [llength $history]} {
4498 set history [lreplace $history $historyindex end $elt]
4500 lappend history $elt
4503 if {$historyindex > 1} {
4504 .tf.bar.leftbut conf -state normal
4506 .tf.bar.leftbut conf -state disabled
4508 .tf.bar.rightbut conf -state disabled
4514 set view [lindex $elt 0]
4515 set cmd [lindex $elt 1]
4516 if {$curview != $view} {
4523 global history historyindex
4525 if {$historyindex > 1} {
4526 incr historyindex -1
4527 godo [lindex $history [expr {$historyindex - 1}]]
4528 .tf.bar.rightbut conf -state normal
4530 if {$historyindex <= 1} {
4531 .tf.bar.leftbut conf -state disabled
4536 global history historyindex
4538 if {$historyindex < [llength $history]} {
4539 set cmd [lindex $history $historyindex]
4542 .tf.bar.leftbut conf -state normal
4544 if {$historyindex >= [llength $history]} {
4545 .tf.bar.rightbut conf -state disabled
4550 global treefilelist treeidlist diffids diffmergeid treepending nullid
4553 catch {unset diffmergeid}
4554 if {![info exists treefilelist($id)]} {
4555 if {![info exists treepending]} {
4556 if {$id ne $nullid} {
4557 set cmd [concat | git ls-tree -r $id]
4559 set cmd [concat | git ls-files]
4561 if {[catch {set gtf [open $cmd r]}]} {
4565 set treefilelist($id) {}
4566 set treeidlist($id) {}
4567 fconfigure $gtf -blocking 0
4568 filerun $gtf [list gettreeline $gtf $id]
4575 proc gettreeline {gtf id} {
4576 global treefilelist treeidlist treepending cmitmode diffids nullid
4579 while {[incr nl] <= 1000 && [gets $gtf line] >= 0} {
4580 if {$diffids ne $nullid} {
4581 set tl [split $line "\t"]
4582 if {[lindex $tl 0 1] ne "blob"} continue
4583 set sha1 [lindex $tl 0 2]
4584 set fname [lindex $tl 1]
4585 if {[string index $fname 0] eq "\""} {
4586 set fname [lindex $fname 0]
4588 lappend treeidlist($id) $sha1
4592 lappend treefilelist($id) $fname
4595 return [expr {$nl >= 1000? 2: 1}]
4599 if {$cmitmode ne "tree"} {
4600 if {![info exists diffmergeid]} {
4601 gettreediffs $diffids
4603 } elseif {$id ne $diffids} {
4612 global treefilelist treeidlist diffids nullid
4613 global ctext commentend
4615 set i [lsearch -exact $treefilelist($diffids) $f]
4617 puts "oops, $f not in list for id $diffids"
4620 if {$diffids ne $nullid} {
4621 set blob [lindex $treeidlist($diffids) $i]
4622 if {[catch {set bf [open [concat | git cat-file blob $blob] r]} err]} {
4623 puts "oops, error reading blob $blob: $err"
4627 if {[catch {set bf [open $f r]} err]} {
4628 puts "oops, can't read $f: $err"
4632 fconfigure $bf -blocking 0
4633 filerun $bf [list getblobline $bf $diffids]
4634 $ctext config -state normal
4635 clear_ctext $commentend
4636 $ctext insert end "\n"
4637 $ctext insert end "$f\n" filesep
4638 $ctext config -state disabled
4639 $ctext yview $commentend
4642 proc getblobline {bf id} {
4643 global diffids cmitmode ctext
4645 if {$id ne $diffids || $cmitmode ne "tree"} {
4649 $ctext config -state normal
4651 while {[incr nl] <= 1000 && [gets $bf line] >= 0} {
4652 $ctext insert end "$line\n"
4655 # delete last newline
4656 $ctext delete "end - 2c" "end - 1c"
4660 $ctext config -state disabled
4661 return [expr {$nl >= 1000? 2: 1}]
4664 proc mergediff {id l} {
4665 global diffmergeid diffopts mdifffd
4671 # this doesn't seem to actually affect anything...
4672 set env(GIT_DIFF_OPTS) $diffopts
4673 set cmd [concat | git diff-tree --no-commit-id --cc $id]
4674 if {[catch {set mdf [open $cmd r]} err]} {
4675 error_popup "Error getting merge diffs: $err"
4678 fconfigure $mdf -blocking 0
4679 set mdifffd($id) $mdf
4680 set np [llength [lindex $parentlist $l]]
4681 filerun $mdf [list getmergediffline $mdf $id $np]
4684 proc getmergediffline {mdf id np} {
4685 global diffmergeid ctext cflist mergemax
4686 global difffilestart mdifffd
4688 $ctext conf -state normal
4690 while {[incr nr] <= 1000 && [gets $mdf line] >= 0} {
4691 if {![info exists diffmergeid] || $id != $diffmergeid
4692 || $mdf != $mdifffd($id)} {
4696 if {[regexp {^diff --cc (.*)} $line match fname]} {
4697 # start of a new file
4698 $ctext insert end "\n"
4699 set here [$ctext index "end - 1c"]
4700 lappend difffilestart $here
4701 add_flist [list $fname]
4702 set l [expr {(78 - [string length $fname]) / 2}]
4703 set pad [string range "----------------------------------------" 1 $l]
4704 $ctext insert end "$pad $fname $pad\n" filesep
4705 } elseif {[regexp {^@@} $line]} {
4706 $ctext insert end "$line\n" hunksep
4707 } elseif {[regexp {^[0-9a-f]{40}$} $line] || [regexp {^index} $line]} {
4710 # parse the prefix - one ' ', '-' or '+' for each parent
4715 for {set j 0} {$j < $np} {incr j} {
4716 set c [string range $line $j $j]
4719 } elseif {$c == "-"} {
4721 } elseif {$c == "+"} {
4730 if {!$isbad && $minuses ne {} && $pluses eq {}} {
4731 # line doesn't appear in result, parents in $minuses have the line
4732 set num [lindex $minuses 0]
4733 } elseif {!$isbad && $pluses ne {} && $minuses eq {}} {
4734 # line appears in result, parents in $pluses don't have the line
4735 lappend tags mresult
4736 set num [lindex $spaces 0]
4739 if {$num >= $mergemax} {
4744 $ctext insert end "$line\n" $tags
4747 $ctext conf -state disabled
4752 return [expr {$nr >= 1000? 2: 1}]
4755 proc startdiff {ids} {
4756 global treediffs diffids treepending diffmergeid nullid
4759 catch {unset diffmergeid}
4760 if {![info exists treediffs($ids)] || [lsearch -exact $ids $nullid] >= 0} {
4761 if {![info exists treepending]} {
4769 proc addtocflist {ids} {
4770 global treediffs cflist
4771 add_flist $treediffs($ids)
4775 proc diffcmd {ids flags} {
4778 set i [lsearch -exact $ids $nullid]
4780 set cmd [concat | git diff-index $flags]
4781 if {[llength $ids] > 1} {
4783 lappend cmd -R [lindex $ids 1]
4785 lappend cmd [lindex $ids 0]
4791 set cmd [concat | git diff-tree --no-commit-id -r $flags $ids]
4796 proc gettreediffs {ids} {
4797 global treediff treepending
4799 set treepending $ids
4801 if {[catch {set gdtf [open [diffcmd $ids {}] r]}]} return
4802 fconfigure $gdtf -blocking 0
4803 filerun $gdtf [list gettreediffline $gdtf $ids]
4806 proc gettreediffline {gdtf ids} {
4807 global treediff treediffs treepending diffids diffmergeid
4811 while {[incr nr] <= 1000 && [gets $gdtf line] >= 0} {
4812 set file [lindex $line 5]
4813 lappend treediff $file
4816 return [expr {$nr >= 1000? 2: 1}]
4819 set treediffs($ids) $treediff
4821 if {$cmitmode eq "tree"} {
4823 } elseif {$ids != $diffids} {
4824 if {![info exists diffmergeid]} {
4825 gettreediffs $diffids
4833 proc getblobdiffs {ids} {
4834 global diffopts blobdifffd diffids env curdifftag curtagstart
4835 global diffinhdr treediffs
4837 set env(GIT_DIFF_OPTS) $diffopts
4838 if {[catch {set bdf [open [diffcmd $ids {-p -C}] r]} err]} {
4839 puts "error getting diffs: $err"
4843 fconfigure $bdf -blocking 0
4844 set blobdifffd($ids) $bdf
4845 set curdifftag Comments
4847 filerun $bdf [list getblobdiffline $bdf $diffids]
4850 proc setinlist {var i val} {
4853 while {[llength [set $var]] < $i} {
4856 if {[llength [set $var]] == $i} {
4863 proc getblobdiffline {bdf ids} {
4864 global diffids blobdifffd ctext curdifftag curtagstart
4865 global diffnexthead diffnextnote difffilestart
4866 global diffinhdr treediffs
4869 $ctext conf -state normal
4870 while {[incr nr] <= 1000 && [gets $bdf line] >= 0} {
4871 if {$ids != $diffids || $bdf != $blobdifffd($ids)} {
4875 if {[regexp {^diff --git a/(.*) b/(.*)} $line match fname newname]} {
4876 # start of a new file
4877 $ctext insert end "\n"
4878 $ctext tag add $curdifftag $curtagstart end
4879 set here [$ctext index "end - 1c"]
4880 set curtagstart $here
4882 set i [lsearch -exact $treediffs($ids) $fname]
4884 setinlist difffilestart $i $here
4886 if {$newname ne $fname} {
4887 set i [lsearch -exact $treediffs($ids) $newname]
4889 setinlist difffilestart $i $here
4892 set curdifftag "f:$fname"
4893 $ctext tag delete $curdifftag
4894 set l [expr {(78 - [string length $header]) / 2}]
4895 set pad [string range "----------------------------------------" \
4897 $ctext insert end "$pad $header $pad\n" filesep
4899 } elseif {$diffinhdr && [string compare -length 3 $line "---"] == 0} {
4901 } elseif {$diffinhdr && [string compare -length 3 $line "+++"] == 0} {
4903 } elseif {[regexp {^@@ -([0-9]+),([0-9]+) \+([0-9]+),([0-9]+) @@(.*)} \
4904 $line match f1l f1c f2l f2c rest]} {
4905 $ctext insert end "$line\n" hunksep
4908 set x [string range $line 0 0]
4909 if {$x == "-" || $x == "+"} {
4910 set tag [expr {$x == "+"}]
4911 $ctext insert end "$line\n" d$tag
4912 } elseif {$x == " "} {
4913 $ctext insert end "$line\n"
4914 } elseif {$diffinhdr || $x == "\\"} {
4915 # e.g. "\ No newline at end of file"
4916 $ctext insert end "$line\n" filesep
4918 # Something else we don't recognize
4919 if {$curdifftag != "Comments"} {
4920 $ctext insert end "\n"
4921 $ctext tag add $curdifftag $curtagstart end
4922 set curtagstart [$ctext index "end - 1c"]
4923 set curdifftag Comments
4925 $ctext insert end "$line\n" filesep
4929 $ctext conf -state disabled
4932 if {$ids == $diffids && $bdf == $blobdifffd($ids)} {
4933 $ctext tag add $curdifftag $curtagstart end
4937 return [expr {$nr >= 1000? 2: 1}]
4940 proc changediffdisp {} {
4941 global ctext diffelide
4943 $ctext tag conf d0 -elide [lindex $diffelide 0]
4944 $ctext tag conf d1 -elide [lindex $diffelide 1]
4948 global difffilestart ctext
4949 set prev [lindex $difffilestart 0]
4950 set here [$ctext index @0,0]
4951 foreach loc $difffilestart {
4952 if {[$ctext compare $loc >= $here]} {
4962 global difffilestart ctext
4963 set here [$ctext index @0,0]
4964 foreach loc $difffilestart {
4965 if {[$ctext compare $loc > $here]} {
4972 proc clear_ctext {{first 1.0}} {
4973 global ctext smarktop smarkbot
4975 set l [lindex [split $first .] 0]
4976 if {![info exists smarktop] || [$ctext compare $first < $smarktop.0]} {
4979 if {![info exists smarkbot] || [$ctext compare $first < $smarkbot.0]} {
4982 $ctext delete $first end
4985 proc incrsearch {name ix op} {
4986 global ctext searchstring searchdirn
4988 $ctext tag remove found 1.0 end
4989 if {[catch {$ctext index anchor}]} {
4990 # no anchor set, use start of selection, or of visible area
4991 set sel [$ctext tag ranges sel]
4993 $ctext mark set anchor [lindex $sel 0]
4994 } elseif {$searchdirn eq "-forwards"} {
4995 $ctext mark set anchor @0,0
4997 $ctext mark set anchor @0,[winfo height $ctext]
5000 if {$searchstring ne {}} {
5001 set here [$ctext search $searchdirn -- $searchstring anchor]
5010 global sstring ctext searchstring searchdirn
5013 $sstring icursor end
5014 set searchdirn -forwards
5015 if {$searchstring ne {}} {
5016 set sel [$ctext tag ranges sel]
5018 set start "[lindex $sel 0] + 1c"
5019 } elseif {[catch {set start [$ctext index anchor]}]} {
5022 set match [$ctext search -count mlen -- $searchstring $start]
5023 $ctext tag remove sel 1.0 end
5029 set mend "$match + $mlen c"
5030 $ctext tag add sel $match $mend
5031 $ctext mark unset anchor
5035 proc dosearchback {} {
5036 global sstring ctext searchstring searchdirn
5039 $sstring icursor end
5040 set searchdirn -backwards
5041 if {$searchstring ne {}} {
5042 set sel [$ctext tag ranges sel]
5044 set start [lindex $sel 0]
5045 } elseif {[catch {set start [$ctext index anchor]}]} {
5046 set start @0,[winfo height $ctext]
5048 set match [$ctext search -backwards -count ml -- $searchstring $start]
5049 $ctext tag remove sel 1.0 end
5055 set mend "$match + $ml c"
5056 $ctext tag add sel $match $mend
5057 $ctext mark unset anchor
5061 proc searchmark {first last} {
5062 global ctext searchstring
5066 set match [$ctext search -count mlen -- $searchstring $mend $last.end]
5067 if {$match eq {}} break
5068 set mend "$match + $mlen c"
5069 $ctext tag add found $match $mend
5073 proc searchmarkvisible {doall} {
5074 global ctext smarktop smarkbot
5076 set topline [lindex [split [$ctext index @0,0] .] 0]
5077 set botline [lindex [split [$ctext index @0,[winfo height $ctext]] .] 0]
5078 if {$doall || $botline < $smarktop || $topline > $smarkbot} {
5079 # no overlap with previous
5080 searchmark $topline $botline
5081 set smarktop $topline
5082 set smarkbot $botline
5084 if {$topline < $smarktop} {
5085 searchmark $topline [expr {$smarktop-1}]
5086 set smarktop $topline
5088 if {$botline > $smarkbot} {
5089 searchmark [expr {$smarkbot+1}] $botline
5090 set smarkbot $botline
5095 proc scrolltext {f0 f1} {
5098 .bleft.sb set $f0 $f1
5099 if {$searchstring ne {}} {
5105 global linespc charspc canvx0 canvy0 mainfont
5106 global xspc1 xspc2 lthickness
5108 set linespc [font metrics $mainfont -linespace]
5109 set charspc [font measure $mainfont "m"]
5110 set canvy0 [expr {int(3 + 0.5 * $linespc)}]
5111 set canvx0 [expr {int(3 + 0.5 * $linespc)}]
5112 set lthickness [expr {int($linespc / 9) + 1}]
5113 set xspc1(0) $linespc
5121 set ymax [lindex [$canv cget -scrollregion] 3]
5122 if {$ymax eq {} || $ymax == 0} return
5123 set span [$canv yview]
5126 allcanvs yview moveto [lindex $span 0]
5128 if {[info exists selectedline]} {
5129 selectline $selectedline 0
5130 allcanvs yview moveto [lindex $span 0]
5134 proc incrfont {inc} {
5135 global mainfont textfont ctext canv phase cflist
5136 global charspc tabstop
5137 global stopped entries
5139 set mainfont [lreplace $mainfont 1 1 [expr {[lindex $mainfont 1] + $inc}]]
5140 set textfont [lreplace $textfont 1 1 [expr {[lindex $textfont 1] + $inc}]]
5142 $ctext conf -font $textfont -tabs "[expr {$tabstop * $charspc}]"
5143 $cflist conf -font $textfont
5144 $ctext tag conf filesep -font [concat $textfont bold]
5145 foreach e $entries {
5146 $e conf -font $mainfont
5148 if {$phase eq "getcommits"} {
5149 $canv itemconf textitems -font $mainfont
5155 global sha1entry sha1string
5156 if {[string length $sha1string] == 40} {
5157 $sha1entry delete 0 end
5161 proc sha1change {n1 n2 op} {
5162 global sha1string currentid sha1but
5163 if {$sha1string == {}
5164 || ([info exists currentid] && $sha1string == $currentid)} {
5169 if {[$sha1but cget -state] == $state} return
5170 if {$state == "normal"} {
5171 $sha1but conf -state normal -relief raised -text "Goto: "
5173 $sha1but conf -state disabled -relief flat -text "SHA1 ID: "
5177 proc gotocommit {} {
5178 global sha1string currentid commitrow tagids headids
5179 global displayorder numcommits curview
5181 if {$sha1string == {}
5182 || ([info exists currentid] && $sha1string == $currentid)} return
5183 if {[info exists tagids($sha1string)]} {
5184 set id $tagids($sha1string)
5185 } elseif {[info exists headids($sha1string)]} {
5186 set id $headids($sha1string)
5188 set id [string tolower $sha1string]
5189 if {[regexp {^[0-9a-f]{4,39}$} $id]} {
5191 foreach i $displayorder {
5192 if {[string match $id* $i]} {
5196 if {$matches ne {}} {
5197 if {[llength $matches] > 1} {
5198 error_popup "Short SHA1 id $id is ambiguous"
5201 set id [lindex $matches 0]
5205 if {[info exists commitrow($curview,$id)]} {
5206 selectline $commitrow($curview,$id) 1
5209 if {[regexp {^[0-9a-fA-F]{4,}$} $sha1string]} {
5214 error_popup "$type $sha1string is not known"
5217 proc lineenter {x y id} {
5218 global hoverx hovery hoverid hovertimer
5219 global commitinfo canv
5221 if {![info exists commitinfo($id)] && ![getcommit $id]} return
5225 if {[info exists hovertimer]} {
5226 after cancel $hovertimer
5228 set hovertimer [after 500 linehover]
5232 proc linemotion {x y id} {
5233 global hoverx hovery hoverid hovertimer
5235 if {[info exists hoverid] && $id == $hoverid} {
5238 if {[info exists hovertimer]} {
5239 after cancel $hovertimer
5241 set hovertimer [after 500 linehover]
5245 proc lineleave {id} {
5246 global hoverid hovertimer canv
5248 if {[info exists hoverid] && $id == $hoverid} {
5250 if {[info exists hovertimer]} {
5251 after cancel $hovertimer
5259 global hoverx hovery hoverid hovertimer
5260 global canv linespc lthickness
5261 global commitinfo mainfont
5263 set text [lindex $commitinfo($hoverid) 0]
5264 set ymax [lindex [$canv cget -scrollregion] 3]
5265 if {$ymax == {}} return
5266 set yfrac [lindex [$canv yview] 0]
5267 set x [expr {$hoverx + 2 * $linespc}]
5268 set y [expr {$hovery + $yfrac * $ymax - $linespc / 2}]
5269 set x0 [expr {$x - 2 * $lthickness}]
5270 set y0 [expr {$y - 2 * $lthickness}]
5271 set x1 [expr {$x + [font measure $mainfont $text] + 2 * $lthickness}]
5272 set y1 [expr {$y + $linespc + 2 * $lthickness}]
5273 set t [$canv create rectangle $x0 $y0 $x1 $y1 \
5274 -fill \#ffff80 -outline black -width 1 -tags hover]
5276 set t [$canv create text $x $y -anchor nw -text $text -tags hover \
5281 proc clickisonarrow {id y} {
5284 set ranges [rowranges $id]
5285 set thresh [expr {2 * $lthickness + 6}]
5286 set n [expr {[llength $ranges] - 1}]
5287 for {set i 1} {$i < $n} {incr i} {
5288 set row [lindex $ranges $i]
5289 if {abs([yc $row] - $y) < $thresh} {
5296 proc arrowjump {id n y} {
5299 # 1 <-> 2, 3 <-> 4, etc...
5300 set n [expr {(($n - 1) ^ 1) + 1}]
5301 set row [lindex [rowranges $id] $n]
5303 set ymax [lindex [$canv cget -scrollregion] 3]
5304 if {$ymax eq {} || $ymax <= 0} return
5305 set view [$canv yview]
5306 set yspan [expr {[lindex $view 1] - [lindex $view 0]}]
5307 set yfrac [expr {$yt / $ymax - $yspan / 2}]
5311 allcanvs yview moveto $yfrac
5314 proc lineclick {x y id isnew} {
5315 global ctext commitinfo children canv thickerline curview
5317 if {![info exists commitinfo($id)] && ![getcommit $id]} return
5322 # draw this line thicker than normal
5326 set ymax [lindex [$canv cget -scrollregion] 3]
5327 if {$ymax eq {}} return
5328 set yfrac [lindex [$canv yview] 0]
5329 set y [expr {$y + $yfrac * $ymax}]
5331 set dirn [clickisonarrow $id $y]
5333 arrowjump $id $dirn $y
5338 addtohistory [list lineclick $x $y $id 0]
5340 # fill the details pane with info about this line
5341 $ctext conf -state normal
5343 $ctext tag conf link -foreground blue -underline 1
5344 $ctext tag bind link <Enter> { %W configure -cursor hand2 }
5345 $ctext tag bind link <Leave> { %W configure -cursor $curtextcursor }
5346 $ctext insert end "Parent:\t"
5347 $ctext insert end $id [list link link0]
5348 $ctext tag bind link0 <1> [list selbyid $id]
5349 set info $commitinfo($id)
5350 $ctext insert end "\n\t[lindex $info 0]\n"
5351 $ctext insert end "\tAuthor:\t[lindex $info 1]\n"
5352 set date [formatdate [lindex $info 2]]
5353 $ctext insert end "\tDate:\t$date\n"
5354 set kids $children($curview,$id)
5356 $ctext insert end "\nChildren:"
5358 foreach child $kids {
5360 if {![info exists commitinfo($child)] && ![getcommit $child]} continue
5361 set info $commitinfo($child)
5362 $ctext insert end "\n\t"
5363 $ctext insert end $child [list link link$i]
5364 $ctext tag bind link$i <1> [list selbyid $child]
5365 $ctext insert end "\n\t[lindex $info 0]"
5366 $ctext insert end "\n\tAuthor:\t[lindex $info 1]"
5367 set date [formatdate [lindex $info 2]]
5368 $ctext insert end "\n\tDate:\t$date\n"
5371 $ctext conf -state disabled
5375 proc normalline {} {
5377 if {[info exists thickerline]} {
5385 global commitrow curview
5386 if {[info exists commitrow($curview,$id)]} {
5387 selectline $commitrow($curview,$id) 1
5393 if {![info exists startmstime]} {
5394 set startmstime [clock clicks -milliseconds]
5396 return [format "%.3f" [expr {([clock click -milliseconds] - $startmstime) / 1000.0}]]
5399 proc rowmenu {x y id} {
5400 global rowctxmenu commitrow selectedline rowmenuid curview nullid
5404 if {![info exists selectedline]
5405 || $commitrow($curview,$id) eq $selectedline} {
5410 if {$id ne $nullid} {
5411 set menu $rowctxmenu
5413 set menu $fakerowmenu
5415 $menu entryconfigure "Diff this*" -state $state
5416 $menu entryconfigure "Diff selected*" -state $state
5417 $menu entryconfigure "Make patch" -state $state
5418 tk_popup $menu $x $y
5421 proc diffvssel {dirn} {
5422 global rowmenuid selectedline displayorder
5424 if {![info exists selectedline]} return
5426 set oldid [lindex $displayorder $selectedline]
5427 set newid $rowmenuid
5429 set oldid $rowmenuid
5430 set newid [lindex $displayorder $selectedline]
5432 addtohistory [list doseldiff $oldid $newid]
5433 doseldiff $oldid $newid
5436 proc doseldiff {oldid newid} {
5440 $ctext conf -state normal
5443 $ctext insert end "From "
5444 $ctext tag conf link -foreground blue -underline 1
5445 $ctext tag bind link <Enter> { %W configure -cursor hand2 }
5446 $ctext tag bind link <Leave> { %W configure -cursor $curtextcursor }
5447 $ctext tag bind link0 <1> [list selbyid $oldid]
5448 $ctext insert end $oldid [list link link0]
5449 $ctext insert end "\n "
5450 $ctext insert end [lindex $commitinfo($oldid) 0]
5451 $ctext insert end "\n\nTo "
5452 $ctext tag bind link1 <1> [list selbyid $newid]
5453 $ctext insert end $newid [list link link1]
5454 $ctext insert end "\n "
5455 $ctext insert end [lindex $commitinfo($newid) 0]
5456 $ctext insert end "\n"
5457 $ctext conf -state disabled
5458 $ctext tag delete Comments
5459 $ctext tag remove found 1.0 end
5460 startdiff [list $oldid $newid]
5464 global rowmenuid currentid commitinfo patchtop patchnum
5466 if {![info exists currentid]} return
5467 set oldid $currentid
5468 set oldhead [lindex $commitinfo($oldid) 0]
5469 set newid $rowmenuid
5470 set newhead [lindex $commitinfo($newid) 0]
5473 catch {destroy $top}
5475 label $top.title -text "Generate patch"
5476 grid $top.title - -pady 10
5477 label $top.from -text "From:"
5478 entry $top.fromsha1 -width 40 -relief flat
5479 $top.fromsha1 insert 0 $oldid
5480 $top.fromsha1 conf -state readonly
5481 grid $top.from $top.fromsha1 -sticky w
5482 entry $top.fromhead -width 60 -relief flat
5483 $top.fromhead insert 0 $oldhead
5484 $top.fromhead conf -state readonly
5485 grid x $top.fromhead -sticky w
5486 label $top.to -text "To:"
5487 entry $top.tosha1 -width 40 -relief flat
5488 $top.tosha1 insert 0 $newid
5489 $top.tosha1 conf -state readonly
5490 grid $top.to $top.tosha1 -sticky w
5491 entry $top.tohead -width 60 -relief flat
5492 $top.tohead insert 0 $newhead
5493 $top.tohead conf -state readonly
5494 grid x $top.tohead -sticky w
5495 button $top.rev -text "Reverse" -command mkpatchrev -padx 5
5496 grid $top.rev x -pady 10
5497 label $top.flab -text "Output file:"
5498 entry $top.fname -width 60
5499 $top.fname insert 0 [file normalize "patch$patchnum.patch"]
5501 grid $top.flab $top.fname -sticky w
5503 button $top.buts.gen -text "Generate" -command mkpatchgo
5504 button $top.buts.can -text "Cancel" -command mkpatchcan
5505 grid $top.buts.gen $top.buts.can
5506 grid columnconfigure $top.buts 0 -weight 1 -uniform a
5507 grid columnconfigure $top.buts 1 -weight 1 -uniform a
5508 grid $top.buts - -pady 10 -sticky ew
5512 proc mkpatchrev {} {
5515 set oldid [$patchtop.fromsha1 get]
5516 set oldhead [$patchtop.fromhead get]
5517 set newid [$patchtop.tosha1 get]
5518 set newhead [$patchtop.tohead get]
5519 foreach e [list fromsha1 fromhead tosha1 tohead] \
5520 v [list $newid $newhead $oldid $oldhead] {
5521 $patchtop.$e conf -state normal
5522 $patchtop.$e delete 0 end
5523 $patchtop.$e insert 0 $v
5524 $patchtop.$e conf -state readonly
5529 global patchtop nullid
5531 set oldid [$patchtop.fromsha1 get]
5532 set newid [$patchtop.tosha1 get]
5533 set fname [$patchtop.fname get]
5534 if {$newid eq $nullid} {
5535 set cmd [list git diff-index -p $oldid]
5536 } elseif {$oldid eq $nullid} {
5537 set cmd [list git diff-index -p -R $newid]
5539 set cmd [list git diff-tree -p $oldid $newid]
5541 lappend cmd >$fname &
5542 if {[catch {eval exec $cmd} err]} {
5543 error_popup "Error creating patch: $err"
5545 catch {destroy $patchtop}
5549 proc mkpatchcan {} {
5552 catch {destroy $patchtop}
5557 global rowmenuid mktagtop commitinfo
5561 catch {destroy $top}
5563 label $top.title -text "Create tag"
5564 grid $top.title - -pady 10
5565 label $top.id -text "ID:"
5566 entry $top.sha1 -width 40 -relief flat
5567 $top.sha1 insert 0 $rowmenuid
5568 $top.sha1 conf -state readonly
5569 grid $top.id $top.sha1 -sticky w
5570 entry $top.head -width 60 -relief flat
5571 $top.head insert 0 [lindex $commitinfo($rowmenuid) 0]
5572 $top.head conf -state readonly
5573 grid x $top.head -sticky w
5574 label $top.tlab -text "Tag name:"
5575 entry $top.tag -width 60
5576 grid $top.tlab $top.tag -sticky w
5578 button $top.buts.gen -text "Create" -command mktaggo
5579 button $top.buts.can -text "Cancel" -command mktagcan
5580 grid $top.buts.gen $top.buts.can
5581 grid columnconfigure $top.buts 0 -weight 1 -uniform a
5582 grid columnconfigure $top.buts 1 -weight 1 -uniform a
5583 grid $top.buts - -pady 10 -sticky ew
5588 global mktagtop env tagids idtags
5590 set id [$mktagtop.sha1 get]
5591 set tag [$mktagtop.tag get]
5593 error_popup "No tag name specified"
5596 if {[info exists tagids($tag)]} {
5597 error_popup "Tag \"$tag\" already exists"
5602 set fname [file join $dir "refs/tags" $tag]
5603 set f [open $fname w]
5607 error_popup "Error creating tag: $err"
5611 set tagids($tag) $id
5612 lappend idtags($id) $tag
5617 proc redrawtags {id} {
5618 global canv linehtag commitrow idpos selectedline curview
5619 global mainfont canvxmax iddrawn
5621 if {![info exists commitrow($curview,$id)]} return
5622 if {![info exists iddrawn($id)]} return
5623 drawcommits $commitrow($curview,$id)
5624 $canv delete tag.$id
5625 set xt [eval drawtags $id $idpos($id)]
5626 $canv coords $linehtag($commitrow($curview,$id)) $xt [lindex $idpos($id) 2]
5627 set text [$canv itemcget $linehtag($commitrow($curview,$id)) -text]
5628 set xr [expr {$xt + [font measure $mainfont $text]}]
5629 if {$xr > $canvxmax} {
5633 if {[info exists selectedline]
5634 && $selectedline == $commitrow($curview,$id)} {
5635 selectline $selectedline 0
5642 catch {destroy $mktagtop}
5651 proc writecommit {} {
5652 global rowmenuid wrcomtop commitinfo wrcomcmd
5654 set top .writecommit
5656 catch {destroy $top}
5658 label $top.title -text "Write commit to file"
5659 grid $top.title - -pady 10
5660 label $top.id -text "ID:"
5661 entry $top.sha1 -width 40 -relief flat
5662 $top.sha1 insert 0 $rowmenuid
5663 $top.sha1 conf -state readonly
5664 grid $top.id $top.sha1 -sticky w
5665 entry $top.head -width 60 -relief flat
5666 $top.head insert 0 [lindex $commitinfo($rowmenuid) 0]
5667 $top.head conf -state readonly
5668 grid x $top.head -sticky w
5669 label $top.clab -text "Command:"
5670 entry $top.cmd -width 60 -textvariable wrcomcmd
5671 grid $top.clab $top.cmd -sticky w -pady 10
5672 label $top.flab -text "Output file:"
5673 entry $top.fname -width 60
5674 $top.fname insert 0 [file normalize "commit-[string range $rowmenuid 0 6]"]
5675 grid $top.flab $top.fname -sticky w
5677 button $top.buts.gen -text "Write" -command wrcomgo
5678 button $top.buts.can -text "Cancel" -command wrcomcan
5679 grid $top.buts.gen $top.buts.can
5680 grid columnconfigure $top.buts 0 -weight 1 -uniform a
5681 grid columnconfigure $top.buts 1 -weight 1 -uniform a
5682 grid $top.buts - -pady 10 -sticky ew
5689 set id [$wrcomtop.sha1 get]
5690 set cmd "echo $id | [$wrcomtop.cmd get]"
5691 set fname [$wrcomtop.fname get]
5692 if {[catch {exec sh -c $cmd >$fname &} err]} {
5693 error_popup "Error writing commit: $err"
5695 catch {destroy $wrcomtop}
5702 catch {destroy $wrcomtop}
5707 global rowmenuid mkbrtop
5710 catch {destroy $top}
5712 label $top.title -text "Create new branch"
5713 grid $top.title - -pady 10
5714 label $top.id -text "ID:"
5715 entry $top.sha1 -width 40 -relief flat
5716 $top.sha1 insert 0 $rowmenuid
5717 $top.sha1 conf -state readonly
5718 grid $top.id $top.sha1 -sticky w
5719 label $top.nlab -text "Name:"
5720 entry $top.name -width 40
5721 grid $top.nlab $top.name -sticky w
5723 button $top.buts.go -text "Create" -command [list mkbrgo $top]
5724 button $top.buts.can -text "Cancel" -command "catch {destroy $top}"
5725 grid $top.buts.go $top.buts.can
5726 grid columnconfigure $top.buts 0 -weight 1 -uniform a
5727 grid columnconfigure $top.buts 1 -weight 1 -uniform a
5728 grid $top.buts - -pady 10 -sticky ew
5733 global headids idheads
5735 set name [$top.name get]
5736 set id [$top.sha1 get]
5738 error_popup "Please specify a name for the new branch"
5741 catch {destroy $top}
5745 exec git branch $name $id
5750 set headids($name) $id
5751 lappend idheads($id) $name
5759 proc cherrypick {} {
5760 global rowmenuid curview commitrow
5763 set oldhead [exec git rev-parse HEAD]
5764 set dheads [descheads $rowmenuid]
5765 if {$dheads ne {} && [lsearch -exact $dheads $oldhead] >= 0} {
5766 set ok [confirm_popup "Commit [string range $rowmenuid 0 7] is already\
5767 included in branch $mainhead -- really re-apply it?"]
5772 # Unfortunately git-cherry-pick writes stuff to stderr even when
5773 # no error occurs, and exec takes that as an indication of error...
5774 if {[catch {exec sh -c "git cherry-pick -r $rowmenuid 2>&1"} err]} {
5779 set newhead [exec git rev-parse HEAD]
5780 if {$newhead eq $oldhead} {
5782 error_popup "No changes committed"
5785 addnewchild $newhead $oldhead
5786 if {[info exists commitrow($curview,$oldhead)]} {
5787 insertrow $commitrow($curview,$oldhead) $newhead
5788 if {$mainhead ne {}} {
5789 movehead $newhead $mainhead
5790 movedhead $newhead $mainhead
5798 # context menu for a head
5799 proc headmenu {x y id head} {
5800 global headmenuid headmenuhead headctxmenu mainhead
5803 set headmenuhead $head
5805 if {$head eq $mainhead} {
5808 $headctxmenu entryconfigure 0 -state $state
5809 $headctxmenu entryconfigure 1 -state $state
5810 tk_popup $headctxmenu $x $y
5814 global headmenuid headmenuhead mainhead headids
5815 global showlocalchanges mainheadid
5817 # check the tree is clean first??
5818 set oldmainhead $mainhead
5823 exec git checkout -q $headmenuhead
5829 set mainhead $headmenuhead
5830 set mainheadid $headmenuid
5831 if {[info exists headids($oldmainhead)]} {
5832 redrawtags $headids($oldmainhead)
5834 redrawtags $headmenuid
5835 if {$showlocalchanges} {
5842 global headmenuid headmenuhead mainhead
5843 global headids idheads
5845 set head $headmenuhead
5847 # this check shouldn't be needed any more...
5848 if {$head eq $mainhead} {
5849 error_popup "Cannot delete the currently checked-out branch"
5852 set dheads [descheads $id]
5853 if {$dheads eq $headids($head)} {
5854 # the stuff on this branch isn't on any other branch
5855 if {![confirm_popup "The commits on branch $head aren't on any other\
5856 branch.\nReally delete branch $head?"]} return
5860 if {[catch {exec git branch -D $head} err]} {
5865 removehead $id $head
5866 removedhead $id $head
5872 # Stuff for finding nearby tags
5873 proc getallcommits {} {
5874 global allcommits allids nbmp nextarc seeds
5884 # Called when the graph might have changed
5885 proc regetallcommits {} {
5886 global allcommits seeds
5888 set cmd [concat | git rev-list --all --parents]
5892 set fd [open $cmd r]
5893 fconfigure $fd -blocking 0
5896 filerun $fd [list getallclines $fd]
5899 # Since most commits have 1 parent and 1 child, we group strings of
5900 # such commits into "arcs" joining branch/merge points (BMPs), which
5901 # are commits that either don't have 1 parent or don't have 1 child.
5903 # arcnos(id) - incoming arcs for BMP, arc we're on for other nodes
5904 # arcout(id) - outgoing arcs for BMP
5905 # arcids(a) - list of IDs on arc including end but not start
5906 # arcstart(a) - BMP ID at start of arc
5907 # arcend(a) - BMP ID at end of arc
5908 # growing(a) - arc a is still growing
5909 # arctags(a) - IDs out of arcids (excluding end) that have tags
5910 # archeads(a) - IDs out of arcids (excluding end) that have heads
5911 # The start of an arc is at the descendent end, so "incoming" means
5912 # coming from descendents, and "outgoing" means going towards ancestors.
5914 proc getallclines {fd} {
5915 global allids allparents allchildren idtags nextarc nbmp
5916 global arcnos arcids arctags arcout arcend arcstart archeads growing
5917 global seeds allcommits
5920 while {[incr nid] <= 1000 && [gets $fd line] >= 0} {
5921 set id [lindex $line 0]
5922 if {[info exists allparents($id)]} {
5927 set olds [lrange $line 1 end]
5928 set allparents($id) $olds
5929 if {![info exists allchildren($id)]} {
5930 set allchildren($id) {}
5935 if {[llength $olds] == 1 && [llength $a] == 1} {
5936 lappend arcids($a) $id
5937 if {[info exists idtags($id)]} {
5938 lappend arctags($a) $id
5940 if {[info exists idheads($id)]} {
5941 lappend archeads($a) $id
5943 if {[info exists allparents($olds)]} {
5944 # seen parent already
5945 if {![info exists arcout($olds)]} {
5948 lappend arcids($a) $olds
5949 set arcend($a) $olds
5952 lappend allchildren($olds) $id
5953 lappend arcnos($olds) $a
5958 foreach a $arcnos($id) {
5959 lappend arcids($a) $id
5966 lappend allchildren($p) $id
5967 set a [incr nextarc]
5968 set arcstart($a) $id
5975 if {[info exists allparents($p)]} {
5976 # seen it already, may need to make a new branch
5977 if {![info exists arcout($p)]} {
5980 lappend arcids($a) $p
5984 lappend arcnos($p) $a
5989 return [expr {$nid >= 1000? 2: 1}]
5992 if {[incr allcommits -1] == 0} {
5999 proc recalcarc {a} {
6000 global arctags archeads arcids idtags idheads
6004 foreach id [lrange $arcids($a) 0 end-1] {
6005 if {[info exists idtags($id)]} {
6008 if {[info exists idheads($id)]} {
6013 set archeads($a) $ah
6017 global arcnos arcids nextarc nbmp arctags archeads idtags idheads
6018 global arcstart arcend arcout allparents growing
6021 if {[llength $a] != 1} {
6022 puts "oops splitarc called but [llength $a] arcs already"
6026 set i [lsearch -exact $arcids($a) $p]
6028 puts "oops splitarc $p not in arc $a"
6031 set na [incr nextarc]
6032 if {[info exists arcend($a)]} {
6033 set arcend($na) $arcend($a)
6035 set l [lindex $allparents([lindex $arcids($a) end]) 0]
6036 set j [lsearch -exact $arcnos($l) $a]
6037 set arcnos($l) [lreplace $arcnos($l) $j $j $na]
6039 set tail [lrange $arcids($a) [expr {$i+1}] end]
6040 set arcids($a) [lrange $arcids($a) 0 $i]
6042 set arcstart($na) $p
6044 set arcids($na) $tail
6045 if {[info exists growing($a)]} {
6052 if {[llength $arcnos($id)] == 1} {
6055 set j [lsearch -exact $arcnos($id) $a]
6056 set arcnos($id) [lreplace $arcnos($id) $j $j $na]
6060 # reconstruct tags and heads lists
6061 if {$arctags($a) ne {} || $archeads($a) ne {}} {
6066 set archeads($na) {}
6070 # Update things for a new commit added that is a child of one
6071 # existing commit. Used when cherry-picking.
6072 proc addnewchild {id p} {
6073 global allids allparents allchildren idtags nextarc nbmp
6074 global arcnos arcids arctags arcout arcend arcstart archeads growing
6078 set allparents($id) [list $p]
6079 set allchildren($id) {}
6083 lappend allchildren($p) $id
6084 set a [incr nextarc]
6085 set arcstart($a) $id
6088 set arcids($a) [list $p]
6090 if {![info exists arcout($p)]} {
6093 lappend arcnos($p) $a
6094 set arcout($id) [list $a]
6097 # Returns 1 if a is an ancestor of b, -1 if b is an ancestor of a,
6098 # or 0 if neither is true.
6099 proc anc_or_desc {a b} {
6100 global arcout arcstart arcend arcnos cached_isanc
6102 if {$arcnos($a) eq $arcnos($b)} {
6103 # Both are on the same arc(s); either both are the same BMP,
6104 # or if one is not a BMP, the other is also not a BMP or is
6105 # the BMP at end of the arc (and it only has 1 incoming arc).
6109 # assert {[llength $arcnos($a)] == 1}
6110 set arc [lindex $arcnos($a) 0]
6111 set i [lsearch -exact $arcids($arc) $a]
6112 set j [lsearch -exact $arcids($arc) $b]
6113 if {$i < 0 || $i > $j} {
6120 if {![info exists arcout($a)]} {
6121 set arc [lindex $arcnos($a) 0]
6122 if {[info exists arcend($arc)]} {
6123 set aend $arcend($arc)
6127 set a $arcstart($arc)
6131 if {![info exists arcout($b)]} {
6132 set arc [lindex $arcnos($b) 0]
6133 if {[info exists arcend($arc)]} {
6134 set bend $arcend($arc)
6138 set b $arcstart($arc)
6148 if {[info exists cached_isanc($a,$bend)]} {
6149 if {$cached_isanc($a,$bend)} {
6153 if {[info exists cached_isanc($b,$aend)]} {
6154 if {$cached_isanc($b,$aend)} {
6157 if {[info exists cached_isanc($a,$bend)]} {
6162 set todo [list $a $b]
6165 for {set i 0} {$i < [llength $todo]} {incr i} {
6166 set x [lindex $todo $i]
6167 if {$anc($x) eq {}} {
6170 foreach arc $arcnos($x) {
6171 set xd $arcstart($arc)
6173 set cached_isanc($a,$bend) 1
6174 set cached_isanc($b,$aend) 0
6176 } elseif {$xd eq $aend} {
6177 set cached_isanc($b,$aend) 1
6178 set cached_isanc($a,$bend) 0
6181 if {![info exists anc($xd)]} {
6182 set anc($xd) $anc($x)
6184 } elseif {$anc($xd) ne $anc($x)} {
6189 set cached_isanc($a,$bend) 0
6190 set cached_isanc($b,$aend) 0
6194 # This identifies whether $desc has an ancestor that is
6195 # a growing tip of the graph and which is not an ancestor of $anc
6196 # and returns 0 if so and 1 if not.
6197 # If we subsequently discover a tag on such a growing tip, and that
6198 # turns out to be a descendent of $anc (which it could, since we
6199 # don't necessarily see children before parents), then $desc
6200 # isn't a good choice to display as a descendent tag of
6201 # $anc (since it is the descendent of another tag which is
6202 # a descendent of $anc). Similarly, $anc isn't a good choice to
6203 # display as a ancestor tag of $desc.
6205 proc is_certain {desc anc} {
6206 global arcnos arcout arcstart arcend growing problems
6209 if {[llength $arcnos($anc)] == 1} {
6210 # tags on the same arc are certain
6211 if {$arcnos($desc) eq $arcnos($anc)} {
6214 if {![info exists arcout($anc)]} {
6215 # if $anc is partway along an arc, use the start of the arc instead
6216 set a [lindex $arcnos($anc) 0]
6217 set anc $arcstart($a)
6220 if {[llength $arcnos($desc)] > 1 || [info exists arcout($desc)]} {
6223 set a [lindex $arcnos($desc) 0]
6229 set anclist [list $x]
6233 for {set i 0} {$i < [llength $anclist] && ($nnh > 0 || $ngrowanc > 0)} {incr i} {
6234 set x [lindex $anclist $i]
6239 foreach a $arcout($x) {
6240 if {[info exists growing($a)]} {
6241 if {![info exists growanc($x)] && $dl($x)} {
6247 if {[info exists dl($y)]} {
6251 if {![info exists done($y)]} {
6254 if {[info exists growanc($x)]} {
6258 for {set k 0} {$k < [llength $xl]} {incr k} {
6259 set z [lindex $xl $k]
6260 foreach c $arcout($z) {
6261 if {[info exists arcend($c)]} {
6263 if {[info exists dl($v)] && $dl($v)} {
6265 if {![info exists done($v)]} {
6268 if {[info exists growanc($v)]} {
6278 } elseif {$y eq $anc || !$dl($x)} {
6289 foreach x [array names growanc] {
6298 proc validate_arctags {a} {
6299 global arctags idtags
6303 foreach id $arctags($a) {
6305 if {![info exists idtags($id)]} {
6306 set na [lreplace $na $i $i]
6313 proc validate_archeads {a} {
6314 global archeads idheads
6317 set na $archeads($a)
6318 foreach id $archeads($a) {
6320 if {![info exists idheads($id)]} {
6321 set na [lreplace $na $i $i]
6325 set archeads($a) $na
6328 # Return the list of IDs that have tags that are descendents of id,
6329 # ignoring IDs that are descendents of IDs already reported.
6330 proc desctags {id} {
6331 global arcnos arcstart arcids arctags idtags allparents
6332 global growing cached_dtags
6334 if {![info exists allparents($id)]} {
6337 set t1 [clock clicks -milliseconds]
6339 if {[llength $arcnos($id)] == 1 && [llength $allparents($id)] == 1} {
6340 # part-way along an arc; check that arc first
6341 set a [lindex $arcnos($id) 0]
6342 if {$arctags($a) ne {}} {
6344 set i [lsearch -exact $arcids($a) $id]
6346 foreach t $arctags($a) {
6347 set j [lsearch -exact $arcids($a) $t]
6355 set id $arcstart($a)
6356 if {[info exists idtags($id)]} {
6360 if {[info exists cached_dtags($id)]} {
6361 return $cached_dtags($id)
6368 for {set i 0} {$i < [llength $todo] && $nc > 0} {incr i} {
6369 set id [lindex $todo $i]
6371 set ta [info exists hastaggedancestor($id)]
6375 # ignore tags on starting node
6376 if {!$ta && $i > 0} {
6377 if {[info exists idtags($id)]} {
6380 } elseif {[info exists cached_dtags($id)]} {
6381 set tagloc($id) $cached_dtags($id)
6385 foreach a $arcnos($id) {
6387 if {!$ta && $arctags($a) ne {}} {
6389 if {$arctags($a) ne {}} {
6390 lappend tagloc($id) [lindex $arctags($a) end]
6393 if {$ta || $arctags($a) ne {}} {
6394 set tomark [list $d]
6395 for {set j 0} {$j < [llength $tomark]} {incr j} {
6396 set dd [lindex $tomark $j]
6397 if {![info exists hastaggedancestor($dd)]} {
6398 if {[info exists done($dd)]} {
6399 foreach b $arcnos($dd) {
6400 lappend tomark $arcstart($b)
6402 if {[info exists tagloc($dd)]} {
6405 } elseif {[info exists queued($dd)]} {
6408 set hastaggedancestor($dd) 1
6412 if {![info exists queued($d)]} {
6415 if {![info exists hastaggedancestor($d)]} {
6422 foreach id [array names tagloc] {
6423 if {![info exists hastaggedancestor($id)]} {
6424 foreach t $tagloc($id) {
6425 if {[lsearch -exact $tags $t] < 0} {
6431 set t2 [clock clicks -milliseconds]
6434 # remove tags that are descendents of other tags
6435 for {set i 0} {$i < [llength $tags]} {incr i} {
6436 set a [lindex $tags $i]
6437 for {set j 0} {$j < $i} {incr j} {
6438 set b [lindex $tags $j]
6439 set r [anc_or_desc $a $b]
6441 set tags [lreplace $tags $j $j]
6444 } elseif {$r == -1} {
6445 set tags [lreplace $tags $i $i]
6452 if {[array names growing] ne {}} {
6453 # graph isn't finished, need to check if any tag could get
6454 # eclipsed by another tag coming later. Simply ignore any
6455 # tags that could later get eclipsed.
6458 if {[is_certain $t $origid]} {
6462 if {$tags eq $ctags} {
6463 set cached_dtags($origid) $tags
6468 set cached_dtags($origid) $tags
6470 set t3 [clock clicks -milliseconds]
6471 if {0 && $t3 - $t1 >= 100} {
6472 puts "iterating descendents ($loopix/[llength $todo] nodes) took\
6473 [expr {$t2-$t1}]+[expr {$t3-$t2}]ms, $nc candidates left"
6479 global arcnos arcids arcout arcend arctags idtags allparents
6480 global growing cached_atags
6482 if {![info exists allparents($id)]} {
6485 set t1 [clock clicks -milliseconds]
6487 if {[llength $arcnos($id)] == 1 && [llength $allparents($id)] == 1} {
6488 # part-way along an arc; check that arc first
6489 set a [lindex $arcnos($id) 0]
6490 if {$arctags($a) ne {}} {
6492 set i [lsearch -exact $arcids($a) $id]
6493 foreach t $arctags($a) {
6494 set j [lsearch -exact $arcids($a) $t]
6500 if {![info exists arcend($a)]} {
6504 if {[info exists idtags($id)]} {
6508 if {[info exists cached_atags($id)]} {
6509 return $cached_atags($id)
6517 for {set i 0} {$i < [llength $todo] && $nc > 0} {incr i} {
6518 set id [lindex $todo $i]
6520 set td [info exists hastaggeddescendent($id)]
6524 # ignore tags on starting node
6525 if {!$td && $i > 0} {
6526 if {[info exists idtags($id)]} {
6529 } elseif {[info exists cached_atags($id)]} {
6530 set tagloc($id) $cached_atags($id)
6534 foreach a $arcout($id) {
6535 if {!$td && $arctags($a) ne {}} {
6537 if {$arctags($a) ne {}} {
6538 lappend tagloc($id) [lindex $arctags($a) 0]
6541 if {![info exists arcend($a)]} continue
6543 if {$td || $arctags($a) ne {}} {
6544 set tomark [list $d]
6545 for {set j 0} {$j < [llength $tomark]} {incr j} {
6546 set dd [lindex $tomark $j]
6547 if {![info exists hastaggeddescendent($dd)]} {
6548 if {[info exists done($dd)]} {
6549 foreach b $arcout($dd) {
6550 if {[info exists arcend($b)]} {
6551 lappend tomark $arcend($b)
6554 if {[info exists tagloc($dd)]} {
6557 } elseif {[info exists queued($dd)]} {
6560 set hastaggeddescendent($dd) 1
6564 if {![info exists queued($d)]} {
6567 if {![info exists hastaggeddescendent($d)]} {
6573 set t2 [clock clicks -milliseconds]
6576 foreach id [array names tagloc] {
6577 if {![info exists hastaggeddescendent($id)]} {
6578 foreach t $tagloc($id) {
6579 if {[lsearch -exact $tags $t] < 0} {
6586 # remove tags that are ancestors of other tags
6587 for {set i 0} {$i < [llength $tags]} {incr i} {
6588 set a [lindex $tags $i]
6589 for {set j 0} {$j < $i} {incr j} {
6590 set b [lindex $tags $j]
6591 set r [anc_or_desc $a $b]
6593 set tags [lreplace $tags $j $j]
6596 } elseif {$r == 1} {
6597 set tags [lreplace $tags $i $i]
6604 if {[array names growing] ne {}} {
6605 # graph isn't finished, need to check if any tag could get
6606 # eclipsed by another tag coming later. Simply ignore any
6607 # tags that could later get eclipsed.
6610 if {[is_certain $origid $t]} {
6614 if {$tags eq $ctags} {
6615 set cached_atags($origid) $tags
6620 set cached_atags($origid) $tags
6622 set t3 [clock clicks -milliseconds]
6623 if {0 && $t3 - $t1 >= 100} {
6624 puts "iterating ancestors ($loopix/[llength $todo] nodes) took\
6625 [expr {$t2-$t1}]+[expr {$t3-$t2}]ms, $nc candidates left"
6630 # Return the list of IDs that have heads that are descendents of id,
6631 # including id itself if it has a head.
6632 proc descheads {id} {
6633 global arcnos arcstart arcids archeads idheads cached_dheads
6636 if {![info exists allparents($id)]} {
6640 if {[llength $arcnos($id)] == 1 && [llength $allparents($id)] == 1} {
6641 # part-way along an arc; check it first
6642 set a [lindex $arcnos($id) 0]
6643 if {$archeads($a) ne {}} {
6644 validate_archeads $a
6645 set i [lsearch -exact $arcids($a) $id]
6646 foreach t $archeads($a) {
6647 set j [lsearch -exact $arcids($a) $t]
6652 set id $arcstart($a)
6657 for {set i 0} {$i < [llength $todo]} {incr i} {
6658 set id [lindex $todo $i]
6659 if {[info exists cached_dheads($id)]} {
6660 set ret [concat $ret $cached_dheads($id)]
6662 if {[info exists idheads($id)]} {
6665 foreach a $arcnos($id) {
6666 if {$archeads($a) ne {}} {
6667 set ret [concat $ret $archeads($a)]
6670 if {![info exists seen($d)]} {
6677 set ret [lsort -unique $ret]
6678 set cached_dheads($origid) $ret
6681 proc addedtag {id} {
6682 global arcnos arcout cached_dtags cached_atags
6684 if {![info exists arcnos($id)]} return
6685 if {![info exists arcout($id)]} {
6686 recalcarc [lindex $arcnos($id) 0]
6688 catch {unset cached_dtags}
6689 catch {unset cached_atags}
6692 proc addedhead {hid head} {
6693 global arcnos arcout cached_dheads
6695 if {![info exists arcnos($hid)]} return
6696 if {![info exists arcout($hid)]} {
6697 recalcarc [lindex $arcnos($hid) 0]
6699 catch {unset cached_dheads}
6702 proc removedhead {hid head} {
6703 global cached_dheads
6705 catch {unset cached_dheads}
6708 proc movedhead {hid head} {
6709 global arcnos arcout cached_dheads
6711 if {![info exists arcnos($hid)]} return
6712 if {![info exists arcout($hid)]} {
6713 recalcarc [lindex $arcnos($hid) 0]
6715 catch {unset cached_dheads}
6718 proc changedrefs {} {
6719 global cached_dheads cached_dtags cached_atags
6720 global arctags archeads arcnos arcout idheads idtags
6722 foreach id [concat [array names idheads] [array names idtags]] {
6723 if {[info exists arcnos($id)] && ![info exists arcout($id)]} {
6724 set a [lindex $arcnos($id) 0]
6725 if {![info exists donearc($a)]} {
6731 catch {unset cached_dtags}
6732 catch {unset cached_atags}
6733 catch {unset cached_dheads}
6736 proc rereadrefs {} {
6737 global idtags idheads idotherrefs mainhead
6739 set refids [concat [array names idtags] \
6740 [array names idheads] [array names idotherrefs]]
6741 foreach id $refids {
6742 if {![info exists ref($id)]} {
6743 set ref($id) [listrefs $id]
6746 set oldmainhead $mainhead
6749 set refids [lsort -unique [concat $refids [array names idtags] \
6750 [array names idheads] [array names idotherrefs]]]
6751 foreach id $refids {
6752 set v [listrefs $id]
6753 if {![info exists ref($id)] || $ref($id) != $v ||
6754 ($id eq $oldmainhead && $id ne $mainhead) ||
6755 ($id eq $mainhead && $id ne $oldmainhead)} {
6761 proc listrefs {id} {
6762 global idtags idheads idotherrefs
6765 if {[info exists idtags($id)]} {
6769 if {[info exists idheads($id)]} {
6773 if {[info exists idotherrefs($id)]} {
6774 set z $idotherrefs($id)
6776 return [list $x $y $z]
6779 proc showtag {tag isnew} {
6780 global ctext tagcontents tagids linknum
6783 addtohistory [list showtag $tag 0]
6785 $ctext conf -state normal
6788 if {[info exists tagcontents($tag)]} {
6789 set text $tagcontents($tag)
6791 set text "Tag: $tag\nId: $tagids($tag)"
6793 appendwithlinks $text {}
6794 $ctext conf -state disabled
6806 global maxwidth maxgraphpct diffopts
6807 global oldprefs prefstop showneartags showlocalchanges
6808 global bgcolor fgcolor ctext diffcolors selectbgcolor
6809 global uifont tabstop
6813 if {[winfo exists $top]} {
6817 foreach v {maxwidth maxgraphpct diffopts showneartags showlocalchanges} {
6818 set oldprefs($v) [set $v]
6821 wm title $top "Gitk preferences"
6822 label $top.ldisp -text "Commit list display options"
6823 $top.ldisp configure -font $uifont
6824 grid $top.ldisp - -sticky w -pady 10
6825 label $top.spacer -text " "
6826 label $top.maxwidthl -text "Maximum graph width (lines)" \
6828 spinbox $top.maxwidth -from 0 -to 100 -width 4 -textvariable maxwidth
6829 grid $top.spacer $top.maxwidthl $top.maxwidth -sticky w
6830 label $top.maxpctl -text "Maximum graph width (% of pane)" \
6832 spinbox $top.maxpct -from 1 -to 100 -width 4 -textvariable maxgraphpct
6833 grid x $top.maxpctl $top.maxpct -sticky w
6834 frame $top.showlocal
6835 label $top.showlocal.l -text "Show local changes" -font optionfont
6836 checkbutton $top.showlocal.b -variable showlocalchanges
6837 pack $top.showlocal.b $top.showlocal.l -side left
6838 grid x $top.showlocal -sticky w
6840 label $top.ddisp -text "Diff display options"
6841 $top.ddisp configure -font $uifont
6842 grid $top.ddisp - -sticky w -pady 10
6843 label $top.diffoptl -text "Options for diff program" \
6845 entry $top.diffopt -width 20 -textvariable diffopts
6846 grid x $top.diffoptl $top.diffopt -sticky w
6848 label $top.ntag.l -text "Display nearby tags" -font optionfont
6849 checkbutton $top.ntag.b -variable showneartags
6850 pack $top.ntag.b $top.ntag.l -side left
6851 grid x $top.ntag -sticky w
6852 label $top.tabstopl -text "tabstop" -font optionfont
6853 entry $top.tabstop -width 10 -textvariable tabstop
6854 grid x $top.tabstopl $top.tabstop -sticky w
6856 label $top.cdisp -text "Colors: press to choose"
6857 $top.cdisp configure -font $uifont
6858 grid $top.cdisp - -sticky w -pady 10
6859 label $top.bg -padx 40 -relief sunk -background $bgcolor
6860 button $top.bgbut -text "Background" -font optionfont \
6861 -command [list choosecolor bgcolor 0 $top.bg background setbg]
6862 grid x $top.bgbut $top.bg -sticky w
6863 label $top.fg -padx 40 -relief sunk -background $fgcolor
6864 button $top.fgbut -text "Foreground" -font optionfont \
6865 -command [list choosecolor fgcolor 0 $top.fg foreground setfg]
6866 grid x $top.fgbut $top.fg -sticky w
6867 label $top.diffold -padx 40 -relief sunk -background [lindex $diffcolors 0]
6868 button $top.diffoldbut -text "Diff: old lines" -font optionfont \
6869 -command [list choosecolor diffcolors 0 $top.diffold "diff old lines" \
6870 [list $ctext tag conf d0 -foreground]]
6871 grid x $top.diffoldbut $top.diffold -sticky w
6872 label $top.diffnew -padx 40 -relief sunk -background [lindex $diffcolors 1]
6873 button $top.diffnewbut -text "Diff: new lines" -font optionfont \
6874 -command [list choosecolor diffcolors 1 $top.diffnew "diff new lines" \
6875 [list $ctext tag conf d1 -foreground]]
6876 grid x $top.diffnewbut $top.diffnew -sticky w
6877 label $top.hunksep -padx 40 -relief sunk -background [lindex $diffcolors 2]
6878 button $top.hunksepbut -text "Diff: hunk header" -font optionfont \
6879 -command [list choosecolor diffcolors 2 $top.hunksep \
6880 "diff hunk header" \
6881 [list $ctext tag conf hunksep -foreground]]
6882 grid x $top.hunksepbut $top.hunksep -sticky w
6883 label $top.selbgsep -padx 40 -relief sunk -background $selectbgcolor
6884 button $top.selbgbut -text "Select bg" -font optionfont \
6885 -command [list choosecolor selectbgcolor 0 $top.bg background setselbg]
6886 grid x $top.selbgbut $top.selbgsep -sticky w
6889 button $top.buts.ok -text "OK" -command prefsok -default active
6890 $top.buts.ok configure -font $uifont
6891 button $top.buts.can -text "Cancel" -command prefscan -default normal
6892 $top.buts.can configure -font $uifont
6893 grid $top.buts.ok $top.buts.can
6894 grid columnconfigure $top.buts 0 -weight 1 -uniform a
6895 grid columnconfigure $top.buts 1 -weight 1 -uniform a
6896 grid $top.buts - - -pady 10 -sticky ew
6897 bind $top <Visibility> "focus $top.buts.ok"
6900 proc choosecolor {v vi w x cmd} {
6903 set c [tk_chooseColor -initialcolor [lindex [set $v] $vi] \
6904 -title "Gitk: choose color for $x"]
6905 if {$c eq {}} return
6906 $w conf -background $c
6912 global bglist cflist
6914 $w configure -selectbackground $c
6916 $cflist tag configure highlight \
6917 -background [$cflist cget -selectbackground]
6918 allcanvs itemconf secsel -fill $c
6925 $w conf -background $c
6933 $w conf -foreground $c
6935 allcanvs itemconf text -fill $c
6936 $canv itemconf circle -outline $c
6940 global maxwidth maxgraphpct diffopts
6941 global oldprefs prefstop showneartags showlocalchanges
6943 foreach v {maxwidth maxgraphpct diffopts showneartags showlocalchanges} {
6944 set $v $oldprefs($v)
6946 catch {destroy $prefstop}
6951 global maxwidth maxgraphpct
6952 global oldprefs prefstop showneartags showlocalchanges
6953 global charspc ctext tabstop
6955 catch {destroy $prefstop}
6957 $ctext configure -tabs "[expr {$tabstop * $charspc}]"
6958 if {$showlocalchanges != $oldprefs(showlocalchanges)} {
6959 if {$showlocalchanges} {
6965 if {$maxwidth != $oldprefs(maxwidth)
6966 || $maxgraphpct != $oldprefs(maxgraphpct)} {
6968 } elseif {$showneartags != $oldprefs(showneartags)} {
6973 proc formatdate {d} {
6975 set d [clock format $d -format "%Y-%m-%d %H:%M:%S"]
6980 # This list of encoding names and aliases is distilled from
6981 # http://www.iana.org/assignments/character-sets.
6982 # Not all of them are supported by Tcl.
6983 set encoding_aliases {
6984 { ANSI_X3.4-1968 iso-ir-6 ANSI_X3.4-1986 ISO_646.irv:1991 ASCII
6985 ISO646-US US-ASCII us IBM367 cp367 csASCII }
6986 { ISO-10646-UTF-1 csISO10646UTF1 }
6987 { ISO_646.basic:1983 ref csISO646basic1983 }
6988 { INVARIANT csINVARIANT }
6989 { ISO_646.irv:1983 iso-ir-2 irv csISO2IntlRefVersion }
6990 { BS_4730 iso-ir-4 ISO646-GB gb uk csISO4UnitedKingdom }
6991 { NATS-SEFI iso-ir-8-1 csNATSSEFI }
6992 { NATS-SEFI-ADD iso-ir-8-2 csNATSSEFIADD }
6993 { NATS-DANO iso-ir-9-1 csNATSDANO }
6994 { NATS-DANO-ADD iso-ir-9-2 csNATSDANOADD }
6995 { SEN_850200_B iso-ir-10 FI ISO646-FI ISO646-SE se csISO10Swedish }
6996 { SEN_850200_C iso-ir-11 ISO646-SE2 se2 csISO11SwedishForNames }
6997 { KS_C_5601-1987 iso-ir-149 KS_C_5601-1989 KSC_5601 korean csKSC56011987 }
6998 { ISO-2022-KR csISO2022KR }
7000 { ISO-2022-JP csISO2022JP }
7001 { ISO-2022-JP-2 csISO2022JP2 }
7002 { JIS_C6220-1969-jp JIS_C6220-1969 iso-ir-13 katakana x0201-7
7004 { JIS_C6220-1969-ro iso-ir-14 jp ISO646-JP csISO14JISC6220ro }
7005 { IT iso-ir-15 ISO646-IT csISO15Italian }
7006 { PT iso-ir-16 ISO646-PT csISO16Portuguese }
7007 { ES iso-ir-17 ISO646-ES csISO17Spanish }
7008 { greek7-old iso-ir-18 csISO18Greek7Old }
7009 { latin-greek iso-ir-19 csISO19LatinGreek }
7010 { DIN_66003 iso-ir-21 de ISO646-DE csISO21German }
7011 { NF_Z_62-010_(1973) iso-ir-25 ISO646-FR1 csISO25French }
7012 { Latin-greek-1 iso-ir-27 csISO27LatinGreek1 }
7013 { ISO_5427 iso-ir-37 csISO5427Cyrillic }
7014 { JIS_C6226-1978 iso-ir-42 csISO42JISC62261978 }
7015 { BS_viewdata iso-ir-47 csISO47BSViewdata }
7016 { INIS iso-ir-49 csISO49INIS }
7017 { INIS-8 iso-ir-50 csISO50INIS8 }
7018 { INIS-cyrillic iso-ir-51 csISO51INISCyrillic }
7019 { ISO_5427:1981 iso-ir-54 ISO5427Cyrillic1981 }
7020 { ISO_5428:1980 iso-ir-55 csISO5428Greek }
7021 { GB_1988-80 iso-ir-57 cn ISO646-CN csISO57GB1988 }
7022 { GB_2312-80 iso-ir-58 chinese csISO58GB231280 }
7023 { NS_4551-1 iso-ir-60 ISO646-NO no csISO60DanishNorwegian
7025 { NS_4551-2 ISO646-NO2 iso-ir-61 no2 csISO61Norwegian2 }
7026 { NF_Z_62-010 iso-ir-69 ISO646-FR fr csISO69French }
7027 { videotex-suppl iso-ir-70 csISO70VideotexSupp1 }
7028 { PT2 iso-ir-84 ISO646-PT2 csISO84Portuguese2 }
7029 { ES2 iso-ir-85 ISO646-ES2 csISO85Spanish2 }
7030 { MSZ_7795.3 iso-ir-86 ISO646-HU hu csISO86Hungarian }
7031 { JIS_C6226-1983 iso-ir-87 x0208 JIS_X0208-1983 csISO87JISX0208 }
7032 { greek7 iso-ir-88 csISO88Greek7 }
7033 { ASMO_449 ISO_9036 arabic7 iso-ir-89 csISO89ASMO449 }
7034 { iso-ir-90 csISO90 }
7035 { JIS_C6229-1984-a iso-ir-91 jp-ocr-a csISO91JISC62291984a }
7036 { JIS_C6229-1984-b iso-ir-92 ISO646-JP-OCR-B jp-ocr-b
7037 csISO92JISC62991984b }
7038 { JIS_C6229-1984-b-add iso-ir-93 jp-ocr-b-add csISO93JIS62291984badd }
7039 { JIS_C6229-1984-hand iso-ir-94 jp-ocr-hand csISO94JIS62291984hand }
7040 { JIS_C6229-1984-hand-add iso-ir-95 jp-ocr-hand-add
7041 csISO95JIS62291984handadd }
7042 { JIS_C6229-1984-kana iso-ir-96 csISO96JISC62291984kana }
7043 { ISO_2033-1983 iso-ir-98 e13b csISO2033 }
7044 { ANSI_X3.110-1983 iso-ir-99 CSA_T500-1983 NAPLPS csISO99NAPLPS }
7045 { ISO_8859-1:1987 iso-ir-100 ISO_8859-1 ISO-8859-1 latin1 l1 IBM819
7047 { ISO_8859-2:1987 iso-ir-101 ISO_8859-2 ISO-8859-2 latin2 l2 csISOLatin2 }
7048 { T.61-7bit iso-ir-102 csISO102T617bit }
7049 { T.61-8bit T.61 iso-ir-103 csISO103T618bit }
7050 { ISO_8859-3:1988 iso-ir-109 ISO_8859-3 ISO-8859-3 latin3 l3 csISOLatin3 }
7051 { ISO_8859-4:1988 iso-ir-110 ISO_8859-4 ISO-8859-4 latin4 l4 csISOLatin4 }
7052 { ECMA-cyrillic iso-ir-111 KOI8-E csISO111ECMACyrillic }
7053 { CSA_Z243.4-1985-1 iso-ir-121 ISO646-CA csa7-1 ca csISO121Canadian1 }
7054 { CSA_Z243.4-1985-2 iso-ir-122 ISO646-CA2 csa7-2 csISO122Canadian2 }
7055 { CSA_Z243.4-1985-gr iso-ir-123 csISO123CSAZ24341985gr }
7056 { ISO_8859-6:1987 iso-ir-127 ISO_8859-6 ISO-8859-6 ECMA-114 ASMO-708
7057 arabic csISOLatinArabic }
7058 { ISO_8859-6-E csISO88596E ISO-8859-6-E }
7059 { ISO_8859-6-I csISO88596I ISO-8859-6-I }
7060 { ISO_8859-7:1987 iso-ir-126 ISO_8859-7 ISO-8859-7 ELOT_928 ECMA-118
7061 greek greek8 csISOLatinGreek }
7062 { T.101-G2 iso-ir-128 csISO128T101G2 }
7063 { ISO_8859-8:1988 iso-ir-138 ISO_8859-8 ISO-8859-8 hebrew
7065 { ISO_8859-8-E csISO88598E ISO-8859-8-E }
7066 { ISO_8859-8-I csISO88598I ISO-8859-8-I }
7067 { CSN_369103 iso-ir-139 csISO139CSN369103 }
7068 { JUS_I.B1.002 iso-ir-141 ISO646-YU js yu csISO141JUSIB1002 }
7069 { ISO_6937-2-add iso-ir-142 csISOTextComm }
7070 { IEC_P27-1 iso-ir-143 csISO143IECP271 }
7071 { ISO_8859-5:1988 iso-ir-144 ISO_8859-5 ISO-8859-5 cyrillic
7072 csISOLatinCyrillic }
7073 { JUS_I.B1.003-serb iso-ir-146 serbian csISO146Serbian }
7074 { JUS_I.B1.003-mac macedonian iso-ir-147 csISO147Macedonian }
7075 { ISO_8859-9:1989 iso-ir-148 ISO_8859-9 ISO-8859-9 latin5 l5 csISOLatin5 }
7076 { greek-ccitt iso-ir-150 csISO150 csISO150GreekCCITT }
7077 { NC_NC00-10:81 cuba iso-ir-151 ISO646-CU csISO151Cuba }
7078 { ISO_6937-2-25 iso-ir-152 csISO6937Add }
7079 { GOST_19768-74 ST_SEV_358-88 iso-ir-153 csISO153GOST1976874 }
7080 { ISO_8859-supp iso-ir-154 latin1-2-5 csISO8859Supp }
7081 { ISO_10367-box iso-ir-155 csISO10367Box }
7082 { ISO-8859-10 iso-ir-157 l6 ISO_8859-10:1992 csISOLatin6 latin6 }
7083 { latin-lap lap iso-ir-158 csISO158Lap }
7084 { JIS_X0212-1990 x0212 iso-ir-159 csISO159JISX02121990 }
7085 { DS_2089 DS2089 ISO646-DK dk csISO646Danish }
7088 { JIS_X0201 X0201 csHalfWidthKatakana }
7089 { KSC5636 ISO646-KR csKSC5636 }
7090 { ISO-10646-UCS-2 csUnicode }
7091 { ISO-10646-UCS-4 csUCS4 }
7092 { DEC-MCS dec csDECMCS }
7093 { hp-roman8 roman8 r8 csHPRoman8 }
7094 { macintosh mac csMacintosh }
7095 { IBM037 cp037 ebcdic-cp-us ebcdic-cp-ca ebcdic-cp-wt ebcdic-cp-nl
7097 { IBM038 EBCDIC-INT cp038 csIBM038 }
7098 { IBM273 CP273 csIBM273 }
7099 { IBM274 EBCDIC-BE CP274 csIBM274 }
7100 { IBM275 EBCDIC-BR cp275 csIBM275 }
7101 { IBM277 EBCDIC-CP-DK EBCDIC-CP-NO csIBM277 }
7102 { IBM278 CP278 ebcdic-cp-fi ebcdic-cp-se csIBM278 }
7103 { IBM280 CP280 ebcdic-cp-it csIBM280 }
7104 { IBM281 EBCDIC-JP-E cp281 csIBM281 }
7105 { IBM284 CP284 ebcdic-cp-es csIBM284 }
7106 { IBM285 CP285 ebcdic-cp-gb csIBM285 }
7107 { IBM290 cp290 EBCDIC-JP-kana csIBM290 }
7108 { IBM297 cp297 ebcdic-cp-fr csIBM297 }
7109 { IBM420 cp420 ebcdic-cp-ar1 csIBM420 }
7110 { IBM423 cp423 ebcdic-cp-gr csIBM423 }
7111 { IBM424 cp424 ebcdic-cp-he csIBM424 }
7112 { IBM437 cp437 437 csPC8CodePage437 }
7113 { IBM500 CP500 ebcdic-cp-be ebcdic-cp-ch csIBM500 }
7114 { IBM775 cp775 csPC775Baltic }
7115 { IBM850 cp850 850 csPC850Multilingual }
7116 { IBM851 cp851 851 csIBM851 }
7117 { IBM852 cp852 852 csPCp852 }
7118 { IBM855 cp855 855 csIBM855 }
7119 { IBM857 cp857 857 csIBM857 }
7120 { IBM860 cp860 860 csIBM860 }
7121 { IBM861 cp861 861 cp-is csIBM861 }
7122 { IBM862 cp862 862 csPC862LatinHebrew }
7123 { IBM863 cp863 863 csIBM863 }
7124 { IBM864 cp864 csIBM864 }
7125 { IBM865 cp865 865 csIBM865 }
7126 { IBM866 cp866 866 csIBM866 }
7127 { IBM868 CP868 cp-ar csIBM868 }
7128 { IBM869 cp869 869 cp-gr csIBM869 }
7129 { IBM870 CP870 ebcdic-cp-roece ebcdic-cp-yu csIBM870 }
7130 { IBM871 CP871 ebcdic-cp-is csIBM871 }
7131 { IBM880 cp880 EBCDIC-Cyrillic csIBM880 }
7132 { IBM891 cp891 csIBM891 }
7133 { IBM903 cp903 csIBM903 }
7134 { IBM904 cp904 904 csIBBM904 }
7135 { IBM905 CP905 ebcdic-cp-tr csIBM905 }
7136 { IBM918 CP918 ebcdic-cp-ar2 csIBM918 }
7137 { IBM1026 CP1026 csIBM1026 }
7138 { EBCDIC-AT-DE csIBMEBCDICATDE }
7139 { EBCDIC-AT-DE-A csEBCDICATDEA }
7140 { EBCDIC-CA-FR csEBCDICCAFR }
7141 { EBCDIC-DK-NO csEBCDICDKNO }
7142 { EBCDIC-DK-NO-A csEBCDICDKNOA }
7143 { EBCDIC-FI-SE csEBCDICFISE }
7144 { EBCDIC-FI-SE-A csEBCDICFISEA }
7145 { EBCDIC-FR csEBCDICFR }
7146 { EBCDIC-IT csEBCDICIT }
7147 { EBCDIC-PT csEBCDICPT }
7148 { EBCDIC-ES csEBCDICES }
7149 { EBCDIC-ES-A csEBCDICESA }
7150 { EBCDIC-ES-S csEBCDICESS }
7151 { EBCDIC-UK csEBCDICUK }
7152 { EBCDIC-US csEBCDICUS }
7153 { UNKNOWN-8BIT csUnknown8BiT }
7154 { MNEMONIC csMnemonic }
7159 { IBM00858 CCSID00858 CP00858 PC-Multilingual-850+euro }
7160 { IBM00924 CCSID00924 CP00924 ebcdic-Latin9--euro }
7161 { IBM01140 CCSID01140 CP01140 ebcdic-us-37+euro }
7162 { IBM01141 CCSID01141 CP01141 ebcdic-de-273+euro }
7163 { IBM01142 CCSID01142 CP01142 ebcdic-dk-277+euro ebcdic-no-277+euro }
7164 { IBM01143 CCSID01143 CP01143 ebcdic-fi-278+euro ebcdic-se-278+euro }
7165 { IBM01144 CCSID01144 CP01144 ebcdic-it-280+euro }
7166 { IBM01145 CCSID01145 CP01145 ebcdic-es-284+euro }
7167 { IBM01146 CCSID01146 CP01146 ebcdic-gb-285+euro }
7168 { IBM01147 CCSID01147 CP01147 ebcdic-fr-297+euro }
7169 { IBM01148 CCSID01148 CP01148 ebcdic-international-500+euro }
7170 { IBM01149 CCSID01149 CP01149 ebcdic-is-871+euro }
7171 { IBM1047 IBM-1047 }
7172 { PTCP154 csPTCP154 PT154 CP154 Cyrillic-Asian }
7173 { Amiga-1251 Ami1251 Amiga1251 Ami-1251 }
7174 { UNICODE-1-1 csUnicode11 }
7177 { UNICODE-1-1-UTF-7 csUnicode11UTF7 }
7178 { ISO-8859-14 iso-ir-199 ISO_8859-14:1998 ISO_8859-14 latin8 iso-celtic
7180 { ISO-8859-15 ISO_8859-15 Latin-9 }
7181 { ISO-8859-16 iso-ir-226 ISO_8859-16:2001 ISO_8859-16 latin10 l10 }
7182 { GBK CP936 MS936 windows-936 }
7183 { JIS_Encoding csJISEncoding }
7184 { Shift_JIS MS_Kanji csShiftJIS }
7185 { Extended_UNIX_Code_Packed_Format_for_Japanese csEUCPkdFmtJapanese
7187 { Extended_UNIX_Code_Fixed_Width_for_Japanese csEUCFixWidJapanese }
7188 { ISO-10646-UCS-Basic csUnicodeASCII }
7189 { ISO-10646-Unicode-Latin1 csUnicodeLatin1 ISO-10646 }
7190 { ISO-Unicode-IBM-1261 csUnicodeIBM1261 }
7191 { ISO-Unicode-IBM-1268 csUnicodeIBM1268 }
7192 { ISO-Unicode-IBM-1276 csUnicodeIBM1276 }
7193 { ISO-Unicode-IBM-1264 csUnicodeIBM1264 }
7194 { ISO-Unicode-IBM-1265 csUnicodeIBM1265 }
7195 { ISO-8859-1-Windows-3.0-Latin-1 csWindows30Latin1 }
7196 { ISO-8859-1-Windows-3.1-Latin-1 csWindows31Latin1 }
7197 { ISO-8859-2-Windows-Latin-2 csWindows31Latin2 }
7198 { ISO-8859-9-Windows-Latin-5 csWindows31Latin5 }
7199 { Adobe-Standard-Encoding csAdobeStandardEncoding }
7200 { Ventura-US csVenturaUS }
7201 { Ventura-International csVenturaInternational }
7202 { PC8-Danish-Norwegian csPC8DanishNorwegian }
7203 { PC8-Turkish csPC8Turkish }
7204 { IBM-Symbols csIBMSymbols }
7205 { IBM-Thai csIBMThai }
7206 { HP-Legal csHPLegal }
7207 { HP-Pi-font csHPPiFont }
7208 { HP-Math8 csHPMath8 }
7209 { Adobe-Symbol-Encoding csHPPSMath }
7210 { HP-DeskTop csHPDesktop }
7211 { Ventura-Math csVenturaMath }
7212 { Microsoft-Publishing csMicrosoftPublishing }
7213 { Windows-31J csWindows31J }
7218 proc tcl_encoding {enc} {
7219 global encoding_aliases
7220 set names [encoding names]
7221 set lcnames [string tolower $names]
7222 set enc [string tolower $enc]
7223 set i [lsearch -exact $lcnames $enc]
7225 # look for "isonnn" instead of "iso-nnn" or "iso_nnn"
7226 if {[regsub {^iso[-_]} $enc iso encx]} {
7227 set i [lsearch -exact $lcnames $encx]
7231 foreach l $encoding_aliases {
7232 set ll [string tolower $l]
7233 if {[lsearch -exact $ll $enc] < 0} continue
7234 # look through the aliases for one that tcl knows about
7236 set i [lsearch -exact $lcnames $e]
7238 if {[regsub {^iso[-_]} $e iso ex]} {
7239 set i [lsearch -exact $lcnames $ex]
7248 return [lindex $names $i]
7255 set diffopts "-U 5 -p"
7256 set wrcomcmd "git diff-tree --stdin -p --pretty"
7260 set gitencoding [exec git config --get i18n.commitencoding]
7262 if {$gitencoding == ""} {
7263 set gitencoding "utf-8"
7265 set tclencoding [tcl_encoding $gitencoding]
7266 if {$tclencoding == {}} {
7267 puts stderr "Warning: encoding $gitencoding is not supported by Tcl/Tk"
7270 set mainfont {Helvetica 9}
7271 set textfont {Courier 9}
7272 set uifont {Helvetica 9 bold}
7274 set findmergefiles 0
7282 set cmitmode "patch"
7283 set wrapcomment "none"
7287 set showlocalchanges 1
7289 set colors {green red blue magenta darkgrey brown orange}
7292 set diffcolors {red "#00a000" blue}
7293 set selectbgcolor gray85
7295 catch {source ~/.gitk}
7297 font create optionfont -family sans-serif -size -12
7301 switch -regexp -- $arg {
7303 "^-d" { set datemode 1 }
7305 lappend revtreeargs $arg
7310 # check that we can find a .git directory somewhere...
7312 if {![file isdirectory $gitdir]} {
7313 show_error {} . "Cannot find the git directory \"$gitdir\"."
7317 set cmdline_files {}
7318 set i [lsearch -exact $revtreeargs "--"]
7320 set cmdline_files [lrange $revtreeargs [expr {$i + 1}] end]
7321 set revtreeargs [lrange $revtreeargs 0 [expr {$i - 1}]]
7322 } elseif {$revtreeargs ne {}} {
7324 set f [eval exec git rev-parse --no-revs --no-flags $revtreeargs]
7325 set cmdline_files [split $f "\n"]
7326 set n [llength $cmdline_files]
7327 set revtreeargs [lrange $revtreeargs 0 end-$n]
7329 # unfortunately we get both stdout and stderr in $err,
7330 # so look for "fatal:".
7331 set i [string first "fatal:" $err]
7333 set err [string range $err [expr {$i + 6}] end]
7335 show_error {} . "Bad arguments to gitk:\n$err"
7340 set nullid "0000000000000000000000000000000000000000"
7347 set highlight_paths {}
7348 set searchdirn -forwards
7358 set selectedhlview None
7367 set lookingforhead 0
7372 wm title . "[file tail $argv0]: [file tail [pwd]]"
7375 if {$cmdline_files ne {} || $revtreeargs ne {}} {
7376 # create a view for the files/dirs specified on the command line
7380 set viewname(1) "Command line"
7381 set viewfiles(1) $cmdline_files
7382 set viewargs(1) $revtreeargs
7385 .bar.view entryconf Edit* -state normal
7386 .bar.view entryconf Delete* -state normal
7389 if {[info exists permviews]} {
7390 foreach v $permviews {
7393 set viewname($n) [lindex $v 0]
7394 set viewfiles($n) [lindex $v 1]
7395 set viewargs($n) [lindex $v 2]