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
87 set startmsecs [clock clicks -milliseconds]
88 set commitidx($view) 0
89 set args $viewargs($view)
90 if {$viewfiles($view) ne {}} {
91 set args [concat $args "--" $viewfiles($view)]
93 set order "--topo-order"
95 set order "--date-order"
98 set fd [open [concat | git rev-list --header $order \
99 --parents --boundary --default HEAD $args] r]
101 puts stderr "Error executing git rev-list: $err"
104 set commfd($view) $fd
105 set leftover($view) {}
106 fconfigure $fd -blocking 0 -translation lf
107 if {$tclencoding != {}} {
108 fconfigure $fd -encoding $tclencoding
110 filerun $fd [list getcommitlines $fd $view]
114 proc stop_rev_list {} {
115 global commfd curview
117 if {![info exists commfd($curview)]} return
118 set fd $commfd($curview)
124 unset commfd($curview)
128 global phase canv mainfont curview
132 start_rev_list $curview
133 show_status "Reading commits..."
136 proc getcommitlines {fd view} {
138 global leftover commfd
139 global displayorder commitidx commitrow commitdata
140 global parentlist childlist children curview hlview
141 global vparentlist vchildlist vdisporder vcmitlisted
143 set stuff [read $fd 500000]
151 # set it blocking so we wait for the process to terminate
152 fconfigure $fd -blocking 1
153 if {[catch {close $fd} err]} {
155 if {$view != $curview} {
156 set fv " for the \"$viewname($view)\" view"
158 if {[string range $err 0 4] == "usage"} {
159 set err "Gitk: error reading commits$fv:\
160 bad arguments to git rev-list."
161 if {$viewname($view) eq "Command line"} {
163 " (Note: arguments to gitk are passed to git rev-list\
164 to allow selection of commits to be displayed.)"
167 set err "Error reading commits$fv: $err"
171 if {$view == $curview} {
172 run chewcommits $view
179 set i [string first "\0" $stuff $start]
181 append leftover($view) [string range $stuff $start end]
185 set cmit $leftover($view)
186 append cmit [string range $stuff 0 [expr {$i - 1}]]
187 set leftover($view) {}
189 set cmit [string range $stuff $start [expr {$i - 1}]]
191 set start [expr {$i + 1}]
192 set j [string first "\n" $cmit]
196 set ids [string range $cmit 0 [expr {$j - 1}]]
197 if {[string range $ids 0 0] == "-"} {
199 set ids [string range $ids 1 end]
203 if {[string length $id] != 40} {
211 if {[string length $shortcmit] > 80} {
212 set shortcmit "[string range $shortcmit 0 80]..."
214 error_popup "Can't parse git rev-list output: {$shortcmit}"
217 set id [lindex $ids 0]
219 set olds [lrange $ids 1 end]
222 if {$i == 0 || [lsearch -exact $olds $p] >= $i} {
223 lappend children($view,$p) $id
230 if {![info exists children($view,$id)]} {
231 set children($view,$id) {}
233 set commitdata($id) [string range $cmit [expr {$j + 1}] end]
234 set commitrow($view,$id) $commitidx($view)
235 incr commitidx($view)
236 if {$view == $curview} {
237 lappend parentlist $olds
238 lappend childlist $children($view,$id)
239 lappend displayorder $id
240 lappend commitlisted $listed
242 lappend vparentlist($view) $olds
243 lappend vchildlist($view) $children($view,$id)
244 lappend vdisporder($view) $id
245 lappend vcmitlisted($view) $listed
250 run chewcommits $view
255 proc chewcommits {view} {
256 global curview hlview commfd
257 global selectedline pending_select
260 if {$view == $curview} {
261 set allread [expr {![info exists commfd($view)]}]
262 set tlimit [expr {[clock clicks -milliseconds] + 50}]
263 set more [layoutmore $tlimit $allread]
264 if {$allread && !$more} {
265 global displayorder commitidx phase
266 global numcommits startmsecs
268 if {[info exists pending_select]} {
269 set row [expr {[lindex $displayorder 0] eq $nullid}]
272 if {$commitidx($curview) > 0} {
273 #set ms [expr {[clock clicks -milliseconds] - $startmsecs}]
274 #puts "overall $ms ms for $numcommits commits"
276 show_status "No commits selected"
282 if {[info exists hlview] && $view == $hlview} {
288 proc readcommit {id} {
289 if {[catch {set contents [exec git cat-file commit $id]}]} return
290 parsecommit $id $contents 0
293 proc updatecommits {} {
294 global viewdata curview phase displayorder
295 global children commitrow selectedline thickerline
302 foreach id $displayorder {
303 catch {unset children($n,$id)}
304 catch {unset commitrow($n,$id)}
307 catch {unset selectedline}
308 catch {unset thickerline}
309 catch {unset viewdata($n)}
316 proc parsecommit {id contents listed} {
317 global commitinfo cdate
326 set hdrend [string first "\n\n" $contents]
328 # should never happen...
329 set hdrend [string length $contents]
331 set header [string range $contents 0 [expr {$hdrend - 1}]]
332 set comment [string range $contents [expr {$hdrend + 2}] end]
333 foreach line [split $header "\n"] {
334 set tag [lindex $line 0]
335 if {$tag == "author"} {
336 set audate [lindex $line end-1]
337 set auname [lrange $line 1 end-2]
338 } elseif {$tag == "committer"} {
339 set comdate [lindex $line end-1]
340 set comname [lrange $line 1 end-2]
344 # take the first line of the comment as the headline
345 set i [string first "\n" $comment]
347 set headline [string trim [string range $comment 0 $i]]
349 set headline $comment
352 # git rev-list indents the comment by 4 spaces;
353 # if we got this via git cat-file, add the indentation
355 foreach line [split $comment "\n"] {
356 append newcomment " "
357 append newcomment $line
358 append newcomment "\n"
360 set comment $newcomment
362 if {$comdate != {}} {
363 set cdate($id) $comdate
365 set commitinfo($id) [list $headline $auname $audate \
366 $comname $comdate $comment]
369 proc getcommit {id} {
370 global commitdata commitinfo
372 if {[info exists commitdata($id)]} {
373 parsecommit $id $commitdata($id) 1
376 if {![info exists commitinfo($id)]} {
377 set commitinfo($id) {"No commit information available"}
384 global tagids idtags headids idheads tagcontents
385 global otherrefids idotherrefs mainhead
387 foreach v {tagids idtags headids idheads otherrefids idotherrefs} {
390 set refd [open [list | git show-ref] r]
391 while {0 <= [set n [gets $refd line]]} {
392 if {![regexp {^([0-9a-f]{40}) refs/([^^]*)$} $line \
396 if {[regexp {^remotes/.*/HEAD$} $path match]} {
399 if {![regexp {^(tags|heads)/(.*)$} $path match type name]} {
403 if {[regexp {^remotes/} $path match]} {
406 if {$type == "tags"} {
407 set tagids($name) $id
408 lappend idtags($id) $name
413 set commit [exec git rev-parse "$id^0"]
414 if {$commit != $id} {
415 set tagids($name) $commit
416 lappend idtags($commit) $name
420 set tagcontents($name) [exec git cat-file tag $id]
422 } elseif { $type == "heads" } {
423 set headids($name) $id
424 lappend idheads($id) $name
426 set otherrefids($name) $id
427 lappend idotherrefs($id) $name
433 set thehead [exec git symbolic-ref HEAD]
434 if {[string match "refs/heads/*" $thehead]} {
435 set mainhead [string range $thehead 11 end]
440 # update things for a head moved to a child of its previous location
441 proc movehead {id name} {
442 global headids idheads
444 removehead $headids($name) $name
445 set headids($name) $id
446 lappend idheads($id) $name
449 # update things when a head has been removed
450 proc removehead {id name} {
451 global headids idheads
453 if {$idheads($id) eq $name} {
456 set i [lsearch -exact $idheads($id) $name]
458 set idheads($id) [lreplace $idheads($id) $i $i]
464 proc show_error {w top msg} {
465 message $w.m -text $msg -justify center -aspect 400
466 pack $w.m -side top -fill x -padx 20 -pady 20
467 button $w.ok -text OK -command "destroy $top"
468 pack $w.ok -side bottom -fill x
469 bind $top <Visibility> "grab $top; focus $top"
470 bind $top <Key-Return> "destroy $top"
474 proc error_popup msg {
478 show_error $w $w $msg
481 proc confirm_popup msg {
487 message $w.m -text $msg -justify center -aspect 400
488 pack $w.m -side top -fill x -padx 20 -pady 20
489 button $w.ok -text OK -command "set confirm_ok 1; destroy $w"
490 pack $w.ok -side left -fill x
491 button $w.cancel -text Cancel -command "destroy $w"
492 pack $w.cancel -side right -fill x
493 bind $w <Visibility> "grab $w; focus $w"
499 global canv canv2 canv3 linespc charspc ctext cflist
500 global textfont mainfont uifont tabstop
501 global findtype findtypemenu findloc findstring fstring geometry
502 global entries sha1entry sha1string sha1but
503 global maincursor textcursor curtextcursor
504 global rowctxmenu mergemax wrapcomment
505 global highlight_files gdttype
506 global searchstring sstring
507 global bgcolor fgcolor bglist fglist diffcolors selectbgcolor
511 .bar add cascade -label "File" -menu .bar.file
512 .bar configure -font $uifont
514 .bar.file add command -label "Update" -command updatecommits
515 .bar.file add command -label "Reread references" -command rereadrefs
516 .bar.file add command -label "Quit" -command doquit
517 .bar.file configure -font $uifont
519 .bar add cascade -label "Edit" -menu .bar.edit
520 .bar.edit add command -label "Preferences" -command doprefs
521 .bar.edit configure -font $uifont
523 menu .bar.view -font $uifont
524 .bar add cascade -label "View" -menu .bar.view
525 .bar.view add command -label "New view..." -command {newview 0}
526 .bar.view add command -label "Edit view..." -command editview \
528 .bar.view add command -label "Delete view" -command delview -state disabled
529 .bar.view add separator
530 .bar.view add radiobutton -label "All files" -command {showview 0} \
531 -variable selectedview -value 0
534 .bar add cascade -label "Help" -menu .bar.help
535 .bar.help add command -label "About gitk" -command about
536 .bar.help add command -label "Key bindings" -command keys
537 .bar.help configure -font $uifont
538 . configure -menu .bar
540 # the gui has upper and lower half, parts of a paned window.
541 panedwindow .ctop -orient vertical
543 # possibly use assumed geometry
544 if {![info exists geometry(pwsash0)]} {
545 set geometry(topheight) [expr {15 * $linespc}]
546 set geometry(topwidth) [expr {80 * $charspc}]
547 set geometry(botheight) [expr {15 * $linespc}]
548 set geometry(botwidth) [expr {50 * $charspc}]
549 set geometry(pwsash0) "[expr {40 * $charspc}] 2"
550 set geometry(pwsash1) "[expr {60 * $charspc}] 2"
553 # the upper half will have a paned window, a scroll bar to the right, and some stuff below
554 frame .tf -height $geometry(topheight) -width $geometry(topwidth)
556 panedwindow .tf.histframe.pwclist -orient horizontal -sashpad 0 -handlesize 4
558 # create three canvases
559 set cscroll .tf.histframe.csb
560 set canv .tf.histframe.pwclist.canv
562 -selectbackground $selectbgcolor \
563 -background $bgcolor -bd 0 \
564 -yscrollincr $linespc -yscrollcommand "scrollcanv $cscroll"
565 .tf.histframe.pwclist add $canv
566 set canv2 .tf.histframe.pwclist.canv2
568 -selectbackground $selectbgcolor \
569 -background $bgcolor -bd 0 -yscrollincr $linespc
570 .tf.histframe.pwclist add $canv2
571 set canv3 .tf.histframe.pwclist.canv3
573 -selectbackground $selectbgcolor \
574 -background $bgcolor -bd 0 -yscrollincr $linespc
575 .tf.histframe.pwclist add $canv3
576 eval .tf.histframe.pwclist sash place 0 $geometry(pwsash0)
577 eval .tf.histframe.pwclist sash place 1 $geometry(pwsash1)
579 # a scroll bar to rule them
580 scrollbar $cscroll -command {allcanvs yview} -highlightthickness 0
581 pack $cscroll -side right -fill y
582 bind .tf.histframe.pwclist <Configure> {resizeclistpanes %W %w}
583 lappend bglist $canv $canv2 $canv3
584 pack .tf.histframe.pwclist -fill both -expand 1 -side left
586 # we have two button bars at bottom of top frame. Bar 1
588 frame .tf.lbar -height 15
590 set sha1entry .tf.bar.sha1
591 set entries $sha1entry
592 set sha1but .tf.bar.sha1label
593 button $sha1but -text "SHA1 ID: " -state disabled -relief flat \
594 -command gotocommit -width 8 -font $uifont
595 $sha1but conf -disabledforeground [$sha1but cget -foreground]
596 pack .tf.bar.sha1label -side left
597 entry $sha1entry -width 40 -font $textfont -textvariable sha1string
598 trace add variable sha1string write sha1change
599 pack $sha1entry -side left -pady 2
601 image create bitmap bm-left -data {
602 #define left_width 16
603 #define left_height 16
604 static unsigned char left_bits[] = {
605 0x00, 0x00, 0xc0, 0x01, 0xe0, 0x00, 0x70, 0x00, 0x38, 0x00, 0x1c, 0x00,
606 0x0e, 0x00, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0x0e, 0x00, 0x1c, 0x00,
607 0x38, 0x00, 0x70, 0x00, 0xe0, 0x00, 0xc0, 0x01};
609 image create bitmap bm-right -data {
610 #define right_width 16
611 #define right_height 16
612 static unsigned char right_bits[] = {
613 0x00, 0x00, 0xc0, 0x01, 0x80, 0x03, 0x00, 0x07, 0x00, 0x0e, 0x00, 0x1c,
614 0x00, 0x38, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0x00, 0x38, 0x00, 0x1c,
615 0x00, 0x0e, 0x00, 0x07, 0x80, 0x03, 0xc0, 0x01};
617 button .tf.bar.leftbut -image bm-left -command goback \
618 -state disabled -width 26
619 pack .tf.bar.leftbut -side left -fill y
620 button .tf.bar.rightbut -image bm-right -command goforw \
621 -state disabled -width 26
622 pack .tf.bar.rightbut -side left -fill y
624 button .tf.bar.findbut -text "Find" -command dofind -font $uifont
625 pack .tf.bar.findbut -side left
627 set fstring .tf.bar.findstring
628 lappend entries $fstring
629 entry $fstring -width 30 -font $textfont -textvariable findstring
630 trace add variable findstring write find_change
631 pack $fstring -side left -expand 1 -fill x -in .tf.bar
633 set findtypemenu [tk_optionMenu .tf.bar.findtype \
634 findtype Exact IgnCase Regexp]
635 trace add variable findtype write find_change
636 .tf.bar.findtype configure -font $uifont
637 .tf.bar.findtype.menu configure -font $uifont
638 set findloc "All fields"
639 tk_optionMenu .tf.bar.findloc findloc "All fields" Headline \
640 Comments Author Committer
641 trace add variable findloc write find_change
642 .tf.bar.findloc configure -font $uifont
643 .tf.bar.findloc.menu configure -font $uifont
644 pack .tf.bar.findloc -side right
645 pack .tf.bar.findtype -side right
647 # build up the bottom bar of upper window
648 label .tf.lbar.flabel -text "Highlight: Commits " \
650 pack .tf.lbar.flabel -side left -fill y
651 set gdttype "touching paths:"
652 set gm [tk_optionMenu .tf.lbar.gdttype gdttype "touching paths:" \
653 "adding/removing string:"]
654 trace add variable gdttype write hfiles_change
655 $gm conf -font $uifont
656 .tf.lbar.gdttype conf -font $uifont
657 pack .tf.lbar.gdttype -side left -fill y
658 entry .tf.lbar.fent -width 25 -font $textfont \
659 -textvariable highlight_files
660 trace add variable highlight_files write hfiles_change
661 lappend entries .tf.lbar.fent
662 pack .tf.lbar.fent -side left -fill x -expand 1
663 label .tf.lbar.vlabel -text " OR in view" -font $uifont
664 pack .tf.lbar.vlabel -side left -fill y
665 global viewhlmenu selectedhlview
666 set viewhlmenu [tk_optionMenu .tf.lbar.vhl selectedhlview None]
667 $viewhlmenu entryconf None -command delvhighlight
668 $viewhlmenu conf -font $uifont
669 .tf.lbar.vhl conf -font $uifont
670 pack .tf.lbar.vhl -side left -fill y
671 label .tf.lbar.rlabel -text " OR " -font $uifont
672 pack .tf.lbar.rlabel -side left -fill y
673 global highlight_related
674 set m [tk_optionMenu .tf.lbar.relm highlight_related None \
675 "Descendent" "Not descendent" "Ancestor" "Not ancestor"]
676 $m conf -font $uifont
677 .tf.lbar.relm conf -font $uifont
678 trace add variable highlight_related write vrel_change
679 pack .tf.lbar.relm -side left -fill y
681 # Finish putting the upper half of the viewer together
682 pack .tf.lbar -in .tf -side bottom -fill x
683 pack .tf.bar -in .tf -side bottom -fill x
684 pack .tf.histframe -fill both -side top -expand 1
686 .ctop paneconfigure .tf -height $geometry(topheight)
687 .ctop paneconfigure .tf -width $geometry(topwidth)
689 # now build up the bottom
690 panedwindow .pwbottom -orient horizontal
692 # lower left, a text box over search bar, scroll bar to the right
693 # if we know window height, then that will set the lower text height, otherwise
694 # we set lower text height which will drive window height
695 if {[info exists geometry(main)]} {
696 frame .bleft -width $geometry(botwidth)
698 frame .bleft -width $geometry(botwidth) -height $geometry(botheight)
703 button .bleft.top.search -text "Search" -command dosearch \
705 pack .bleft.top.search -side left -padx 5
706 set sstring .bleft.top.sstring
707 entry $sstring -width 20 -font $textfont -textvariable searchstring
708 lappend entries $sstring
709 trace add variable searchstring write incrsearch
710 pack $sstring -side left -expand 1 -fill x
711 radiobutton .bleft.mid.diff -text "Diff" \
712 -command changediffdisp -variable diffelide -value {0 0}
713 radiobutton .bleft.mid.old -text "Old version" \
714 -command changediffdisp -variable diffelide -value {0 1}
715 radiobutton .bleft.mid.new -text "New version" \
716 -command changediffdisp -variable diffelide -value {1 0}
717 pack .bleft.mid.diff .bleft.mid.old .bleft.mid.new -side left
718 set ctext .bleft.ctext
719 text $ctext -background $bgcolor -foreground $fgcolor \
720 -tabs "[expr {$tabstop * $charspc}]" \
721 -state disabled -font $textfont \
722 -yscrollcommand scrolltext -wrap none
723 scrollbar .bleft.sb -command "$ctext yview"
724 pack .bleft.top -side top -fill x
725 pack .bleft.mid -side top -fill x
726 pack .bleft.sb -side right -fill y
727 pack $ctext -side left -fill both -expand 1
728 lappend bglist $ctext
729 lappend fglist $ctext
731 $ctext tag conf comment -wrap $wrapcomment
732 $ctext tag conf filesep -font [concat $textfont bold] -back "#aaaaaa"
733 $ctext tag conf hunksep -fore [lindex $diffcolors 2]
734 $ctext tag conf d0 -fore [lindex $diffcolors 0]
735 $ctext tag conf d1 -fore [lindex $diffcolors 1]
736 $ctext tag conf m0 -fore red
737 $ctext tag conf m1 -fore blue
738 $ctext tag conf m2 -fore green
739 $ctext tag conf m3 -fore purple
740 $ctext tag conf m4 -fore brown
741 $ctext tag conf m5 -fore "#009090"
742 $ctext tag conf m6 -fore magenta
743 $ctext tag conf m7 -fore "#808000"
744 $ctext tag conf m8 -fore "#009000"
745 $ctext tag conf m9 -fore "#ff0080"
746 $ctext tag conf m10 -fore cyan
747 $ctext tag conf m11 -fore "#b07070"
748 $ctext tag conf m12 -fore "#70b0f0"
749 $ctext tag conf m13 -fore "#70f0b0"
750 $ctext tag conf m14 -fore "#f0b070"
751 $ctext tag conf m15 -fore "#ff70b0"
752 $ctext tag conf mmax -fore darkgrey
754 $ctext tag conf mresult -font [concat $textfont bold]
755 $ctext tag conf msep -font [concat $textfont bold]
756 $ctext tag conf found -back yellow
759 .pwbottom paneconfigure .bleft -width $geometry(botwidth)
764 radiobutton .bright.mode.patch -text "Patch" \
765 -command reselectline -variable cmitmode -value "patch"
766 .bright.mode.patch configure -font $uifont
767 radiobutton .bright.mode.tree -text "Tree" \
768 -command reselectline -variable cmitmode -value "tree"
769 .bright.mode.tree configure -font $uifont
770 grid .bright.mode.patch .bright.mode.tree -sticky ew
771 pack .bright.mode -side top -fill x
772 set cflist .bright.cfiles
773 set indent [font measure $mainfont "nn"]
775 -selectbackground $selectbgcolor \
776 -background $bgcolor -foreground $fgcolor \
778 -tabs [list $indent [expr {2 * $indent}]] \
779 -yscrollcommand ".bright.sb set" \
780 -cursor [. cget -cursor] \
781 -spacing1 1 -spacing3 1
782 lappend bglist $cflist
783 lappend fglist $cflist
784 scrollbar .bright.sb -command "$cflist yview"
785 pack .bright.sb -side right -fill y
786 pack $cflist -side left -fill both -expand 1
787 $cflist tag configure highlight \
788 -background [$cflist cget -selectbackground]
789 $cflist tag configure bold -font [concat $mainfont bold]
791 .pwbottom add .bright
794 # restore window position if known
795 if {[info exists geometry(main)]} {
796 wm geometry . "$geometry(main)"
799 bind .pwbottom <Configure> {resizecdetpanes %W %w}
800 pack .ctop -fill both -expand 1
801 bindall <1> {selcanvline %W %x %y}
802 #bindall <B1-Motion> {selcanvline %W %x %y}
803 bindall <ButtonRelease-4> "allcanvs yview scroll -5 units"
804 bindall <ButtonRelease-5> "allcanvs yview scroll 5 units"
805 bindall <2> "canvscan mark %W %x %y"
806 bindall <B2-Motion> "canvscan dragto %W %x %y"
807 bindkey <Home> selfirstline
808 bindkey <End> sellastline
809 bind . <Key-Up> "selnextline -1"
810 bind . <Key-Down> "selnextline 1"
811 bind . <Shift-Key-Up> "next_highlight -1"
812 bind . <Shift-Key-Down> "next_highlight 1"
813 bindkey <Key-Right> "goforw"
814 bindkey <Key-Left> "goback"
815 bind . <Key-Prior> "selnextpage -1"
816 bind . <Key-Next> "selnextpage 1"
817 bind . <Control-Home> "allcanvs yview moveto 0.0"
818 bind . <Control-End> "allcanvs yview moveto 1.0"
819 bind . <Control-Key-Up> "allcanvs yview scroll -1 units"
820 bind . <Control-Key-Down> "allcanvs yview scroll 1 units"
821 bind . <Control-Key-Prior> "allcanvs yview scroll -1 pages"
822 bind . <Control-Key-Next> "allcanvs yview scroll 1 pages"
823 bindkey <Key-Delete> "$ctext yview scroll -1 pages"
824 bindkey <Key-BackSpace> "$ctext yview scroll -1 pages"
825 bindkey <Key-space> "$ctext yview scroll 1 pages"
826 bindkey p "selnextline -1"
827 bindkey n "selnextline 1"
830 bindkey i "selnextline -1"
831 bindkey k "selnextline 1"
834 bindkey b "$ctext yview scroll -1 pages"
835 bindkey d "$ctext yview scroll 18 units"
836 bindkey u "$ctext yview scroll -18 units"
837 bindkey / {findnext 1}
838 bindkey <Key-Return> {findnext 0}
841 bindkey <F5> updatecommits
842 bind . <Control-q> doquit
843 bind . <Control-f> dofind
844 bind . <Control-g> {findnext 0}
845 bind . <Control-r> dosearchback
846 bind . <Control-s> dosearch
847 bind . <Control-equal> {incrfont 1}
848 bind . <Control-KP_Add> {incrfont 1}
849 bind . <Control-minus> {incrfont -1}
850 bind . <Control-KP_Subtract> {incrfont -1}
851 wm protocol . WM_DELETE_WINDOW doquit
852 bind . <Button-1> "click %W"
853 bind $fstring <Key-Return> dofind
854 bind $sha1entry <Key-Return> gotocommit
855 bind $sha1entry <<PasteSelection>> clearsha1
856 bind $cflist <1> {sel_flist %W %x %y; break}
857 bind $cflist <B1-Motion> {sel_flist %W %x %y; break}
858 bind $cflist <ButtonRelease-1> {treeclick %W %x %y}
860 set maincursor [. cget -cursor]
861 set textcursor [$ctext cget -cursor]
862 set curtextcursor $textcursor
864 set rowctxmenu .rowctxmenu
865 menu $rowctxmenu -tearoff 0
866 $rowctxmenu add command -label "Diff this -> selected" \
867 -command {diffvssel 0}
868 $rowctxmenu add command -label "Diff selected -> this" \
869 -command {diffvssel 1}
870 $rowctxmenu add command -label "Make patch" -command mkpatch
871 $rowctxmenu add command -label "Create tag" -command mktag
872 $rowctxmenu add command -label "Write commit to file" -command writecommit
873 $rowctxmenu add command -label "Create new branch" -command mkbranch
874 $rowctxmenu add command -label "Cherry-pick this commit" \
877 set headctxmenu .headctxmenu
878 menu $headctxmenu -tearoff 0
879 $headctxmenu add command -label "Check out this branch" \
881 $headctxmenu add command -label "Remove this branch" \
885 # mouse-2 makes all windows scan vertically, but only the one
886 # the cursor is in scans horizontally
887 proc canvscan {op w x y} {
888 global canv canv2 canv3
889 foreach c [list $canv $canv2 $canv3] {
898 proc scrollcanv {cscroll f0 f1} {
904 # when we make a key binding for the toplevel, make sure
905 # it doesn't get triggered when that key is pressed in the
906 # find string entry widget.
907 proc bindkey {ev script} {
910 set escript [bind Entry $ev]
911 if {$escript == {}} {
912 set escript [bind Entry <Key>]
915 bind $e $ev "$escript; break"
919 # set the focus back to the toplevel for any click outside
930 global canv canv2 canv3 ctext cflist mainfont textfont uifont tabstop
931 global stuffsaved findmergefiles maxgraphpct
932 global maxwidth showneartags
933 global viewname viewfiles viewargs viewperm nextviewnum
934 global cmitmode wrapcomment
935 global colors bgcolor fgcolor diffcolors selectbgcolor
937 if {$stuffsaved} return
938 if {![winfo viewable .]} return
940 set f [open "~/.gitk-new" w]
941 puts $f [list set mainfont $mainfont]
942 puts $f [list set textfont $textfont]
943 puts $f [list set uifont $uifont]
944 puts $f [list set tabstop $tabstop]
945 puts $f [list set findmergefiles $findmergefiles]
946 puts $f [list set maxgraphpct $maxgraphpct]
947 puts $f [list set maxwidth $maxwidth]
948 puts $f [list set cmitmode $cmitmode]
949 puts $f [list set wrapcomment $wrapcomment]
950 puts $f [list set showneartags $showneartags]
951 puts $f [list set bgcolor $bgcolor]
952 puts $f [list set fgcolor $fgcolor]
953 puts $f [list set colors $colors]
954 puts $f [list set diffcolors $diffcolors]
955 puts $f [list set selectbgcolor $selectbgcolor]
957 puts $f "set geometry(main) [wm geometry .]"
958 puts $f "set geometry(topwidth) [winfo width .tf]"
959 puts $f "set geometry(topheight) [winfo height .tf]"
960 puts $f "set geometry(pwsash0) \"[.tf.histframe.pwclist sash coord 0]\""
961 puts $f "set geometry(pwsash1) \"[.tf.histframe.pwclist sash coord 1]\""
962 puts $f "set geometry(botwidth) [winfo width .bleft]"
963 puts $f "set geometry(botheight) [winfo height .bleft]"
965 puts -nonewline $f "set permviews {"
966 for {set v 0} {$v < $nextviewnum} {incr v} {
968 puts $f "{[list $viewname($v) $viewfiles($v) $viewargs($v)]}"
973 file rename -force "~/.gitk-new" "~/.gitk"
978 proc resizeclistpanes {win w} {
980 if {[info exists oldwidth($win)]} {
981 set s0 [$win sash coord 0]
982 set s1 [$win sash coord 1]
984 set sash0 [expr {int($w/2 - 2)}]
985 set sash1 [expr {int($w*5/6 - 2)}]
987 set factor [expr {1.0 * $w / $oldwidth($win)}]
988 set sash0 [expr {int($factor * [lindex $s0 0])}]
989 set sash1 [expr {int($factor * [lindex $s1 0])}]
993 if {$sash1 < $sash0 + 20} {
994 set sash1 [expr {$sash0 + 20}]
996 if {$sash1 > $w - 10} {
997 set sash1 [expr {$w - 10}]
998 if {$sash0 > $sash1 - 20} {
999 set sash0 [expr {$sash1 - 20}]
1003 $win sash place 0 $sash0 [lindex $s0 1]
1004 $win sash place 1 $sash1 [lindex $s1 1]
1006 set oldwidth($win) $w
1009 proc resizecdetpanes {win w} {
1011 if {[info exists oldwidth($win)]} {
1012 set s0 [$win sash coord 0]
1014 set sash0 [expr {int($w*3/4 - 2)}]
1016 set factor [expr {1.0 * $w / $oldwidth($win)}]
1017 set sash0 [expr {int($factor * [lindex $s0 0])}]
1021 if {$sash0 > $w - 15} {
1022 set sash0 [expr {$w - 15}]
1025 $win sash place 0 $sash0 [lindex $s0 1]
1027 set oldwidth($win) $w
1030 proc allcanvs args {
1031 global canv canv2 canv3
1037 proc bindall {event action} {
1038 global canv canv2 canv3
1039 bind $canv $event $action
1040 bind $canv2 $event $action
1041 bind $canv3 $event $action
1047 if {[winfo exists $w]} {
1052 wm title $w "About gitk"
1053 message $w.m -text {
1054 Gitk - a commit viewer for git
1056 Copyright © 2005-2006 Paul Mackerras
1058 Use and redistribute under the terms of the GNU General Public License} \
1059 -justify center -aspect 400 -border 2 -bg white -relief groove
1060 pack $w.m -side top -fill x -padx 2 -pady 2
1061 $w.m configure -font $uifont
1062 button $w.ok -text Close -command "destroy $w" -default active
1063 pack $w.ok -side bottom
1064 $w.ok configure -font $uifont
1065 bind $w <Visibility> "focus $w.ok"
1066 bind $w <Key-Escape> "destroy $w"
1067 bind $w <Key-Return> "destroy $w"
1073 if {[winfo exists $w]} {
1078 wm title $w "Gitk key bindings"
1079 message $w.m -text {
1083 <Home> Move to first commit
1084 <End> Move to last commit
1085 <Up>, p, i Move up one commit
1086 <Down>, n, k Move down one commit
1087 <Left>, z, j Go back in history list
1088 <Right>, x, l Go forward in history list
1089 <PageUp> Move up one page in commit list
1090 <PageDown> Move down one page in commit list
1091 <Ctrl-Home> Scroll to top of commit list
1092 <Ctrl-End> Scroll to bottom of commit list
1093 <Ctrl-Up> Scroll commit list up one line
1094 <Ctrl-Down> Scroll commit list down one line
1095 <Ctrl-PageUp> Scroll commit list up one page
1096 <Ctrl-PageDown> Scroll commit list down one page
1097 <Shift-Up> Move to previous highlighted line
1098 <Shift-Down> Move to next highlighted line
1099 <Delete>, b Scroll diff view up one page
1100 <Backspace> Scroll diff view up one page
1101 <Space> Scroll diff view down one page
1102 u Scroll diff view up 18 lines
1103 d Scroll diff view down 18 lines
1105 <Ctrl-G> Move to next find hit
1106 <Return> Move to next find hit
1107 / Move to next find hit, or redo find
1108 ? Move to previous find hit
1109 f Scroll diff view to next file
1110 <Ctrl-S> Search for next hit in diff view
1111 <Ctrl-R> Search for previous hit in diff view
1112 <Ctrl-KP+> Increase font size
1113 <Ctrl-plus> Increase font size
1114 <Ctrl-KP-> Decrease font size
1115 <Ctrl-minus> Decrease font size
1118 -justify left -bg white -border 2 -relief groove
1119 pack $w.m -side top -fill both -padx 2 -pady 2
1120 $w.m configure -font $uifont
1121 button $w.ok -text Close -command "destroy $w" -default active
1122 pack $w.ok -side bottom
1123 $w.ok configure -font $uifont
1124 bind $w <Visibility> "focus $w.ok"
1125 bind $w <Key-Escape> "destroy $w"
1126 bind $w <Key-Return> "destroy $w"
1129 # Procedures for manipulating the file list window at the
1130 # bottom right of the overall window.
1132 proc treeview {w l openlevs} {
1133 global treecontents treediropen treeheight treeparent treeindex
1143 set treecontents() {}
1144 $w conf -state normal
1146 while {[string range $f 0 $prefixend] ne $prefix} {
1147 if {$lev <= $openlevs} {
1148 $w mark set e:$treeindex($prefix) "end -1c"
1149 $w mark gravity e:$treeindex($prefix) left
1151 set treeheight($prefix) $ht
1152 incr ht [lindex $htstack end]
1153 set htstack [lreplace $htstack end end]
1154 set prefixend [lindex $prefendstack end]
1155 set prefendstack [lreplace $prefendstack end end]
1156 set prefix [string range $prefix 0 $prefixend]
1159 set tail [string range $f [expr {$prefixend+1}] end]
1160 while {[set slash [string first "/" $tail]] >= 0} {
1163 lappend prefendstack $prefixend
1164 incr prefixend [expr {$slash + 1}]
1165 set d [string range $tail 0 $slash]
1166 lappend treecontents($prefix) $d
1167 set oldprefix $prefix
1169 set treecontents($prefix) {}
1170 set treeindex($prefix) [incr ix]
1171 set treeparent($prefix) $oldprefix
1172 set tail [string range $tail [expr {$slash+1}] end]
1173 if {$lev <= $openlevs} {
1175 set treediropen($prefix) [expr {$lev < $openlevs}]
1176 set bm [expr {$lev == $openlevs? "tri-rt": "tri-dn"}]
1177 $w mark set d:$ix "end -1c"
1178 $w mark gravity d:$ix left
1180 for {set i 0} {$i < $lev} {incr i} {append str "\t"}
1182 $w image create end -align center -image $bm -padx 1 \
1184 $w insert end $d [highlight_tag $prefix]
1185 $w mark set s:$ix "end -1c"
1186 $w mark gravity s:$ix left
1191 if {$lev <= $openlevs} {
1194 for {set i 0} {$i < $lev} {incr i} {append str "\t"}
1196 $w insert end $tail [highlight_tag $f]
1198 lappend treecontents($prefix) $tail
1201 while {$htstack ne {}} {
1202 set treeheight($prefix) $ht
1203 incr ht [lindex $htstack end]
1204 set htstack [lreplace $htstack end end]
1206 $w conf -state disabled
1209 proc linetoelt {l} {
1210 global treeheight treecontents
1215 foreach e $treecontents($prefix) {
1220 if {[string index $e end] eq "/"} {
1221 set n $treeheight($prefix$e)
1233 proc highlight_tree {y prefix} {
1234 global treeheight treecontents cflist
1236 foreach e $treecontents($prefix) {
1238 if {[highlight_tag $path] ne {}} {
1239 $cflist tag add bold $y.0 "$y.0 lineend"
1242 if {[string index $e end] eq "/" && $treeheight($path) > 1} {
1243 set y [highlight_tree $y $path]
1249 proc treeclosedir {w dir} {
1250 global treediropen treeheight treeparent treeindex
1252 set ix $treeindex($dir)
1253 $w conf -state normal
1254 $w delete s:$ix e:$ix
1255 set treediropen($dir) 0
1256 $w image configure a:$ix -image tri-rt
1257 $w conf -state disabled
1258 set n [expr {1 - $treeheight($dir)}]
1259 while {$dir ne {}} {
1260 incr treeheight($dir) $n
1261 set dir $treeparent($dir)
1265 proc treeopendir {w dir} {
1266 global treediropen treeheight treeparent treecontents treeindex
1268 set ix $treeindex($dir)
1269 $w conf -state normal
1270 $w image configure a:$ix -image tri-dn
1271 $w mark set e:$ix s:$ix
1272 $w mark gravity e:$ix right
1275 set n [llength $treecontents($dir)]
1276 for {set x $dir} {$x ne {}} {set x $treeparent($x)} {
1279 incr treeheight($x) $n
1281 foreach e $treecontents($dir) {
1283 if {[string index $e end] eq "/"} {
1284 set iy $treeindex($de)
1285 $w mark set d:$iy e:$ix
1286 $w mark gravity d:$iy left
1287 $w insert e:$ix $str
1288 set treediropen($de) 0
1289 $w image create e:$ix -align center -image tri-rt -padx 1 \
1291 $w insert e:$ix $e [highlight_tag $de]
1292 $w mark set s:$iy e:$ix
1293 $w mark gravity s:$iy left
1294 set treeheight($de) 1
1296 $w insert e:$ix $str
1297 $w insert e:$ix $e [highlight_tag $de]
1300 $w mark gravity e:$ix left
1301 $w conf -state disabled
1302 set treediropen($dir) 1
1303 set top [lindex [split [$w index @0,0] .] 0]
1304 set ht [$w cget -height]
1305 set l [lindex [split [$w index s:$ix] .] 0]
1308 } elseif {$l + $n + 1 > $top + $ht} {
1309 set top [expr {$l + $n + 2 - $ht}]
1317 proc treeclick {w x y} {
1318 global treediropen cmitmode ctext cflist cflist_top
1320 if {$cmitmode ne "tree"} return
1321 if {![info exists cflist_top]} return
1322 set l [lindex [split [$w index "@$x,$y"] "."] 0]
1323 $cflist tag remove highlight $cflist_top.0 "$cflist_top.0 lineend"
1324 $cflist tag add highlight $l.0 "$l.0 lineend"
1330 set e [linetoelt $l]
1331 if {[string index $e end] ne "/"} {
1333 } elseif {$treediropen($e)} {
1340 proc setfilelist {id} {
1341 global treefilelist cflist
1343 treeview $cflist $treefilelist($id) 0
1346 image create bitmap tri-rt -background black -foreground blue -data {
1347 #define tri-rt_width 13
1348 #define tri-rt_height 13
1349 static unsigned char tri-rt_bits[] = {
1350 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x30, 0x00, 0x70, 0x00, 0xf0, 0x00,
1351 0xf0, 0x01, 0xf0, 0x00, 0x70, 0x00, 0x30, 0x00, 0x10, 0x00, 0x00, 0x00,
1354 #define tri-rt-mask_width 13
1355 #define tri-rt-mask_height 13
1356 static unsigned char tri-rt-mask_bits[] = {
1357 0x08, 0x00, 0x18, 0x00, 0x38, 0x00, 0x78, 0x00, 0xf8, 0x00, 0xf8, 0x01,
1358 0xf8, 0x03, 0xf8, 0x01, 0xf8, 0x00, 0x78, 0x00, 0x38, 0x00, 0x18, 0x00,
1361 image create bitmap tri-dn -background black -foreground blue -data {
1362 #define tri-dn_width 13
1363 #define tri-dn_height 13
1364 static unsigned char tri-dn_bits[] = {
1365 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x07, 0xf8, 0x03,
1366 0xf0, 0x01, 0xe0, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1369 #define tri-dn-mask_width 13
1370 #define tri-dn-mask_height 13
1371 static unsigned char tri-dn-mask_bits[] = {
1372 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x1f, 0xfe, 0x0f, 0xfc, 0x07,
1373 0xf8, 0x03, 0xf0, 0x01, 0xe0, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
1377 proc init_flist {first} {
1378 global cflist cflist_top selectedline difffilestart
1380 $cflist conf -state normal
1381 $cflist delete 0.0 end
1383 $cflist insert end $first
1385 $cflist tag add highlight 1.0 "1.0 lineend"
1387 catch {unset cflist_top}
1389 $cflist conf -state disabled
1390 set difffilestart {}
1393 proc highlight_tag {f} {
1394 global highlight_paths
1396 foreach p $highlight_paths {
1397 if {[string match $p $f]} {
1404 proc highlight_filelist {} {
1405 global cmitmode cflist
1407 $cflist conf -state normal
1408 if {$cmitmode ne "tree"} {
1409 set end [lindex [split [$cflist index end] .] 0]
1410 for {set l 2} {$l < $end} {incr l} {
1411 set line [$cflist get $l.0 "$l.0 lineend"]
1412 if {[highlight_tag $line] ne {}} {
1413 $cflist tag add bold $l.0 "$l.0 lineend"
1419 $cflist conf -state disabled
1422 proc unhighlight_filelist {} {
1425 $cflist conf -state normal
1426 $cflist tag remove bold 1.0 end
1427 $cflist conf -state disabled
1430 proc add_flist {fl} {
1433 $cflist conf -state normal
1435 $cflist insert end "\n"
1436 $cflist insert end $f [highlight_tag $f]
1438 $cflist conf -state disabled
1441 proc sel_flist {w x y} {
1442 global ctext difffilestart cflist cflist_top cmitmode
1444 if {$cmitmode eq "tree"} return
1445 if {![info exists cflist_top]} return
1446 set l [lindex [split [$w index "@$x,$y"] "."] 0]
1447 $cflist tag remove highlight $cflist_top.0 "$cflist_top.0 lineend"
1448 $cflist tag add highlight $l.0 "$l.0 lineend"
1453 catch {$ctext yview [lindex $difffilestart [expr {$l - 2}]]}
1457 # Functions for adding and removing shell-type quoting
1459 proc shellquote {str} {
1460 if {![string match "*\['\"\\ \t]*" $str]} {
1463 if {![string match "*\['\"\\]*" $str]} {
1466 if {![string match "*'*" $str]} {
1469 return "\"[string map {\" \\\" \\ \\\\} $str]\""
1472 proc shellarglist {l} {
1478 append str [shellquote $a]
1483 proc shelldequote {str} {
1488 if {![regexp -start $used -indices "\['\"\\\\ \t]" $str first]} {
1489 append ret [string range $str $used end]
1490 set used [string length $str]
1493 set first [lindex $first 0]
1494 set ch [string index $str $first]
1495 if {$first > $used} {
1496 append ret [string range $str $used [expr {$first - 1}]]
1499 if {$ch eq " " || $ch eq "\t"} break
1502 set first [string first "'" $str $used]
1504 error "unmatched single-quote"
1506 append ret [string range $str $used [expr {$first - 1}]]
1511 if {$used >= [string length $str]} {
1512 error "trailing backslash"
1514 append ret [string index $str $used]
1519 if {![regexp -start $used -indices "\[\"\\\\]" $str first]} {
1520 error "unmatched double-quote"
1522 set first [lindex $first 0]
1523 set ch [string index $str $first]
1524 if {$first > $used} {
1525 append ret [string range $str $used [expr {$first - 1}]]
1528 if {$ch eq "\""} break
1530 append ret [string index $str $used]
1534 return [list $used $ret]
1537 proc shellsplit {str} {
1540 set str [string trimleft $str]
1541 if {$str eq {}} break
1542 set dq [shelldequote $str]
1543 set n [lindex $dq 0]
1544 set word [lindex $dq 1]
1545 set str [string range $str $n end]
1551 # Code to implement multiple views
1553 proc newview {ishighlight} {
1554 global nextviewnum newviewname newviewperm uifont newishighlight
1555 global newviewargs revtreeargs
1557 set newishighlight $ishighlight
1559 if {[winfo exists $top]} {
1563 set newviewname($nextviewnum) "View $nextviewnum"
1564 set newviewperm($nextviewnum) 0
1565 set newviewargs($nextviewnum) [shellarglist $revtreeargs]
1566 vieweditor $top $nextviewnum "Gitk view definition"
1571 global viewname viewperm newviewname newviewperm
1572 global viewargs newviewargs
1574 set top .gitkvedit-$curview
1575 if {[winfo exists $top]} {
1579 set newviewname($curview) $viewname($curview)
1580 set newviewperm($curview) $viewperm($curview)
1581 set newviewargs($curview) [shellarglist $viewargs($curview)]
1582 vieweditor $top $curview "Gitk: edit view $viewname($curview)"
1585 proc vieweditor {top n title} {
1586 global newviewname newviewperm viewfiles
1590 wm title $top $title
1591 label $top.nl -text "Name" -font $uifont
1592 entry $top.name -width 20 -textvariable newviewname($n) -font $uifont
1593 grid $top.nl $top.name -sticky w -pady 5
1594 checkbutton $top.perm -text "Remember this view" -variable newviewperm($n) \
1596 grid $top.perm - -pady 5 -sticky w
1597 message $top.al -aspect 1000 -font $uifont \
1598 -text "Commits to include (arguments to git rev-list):"
1599 grid $top.al - -sticky w -pady 5
1600 entry $top.args -width 50 -textvariable newviewargs($n) \
1601 -background white -font $uifont
1602 grid $top.args - -sticky ew -padx 5
1603 message $top.l -aspect 1000 -font $uifont \
1604 -text "Enter files and directories to include, one per line:"
1605 grid $top.l - -sticky w
1606 text $top.t -width 40 -height 10 -background white -font $uifont
1607 if {[info exists viewfiles($n)]} {
1608 foreach f $viewfiles($n) {
1609 $top.t insert end $f
1610 $top.t insert end "\n"
1612 $top.t delete {end - 1c} end
1613 $top.t mark set insert 0.0
1615 grid $top.t - -sticky ew -padx 5
1617 button $top.buts.ok -text "OK" -command [list newviewok $top $n] \
1619 button $top.buts.can -text "Cancel" -command [list destroy $top] \
1621 grid $top.buts.ok $top.buts.can
1622 grid columnconfigure $top.buts 0 -weight 1 -uniform a
1623 grid columnconfigure $top.buts 1 -weight 1 -uniform a
1624 grid $top.buts - -pady 10 -sticky ew
1628 proc doviewmenu {m first cmd op argv} {
1629 set nmenu [$m index end]
1630 for {set i $first} {$i <= $nmenu} {incr i} {
1631 if {[$m entrycget $i -command] eq $cmd} {
1632 eval $m $op $i $argv
1638 proc allviewmenus {n op args} {
1641 doviewmenu .bar.view 5 [list showview $n] $op $args
1642 doviewmenu $viewhlmenu 1 [list addvhighlight $n] $op $args
1645 proc newviewok {top n} {
1646 global nextviewnum newviewperm newviewname newishighlight
1647 global viewname viewfiles viewperm selectedview curview
1648 global viewargs newviewargs viewhlmenu
1651 set newargs [shellsplit $newviewargs($n)]
1653 error_popup "Error in commit selection arguments: $err"
1659 foreach f [split [$top.t get 0.0 end] "\n"] {
1660 set ft [string trim $f]
1665 if {![info exists viewfiles($n)]} {
1666 # creating a new view
1668 set viewname($n) $newviewname($n)
1669 set viewperm($n) $newviewperm($n)
1670 set viewfiles($n) $files
1671 set viewargs($n) $newargs
1673 if {!$newishighlight} {
1676 run addvhighlight $n
1679 # editing an existing view
1680 set viewperm($n) $newviewperm($n)
1681 if {$newviewname($n) ne $viewname($n)} {
1682 set viewname($n) $newviewname($n)
1683 doviewmenu .bar.view 5 [list showview $n] \
1684 entryconf [list -label $viewname($n)]
1685 doviewmenu $viewhlmenu 1 [list addvhighlight $n] \
1686 entryconf [list -label $viewname($n) -value $viewname($n)]
1688 if {$files ne $viewfiles($n) || $newargs ne $viewargs($n)} {
1689 set viewfiles($n) $files
1690 set viewargs($n) $newargs
1691 if {$curview == $n} {
1696 catch {destroy $top}
1700 global curview viewdata viewperm hlview selectedhlview
1702 if {$curview == 0} return
1703 if {[info exists hlview] && $hlview == $curview} {
1704 set selectedhlview None
1707 allviewmenus $curview delete
1708 set viewdata($curview) {}
1709 set viewperm($curview) 0
1713 proc addviewmenu {n} {
1714 global viewname viewhlmenu
1716 .bar.view add radiobutton -label $viewname($n) \
1717 -command [list showview $n] -variable selectedview -value $n
1718 $viewhlmenu add radiobutton -label $viewname($n) \
1719 -command [list addvhighlight $n] -variable selectedhlview
1722 proc flatten {var} {
1726 foreach i [array names $var] {
1727 lappend ret $i [set $var\($i\)]
1732 proc unflatten {var l} {
1742 global curview viewdata viewfiles
1743 global displayorder parentlist childlist rowidlist rowoffsets
1744 global colormap rowtextx commitrow nextcolor canvxmax
1745 global numcommits rowrangelist commitlisted idrowranges
1746 global selectedline currentid canv canvy0
1747 global matchinglines treediffs
1748 global pending_select phase
1749 global commitidx rowlaidout rowoptim linesegends
1751 global selectedview selectfirst
1752 global vparentlist vchildlist vdisporder vcmitlisted
1753 global hlview selectedhlview
1755 if {$n == $curview} return
1757 if {[info exists selectedline]} {
1758 set selid $currentid
1759 set y [yc $selectedline]
1760 set ymax [lindex [$canv cget -scrollregion] 3]
1761 set span [$canv yview]
1762 set ytop [expr {[lindex $span 0] * $ymax}]
1763 set ybot [expr {[lindex $span 1] * $ymax}]
1764 if {$ytop < $y && $y < $ybot} {
1765 set yscreen [expr {$y - $ytop}]
1767 set yscreen [expr {($ybot - $ytop) / 2}]
1769 } elseif {[info exists pending_select]} {
1770 set selid $pending_select
1771 unset pending_select
1776 if {$curview >= 0} {
1777 set vparentlist($curview) $parentlist
1778 set vchildlist($curview) $childlist
1779 set vdisporder($curview) $displayorder
1780 set vcmitlisted($curview) $commitlisted
1782 set viewdata($curview) \
1783 [list $phase $rowidlist $rowoffsets $rowrangelist \
1784 [flatten idrowranges] [flatten idinlist] \
1785 $rowlaidout $rowoptim $numcommits $linesegends]
1786 } elseif {![info exists viewdata($curview)]
1787 || [lindex $viewdata($curview) 0] ne {}} {
1788 set viewdata($curview) \
1789 [list {} $rowidlist $rowoffsets $rowrangelist]
1792 catch {unset matchinglines}
1793 catch {unset treediffs}
1795 if {[info exists hlview] && $hlview == $n} {
1797 set selectedhlview None
1802 .bar.view entryconf Edit* -state [expr {$n == 0? "disabled": "normal"}]
1803 .bar.view entryconf Delete* -state [expr {$n == 0? "disabled": "normal"}]
1805 if {![info exists viewdata($n)]} {
1807 set pending_select $selid
1814 set phase [lindex $v 0]
1815 set displayorder $vdisporder($n)
1816 set parentlist $vparentlist($n)
1817 set childlist $vchildlist($n)
1818 set commitlisted $vcmitlisted($n)
1819 set rowidlist [lindex $v 1]
1820 set rowoffsets [lindex $v 2]
1821 set rowrangelist [lindex $v 3]
1823 set numcommits [llength $displayorder]
1824 catch {unset idrowranges}
1826 unflatten idrowranges [lindex $v 4]
1827 unflatten idinlist [lindex $v 5]
1828 set rowlaidout [lindex $v 6]
1829 set rowoptim [lindex $v 7]
1830 set numcommits [lindex $v 8]
1831 set linesegends [lindex $v 9]
1834 catch {unset colormap}
1835 catch {unset rowtextx}
1837 set canvxmax [$canv cget -width]
1844 if {$selid ne {} && [info exists commitrow($n,$selid)]} {
1845 set row $commitrow($n,$selid)
1846 # try to get the selected row in the same position on the screen
1847 set ymax [lindex [$canv cget -scrollregion] 3]
1848 set ytop [expr {[yc $row] - $yscreen}]
1852 set yf [expr {$ytop * 1.0 / $ymax}]
1854 allcanvs yview moveto $yf
1858 } elseif {$selid ne {}} {
1859 set pending_select $selid
1861 if {$numcommits > 0} {
1868 if {$phase eq "getcommits"} {
1869 show_status "Reading commits..."
1872 } elseif {$numcommits == 0} {
1873 show_status "No commits selected"
1877 # Stuff relating to the highlighting facility
1879 proc ishighlighted {row} {
1880 global vhighlights fhighlights nhighlights rhighlights
1882 if {[info exists nhighlights($row)] && $nhighlights($row) > 0} {
1883 return $nhighlights($row)
1885 if {[info exists vhighlights($row)] && $vhighlights($row) > 0} {
1886 return $vhighlights($row)
1888 if {[info exists fhighlights($row)] && $fhighlights($row) > 0} {
1889 return $fhighlights($row)
1891 if {[info exists rhighlights($row)] && $rhighlights($row) > 0} {
1892 return $rhighlights($row)
1897 proc bolden {row font} {
1898 global canv linehtag selectedline boldrows
1900 lappend boldrows $row
1901 $canv itemconf $linehtag($row) -font $font
1902 if {[info exists selectedline] && $row == $selectedline} {
1904 set t [eval $canv create rect [$canv bbox $linehtag($row)] \
1905 -outline {{}} -tags secsel \
1906 -fill [$canv cget -selectbackground]]
1911 proc bolden_name {row font} {
1912 global canv2 linentag selectedline boldnamerows
1914 lappend boldnamerows $row
1915 $canv2 itemconf $linentag($row) -font $font
1916 if {[info exists selectedline] && $row == $selectedline} {
1917 $canv2 delete secsel
1918 set t [eval $canv2 create rect [$canv2 bbox $linentag($row)] \
1919 -outline {{}} -tags secsel \
1920 -fill [$canv2 cget -selectbackground]]
1926 global mainfont boldrows
1929 foreach row $boldrows {
1930 if {![ishighlighted $row]} {
1931 bolden $row $mainfont
1933 lappend stillbold $row
1936 set boldrows $stillbold
1939 proc addvhighlight {n} {
1940 global hlview curview viewdata vhl_done vhighlights commitidx
1942 if {[info exists hlview]} {
1946 if {$n != $curview && ![info exists viewdata($n)]} {
1947 set viewdata($n) [list getcommits {{}} {{}} {} {} {} 0 0 0 {}]
1948 set vparentlist($n) {}
1949 set vchildlist($n) {}
1950 set vdisporder($n) {}
1951 set vcmitlisted($n) {}
1954 set vhl_done $commitidx($hlview)
1955 if {$vhl_done > 0} {
1960 proc delvhighlight {} {
1961 global hlview vhighlights
1963 if {![info exists hlview]} return
1965 catch {unset vhighlights}
1969 proc vhighlightmore {} {
1970 global hlview vhl_done commitidx vhighlights
1971 global displayorder vdisporder curview mainfont
1973 set font [concat $mainfont bold]
1974 set max $commitidx($hlview)
1975 if {$hlview == $curview} {
1976 set disp $displayorder
1978 set disp $vdisporder($hlview)
1980 set vr [visiblerows]
1981 set r0 [lindex $vr 0]
1982 set r1 [lindex $vr 1]
1983 for {set i $vhl_done} {$i < $max} {incr i} {
1984 set id [lindex $disp $i]
1985 if {[info exists commitrow($curview,$id)]} {
1986 set row $commitrow($curview,$id)
1987 if {$r0 <= $row && $row <= $r1} {
1988 if {![highlighted $row]} {
1991 set vhighlights($row) 1
1998 proc askvhighlight {row id} {
1999 global hlview vhighlights commitrow iddrawn mainfont
2001 if {[info exists commitrow($hlview,$id)]} {
2002 if {[info exists iddrawn($id)] && ![ishighlighted $row]} {
2003 bolden $row [concat $mainfont bold]
2005 set vhighlights($row) 1
2007 set vhighlights($row) 0
2011 proc hfiles_change {name ix op} {
2012 global highlight_files filehighlight fhighlights fh_serial
2013 global mainfont highlight_paths
2015 if {[info exists filehighlight]} {
2016 # delete previous highlights
2017 catch {close $filehighlight}
2019 catch {unset fhighlights}
2021 unhighlight_filelist
2023 set highlight_paths {}
2024 after cancel do_file_hl $fh_serial
2026 if {$highlight_files ne {}} {
2027 after 300 do_file_hl $fh_serial
2031 proc makepatterns {l} {
2034 set ee [string map {"*" "\\*" "?" "\\?" "\[" "\\\[" "\\" "\\\\"} $e]
2035 if {[string index $ee end] eq "/"} {
2045 proc do_file_hl {serial} {
2046 global highlight_files filehighlight highlight_paths gdttype fhl_list
2048 if {$gdttype eq "touching paths:"} {
2049 if {[catch {set paths [shellsplit $highlight_files]}]} return
2050 set highlight_paths [makepatterns $paths]
2052 set gdtargs [concat -- $paths]
2054 set gdtargs [list "-S$highlight_files"]
2056 set cmd [concat | git diff-tree -r -s --stdin $gdtargs]
2057 set filehighlight [open $cmd r+]
2058 fconfigure $filehighlight -blocking 0
2059 filerun $filehighlight readfhighlight
2065 proc flushhighlights {} {
2066 global filehighlight fhl_list
2068 if {[info exists filehighlight]} {
2070 puts $filehighlight ""
2071 flush $filehighlight
2075 proc askfilehighlight {row id} {
2076 global filehighlight fhighlights fhl_list
2078 lappend fhl_list $id
2079 set fhighlights($row) -1
2080 puts $filehighlight $id
2083 proc readfhighlight {} {
2084 global filehighlight fhighlights commitrow curview mainfont iddrawn
2087 if {![info exists filehighlight]} {
2091 while {[incr nr] <= 100 && [gets $filehighlight line] >= 0} {
2092 set line [string trim $line]
2093 set i [lsearch -exact $fhl_list $line]
2094 if {$i < 0} continue
2095 for {set j 0} {$j < $i} {incr j} {
2096 set id [lindex $fhl_list $j]
2097 if {[info exists commitrow($curview,$id)]} {
2098 set fhighlights($commitrow($curview,$id)) 0
2101 set fhl_list [lrange $fhl_list [expr {$i+1}] end]
2102 if {$line eq {}} continue
2103 if {![info exists commitrow($curview,$line)]} continue
2104 set row $commitrow($curview,$line)
2105 if {[info exists iddrawn($line)] && ![ishighlighted $row]} {
2106 bolden $row [concat $mainfont bold]
2108 set fhighlights($row) 1
2110 if {[eof $filehighlight]} {
2112 puts "oops, git diff-tree died"
2113 catch {close $filehighlight}
2121 proc find_change {name ix op} {
2122 global nhighlights mainfont boldnamerows
2123 global findstring findpattern findtype
2125 # delete previous highlights, if any
2126 foreach row $boldnamerows {
2127 bolden_name $row $mainfont
2130 catch {unset nhighlights}
2132 if {$findtype ne "Regexp"} {
2133 set e [string map {"*" "\\*" "?" "\\?" "\[" "\\\[" "\\" "\\\\"} \
2135 set findpattern "*$e*"
2140 proc askfindhighlight {row id} {
2141 global nhighlights commitinfo iddrawn mainfont
2142 global findstring findtype findloc findpattern
2144 if {![info exists commitinfo($id)]} {
2147 set info $commitinfo($id)
2149 set fldtypes {Headline Author Date Committer CDate Comments}
2150 foreach f $info ty $fldtypes {
2151 if {$findloc ne "All fields" && $findloc ne $ty} {
2154 if {$findtype eq "Regexp"} {
2155 set doesmatch [regexp $findstring $f]
2156 } elseif {$findtype eq "IgnCase"} {
2157 set doesmatch [string match -nocase $findpattern $f]
2159 set doesmatch [string match $findpattern $f]
2162 if {$ty eq "Author"} {
2169 if {[info exists iddrawn($id)]} {
2170 if {$isbold && ![ishighlighted $row]} {
2171 bolden $row [concat $mainfont bold]
2174 bolden_name $row [concat $mainfont bold]
2177 set nhighlights($row) $isbold
2180 proc vrel_change {name ix op} {
2181 global highlight_related
2184 if {$highlight_related ne "None"} {
2189 # prepare for testing whether commits are descendents or ancestors of a
2190 proc rhighlight_sel {a} {
2191 global descendent desc_todo ancestor anc_todo
2192 global highlight_related rhighlights
2194 catch {unset descendent}
2195 set desc_todo [list $a]
2196 catch {unset ancestor}
2197 set anc_todo [list $a]
2198 if {$highlight_related ne "None"} {
2204 proc rhighlight_none {} {
2207 catch {unset rhighlights}
2211 proc is_descendent {a} {
2212 global curview children commitrow descendent desc_todo
2215 set la $commitrow($v,$a)
2219 for {set i 0} {$i < [llength $todo]} {incr i} {
2220 set do [lindex $todo $i]
2221 if {$commitrow($v,$do) < $la} {
2222 lappend leftover $do
2225 foreach nk $children($v,$do) {
2226 if {![info exists descendent($nk)]} {
2227 set descendent($nk) 1
2235 set desc_todo [concat $leftover [lrange $todo [expr {$i+1}] end]]
2239 set descendent($a) 0
2240 set desc_todo $leftover
2243 proc is_ancestor {a} {
2244 global curview parentlist commitrow ancestor anc_todo
2247 set la $commitrow($v,$a)
2251 for {set i 0} {$i < [llength $todo]} {incr i} {
2252 set do [lindex $todo $i]
2253 if {![info exists commitrow($v,$do)] || $commitrow($v,$do) > $la} {
2254 lappend leftover $do
2257 foreach np [lindex $parentlist $commitrow($v,$do)] {
2258 if {![info exists ancestor($np)]} {
2267 set anc_todo [concat $leftover [lrange $todo [expr {$i+1}] end]]
2272 set anc_todo $leftover
2275 proc askrelhighlight {row id} {
2276 global descendent highlight_related iddrawn mainfont rhighlights
2277 global selectedline ancestor
2279 if {![info exists selectedline]} return
2281 if {$highlight_related eq "Descendent" ||
2282 $highlight_related eq "Not descendent"} {
2283 if {![info exists descendent($id)]} {
2286 if {$descendent($id) == ($highlight_related eq "Descendent")} {
2289 } elseif {$highlight_related eq "Ancestor" ||
2290 $highlight_related eq "Not ancestor"} {
2291 if {![info exists ancestor($id)]} {
2294 if {$ancestor($id) == ($highlight_related eq "Ancestor")} {
2298 if {[info exists iddrawn($id)]} {
2299 if {$isbold && ![ishighlighted $row]} {
2300 bolden $row [concat $mainfont bold]
2303 set rhighlights($row) $isbold
2306 proc next_hlcont {} {
2307 global fhl_row fhl_dirn displayorder numcommits
2308 global vhighlights fhighlights nhighlights rhighlights
2309 global hlview filehighlight findstring highlight_related
2311 if {![info exists fhl_dirn] || $fhl_dirn == 0} return
2314 if {$row < 0 || $row >= $numcommits} {
2319 set id [lindex $displayorder $row]
2320 if {[info exists hlview]} {
2321 if {![info exists vhighlights($row)]} {
2322 askvhighlight $row $id
2324 if {$vhighlights($row) > 0} break
2326 if {$findstring ne {}} {
2327 if {![info exists nhighlights($row)]} {
2328 askfindhighlight $row $id
2330 if {$nhighlights($row) > 0} break
2332 if {$highlight_related ne "None"} {
2333 if {![info exists rhighlights($row)]} {
2334 askrelhighlight $row $id
2336 if {$rhighlights($row) > 0} break
2338 if {[info exists filehighlight]} {
2339 if {![info exists fhighlights($row)]} {
2340 # ask for a few more while we're at it...
2342 for {set n 0} {$n < 100} {incr n} {
2343 if {![info exists fhighlights($r)]} {
2344 askfilehighlight $r [lindex $displayorder $r]
2347 if {$r < 0 || $r >= $numcommits} break
2351 if {$fhighlights($row) < 0} {
2355 if {$fhighlights($row) > 0} break
2363 proc next_highlight {dirn} {
2364 global selectedline fhl_row fhl_dirn
2365 global hlview filehighlight findstring highlight_related
2367 if {![info exists selectedline]} return
2368 if {!([info exists hlview] || $findstring ne {} ||
2369 $highlight_related ne "None" || [info exists filehighlight])} return
2370 set fhl_row [expr {$selectedline + $dirn}]
2375 proc cancel_next_highlight {} {
2381 # Graph layout functions
2383 proc shortids {ids} {
2386 if {[llength $id] > 1} {
2387 lappend res [shortids $id]
2388 } elseif {[regexp {^[0-9a-f]{40}$} $id]} {
2389 lappend res [string range $id 0 7]
2397 proc incrange {l x o} {
2400 set e [lindex $l $x]
2402 lset l $x [expr {$e + $o}]
2411 for {} {$n > 0} {incr n -1} {
2417 proc usedinrange {id l1 l2} {
2418 global children commitrow childlist curview
2420 if {[info exists commitrow($curview,$id)]} {
2421 set r $commitrow($curview,$id)
2422 if {$l1 <= $r && $r <= $l2} {
2423 return [expr {$r - $l1 + 1}]
2425 set kids [lindex $childlist $r]
2427 set kids $children($curview,$id)
2430 set r $commitrow($curview,$c)
2431 if {$l1 <= $r && $r <= $l2} {
2432 return [expr {$r - $l1 + 1}]
2438 proc sanity {row {full 0}} {
2439 global rowidlist rowoffsets
2442 set ids [lindex $rowidlist $row]
2445 if {$id eq {}} continue
2446 if {$col < [llength $ids] - 1 &&
2447 [lsearch -exact -start [expr {$col+1}] $ids $id] >= 0} {
2448 puts "oops: [shortids $id] repeated in row $row col $col: {[shortids [lindex $rowidlist $row]]}"
2450 set o [lindex $rowoffsets $row $col]
2456 if {[lindex $rowidlist $y $x] != $id} {
2457 puts "oops: rowoffsets wrong at row [expr {$y+1}] col [expr {$x-$o}]"
2458 puts " id=[shortids $id] check started at row $row"
2459 for {set i $row} {$i >= $y} {incr i -1} {
2460 puts " row $i ids={[shortids [lindex $rowidlist $i]]} offs={[lindex $rowoffsets $i]}"
2465 set o [lindex $rowoffsets $y $x]
2470 proc makeuparrow {oid x y z} {
2471 global rowidlist rowoffsets uparrowlen idrowranges
2473 for {set i 1} {$i < $uparrowlen && $y > 1} {incr i} {
2476 set off0 [lindex $rowoffsets $y]
2477 for {set x0 $x} {1} {incr x0} {
2478 if {$x0 >= [llength $off0]} {
2479 set x0 [llength [lindex $rowoffsets [expr {$y-1}]]]
2482 set z [lindex $off0 $x0]
2488 set z [expr {$x0 - $x}]
2489 lset rowidlist $y [linsert [lindex $rowidlist $y] $x $oid]
2490 lset rowoffsets $y [linsert [lindex $rowoffsets $y] $x $z]
2492 set tmp [lreplace [lindex $rowoffsets $y] $x $x {}]
2493 lset rowoffsets $y [incrange $tmp [expr {$x+1}] -1]
2494 lappend idrowranges($oid) $y
2497 proc initlayout {} {
2498 global rowidlist rowoffsets displayorder commitlisted
2499 global rowlaidout rowoptim
2500 global idinlist rowchk rowrangelist idrowranges
2501 global numcommits canvxmax canv
2503 global parentlist childlist children
2504 global colormap rowtextx
2505 global linesegends selectfirst
2516 catch {unset idinlist}
2517 catch {unset rowchk}
2520 set canvxmax [$canv cget -width]
2521 catch {unset colormap}
2522 catch {unset rowtextx}
2523 catch {unset idrowranges}
2528 proc setcanvscroll {} {
2529 global canv canv2 canv3 numcommits linespc canvxmax canvy0
2531 set ymax [expr {$canvy0 + ($numcommits - 0.5) * $linespc + 2}]
2532 $canv conf -scrollregion [list 0 0 $canvxmax $ymax]
2533 $canv2 conf -scrollregion [list 0 0 0 $ymax]
2534 $canv3 conf -scrollregion [list 0 0 0 $ymax]
2537 proc visiblerows {} {
2538 global canv numcommits linespc
2540 set ymax [lindex [$canv cget -scrollregion] 3]
2541 if {$ymax eq {} || $ymax == 0} return
2543 set y0 [expr {int([lindex $f 0] * $ymax)}]
2544 set r0 [expr {int(($y0 - 3) / $linespc) - 1}]
2548 set y1 [expr {int([lindex $f 1] * $ymax)}]
2549 set r1 [expr {int(($y1 - 3) / $linespc) + 1}]
2550 if {$r1 >= $numcommits} {
2551 set r1 [expr {$numcommits - 1}]
2553 return [list $r0 $r1]
2556 proc layoutmore {tmax allread} {
2557 global rowlaidout rowoptim commitidx numcommits optim_delay
2558 global uparrowlen curview rowidlist idinlist
2560 set showdelay $optim_delay
2561 set optdelay [expr {$uparrowlen + 1}]
2563 if {$rowoptim - $showdelay > $numcommits} {
2564 showstuff [expr {$rowoptim - $showdelay}]
2565 } elseif {$rowlaidout - $optdelay > $rowoptim} {
2566 set nr [expr {$rowlaidout - $optdelay - $rowoptim}]
2570 optimize_rows $rowoptim 0 [expr {$rowoptim + $nr}]
2572 } elseif {$commitidx($curview) > $rowlaidout} {
2573 set nr [expr {$commitidx($curview) - $rowlaidout}]
2574 # may need to increase this threshold if uparrowlen or
2575 # mingaplen are increased...
2580 set rowlaidout [layoutrows $row [expr {$row + $nr}] $allread]
2581 if {$rowlaidout == $row} {
2584 } elseif {$allread} {
2586 set nrows $commitidx($curview)
2587 if {[lindex $rowidlist $nrows] ne {} ||
2588 [array names idinlist] ne {}} {
2590 set rowlaidout $commitidx($curview)
2591 } elseif {$rowoptim == $nrows} {
2593 if {$numcommits == $nrows} {
2600 if {$tmax ne {} && [clock clicks -milliseconds] >= $tmax} {
2606 proc showstuff {canshow} {
2607 global numcommits commitrow pending_select selectedline
2608 global linesegends idrowranges idrangedrawn curview
2609 global displayorder selectfirst
2611 if {$numcommits == 0} {
2613 set phase "incrdraw"
2617 set numcommits $canshow
2619 set rows [visiblerows]
2620 set r0 [lindex $rows 0]
2621 set r1 [lindex $rows 1]
2623 for {set r $row} {$r < $canshow} {incr r} {
2624 foreach id [lindex $linesegends [expr {$r+1}]] {
2626 foreach {s e} [rowranges $id] {
2628 if {$e ne {} && $e < $numcommits && $s <= $r1 && $e >= $r0
2629 && ![info exists idrangedrawn($id,$i)]} {
2631 set idrangedrawn($id,$i) 1
2636 if {$canshow > $r1} {
2639 while {$row < $canshow} {
2643 if {[info exists pending_select] &&
2644 [info exists commitrow($curview,$pending_select)] &&
2645 $commitrow($curview,$pending_select) < $numcommits} {
2646 selectline $commitrow($curview,$pending_select) 1
2649 if {[info exists selectedline] || [info exists pending_select]} {
2658 proc layoutrows {row endrow last} {
2659 global rowidlist rowoffsets displayorder
2660 global uparrowlen downarrowlen maxwidth mingaplen
2661 global childlist parentlist
2662 global idrowranges linesegends
2663 global commitidx curview
2664 global idinlist rowchk rowrangelist
2666 set idlist [lindex $rowidlist $row]
2667 set offs [lindex $rowoffsets $row]
2668 while {$row < $endrow} {
2669 set id [lindex $displayorder $row]
2672 foreach p [lindex $parentlist $row] {
2673 if {![info exists idinlist($p)]} {
2675 } elseif {!$idinlist($p)} {
2680 set nev [expr {[llength $idlist] + [llength $newolds]
2681 + [llength $oldolds] - $maxwidth + 1}]
2684 $row + $uparrowlen + $mingaplen >= $commitidx($curview)} break
2685 for {set x [llength $idlist]} {[incr x -1] >= 0} {} {
2686 set i [lindex $idlist $x]
2687 if {![info exists rowchk($i)] || $row >= $rowchk($i)} {
2688 set r [usedinrange $i [expr {$row - $downarrowlen}] \
2689 [expr {$row + $uparrowlen + $mingaplen}]]
2691 set idlist [lreplace $idlist $x $x]
2692 set offs [lreplace $offs $x $x]
2693 set offs [incrange $offs $x 1]
2695 set rm1 [expr {$row - 1}]
2697 lappend idrowranges($i) $rm1
2698 if {[incr nev -1] <= 0} break
2701 set rowchk($id) [expr {$row + $r}]
2704 lset rowidlist $row $idlist
2705 lset rowoffsets $row $offs
2707 lappend linesegends $lse
2708 set col [lsearch -exact $idlist $id]
2710 set col [llength $idlist]
2712 lset rowidlist $row $idlist
2714 if {[lindex $childlist $row] ne {}} {
2715 set z [expr {[llength [lindex $rowidlist [expr {$row-1}]]] - $col}]
2719 lset rowoffsets $row $offs
2721 makeuparrow $id $col $row $z
2727 if {[info exists idrowranges($id)]} {
2728 set ranges $idrowranges($id)
2730 unset idrowranges($id)
2732 lappend rowrangelist $ranges
2734 set offs [ntimes [llength $idlist] 0]
2735 set l [llength $newolds]
2736 set idlist [eval lreplace \$idlist $col $col $newolds]
2739 set offs [lrange $offs 0 [expr {$col - 1}]]
2740 foreach x $newolds {
2745 set tmp [expr {[llength $idlist] - [llength $offs]}]
2747 set offs [concat $offs [ntimes $tmp $o]]
2752 foreach i $newolds {
2754 set idrowranges($i) $row
2757 foreach oid $oldolds {
2758 set idinlist($oid) 1
2759 set idlist [linsert $idlist $col $oid]
2760 set offs [linsert $offs $col $o]
2761 makeuparrow $oid $col $row $o
2764 lappend rowidlist $idlist
2765 lappend rowoffsets $offs
2770 proc addextraid {id row} {
2771 global displayorder commitrow commitinfo
2772 global commitidx commitlisted
2773 global parentlist childlist children curview
2775 incr commitidx($curview)
2776 lappend displayorder $id
2777 lappend commitlisted 0
2778 lappend parentlist {}
2779 set commitrow($curview,$id) $row
2781 if {![info exists commitinfo($id)]} {
2782 set commitinfo($id) {"No commit information available"}
2784 if {![info exists children($curview,$id)]} {
2785 set children($curview,$id) {}
2787 lappend childlist $children($curview,$id)
2790 proc layouttail {} {
2791 global rowidlist rowoffsets idinlist commitidx curview
2792 global idrowranges rowrangelist
2794 set row $commitidx($curview)
2795 set idlist [lindex $rowidlist $row]
2796 while {$idlist ne {}} {
2797 set col [expr {[llength $idlist] - 1}]
2798 set id [lindex $idlist $col]
2801 lappend idrowranges($id) $row
2802 lappend rowrangelist $idrowranges($id)
2803 unset idrowranges($id)
2805 set offs [ntimes $col 0]
2806 set idlist [lreplace $idlist $col $col]
2807 lappend rowidlist $idlist
2808 lappend rowoffsets $offs
2811 foreach id [array names idinlist] {
2814 lset rowidlist $row [list $id]
2815 lset rowoffsets $row 0
2816 makeuparrow $id 0 $row 0
2817 lappend idrowranges($id) $row
2818 lappend rowrangelist $idrowranges($id)
2819 unset idrowranges($id)
2821 lappend rowidlist {}
2822 lappend rowoffsets {}
2826 proc insert_pad {row col npad} {
2827 global rowidlist rowoffsets
2829 set pad [ntimes $npad {}]
2830 lset rowidlist $row [eval linsert [list [lindex $rowidlist $row]] $col $pad]
2831 set tmp [eval linsert [list [lindex $rowoffsets $row]] $col $pad]
2832 lset rowoffsets $row [incrange $tmp [expr {$col + $npad}] [expr {-$npad}]]
2835 proc optimize_rows {row col endrow} {
2836 global rowidlist rowoffsets idrowranges displayorder
2838 for {} {$row < $endrow} {incr row} {
2839 set idlist [lindex $rowidlist $row]
2840 set offs [lindex $rowoffsets $row]
2842 for {} {$col < [llength $offs]} {incr col} {
2843 if {[lindex $idlist $col] eq {}} {
2847 set z [lindex $offs $col]
2848 if {$z eq {}} continue
2850 set x0 [expr {$col + $z}]
2851 set y0 [expr {$row - 1}]
2852 set z0 [lindex $rowoffsets $y0 $x0]
2854 set id [lindex $idlist $col]
2855 set ranges [rowranges $id]
2856 if {$ranges ne {} && $y0 > [lindex $ranges 0]} {
2860 # Looking at lines from this row to the previous row,
2861 # make them go straight up if they end in an arrow on
2862 # the previous row; otherwise make them go straight up
2864 if {$z < -1 || ($z < 0 && $isarrow)} {
2865 # Line currently goes left too much;
2866 # insert pads in the previous row, then optimize it
2867 set npad [expr {-1 - $z + $isarrow}]
2868 set offs [incrange $offs $col $npad]
2869 insert_pad $y0 $x0 $npad
2871 optimize_rows $y0 $x0 $row
2873 set z [lindex $offs $col]
2874 set x0 [expr {$col + $z}]
2875 set z0 [lindex $rowoffsets $y0 $x0]
2876 } elseif {$z > 1 || ($z > 0 && $isarrow)} {
2877 # Line currently goes right too much;
2878 # insert pads in this line and adjust the next's rowoffsets
2879 set npad [expr {$z - 1 + $isarrow}]
2880 set y1 [expr {$row + 1}]
2881 set offs2 [lindex $rowoffsets $y1]
2885 if {$z eq {} || $x1 + $z < $col} continue
2886 if {$x1 + $z > $col} {
2889 lset rowoffsets $y1 [incrange $offs2 $x1 $npad]
2892 set pad [ntimes $npad {}]
2893 set idlist [eval linsert \$idlist $col $pad]
2894 set tmp [eval linsert \$offs $col $pad]
2896 set offs [incrange $tmp $col [expr {-$npad}]]
2897 set z [lindex $offs $col]
2900 if {$z0 eq {} && !$isarrow} {
2901 # this line links to its first child on row $row-2
2902 set rm2 [expr {$row - 2}]
2903 set id [lindex $displayorder $rm2]
2904 set xc [lsearch -exact [lindex $rowidlist $rm2] $id]
2906 set z0 [expr {$xc - $x0}]
2909 # avoid lines jigging left then immediately right
2910 if {$z0 ne {} && $z < 0 && $z0 > 0} {
2911 insert_pad $y0 $x0 1
2912 set offs [incrange $offs $col 1]
2913 optimize_rows $y0 [expr {$x0 + 1}] $row
2918 # Find the first column that doesn't have a line going right
2919 for {set col [llength $idlist]} {[incr col -1] >= 0} {} {
2920 set o [lindex $offs $col]
2922 # check if this is the link to the first child
2923 set id [lindex $idlist $col]
2924 set ranges [rowranges $id]
2925 if {$ranges ne {} && $row == [lindex $ranges 0]} {
2926 # it is, work out offset to child
2927 set y0 [expr {$row - 1}]
2928 set id [lindex $displayorder $y0]
2929 set x0 [lsearch -exact [lindex $rowidlist $y0] $id]
2931 set o [expr {$x0 - $col}]
2935 if {$o eq {} || $o <= 0} break
2937 # Insert a pad at that column as long as it has a line and
2938 # isn't the last column, and adjust the next row' offsets
2939 if {$o ne {} && [incr col] < [llength $idlist]} {
2940 set y1 [expr {$row + 1}]
2941 set offs2 [lindex $rowoffsets $y1]
2945 if {$z eq {} || $x1 + $z < $col} continue
2946 lset rowoffsets $y1 [incrange $offs2 $x1 1]
2949 set idlist [linsert $idlist $col {}]
2950 set tmp [linsert $offs $col {}]
2952 set offs [incrange $tmp $col -1]
2955 lset rowidlist $row $idlist
2956 lset rowoffsets $row $offs
2962 global canvx0 linespc
2963 return [expr {$canvx0 + $col * $linespc}]
2967 global canvy0 linespc
2968 return [expr {$canvy0 + $row * $linespc}]
2971 proc linewidth {id} {
2972 global thickerline lthickness
2975 if {[info exists thickerline] && $id eq $thickerline} {
2976 set wid [expr {2 * $lthickness}]
2981 proc rowranges {id} {
2982 global phase idrowranges commitrow rowlaidout rowrangelist curview
2986 ([info exists commitrow($curview,$id)]
2987 && $commitrow($curview,$id) < $rowlaidout)} {
2988 set ranges [lindex $rowrangelist $commitrow($curview,$id)]
2989 } elseif {[info exists idrowranges($id)]} {
2990 set ranges $idrowranges($id)
2995 proc drawlineseg {id i} {
2996 global rowoffsets rowidlist
2998 global canv colormap linespc
2999 global numcommits commitrow curview
3001 set ranges [rowranges $id]
3003 if {[info exists commitrow($curview,$id)]
3004 && $commitrow($curview,$id) < $numcommits} {
3005 set downarrow [expr {$i < [llength $ranges] / 2 - 1}]
3009 set startrow [lindex $ranges [expr {2 * $i}]]
3010 set row [lindex $ranges [expr {2 * $i + 1}]]
3011 if {$startrow == $row} return
3014 set col [lsearch -exact [lindex $rowidlist $row] $id]
3016 puts "oops: drawline: id $id not on row $row"
3022 set o [lindex $rowoffsets $row $col]
3025 # changing direction
3026 set x [xc $row $col]
3028 lappend coords $x $y
3034 set x [xc $row $col]
3036 lappend coords $x $y
3038 # draw the link to the first child as part of this line
3040 set child [lindex $displayorder $row]
3041 set ccol [lsearch -exact [lindex $rowidlist $row] $child]
3043 set x [xc $row $ccol]
3045 if {$ccol < $col - 1} {
3046 lappend coords [xc $row [expr {$col - 1}]] [yc $row]
3047 } elseif {$ccol > $col + 1} {
3048 lappend coords [xc $row [expr {$col + 1}]] [yc $row]
3050 lappend coords $x $y
3053 if {[llength $coords] < 4} return
3055 # This line has an arrow at the lower end: check if the arrow is
3056 # on a diagonal segment, and if so, work around the Tk 8.4
3057 # refusal to draw arrows on diagonal lines.
3058 set x0 [lindex $coords 0]
3059 set x1 [lindex $coords 2]
3061 set y0 [lindex $coords 1]
3062 set y1 [lindex $coords 3]
3063 if {$y0 - $y1 <= 2 * $linespc && $x1 == [lindex $coords 4]} {
3064 # we have a nearby vertical segment, just trim off the diag bit
3065 set coords [lrange $coords 2 end]
3067 set slope [expr {($x0 - $x1) / ($y0 - $y1)}]
3068 set xi [expr {$x0 - $slope * $linespc / 2}]
3069 set yi [expr {$y0 - $linespc / 2}]
3070 set coords [lreplace $coords 0 1 $xi $y0 $xi $yi]
3074 set arrow [expr {2 * ($i > 0) + $downarrow}]
3075 set arrow [lindex {none first last both} $arrow]
3076 set t [$canv create line $coords -width [linewidth $id] \
3077 -fill $colormap($id) -tags lines.$id -arrow $arrow]
3082 proc drawparentlinks {id row col olds} {
3083 global rowidlist canv colormap
3085 set row2 [expr {$row + 1}]
3086 set x [xc $row $col]
3089 set ids [lindex $rowidlist $row2]
3090 # rmx = right-most X coord used
3093 set i [lsearch -exact $ids $p]
3095 puts "oops, parent $p of $id not in list"
3098 set x2 [xc $row2 $i]
3102 set ranges [rowranges $p]
3103 if {$ranges ne {} && $row2 == [lindex $ranges 0]
3104 && $row2 < [lindex $ranges 1]} {
3105 # drawlineseg will do this one for us
3109 # should handle duplicated parents here...
3110 set coords [list $x $y]
3111 if {$i < $col - 1} {
3112 lappend coords [xc $row [expr {$i + 1}]] $y
3113 } elseif {$i > $col + 1} {
3114 lappend coords [xc $row [expr {$i - 1}]] $y
3116 lappend coords $x2 $y2
3117 set t [$canv create line $coords -width [linewidth $p] \
3118 -fill $colormap($p) -tags lines.$p]
3125 proc drawlines {id} {
3126 global colormap canv
3128 global children iddrawn commitrow rowidlist curview
3130 $canv delete lines.$id
3131 set nr [expr {[llength [rowranges $id]] / 2}]
3132 for {set i 0} {$i < $nr} {incr i} {
3133 if {[info exists idrangedrawn($id,$i)]} {
3137 foreach child $children($curview,$id) {
3138 if {[info exists iddrawn($child)]} {
3139 set row $commitrow($curview,$child)
3140 set col [lsearch -exact [lindex $rowidlist $row] $child]
3142 drawparentlinks $child $row $col [list $id]
3148 proc drawcmittext {id row col rmx} {
3149 global linespc canv canv2 canv3 canvy0 fgcolor
3150 global commitlisted commitinfo rowidlist
3151 global rowtextx idpos idtags idheads idotherrefs
3152 global linehtag linentag linedtag
3153 global mainfont canvxmax boldrows boldnamerows fgcolor
3155 set ofill [expr {[lindex $commitlisted $row]? "blue": "white"}]
3156 set x [xc $row $col]
3158 set orad [expr {$linespc / 3}]
3159 set t [$canv create oval [expr {$x - $orad}] [expr {$y - $orad}] \
3160 [expr {$x + $orad - 1}] [expr {$y + $orad - 1}] \
3161 -fill $ofill -outline $fgcolor -width 1 -tags circle]
3163 $canv bind $t <1> {selcanvline {} %x %y}
3164 set xt [xc $row [llength [lindex $rowidlist $row]]]
3168 set rowtextx($row) $xt
3169 set idpos($id) [list $x $xt $y]
3170 if {[info exists idtags($id)] || [info exists idheads($id)]
3171 || [info exists idotherrefs($id)]} {
3172 set xt [drawtags $id $x $xt $y]
3174 set headline [lindex $commitinfo($id) 0]
3175 set name [lindex $commitinfo($id) 1]
3176 set date [lindex $commitinfo($id) 2]
3177 set date [formatdate $date]
3180 set isbold [ishighlighted $row]
3182 lappend boldrows $row
3185 lappend boldnamerows $row
3189 set linehtag($row) [$canv create text $xt $y -anchor w -fill $fgcolor \
3190 -text $headline -font $font -tags text]
3191 $canv bind $linehtag($row) <Button-3> "rowmenu %X %Y $id"
3192 set linentag($row) [$canv2 create text 3 $y -anchor w -fill $fgcolor \
3193 -text $name -font $nfont -tags text]
3194 set linedtag($row) [$canv3 create text 3 $y -anchor w -fill $fgcolor \
3195 -text $date -font $mainfont -tags text]
3196 set xr [expr {$xt + [font measure $mainfont $headline]}]
3197 if {$xr > $canvxmax} {
3203 proc drawcmitrow {row} {
3204 global displayorder rowidlist
3205 global idrangedrawn iddrawn
3206 global commitinfo parentlist numcommits
3207 global filehighlight fhighlights findstring nhighlights
3208 global hlview vhighlights
3209 global highlight_related rhighlights
3211 if {$row >= $numcommits} return
3212 foreach id [lindex $rowidlist $row] {
3213 if {$id eq {}} continue
3215 foreach {s e} [rowranges $id] {
3217 if {$row < $s} continue
3220 if {$e < $numcommits && ![info exists idrangedrawn($id,$i)]} {
3222 set idrangedrawn($id,$i) 1
3229 set id [lindex $displayorder $row]
3230 if {[info exists hlview] && ![info exists vhighlights($row)]} {
3231 askvhighlight $row $id
3233 if {[info exists filehighlight] && ![info exists fhighlights($row)]} {
3234 askfilehighlight $row $id
3236 if {$findstring ne {} && ![info exists nhighlights($row)]} {
3237 askfindhighlight $row $id
3239 if {$highlight_related ne "None" && ![info exists rhighlights($row)]} {
3240 askrelhighlight $row $id
3242 if {[info exists iddrawn($id)]} return
3243 set col [lsearch -exact [lindex $rowidlist $row] $id]
3245 puts "oops, row $row id $id not in list"
3248 if {![info exists commitinfo($id)]} {
3252 set olds [lindex $parentlist $row]
3254 set rmx [drawparentlinks $id $row $col $olds]
3258 drawcmittext $id $row $col $rmx
3262 proc drawfrac {f0 f1} {
3263 global numcommits canv
3266 set ymax [lindex [$canv cget -scrollregion] 3]
3267 if {$ymax eq {} || $ymax == 0} return
3268 set y0 [expr {int($f0 * $ymax)}]
3269 set row [expr {int(($y0 - 3) / $linespc) - 1}]
3273 set y1 [expr {int($f1 * $ymax)}]
3274 set endrow [expr {int(($y1 - 3) / $linespc) + 1}]
3275 if {$endrow >= $numcommits} {
3276 set endrow [expr {$numcommits - 1}]
3278 for {} {$row <= $endrow} {incr row} {
3283 proc drawvisible {} {
3285 eval drawfrac [$canv yview]
3288 proc clear_display {} {
3289 global iddrawn idrangedrawn
3290 global vhighlights fhighlights nhighlights rhighlights
3293 catch {unset iddrawn}
3294 catch {unset idrangedrawn}
3295 catch {unset vhighlights}
3296 catch {unset fhighlights}
3297 catch {unset nhighlights}
3298 catch {unset rhighlights}
3301 proc findcrossings {id} {
3302 global rowidlist parentlist numcommits rowoffsets displayorder
3306 foreach {s e} [rowranges $id] {
3307 if {$e >= $numcommits} {
3308 set e [expr {$numcommits - 1}]
3310 if {$e <= $s} continue
3311 set x [lsearch -exact [lindex $rowidlist $e] $id]
3313 puts "findcrossings: oops, no [shortids $id] in row $e"
3316 for {set row $e} {[incr row -1] >= $s} {} {
3317 set olds [lindex $parentlist $row]
3318 set kid [lindex $displayorder $row]
3319 set kidx [lsearch -exact [lindex $rowidlist $row] $kid]
3320 if {$kidx < 0} continue
3321 set nextrow [lindex $rowidlist [expr {$row + 1}]]
3323 set px [lsearch -exact $nextrow $p]
3324 if {$px < 0} continue
3325 if {($kidx < $x && $x < $px) || ($px < $x && $x < $kidx)} {
3326 if {[lsearch -exact $ccross $p] >= 0} continue
3327 if {$x == $px + ($kidx < $px? -1: 1)} {
3329 } elseif {[lsearch -exact $cross $p] < 0} {
3334 set inc [lindex $rowoffsets $row $x]
3335 if {$inc eq {}} break
3339 return [concat $ccross {{}} $cross]
3342 proc assigncolor {id} {
3343 global colormap colors nextcolor
3344 global commitrow parentlist children children curview
3346 if {[info exists colormap($id)]} return
3347 set ncolors [llength $colors]
3348 if {[info exists children($curview,$id)]} {
3349 set kids $children($curview,$id)
3353 if {[llength $kids] == 1} {
3354 set child [lindex $kids 0]
3355 if {[info exists colormap($child)]
3356 && [llength [lindex $parentlist $commitrow($curview,$child)]] == 1} {
3357 set colormap($id) $colormap($child)
3363 foreach x [findcrossings $id] {
3365 # delimiter between corner crossings and other crossings
3366 if {[llength $badcolors] >= $ncolors - 1} break
3367 set origbad $badcolors
3369 if {[info exists colormap($x)]
3370 && [lsearch -exact $badcolors $colormap($x)] < 0} {
3371 lappend badcolors $colormap($x)
3374 if {[llength $badcolors] >= $ncolors} {
3375 set badcolors $origbad
3377 set origbad $badcolors
3378 if {[llength $badcolors] < $ncolors - 1} {
3379 foreach child $kids {
3380 if {[info exists colormap($child)]
3381 && [lsearch -exact $badcolors $colormap($child)] < 0} {
3382 lappend badcolors $colormap($child)
3384 foreach p [lindex $parentlist $commitrow($curview,$child)] {
3385 if {[info exists colormap($p)]
3386 && [lsearch -exact $badcolors $colormap($p)] < 0} {
3387 lappend badcolors $colormap($p)
3391 if {[llength $badcolors] >= $ncolors} {
3392 set badcolors $origbad
3395 for {set i 0} {$i <= $ncolors} {incr i} {
3396 set c [lindex $colors $nextcolor]
3397 if {[incr nextcolor] >= $ncolors} {
3400 if {[lsearch -exact $badcolors $c]} break
3402 set colormap($id) $c
3405 proc bindline {t id} {
3408 $canv bind $t <Enter> "lineenter %x %y $id"
3409 $canv bind $t <Motion> "linemotion %x %y $id"
3410 $canv bind $t <Leave> "lineleave $id"
3411 $canv bind $t <Button-1> "lineclick %x %y $id 1"
3414 proc drawtags {id x xt y1} {
3415 global idtags idheads idotherrefs mainhead
3416 global linespc lthickness
3417 global canv mainfont commitrow rowtextx curview fgcolor bgcolor
3422 if {[info exists idtags($id)]} {
3423 set marks $idtags($id)
3424 set ntags [llength $marks]
3426 if {[info exists idheads($id)]} {
3427 set marks [concat $marks $idheads($id)]
3428 set nheads [llength $idheads($id)]
3430 if {[info exists idotherrefs($id)]} {
3431 set marks [concat $marks $idotherrefs($id)]
3437 set delta [expr {int(0.5 * ($linespc - $lthickness))}]
3438 set yt [expr {$y1 - 0.5 * $linespc}]
3439 set yb [expr {$yt + $linespc - 1}]
3443 foreach tag $marks {
3445 if {$i >= $ntags && $i < $ntags + $nheads && $tag eq $mainhead} {
3446 set wid [font measure [concat $mainfont bold] $tag]
3448 set wid [font measure $mainfont $tag]
3452 set xt [expr {$xt + $delta + $wid + $lthickness + $linespc}]
3454 set t [$canv create line $x $y1 [lindex $xvals end] $y1 \
3455 -width $lthickness -fill black -tags tag.$id]
3457 foreach tag $marks x $xvals wid $wvals {
3458 set xl [expr {$x + $delta}]
3459 set xr [expr {$x + $delta + $wid + $lthickness}]
3461 if {[incr ntags -1] >= 0} {
3463 set t [$canv create polygon $x [expr {$yt + $delta}] $xl $yt \
3464 $xr $yt $xr $yb $xl $yb $x [expr {$yb - $delta}] \
3465 -width 1 -outline black -fill yellow -tags tag.$id]
3466 $canv bind $t <1> [list showtag $tag 1]
3467 set rowtextx($commitrow($curview,$id)) [expr {$xr + $linespc}]
3469 # draw a head or other ref
3470 if {[incr nheads -1] >= 0} {
3472 if {$tag eq $mainhead} {
3478 set xl [expr {$xl - $delta/2}]
3479 $canv create polygon $x $yt $xr $yt $xr $yb $x $yb \
3480 -width 1 -outline black -fill $col -tags tag.$id
3481 if {[regexp {^(remotes/.*/|remotes/)} $tag match remoteprefix]} {
3482 set rwid [font measure $mainfont $remoteprefix]
3483 set xi [expr {$x + 1}]
3484 set yti [expr {$yt + 1}]
3485 set xri [expr {$x + $rwid}]
3486 $canv create polygon $xi $yti $xri $yti $xri $yb $xi $yb \
3487 -width 0 -fill "#ffddaa" -tags tag.$id
3490 set t [$canv create text $xl $y1 -anchor w -text $tag -fill $fgcolor \
3491 -font $font -tags [list tag.$id text]]
3493 $canv bind $t <1> [list showtag $tag 1]
3494 } elseif {$nheads >= 0} {
3495 $canv bind $t <Button-3> [list headmenu %X %Y $id $tag]
3501 proc xcoord {i level ln} {
3502 global canvx0 xspc1 xspc2
3504 set x [expr {$canvx0 + $i * $xspc1($ln)}]
3505 if {$i > 0 && $i == $level} {
3506 set x [expr {$x + 0.5 * ($xspc2 - $xspc1($ln))}]
3507 } elseif {$i > $level} {
3508 set x [expr {$x + $xspc2 - $xspc1($ln)}]
3513 proc show_status {msg} {
3514 global canv mainfont fgcolor
3517 $canv create text 3 3 -anchor nw -text $msg -font $mainfont \
3518 -tags text -fill $fgcolor
3521 # Insert a new commit as the child of the commit on row $row.
3522 # The new commit will be displayed on row $row and the commits
3523 # on that row and below will move down one row.
3524 proc insertrow {row newcmit} {
3525 global displayorder parentlist childlist commitlisted
3526 global commitrow curview rowidlist rowoffsets numcommits
3527 global rowrangelist idrowranges rowlaidout rowoptim numcommits
3528 global linesegends selectedline
3530 if {$row >= $numcommits} {
3531 puts "oops, inserting new row $row but only have $numcommits rows"
3534 set p [lindex $displayorder $row]
3535 set displayorder [linsert $displayorder $row $newcmit]
3536 set parentlist [linsert $parentlist $row $p]
3537 set kids [lindex $childlist $row]
3538 lappend kids $newcmit
3539 lset childlist $row $kids
3540 set childlist [linsert $childlist $row {}]
3541 set commitlisted [linsert $commitlisted $row 1]
3542 set l [llength $displayorder]
3543 for {set r $row} {$r < $l} {incr r} {
3544 set id [lindex $displayorder $r]
3545 set commitrow($curview,$id) $r
3548 set idlist [lindex $rowidlist $row]
3549 set offs [lindex $rowoffsets $row]
3552 if {$x eq {} || ($x eq $p && [llength $kids] == 1)} {
3558 if {[llength $kids] == 1} {
3559 set col [lsearch -exact $idlist $p]
3560 lset idlist $col $newcmit
3562 set col [llength $idlist]
3563 lappend idlist $newcmit
3565 lset rowoffsets $row $offs
3567 set rowidlist [linsert $rowidlist $row $idlist]
3568 set rowoffsets [linsert $rowoffsets [expr {$row+1}] $newoffs]
3570 set rowrangelist [linsert $rowrangelist $row {}]
3571 set l [llength $rowrangelist]
3572 for {set r 0} {$r < $l} {incr r} {
3573 set ranges [lindex $rowrangelist $r]
3574 if {$ranges ne {} && [lindex $ranges end] >= $row} {
3578 lappend newranges [expr {$x + 1}]
3580 lappend newranges $x
3583 lset rowrangelist $r $newranges
3586 if {[llength $kids] > 1} {
3587 set rp1 [expr {$row + 1}]
3588 set ranges [lindex $rowrangelist $rp1]
3589 if {$ranges eq {}} {
3590 set ranges [list $row $rp1]
3591 } elseif {[lindex $ranges end-1] == $rp1} {
3592 lset ranges end-1 $row
3594 lset rowrangelist $rp1 $ranges
3596 foreach id [array names idrowranges] {
3597 set ranges $idrowranges($id)
3598 if {$ranges ne {} && [lindex $ranges end] >= $row} {
3602 lappend newranges [expr {$x + 1}]
3604 lappend newranges $x
3607 set idrowranges($id) $newranges
3611 set linesegends [linsert $linesegends $row {}]
3617 if {[info exists selectedline] && $selectedline >= $row} {
3623 # Don't change the text pane cursor if it is currently the hand cursor,
3624 # showing that we are over a sha1 ID link.
3625 proc settextcursor {c} {
3626 global ctext curtextcursor
3628 if {[$ctext cget -cursor] == $curtextcursor} {
3629 $ctext config -cursor $c
3631 set curtextcursor $c
3634 proc nowbusy {what} {
3637 if {[array names isbusy] eq {}} {
3638 . config -cursor watch
3644 proc notbusy {what} {
3645 global isbusy maincursor textcursor
3647 catch {unset isbusy($what)}
3648 if {[array names isbusy] eq {}} {
3649 . config -cursor $maincursor
3650 settextcursor $textcursor
3654 proc findmatches {f} {
3655 global findtype foundstring foundstrlen
3656 if {$findtype == "Regexp"} {
3657 set matches [regexp -indices -all -inline $foundstring $f]
3659 if {$findtype == "IgnCase"} {
3660 set str [string tolower $f]
3666 while {[set j [string first $foundstring $str $i]] >= 0} {
3667 lappend matches [list $j [expr {$j+$foundstrlen-1}]]
3668 set i [expr {$j + $foundstrlen}]
3675 global findtype findloc findstring markedmatches commitinfo
3676 global numcommits displayorder linehtag linentag linedtag
3677 global mainfont canv canv2 canv3 selectedline
3678 global matchinglines foundstring foundstrlen matchstring
3683 cancel_next_highlight
3685 set matchinglines {}
3686 if {$findtype == "IgnCase"} {
3687 set foundstring [string tolower $findstring]
3689 set foundstring $findstring
3691 set foundstrlen [string length $findstring]
3692 if {$foundstrlen == 0} return
3693 regsub -all {[*?\[\\]} $foundstring {\\&} matchstring
3694 set matchstring "*$matchstring*"
3695 if {![info exists selectedline]} {
3698 set oldsel $selectedline
3701 set fldtypes {Headline Author Date Committer CDate Comments}
3703 foreach id $displayorder {
3704 set d $commitdata($id)
3706 if {$findtype == "Regexp"} {
3707 set doesmatch [regexp $foundstring $d]
3708 } elseif {$findtype == "IgnCase"} {
3709 set doesmatch [string match -nocase $matchstring $d]
3711 set doesmatch [string match $matchstring $d]
3713 if {!$doesmatch} continue
3714 if {![info exists commitinfo($id)]} {
3717 set info $commitinfo($id)
3719 foreach f $info ty $fldtypes {
3720 if {$findloc != "All fields" && $findloc != $ty} {
3723 set matches [findmatches $f]
3724 if {$matches == {}} continue
3726 if {$ty == "Headline"} {
3728 markmatches $canv $l $f $linehtag($l) $matches $mainfont
3729 } elseif {$ty == "Author"} {
3731 markmatches $canv2 $l $f $linentag($l) $matches $mainfont
3732 } elseif {$ty == "Date"} {
3734 markmatches $canv3 $l $f $linedtag($l) $matches $mainfont
3738 lappend matchinglines $l
3739 if {!$didsel && $l > $oldsel} {
3745 if {$matchinglines == {}} {
3747 } elseif {!$didsel} {
3748 findselectline [lindex $matchinglines 0]
3752 proc findselectline {l} {
3753 global findloc commentend ctext
3755 if {$findloc == "All fields" || $findloc == "Comments"} {
3756 # highlight the matches in the comments
3757 set f [$ctext get 1.0 $commentend]
3758 set matches [findmatches $f]
3759 foreach match $matches {
3760 set start [lindex $match 0]
3761 set end [expr {[lindex $match 1] + 1}]
3762 $ctext tag add found "1.0 + $start c" "1.0 + $end c"
3767 proc findnext {restart} {
3768 global matchinglines selectedline
3769 if {![info exists matchinglines]} {
3775 if {![info exists selectedline]} return
3776 foreach l $matchinglines {
3777 if {$l > $selectedline} {
3786 global matchinglines selectedline
3787 if {![info exists matchinglines]} {
3791 if {![info exists selectedline]} return
3793 foreach l $matchinglines {
3794 if {$l >= $selectedline} break
3798 findselectline $prev
3804 proc stopfindproc {{done 0}} {
3805 global findprocpid findprocfile findids
3806 global ctext findoldcursor phase maincursor textcursor
3807 global findinprogress
3809 catch {unset findids}
3810 if {[info exists findprocpid]} {
3812 catch {exec kill $findprocpid}
3814 catch {close $findprocfile}
3817 catch {unset findinprogress}
3821 # mark a commit as matching by putting a yellow background
3822 # behind the headline
3823 proc markheadline {l id} {
3824 global canv mainfont linehtag
3827 set bbox [$canv bbox $linehtag($l)]
3828 set t [$canv create rect $bbox -outline {} -tags matches -fill yellow]
3832 # mark the bits of a headline, author or date that match a find string
3833 proc markmatches {canv l str tag matches font} {
3834 set bbox [$canv bbox $tag]
3835 set x0 [lindex $bbox 0]
3836 set y0 [lindex $bbox 1]
3837 set y1 [lindex $bbox 3]
3838 foreach match $matches {
3839 set start [lindex $match 0]
3840 set end [lindex $match 1]
3841 if {$start > $end} continue
3842 set xoff [font measure $font [string range $str 0 [expr {$start-1}]]]
3843 set xlen [font measure $font [string range $str 0 [expr {$end}]]]
3844 set t [$canv create rect [expr {$x0+$xoff}] $y0 \
3845 [expr {$x0+$xlen+2}] $y1 \
3846 -outline {} -tags matches -fill yellow]
3851 proc unmarkmatches {} {
3852 global matchinglines findids
3853 allcanvs delete matches
3854 catch {unset matchinglines}
3855 catch {unset findids}
3858 proc selcanvline {w x y} {
3859 global canv canvy0 ctext linespc
3861 set ymax [lindex [$canv cget -scrollregion] 3]
3862 if {$ymax == {}} return
3863 set yfrac [lindex [$canv yview] 0]
3864 set y [expr {$y + $yfrac * $ymax}]
3865 set l [expr {int(($y - $canvy0) / $linespc + 0.5)}]
3870 if {![info exists rowtextx($l)] || $x < $rowtextx($l)} return
3876 proc commit_descriptor {p} {
3878 if {![info exists commitinfo($p)]} {
3882 if {[llength $commitinfo($p)] > 1} {
3883 set l [lindex $commitinfo($p) 0]
3888 # append some text to the ctext widget, and make any SHA1 ID
3889 # that we know about be a clickable link.
3890 proc appendwithlinks {text tags} {
3891 global ctext commitrow linknum curview
3893 set start [$ctext index "end - 1c"]
3894 $ctext insert end $text $tags
3895 set links [regexp -indices -all -inline {[0-9a-f]{40}} $text]
3899 set linkid [string range $text $s $e]
3900 if {![info exists commitrow($curview,$linkid)]} continue
3902 $ctext tag add link "$start + $s c" "$start + $e c"
3903 $ctext tag add link$linknum "$start + $s c" "$start + $e c"
3904 $ctext tag bind link$linknum <1> \
3905 [list selectline $commitrow($curview,$linkid) 1]
3908 $ctext tag conf link -foreground blue -underline 1
3909 $ctext tag bind link <Enter> { %W configure -cursor hand2 }
3910 $ctext tag bind link <Leave> { %W configure -cursor $curtextcursor }
3913 proc viewnextline {dir} {
3917 set ymax [lindex [$canv cget -scrollregion] 3]
3918 set wnow [$canv yview]
3919 set wtop [expr {[lindex $wnow 0] * $ymax}]
3920 set newtop [expr {$wtop + $dir * $linespc}]
3923 } elseif {$newtop > $ymax} {
3926 allcanvs yview moveto [expr {$newtop * 1.0 / $ymax}]
3929 # add a list of tag or branch names at position pos
3930 # returns the number of names inserted
3931 proc appendrefs {pos ids var} {
3932 global ctext commitrow linknum curview $var maxrefs
3934 if {[catch {$ctext index $pos}]} {
3937 $ctext conf -state normal
3938 $ctext delete $pos "$pos lineend"
3941 foreach tag [set $var\($id\)] {
3942 lappend tags [list $tag $id]
3945 if {[llength $tags] > $maxrefs} {
3946 $ctext insert $pos "many ([llength $tags])"
3948 set tags [lsort -index 0 -decreasing $tags]
3951 set id [lindex $ti 1]
3954 $ctext tag delete $lk
3955 $ctext insert $pos $sep
3956 $ctext insert $pos [lindex $ti 0] $lk
3957 if {[info exists commitrow($curview,$id)]} {
3958 $ctext tag conf $lk -foreground blue
3959 $ctext tag bind $lk <1> \
3960 [list selectline $commitrow($curview,$id) 1]
3961 $ctext tag conf $lk -underline 1
3962 $ctext tag bind $lk <Enter> { %W configure -cursor hand2 }
3963 $ctext tag bind $lk <Leave> \
3964 { %W configure -cursor $curtextcursor }
3969 $ctext conf -state disabled
3970 return [llength $tags]
3973 # called when we have finished computing the nearby tags
3974 proc dispneartags {delay} {
3975 global selectedline currentid showneartags tagphase
3977 if {![info exists selectedline] || !$showneartags} return
3978 after cancel dispnexttag
3980 after 200 dispnexttag
3983 after idle dispnexttag
3988 proc dispnexttag {} {
3989 global selectedline currentid showneartags tagphase ctext
3991 if {![info exists selectedline] || !$showneartags} return
3992 switch -- $tagphase {
3994 set dtags [desctags $currentid]
3996 appendrefs precedes $dtags idtags
4000 set atags [anctags $currentid]
4002 appendrefs follows $atags idtags
4006 set dheads [descheads $currentid]
4007 if {$dheads ne {}} {
4008 if {[appendrefs branch $dheads idheads] > 1
4009 && [$ctext get "branch -3c"] eq "h"} {
4010 # turn "Branch" into "Branches"
4011 $ctext conf -state normal
4012 $ctext insert "branch -2c" "es"
4013 $ctext conf -state disabled
4018 if {[incr tagphase] <= 2} {
4019 after idle dispnexttag
4023 proc selectline {l isnew} {
4024 global canv canv2 canv3 ctext commitinfo selectedline
4025 global displayorder linehtag linentag linedtag
4026 global canvy0 linespc parentlist childlist
4027 global currentid sha1entry
4028 global commentend idtags linknum
4029 global mergemax numcommits pending_select
4030 global cmitmode showneartags allcommits
4032 catch {unset pending_select}
4035 cancel_next_highlight
4036 if {$l < 0 || $l >= $numcommits} return
4037 set y [expr {$canvy0 + $l * $linespc}]
4038 set ymax [lindex [$canv cget -scrollregion] 3]
4039 set ytop [expr {$y - $linespc - 1}]
4040 set ybot [expr {$y + $linespc + 1}]
4041 set wnow [$canv yview]
4042 set wtop [expr {[lindex $wnow 0] * $ymax}]
4043 set wbot [expr {[lindex $wnow 1] * $ymax}]
4044 set wh [expr {$wbot - $wtop}]
4046 if {$ytop < $wtop} {
4047 if {$ybot < $wtop} {
4048 set newtop [expr {$y - $wh / 2.0}]
4051 if {$newtop > $wtop - $linespc} {
4052 set newtop [expr {$wtop - $linespc}]
4055 } elseif {$ybot > $wbot} {
4056 if {$ytop > $wbot} {
4057 set newtop [expr {$y - $wh / 2.0}]
4059 set newtop [expr {$ybot - $wh}]
4060 if {$newtop < $wtop + $linespc} {
4061 set newtop [expr {$wtop + $linespc}]
4065 if {$newtop != $wtop} {
4069 allcanvs yview moveto [expr {$newtop * 1.0 / $ymax}]
4073 if {![info exists linehtag($l)]} return
4075 set t [eval $canv create rect [$canv bbox $linehtag($l)] -outline {{}} \
4076 -tags secsel -fill [$canv cget -selectbackground]]
4078 $canv2 delete secsel
4079 set t [eval $canv2 create rect [$canv2 bbox $linentag($l)] -outline {{}} \
4080 -tags secsel -fill [$canv2 cget -selectbackground]]
4082 $canv3 delete secsel
4083 set t [eval $canv3 create rect [$canv3 bbox $linedtag($l)] -outline {{}} \
4084 -tags secsel -fill [$canv3 cget -selectbackground]]
4088 addtohistory [list selectline $l 0]
4093 set id [lindex $displayorder $l]
4095 $sha1entry delete 0 end
4096 $sha1entry insert 0 $id
4097 $sha1entry selection from 0
4098 $sha1entry selection to end
4101 $ctext conf -state normal
4104 set info $commitinfo($id)
4105 set date [formatdate [lindex $info 2]]
4106 $ctext insert end "Author: [lindex $info 1] $date\n"
4107 set date [formatdate [lindex $info 4]]
4108 $ctext insert end "Committer: [lindex $info 3] $date\n"
4109 if {[info exists idtags($id)]} {
4110 $ctext insert end "Tags:"
4111 foreach tag $idtags($id) {
4112 $ctext insert end " $tag"
4114 $ctext insert end "\n"
4118 set olds [lindex $parentlist $l]
4119 if {[llength $olds] > 1} {
4122 if {$np >= $mergemax} {
4127 $ctext insert end "Parent: " $tag
4128 appendwithlinks [commit_descriptor $p] {}
4133 append headers "Parent: [commit_descriptor $p]"
4137 foreach c [lindex $childlist $l] {
4138 append headers "Child: [commit_descriptor $c]"
4141 # make anything that looks like a SHA1 ID be a clickable link
4142 appendwithlinks $headers {}
4143 if {$showneartags} {
4144 if {![info exists allcommits]} {
4147 $ctext insert end "Branch: "
4148 $ctext mark set branch "end -1c"
4149 $ctext mark gravity branch left
4150 $ctext insert end "\nFollows: "
4151 $ctext mark set follows "end -1c"
4152 $ctext mark gravity follows left
4153 $ctext insert end "\nPrecedes: "
4154 $ctext mark set precedes "end -1c"
4155 $ctext mark gravity precedes left
4156 $ctext insert end "\n"
4159 $ctext insert end "\n"
4160 appendwithlinks [lindex $info 5] {comment}
4162 $ctext tag delete Comments
4163 $ctext tag remove found 1.0 end
4164 $ctext conf -state disabled
4165 set commentend [$ctext index "end - 1c"]
4167 init_flist "Comments"
4168 if {$cmitmode eq "tree"} {
4170 } elseif {[llength $olds] <= 1} {
4177 proc selfirstline {} {
4182 proc sellastline {} {
4185 set l [expr {$numcommits - 1}]
4189 proc selnextline {dir} {
4191 if {![info exists selectedline]} return
4192 set l [expr {$selectedline + $dir}]
4197 proc selnextpage {dir} {
4198 global canv linespc selectedline numcommits
4200 set lpp [expr {([winfo height $canv] - 2) / $linespc}]
4204 allcanvs yview scroll [expr {$dir * $lpp}] units
4206 if {![info exists selectedline]} return
4207 set l [expr {$selectedline + $dir * $lpp}]
4210 } elseif {$l >= $numcommits} {
4211 set l [expr $numcommits - 1]
4217 proc unselectline {} {
4218 global selectedline currentid
4220 catch {unset selectedline}
4221 catch {unset currentid}
4222 allcanvs delete secsel
4224 cancel_next_highlight
4227 proc reselectline {} {
4230 if {[info exists selectedline]} {
4231 selectline $selectedline 0
4235 proc addtohistory {cmd} {
4236 global history historyindex curview
4238 set elt [list $curview $cmd]
4239 if {$historyindex > 0
4240 && [lindex $history [expr {$historyindex - 1}]] == $elt} {
4244 if {$historyindex < [llength $history]} {
4245 set history [lreplace $history $historyindex end $elt]
4247 lappend history $elt
4250 if {$historyindex > 1} {
4251 .tf.bar.leftbut conf -state normal
4253 .tf.bar.leftbut conf -state disabled
4255 .tf.bar.rightbut conf -state disabled
4261 set view [lindex $elt 0]
4262 set cmd [lindex $elt 1]
4263 if {$curview != $view} {
4270 global history historyindex
4272 if {$historyindex > 1} {
4273 incr historyindex -1
4274 godo [lindex $history [expr {$historyindex - 1}]]
4275 .tf.bar.rightbut conf -state normal
4277 if {$historyindex <= 1} {
4278 .tf.bar.leftbut conf -state disabled
4283 global history historyindex
4285 if {$historyindex < [llength $history]} {
4286 set cmd [lindex $history $historyindex]
4289 .tf.bar.leftbut conf -state normal
4291 if {$historyindex >= [llength $history]} {
4292 .tf.bar.rightbut conf -state disabled
4297 global treefilelist treeidlist diffids diffmergeid treepending
4300 catch {unset diffmergeid}
4301 if {![info exists treefilelist($id)]} {
4302 if {![info exists treepending]} {
4303 if {[catch {set gtf [open [concat | git ls-tree -r $id] r]}]} {
4307 set treefilelist($id) {}
4308 set treeidlist($id) {}
4309 fconfigure $gtf -blocking 0
4310 filerun $gtf [list gettreeline $gtf $id]
4317 proc gettreeline {gtf id} {
4318 global treefilelist treeidlist treepending cmitmode diffids
4321 while {[incr nl] <= 1000 && [gets $gtf line] >= 0} {
4322 set tl [split $line "\t"]
4323 if {[lindex $tl 0 1] ne "blob"} continue
4324 set sha1 [lindex $tl 0 2]
4325 set fname [lindex $tl 1]
4326 if {[string index $fname 0] eq "\""} {
4327 set fname [lindex $fname 0]
4329 lappend treeidlist($id) $sha1
4330 lappend treefilelist($id) $fname
4333 return [expr {$nl >= 1000? 2: 1}]
4337 if {$cmitmode ne "tree"} {
4338 if {![info exists diffmergeid]} {
4339 gettreediffs $diffids
4341 } elseif {$id ne $diffids} {
4350 global treefilelist treeidlist diffids
4351 global ctext commentend
4353 set i [lsearch -exact $treefilelist($diffids) $f]
4355 puts "oops, $f not in list for id $diffids"
4358 set blob [lindex $treeidlist($diffids) $i]
4359 if {[catch {set bf [open [concat | git cat-file blob $blob] r]} err]} {
4360 puts "oops, error reading blob $blob: $err"
4363 fconfigure $bf -blocking 0
4364 filerun $bf [list getblobline $bf $diffids]
4365 $ctext config -state normal
4366 clear_ctext $commentend
4367 $ctext insert end "\n"
4368 $ctext insert end "$f\n" filesep
4369 $ctext config -state disabled
4370 $ctext yview $commentend
4373 proc getblobline {bf id} {
4374 global diffids cmitmode ctext
4376 if {$id ne $diffids || $cmitmode ne "tree"} {
4380 $ctext config -state normal
4382 while {[incr nl] <= 1000 && [gets $bf line] >= 0} {
4383 $ctext insert end "$line\n"
4386 # delete last newline
4387 $ctext delete "end - 2c" "end - 1c"
4391 $ctext config -state disabled
4392 return [expr {$nl >= 1000? 2: 1}]
4395 proc mergediff {id l} {
4396 global diffmergeid diffopts mdifffd
4402 # this doesn't seem to actually affect anything...
4403 set env(GIT_DIFF_OPTS) $diffopts
4404 set cmd [concat | git diff-tree --no-commit-id --cc $id]
4405 if {[catch {set mdf [open $cmd r]} err]} {
4406 error_popup "Error getting merge diffs: $err"
4409 fconfigure $mdf -blocking 0
4410 set mdifffd($id) $mdf
4411 set np [llength [lindex $parentlist $l]]
4412 filerun $mdf [list getmergediffline $mdf $id $np]
4415 proc getmergediffline {mdf id np} {
4416 global diffmergeid ctext cflist mergemax
4417 global difffilestart mdifffd
4419 $ctext conf -state normal
4421 while {[incr nr] <= 1000 && [gets $mdf line] >= 0} {
4422 if {![info exists diffmergeid] || $id != $diffmergeid
4423 || $mdf != $mdifffd($id)} {
4427 if {[regexp {^diff --cc (.*)} $line match fname]} {
4428 # start of a new file
4429 $ctext insert end "\n"
4430 set here [$ctext index "end - 1c"]
4431 lappend difffilestart $here
4432 add_flist [list $fname]
4433 set l [expr {(78 - [string length $fname]) / 2}]
4434 set pad [string range "----------------------------------------" 1 $l]
4435 $ctext insert end "$pad $fname $pad\n" filesep
4436 } elseif {[regexp {^@@} $line]} {
4437 $ctext insert end "$line\n" hunksep
4438 } elseif {[regexp {^[0-9a-f]{40}$} $line] || [regexp {^index} $line]} {
4441 # parse the prefix - one ' ', '-' or '+' for each parent
4446 for {set j 0} {$j < $np} {incr j} {
4447 set c [string range $line $j $j]
4450 } elseif {$c == "-"} {
4452 } elseif {$c == "+"} {
4461 if {!$isbad && $minuses ne {} && $pluses eq {}} {
4462 # line doesn't appear in result, parents in $minuses have the line
4463 set num [lindex $minuses 0]
4464 } elseif {!$isbad && $pluses ne {} && $minuses eq {}} {
4465 # line appears in result, parents in $pluses don't have the line
4466 lappend tags mresult
4467 set num [lindex $spaces 0]
4470 if {$num >= $mergemax} {
4475 $ctext insert end "$line\n" $tags
4478 $ctext conf -state disabled
4483 return [expr {$nr >= 1000? 2: 1}]
4486 proc startdiff {ids} {
4487 global treediffs diffids treepending diffmergeid
4490 catch {unset diffmergeid}
4491 if {![info exists treediffs($ids)]} {
4492 if {![info exists treepending]} {
4500 proc addtocflist {ids} {
4501 global treediffs cflist
4502 add_flist $treediffs($ids)
4506 proc gettreediffs {ids} {
4507 global treediff treepending
4508 set treepending $ids
4511 {set gdtf [open [concat | git diff-tree --no-commit-id -r $ids] r]} \
4513 fconfigure $gdtf -blocking 0
4514 filerun $gdtf [list gettreediffline $gdtf $ids]
4517 proc gettreediffline {gdtf ids} {
4518 global treediff treediffs treepending diffids diffmergeid
4522 while {[incr nr] <= 1000 && [gets $gdtf line] >= 0} {
4523 set file [lindex $line 5]
4524 lappend treediff $file
4527 return [expr {$nr >= 1000? 2: 1}]
4530 set treediffs($ids) $treediff
4532 if {$cmitmode eq "tree"} {
4534 } elseif {$ids != $diffids} {
4535 if {![info exists diffmergeid]} {
4536 gettreediffs $diffids
4544 proc getblobdiffs {ids} {
4545 global diffopts blobdifffd diffids env curdifftag curtagstart
4546 global diffinhdr treediffs
4548 set env(GIT_DIFF_OPTS) $diffopts
4549 set cmd [concat | git diff-tree --no-commit-id -r -p -C $ids]
4550 if {[catch {set bdf [open $cmd r]} err]} {
4551 puts "error getting diffs: $err"
4555 fconfigure $bdf -blocking 0
4556 set blobdifffd($ids) $bdf
4557 set curdifftag Comments
4559 filerun $bdf [list getblobdiffline $bdf $diffids]
4562 proc setinlist {var i val} {
4565 while {[llength [set $var]] < $i} {
4568 if {[llength [set $var]] == $i} {
4575 proc getblobdiffline {bdf ids} {
4576 global diffids blobdifffd ctext curdifftag curtagstart
4577 global diffnexthead diffnextnote difffilestart
4578 global diffinhdr treediffs
4581 $ctext conf -state normal
4582 while {[incr nr] <= 1000 && [gets $bdf line] >= 0} {
4583 if {$ids != $diffids || $bdf != $blobdifffd($ids)} {
4587 if {[regexp {^diff --git a/(.*) b/(.*)} $line match fname newname]} {
4588 # start of a new file
4589 $ctext insert end "\n"
4590 $ctext tag add $curdifftag $curtagstart end
4591 set here [$ctext index "end - 1c"]
4592 set curtagstart $here
4594 set i [lsearch -exact $treediffs($ids) $fname]
4596 setinlist difffilestart $i $here
4598 if {$newname ne $fname} {
4599 set i [lsearch -exact $treediffs($ids) $newname]
4601 setinlist difffilestart $i $here
4604 set curdifftag "f:$fname"
4605 $ctext tag delete $curdifftag
4606 set l [expr {(78 - [string length $header]) / 2}]
4607 set pad [string range "----------------------------------------" \
4609 $ctext insert end "$pad $header $pad\n" filesep
4611 } elseif {$diffinhdr && [string compare -length 3 $line "---"] == 0} {
4613 } elseif {$diffinhdr && [string compare -length 3 $line "+++"] == 0} {
4615 } elseif {[regexp {^@@ -([0-9]+),([0-9]+) \+([0-9]+),([0-9]+) @@(.*)} \
4616 $line match f1l f1c f2l f2c rest]} {
4617 $ctext insert end "$line\n" hunksep
4620 set x [string range $line 0 0]
4621 if {$x == "-" || $x == "+"} {
4622 set tag [expr {$x == "+"}]
4623 $ctext insert end "$line\n" d$tag
4624 } elseif {$x == " "} {
4625 $ctext insert end "$line\n"
4626 } elseif {$diffinhdr || $x == "\\"} {
4627 # e.g. "\ No newline at end of file"
4628 $ctext insert end "$line\n" filesep
4630 # Something else we don't recognize
4631 if {$curdifftag != "Comments"} {
4632 $ctext insert end "\n"
4633 $ctext tag add $curdifftag $curtagstart end
4634 set curtagstart [$ctext index "end - 1c"]
4635 set curdifftag Comments
4637 $ctext insert end "$line\n" filesep
4641 $ctext conf -state disabled
4644 if {$ids == $diffids && $bdf == $blobdifffd($ids)} {
4645 $ctext tag add $curdifftag $curtagstart end
4649 return [expr {$nr >= 1000? 2: 1}]
4652 proc changediffdisp {} {
4653 global ctext diffelide
4655 $ctext tag conf d0 -elide [lindex $diffelide 0]
4656 $ctext tag conf d1 -elide [lindex $diffelide 1]
4660 global difffilestart ctext
4661 set prev [lindex $difffilestart 0]
4662 set here [$ctext index @0,0]
4663 foreach loc $difffilestart {
4664 if {[$ctext compare $loc >= $here]} {
4674 global difffilestart ctext
4675 set here [$ctext index @0,0]
4676 foreach loc $difffilestart {
4677 if {[$ctext compare $loc > $here]} {
4684 proc clear_ctext {{first 1.0}} {
4685 global ctext smarktop smarkbot
4687 set l [lindex [split $first .] 0]
4688 if {![info exists smarktop] || [$ctext compare $first < $smarktop.0]} {
4691 if {![info exists smarkbot] || [$ctext compare $first < $smarkbot.0]} {
4694 $ctext delete $first end
4697 proc incrsearch {name ix op} {
4698 global ctext searchstring searchdirn
4700 $ctext tag remove found 1.0 end
4701 if {[catch {$ctext index anchor}]} {
4702 # no anchor set, use start of selection, or of visible area
4703 set sel [$ctext tag ranges sel]
4705 $ctext mark set anchor [lindex $sel 0]
4706 } elseif {$searchdirn eq "-forwards"} {
4707 $ctext mark set anchor @0,0
4709 $ctext mark set anchor @0,[winfo height $ctext]
4712 if {$searchstring ne {}} {
4713 set here [$ctext search $searchdirn -- $searchstring anchor]
4722 global sstring ctext searchstring searchdirn
4725 $sstring icursor end
4726 set searchdirn -forwards
4727 if {$searchstring ne {}} {
4728 set sel [$ctext tag ranges sel]
4730 set start "[lindex $sel 0] + 1c"
4731 } elseif {[catch {set start [$ctext index anchor]}]} {
4734 set match [$ctext search -count mlen -- $searchstring $start]
4735 $ctext tag remove sel 1.0 end
4741 set mend "$match + $mlen c"
4742 $ctext tag add sel $match $mend
4743 $ctext mark unset anchor
4747 proc dosearchback {} {
4748 global sstring ctext searchstring searchdirn
4751 $sstring icursor end
4752 set searchdirn -backwards
4753 if {$searchstring ne {}} {
4754 set sel [$ctext tag ranges sel]
4756 set start [lindex $sel 0]
4757 } elseif {[catch {set start [$ctext index anchor]}]} {
4758 set start @0,[winfo height $ctext]
4760 set match [$ctext search -backwards -count ml -- $searchstring $start]
4761 $ctext tag remove sel 1.0 end
4767 set mend "$match + $ml c"
4768 $ctext tag add sel $match $mend
4769 $ctext mark unset anchor
4773 proc searchmark {first last} {
4774 global ctext searchstring
4778 set match [$ctext search -count mlen -- $searchstring $mend $last.end]
4779 if {$match eq {}} break
4780 set mend "$match + $mlen c"
4781 $ctext tag add found $match $mend
4785 proc searchmarkvisible {doall} {
4786 global ctext smarktop smarkbot
4788 set topline [lindex [split [$ctext index @0,0] .] 0]
4789 set botline [lindex [split [$ctext index @0,[winfo height $ctext]] .] 0]
4790 if {$doall || $botline < $smarktop || $topline > $smarkbot} {
4791 # no overlap with previous
4792 searchmark $topline $botline
4793 set smarktop $topline
4794 set smarkbot $botline
4796 if {$topline < $smarktop} {
4797 searchmark $topline [expr {$smarktop-1}]
4798 set smarktop $topline
4800 if {$botline > $smarkbot} {
4801 searchmark [expr {$smarkbot+1}] $botline
4802 set smarkbot $botline
4807 proc scrolltext {f0 f1} {
4810 .bleft.sb set $f0 $f1
4811 if {$searchstring ne {}} {
4817 global linespc charspc canvx0 canvy0 mainfont
4818 global xspc1 xspc2 lthickness
4820 set linespc [font metrics $mainfont -linespace]
4821 set charspc [font measure $mainfont "m"]
4822 set canvy0 [expr {int(3 + 0.5 * $linespc)}]
4823 set canvx0 [expr {int(3 + 0.5 * $linespc)}]
4824 set lthickness [expr {int($linespc / 9) + 1}]
4825 set xspc1(0) $linespc
4833 set ymax [lindex [$canv cget -scrollregion] 3]
4834 if {$ymax eq {} || $ymax == 0} return
4835 set span [$canv yview]
4838 allcanvs yview moveto [lindex $span 0]
4840 if {[info exists selectedline]} {
4841 selectline $selectedline 0
4842 allcanvs yview moveto [lindex $span 0]
4846 proc incrfont {inc} {
4847 global mainfont textfont ctext canv phase cflist
4848 global charspc tabstop
4849 global stopped entries
4851 set mainfont [lreplace $mainfont 1 1 [expr {[lindex $mainfont 1] + $inc}]]
4852 set textfont [lreplace $textfont 1 1 [expr {[lindex $textfont 1] + $inc}]]
4854 $ctext conf -font $textfont -tabs "[expr {$tabstop * $charspc}]"
4855 $cflist conf -font $textfont
4856 $ctext tag conf filesep -font [concat $textfont bold]
4857 foreach e $entries {
4858 $e conf -font $mainfont
4860 if {$phase eq "getcommits"} {
4861 $canv itemconf textitems -font $mainfont
4867 global sha1entry sha1string
4868 if {[string length $sha1string] == 40} {
4869 $sha1entry delete 0 end
4873 proc sha1change {n1 n2 op} {
4874 global sha1string currentid sha1but
4875 if {$sha1string == {}
4876 || ([info exists currentid] && $sha1string == $currentid)} {
4881 if {[$sha1but cget -state] == $state} return
4882 if {$state == "normal"} {
4883 $sha1but conf -state normal -relief raised -text "Goto: "
4885 $sha1but conf -state disabled -relief flat -text "SHA1 ID: "
4889 proc gotocommit {} {
4890 global sha1string currentid commitrow tagids headids
4891 global displayorder numcommits curview
4893 if {$sha1string == {}
4894 || ([info exists currentid] && $sha1string == $currentid)} return
4895 if {[info exists tagids($sha1string)]} {
4896 set id $tagids($sha1string)
4897 } elseif {[info exists headids($sha1string)]} {
4898 set id $headids($sha1string)
4900 set id [string tolower $sha1string]
4901 if {[regexp {^[0-9a-f]{4,39}$} $id]} {
4903 foreach i $displayorder {
4904 if {[string match $id* $i]} {
4908 if {$matches ne {}} {
4909 if {[llength $matches] > 1} {
4910 error_popup "Short SHA1 id $id is ambiguous"
4913 set id [lindex $matches 0]
4917 if {[info exists commitrow($curview,$id)]} {
4918 selectline $commitrow($curview,$id) 1
4921 if {[regexp {^[0-9a-fA-F]{4,}$} $sha1string]} {
4926 error_popup "$type $sha1string is not known"
4929 proc lineenter {x y id} {
4930 global hoverx hovery hoverid hovertimer
4931 global commitinfo canv
4933 if {![info exists commitinfo($id)] && ![getcommit $id]} return
4937 if {[info exists hovertimer]} {
4938 after cancel $hovertimer
4940 set hovertimer [after 500 linehover]
4944 proc linemotion {x y id} {
4945 global hoverx hovery hoverid hovertimer
4947 if {[info exists hoverid] && $id == $hoverid} {
4950 if {[info exists hovertimer]} {
4951 after cancel $hovertimer
4953 set hovertimer [after 500 linehover]
4957 proc lineleave {id} {
4958 global hoverid hovertimer canv
4960 if {[info exists hoverid] && $id == $hoverid} {
4962 if {[info exists hovertimer]} {
4963 after cancel $hovertimer
4971 global hoverx hovery hoverid hovertimer
4972 global canv linespc lthickness
4973 global commitinfo mainfont
4975 set text [lindex $commitinfo($hoverid) 0]
4976 set ymax [lindex [$canv cget -scrollregion] 3]
4977 if {$ymax == {}} return
4978 set yfrac [lindex [$canv yview] 0]
4979 set x [expr {$hoverx + 2 * $linespc}]
4980 set y [expr {$hovery + $yfrac * $ymax - $linespc / 2}]
4981 set x0 [expr {$x - 2 * $lthickness}]
4982 set y0 [expr {$y - 2 * $lthickness}]
4983 set x1 [expr {$x + [font measure $mainfont $text] + 2 * $lthickness}]
4984 set y1 [expr {$y + $linespc + 2 * $lthickness}]
4985 set t [$canv create rectangle $x0 $y0 $x1 $y1 \
4986 -fill \#ffff80 -outline black -width 1 -tags hover]
4988 set t [$canv create text $x $y -anchor nw -text $text -tags hover \
4993 proc clickisonarrow {id y} {
4996 set ranges [rowranges $id]
4997 set thresh [expr {2 * $lthickness + 6}]
4998 set n [expr {[llength $ranges] - 1}]
4999 for {set i 1} {$i < $n} {incr i} {
5000 set row [lindex $ranges $i]
5001 if {abs([yc $row] - $y) < $thresh} {
5008 proc arrowjump {id n y} {
5011 # 1 <-> 2, 3 <-> 4, etc...
5012 set n [expr {(($n - 1) ^ 1) + 1}]
5013 set row [lindex [rowranges $id] $n]
5015 set ymax [lindex [$canv cget -scrollregion] 3]
5016 if {$ymax eq {} || $ymax <= 0} return
5017 set view [$canv yview]
5018 set yspan [expr {[lindex $view 1] - [lindex $view 0]}]
5019 set yfrac [expr {$yt / $ymax - $yspan / 2}]
5023 allcanvs yview moveto $yfrac
5026 proc lineclick {x y id isnew} {
5027 global ctext commitinfo children canv thickerline curview
5029 if {![info exists commitinfo($id)] && ![getcommit $id]} return
5034 # draw this line thicker than normal
5038 set ymax [lindex [$canv cget -scrollregion] 3]
5039 if {$ymax eq {}} return
5040 set yfrac [lindex [$canv yview] 0]
5041 set y [expr {$y + $yfrac * $ymax}]
5043 set dirn [clickisonarrow $id $y]
5045 arrowjump $id $dirn $y
5050 addtohistory [list lineclick $x $y $id 0]
5052 # fill the details pane with info about this line
5053 $ctext conf -state normal
5055 $ctext tag conf link -foreground blue -underline 1
5056 $ctext tag bind link <Enter> { %W configure -cursor hand2 }
5057 $ctext tag bind link <Leave> { %W configure -cursor $curtextcursor }
5058 $ctext insert end "Parent:\t"
5059 $ctext insert end $id [list link link0]
5060 $ctext tag bind link0 <1> [list selbyid $id]
5061 set info $commitinfo($id)
5062 $ctext insert end "\n\t[lindex $info 0]\n"
5063 $ctext insert end "\tAuthor:\t[lindex $info 1]\n"
5064 set date [formatdate [lindex $info 2]]
5065 $ctext insert end "\tDate:\t$date\n"
5066 set kids $children($curview,$id)
5068 $ctext insert end "\nChildren:"
5070 foreach child $kids {
5072 if {![info exists commitinfo($child)] && ![getcommit $child]} continue
5073 set info $commitinfo($child)
5074 $ctext insert end "\n\t"
5075 $ctext insert end $child [list link link$i]
5076 $ctext tag bind link$i <1> [list selbyid $child]
5077 $ctext insert end "\n\t[lindex $info 0]"
5078 $ctext insert end "\n\tAuthor:\t[lindex $info 1]"
5079 set date [formatdate [lindex $info 2]]
5080 $ctext insert end "\n\tDate:\t$date\n"
5083 $ctext conf -state disabled
5087 proc normalline {} {
5089 if {[info exists thickerline]} {
5097 global commitrow curview
5098 if {[info exists commitrow($curview,$id)]} {
5099 selectline $commitrow($curview,$id) 1
5105 if {![info exists startmstime]} {
5106 set startmstime [clock clicks -milliseconds]
5108 return [format "%.3f" [expr {([clock click -milliseconds] - $startmstime) / 1000.0}]]
5111 proc rowmenu {x y id} {
5112 global rowctxmenu commitrow selectedline rowmenuid curview
5114 if {![info exists selectedline]
5115 || $commitrow($curview,$id) eq $selectedline} {
5120 $rowctxmenu entryconfigure "Diff this*" -state $state
5121 $rowctxmenu entryconfigure "Diff selected*" -state $state
5122 $rowctxmenu entryconfigure "Make patch" -state $state
5124 tk_popup $rowctxmenu $x $y
5127 proc diffvssel {dirn} {
5128 global rowmenuid selectedline displayorder
5130 if {![info exists selectedline]} return
5132 set oldid [lindex $displayorder $selectedline]
5133 set newid $rowmenuid
5135 set oldid $rowmenuid
5136 set newid [lindex $displayorder $selectedline]
5138 addtohistory [list doseldiff $oldid $newid]
5139 doseldiff $oldid $newid
5142 proc doseldiff {oldid newid} {
5146 $ctext conf -state normal
5149 $ctext insert end "From "
5150 $ctext tag conf link -foreground blue -underline 1
5151 $ctext tag bind link <Enter> { %W configure -cursor hand2 }
5152 $ctext tag bind link <Leave> { %W configure -cursor $curtextcursor }
5153 $ctext tag bind link0 <1> [list selbyid $oldid]
5154 $ctext insert end $oldid [list link link0]
5155 $ctext insert end "\n "
5156 $ctext insert end [lindex $commitinfo($oldid) 0]
5157 $ctext insert end "\n\nTo "
5158 $ctext tag bind link1 <1> [list selbyid $newid]
5159 $ctext insert end $newid [list link link1]
5160 $ctext insert end "\n "
5161 $ctext insert end [lindex $commitinfo($newid) 0]
5162 $ctext insert end "\n"
5163 $ctext conf -state disabled
5164 $ctext tag delete Comments
5165 $ctext tag remove found 1.0 end
5166 startdiff [list $oldid $newid]
5170 global rowmenuid currentid commitinfo patchtop patchnum
5172 if {![info exists currentid]} return
5173 set oldid $currentid
5174 set oldhead [lindex $commitinfo($oldid) 0]
5175 set newid $rowmenuid
5176 set newhead [lindex $commitinfo($newid) 0]
5179 catch {destroy $top}
5181 label $top.title -text "Generate patch"
5182 grid $top.title - -pady 10
5183 label $top.from -text "From:"
5184 entry $top.fromsha1 -width 40 -relief flat
5185 $top.fromsha1 insert 0 $oldid
5186 $top.fromsha1 conf -state readonly
5187 grid $top.from $top.fromsha1 -sticky w
5188 entry $top.fromhead -width 60 -relief flat
5189 $top.fromhead insert 0 $oldhead
5190 $top.fromhead conf -state readonly
5191 grid x $top.fromhead -sticky w
5192 label $top.to -text "To:"
5193 entry $top.tosha1 -width 40 -relief flat
5194 $top.tosha1 insert 0 $newid
5195 $top.tosha1 conf -state readonly
5196 grid $top.to $top.tosha1 -sticky w
5197 entry $top.tohead -width 60 -relief flat
5198 $top.tohead insert 0 $newhead
5199 $top.tohead conf -state readonly
5200 grid x $top.tohead -sticky w
5201 button $top.rev -text "Reverse" -command mkpatchrev -padx 5
5202 grid $top.rev x -pady 10
5203 label $top.flab -text "Output file:"
5204 entry $top.fname -width 60
5205 $top.fname insert 0 [file normalize "patch$patchnum.patch"]
5207 grid $top.flab $top.fname -sticky w
5209 button $top.buts.gen -text "Generate" -command mkpatchgo
5210 button $top.buts.can -text "Cancel" -command mkpatchcan
5211 grid $top.buts.gen $top.buts.can
5212 grid columnconfigure $top.buts 0 -weight 1 -uniform a
5213 grid columnconfigure $top.buts 1 -weight 1 -uniform a
5214 grid $top.buts - -pady 10 -sticky ew
5218 proc mkpatchrev {} {
5221 set oldid [$patchtop.fromsha1 get]
5222 set oldhead [$patchtop.fromhead get]
5223 set newid [$patchtop.tosha1 get]
5224 set newhead [$patchtop.tohead get]
5225 foreach e [list fromsha1 fromhead tosha1 tohead] \
5226 v [list $newid $newhead $oldid $oldhead] {
5227 $patchtop.$e conf -state normal
5228 $patchtop.$e delete 0 end
5229 $patchtop.$e insert 0 $v
5230 $patchtop.$e conf -state readonly
5237 set oldid [$patchtop.fromsha1 get]
5238 set newid [$patchtop.tosha1 get]
5239 set fname [$patchtop.fname get]
5240 if {[catch {exec git diff-tree -p $oldid $newid >$fname &} err]} {
5241 error_popup "Error creating patch: $err"
5243 catch {destroy $patchtop}
5247 proc mkpatchcan {} {
5250 catch {destroy $patchtop}
5255 global rowmenuid mktagtop commitinfo
5259 catch {destroy $top}
5261 label $top.title -text "Create tag"
5262 grid $top.title - -pady 10
5263 label $top.id -text "ID:"
5264 entry $top.sha1 -width 40 -relief flat
5265 $top.sha1 insert 0 $rowmenuid
5266 $top.sha1 conf -state readonly
5267 grid $top.id $top.sha1 -sticky w
5268 entry $top.head -width 60 -relief flat
5269 $top.head insert 0 [lindex $commitinfo($rowmenuid) 0]
5270 $top.head conf -state readonly
5271 grid x $top.head -sticky w
5272 label $top.tlab -text "Tag name:"
5273 entry $top.tag -width 60
5274 grid $top.tlab $top.tag -sticky w
5276 button $top.buts.gen -text "Create" -command mktaggo
5277 button $top.buts.can -text "Cancel" -command mktagcan
5278 grid $top.buts.gen $top.buts.can
5279 grid columnconfigure $top.buts 0 -weight 1 -uniform a
5280 grid columnconfigure $top.buts 1 -weight 1 -uniform a
5281 grid $top.buts - -pady 10 -sticky ew
5286 global mktagtop env tagids idtags
5288 set id [$mktagtop.sha1 get]
5289 set tag [$mktagtop.tag get]
5291 error_popup "No tag name specified"
5294 if {[info exists tagids($tag)]} {
5295 error_popup "Tag \"$tag\" already exists"
5300 set fname [file join $dir "refs/tags" $tag]
5301 set f [open $fname w]
5305 error_popup "Error creating tag: $err"
5309 set tagids($tag) $id
5310 lappend idtags($id) $tag
5315 proc redrawtags {id} {
5316 global canv linehtag commitrow idpos selectedline curview
5317 global mainfont canvxmax
5319 if {![info exists commitrow($curview,$id)]} return
5320 drawcmitrow $commitrow($curview,$id)
5321 $canv delete tag.$id
5322 set xt [eval drawtags $id $idpos($id)]
5323 $canv coords $linehtag($commitrow($curview,$id)) $xt [lindex $idpos($id) 2]
5324 set text [$canv itemcget $linehtag($commitrow($curview,$id)) -text]
5325 set xr [expr {$xt + [font measure $mainfont $text]}]
5326 if {$xr > $canvxmax} {
5330 if {[info exists selectedline]
5331 && $selectedline == $commitrow($curview,$id)} {
5332 selectline $selectedline 0
5339 catch {destroy $mktagtop}
5348 proc writecommit {} {
5349 global rowmenuid wrcomtop commitinfo wrcomcmd
5351 set top .writecommit
5353 catch {destroy $top}
5355 label $top.title -text "Write commit to file"
5356 grid $top.title - -pady 10
5357 label $top.id -text "ID:"
5358 entry $top.sha1 -width 40 -relief flat
5359 $top.sha1 insert 0 $rowmenuid
5360 $top.sha1 conf -state readonly
5361 grid $top.id $top.sha1 -sticky w
5362 entry $top.head -width 60 -relief flat
5363 $top.head insert 0 [lindex $commitinfo($rowmenuid) 0]
5364 $top.head conf -state readonly
5365 grid x $top.head -sticky w
5366 label $top.clab -text "Command:"
5367 entry $top.cmd -width 60 -textvariable wrcomcmd
5368 grid $top.clab $top.cmd -sticky w -pady 10
5369 label $top.flab -text "Output file:"
5370 entry $top.fname -width 60
5371 $top.fname insert 0 [file normalize "commit-[string range $rowmenuid 0 6]"]
5372 grid $top.flab $top.fname -sticky w
5374 button $top.buts.gen -text "Write" -command wrcomgo
5375 button $top.buts.can -text "Cancel" -command wrcomcan
5376 grid $top.buts.gen $top.buts.can
5377 grid columnconfigure $top.buts 0 -weight 1 -uniform a
5378 grid columnconfigure $top.buts 1 -weight 1 -uniform a
5379 grid $top.buts - -pady 10 -sticky ew
5386 set id [$wrcomtop.sha1 get]
5387 set cmd "echo $id | [$wrcomtop.cmd get]"
5388 set fname [$wrcomtop.fname get]
5389 if {[catch {exec sh -c $cmd >$fname &} err]} {
5390 error_popup "Error writing commit: $err"
5392 catch {destroy $wrcomtop}
5399 catch {destroy $wrcomtop}
5404 global rowmenuid mkbrtop
5407 catch {destroy $top}
5409 label $top.title -text "Create new branch"
5410 grid $top.title - -pady 10
5411 label $top.id -text "ID:"
5412 entry $top.sha1 -width 40 -relief flat
5413 $top.sha1 insert 0 $rowmenuid
5414 $top.sha1 conf -state readonly
5415 grid $top.id $top.sha1 -sticky w
5416 label $top.nlab -text "Name:"
5417 entry $top.name -width 40
5418 grid $top.nlab $top.name -sticky w
5420 button $top.buts.go -text "Create" -command [list mkbrgo $top]
5421 button $top.buts.can -text "Cancel" -command "catch {destroy $top}"
5422 grid $top.buts.go $top.buts.can
5423 grid columnconfigure $top.buts 0 -weight 1 -uniform a
5424 grid columnconfigure $top.buts 1 -weight 1 -uniform a
5425 grid $top.buts - -pady 10 -sticky ew
5430 global headids idheads
5432 set name [$top.name get]
5433 set id [$top.sha1 get]
5435 error_popup "Please specify a name for the new branch"
5438 catch {destroy $top}
5442 exec git branch $name $id
5447 set headids($name) $id
5448 lappend idheads($id) $name
5456 proc cherrypick {} {
5457 global rowmenuid curview commitrow
5460 set oldhead [exec git rev-parse HEAD]
5461 set dheads [descheads $rowmenuid]
5462 if {$dheads ne {} && [lsearch -exact $dheads $oldhead] >= 0} {
5463 set ok [confirm_popup "Commit [string range $rowmenuid 0 7] is already\
5464 included in branch $mainhead -- really re-apply it?"]
5469 # Unfortunately git-cherry-pick writes stuff to stderr even when
5470 # no error occurs, and exec takes that as an indication of error...
5471 if {[catch {exec sh -c "git cherry-pick -r $rowmenuid 2>&1"} err]} {
5476 set newhead [exec git rev-parse HEAD]
5477 if {$newhead eq $oldhead} {
5479 error_popup "No changes committed"
5482 addnewchild $newhead $oldhead
5483 if {[info exists commitrow($curview,$oldhead)]} {
5484 insertrow $commitrow($curview,$oldhead) $newhead
5485 if {$mainhead ne {}} {
5486 movehead $newhead $mainhead
5487 movedhead $newhead $mainhead
5495 # context menu for a head
5496 proc headmenu {x y id head} {
5497 global headmenuid headmenuhead headctxmenu
5500 set headmenuhead $head
5501 tk_popup $headctxmenu $x $y
5505 global headmenuid headmenuhead mainhead headids
5507 # check the tree is clean first??
5508 set oldmainhead $mainhead
5512 exec git checkout -q $headmenuhead
5518 set mainhead $headmenuhead
5519 if {[info exists headids($oldmainhead)]} {
5520 redrawtags $headids($oldmainhead)
5522 redrawtags $headmenuid
5527 global headmenuid headmenuhead mainhead
5528 global headids idheads
5530 set head $headmenuhead
5532 if {$head eq $mainhead} {
5533 error_popup "Cannot delete the currently checked-out branch"
5536 set dheads [descheads $id]
5537 if {$dheads eq $headids($head)} {
5538 # the stuff on this branch isn't on any other branch
5539 if {![confirm_popup "The commits on branch $head aren't on any other\
5540 branch.\nReally delete branch $head?"]} return
5544 if {[catch {exec git branch -D $head} err]} {
5549 removehead $id $head
5550 removedhead $id $head
5556 # Stuff for finding nearby tags
5557 proc getallcommits {} {
5558 global allcommits allids nbmp nextarc seeds
5568 # Called when the graph might have changed
5569 proc regetallcommits {} {
5570 global allcommits seeds
5572 set cmd [concat | git rev-list --all --parents]
5576 set fd [open $cmd r]
5577 fconfigure $fd -blocking 0
5580 filerun $fd [list getallclines $fd]
5583 # Since most commits have 1 parent and 1 child, we group strings of
5584 # such commits into "arcs" joining branch/merge points (BMPs), which
5585 # are commits that either don't have 1 parent or don't have 1 child.
5587 # arcnos(id) - incoming arcs for BMP, arc we're on for other nodes
5588 # arcout(id) - outgoing arcs for BMP
5589 # arcids(a) - list of IDs on arc including end but not start
5590 # arcstart(a) - BMP ID at start of arc
5591 # arcend(a) - BMP ID at end of arc
5592 # growing(a) - arc a is still growing
5593 # arctags(a) - IDs out of arcids (excluding end) that have tags
5594 # archeads(a) - IDs out of arcids (excluding end) that have heads
5595 # The start of an arc is at the descendent end, so "incoming" means
5596 # coming from descendents, and "outgoing" means going towards ancestors.
5598 proc getallclines {fd} {
5599 global allids allparents allchildren idtags nextarc nbmp
5600 global arcnos arcids arctags arcout arcend arcstart archeads growing
5601 global seeds allcommits
5604 while {[incr nid] <= 1000 && [gets $fd line] >= 0} {
5605 set id [lindex $line 0]
5606 if {[info exists allparents($id)]} {
5611 set olds [lrange $line 1 end]
5612 set allparents($id) $olds
5613 if {![info exists allchildren($id)]} {
5614 set allchildren($id) {}
5619 if {[llength $olds] == 1 && [llength $a] == 1} {
5620 lappend arcids($a) $id
5621 if {[info exists idtags($id)]} {
5622 lappend arctags($a) $id
5624 if {[info exists idheads($id)]} {
5625 lappend archeads($a) $id
5627 if {[info exists allparents($olds)]} {
5628 # seen parent already
5629 if {![info exists arcout($olds)]} {
5632 lappend arcids($a) $olds
5633 set arcend($a) $olds
5636 lappend allchildren($olds) $id
5637 lappend arcnos($olds) $a
5642 foreach a $arcnos($id) {
5643 lappend arcids($a) $id
5650 lappend allchildren($p) $id
5651 set a [incr nextarc]
5652 set arcstart($a) $id
5659 if {[info exists allparents($p)]} {
5660 # seen it already, may need to make a new branch
5661 if {![info exists arcout($p)]} {
5664 lappend arcids($a) $p
5668 lappend arcnos($p) $a
5673 return [expr {$nid >= 1000? 2: 1}]
5676 if {[incr allcommits -1] == 0} {
5683 proc recalcarc {a} {
5684 global arctags archeads arcids idtags idheads
5688 foreach id [lrange $arcids($a) 0 end-1] {
5689 if {[info exists idtags($id)]} {
5692 if {[info exists idheads($id)]} {
5697 set archeads($a) $ah
5701 global arcnos arcids nextarc nbmp arctags archeads idtags idheads
5702 global arcstart arcend arcout allparents growing
5705 if {[llength $a] != 1} {
5706 puts "oops splitarc called but [llength $a] arcs already"
5710 set i [lsearch -exact $arcids($a) $p]
5712 puts "oops splitarc $p not in arc $a"
5715 set na [incr nextarc]
5716 if {[info exists arcend($a)]} {
5717 set arcend($na) $arcend($a)
5719 set l [lindex $allparents([lindex $arcids($a) end]) 0]
5720 set j [lsearch -exact $arcnos($l) $a]
5721 set arcnos($l) [lreplace $arcnos($l) $j $j $na]
5723 set tail [lrange $arcids($a) [expr {$i+1}] end]
5724 set arcids($a) [lrange $arcids($a) 0 $i]
5726 set arcstart($na) $p
5728 set arcids($na) $tail
5729 if {[info exists growing($a)]} {
5736 if {[llength $arcnos($id)] == 1} {
5739 set j [lsearch -exact $arcnos($id) $a]
5740 set arcnos($id) [lreplace $arcnos($id) $j $j $na]
5744 # reconstruct tags and heads lists
5745 if {$arctags($a) ne {} || $archeads($a) ne {}} {
5750 set archeads($na) {}
5754 # Update things for a new commit added that is a child of one
5755 # existing commit. Used when cherry-picking.
5756 proc addnewchild {id p} {
5757 global allids allparents allchildren idtags nextarc nbmp
5758 global arcnos arcids arctags arcout arcend arcstart archeads growing
5762 set allparents($id) [list $p]
5763 set allchildren($id) {}
5767 lappend allchildren($p) $id
5768 set a [incr nextarc]
5769 set arcstart($a) $id
5772 set arcids($a) [list $p]
5774 if {![info exists arcout($p)]} {
5777 lappend arcnos($p) $a
5778 set arcout($id) [list $a]
5781 # Returns 1 if a is an ancestor of b, -1 if b is an ancestor of a,
5782 # or 0 if neither is true.
5783 proc anc_or_desc {a b} {
5784 global arcout arcstart arcend arcnos cached_isanc
5786 if {$arcnos($a) eq $arcnos($b)} {
5787 # Both are on the same arc(s); either both are the same BMP,
5788 # or if one is not a BMP, the other is also not a BMP or is
5789 # the BMP at end of the arc (and it only has 1 incoming arc).
5793 # assert {[llength $arcnos($a)] == 1}
5794 set arc [lindex $arcnos($a) 0]
5795 set i [lsearch -exact $arcids($arc) $a]
5796 set j [lsearch -exact $arcids($arc) $b]
5797 if {$i < 0 || $i > $j} {
5804 if {![info exists arcout($a)]} {
5805 set arc [lindex $arcnos($a) 0]
5806 if {[info exists arcend($arc)]} {
5807 set aend $arcend($arc)
5811 set a $arcstart($arc)
5815 if {![info exists arcout($b)]} {
5816 set arc [lindex $arcnos($b) 0]
5817 if {[info exists arcend($arc)]} {
5818 set bend $arcend($arc)
5822 set b $arcstart($arc)
5832 if {[info exists cached_isanc($a,$bend)]} {
5833 if {$cached_isanc($a,$bend)} {
5837 if {[info exists cached_isanc($b,$aend)]} {
5838 if {$cached_isanc($b,$aend)} {
5841 if {[info exists cached_isanc($a,$bend)]} {
5846 set todo [list $a $b]
5849 for {set i 0} {$i < [llength $todo]} {incr i} {
5850 set x [lindex $todo $i]
5851 if {$anc($x) eq {}} {
5854 foreach arc $arcnos($x) {
5855 set xd $arcstart($arc)
5857 set cached_isanc($a,$bend) 1
5858 set cached_isanc($b,$aend) 0
5860 } elseif {$xd eq $aend} {
5861 set cached_isanc($b,$aend) 1
5862 set cached_isanc($a,$bend) 0
5865 if {![info exists anc($xd)]} {
5866 set anc($xd) $anc($x)
5868 } elseif {$anc($xd) ne $anc($x)} {
5873 set cached_isanc($a,$bend) 0
5874 set cached_isanc($b,$aend) 0
5878 # This identifies whether $desc has an ancestor that is
5879 # a growing tip of the graph and which is not an ancestor of $anc
5880 # and returns 0 if so and 1 if not.
5881 # If we subsequently discover a tag on such a growing tip, and that
5882 # turns out to be a descendent of $anc (which it could, since we
5883 # don't necessarily see children before parents), then $desc
5884 # isn't a good choice to display as a descendent tag of
5885 # $anc (since it is the descendent of another tag which is
5886 # a descendent of $anc). Similarly, $anc isn't a good choice to
5887 # display as a ancestor tag of $desc.
5889 proc is_certain {desc anc} {
5890 global arcnos arcout arcstart arcend growing problems
5893 if {[llength $arcnos($anc)] == 1} {
5894 # tags on the same arc are certain
5895 if {$arcnos($desc) eq $arcnos($anc)} {
5898 if {![info exists arcout($anc)]} {
5899 # if $anc is partway along an arc, use the start of the arc instead
5900 set a [lindex $arcnos($anc) 0]
5901 set anc $arcstart($a)
5904 if {[llength $arcnos($desc)] > 1 || [info exists arcout($desc)]} {
5907 set a [lindex $arcnos($desc) 0]
5913 set anclist [list $x]
5917 for {set i 0} {$i < [llength $anclist] && ($nnh > 0 || $ngrowanc > 0)} {incr i} {
5918 set x [lindex $anclist $i]
5923 foreach a $arcout($x) {
5924 if {[info exists growing($a)]} {
5925 if {![info exists growanc($x)] && $dl($x)} {
5931 if {[info exists dl($y)]} {
5935 if {![info exists done($y)]} {
5938 if {[info exists growanc($x)]} {
5942 for {set k 0} {$k < [llength $xl]} {incr k} {
5943 set z [lindex $xl $k]
5944 foreach c $arcout($z) {
5945 if {[info exists arcend($c)]} {
5947 if {[info exists dl($v)] && $dl($v)} {
5949 if {![info exists done($v)]} {
5952 if {[info exists growanc($v)]} {
5962 } elseif {$y eq $anc || !$dl($x)} {
5973 foreach x [array names growanc] {
5982 proc validate_arctags {a} {
5983 global arctags idtags
5987 foreach id $arctags($a) {
5989 if {![info exists idtags($id)]} {
5990 set na [lreplace $na $i $i]
5997 proc validate_archeads {a} {
5998 global archeads idheads
6001 set na $archeads($a)
6002 foreach id $archeads($a) {
6004 if {![info exists idheads($id)]} {
6005 set na [lreplace $na $i $i]
6009 set archeads($a) $na
6012 # Return the list of IDs that have tags that are descendents of id,
6013 # ignoring IDs that are descendents of IDs already reported.
6014 proc desctags {id} {
6015 global arcnos arcstart arcids arctags idtags allparents
6016 global growing cached_dtags
6018 if {![info exists allparents($id)]} {
6021 set t1 [clock clicks -milliseconds]
6023 if {[llength $arcnos($id)] == 1 && [llength $allparents($id)] == 1} {
6024 # part-way along an arc; check that arc first
6025 set a [lindex $arcnos($id) 0]
6026 if {$arctags($a) ne {}} {
6028 set i [lsearch -exact $arcids($a) $id]
6030 foreach t $arctags($a) {
6031 set j [lsearch -exact $arcids($a) $t]
6039 set id $arcstart($a)
6040 if {[info exists idtags($id)]} {
6044 if {[info exists cached_dtags($id)]} {
6045 return $cached_dtags($id)
6052 for {set i 0} {$i < [llength $todo] && $nc > 0} {incr i} {
6053 set id [lindex $todo $i]
6055 set ta [info exists hastaggedancestor($id)]
6059 # ignore tags on starting node
6060 if {!$ta && $i > 0} {
6061 if {[info exists idtags($id)]} {
6064 } elseif {[info exists cached_dtags($id)]} {
6065 set tagloc($id) $cached_dtags($id)
6069 foreach a $arcnos($id) {
6071 if {!$ta && $arctags($a) ne {}} {
6073 if {$arctags($a) ne {}} {
6074 lappend tagloc($id) [lindex $arctags($a) end]
6077 if {$ta || $arctags($a) ne {}} {
6078 set tomark [list $d]
6079 for {set j 0} {$j < [llength $tomark]} {incr j} {
6080 set dd [lindex $tomark $j]
6081 if {![info exists hastaggedancestor($dd)]} {
6082 if {[info exists done($dd)]} {
6083 foreach b $arcnos($dd) {
6084 lappend tomark $arcstart($b)
6086 if {[info exists tagloc($dd)]} {
6089 } elseif {[info exists queued($dd)]} {
6092 set hastaggedancestor($dd) 1
6096 if {![info exists queued($d)]} {
6099 if {![info exists hastaggedancestor($d)]} {
6106 foreach id [array names tagloc] {
6107 if {![info exists hastaggedancestor($id)]} {
6108 foreach t $tagloc($id) {
6109 if {[lsearch -exact $tags $t] < 0} {
6115 set t2 [clock clicks -milliseconds]
6118 # remove tags that are descendents of other tags
6119 for {set i 0} {$i < [llength $tags]} {incr i} {
6120 set a [lindex $tags $i]
6121 for {set j 0} {$j < $i} {incr j} {
6122 set b [lindex $tags $j]
6123 set r [anc_or_desc $a $b]
6125 set tags [lreplace $tags $j $j]
6128 } elseif {$r == -1} {
6129 set tags [lreplace $tags $i $i]
6136 if {[array names growing] ne {}} {
6137 # graph isn't finished, need to check if any tag could get
6138 # eclipsed by another tag coming later. Simply ignore any
6139 # tags that could later get eclipsed.
6142 if {[is_certain $t $origid]} {
6146 if {$tags eq $ctags} {
6147 set cached_dtags($origid) $tags
6152 set cached_dtags($origid) $tags
6154 set t3 [clock clicks -milliseconds]
6155 if {0 && $t3 - $t1 >= 100} {
6156 puts "iterating descendents ($loopix/[llength $todo] nodes) took\
6157 [expr {$t2-$t1}]+[expr {$t3-$t2}]ms, $nc candidates left"
6163 global arcnos arcids arcout arcend arctags idtags allparents
6164 global growing cached_atags
6166 if {![info exists allparents($id)]} {
6169 set t1 [clock clicks -milliseconds]
6171 if {[llength $arcnos($id)] == 1 && [llength $allparents($id)] == 1} {
6172 # part-way along an arc; check that arc first
6173 set a [lindex $arcnos($id) 0]
6174 if {$arctags($a) ne {}} {
6176 set i [lsearch -exact $arcids($a) $id]
6177 foreach t $arctags($a) {
6178 set j [lsearch -exact $arcids($a) $t]
6184 if {![info exists arcend($a)]} {
6188 if {[info exists idtags($id)]} {
6192 if {[info exists cached_atags($id)]} {
6193 return $cached_atags($id)
6201 for {set i 0} {$i < [llength $todo] && $nc > 0} {incr i} {
6202 set id [lindex $todo $i]
6204 set td [info exists hastaggeddescendent($id)]
6208 # ignore tags on starting node
6209 if {!$td && $i > 0} {
6210 if {[info exists idtags($id)]} {
6213 } elseif {[info exists cached_atags($id)]} {
6214 set tagloc($id) $cached_atags($id)
6218 foreach a $arcout($id) {
6219 if {!$td && $arctags($a) ne {}} {
6221 if {$arctags($a) ne {}} {
6222 lappend tagloc($id) [lindex $arctags($a) 0]
6225 if {![info exists arcend($a)]} continue
6227 if {$td || $arctags($a) ne {}} {
6228 set tomark [list $d]
6229 for {set j 0} {$j < [llength $tomark]} {incr j} {
6230 set dd [lindex $tomark $j]
6231 if {![info exists hastaggeddescendent($dd)]} {
6232 if {[info exists done($dd)]} {
6233 foreach b $arcout($dd) {
6234 if {[info exists arcend($b)]} {
6235 lappend tomark $arcend($b)
6238 if {[info exists tagloc($dd)]} {
6241 } elseif {[info exists queued($dd)]} {
6244 set hastaggeddescendent($dd) 1
6248 if {![info exists queued($d)]} {
6251 if {![info exists hastaggeddescendent($d)]} {
6257 set t2 [clock clicks -milliseconds]
6260 foreach id [array names tagloc] {
6261 if {![info exists hastaggeddescendent($id)]} {
6262 foreach t $tagloc($id) {
6263 if {[lsearch -exact $tags $t] < 0} {
6270 # remove tags that are ancestors of other tags
6271 for {set i 0} {$i < [llength $tags]} {incr i} {
6272 set a [lindex $tags $i]
6273 for {set j 0} {$j < $i} {incr j} {
6274 set b [lindex $tags $j]
6275 set r [anc_or_desc $a $b]
6277 set tags [lreplace $tags $j $j]
6280 } elseif {$r == 1} {
6281 set tags [lreplace $tags $i $i]
6288 if {[array names growing] ne {}} {
6289 # graph isn't finished, need to check if any tag could get
6290 # eclipsed by another tag coming later. Simply ignore any
6291 # tags that could later get eclipsed.
6294 if {[is_certain $origid $t]} {
6298 if {$tags eq $ctags} {
6299 set cached_atags($origid) $tags
6304 set cached_atags($origid) $tags
6306 set t3 [clock clicks -milliseconds]
6307 if {0 && $t3 - $t1 >= 100} {
6308 puts "iterating ancestors ($loopix/[llength $todo] nodes) took\
6309 [expr {$t2-$t1}]+[expr {$t3-$t2}]ms, $nc candidates left"
6314 # Return the list of IDs that have heads that are descendents of id,
6315 # including id itself if it has a head.
6316 proc descheads {id} {
6317 global arcnos arcstart arcids archeads idheads cached_dheads
6320 if {![info exists allparents($id)]} {
6324 if {[llength $arcnos($id)] == 1 && [llength $allparents($id)] == 1} {
6325 # part-way along an arc; check it first
6326 set a [lindex $arcnos($id) 0]
6327 if {$archeads($a) ne {}} {
6328 validate_archeads $a
6329 set i [lsearch -exact $arcids($a) $id]
6330 foreach t $archeads($a) {
6331 set j [lsearch -exact $arcids($a) $t]
6336 set id $arcstart($a)
6341 for {set i 0} {$i < [llength $todo]} {incr i} {
6342 set id [lindex $todo $i]
6343 if {[info exists cached_dheads($id)]} {
6344 set ret [concat $ret $cached_dheads($id)]
6346 if {[info exists idheads($id)]} {
6349 foreach a $arcnos($id) {
6350 if {$archeads($a) ne {}} {
6351 set ret [concat $ret $archeads($a)]
6354 if {![info exists seen($d)]} {
6361 set ret [lsort -unique $ret]
6362 set cached_dheads($origid) $ret
6365 proc addedtag {id} {
6366 global arcnos arcout cached_dtags cached_atags
6368 if {![info exists arcnos($id)]} return
6369 if {![info exists arcout($id)]} {
6370 recalcarc [lindex $arcnos($id) 0]
6372 catch {unset cached_dtags}
6373 catch {unset cached_atags}
6376 proc addedhead {hid head} {
6377 global arcnos arcout cached_dheads
6379 if {![info exists arcnos($hid)]} return
6380 if {![info exists arcout($hid)]} {
6381 recalcarc [lindex $arcnos($hid) 0]
6383 catch {unset cached_dheads}
6386 proc removedhead {hid head} {
6387 global cached_dheads
6389 catch {unset cached_dheads}
6392 proc movedhead {hid head} {
6393 global arcnos arcout cached_dheads
6395 if {![info exists arcnos($hid)]} return
6396 if {![info exists arcout($hid)]} {
6397 recalcarc [lindex $arcnos($hid) 0]
6399 catch {unset cached_dheads}
6402 proc changedrefs {} {
6403 global cached_dheads cached_dtags cached_atags
6404 global arctags archeads arcnos arcout idheads idtags
6406 foreach id [concat [array names idheads] [array names idtags]] {
6407 if {[info exists arcnos($id)] && ![info exists arcout($id)]} {
6408 set a [lindex $arcnos($id) 0]
6409 if {![info exists donearc($a)]} {
6415 catch {unset cached_dtags}
6416 catch {unset cached_atags}
6417 catch {unset cached_dheads}
6420 proc rereadrefs {} {
6421 global idtags idheads idotherrefs mainhead
6423 set refids [concat [array names idtags] \
6424 [array names idheads] [array names idotherrefs]]
6425 foreach id $refids {
6426 if {![info exists ref($id)]} {
6427 set ref($id) [listrefs $id]
6430 set oldmainhead $mainhead
6433 set refids [lsort -unique [concat $refids [array names idtags] \
6434 [array names idheads] [array names idotherrefs]]]
6435 foreach id $refids {
6436 set v [listrefs $id]
6437 if {![info exists ref($id)] || $ref($id) != $v ||
6438 ($id eq $oldmainhead && $id ne $mainhead) ||
6439 ($id eq $mainhead && $id ne $oldmainhead)} {
6445 proc listrefs {id} {
6446 global idtags idheads idotherrefs
6449 if {[info exists idtags($id)]} {
6453 if {[info exists idheads($id)]} {
6457 if {[info exists idotherrefs($id)]} {
6458 set z $idotherrefs($id)
6460 return [list $x $y $z]
6463 proc showtag {tag isnew} {
6464 global ctext tagcontents tagids linknum
6467 addtohistory [list showtag $tag 0]
6469 $ctext conf -state normal
6472 if {[info exists tagcontents($tag)]} {
6473 set text $tagcontents($tag)
6475 set text "Tag: $tag\nId: $tagids($tag)"
6477 appendwithlinks $text {}
6478 $ctext conf -state disabled
6490 global maxwidth maxgraphpct diffopts
6491 global oldprefs prefstop showneartags
6492 global bgcolor fgcolor ctext diffcolors selectbgcolor
6493 global uifont tabstop
6497 if {[winfo exists $top]} {
6501 foreach v {maxwidth maxgraphpct diffopts showneartags} {
6502 set oldprefs($v) [set $v]
6505 wm title $top "Gitk preferences"
6506 label $top.ldisp -text "Commit list display options"
6507 $top.ldisp configure -font $uifont
6508 grid $top.ldisp - -sticky w -pady 10
6509 label $top.spacer -text " "
6510 label $top.maxwidthl -text "Maximum graph width (lines)" \
6512 spinbox $top.maxwidth -from 0 -to 100 -width 4 -textvariable maxwidth
6513 grid $top.spacer $top.maxwidthl $top.maxwidth -sticky w
6514 label $top.maxpctl -text "Maximum graph width (% of pane)" \
6516 spinbox $top.maxpct -from 1 -to 100 -width 4 -textvariable maxgraphpct
6517 grid x $top.maxpctl $top.maxpct -sticky w
6519 label $top.ddisp -text "Diff display options"
6520 $top.ddisp configure -font $uifont
6521 grid $top.ddisp - -sticky w -pady 10
6522 label $top.diffoptl -text "Options for diff program" \
6524 entry $top.diffopt -width 20 -textvariable diffopts
6525 grid x $top.diffoptl $top.diffopt -sticky w
6527 label $top.ntag.l -text "Display nearby tags" -font optionfont
6528 checkbutton $top.ntag.b -variable showneartags
6529 pack $top.ntag.b $top.ntag.l -side left
6530 grid x $top.ntag -sticky w
6531 label $top.tabstopl -text "tabstop" -font optionfont
6532 entry $top.tabstop -width 10 -textvariable tabstop
6533 grid x $top.tabstopl $top.tabstop -sticky w
6535 label $top.cdisp -text "Colors: press to choose"
6536 $top.cdisp configure -font $uifont
6537 grid $top.cdisp - -sticky w -pady 10
6538 label $top.bg -padx 40 -relief sunk -background $bgcolor
6539 button $top.bgbut -text "Background" -font optionfont \
6540 -command [list choosecolor bgcolor 0 $top.bg background setbg]
6541 grid x $top.bgbut $top.bg -sticky w
6542 label $top.fg -padx 40 -relief sunk -background $fgcolor
6543 button $top.fgbut -text "Foreground" -font optionfont \
6544 -command [list choosecolor fgcolor 0 $top.fg foreground setfg]
6545 grid x $top.fgbut $top.fg -sticky w
6546 label $top.diffold -padx 40 -relief sunk -background [lindex $diffcolors 0]
6547 button $top.diffoldbut -text "Diff: old lines" -font optionfont \
6548 -command [list choosecolor diffcolors 0 $top.diffold "diff old lines" \
6549 [list $ctext tag conf d0 -foreground]]
6550 grid x $top.diffoldbut $top.diffold -sticky w
6551 label $top.diffnew -padx 40 -relief sunk -background [lindex $diffcolors 1]
6552 button $top.diffnewbut -text "Diff: new lines" -font optionfont \
6553 -command [list choosecolor diffcolors 1 $top.diffnew "diff new lines" \
6554 [list $ctext tag conf d1 -foreground]]
6555 grid x $top.diffnewbut $top.diffnew -sticky w
6556 label $top.hunksep -padx 40 -relief sunk -background [lindex $diffcolors 2]
6557 button $top.hunksepbut -text "Diff: hunk header" -font optionfont \
6558 -command [list choosecolor diffcolors 2 $top.hunksep \
6559 "diff hunk header" \
6560 [list $ctext tag conf hunksep -foreground]]
6561 grid x $top.hunksepbut $top.hunksep -sticky w
6562 label $top.selbgsep -padx 40 -relief sunk -background $selectbgcolor
6563 button $top.selbgbut -text "Select bg" -font optionfont \
6564 -command [list choosecolor selectbgcolor 0 $top.bg background setselbg]
6565 grid x $top.selbgbut $top.selbgsep -sticky w
6568 button $top.buts.ok -text "OK" -command prefsok -default active
6569 $top.buts.ok configure -font $uifont
6570 button $top.buts.can -text "Cancel" -command prefscan -default normal
6571 $top.buts.can configure -font $uifont
6572 grid $top.buts.ok $top.buts.can
6573 grid columnconfigure $top.buts 0 -weight 1 -uniform a
6574 grid columnconfigure $top.buts 1 -weight 1 -uniform a
6575 grid $top.buts - - -pady 10 -sticky ew
6576 bind $top <Visibility> "focus $top.buts.ok"
6579 proc choosecolor {v vi w x cmd} {
6582 set c [tk_chooseColor -initialcolor [lindex [set $v] $vi] \
6583 -title "Gitk: choose color for $x"]
6584 if {$c eq {}} return
6585 $w conf -background $c
6591 global bglist cflist
6593 $w configure -selectbackground $c
6595 $cflist tag configure highlight \
6596 -background [$cflist cget -selectbackground]
6597 allcanvs itemconf secsel -fill $c
6604 $w conf -background $c
6612 $w conf -foreground $c
6614 allcanvs itemconf text -fill $c
6615 $canv itemconf circle -outline $c
6619 global maxwidth maxgraphpct diffopts
6620 global oldprefs prefstop showneartags
6622 foreach v {maxwidth maxgraphpct diffopts showneartags} {
6623 set $v $oldprefs($v)
6625 catch {destroy $prefstop}
6630 global maxwidth maxgraphpct
6631 global oldprefs prefstop showneartags
6632 global charspc ctext tabstop
6634 catch {destroy $prefstop}
6636 $ctext configure -tabs "[expr {$tabstop * $charspc}]"
6637 if {$maxwidth != $oldprefs(maxwidth)
6638 || $maxgraphpct != $oldprefs(maxgraphpct)} {
6640 } elseif {$showneartags != $oldprefs(showneartags)} {
6645 proc formatdate {d} {
6646 return [clock format $d -format "%Y-%m-%d %H:%M:%S"]
6649 # This list of encoding names and aliases is distilled from
6650 # http://www.iana.org/assignments/character-sets.
6651 # Not all of them are supported by Tcl.
6652 set encoding_aliases {
6653 { ANSI_X3.4-1968 iso-ir-6 ANSI_X3.4-1986 ISO_646.irv:1991 ASCII
6654 ISO646-US US-ASCII us IBM367 cp367 csASCII }
6655 { ISO-10646-UTF-1 csISO10646UTF1 }
6656 { ISO_646.basic:1983 ref csISO646basic1983 }
6657 { INVARIANT csINVARIANT }
6658 { ISO_646.irv:1983 iso-ir-2 irv csISO2IntlRefVersion }
6659 { BS_4730 iso-ir-4 ISO646-GB gb uk csISO4UnitedKingdom }
6660 { NATS-SEFI iso-ir-8-1 csNATSSEFI }
6661 { NATS-SEFI-ADD iso-ir-8-2 csNATSSEFIADD }
6662 { NATS-DANO iso-ir-9-1 csNATSDANO }
6663 { NATS-DANO-ADD iso-ir-9-2 csNATSDANOADD }
6664 { SEN_850200_B iso-ir-10 FI ISO646-FI ISO646-SE se csISO10Swedish }
6665 { SEN_850200_C iso-ir-11 ISO646-SE2 se2 csISO11SwedishForNames }
6666 { KS_C_5601-1987 iso-ir-149 KS_C_5601-1989 KSC_5601 korean csKSC56011987 }
6667 { ISO-2022-KR csISO2022KR }
6669 { ISO-2022-JP csISO2022JP }
6670 { ISO-2022-JP-2 csISO2022JP2 }
6671 { JIS_C6220-1969-jp JIS_C6220-1969 iso-ir-13 katakana x0201-7
6673 { JIS_C6220-1969-ro iso-ir-14 jp ISO646-JP csISO14JISC6220ro }
6674 { IT iso-ir-15 ISO646-IT csISO15Italian }
6675 { PT iso-ir-16 ISO646-PT csISO16Portuguese }
6676 { ES iso-ir-17 ISO646-ES csISO17Spanish }
6677 { greek7-old iso-ir-18 csISO18Greek7Old }
6678 { latin-greek iso-ir-19 csISO19LatinGreek }
6679 { DIN_66003 iso-ir-21 de ISO646-DE csISO21German }
6680 { NF_Z_62-010_(1973) iso-ir-25 ISO646-FR1 csISO25French }
6681 { Latin-greek-1 iso-ir-27 csISO27LatinGreek1 }
6682 { ISO_5427 iso-ir-37 csISO5427Cyrillic }
6683 { JIS_C6226-1978 iso-ir-42 csISO42JISC62261978 }
6684 { BS_viewdata iso-ir-47 csISO47BSViewdata }
6685 { INIS iso-ir-49 csISO49INIS }
6686 { INIS-8 iso-ir-50 csISO50INIS8 }
6687 { INIS-cyrillic iso-ir-51 csISO51INISCyrillic }
6688 { ISO_5427:1981 iso-ir-54 ISO5427Cyrillic1981 }
6689 { ISO_5428:1980 iso-ir-55 csISO5428Greek }
6690 { GB_1988-80 iso-ir-57 cn ISO646-CN csISO57GB1988 }
6691 { GB_2312-80 iso-ir-58 chinese csISO58GB231280 }
6692 { NS_4551-1 iso-ir-60 ISO646-NO no csISO60DanishNorwegian
6694 { NS_4551-2 ISO646-NO2 iso-ir-61 no2 csISO61Norwegian2 }
6695 { NF_Z_62-010 iso-ir-69 ISO646-FR fr csISO69French }
6696 { videotex-suppl iso-ir-70 csISO70VideotexSupp1 }
6697 { PT2 iso-ir-84 ISO646-PT2 csISO84Portuguese2 }
6698 { ES2 iso-ir-85 ISO646-ES2 csISO85Spanish2 }
6699 { MSZ_7795.3 iso-ir-86 ISO646-HU hu csISO86Hungarian }
6700 { JIS_C6226-1983 iso-ir-87 x0208 JIS_X0208-1983 csISO87JISX0208 }
6701 { greek7 iso-ir-88 csISO88Greek7 }
6702 { ASMO_449 ISO_9036 arabic7 iso-ir-89 csISO89ASMO449 }
6703 { iso-ir-90 csISO90 }
6704 { JIS_C6229-1984-a iso-ir-91 jp-ocr-a csISO91JISC62291984a }
6705 { JIS_C6229-1984-b iso-ir-92 ISO646-JP-OCR-B jp-ocr-b
6706 csISO92JISC62991984b }
6707 { JIS_C6229-1984-b-add iso-ir-93 jp-ocr-b-add csISO93JIS62291984badd }
6708 { JIS_C6229-1984-hand iso-ir-94 jp-ocr-hand csISO94JIS62291984hand }
6709 { JIS_C6229-1984-hand-add iso-ir-95 jp-ocr-hand-add
6710 csISO95JIS62291984handadd }
6711 { JIS_C6229-1984-kana iso-ir-96 csISO96JISC62291984kana }
6712 { ISO_2033-1983 iso-ir-98 e13b csISO2033 }
6713 { ANSI_X3.110-1983 iso-ir-99 CSA_T500-1983 NAPLPS csISO99NAPLPS }
6714 { ISO_8859-1:1987 iso-ir-100 ISO_8859-1 ISO-8859-1 latin1 l1 IBM819
6716 { ISO_8859-2:1987 iso-ir-101 ISO_8859-2 ISO-8859-2 latin2 l2 csISOLatin2 }
6717 { T.61-7bit iso-ir-102 csISO102T617bit }
6718 { T.61-8bit T.61 iso-ir-103 csISO103T618bit }
6719 { ISO_8859-3:1988 iso-ir-109 ISO_8859-3 ISO-8859-3 latin3 l3 csISOLatin3 }
6720 { ISO_8859-4:1988 iso-ir-110 ISO_8859-4 ISO-8859-4 latin4 l4 csISOLatin4 }
6721 { ECMA-cyrillic iso-ir-111 KOI8-E csISO111ECMACyrillic }
6722 { CSA_Z243.4-1985-1 iso-ir-121 ISO646-CA csa7-1 ca csISO121Canadian1 }
6723 { CSA_Z243.4-1985-2 iso-ir-122 ISO646-CA2 csa7-2 csISO122Canadian2 }
6724 { CSA_Z243.4-1985-gr iso-ir-123 csISO123CSAZ24341985gr }
6725 { ISO_8859-6:1987 iso-ir-127 ISO_8859-6 ISO-8859-6 ECMA-114 ASMO-708
6726 arabic csISOLatinArabic }
6727 { ISO_8859-6-E csISO88596E ISO-8859-6-E }
6728 { ISO_8859-6-I csISO88596I ISO-8859-6-I }
6729 { ISO_8859-7:1987 iso-ir-126 ISO_8859-7 ISO-8859-7 ELOT_928 ECMA-118
6730 greek greek8 csISOLatinGreek }
6731 { T.101-G2 iso-ir-128 csISO128T101G2 }
6732 { ISO_8859-8:1988 iso-ir-138 ISO_8859-8 ISO-8859-8 hebrew
6734 { ISO_8859-8-E csISO88598E ISO-8859-8-E }
6735 { ISO_8859-8-I csISO88598I ISO-8859-8-I }
6736 { CSN_369103 iso-ir-139 csISO139CSN369103 }
6737 { JUS_I.B1.002 iso-ir-141 ISO646-YU js yu csISO141JUSIB1002 }
6738 { ISO_6937-2-add iso-ir-142 csISOTextComm }
6739 { IEC_P27-1 iso-ir-143 csISO143IECP271 }
6740 { ISO_8859-5:1988 iso-ir-144 ISO_8859-5 ISO-8859-5 cyrillic
6741 csISOLatinCyrillic }
6742 { JUS_I.B1.003-serb iso-ir-146 serbian csISO146Serbian }
6743 { JUS_I.B1.003-mac macedonian iso-ir-147 csISO147Macedonian }
6744 { ISO_8859-9:1989 iso-ir-148 ISO_8859-9 ISO-8859-9 latin5 l5 csISOLatin5 }
6745 { greek-ccitt iso-ir-150 csISO150 csISO150GreekCCITT }
6746 { NC_NC00-10:81 cuba iso-ir-151 ISO646-CU csISO151Cuba }
6747 { ISO_6937-2-25 iso-ir-152 csISO6937Add }
6748 { GOST_19768-74 ST_SEV_358-88 iso-ir-153 csISO153GOST1976874 }
6749 { ISO_8859-supp iso-ir-154 latin1-2-5 csISO8859Supp }
6750 { ISO_10367-box iso-ir-155 csISO10367Box }
6751 { ISO-8859-10 iso-ir-157 l6 ISO_8859-10:1992 csISOLatin6 latin6 }
6752 { latin-lap lap iso-ir-158 csISO158Lap }
6753 { JIS_X0212-1990 x0212 iso-ir-159 csISO159JISX02121990 }
6754 { DS_2089 DS2089 ISO646-DK dk csISO646Danish }
6757 { JIS_X0201 X0201 csHalfWidthKatakana }
6758 { KSC5636 ISO646-KR csKSC5636 }
6759 { ISO-10646-UCS-2 csUnicode }
6760 { ISO-10646-UCS-4 csUCS4 }
6761 { DEC-MCS dec csDECMCS }
6762 { hp-roman8 roman8 r8 csHPRoman8 }
6763 { macintosh mac csMacintosh }
6764 { IBM037 cp037 ebcdic-cp-us ebcdic-cp-ca ebcdic-cp-wt ebcdic-cp-nl
6766 { IBM038 EBCDIC-INT cp038 csIBM038 }
6767 { IBM273 CP273 csIBM273 }
6768 { IBM274 EBCDIC-BE CP274 csIBM274 }
6769 { IBM275 EBCDIC-BR cp275 csIBM275 }
6770 { IBM277 EBCDIC-CP-DK EBCDIC-CP-NO csIBM277 }
6771 { IBM278 CP278 ebcdic-cp-fi ebcdic-cp-se csIBM278 }
6772 { IBM280 CP280 ebcdic-cp-it csIBM280 }
6773 { IBM281 EBCDIC-JP-E cp281 csIBM281 }
6774 { IBM284 CP284 ebcdic-cp-es csIBM284 }
6775 { IBM285 CP285 ebcdic-cp-gb csIBM285 }
6776 { IBM290 cp290 EBCDIC-JP-kana csIBM290 }
6777 { IBM297 cp297 ebcdic-cp-fr csIBM297 }
6778 { IBM420 cp420 ebcdic-cp-ar1 csIBM420 }
6779 { IBM423 cp423 ebcdic-cp-gr csIBM423 }
6780 { IBM424 cp424 ebcdic-cp-he csIBM424 }
6781 { IBM437 cp437 437 csPC8CodePage437 }
6782 { IBM500 CP500 ebcdic-cp-be ebcdic-cp-ch csIBM500 }
6783 { IBM775 cp775 csPC775Baltic }
6784 { IBM850 cp850 850 csPC850Multilingual }
6785 { IBM851 cp851 851 csIBM851 }
6786 { IBM852 cp852 852 csPCp852 }
6787 { IBM855 cp855 855 csIBM855 }
6788 { IBM857 cp857 857 csIBM857 }
6789 { IBM860 cp860 860 csIBM860 }
6790 { IBM861 cp861 861 cp-is csIBM861 }
6791 { IBM862 cp862 862 csPC862LatinHebrew }
6792 { IBM863 cp863 863 csIBM863 }
6793 { IBM864 cp864 csIBM864 }
6794 { IBM865 cp865 865 csIBM865 }
6795 { IBM866 cp866 866 csIBM866 }
6796 { IBM868 CP868 cp-ar csIBM868 }
6797 { IBM869 cp869 869 cp-gr csIBM869 }
6798 { IBM870 CP870 ebcdic-cp-roece ebcdic-cp-yu csIBM870 }
6799 { IBM871 CP871 ebcdic-cp-is csIBM871 }
6800 { IBM880 cp880 EBCDIC-Cyrillic csIBM880 }
6801 { IBM891 cp891 csIBM891 }
6802 { IBM903 cp903 csIBM903 }
6803 { IBM904 cp904 904 csIBBM904 }
6804 { IBM905 CP905 ebcdic-cp-tr csIBM905 }
6805 { IBM918 CP918 ebcdic-cp-ar2 csIBM918 }
6806 { IBM1026 CP1026 csIBM1026 }
6807 { EBCDIC-AT-DE csIBMEBCDICATDE }
6808 { EBCDIC-AT-DE-A csEBCDICATDEA }
6809 { EBCDIC-CA-FR csEBCDICCAFR }
6810 { EBCDIC-DK-NO csEBCDICDKNO }
6811 { EBCDIC-DK-NO-A csEBCDICDKNOA }
6812 { EBCDIC-FI-SE csEBCDICFISE }
6813 { EBCDIC-FI-SE-A csEBCDICFISEA }
6814 { EBCDIC-FR csEBCDICFR }
6815 { EBCDIC-IT csEBCDICIT }
6816 { EBCDIC-PT csEBCDICPT }
6817 { EBCDIC-ES csEBCDICES }
6818 { EBCDIC-ES-A csEBCDICESA }
6819 { EBCDIC-ES-S csEBCDICESS }
6820 { EBCDIC-UK csEBCDICUK }
6821 { EBCDIC-US csEBCDICUS }
6822 { UNKNOWN-8BIT csUnknown8BiT }
6823 { MNEMONIC csMnemonic }
6828 { IBM00858 CCSID00858 CP00858 PC-Multilingual-850+euro }
6829 { IBM00924 CCSID00924 CP00924 ebcdic-Latin9--euro }
6830 { IBM01140 CCSID01140 CP01140 ebcdic-us-37+euro }
6831 { IBM01141 CCSID01141 CP01141 ebcdic-de-273+euro }
6832 { IBM01142 CCSID01142 CP01142 ebcdic-dk-277+euro ebcdic-no-277+euro }
6833 { IBM01143 CCSID01143 CP01143 ebcdic-fi-278+euro ebcdic-se-278+euro }
6834 { IBM01144 CCSID01144 CP01144 ebcdic-it-280+euro }
6835 { IBM01145 CCSID01145 CP01145 ebcdic-es-284+euro }
6836 { IBM01146 CCSID01146 CP01146 ebcdic-gb-285+euro }
6837 { IBM01147 CCSID01147 CP01147 ebcdic-fr-297+euro }
6838 { IBM01148 CCSID01148 CP01148 ebcdic-international-500+euro }
6839 { IBM01149 CCSID01149 CP01149 ebcdic-is-871+euro }
6840 { IBM1047 IBM-1047 }
6841 { PTCP154 csPTCP154 PT154 CP154 Cyrillic-Asian }
6842 { Amiga-1251 Ami1251 Amiga1251 Ami-1251 }
6843 { UNICODE-1-1 csUnicode11 }
6846 { UNICODE-1-1-UTF-7 csUnicode11UTF7 }
6847 { ISO-8859-14 iso-ir-199 ISO_8859-14:1998 ISO_8859-14 latin8 iso-celtic
6849 { ISO-8859-15 ISO_8859-15 Latin-9 }
6850 { ISO-8859-16 iso-ir-226 ISO_8859-16:2001 ISO_8859-16 latin10 l10 }
6851 { GBK CP936 MS936 windows-936 }
6852 { JIS_Encoding csJISEncoding }
6853 { Shift_JIS MS_Kanji csShiftJIS }
6854 { Extended_UNIX_Code_Packed_Format_for_Japanese csEUCPkdFmtJapanese
6856 { Extended_UNIX_Code_Fixed_Width_for_Japanese csEUCFixWidJapanese }
6857 { ISO-10646-UCS-Basic csUnicodeASCII }
6858 { ISO-10646-Unicode-Latin1 csUnicodeLatin1 ISO-10646 }
6859 { ISO-Unicode-IBM-1261 csUnicodeIBM1261 }
6860 { ISO-Unicode-IBM-1268 csUnicodeIBM1268 }
6861 { ISO-Unicode-IBM-1276 csUnicodeIBM1276 }
6862 { ISO-Unicode-IBM-1264 csUnicodeIBM1264 }
6863 { ISO-Unicode-IBM-1265 csUnicodeIBM1265 }
6864 { ISO-8859-1-Windows-3.0-Latin-1 csWindows30Latin1 }
6865 { ISO-8859-1-Windows-3.1-Latin-1 csWindows31Latin1 }
6866 { ISO-8859-2-Windows-Latin-2 csWindows31Latin2 }
6867 { ISO-8859-9-Windows-Latin-5 csWindows31Latin5 }
6868 { Adobe-Standard-Encoding csAdobeStandardEncoding }
6869 { Ventura-US csVenturaUS }
6870 { Ventura-International csVenturaInternational }
6871 { PC8-Danish-Norwegian csPC8DanishNorwegian }
6872 { PC8-Turkish csPC8Turkish }
6873 { IBM-Symbols csIBMSymbols }
6874 { IBM-Thai csIBMThai }
6875 { HP-Legal csHPLegal }
6876 { HP-Pi-font csHPPiFont }
6877 { HP-Math8 csHPMath8 }
6878 { Adobe-Symbol-Encoding csHPPSMath }
6879 { HP-DeskTop csHPDesktop }
6880 { Ventura-Math csVenturaMath }
6881 { Microsoft-Publishing csMicrosoftPublishing }
6882 { Windows-31J csWindows31J }
6887 proc tcl_encoding {enc} {
6888 global encoding_aliases
6889 set names [encoding names]
6890 set lcnames [string tolower $names]
6891 set enc [string tolower $enc]
6892 set i [lsearch -exact $lcnames $enc]
6894 # look for "isonnn" instead of "iso-nnn" or "iso_nnn"
6895 if {[regsub {^iso[-_]} $enc iso encx]} {
6896 set i [lsearch -exact $lcnames $encx]
6900 foreach l $encoding_aliases {
6901 set ll [string tolower $l]
6902 if {[lsearch -exact $ll $enc] < 0} continue
6903 # look through the aliases for one that tcl knows about
6905 set i [lsearch -exact $lcnames $e]
6907 if {[regsub {^iso[-_]} $e iso ex]} {
6908 set i [lsearch -exact $lcnames $ex]
6917 return [lindex $names $i]
6924 set diffopts "-U 5 -p"
6925 set wrcomcmd "git diff-tree --stdin -p --pretty"
6929 set gitencoding [exec git config --get i18n.commitencoding]
6931 if {$gitencoding == ""} {
6932 set gitencoding "utf-8"
6934 set tclencoding [tcl_encoding $gitencoding]
6935 if {$tclencoding == {}} {
6936 puts stderr "Warning: encoding $gitencoding is not supported by Tcl/Tk"
6939 set mainfont {Helvetica 9}
6940 set textfont {Courier 9}
6941 set uifont {Helvetica 9 bold}
6943 set findmergefiles 0
6951 set cmitmode "patch"
6952 set wrapcomment "none"
6956 set colors {green red blue magenta darkgrey brown orange}
6959 set diffcolors {red "#00a000" blue}
6960 set selectbgcolor gray85
6962 catch {source ~/.gitk}
6964 font create optionfont -family sans-serif -size -12
6968 switch -regexp -- $arg {
6970 "^-d" { set datemode 1 }
6972 lappend revtreeargs $arg
6977 # check that we can find a .git directory somewhere...
6979 if {![file isdirectory $gitdir]} {
6980 show_error {} . "Cannot find the git directory \"$gitdir\"."
6984 set cmdline_files {}
6985 set i [lsearch -exact $revtreeargs "--"]
6987 set cmdline_files [lrange $revtreeargs [expr {$i + 1}] end]
6988 set revtreeargs [lrange $revtreeargs 0 [expr {$i - 1}]]
6989 } elseif {$revtreeargs ne {}} {
6991 set f [eval exec git rev-parse --no-revs --no-flags $revtreeargs]
6992 set cmdline_files [split $f "\n"]
6993 set n [llength $cmdline_files]
6994 set revtreeargs [lrange $revtreeargs 0 end-$n]
6996 # unfortunately we get both stdout and stderr in $err,
6997 # so look for "fatal:".
6998 set i [string first "fatal:" $err]
7000 set err [string range $err [expr {$i + 6}] end]
7002 show_error {} . "Bad arguments to gitk:\n$err"
7012 set highlight_paths {}
7013 set searchdirn -forwards
7023 set selectedhlview None
7034 wm title . "[file tail $argv0]: [file tail [pwd]]"
7037 if {$cmdline_files ne {} || $revtreeargs ne {}} {
7038 # create a view for the files/dirs specified on the command line
7042 set viewname(1) "Command line"
7043 set viewfiles(1) $cmdline_files
7044 set viewargs(1) $revtreeargs
7047 .bar.view entryconf Edit* -state normal
7048 .bar.view entryconf Delete* -state normal
7051 if {[info exists permviews]} {
7052 foreach v $permviews {
7055 set viewname($n) [lindex $v 0]
7056 set viewfiles($n) [lindex $v 1]
7057 set viewargs($n) [lindex $v 2]