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 proc start_rev_list {view} {
20 global startmsecs nextupdate
21 global commfd leftover tclencoding datemode
22 global viewargs viewfiles commitidx
24 set startmsecs [clock clicks -milliseconds]
25 set nextupdate [expr {$startmsecs + 100}]
26 set commitidx($view) 0
27 set args $viewargs($view)
28 if {$viewfiles($view) ne {}} {
29 set args [concat $args "--" $viewfiles($view)]
31 set order "--topo-order"
33 set order "--date-order"
36 set fd [open [concat | git rev-list --header $order \
37 --parents --boundary --default HEAD $args] r]
39 puts stderr "Error executing git rev-list: $err"
43 set leftover($view) {}
44 fconfigure $fd -blocking 0 -translation lf
45 if {$tclencoding != {}} {
46 fconfigure $fd -encoding $tclencoding
48 fileevent $fd readable [list getcommitlines $fd $view]
52 proc stop_rev_list {} {
55 if {![info exists commfd($curview)]} return
56 set fd $commfd($curview)
62 unset commfd($curview)
66 global phase canv mainfont curview
70 start_rev_list $curview
71 show_status "Reading commits..."
74 proc getcommitlines {fd view} {
75 global commitlisted nextupdate
76 global leftover commfd
77 global displayorder commitidx commitrow commitdata
78 global parentlist childlist children curview hlview
79 global vparentlist vchildlist vdisporder vcmitlisted
81 set stuff [read $fd 500000]
83 if {![eof $fd]} return
87 # set it blocking so we wait for the process to terminate
88 fconfigure $fd -blocking 1
89 if {[catch {close $fd} err]} {
91 if {$view != $curview} {
92 set fv " for the \"$viewname($view)\" view"
94 if {[string range $err 0 4] == "usage"} {
95 set err "Gitk: error reading commits$fv:\
96 bad arguments to git rev-list."
97 if {$viewname($view) eq "Command line"} {
99 " (Note: arguments to gitk are passed to git rev-list\
100 to allow selection of commits to be displayed.)"
103 set err "Error reading commits$fv: $err"
107 if {$view == $curview} {
108 after idle finishcommits
115 set i [string first "\0" $stuff $start]
117 append leftover($view) [string range $stuff $start end]
121 set cmit $leftover($view)
122 append cmit [string range $stuff 0 [expr {$i - 1}]]
123 set leftover($view) {}
125 set cmit [string range $stuff $start [expr {$i - 1}]]
127 set start [expr {$i + 1}]
128 set j [string first "\n" $cmit]
132 set ids [string range $cmit 0 [expr {$j - 1}]]
133 if {[string range $ids 0 0] == "-"} {
135 set ids [string range $ids 1 end]
139 if {[string length $id] != 40} {
147 if {[string length $shortcmit] > 80} {
148 set shortcmit "[string range $shortcmit 0 80]..."
150 error_popup "Can't parse git rev-list output: {$shortcmit}"
153 set id [lindex $ids 0]
155 set olds [lrange $ids 1 end]
158 if {$i == 0 || [lsearch -exact $olds $p] >= $i} {
159 lappend children($view,$p) $id
166 if {![info exists children($view,$id)]} {
167 set children($view,$id) {}
169 set commitdata($id) [string range $cmit [expr {$j + 1}] end]
170 set commitrow($view,$id) $commitidx($view)
171 incr commitidx($view)
172 if {$view == $curview} {
173 lappend parentlist $olds
174 lappend childlist $children($view,$id)
175 lappend displayorder $id
176 lappend commitlisted $listed
178 lappend vparentlist($view) $olds
179 lappend vchildlist($view) $children($view,$id)
180 lappend vdisporder($view) $id
181 lappend vcmitlisted($view) $listed
186 if {$view == $curview} {
187 while {[layoutmore $nextupdate]} doupdate
188 } elseif {[info exists hlview] && $view == $hlview} {
192 if {[clock clicks -milliseconds] >= $nextupdate} {
198 global commfd nextupdate numcommits
200 foreach v [array names commfd] {
201 fileevent $commfd($v) readable {}
204 set nextupdate [expr {[clock clicks -milliseconds] + 100}]
205 foreach v [array names commfd] {
207 fileevent $fd readable [list getcommitlines $fd $v]
211 proc readcommit {id} {
212 if {[catch {set contents [exec git cat-file commit $id]}]} return
213 parsecommit $id $contents 0
216 proc updatecommits {} {
217 global viewdata curview phase displayorder
218 global children commitrow selectedline thickerline
225 foreach id $displayorder {
226 catch {unset children($n,$id)}
227 catch {unset commitrow($n,$id)}
230 catch {unset selectedline}
231 catch {unset thickerline}
232 catch {unset viewdata($n)}
238 proc parsecommit {id contents listed} {
239 global commitinfo cdate
248 set hdrend [string first "\n\n" $contents]
250 # should never happen...
251 set hdrend [string length $contents]
253 set header [string range $contents 0 [expr {$hdrend - 1}]]
254 set comment [string range $contents [expr {$hdrend + 2}] end]
255 foreach line [split $header "\n"] {
256 set tag [lindex $line 0]
257 if {$tag == "author"} {
258 set audate [lindex $line end-1]
259 set auname [lrange $line 1 end-2]
260 } elseif {$tag == "committer"} {
261 set comdate [lindex $line end-1]
262 set comname [lrange $line 1 end-2]
266 # take the first line of the comment as the headline
267 set i [string first "\n" $comment]
269 set headline [string trim [string range $comment 0 $i]]
271 set headline $comment
274 # git rev-list indents the comment by 4 spaces;
275 # if we got this via git cat-file, add the indentation
277 foreach line [split $comment "\n"] {
278 append newcomment " "
279 append newcomment $line
280 append newcomment "\n"
282 set comment $newcomment
284 if {$comdate != {}} {
285 set cdate($id) $comdate
287 set commitinfo($id) [list $headline $auname $audate \
288 $comname $comdate $comment]
291 proc getcommit {id} {
292 global commitdata commitinfo
294 if {[info exists commitdata($id)]} {
295 parsecommit $id $commitdata($id) 1
298 if {![info exists commitinfo($id)]} {
299 set commitinfo($id) {"No commit information available"}
306 global tagids idtags headids idheads tagcontents
307 global otherrefids idotherrefs mainhead
309 foreach v {tagids idtags headids idheads otherrefids idotherrefs} {
312 set refd [open [list | git show-ref] r]
313 while {0 <= [set n [gets $refd line]]} {
314 if {![regexp {^([0-9a-f]{40}) refs/([^^]*)$} $line \
318 if {[regexp {^remotes/.*/HEAD$} $path match]} {
321 if {![regexp {^(tags|heads)/(.*)$} $path match type name]} {
325 if {[regexp {^remotes/} $path match]} {
328 if {$type == "tags"} {
329 set tagids($name) $id
330 lappend idtags($id) $name
335 set commit [exec git rev-parse "$id^0"]
336 if {$commit != $id} {
337 set tagids($name) $commit
338 lappend idtags($commit) $name
342 set tagcontents($name) [exec git cat-file tag $id]
344 } elseif { $type == "heads" } {
345 set headids($name) $id
346 lappend idheads($id) $name
348 set otherrefids($name) $id
349 lappend idotherrefs($id) $name
355 set thehead [exec git symbolic-ref HEAD]
356 if {[string match "refs/heads/*" $thehead]} {
357 set mainhead [string range $thehead 11 end]
362 proc show_error {w top msg} {
363 message $w.m -text $msg -justify center -aspect 400
364 pack $w.m -side top -fill x -padx 20 -pady 20
365 button $w.ok -text OK -command "destroy $top"
366 pack $w.ok -side bottom -fill x
367 bind $top <Visibility> "grab $top; focus $top"
368 bind $top <Key-Return> "destroy $top"
372 proc error_popup msg {
376 show_error $w $w $msg
379 proc confirm_popup msg {
385 message $w.m -text $msg -justify center -aspect 400
386 pack $w.m -side top -fill x -padx 20 -pady 20
387 button $w.ok -text OK -command "set confirm_ok 1; destroy $w"
388 pack $w.ok -side left -fill x
389 button $w.cancel -text Cancel -command "destroy $w"
390 pack $w.cancel -side right -fill x
391 bind $w <Visibility> "grab $w; focus $w"
397 global canv canv2 canv3 linespc charspc ctext cflist
398 global textfont mainfont uifont
399 global findtype findtypemenu findloc findstring fstring geometry
400 global entries sha1entry sha1string sha1but
401 global maincursor textcursor curtextcursor
402 global rowctxmenu mergemax wrapcomment
403 global highlight_files gdttype
404 global searchstring sstring
405 global bgcolor fgcolor bglist fglist diffcolors
409 .bar add cascade -label "File" -menu .bar.file
410 .bar configure -font $uifont
412 .bar.file add command -label "Update" -command updatecommits
413 .bar.file add command -label "Reread references" -command rereadrefs
414 .bar.file add command -label "Quit" -command doquit
415 .bar.file configure -font $uifont
417 .bar add cascade -label "Edit" -menu .bar.edit
418 .bar.edit add command -label "Preferences" -command doprefs
419 .bar.edit configure -font $uifont
421 menu .bar.view -font $uifont
422 .bar add cascade -label "View" -menu .bar.view
423 .bar.view add command -label "New view..." -command {newview 0}
424 .bar.view add command -label "Edit view..." -command editview \
426 .bar.view add command -label "Delete view" -command delview -state disabled
427 .bar.view add separator
428 .bar.view add radiobutton -label "All files" -command {showview 0} \
429 -variable selectedview -value 0
432 .bar add cascade -label "Help" -menu .bar.help
433 .bar.help add command -label "About gitk" -command about
434 .bar.help add command -label "Key bindings" -command keys
435 .bar.help configure -font $uifont
436 . configure -menu .bar
438 # the gui has upper and lower half, parts of a paned window.
439 panedwindow .ctop -orient vertical
441 # possibly use assumed geometry
442 if {![info exists geometry(topheight)]} {
443 set geometry(topheight) [expr {15 * $linespc}]
444 set geometry(topwidth) [expr {80 * $charspc}]
445 set geometry(botheight) [expr {15 * $linespc}]
446 set geometry(botwidth) [expr {50 * $charspc}]
447 set geometry(canv) [expr {40 * $charspc}]
448 set geometry(canv2) [expr {20 * $charspc}]
449 set geometry(canv3) [expr {20 * $charspc}]
452 # the upper half will have a paned window, a scroll bar to the right, and some stuff below
453 frame .tf -height $geometry(topheight) -width $geometry(topwidth)
455 panedwindow .tf.histframe.pwclist -orient horizontal -sashpad 0 -handlesize 4
457 # create three canvases
458 set cscroll .tf.histframe.csb
459 set canv .tf.histframe.pwclist.canv
460 canvas $canv -width $geometry(canv) \
461 -background $bgcolor -bd 0 \
462 -yscrollincr $linespc -yscrollcommand "scrollcanv $cscroll"
463 .tf.histframe.pwclist add $canv
464 set canv2 .tf.histframe.pwclist.canv2
465 canvas $canv2 -width $geometry(canv2) \
466 -background $bgcolor -bd 0 -yscrollincr $linespc
467 .tf.histframe.pwclist add $canv2
468 set canv3 .tf.histframe.pwclist.canv3
469 canvas $canv3 -width $geometry(canv3) \
470 -background $bgcolor -bd 0 -yscrollincr $linespc
471 .tf.histframe.pwclist add $canv3
473 # a scroll bar to rule them
474 scrollbar $cscroll -command {allcanvs yview} -highlightthickness 0
475 pack $cscroll -side right -fill y
476 bind .tf.histframe.pwclist <Configure> {resizeclistpanes %W %w}
477 lappend bglist $canv $canv2 $canv3
478 pack .tf.histframe.pwclist -fill both -expand 1 -side left
480 # we have two button bars at bottom of top frame. Bar 1
482 frame .tf.lbar -height 15
484 set sha1entry .tf.bar.sha1
485 set entries $sha1entry
486 set sha1but .tf.bar.sha1label
487 button $sha1but -text "SHA1 ID: " -state disabled -relief flat \
488 -command gotocommit -width 8 -font $uifont
489 $sha1but conf -disabledforeground [$sha1but cget -foreground]
490 pack .tf.bar.sha1label -side left
491 entry $sha1entry -width 40 -font $textfont -textvariable sha1string
492 trace add variable sha1string write sha1change
493 pack $sha1entry -side left -pady 2
495 image create bitmap bm-left -data {
496 #define left_width 16
497 #define left_height 16
498 static unsigned char left_bits[] = {
499 0x00, 0x00, 0xc0, 0x01, 0xe0, 0x00, 0x70, 0x00, 0x38, 0x00, 0x1c, 0x00,
500 0x0e, 0x00, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0x0e, 0x00, 0x1c, 0x00,
501 0x38, 0x00, 0x70, 0x00, 0xe0, 0x00, 0xc0, 0x01};
503 image create bitmap bm-right -data {
504 #define right_width 16
505 #define right_height 16
506 static unsigned char right_bits[] = {
507 0x00, 0x00, 0xc0, 0x01, 0x80, 0x03, 0x00, 0x07, 0x00, 0x0e, 0x00, 0x1c,
508 0x00, 0x38, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0x00, 0x38, 0x00, 0x1c,
509 0x00, 0x0e, 0x00, 0x07, 0x80, 0x03, 0xc0, 0x01};
511 button .tf.bar.leftbut -image bm-left -command goback \
512 -state disabled -width 26
513 pack .tf.bar.leftbut -side left -fill y
514 button .tf.bar.rightbut -image bm-right -command goforw \
515 -state disabled -width 26
516 pack .tf.bar.rightbut -side left -fill y
518 button .tf.bar.findbut -text "Find" -command dofind -font $uifont
519 pack .tf.bar.findbut -side left
521 set fstring .tf.bar.findstring
522 lappend entries $fstring
523 entry $fstring -width 30 -font $textfont -textvariable findstring
524 trace add variable findstring write find_change
525 pack $fstring -side left -expand 1 -fill x -in .tf.bar
527 set findtypemenu [tk_optionMenu .tf.bar.findtype \
528 findtype Exact IgnCase Regexp]
529 trace add variable findtype write find_change
530 .tf.bar.findtype configure -font $uifont
531 .tf.bar.findtype.menu configure -font $uifont
532 set findloc "All fields"
533 tk_optionMenu .tf.bar.findloc findloc "All fields" Headline \
534 Comments Author Committer
535 trace add variable findloc write find_change
536 .tf.bar.findloc configure -font $uifont
537 .tf.bar.findloc.menu configure -font $uifont
538 pack .tf.bar.findloc -side right
539 pack .tf.bar.findtype -side right
541 # build up the bottom bar of upper window
542 label .tf.lbar.flabel -text "Highlight: Commits " \
544 pack .tf.lbar.flabel -side left -fill y
545 set gdttype "touching paths:"
546 set gm [tk_optionMenu .tf.lbar.gdttype gdttype "touching paths:" \
547 "adding/removing string:"]
548 trace add variable gdttype write hfiles_change
549 $gm conf -font $uifont
550 .tf.lbar.gdttype conf -font $uifont
551 pack .tf.lbar.gdttype -side left -fill y
552 entry .tf.lbar.fent -width 25 -font $textfont \
553 -textvariable highlight_files
554 trace add variable highlight_files write hfiles_change
555 lappend entries .tf.lbar.fent
556 pack .tf.lbar.fent -side left -fill x -expand 1
557 label .tf.lbar.vlabel -text " OR in view" -font $uifont
558 pack .tf.lbar.vlabel -side left -fill y
559 global viewhlmenu selectedhlview
560 set viewhlmenu [tk_optionMenu .tf.lbar.vhl selectedhlview None]
561 $viewhlmenu entryconf None -command delvhighlight
562 $viewhlmenu conf -font $uifont
563 .tf.lbar.vhl conf -font $uifont
564 pack .tf.lbar.vhl -side left -fill y
565 label .tf.lbar.rlabel -text " OR " -font $uifont
566 pack .tf.lbar.rlabel -side left -fill y
567 global highlight_related
568 set m [tk_optionMenu .tf.lbar.relm highlight_related None \
569 "Descendent" "Not descendent" "Ancestor" "Not ancestor"]
570 $m conf -font $uifont
571 .tf.lbar.relm conf -font $uifont
572 trace add variable highlight_related write vrel_change
573 pack .tf.lbar.relm -side left -fill y
575 # Finish putting the upper half of the viewer together
576 pack .tf.lbar -in .tf -side bottom -fill x
577 pack .tf.bar -in .tf -side bottom -fill x
578 pack .tf.histframe -fill both -side top -expand 1
581 # now build up the bottom
582 panedwindow .pwbottom -orient horizontal
584 # lower left, a text box over search bar, scroll bar to the right
585 # if we know window height, then that will set the lower text height, otherwise
586 # we set lower text height which will drive window height
587 if {[info exists geometry(main)]} {
588 frame .bleft -width $geometry(botwidth)
590 frame .bleft -width $geometry(botwidth) -height $geometry(botheight)
594 button .bleft.top.search -text "Search" -command dosearch \
596 pack .bleft.top.search -side left -padx 5
597 set sstring .bleft.top.sstring
598 entry $sstring -width 20 -font $textfont -textvariable searchstring
599 lappend entries $sstring
600 trace add variable searchstring write incrsearch
601 pack $sstring -side left -expand 1 -fill x
602 set ctext .bleft.ctext
603 text $ctext -background $bgcolor -foreground $fgcolor \
604 -state disabled -font $textfont \
605 -yscrollcommand scrolltext -wrap none
606 scrollbar .bleft.sb -command "$ctext yview"
607 pack .bleft.top -side top -fill x
608 pack .bleft.sb -side right -fill y
609 pack $ctext -side left -fill both -expand 1
610 lappend bglist $ctext
611 lappend fglist $ctext
613 $ctext tag conf comment -wrap $wrapcomment
614 $ctext tag conf filesep -font [concat $textfont bold] -back "#aaaaaa"
615 $ctext tag conf hunksep -fore [lindex $diffcolors 2]
616 $ctext tag conf d0 -fore [lindex $diffcolors 0]
617 $ctext tag conf d1 -fore [lindex $diffcolors 1]
618 $ctext tag conf m0 -fore red
619 $ctext tag conf m1 -fore blue
620 $ctext tag conf m2 -fore green
621 $ctext tag conf m3 -fore purple
622 $ctext tag conf m4 -fore brown
623 $ctext tag conf m5 -fore "#009090"
624 $ctext tag conf m6 -fore magenta
625 $ctext tag conf m7 -fore "#808000"
626 $ctext tag conf m8 -fore "#009000"
627 $ctext tag conf m9 -fore "#ff0080"
628 $ctext tag conf m10 -fore cyan
629 $ctext tag conf m11 -fore "#b07070"
630 $ctext tag conf m12 -fore "#70b0f0"
631 $ctext tag conf m13 -fore "#70f0b0"
632 $ctext tag conf m14 -fore "#f0b070"
633 $ctext tag conf m15 -fore "#ff70b0"
634 $ctext tag conf mmax -fore darkgrey
636 $ctext tag conf mresult -font [concat $textfont bold]
637 $ctext tag conf msep -font [concat $textfont bold]
638 $ctext tag conf found -back yellow
645 radiobutton .bright.mode.patch -text "Patch" \
646 -command reselectline -variable cmitmode -value "patch"
647 radiobutton .bright.mode.tree -text "Tree" \
648 -command reselectline -variable cmitmode -value "tree"
649 grid .bright.mode.patch .bright.mode.tree -sticky ew
650 pack .bright.mode -side top -fill x
651 set cflist .bright.cfiles
652 set indent [font measure $mainfont "nn"]
654 -background $bgcolor -foreground $fgcolor \
656 -tabs [list $indent [expr {2 * $indent}]] \
657 -yscrollcommand ".bright.sb set" \
658 -cursor [. cget -cursor] \
659 -spacing1 1 -spacing3 1
660 lappend bglist $cflist
661 lappend fglist $cflist
662 scrollbar .bright.sb -command "$cflist yview"
663 pack .bright.sb -side right -fill y
664 pack $cflist -side left -fill both -expand 1
665 $cflist tag configure highlight \
666 -background [$cflist cget -selectbackground]
667 $cflist tag configure bold -font [concat $mainfont bold]
669 .pwbottom add .bright
672 # restore window position if known
673 if {[info exists geometry(main)]} {
674 wm geometry . "$geometry(main)"
677 bind .pwbottom <Configure> {resizecdetpanes %W %w}
678 pack .ctop -fill both -expand 1
679 bindall <1> {selcanvline %W %x %y}
680 #bindall <B1-Motion> {selcanvline %W %x %y}
681 bindall <ButtonRelease-4> "allcanvs yview scroll -5 units"
682 bindall <ButtonRelease-5> "allcanvs yview scroll 5 units"
683 bindall <2> "canvscan mark %W %x %y"
684 bindall <B2-Motion> "canvscan dragto %W %x %y"
685 bindkey <Home> selfirstline
686 bindkey <End> sellastline
687 bind . <Key-Up> "selnextline -1"
688 bind . <Key-Down> "selnextline 1"
689 bind . <Shift-Key-Up> "next_highlight -1"
690 bind . <Shift-Key-Down> "next_highlight 1"
691 bindkey <Key-Right> "goforw"
692 bindkey <Key-Left> "goback"
693 bind . <Key-Prior> "selnextpage -1"
694 bind . <Key-Next> "selnextpage 1"
695 bind . <Control-Home> "allcanvs yview moveto 0.0"
696 bind . <Control-End> "allcanvs yview moveto 1.0"
697 bind . <Control-Key-Up> "allcanvs yview scroll -1 units"
698 bind . <Control-Key-Down> "allcanvs yview scroll 1 units"
699 bind . <Control-Key-Prior> "allcanvs yview scroll -1 pages"
700 bind . <Control-Key-Next> "allcanvs yview scroll 1 pages"
701 bindkey <Key-Delete> "$ctext yview scroll -1 pages"
702 bindkey <Key-BackSpace> "$ctext yview scroll -1 pages"
703 bindkey <Key-space> "$ctext yview scroll 1 pages"
704 bindkey p "selnextline -1"
705 bindkey n "selnextline 1"
708 bindkey i "selnextline -1"
709 bindkey k "selnextline 1"
712 bindkey b "$ctext yview scroll -1 pages"
713 bindkey d "$ctext yview scroll 18 units"
714 bindkey u "$ctext yview scroll -18 units"
715 bindkey / {findnext 1}
716 bindkey <Key-Return> {findnext 0}
719 bind . <Control-q> doquit
720 bind . <Control-f> dofind
721 bind . <Control-g> {findnext 0}
722 bind . <Control-r> dosearchback
723 bind . <Control-s> dosearch
724 bind . <Control-equal> {incrfont 1}
725 bind . <Control-KP_Add> {incrfont 1}
726 bind . <Control-minus> {incrfont -1}
727 bind . <Control-KP_Subtract> {incrfont -1}
728 wm protocol . WM_DELETE_WINDOW doquit
729 bind . <Button-1> "click %W"
730 bind $fstring <Key-Return> dofind
731 bind $sha1entry <Key-Return> gotocommit
732 bind $sha1entry <<PasteSelection>> clearsha1
733 bind $cflist <1> {sel_flist %W %x %y; break}
734 bind $cflist <B1-Motion> {sel_flist %W %x %y; break}
735 bind $cflist <ButtonRelease-1> {treeclick %W %x %y}
737 set maincursor [. cget -cursor]
738 set textcursor [$ctext cget -cursor]
739 set curtextcursor $textcursor
741 set rowctxmenu .rowctxmenu
742 menu $rowctxmenu -tearoff 0
743 $rowctxmenu add command -label "Diff this -> selected" \
744 -command {diffvssel 0}
745 $rowctxmenu add command -label "Diff selected -> this" \
746 -command {diffvssel 1}
747 $rowctxmenu add command -label "Make patch" -command mkpatch
748 $rowctxmenu add command -label "Create tag" -command mktag
749 $rowctxmenu add command -label "Write commit to file" -command writecommit
750 $rowctxmenu add command -label "Create new branch" -command mkbranch
751 $rowctxmenu add command -label "Cherry-pick this commit" \
754 set headctxmenu .headctxmenu
755 menu $headctxmenu -tearoff 0
756 $headctxmenu add command -label "Check out this branch" \
758 $headctxmenu add command -label "Remove this branch" \
762 # mouse-2 makes all windows scan vertically, but only the one
763 # the cursor is in scans horizontally
764 proc canvscan {op w x y} {
765 global canv canv2 canv3
766 foreach c [list $canv $canv2 $canv3] {
775 proc scrollcanv {cscroll f0 f1} {
781 # when we make a key binding for the toplevel, make sure
782 # it doesn't get triggered when that key is pressed in the
783 # find string entry widget.
784 proc bindkey {ev script} {
787 set escript [bind Entry $ev]
788 if {$escript == {}} {
789 set escript [bind Entry <Key>]
792 bind $e $ev "$escript; break"
796 # set the focus back to the toplevel for any click outside
807 global canv canv2 canv3 ctext cflist mainfont textfont uifont
808 global stuffsaved findmergefiles maxgraphpct
809 global maxwidth showneartags
810 global viewname viewfiles viewargs viewperm nextviewnum
811 global cmitmode wrapcomment
812 global colors bgcolor fgcolor diffcolors
814 if {$stuffsaved} return
815 if {![winfo viewable .]} return
817 set f [open "~/.gitk-new" w]
818 puts $f [list set mainfont $mainfont]
819 puts $f [list set textfont $textfont]
820 puts $f [list set uifont $uifont]
821 puts $f [list set findmergefiles $findmergefiles]
822 puts $f [list set maxgraphpct $maxgraphpct]
823 puts $f [list set maxwidth $maxwidth]
824 puts $f [list set cmitmode $cmitmode]
825 puts $f [list set wrapcomment $wrapcomment]
826 puts $f [list set showneartags $showneartags]
827 puts $f [list set bgcolor $bgcolor]
828 puts $f [list set fgcolor $fgcolor]
829 puts $f [list set colors $colors]
830 puts $f [list set diffcolors $diffcolors]
832 puts $f "set geometry(main) [wm geometry .]"
833 puts $f "set geometry(topwidth) [winfo width .tf]"
834 puts $f "set geometry(topheight) [winfo height .tf]"
835 puts $f "set geometry(canv) [winfo width $canv]"
836 puts $f "set geometry(canv2) [winfo width $canv2]"
837 puts $f "set geometry(canv3) [winfo width $canv3]"
838 puts $f "set geometry(botwidth) [winfo width .bleft]"
839 puts $f "set geometry(botheight) [winfo height .bleft]"
841 puts -nonewline $f "set permviews {"
842 for {set v 0} {$v < $nextviewnum} {incr v} {
844 puts $f "{[list $viewname($v) $viewfiles($v) $viewargs($v)]}"
849 file rename -force "~/.gitk-new" "~/.gitk"
854 proc resizeclistpanes {win w} {
856 if {[info exists oldwidth($win)]} {
857 set s0 [$win sash coord 0]
858 set s1 [$win sash coord 1]
860 set sash0 [expr {int($w/2 - 2)}]
861 set sash1 [expr {int($w*5/6 - 2)}]
863 set factor [expr {1.0 * $w / $oldwidth($win)}]
864 set sash0 [expr {int($factor * [lindex $s0 0])}]
865 set sash1 [expr {int($factor * [lindex $s1 0])}]
869 if {$sash1 < $sash0 + 20} {
870 set sash1 [expr {$sash0 + 20}]
872 if {$sash1 > $w - 10} {
873 set sash1 [expr {$w - 10}]
874 if {$sash0 > $sash1 - 20} {
875 set sash0 [expr {$sash1 - 20}]
879 $win sash place 0 $sash0 [lindex $s0 1]
880 $win sash place 1 $sash1 [lindex $s1 1]
882 set oldwidth($win) $w
885 proc resizecdetpanes {win w} {
887 if {[info exists oldwidth($win)]} {
888 set s0 [$win sash coord 0]
890 set sash0 [expr {int($w*3/4 - 2)}]
892 set factor [expr {1.0 * $w / $oldwidth($win)}]
893 set sash0 [expr {int($factor * [lindex $s0 0])}]
897 if {$sash0 > $w - 15} {
898 set sash0 [expr {$w - 15}]
901 $win sash place 0 $sash0 [lindex $s0 1]
903 set oldwidth($win) $w
907 global canv canv2 canv3
913 proc bindall {event action} {
914 global canv canv2 canv3
915 bind $canv $event $action
916 bind $canv2 $event $action
917 bind $canv3 $event $action
922 if {[winfo exists $w]} {
927 wm title $w "About gitk"
929 Gitk - a commit viewer for git
931 Copyright © 2005-2006 Paul Mackerras
933 Use and redistribute under the terms of the GNU General Public License} \
934 -justify center -aspect 400
935 pack $w.m -side top -fill x -padx 20 -pady 20
936 button $w.ok -text Close -command "destroy $w"
937 pack $w.ok -side bottom
942 if {[winfo exists $w]} {
947 wm title $w "Gitk key bindings"
952 <Home> Move to first commit
953 <End> Move to last commit
954 <Up>, p, i Move up one commit
955 <Down>, n, k Move down one commit
956 <Left>, z, j Go back in history list
957 <Right>, x, l Go forward in history list
958 <PageUp> Move up one page in commit list
959 <PageDown> Move down one page in commit list
960 <Ctrl-Home> Scroll to top of commit list
961 <Ctrl-End> Scroll to bottom of commit list
962 <Ctrl-Up> Scroll commit list up one line
963 <Ctrl-Down> Scroll commit list down one line
964 <Ctrl-PageUp> Scroll commit list up one page
965 <Ctrl-PageDown> Scroll commit list down one page
966 <Shift-Up> Move to previous highlighted line
967 <Shift-Down> Move to next highlighted line
968 <Delete>, b Scroll diff view up one page
969 <Backspace> Scroll diff view up one page
970 <Space> Scroll diff view down one page
971 u Scroll diff view up 18 lines
972 d Scroll diff view down 18 lines
974 <Ctrl-G> Move to next find hit
975 <Return> Move to next find hit
976 / Move to next find hit, or redo find
977 ? Move to previous find hit
978 f Scroll diff view to next file
979 <Ctrl-S> Search for next hit in diff view
980 <Ctrl-R> Search for previous hit in diff view
981 <Ctrl-KP+> Increase font size
982 <Ctrl-plus> Increase font size
983 <Ctrl-KP-> Decrease font size
984 <Ctrl-minus> Decrease font size
986 -justify left -bg white -border 2 -relief sunken
987 pack $w.m -side top -fill both
988 button $w.ok -text Close -command "destroy $w"
989 pack $w.ok -side bottom
992 # Procedures for manipulating the file list window at the
993 # bottom right of the overall window.
995 proc treeview {w l openlevs} {
996 global treecontents treediropen treeheight treeparent treeindex
1006 set treecontents() {}
1007 $w conf -state normal
1009 while {[string range $f 0 $prefixend] ne $prefix} {
1010 if {$lev <= $openlevs} {
1011 $w mark set e:$treeindex($prefix) "end -1c"
1012 $w mark gravity e:$treeindex($prefix) left
1014 set treeheight($prefix) $ht
1015 incr ht [lindex $htstack end]
1016 set htstack [lreplace $htstack end end]
1017 set prefixend [lindex $prefendstack end]
1018 set prefendstack [lreplace $prefendstack end end]
1019 set prefix [string range $prefix 0 $prefixend]
1022 set tail [string range $f [expr {$prefixend+1}] end]
1023 while {[set slash [string first "/" $tail]] >= 0} {
1026 lappend prefendstack $prefixend
1027 incr prefixend [expr {$slash + 1}]
1028 set d [string range $tail 0 $slash]
1029 lappend treecontents($prefix) $d
1030 set oldprefix $prefix
1032 set treecontents($prefix) {}
1033 set treeindex($prefix) [incr ix]
1034 set treeparent($prefix) $oldprefix
1035 set tail [string range $tail [expr {$slash+1}] end]
1036 if {$lev <= $openlevs} {
1038 set treediropen($prefix) [expr {$lev < $openlevs}]
1039 set bm [expr {$lev == $openlevs? "tri-rt": "tri-dn"}]
1040 $w mark set d:$ix "end -1c"
1041 $w mark gravity d:$ix left
1043 for {set i 0} {$i < $lev} {incr i} {append str "\t"}
1045 $w image create end -align center -image $bm -padx 1 \
1047 $w insert end $d [highlight_tag $prefix]
1048 $w mark set s:$ix "end -1c"
1049 $w mark gravity s:$ix left
1054 if {$lev <= $openlevs} {
1057 for {set i 0} {$i < $lev} {incr i} {append str "\t"}
1059 $w insert end $tail [highlight_tag $f]
1061 lappend treecontents($prefix) $tail
1064 while {$htstack ne {}} {
1065 set treeheight($prefix) $ht
1066 incr ht [lindex $htstack end]
1067 set htstack [lreplace $htstack end end]
1069 $w conf -state disabled
1072 proc linetoelt {l} {
1073 global treeheight treecontents
1078 foreach e $treecontents($prefix) {
1083 if {[string index $e end] eq "/"} {
1084 set n $treeheight($prefix$e)
1096 proc highlight_tree {y prefix} {
1097 global treeheight treecontents cflist
1099 foreach e $treecontents($prefix) {
1101 if {[highlight_tag $path] ne {}} {
1102 $cflist tag add bold $y.0 "$y.0 lineend"
1105 if {[string index $e end] eq "/" && $treeheight($path) > 1} {
1106 set y [highlight_tree $y $path]
1112 proc treeclosedir {w dir} {
1113 global treediropen treeheight treeparent treeindex
1115 set ix $treeindex($dir)
1116 $w conf -state normal
1117 $w delete s:$ix e:$ix
1118 set treediropen($dir) 0
1119 $w image configure a:$ix -image tri-rt
1120 $w conf -state disabled
1121 set n [expr {1 - $treeheight($dir)}]
1122 while {$dir ne {}} {
1123 incr treeheight($dir) $n
1124 set dir $treeparent($dir)
1128 proc treeopendir {w dir} {
1129 global treediropen treeheight treeparent treecontents treeindex
1131 set ix $treeindex($dir)
1132 $w conf -state normal
1133 $w image configure a:$ix -image tri-dn
1134 $w mark set e:$ix s:$ix
1135 $w mark gravity e:$ix right
1138 set n [llength $treecontents($dir)]
1139 for {set x $dir} {$x ne {}} {set x $treeparent($x)} {
1142 incr treeheight($x) $n
1144 foreach e $treecontents($dir) {
1146 if {[string index $e end] eq "/"} {
1147 set iy $treeindex($de)
1148 $w mark set d:$iy e:$ix
1149 $w mark gravity d:$iy left
1150 $w insert e:$ix $str
1151 set treediropen($de) 0
1152 $w image create e:$ix -align center -image tri-rt -padx 1 \
1154 $w insert e:$ix $e [highlight_tag $de]
1155 $w mark set s:$iy e:$ix
1156 $w mark gravity s:$iy left
1157 set treeheight($de) 1
1159 $w insert e:$ix $str
1160 $w insert e:$ix $e [highlight_tag $de]
1163 $w mark gravity e:$ix left
1164 $w conf -state disabled
1165 set treediropen($dir) 1
1166 set top [lindex [split [$w index @0,0] .] 0]
1167 set ht [$w cget -height]
1168 set l [lindex [split [$w index s:$ix] .] 0]
1171 } elseif {$l + $n + 1 > $top + $ht} {
1172 set top [expr {$l + $n + 2 - $ht}]
1180 proc treeclick {w x y} {
1181 global treediropen cmitmode ctext cflist cflist_top
1183 if {$cmitmode ne "tree"} return
1184 if {![info exists cflist_top]} return
1185 set l [lindex [split [$w index "@$x,$y"] "."] 0]
1186 $cflist tag remove highlight $cflist_top.0 "$cflist_top.0 lineend"
1187 $cflist tag add highlight $l.0 "$l.0 lineend"
1193 set e [linetoelt $l]
1194 if {[string index $e end] ne "/"} {
1196 } elseif {$treediropen($e)} {
1203 proc setfilelist {id} {
1204 global treefilelist cflist
1206 treeview $cflist $treefilelist($id) 0
1209 image create bitmap tri-rt -background black -foreground blue -data {
1210 #define tri-rt_width 13
1211 #define tri-rt_height 13
1212 static unsigned char tri-rt_bits[] = {
1213 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x30, 0x00, 0x70, 0x00, 0xf0, 0x00,
1214 0xf0, 0x01, 0xf0, 0x00, 0x70, 0x00, 0x30, 0x00, 0x10, 0x00, 0x00, 0x00,
1217 #define tri-rt-mask_width 13
1218 #define tri-rt-mask_height 13
1219 static unsigned char tri-rt-mask_bits[] = {
1220 0x08, 0x00, 0x18, 0x00, 0x38, 0x00, 0x78, 0x00, 0xf8, 0x00, 0xf8, 0x01,
1221 0xf8, 0x03, 0xf8, 0x01, 0xf8, 0x00, 0x78, 0x00, 0x38, 0x00, 0x18, 0x00,
1224 image create bitmap tri-dn -background black -foreground blue -data {
1225 #define tri-dn_width 13
1226 #define tri-dn_height 13
1227 static unsigned char tri-dn_bits[] = {
1228 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x07, 0xf8, 0x03,
1229 0xf0, 0x01, 0xe0, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1232 #define tri-dn-mask_width 13
1233 #define tri-dn-mask_height 13
1234 static unsigned char tri-dn-mask_bits[] = {
1235 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x1f, 0xfe, 0x0f, 0xfc, 0x07,
1236 0xf8, 0x03, 0xf0, 0x01, 0xe0, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
1240 proc init_flist {first} {
1241 global cflist cflist_top selectedline difffilestart
1243 $cflist conf -state normal
1244 $cflist delete 0.0 end
1246 $cflist insert end $first
1248 $cflist tag add highlight 1.0 "1.0 lineend"
1250 catch {unset cflist_top}
1252 $cflist conf -state disabled
1253 set difffilestart {}
1256 proc highlight_tag {f} {
1257 global highlight_paths
1259 foreach p $highlight_paths {
1260 if {[string match $p $f]} {
1267 proc highlight_filelist {} {
1268 global cmitmode cflist
1270 $cflist conf -state normal
1271 if {$cmitmode ne "tree"} {
1272 set end [lindex [split [$cflist index end] .] 0]
1273 for {set l 2} {$l < $end} {incr l} {
1274 set line [$cflist get $l.0 "$l.0 lineend"]
1275 if {[highlight_tag $line] ne {}} {
1276 $cflist tag add bold $l.0 "$l.0 lineend"
1282 $cflist conf -state disabled
1285 proc unhighlight_filelist {} {
1288 $cflist conf -state normal
1289 $cflist tag remove bold 1.0 end
1290 $cflist conf -state disabled
1293 proc add_flist {fl} {
1296 $cflist conf -state normal
1298 $cflist insert end "\n"
1299 $cflist insert end $f [highlight_tag $f]
1301 $cflist conf -state disabled
1304 proc sel_flist {w x y} {
1305 global ctext difffilestart cflist cflist_top cmitmode
1307 if {$cmitmode eq "tree"} return
1308 if {![info exists cflist_top]} return
1309 set l [lindex [split [$w index "@$x,$y"] "."] 0]
1310 $cflist tag remove highlight $cflist_top.0 "$cflist_top.0 lineend"
1311 $cflist tag add highlight $l.0 "$l.0 lineend"
1316 catch {$ctext yview [lindex $difffilestart [expr {$l - 2}]]}
1320 # Functions for adding and removing shell-type quoting
1322 proc shellquote {str} {
1323 if {![string match "*\['\"\\ \t]*" $str]} {
1326 if {![string match "*\['\"\\]*" $str]} {
1329 if {![string match "*'*" $str]} {
1332 return "\"[string map {\" \\\" \\ \\\\} $str]\""
1335 proc shellarglist {l} {
1341 append str [shellquote $a]
1346 proc shelldequote {str} {
1351 if {![regexp -start $used -indices "\['\"\\\\ \t]" $str first]} {
1352 append ret [string range $str $used end]
1353 set used [string length $str]
1356 set first [lindex $first 0]
1357 set ch [string index $str $first]
1358 if {$first > $used} {
1359 append ret [string range $str $used [expr {$first - 1}]]
1362 if {$ch eq " " || $ch eq "\t"} break
1365 set first [string first "'" $str $used]
1367 error "unmatched single-quote"
1369 append ret [string range $str $used [expr {$first - 1}]]
1374 if {$used >= [string length $str]} {
1375 error "trailing backslash"
1377 append ret [string index $str $used]
1382 if {![regexp -start $used -indices "\[\"\\\\]" $str first]} {
1383 error "unmatched double-quote"
1385 set first [lindex $first 0]
1386 set ch [string index $str $first]
1387 if {$first > $used} {
1388 append ret [string range $str $used [expr {$first - 1}]]
1391 if {$ch eq "\""} break
1393 append ret [string index $str $used]
1397 return [list $used $ret]
1400 proc shellsplit {str} {
1403 set str [string trimleft $str]
1404 if {$str eq {}} break
1405 set dq [shelldequote $str]
1406 set n [lindex $dq 0]
1407 set word [lindex $dq 1]
1408 set str [string range $str $n end]
1414 # Code to implement multiple views
1416 proc newview {ishighlight} {
1417 global nextviewnum newviewname newviewperm uifont newishighlight
1418 global newviewargs revtreeargs
1420 set newishighlight $ishighlight
1422 if {[winfo exists $top]} {
1426 set newviewname($nextviewnum) "View $nextviewnum"
1427 set newviewperm($nextviewnum) 0
1428 set newviewargs($nextviewnum) [shellarglist $revtreeargs]
1429 vieweditor $top $nextviewnum "Gitk view definition"
1434 global viewname viewperm newviewname newviewperm
1435 global viewargs newviewargs
1437 set top .gitkvedit-$curview
1438 if {[winfo exists $top]} {
1442 set newviewname($curview) $viewname($curview)
1443 set newviewperm($curview) $viewperm($curview)
1444 set newviewargs($curview) [shellarglist $viewargs($curview)]
1445 vieweditor $top $curview "Gitk: edit view $viewname($curview)"
1448 proc vieweditor {top n title} {
1449 global newviewname newviewperm viewfiles
1453 wm title $top $title
1454 label $top.nl -text "Name" -font $uifont
1455 entry $top.name -width 20 -textvariable newviewname($n)
1456 grid $top.nl $top.name -sticky w -pady 5
1457 checkbutton $top.perm -text "Remember this view" -variable newviewperm($n)
1458 grid $top.perm - -pady 5 -sticky w
1459 message $top.al -aspect 1000 -font $uifont \
1460 -text "Commits to include (arguments to git rev-list):"
1461 grid $top.al - -sticky w -pady 5
1462 entry $top.args -width 50 -textvariable newviewargs($n) \
1464 grid $top.args - -sticky ew -padx 5
1465 message $top.l -aspect 1000 -font $uifont \
1466 -text "Enter files and directories to include, one per line:"
1467 grid $top.l - -sticky w
1468 text $top.t -width 40 -height 10 -background white
1469 if {[info exists viewfiles($n)]} {
1470 foreach f $viewfiles($n) {
1471 $top.t insert end $f
1472 $top.t insert end "\n"
1474 $top.t delete {end - 1c} end
1475 $top.t mark set insert 0.0
1477 grid $top.t - -sticky ew -padx 5
1479 button $top.buts.ok -text "OK" -command [list newviewok $top $n]
1480 button $top.buts.can -text "Cancel" -command [list destroy $top]
1481 grid $top.buts.ok $top.buts.can
1482 grid columnconfigure $top.buts 0 -weight 1 -uniform a
1483 grid columnconfigure $top.buts 1 -weight 1 -uniform a
1484 grid $top.buts - -pady 10 -sticky ew
1488 proc doviewmenu {m first cmd op argv} {
1489 set nmenu [$m index end]
1490 for {set i $first} {$i <= $nmenu} {incr i} {
1491 if {[$m entrycget $i -command] eq $cmd} {
1492 eval $m $op $i $argv
1498 proc allviewmenus {n op args} {
1501 doviewmenu .bar.view 5 [list showview $n] $op $args
1502 doviewmenu $viewhlmenu 1 [list addvhighlight $n] $op $args
1505 proc newviewok {top n} {
1506 global nextviewnum newviewperm newviewname newishighlight
1507 global viewname viewfiles viewperm selectedview curview
1508 global viewargs newviewargs viewhlmenu
1511 set newargs [shellsplit $newviewargs($n)]
1513 error_popup "Error in commit selection arguments: $err"
1519 foreach f [split [$top.t get 0.0 end] "\n"] {
1520 set ft [string trim $f]
1525 if {![info exists viewfiles($n)]} {
1526 # creating a new view
1528 set viewname($n) $newviewname($n)
1529 set viewperm($n) $newviewperm($n)
1530 set viewfiles($n) $files
1531 set viewargs($n) $newargs
1533 if {!$newishighlight} {
1534 after idle showview $n
1536 after idle addvhighlight $n
1539 # editing an existing view
1540 set viewperm($n) $newviewperm($n)
1541 if {$newviewname($n) ne $viewname($n)} {
1542 set viewname($n) $newviewname($n)
1543 doviewmenu .bar.view 5 [list showview $n] \
1544 entryconf [list -label $viewname($n)]
1545 doviewmenu $viewhlmenu 1 [list addvhighlight $n] \
1546 entryconf [list -label $viewname($n) -value $viewname($n)]
1548 if {$files ne $viewfiles($n) || $newargs ne $viewargs($n)} {
1549 set viewfiles($n) $files
1550 set viewargs($n) $newargs
1551 if {$curview == $n} {
1552 after idle updatecommits
1556 catch {destroy $top}
1560 global curview viewdata viewperm hlview selectedhlview
1562 if {$curview == 0} return
1563 if {[info exists hlview] && $hlview == $curview} {
1564 set selectedhlview None
1567 allviewmenus $curview delete
1568 set viewdata($curview) {}
1569 set viewperm($curview) 0
1573 proc addviewmenu {n} {
1574 global viewname viewhlmenu
1576 .bar.view add radiobutton -label $viewname($n) \
1577 -command [list showview $n] -variable selectedview -value $n
1578 $viewhlmenu add radiobutton -label $viewname($n) \
1579 -command [list addvhighlight $n] -variable selectedhlview
1582 proc flatten {var} {
1586 foreach i [array names $var] {
1587 lappend ret $i [set $var\($i\)]
1592 proc unflatten {var l} {
1602 global curview viewdata viewfiles
1603 global displayorder parentlist childlist rowidlist rowoffsets
1604 global colormap rowtextx commitrow nextcolor canvxmax
1605 global numcommits rowrangelist commitlisted idrowranges
1606 global selectedline currentid canv canvy0
1607 global matchinglines treediffs
1608 global pending_select phase
1609 global commitidx rowlaidout rowoptim linesegends
1610 global commfd nextupdate
1612 global vparentlist vchildlist vdisporder vcmitlisted
1613 global hlview selectedhlview
1615 if {$n == $curview} return
1617 if {[info exists selectedline]} {
1618 set selid $currentid
1619 set y [yc $selectedline]
1620 set ymax [lindex [$canv cget -scrollregion] 3]
1621 set span [$canv yview]
1622 set ytop [expr {[lindex $span 0] * $ymax}]
1623 set ybot [expr {[lindex $span 1] * $ymax}]
1624 if {$ytop < $y && $y < $ybot} {
1625 set yscreen [expr {$y - $ytop}]
1627 set yscreen [expr {($ybot - $ytop) / 2}]
1633 if {$curview >= 0} {
1634 set vparentlist($curview) $parentlist
1635 set vchildlist($curview) $childlist
1636 set vdisporder($curview) $displayorder
1637 set vcmitlisted($curview) $commitlisted
1639 set viewdata($curview) \
1640 [list $phase $rowidlist $rowoffsets $rowrangelist \
1641 [flatten idrowranges] [flatten idinlist] \
1642 $rowlaidout $rowoptim $numcommits $linesegends]
1643 } elseif {![info exists viewdata($curview)]
1644 || [lindex $viewdata($curview) 0] ne {}} {
1645 set viewdata($curview) \
1646 [list {} $rowidlist $rowoffsets $rowrangelist]
1649 catch {unset matchinglines}
1650 catch {unset treediffs}
1652 if {[info exists hlview] && $hlview == $n} {
1654 set selectedhlview None
1659 .bar.view entryconf Edit* -state [expr {$n == 0? "disabled": "normal"}]
1660 .bar.view entryconf Delete* -state [expr {$n == 0? "disabled": "normal"}]
1662 if {![info exists viewdata($n)]} {
1663 set pending_select $selid
1669 set phase [lindex $v 0]
1670 set displayorder $vdisporder($n)
1671 set parentlist $vparentlist($n)
1672 set childlist $vchildlist($n)
1673 set commitlisted $vcmitlisted($n)
1674 set rowidlist [lindex $v 1]
1675 set rowoffsets [lindex $v 2]
1676 set rowrangelist [lindex $v 3]
1678 set numcommits [llength $displayorder]
1679 catch {unset idrowranges}
1681 unflatten idrowranges [lindex $v 4]
1682 unflatten idinlist [lindex $v 5]
1683 set rowlaidout [lindex $v 6]
1684 set rowoptim [lindex $v 7]
1685 set numcommits [lindex $v 8]
1686 set linesegends [lindex $v 9]
1689 catch {unset colormap}
1690 catch {unset rowtextx}
1692 set canvxmax [$canv cget -width]
1698 if {$selid ne {} && [info exists commitrow($n,$selid)]} {
1699 set row $commitrow($n,$selid)
1700 # try to get the selected row in the same position on the screen
1701 set ymax [lindex [$canv cget -scrollregion] 3]
1702 set ytop [expr {[yc $row] - $yscreen}]
1706 set yf [expr {$ytop * 1.0 / $ymax}]
1708 allcanvs yview moveto $yf
1712 if {$phase eq "getcommits"} {
1713 show_status "Reading commits..."
1715 if {[info exists commfd($n)]} {
1720 } elseif {$numcommits == 0} {
1721 show_status "No commits selected"
1725 # Stuff relating to the highlighting facility
1727 proc ishighlighted {row} {
1728 global vhighlights fhighlights nhighlights rhighlights
1730 if {[info exists nhighlights($row)] && $nhighlights($row) > 0} {
1731 return $nhighlights($row)
1733 if {[info exists vhighlights($row)] && $vhighlights($row) > 0} {
1734 return $vhighlights($row)
1736 if {[info exists fhighlights($row)] && $fhighlights($row) > 0} {
1737 return $fhighlights($row)
1739 if {[info exists rhighlights($row)] && $rhighlights($row) > 0} {
1740 return $rhighlights($row)
1745 proc bolden {row font} {
1746 global canv linehtag selectedline boldrows
1748 lappend boldrows $row
1749 $canv itemconf $linehtag($row) -font $font
1750 if {[info exists selectedline] && $row == $selectedline} {
1752 set t [eval $canv create rect [$canv bbox $linehtag($row)] \
1753 -outline {{}} -tags secsel \
1754 -fill [$canv cget -selectbackground]]
1759 proc bolden_name {row font} {
1760 global canv2 linentag selectedline boldnamerows
1762 lappend boldnamerows $row
1763 $canv2 itemconf $linentag($row) -font $font
1764 if {[info exists selectedline] && $row == $selectedline} {
1765 $canv2 delete secsel
1766 set t [eval $canv2 create rect [$canv2 bbox $linentag($row)] \
1767 -outline {{}} -tags secsel \
1768 -fill [$canv2 cget -selectbackground]]
1774 global mainfont boldrows
1777 foreach row $boldrows {
1778 if {![ishighlighted $row]} {
1779 bolden $row $mainfont
1781 lappend stillbold $row
1784 set boldrows $stillbold
1787 proc addvhighlight {n} {
1788 global hlview curview viewdata vhl_done vhighlights commitidx
1790 if {[info exists hlview]} {
1794 if {$n != $curview && ![info exists viewdata($n)]} {
1795 set viewdata($n) [list getcommits {{}} {{}} {} {} {} 0 0 0 {}]
1796 set vparentlist($n) {}
1797 set vchildlist($n) {}
1798 set vdisporder($n) {}
1799 set vcmitlisted($n) {}
1802 set vhl_done $commitidx($hlview)
1803 if {$vhl_done > 0} {
1808 proc delvhighlight {} {
1809 global hlview vhighlights
1811 if {![info exists hlview]} return
1813 catch {unset vhighlights}
1817 proc vhighlightmore {} {
1818 global hlview vhl_done commitidx vhighlights
1819 global displayorder vdisporder curview mainfont
1821 set font [concat $mainfont bold]
1822 set max $commitidx($hlview)
1823 if {$hlview == $curview} {
1824 set disp $displayorder
1826 set disp $vdisporder($hlview)
1828 set vr [visiblerows]
1829 set r0 [lindex $vr 0]
1830 set r1 [lindex $vr 1]
1831 for {set i $vhl_done} {$i < $max} {incr i} {
1832 set id [lindex $disp $i]
1833 if {[info exists commitrow($curview,$id)]} {
1834 set row $commitrow($curview,$id)
1835 if {$r0 <= $row && $row <= $r1} {
1836 if {![highlighted $row]} {
1839 set vhighlights($row) 1
1846 proc askvhighlight {row id} {
1847 global hlview vhighlights commitrow iddrawn mainfont
1849 if {[info exists commitrow($hlview,$id)]} {
1850 if {[info exists iddrawn($id)] && ![ishighlighted $row]} {
1851 bolden $row [concat $mainfont bold]
1853 set vhighlights($row) 1
1855 set vhighlights($row) 0
1859 proc hfiles_change {name ix op} {
1860 global highlight_files filehighlight fhighlights fh_serial
1861 global mainfont highlight_paths
1863 if {[info exists filehighlight]} {
1864 # delete previous highlights
1865 catch {close $filehighlight}
1867 catch {unset fhighlights}
1869 unhighlight_filelist
1871 set highlight_paths {}
1872 after cancel do_file_hl $fh_serial
1874 if {$highlight_files ne {}} {
1875 after 300 do_file_hl $fh_serial
1879 proc makepatterns {l} {
1882 set ee [string map {"*" "\\*" "?" "\\?" "\[" "\\\[" "\\" "\\\\"} $e]
1883 if {[string index $ee end] eq "/"} {
1893 proc do_file_hl {serial} {
1894 global highlight_files filehighlight highlight_paths gdttype fhl_list
1896 if {$gdttype eq "touching paths:"} {
1897 if {[catch {set paths [shellsplit $highlight_files]}]} return
1898 set highlight_paths [makepatterns $paths]
1900 set gdtargs [concat -- $paths]
1902 set gdtargs [list "-S$highlight_files"]
1904 set cmd [concat | git-diff-tree -r -s --stdin $gdtargs]
1905 set filehighlight [open $cmd r+]
1906 fconfigure $filehighlight -blocking 0
1907 fileevent $filehighlight readable readfhighlight
1913 proc flushhighlights {} {
1914 global filehighlight fhl_list
1916 if {[info exists filehighlight]} {
1918 puts $filehighlight ""
1919 flush $filehighlight
1923 proc askfilehighlight {row id} {
1924 global filehighlight fhighlights fhl_list
1926 lappend fhl_list $id
1927 set fhighlights($row) -1
1928 puts $filehighlight $id
1931 proc readfhighlight {} {
1932 global filehighlight fhighlights commitrow curview mainfont iddrawn
1935 while {[gets $filehighlight line] >= 0} {
1936 set line [string trim $line]
1937 set i [lsearch -exact $fhl_list $line]
1938 if {$i < 0} continue
1939 for {set j 0} {$j < $i} {incr j} {
1940 set id [lindex $fhl_list $j]
1941 if {[info exists commitrow($curview,$id)]} {
1942 set fhighlights($commitrow($curview,$id)) 0
1945 set fhl_list [lrange $fhl_list [expr {$i+1}] end]
1946 if {$line eq {}} continue
1947 if {![info exists commitrow($curview,$line)]} continue
1948 set row $commitrow($curview,$line)
1949 if {[info exists iddrawn($line)] && ![ishighlighted $row]} {
1950 bolden $row [concat $mainfont bold]
1952 set fhighlights($row) 1
1954 if {[eof $filehighlight]} {
1956 puts "oops, git-diff-tree died"
1957 catch {close $filehighlight}
1963 proc find_change {name ix op} {
1964 global nhighlights mainfont boldnamerows
1965 global findstring findpattern findtype
1967 # delete previous highlights, if any
1968 foreach row $boldnamerows {
1969 bolden_name $row $mainfont
1972 catch {unset nhighlights}
1974 if {$findtype ne "Regexp"} {
1975 set e [string map {"*" "\\*" "?" "\\?" "\[" "\\\[" "\\" "\\\\"} \
1977 set findpattern "*$e*"
1982 proc askfindhighlight {row id} {
1983 global nhighlights commitinfo iddrawn mainfont
1984 global findstring findtype findloc findpattern
1986 if {![info exists commitinfo($id)]} {
1989 set info $commitinfo($id)
1991 set fldtypes {Headline Author Date Committer CDate Comments}
1992 foreach f $info ty $fldtypes {
1993 if {$findloc ne "All fields" && $findloc ne $ty} {
1996 if {$findtype eq "Regexp"} {
1997 set doesmatch [regexp $findstring $f]
1998 } elseif {$findtype eq "IgnCase"} {
1999 set doesmatch [string match -nocase $findpattern $f]
2001 set doesmatch [string match $findpattern $f]
2004 if {$ty eq "Author"} {
2011 if {[info exists iddrawn($id)]} {
2012 if {$isbold && ![ishighlighted $row]} {
2013 bolden $row [concat $mainfont bold]
2016 bolden_name $row [concat $mainfont bold]
2019 set nhighlights($row) $isbold
2022 proc vrel_change {name ix op} {
2023 global highlight_related
2026 if {$highlight_related ne "None"} {
2027 after idle drawvisible
2031 # prepare for testing whether commits are descendents or ancestors of a
2032 proc rhighlight_sel {a} {
2033 global descendent desc_todo ancestor anc_todo
2034 global highlight_related rhighlights
2036 catch {unset descendent}
2037 set desc_todo [list $a]
2038 catch {unset ancestor}
2039 set anc_todo [list $a]
2040 if {$highlight_related ne "None"} {
2042 after idle drawvisible
2046 proc rhighlight_none {} {
2049 catch {unset rhighlights}
2053 proc is_descendent {a} {
2054 global curview children commitrow descendent desc_todo
2057 set la $commitrow($v,$a)
2061 for {set i 0} {$i < [llength $todo]} {incr i} {
2062 set do [lindex $todo $i]
2063 if {$commitrow($v,$do) < $la} {
2064 lappend leftover $do
2067 foreach nk $children($v,$do) {
2068 if {![info exists descendent($nk)]} {
2069 set descendent($nk) 1
2077 set desc_todo [concat $leftover [lrange $todo [expr {$i+1}] end]]
2081 set descendent($a) 0
2082 set desc_todo $leftover
2085 proc is_ancestor {a} {
2086 global curview parentlist commitrow ancestor anc_todo
2089 set la $commitrow($v,$a)
2093 for {set i 0} {$i < [llength $todo]} {incr i} {
2094 set do [lindex $todo $i]
2095 if {![info exists commitrow($v,$do)] || $commitrow($v,$do) > $la} {
2096 lappend leftover $do
2099 foreach np [lindex $parentlist $commitrow($v,$do)] {
2100 if {![info exists ancestor($np)]} {
2109 set anc_todo [concat $leftover [lrange $todo [expr {$i+1}] end]]
2114 set anc_todo $leftover
2117 proc askrelhighlight {row id} {
2118 global descendent highlight_related iddrawn mainfont rhighlights
2119 global selectedline ancestor
2121 if {![info exists selectedline]} return
2123 if {$highlight_related eq "Descendent" ||
2124 $highlight_related eq "Not descendent"} {
2125 if {![info exists descendent($id)]} {
2128 if {$descendent($id) == ($highlight_related eq "Descendent")} {
2131 } elseif {$highlight_related eq "Ancestor" ||
2132 $highlight_related eq "Not ancestor"} {
2133 if {![info exists ancestor($id)]} {
2136 if {$ancestor($id) == ($highlight_related eq "Ancestor")} {
2140 if {[info exists iddrawn($id)]} {
2141 if {$isbold && ![ishighlighted $row]} {
2142 bolden $row [concat $mainfont bold]
2145 set rhighlights($row) $isbold
2148 proc next_hlcont {} {
2149 global fhl_row fhl_dirn displayorder numcommits
2150 global vhighlights fhighlights nhighlights rhighlights
2151 global hlview filehighlight findstring highlight_related
2153 if {![info exists fhl_dirn] || $fhl_dirn == 0} return
2156 if {$row < 0 || $row >= $numcommits} {
2161 set id [lindex $displayorder $row]
2162 if {[info exists hlview]} {
2163 if {![info exists vhighlights($row)]} {
2164 askvhighlight $row $id
2166 if {$vhighlights($row) > 0} break
2168 if {$findstring ne {}} {
2169 if {![info exists nhighlights($row)]} {
2170 askfindhighlight $row $id
2172 if {$nhighlights($row) > 0} break
2174 if {$highlight_related ne "None"} {
2175 if {![info exists rhighlights($row)]} {
2176 askrelhighlight $row $id
2178 if {$rhighlights($row) > 0} break
2180 if {[info exists filehighlight]} {
2181 if {![info exists fhighlights($row)]} {
2182 # ask for a few more while we're at it...
2184 for {set n 0} {$n < 100} {incr n} {
2185 if {![info exists fhighlights($r)]} {
2186 askfilehighlight $r [lindex $displayorder $r]
2189 if {$r < 0 || $r >= $numcommits} break
2193 if {$fhighlights($row) < 0} {
2197 if {$fhighlights($row) > 0} break
2205 proc next_highlight {dirn} {
2206 global selectedline fhl_row fhl_dirn
2207 global hlview filehighlight findstring highlight_related
2209 if {![info exists selectedline]} return
2210 if {!([info exists hlview] || $findstring ne {} ||
2211 $highlight_related ne "None" || [info exists filehighlight])} return
2212 set fhl_row [expr {$selectedline + $dirn}]
2217 proc cancel_next_highlight {} {
2223 # Graph layout functions
2225 proc shortids {ids} {
2228 if {[llength $id] > 1} {
2229 lappend res [shortids $id]
2230 } elseif {[regexp {^[0-9a-f]{40}$} $id]} {
2231 lappend res [string range $id 0 7]
2239 proc incrange {l x o} {
2242 set e [lindex $l $x]
2244 lset l $x [expr {$e + $o}]
2253 for {} {$n > 0} {incr n -1} {
2259 proc usedinrange {id l1 l2} {
2260 global children commitrow childlist curview
2262 if {[info exists commitrow($curview,$id)]} {
2263 set r $commitrow($curview,$id)
2264 if {$l1 <= $r && $r <= $l2} {
2265 return [expr {$r - $l1 + 1}]
2267 set kids [lindex $childlist $r]
2269 set kids $children($curview,$id)
2272 set r $commitrow($curview,$c)
2273 if {$l1 <= $r && $r <= $l2} {
2274 return [expr {$r - $l1 + 1}]
2280 proc sanity {row {full 0}} {
2281 global rowidlist rowoffsets
2284 set ids [lindex $rowidlist $row]
2287 if {$id eq {}} continue
2288 if {$col < [llength $ids] - 1 &&
2289 [lsearch -exact -start [expr {$col+1}] $ids $id] >= 0} {
2290 puts "oops: [shortids $id] repeated in row $row col $col: {[shortids [lindex $rowidlist $row]]}"
2292 set o [lindex $rowoffsets $row $col]
2298 if {[lindex $rowidlist $y $x] != $id} {
2299 puts "oops: rowoffsets wrong at row [expr {$y+1}] col [expr {$x-$o}]"
2300 puts " id=[shortids $id] check started at row $row"
2301 for {set i $row} {$i >= $y} {incr i -1} {
2302 puts " row $i ids={[shortids [lindex $rowidlist $i]]} offs={[lindex $rowoffsets $i]}"
2307 set o [lindex $rowoffsets $y $x]
2312 proc makeuparrow {oid x y z} {
2313 global rowidlist rowoffsets uparrowlen idrowranges
2315 for {set i 1} {$i < $uparrowlen && $y > 1} {incr i} {
2318 set off0 [lindex $rowoffsets $y]
2319 for {set x0 $x} {1} {incr x0} {
2320 if {$x0 >= [llength $off0]} {
2321 set x0 [llength [lindex $rowoffsets [expr {$y-1}]]]
2324 set z [lindex $off0 $x0]
2330 set z [expr {$x0 - $x}]
2331 lset rowidlist $y [linsert [lindex $rowidlist $y] $x $oid]
2332 lset rowoffsets $y [linsert [lindex $rowoffsets $y] $x $z]
2334 set tmp [lreplace [lindex $rowoffsets $y] $x $x {}]
2335 lset rowoffsets $y [incrange $tmp [expr {$x+1}] -1]
2336 lappend idrowranges($oid) $y
2339 proc initlayout {} {
2340 global rowidlist rowoffsets displayorder commitlisted
2341 global rowlaidout rowoptim
2342 global idinlist rowchk rowrangelist idrowranges
2343 global numcommits canvxmax canv
2345 global parentlist childlist children
2346 global colormap rowtextx
2358 catch {unset idinlist}
2359 catch {unset rowchk}
2362 set canvxmax [$canv cget -width]
2363 catch {unset colormap}
2364 catch {unset rowtextx}
2365 catch {unset idrowranges}
2369 proc setcanvscroll {} {
2370 global canv canv2 canv3 numcommits linespc canvxmax canvy0
2372 set ymax [expr {$canvy0 + ($numcommits - 0.5) * $linespc + 2}]
2373 $canv conf -scrollregion [list 0 0 $canvxmax $ymax]
2374 $canv2 conf -scrollregion [list 0 0 0 $ymax]
2375 $canv3 conf -scrollregion [list 0 0 0 $ymax]
2378 proc visiblerows {} {
2379 global canv numcommits linespc
2381 set ymax [lindex [$canv cget -scrollregion] 3]
2382 if {$ymax eq {} || $ymax == 0} return
2384 set y0 [expr {int([lindex $f 0] * $ymax)}]
2385 set r0 [expr {int(($y0 - 3) / $linespc) - 1}]
2389 set y1 [expr {int([lindex $f 1] * $ymax)}]
2390 set r1 [expr {int(($y1 - 3) / $linespc) + 1}]
2391 if {$r1 >= $numcommits} {
2392 set r1 [expr {$numcommits - 1}]
2394 return [list $r0 $r1]
2397 proc layoutmore {tmax} {
2398 global rowlaidout rowoptim commitidx numcommits optim_delay
2399 global uparrowlen curview
2402 if {$rowoptim - $optim_delay > $numcommits} {
2403 showstuff [expr {$rowoptim - $optim_delay}]
2404 } elseif {$rowlaidout - $uparrowlen - 1 > $rowoptim} {
2405 set nr [expr {$rowlaidout - $uparrowlen - 1 - $rowoptim}]
2409 optimize_rows $rowoptim 0 [expr {$rowoptim + $nr}]
2411 } elseif {$commitidx($curview) > $rowlaidout} {
2412 set nr [expr {$commitidx($curview) - $rowlaidout}]
2413 # may need to increase this threshold if uparrowlen or
2414 # mingaplen are increased...
2419 set rowlaidout [layoutrows $row [expr {$row + $nr}] 0]
2420 if {$rowlaidout == $row} {
2426 if {$tmax ne {} && [clock clicks -milliseconds] >= $tmax} {
2432 proc showstuff {canshow} {
2433 global numcommits commitrow pending_select selectedline
2434 global linesegends idrowranges idrangedrawn curview
2436 if {$numcommits == 0} {
2438 set phase "incrdraw"
2442 set numcommits $canshow
2444 set rows [visiblerows]
2445 set r0 [lindex $rows 0]
2446 set r1 [lindex $rows 1]
2448 for {set r $row} {$r < $canshow} {incr r} {
2449 foreach id [lindex $linesegends [expr {$r+1}]] {
2451 foreach {s e} [rowranges $id] {
2453 if {$e ne {} && $e < $numcommits && $s <= $r1 && $e >= $r0
2454 && ![info exists idrangedrawn($id,$i)]} {
2456 set idrangedrawn($id,$i) 1
2461 if {$canshow > $r1} {
2464 while {$row < $canshow} {
2468 if {[info exists pending_select] &&
2469 [info exists commitrow($curview,$pending_select)] &&
2470 $commitrow($curview,$pending_select) < $numcommits} {
2471 selectline $commitrow($curview,$pending_select) 1
2473 if {![info exists selectedline] && ![info exists pending_select]} {
2478 proc layoutrows {row endrow last} {
2479 global rowidlist rowoffsets displayorder
2480 global uparrowlen downarrowlen maxwidth mingaplen
2481 global childlist parentlist
2482 global idrowranges linesegends
2483 global commitidx curview
2484 global idinlist rowchk rowrangelist
2486 set idlist [lindex $rowidlist $row]
2487 set offs [lindex $rowoffsets $row]
2488 while {$row < $endrow} {
2489 set id [lindex $displayorder $row]
2492 foreach p [lindex $parentlist $row] {
2493 if {![info exists idinlist($p)]} {
2495 } elseif {!$idinlist($p)} {
2500 set nev [expr {[llength $idlist] + [llength $newolds]
2501 + [llength $oldolds] - $maxwidth + 1}]
2504 $row + $uparrowlen + $mingaplen >= $commitidx($curview)} break
2505 for {set x [llength $idlist]} {[incr x -1] >= 0} {} {
2506 set i [lindex $idlist $x]
2507 if {![info exists rowchk($i)] || $row >= $rowchk($i)} {
2508 set r [usedinrange $i [expr {$row - $downarrowlen}] \
2509 [expr {$row + $uparrowlen + $mingaplen}]]
2511 set idlist [lreplace $idlist $x $x]
2512 set offs [lreplace $offs $x $x]
2513 set offs [incrange $offs $x 1]
2515 set rm1 [expr {$row - 1}]
2517 lappend idrowranges($i) $rm1
2518 if {[incr nev -1] <= 0} break
2521 set rowchk($id) [expr {$row + $r}]
2524 lset rowidlist $row $idlist
2525 lset rowoffsets $row $offs
2527 lappend linesegends $lse
2528 set col [lsearch -exact $idlist $id]
2530 set col [llength $idlist]
2532 lset rowidlist $row $idlist
2534 if {[lindex $childlist $row] ne {}} {
2535 set z [expr {[llength [lindex $rowidlist [expr {$row-1}]]] - $col}]
2539 lset rowoffsets $row $offs
2541 makeuparrow $id $col $row $z
2547 if {[info exists idrowranges($id)]} {
2548 set ranges $idrowranges($id)
2550 unset idrowranges($id)
2552 lappend rowrangelist $ranges
2554 set offs [ntimes [llength $idlist] 0]
2555 set l [llength $newolds]
2556 set idlist [eval lreplace \$idlist $col $col $newolds]
2559 set offs [lrange $offs 0 [expr {$col - 1}]]
2560 foreach x $newolds {
2565 set tmp [expr {[llength $idlist] - [llength $offs]}]
2567 set offs [concat $offs [ntimes $tmp $o]]
2572 foreach i $newolds {
2574 set idrowranges($i) $row
2577 foreach oid $oldolds {
2578 set idinlist($oid) 1
2579 set idlist [linsert $idlist $col $oid]
2580 set offs [linsert $offs $col $o]
2581 makeuparrow $oid $col $row $o
2584 lappend rowidlist $idlist
2585 lappend rowoffsets $offs
2590 proc addextraid {id row} {
2591 global displayorder commitrow commitinfo
2592 global commitidx commitlisted
2593 global parentlist childlist children curview
2595 incr commitidx($curview)
2596 lappend displayorder $id
2597 lappend commitlisted 0
2598 lappend parentlist {}
2599 set commitrow($curview,$id) $row
2601 if {![info exists commitinfo($id)]} {
2602 set commitinfo($id) {"No commit information available"}
2604 if {![info exists children($curview,$id)]} {
2605 set children($curview,$id) {}
2607 lappend childlist $children($curview,$id)
2610 proc layouttail {} {
2611 global rowidlist rowoffsets idinlist commitidx curview
2612 global idrowranges rowrangelist
2614 set row $commitidx($curview)
2615 set idlist [lindex $rowidlist $row]
2616 while {$idlist ne {}} {
2617 set col [expr {[llength $idlist] - 1}]
2618 set id [lindex $idlist $col]
2621 lappend idrowranges($id) $row
2622 lappend rowrangelist $idrowranges($id)
2623 unset idrowranges($id)
2625 set offs [ntimes $col 0]
2626 set idlist [lreplace $idlist $col $col]
2627 lappend rowidlist $idlist
2628 lappend rowoffsets $offs
2631 foreach id [array names idinlist] {
2633 lset rowidlist $row [list $id]
2634 lset rowoffsets $row 0
2635 makeuparrow $id 0 $row 0
2636 lappend idrowranges($id) $row
2637 lappend rowrangelist $idrowranges($id)
2638 unset idrowranges($id)
2640 lappend rowidlist {}
2641 lappend rowoffsets {}
2645 proc insert_pad {row col npad} {
2646 global rowidlist rowoffsets
2648 set pad [ntimes $npad {}]
2649 lset rowidlist $row [eval linsert [list [lindex $rowidlist $row]] $col $pad]
2650 set tmp [eval linsert [list [lindex $rowoffsets $row]] $col $pad]
2651 lset rowoffsets $row [incrange $tmp [expr {$col + $npad}] [expr {-$npad}]]
2654 proc optimize_rows {row col endrow} {
2655 global rowidlist rowoffsets idrowranges displayorder
2657 for {} {$row < $endrow} {incr row} {
2658 set idlist [lindex $rowidlist $row]
2659 set offs [lindex $rowoffsets $row]
2661 for {} {$col < [llength $offs]} {incr col} {
2662 if {[lindex $idlist $col] eq {}} {
2666 set z [lindex $offs $col]
2667 if {$z eq {}} continue
2669 set x0 [expr {$col + $z}]
2670 set y0 [expr {$row - 1}]
2671 set z0 [lindex $rowoffsets $y0 $x0]
2673 set id [lindex $idlist $col]
2674 set ranges [rowranges $id]
2675 if {$ranges ne {} && $y0 > [lindex $ranges 0]} {
2679 if {$z < -1 || ($z < 0 && $isarrow)} {
2680 set npad [expr {-1 - $z + $isarrow}]
2681 set offs [incrange $offs $col $npad]
2682 insert_pad $y0 $x0 $npad
2684 optimize_rows $y0 $x0 $row
2686 set z [lindex $offs $col]
2687 set x0 [expr {$col + $z}]
2688 set z0 [lindex $rowoffsets $y0 $x0]
2689 } elseif {$z > 1 || ($z > 0 && $isarrow)} {
2690 set npad [expr {$z - 1 + $isarrow}]
2691 set y1 [expr {$row + 1}]
2692 set offs2 [lindex $rowoffsets $y1]
2696 if {$z eq {} || $x1 + $z < $col} continue
2697 if {$x1 + $z > $col} {
2700 lset rowoffsets $y1 [incrange $offs2 $x1 $npad]
2703 set pad [ntimes $npad {}]
2704 set idlist [eval linsert \$idlist $col $pad]
2705 set tmp [eval linsert \$offs $col $pad]
2707 set offs [incrange $tmp $col [expr {-$npad}]]
2708 set z [lindex $offs $col]
2711 if {$z0 eq {} && !$isarrow} {
2712 # this line links to its first child on row $row-2
2713 set rm2 [expr {$row - 2}]
2714 set id [lindex $displayorder $rm2]
2715 set xc [lsearch -exact [lindex $rowidlist $rm2] $id]
2717 set z0 [expr {$xc - $x0}]
2720 if {$z0 ne {} && $z < 0 && $z0 > 0} {
2721 insert_pad $y0 $x0 1
2722 set offs [incrange $offs $col 1]
2723 optimize_rows $y0 [expr {$x0 + 1}] $row
2728 for {set col [llength $idlist]} {[incr col -1] >= 0} {} {
2729 set o [lindex $offs $col]
2731 # check if this is the link to the first child
2732 set id [lindex $idlist $col]
2733 set ranges [rowranges $id]
2734 if {$ranges ne {} && $row == [lindex $ranges 0]} {
2735 # it is, work out offset to child
2736 set y0 [expr {$row - 1}]
2737 set id [lindex $displayorder $y0]
2738 set x0 [lsearch -exact [lindex $rowidlist $y0] $id]
2740 set o [expr {$x0 - $col}]
2744 if {$o eq {} || $o <= 0} break
2746 if {$o ne {} && [incr col] < [llength $idlist]} {
2747 set y1 [expr {$row + 1}]
2748 set offs2 [lindex $rowoffsets $y1]
2752 if {$z eq {} || $x1 + $z < $col} continue
2753 lset rowoffsets $y1 [incrange $offs2 $x1 1]
2756 set idlist [linsert $idlist $col {}]
2757 set tmp [linsert $offs $col {}]
2759 set offs [incrange $tmp $col -1]
2762 lset rowidlist $row $idlist
2763 lset rowoffsets $row $offs
2769 global canvx0 linespc
2770 return [expr {$canvx0 + $col * $linespc}]
2774 global canvy0 linespc
2775 return [expr {$canvy0 + $row * $linespc}]
2778 proc linewidth {id} {
2779 global thickerline lthickness
2782 if {[info exists thickerline] && $id eq $thickerline} {
2783 set wid [expr {2 * $lthickness}]
2788 proc rowranges {id} {
2789 global phase idrowranges commitrow rowlaidout rowrangelist curview
2793 ([info exists commitrow($curview,$id)]
2794 && $commitrow($curview,$id) < $rowlaidout)} {
2795 set ranges [lindex $rowrangelist $commitrow($curview,$id)]
2796 } elseif {[info exists idrowranges($id)]} {
2797 set ranges $idrowranges($id)
2802 proc drawlineseg {id i} {
2803 global rowoffsets rowidlist
2805 global canv colormap linespc
2806 global numcommits commitrow curview
2808 set ranges [rowranges $id]
2810 if {[info exists commitrow($curview,$id)]
2811 && $commitrow($curview,$id) < $numcommits} {
2812 set downarrow [expr {$i < [llength $ranges] / 2 - 1}]
2816 set startrow [lindex $ranges [expr {2 * $i}]]
2817 set row [lindex $ranges [expr {2 * $i + 1}]]
2818 if {$startrow == $row} return
2821 set col [lsearch -exact [lindex $rowidlist $row] $id]
2823 puts "oops: drawline: id $id not on row $row"
2829 set o [lindex $rowoffsets $row $col]
2832 # changing direction
2833 set x [xc $row $col]
2835 lappend coords $x $y
2841 set x [xc $row $col]
2843 lappend coords $x $y
2845 # draw the link to the first child as part of this line
2847 set child [lindex $displayorder $row]
2848 set ccol [lsearch -exact [lindex $rowidlist $row] $child]
2850 set x [xc $row $ccol]
2852 if {$ccol < $col - 1} {
2853 lappend coords [xc $row [expr {$col - 1}]] [yc $row]
2854 } elseif {$ccol > $col + 1} {
2855 lappend coords [xc $row [expr {$col + 1}]] [yc $row]
2857 lappend coords $x $y
2860 if {[llength $coords] < 4} return
2862 # This line has an arrow at the lower end: check if the arrow is
2863 # on a diagonal segment, and if so, work around the Tk 8.4
2864 # refusal to draw arrows on diagonal lines.
2865 set x0 [lindex $coords 0]
2866 set x1 [lindex $coords 2]
2868 set y0 [lindex $coords 1]
2869 set y1 [lindex $coords 3]
2870 if {$y0 - $y1 <= 2 * $linespc && $x1 == [lindex $coords 4]} {
2871 # we have a nearby vertical segment, just trim off the diag bit
2872 set coords [lrange $coords 2 end]
2874 set slope [expr {($x0 - $x1) / ($y0 - $y1)}]
2875 set xi [expr {$x0 - $slope * $linespc / 2}]
2876 set yi [expr {$y0 - $linespc / 2}]
2877 set coords [lreplace $coords 0 1 $xi $y0 $xi $yi]
2881 set arrow [expr {2 * ($i > 0) + $downarrow}]
2882 set arrow [lindex {none first last both} $arrow]
2883 set t [$canv create line $coords -width [linewidth $id] \
2884 -fill $colormap($id) -tags lines.$id -arrow $arrow]
2889 proc drawparentlinks {id row col olds} {
2890 global rowidlist canv colormap
2892 set row2 [expr {$row + 1}]
2893 set x [xc $row $col]
2896 set ids [lindex $rowidlist $row2]
2897 # rmx = right-most X coord used
2900 set i [lsearch -exact $ids $p]
2902 puts "oops, parent $p of $id not in list"
2905 set x2 [xc $row2 $i]
2909 set ranges [rowranges $p]
2910 if {$ranges ne {} && $row2 == [lindex $ranges 0]
2911 && $row2 < [lindex $ranges 1]} {
2912 # drawlineseg will do this one for us
2916 # should handle duplicated parents here...
2917 set coords [list $x $y]
2918 if {$i < $col - 1} {
2919 lappend coords [xc $row [expr {$i + 1}]] $y
2920 } elseif {$i > $col + 1} {
2921 lappend coords [xc $row [expr {$i - 1}]] $y
2923 lappend coords $x2 $y2
2924 set t [$canv create line $coords -width [linewidth $p] \
2925 -fill $colormap($p) -tags lines.$p]
2932 proc drawlines {id} {
2933 global colormap canv
2935 global children iddrawn commitrow rowidlist curview
2937 $canv delete lines.$id
2938 set nr [expr {[llength [rowranges $id]] / 2}]
2939 for {set i 0} {$i < $nr} {incr i} {
2940 if {[info exists idrangedrawn($id,$i)]} {
2944 foreach child $children($curview,$id) {
2945 if {[info exists iddrawn($child)]} {
2946 set row $commitrow($curview,$child)
2947 set col [lsearch -exact [lindex $rowidlist $row] $child]
2949 drawparentlinks $child $row $col [list $id]
2955 proc drawcmittext {id row col rmx} {
2956 global linespc canv canv2 canv3 canvy0 fgcolor
2957 global commitlisted commitinfo rowidlist
2958 global rowtextx idpos idtags idheads idotherrefs
2959 global linehtag linentag linedtag
2960 global mainfont canvxmax boldrows boldnamerows fgcolor
2962 set ofill [expr {[lindex $commitlisted $row]? "blue": "white"}]
2963 set x [xc $row $col]
2965 set orad [expr {$linespc / 3}]
2966 set t [$canv create oval [expr {$x - $orad}] [expr {$y - $orad}] \
2967 [expr {$x + $orad - 1}] [expr {$y + $orad - 1}] \
2968 -fill $ofill -outline $fgcolor -width 1 -tags circle]
2970 $canv bind $t <1> {selcanvline {} %x %y}
2971 set xt [xc $row [llength [lindex $rowidlist $row]]]
2975 set rowtextx($row) $xt
2976 set idpos($id) [list $x $xt $y]
2977 if {[info exists idtags($id)] || [info exists idheads($id)]
2978 || [info exists idotherrefs($id)]} {
2979 set xt [drawtags $id $x $xt $y]
2981 set headline [lindex $commitinfo($id) 0]
2982 set name [lindex $commitinfo($id) 1]
2983 set date [lindex $commitinfo($id) 2]
2984 set date [formatdate $date]
2987 set isbold [ishighlighted $row]
2989 lappend boldrows $row
2992 lappend boldnamerows $row
2996 set linehtag($row) [$canv create text $xt $y -anchor w -fill $fgcolor \
2997 -text $headline -font $font -tags text]
2998 $canv bind $linehtag($row) <Button-3> "rowmenu %X %Y $id"
2999 set linentag($row) [$canv2 create text 3 $y -anchor w -fill $fgcolor \
3000 -text $name -font $nfont -tags text]
3001 set linedtag($row) [$canv3 create text 3 $y -anchor w -fill $fgcolor \
3002 -text $date -font $mainfont -tags text]
3003 set xr [expr {$xt + [font measure $mainfont $headline]}]
3004 if {$xr > $canvxmax} {
3010 proc drawcmitrow {row} {
3011 global displayorder rowidlist
3012 global idrangedrawn iddrawn
3013 global commitinfo parentlist numcommits
3014 global filehighlight fhighlights findstring nhighlights
3015 global hlview vhighlights
3016 global highlight_related rhighlights
3018 if {$row >= $numcommits} return
3019 foreach id [lindex $rowidlist $row] {
3020 if {$id eq {}} continue
3022 foreach {s e} [rowranges $id] {
3024 if {$row < $s} continue
3027 if {$e < $numcommits && ![info exists idrangedrawn($id,$i)]} {
3029 set idrangedrawn($id,$i) 1
3036 set id [lindex $displayorder $row]
3037 if {[info exists hlview] && ![info exists vhighlights($row)]} {
3038 askvhighlight $row $id
3040 if {[info exists filehighlight] && ![info exists fhighlights($row)]} {
3041 askfilehighlight $row $id
3043 if {$findstring ne {} && ![info exists nhighlights($row)]} {
3044 askfindhighlight $row $id
3046 if {$highlight_related ne "None" && ![info exists rhighlights($row)]} {
3047 askrelhighlight $row $id
3049 if {[info exists iddrawn($id)]} return
3050 set col [lsearch -exact [lindex $rowidlist $row] $id]
3052 puts "oops, row $row id $id not in list"
3055 if {![info exists commitinfo($id)]} {
3059 set olds [lindex $parentlist $row]
3061 set rmx [drawparentlinks $id $row $col $olds]
3065 drawcmittext $id $row $col $rmx
3069 proc drawfrac {f0 f1} {
3070 global numcommits canv
3073 set ymax [lindex [$canv cget -scrollregion] 3]
3074 if {$ymax eq {} || $ymax == 0} return
3075 set y0 [expr {int($f0 * $ymax)}]
3076 set row [expr {int(($y0 - 3) / $linespc) - 1}]
3080 set y1 [expr {int($f1 * $ymax)}]
3081 set endrow [expr {int(($y1 - 3) / $linespc) + 1}]
3082 if {$endrow >= $numcommits} {
3083 set endrow [expr {$numcommits - 1}]
3085 for {} {$row <= $endrow} {incr row} {
3090 proc drawvisible {} {
3092 eval drawfrac [$canv yview]
3095 proc clear_display {} {
3096 global iddrawn idrangedrawn
3097 global vhighlights fhighlights nhighlights rhighlights
3100 catch {unset iddrawn}
3101 catch {unset idrangedrawn}
3102 catch {unset vhighlights}
3103 catch {unset fhighlights}
3104 catch {unset nhighlights}
3105 catch {unset rhighlights}
3108 proc findcrossings {id} {
3109 global rowidlist parentlist numcommits rowoffsets displayorder
3113 foreach {s e} [rowranges $id] {
3114 if {$e >= $numcommits} {
3115 set e [expr {$numcommits - 1}]
3117 if {$e <= $s} continue
3118 set x [lsearch -exact [lindex $rowidlist $e] $id]
3120 puts "findcrossings: oops, no [shortids $id] in row $e"
3123 for {set row $e} {[incr row -1] >= $s} {} {
3124 set olds [lindex $parentlist $row]
3125 set kid [lindex $displayorder $row]
3126 set kidx [lsearch -exact [lindex $rowidlist $row] $kid]
3127 if {$kidx < 0} continue
3128 set nextrow [lindex $rowidlist [expr {$row + 1}]]
3130 set px [lsearch -exact $nextrow $p]
3131 if {$px < 0} continue
3132 if {($kidx < $x && $x < $px) || ($px < $x && $x < $kidx)} {
3133 if {[lsearch -exact $ccross $p] >= 0} continue
3134 if {$x == $px + ($kidx < $px? -1: 1)} {
3136 } elseif {[lsearch -exact $cross $p] < 0} {
3141 set inc [lindex $rowoffsets $row $x]
3142 if {$inc eq {}} break
3146 return [concat $ccross {{}} $cross]
3149 proc assigncolor {id} {
3150 global colormap colors nextcolor
3151 global commitrow parentlist children children curview
3153 if {[info exists colormap($id)]} return
3154 set ncolors [llength $colors]
3155 if {[info exists children($curview,$id)]} {
3156 set kids $children($curview,$id)
3160 if {[llength $kids] == 1} {
3161 set child [lindex $kids 0]
3162 if {[info exists colormap($child)]
3163 && [llength [lindex $parentlist $commitrow($curview,$child)]] == 1} {
3164 set colormap($id) $colormap($child)
3170 foreach x [findcrossings $id] {
3172 # delimiter between corner crossings and other crossings
3173 if {[llength $badcolors] >= $ncolors - 1} break
3174 set origbad $badcolors
3176 if {[info exists colormap($x)]
3177 && [lsearch -exact $badcolors $colormap($x)] < 0} {
3178 lappend badcolors $colormap($x)
3181 if {[llength $badcolors] >= $ncolors} {
3182 set badcolors $origbad
3184 set origbad $badcolors
3185 if {[llength $badcolors] < $ncolors - 1} {
3186 foreach child $kids {
3187 if {[info exists colormap($child)]
3188 && [lsearch -exact $badcolors $colormap($child)] < 0} {
3189 lappend badcolors $colormap($child)
3191 foreach p [lindex $parentlist $commitrow($curview,$child)] {
3192 if {[info exists colormap($p)]
3193 && [lsearch -exact $badcolors $colormap($p)] < 0} {
3194 lappend badcolors $colormap($p)
3198 if {[llength $badcolors] >= $ncolors} {
3199 set badcolors $origbad
3202 for {set i 0} {$i <= $ncolors} {incr i} {
3203 set c [lindex $colors $nextcolor]
3204 if {[incr nextcolor] >= $ncolors} {
3207 if {[lsearch -exact $badcolors $c]} break
3209 set colormap($id) $c
3212 proc bindline {t id} {
3215 $canv bind $t <Enter> "lineenter %x %y $id"
3216 $canv bind $t <Motion> "linemotion %x %y $id"
3217 $canv bind $t <Leave> "lineleave $id"
3218 $canv bind $t <Button-1> "lineclick %x %y $id 1"
3221 proc drawtags {id x xt y1} {
3222 global idtags idheads idotherrefs mainhead
3223 global linespc lthickness
3224 global canv mainfont commitrow rowtextx curview fgcolor bgcolor
3229 if {[info exists idtags($id)]} {
3230 set marks $idtags($id)
3231 set ntags [llength $marks]
3233 if {[info exists idheads($id)]} {
3234 set marks [concat $marks $idheads($id)]
3235 set nheads [llength $idheads($id)]
3237 if {[info exists idotherrefs($id)]} {
3238 set marks [concat $marks $idotherrefs($id)]
3244 set delta [expr {int(0.5 * ($linespc - $lthickness))}]
3245 set yt [expr {$y1 - 0.5 * $linespc}]
3246 set yb [expr {$yt + $linespc - 1}]
3250 foreach tag $marks {
3252 if {$i >= $ntags && $i < $ntags + $nheads && $tag eq $mainhead} {
3253 set wid [font measure [concat $mainfont bold] $tag]
3255 set wid [font measure $mainfont $tag]
3259 set xt [expr {$xt + $delta + $wid + $lthickness + $linespc}]
3261 set t [$canv create line $x $y1 [lindex $xvals end] $y1 \
3262 -width $lthickness -fill black -tags tag.$id]
3264 foreach tag $marks x $xvals wid $wvals {
3265 set xl [expr {$x + $delta}]
3266 set xr [expr {$x + $delta + $wid + $lthickness}]
3268 if {[incr ntags -1] >= 0} {
3270 set t [$canv create polygon $x [expr {$yt + $delta}] $xl $yt \
3271 $xr $yt $xr $yb $xl $yb $x [expr {$yb - $delta}] \
3272 -width 1 -outline black -fill yellow -tags tag.$id]
3273 $canv bind $t <1> [list showtag $tag 1]
3274 set rowtextx($commitrow($curview,$id)) [expr {$xr + $linespc}]
3276 # draw a head or other ref
3277 if {[incr nheads -1] >= 0} {
3279 if {$tag eq $mainhead} {
3285 set xl [expr {$xl - $delta/2}]
3286 $canv create polygon $x $yt $xr $yt $xr $yb $x $yb \
3287 -width 1 -outline black -fill $col -tags tag.$id
3288 if {[regexp {^(remotes/.*/|remotes/)} $tag match remoteprefix]} {
3289 set rwid [font measure $mainfont $remoteprefix]
3290 set xi [expr {$x + 1}]
3291 set yti [expr {$yt + 1}]
3292 set xri [expr {$x + $rwid}]
3293 $canv create polygon $xi $yti $xri $yti $xri $yb $xi $yb \
3294 -width 0 -fill "#ffddaa" -tags tag.$id
3297 set t [$canv create text $xl $y1 -anchor w -text $tag -fill $fgcolor \
3298 -font $font -tags [list tag.$id text]]
3300 $canv bind $t <1> [list showtag $tag 1]
3301 } elseif {$nheads >= 0} {
3302 $canv bind $t <Button-3> [list headmenu %X %Y $id $tag]
3308 proc xcoord {i level ln} {
3309 global canvx0 xspc1 xspc2
3311 set x [expr {$canvx0 + $i * $xspc1($ln)}]
3312 if {$i > 0 && $i == $level} {
3313 set x [expr {$x + 0.5 * ($xspc2 - $xspc1($ln))}]
3314 } elseif {$i > $level} {
3315 set x [expr {$x + $xspc2 - $xspc1($ln)}]
3320 proc show_status {msg} {
3321 global canv mainfont fgcolor
3324 $canv create text 3 3 -anchor nw -text $msg -font $mainfont \
3325 -tags text -fill $fgcolor
3328 proc finishcommits {} {
3329 global commitidx phase curview
3330 global pending_select
3332 if {$commitidx($curview) > 0} {
3335 show_status "No commits selected"
3338 catch {unset pending_select}
3341 # Insert a new commit as the child of the commit on row $row.
3342 # The new commit will be displayed on row $row and the commits
3343 # on that row and below will move down one row.
3344 proc insertrow {row newcmit} {
3345 global displayorder parentlist childlist commitlisted
3346 global commitrow curview rowidlist rowoffsets numcommits
3347 global rowrangelist idrowranges rowlaidout rowoptim numcommits
3348 global linesegends selectedline
3350 if {$row >= $numcommits} {
3351 puts "oops, inserting new row $row but only have $numcommits rows"
3354 set p [lindex $displayorder $row]
3355 set displayorder [linsert $displayorder $row $newcmit]
3356 set parentlist [linsert $parentlist $row $p]
3357 set kids [lindex $childlist $row]
3358 lappend kids $newcmit
3359 lset childlist $row $kids
3360 set childlist [linsert $childlist $row {}]
3361 set commitlisted [linsert $commitlisted $row 1]
3362 set l [llength $displayorder]
3363 for {set r $row} {$r < $l} {incr r} {
3364 set id [lindex $displayorder $r]
3365 set commitrow($curview,$id) $r
3368 set idlist [lindex $rowidlist $row]
3369 set offs [lindex $rowoffsets $row]
3372 if {$x eq {} || ($x eq $p && [llength $kids] == 1)} {
3378 if {[llength $kids] == 1} {
3379 set col [lsearch -exact $idlist $p]
3380 lset idlist $col $newcmit
3382 set col [llength $idlist]
3383 lappend idlist $newcmit
3385 lset rowoffsets $row $offs
3387 set rowidlist [linsert $rowidlist $row $idlist]
3388 set rowoffsets [linsert $rowoffsets [expr {$row+1}] $newoffs]
3390 set rowrangelist [linsert $rowrangelist $row {}]
3391 set l [llength $rowrangelist]
3392 for {set r 0} {$r < $l} {incr r} {
3393 set ranges [lindex $rowrangelist $r]
3394 if {$ranges ne {} && [lindex $ranges end] >= $row} {
3398 lappend newranges [expr {$x + 1}]
3400 lappend newranges $x
3403 lset rowrangelist $r $newranges
3406 if {[llength $kids] > 1} {
3407 set rp1 [expr {$row + 1}]
3408 set ranges [lindex $rowrangelist $rp1]
3409 if {$ranges eq {}} {
3410 set ranges [list $row $rp1]
3411 } elseif {[lindex $ranges end-1] == $rp1} {
3412 lset ranges end-1 $row
3414 lset rowrangelist $rp1 $ranges
3416 foreach id [array names idrowranges] {
3417 set ranges $idrowranges($id)
3418 if {$ranges ne {} && [lindex $ranges end] >= $row} {
3422 lappend newranges [expr {$x + 1}]
3424 lappend newranges $x
3427 set idrowranges($id) $newranges
3431 set linesegends [linsert $linesegends $row {}]
3437 if {[info exists selectedline] && $selectedline >= $row} {
3443 # Don't change the text pane cursor if it is currently the hand cursor,
3444 # showing that we are over a sha1 ID link.
3445 proc settextcursor {c} {
3446 global ctext curtextcursor
3448 if {[$ctext cget -cursor] == $curtextcursor} {
3449 $ctext config -cursor $c
3451 set curtextcursor $c
3454 proc nowbusy {what} {
3457 if {[array names isbusy] eq {}} {
3458 . config -cursor watch
3464 proc notbusy {what} {
3465 global isbusy maincursor textcursor
3467 catch {unset isbusy($what)}
3468 if {[array names isbusy] eq {}} {
3469 . config -cursor $maincursor
3470 settextcursor $textcursor
3476 global rowlaidout commitidx curview
3477 global pending_select
3480 layoutrows $rowlaidout $commitidx($curview) 1
3482 optimize_rows $row 0 $commitidx($curview)
3483 showstuff $commitidx($curview)
3484 if {[info exists pending_select]} {
3488 set drawmsecs [expr {[clock clicks -milliseconds] - $startmsecs}]
3490 #puts "overall $drawmsecs ms for $numcommits commits"
3493 proc findmatches {f} {
3494 global findtype foundstring foundstrlen
3495 if {$findtype == "Regexp"} {
3496 set matches [regexp -indices -all -inline $foundstring $f]
3498 if {$findtype == "IgnCase"} {
3499 set str [string tolower $f]
3505 while {[set j [string first $foundstring $str $i]] >= 0} {
3506 lappend matches [list $j [expr {$j+$foundstrlen-1}]]
3507 set i [expr {$j + $foundstrlen}]
3514 global findtype findloc findstring markedmatches commitinfo
3515 global numcommits displayorder linehtag linentag linedtag
3516 global mainfont canv canv2 canv3 selectedline
3517 global matchinglines foundstring foundstrlen matchstring
3522 cancel_next_highlight
3524 set matchinglines {}
3525 if {$findtype == "IgnCase"} {
3526 set foundstring [string tolower $findstring]
3528 set foundstring $findstring
3530 set foundstrlen [string length $findstring]
3531 if {$foundstrlen == 0} return
3532 regsub -all {[*?\[\\]} $foundstring {\\&} matchstring
3533 set matchstring "*$matchstring*"
3534 if {![info exists selectedline]} {
3537 set oldsel $selectedline
3540 set fldtypes {Headline Author Date Committer CDate Comments}
3542 foreach id $displayorder {
3543 set d $commitdata($id)
3545 if {$findtype == "Regexp"} {
3546 set doesmatch [regexp $foundstring $d]
3547 } elseif {$findtype == "IgnCase"} {
3548 set doesmatch [string match -nocase $matchstring $d]
3550 set doesmatch [string match $matchstring $d]
3552 if {!$doesmatch} continue
3553 if {![info exists commitinfo($id)]} {
3556 set info $commitinfo($id)
3558 foreach f $info ty $fldtypes {
3559 if {$findloc != "All fields" && $findloc != $ty} {
3562 set matches [findmatches $f]
3563 if {$matches == {}} continue
3565 if {$ty == "Headline"} {
3567 markmatches $canv $l $f $linehtag($l) $matches $mainfont
3568 } elseif {$ty == "Author"} {
3570 markmatches $canv2 $l $f $linentag($l) $matches $mainfont
3571 } elseif {$ty == "Date"} {
3573 markmatches $canv3 $l $f $linedtag($l) $matches $mainfont
3577 lappend matchinglines $l
3578 if {!$didsel && $l > $oldsel} {
3584 if {$matchinglines == {}} {
3586 } elseif {!$didsel} {
3587 findselectline [lindex $matchinglines 0]
3591 proc findselectline {l} {
3592 global findloc commentend ctext
3594 if {$findloc == "All fields" || $findloc == "Comments"} {
3595 # highlight the matches in the comments
3596 set f [$ctext get 1.0 $commentend]
3597 set matches [findmatches $f]
3598 foreach match $matches {
3599 set start [lindex $match 0]
3600 set end [expr {[lindex $match 1] + 1}]
3601 $ctext tag add found "1.0 + $start c" "1.0 + $end c"
3606 proc findnext {restart} {
3607 global matchinglines selectedline
3608 if {![info exists matchinglines]} {
3614 if {![info exists selectedline]} return
3615 foreach l $matchinglines {
3616 if {$l > $selectedline} {
3625 global matchinglines selectedline
3626 if {![info exists matchinglines]} {
3630 if {![info exists selectedline]} return
3632 foreach l $matchinglines {
3633 if {$l >= $selectedline} break
3637 findselectline $prev
3643 proc stopfindproc {{done 0}} {
3644 global findprocpid findprocfile findids
3645 global ctext findoldcursor phase maincursor textcursor
3646 global findinprogress
3648 catch {unset findids}
3649 if {[info exists findprocpid]} {
3651 catch {exec kill $findprocpid}
3653 catch {close $findprocfile}
3656 catch {unset findinprogress}
3660 # mark a commit as matching by putting a yellow background
3661 # behind the headline
3662 proc markheadline {l id} {
3663 global canv mainfont linehtag
3666 set bbox [$canv bbox $linehtag($l)]
3667 set t [$canv create rect $bbox -outline {} -tags matches -fill yellow]
3671 # mark the bits of a headline, author or date that match a find string
3672 proc markmatches {canv l str tag matches font} {
3673 set bbox [$canv bbox $tag]
3674 set x0 [lindex $bbox 0]
3675 set y0 [lindex $bbox 1]
3676 set y1 [lindex $bbox 3]
3677 foreach match $matches {
3678 set start [lindex $match 0]
3679 set end [lindex $match 1]
3680 if {$start > $end} continue
3681 set xoff [font measure $font [string range $str 0 [expr {$start-1}]]]
3682 set xlen [font measure $font [string range $str 0 [expr {$end}]]]
3683 set t [$canv create rect [expr {$x0+$xoff}] $y0 \
3684 [expr {$x0+$xlen+2}] $y1 \
3685 -outline {} -tags matches -fill yellow]
3690 proc unmarkmatches {} {
3691 global matchinglines findids
3692 allcanvs delete matches
3693 catch {unset matchinglines}
3694 catch {unset findids}
3697 proc selcanvline {w x y} {
3698 global canv canvy0 ctext linespc
3700 set ymax [lindex [$canv cget -scrollregion] 3]
3701 if {$ymax == {}} return
3702 set yfrac [lindex [$canv yview] 0]
3703 set y [expr {$y + $yfrac * $ymax}]
3704 set l [expr {int(($y - $canvy0) / $linespc + 0.5)}]
3709 if {![info exists rowtextx($l)] || $x < $rowtextx($l)} return
3715 proc commit_descriptor {p} {
3717 if {![info exists commitinfo($p)]} {
3721 if {[llength $commitinfo($p)] > 1} {
3722 set l [lindex $commitinfo($p) 0]
3727 # append some text to the ctext widget, and make any SHA1 ID
3728 # that we know about be a clickable link.
3729 proc appendwithlinks {text tags} {
3730 global ctext commitrow linknum curview
3732 set start [$ctext index "end - 1c"]
3733 $ctext insert end $text $tags
3734 set links [regexp -indices -all -inline {[0-9a-f]{40}} $text]
3738 set linkid [string range $text $s $e]
3739 if {![info exists commitrow($curview,$linkid)]} continue
3741 $ctext tag add link "$start + $s c" "$start + $e c"
3742 $ctext tag add link$linknum "$start + $s c" "$start + $e c"
3743 $ctext tag bind link$linknum <1> \
3744 [list selectline $commitrow($curview,$linkid) 1]
3747 $ctext tag conf link -foreground blue -underline 1
3748 $ctext tag bind link <Enter> { %W configure -cursor hand2 }
3749 $ctext tag bind link <Leave> { %W configure -cursor $curtextcursor }
3752 proc viewnextline {dir} {
3756 set ymax [lindex [$canv cget -scrollregion] 3]
3757 set wnow [$canv yview]
3758 set wtop [expr {[lindex $wnow 0] * $ymax}]
3759 set newtop [expr {$wtop + $dir * $linespc}]
3762 } elseif {$newtop > $ymax} {
3765 allcanvs yview moveto [expr {$newtop * 1.0 / $ymax}]
3768 # add a list of tag or branch names at position pos
3769 # returns the number of names inserted
3770 proc appendrefs {pos tags var} {
3771 global ctext commitrow linknum curview $var
3773 if {[catch {$ctext index $pos}]} {
3776 set tags [lsort $tags]
3779 set id [set $var\($tag\)]
3782 $ctext insert $pos $sep
3783 $ctext insert $pos $tag $lk
3784 $ctext tag conf $lk -foreground blue
3785 if {[info exists commitrow($curview,$id)]} {
3786 $ctext tag bind $lk <1> \
3787 [list selectline $commitrow($curview,$id) 1]
3788 $ctext tag conf $lk -underline 1
3789 $ctext tag bind $lk <Enter> { %W configure -cursor hand2 }
3790 $ctext tag bind $lk <Leave> { %W configure -cursor $curtextcursor }
3794 return [llength $tags]
3797 proc taglist {ids} {
3802 foreach tag $idtags($id) {
3809 # called when we have finished computing the nearby tags
3810 proc dispneartags {} {
3811 global selectedline currentid ctext anc_tags desc_tags showneartags
3814 if {![info exists selectedline] || !$showneartags} return
3816 $ctext conf -state normal
3817 if {[info exists desc_heads($id)]} {
3818 if {[appendrefs branch $desc_heads($id) headids] > 1} {
3819 $ctext insert "branch -2c" "es"
3822 if {[info exists anc_tags($id)]} {
3823 appendrefs follows [taglist $anc_tags($id)] tagids
3825 if {[info exists desc_tags($id)]} {
3826 appendrefs precedes [taglist $desc_tags($id)] tagids
3828 $ctext conf -state disabled
3831 proc selectline {l isnew} {
3832 global canv canv2 canv3 ctext commitinfo selectedline
3833 global displayorder linehtag linentag linedtag
3834 global canvy0 linespc parentlist childlist
3835 global currentid sha1entry
3836 global commentend idtags linknum
3837 global mergemax numcommits pending_select
3838 global cmitmode desc_tags anc_tags showneartags allcommits desc_heads
3840 catch {unset pending_select}
3843 cancel_next_highlight
3844 if {$l < 0 || $l >= $numcommits} return
3845 set y [expr {$canvy0 + $l * $linespc}]
3846 set ymax [lindex [$canv cget -scrollregion] 3]
3847 set ytop [expr {$y - $linespc - 1}]
3848 set ybot [expr {$y + $linespc + 1}]
3849 set wnow [$canv yview]
3850 set wtop [expr {[lindex $wnow 0] * $ymax}]
3851 set wbot [expr {[lindex $wnow 1] * $ymax}]
3852 set wh [expr {$wbot - $wtop}]
3854 if {$ytop < $wtop} {
3855 if {$ybot < $wtop} {
3856 set newtop [expr {$y - $wh / 2.0}]
3859 if {$newtop > $wtop - $linespc} {
3860 set newtop [expr {$wtop - $linespc}]
3863 } elseif {$ybot > $wbot} {
3864 if {$ytop > $wbot} {
3865 set newtop [expr {$y - $wh / 2.0}]
3867 set newtop [expr {$ybot - $wh}]
3868 if {$newtop < $wtop + $linespc} {
3869 set newtop [expr {$wtop + $linespc}]
3873 if {$newtop != $wtop} {
3877 allcanvs yview moveto [expr {$newtop * 1.0 / $ymax}]
3881 if {![info exists linehtag($l)]} return
3883 set t [eval $canv create rect [$canv bbox $linehtag($l)] -outline {{}} \
3884 -tags secsel -fill [$canv cget -selectbackground]]
3886 $canv2 delete secsel
3887 set t [eval $canv2 create rect [$canv2 bbox $linentag($l)] -outline {{}} \
3888 -tags secsel -fill [$canv2 cget -selectbackground]]
3890 $canv3 delete secsel
3891 set t [eval $canv3 create rect [$canv3 bbox $linedtag($l)] -outline {{}} \
3892 -tags secsel -fill [$canv3 cget -selectbackground]]
3896 addtohistory [list selectline $l 0]
3901 set id [lindex $displayorder $l]
3903 $sha1entry delete 0 end
3904 $sha1entry insert 0 $id
3905 $sha1entry selection from 0
3906 $sha1entry selection to end
3909 $ctext conf -state normal
3912 set info $commitinfo($id)
3913 set date [formatdate [lindex $info 2]]
3914 $ctext insert end "Author: [lindex $info 1] $date\n"
3915 set date [formatdate [lindex $info 4]]
3916 $ctext insert end "Committer: [lindex $info 3] $date\n"
3917 if {[info exists idtags($id)]} {
3918 $ctext insert end "Tags:"
3919 foreach tag $idtags($id) {
3920 $ctext insert end " $tag"
3922 $ctext insert end "\n"
3926 set olds [lindex $parentlist $l]
3927 if {[llength $olds] > 1} {
3930 if {$np >= $mergemax} {
3935 $ctext insert end "Parent: " $tag
3936 appendwithlinks [commit_descriptor $p] {}
3941 append headers "Parent: [commit_descriptor $p]"
3945 foreach c [lindex $childlist $l] {
3946 append headers "Child: [commit_descriptor $c]"
3949 # make anything that looks like a SHA1 ID be a clickable link
3950 appendwithlinks $headers {}
3951 if {$showneartags} {
3952 if {![info exists allcommits]} {
3955 $ctext insert end "Branch: "
3956 $ctext mark set branch "end -1c"
3957 $ctext mark gravity branch left
3958 if {[info exists desc_heads($id)]} {
3959 if {[appendrefs branch $desc_heads($id) headids] > 1} {
3960 # turn "Branch" into "Branches"
3961 $ctext insert "branch -2c" "es"
3964 $ctext insert end "\nFollows: "
3965 $ctext mark set follows "end -1c"
3966 $ctext mark gravity follows left
3967 if {[info exists anc_tags($id)]} {
3968 appendrefs follows [taglist $anc_tags($id)] tagids
3970 $ctext insert end "\nPrecedes: "
3971 $ctext mark set precedes "end -1c"
3972 $ctext mark gravity precedes left
3973 if {[info exists desc_tags($id)]} {
3974 appendrefs precedes [taglist $desc_tags($id)] tagids
3976 $ctext insert end "\n"
3978 $ctext insert end "\n"
3979 appendwithlinks [lindex $info 5] {comment}
3981 $ctext tag delete Comments
3982 $ctext tag remove found 1.0 end
3983 $ctext conf -state disabled
3984 set commentend [$ctext index "end - 1c"]
3986 init_flist "Comments"
3987 if {$cmitmode eq "tree"} {
3989 } elseif {[llength $olds] <= 1} {
3996 proc selfirstline {} {
4001 proc sellastline {} {
4004 set l [expr {$numcommits - 1}]
4008 proc selnextline {dir} {
4010 if {![info exists selectedline]} return
4011 set l [expr {$selectedline + $dir}]
4016 proc selnextpage {dir} {
4017 global canv linespc selectedline numcommits
4019 set lpp [expr {([winfo height $canv] - 2) / $linespc}]
4023 allcanvs yview scroll [expr {$dir * $lpp}] units
4025 if {![info exists selectedline]} return
4026 set l [expr {$selectedline + $dir * $lpp}]
4029 } elseif {$l >= $numcommits} {
4030 set l [expr $numcommits - 1]
4036 proc unselectline {} {
4037 global selectedline currentid
4039 catch {unset selectedline}
4040 catch {unset currentid}
4041 allcanvs delete secsel
4043 cancel_next_highlight
4046 proc reselectline {} {
4049 if {[info exists selectedline]} {
4050 selectline $selectedline 0
4054 proc addtohistory {cmd} {
4055 global history historyindex curview
4057 set elt [list $curview $cmd]
4058 if {$historyindex > 0
4059 && [lindex $history [expr {$historyindex - 1}]] == $elt} {
4063 if {$historyindex < [llength $history]} {
4064 set history [lreplace $history $historyindex end $elt]
4066 lappend history $elt
4069 if {$historyindex > 1} {
4070 .tf.bar.leftbut conf -state normal
4072 .tf.bar.leftbut conf -state disabled
4074 .tf.bar.rightbut conf -state disabled
4080 set view [lindex $elt 0]
4081 set cmd [lindex $elt 1]
4082 if {$curview != $view} {
4089 global history historyindex
4091 if {$historyindex > 1} {
4092 incr historyindex -1
4093 godo [lindex $history [expr {$historyindex - 1}]]
4094 .tf.bar.rightbut conf -state normal
4096 if {$historyindex <= 1} {
4097 .tf.bar.leftbut conf -state disabled
4102 global history historyindex
4104 if {$historyindex < [llength $history]} {
4105 set cmd [lindex $history $historyindex]
4108 .tf.bar.leftbut conf -state normal
4110 if {$historyindex >= [llength $history]} {
4111 .tf.bar.rightbut conf -state disabled
4116 global treefilelist treeidlist diffids diffmergeid treepending
4119 catch {unset diffmergeid}
4120 if {![info exists treefilelist($id)]} {
4121 if {![info exists treepending]} {
4122 if {[catch {set gtf [open [concat | git ls-tree -r $id] r]}]} {
4126 set treefilelist($id) {}
4127 set treeidlist($id) {}
4128 fconfigure $gtf -blocking 0
4129 fileevent $gtf readable [list gettreeline $gtf $id]
4136 proc gettreeline {gtf id} {
4137 global treefilelist treeidlist treepending cmitmode diffids
4139 while {[gets $gtf line] >= 0} {
4140 if {[lindex $line 1] ne "blob"} continue
4141 set sha1 [lindex $line 2]
4142 set fname [lindex $line 3]
4143 lappend treefilelist($id) $fname
4144 lappend treeidlist($id) $sha1
4146 if {![eof $gtf]} return
4149 if {$cmitmode ne "tree"} {
4150 if {![info exists diffmergeid]} {
4151 gettreediffs $diffids
4153 } elseif {$id ne $diffids} {
4161 global treefilelist treeidlist diffids
4162 global ctext commentend
4164 set i [lsearch -exact $treefilelist($diffids) $f]
4166 puts "oops, $f not in list for id $diffids"
4169 set blob [lindex $treeidlist($diffids) $i]
4170 if {[catch {set bf [open [concat | git cat-file blob $blob] r]} err]} {
4171 puts "oops, error reading blob $blob: $err"
4174 fconfigure $bf -blocking 0
4175 fileevent $bf readable [list getblobline $bf $diffids]
4176 $ctext config -state normal
4177 clear_ctext $commentend
4178 $ctext insert end "\n"
4179 $ctext insert end "$f\n" filesep
4180 $ctext config -state disabled
4181 $ctext yview $commentend
4184 proc getblobline {bf id} {
4185 global diffids cmitmode ctext
4187 if {$id ne $diffids || $cmitmode ne "tree"} {
4191 $ctext config -state normal
4192 while {[gets $bf line] >= 0} {
4193 $ctext insert end "$line\n"
4196 # delete last newline
4197 $ctext delete "end - 2c" "end - 1c"
4200 $ctext config -state disabled
4203 proc mergediff {id l} {
4204 global diffmergeid diffopts mdifffd
4210 # this doesn't seem to actually affect anything...
4211 set env(GIT_DIFF_OPTS) $diffopts
4212 set cmd [concat | git diff-tree --no-commit-id --cc $id]
4213 if {[catch {set mdf [open $cmd r]} err]} {
4214 error_popup "Error getting merge diffs: $err"
4217 fconfigure $mdf -blocking 0
4218 set mdifffd($id) $mdf
4219 set np [llength [lindex $parentlist $l]]
4220 fileevent $mdf readable [list getmergediffline $mdf $id $np]
4221 set nextupdate [expr {[clock clicks -milliseconds] + 100}]
4224 proc getmergediffline {mdf id np} {
4225 global diffmergeid ctext cflist nextupdate mergemax
4226 global difffilestart mdifffd
4228 set n [gets $mdf line]
4235 if {![info exists diffmergeid] || $id != $diffmergeid
4236 || $mdf != $mdifffd($id)} {
4239 $ctext conf -state normal
4240 if {[regexp {^diff --cc (.*)} $line match fname]} {
4241 # start of a new file
4242 $ctext insert end "\n"
4243 set here [$ctext index "end - 1c"]
4244 lappend difffilestart $here
4245 add_flist [list $fname]
4246 set l [expr {(78 - [string length $fname]) / 2}]
4247 set pad [string range "----------------------------------------" 1 $l]
4248 $ctext insert end "$pad $fname $pad\n" filesep
4249 } elseif {[regexp {^@@} $line]} {
4250 $ctext insert end "$line\n" hunksep
4251 } elseif {[regexp {^[0-9a-f]{40}$} $line] || [regexp {^index} $line]} {
4254 # parse the prefix - one ' ', '-' or '+' for each parent
4259 for {set j 0} {$j < $np} {incr j} {
4260 set c [string range $line $j $j]
4263 } elseif {$c == "-"} {
4265 } elseif {$c == "+"} {
4274 if {!$isbad && $minuses ne {} && $pluses eq {}} {
4275 # line doesn't appear in result, parents in $minuses have the line
4276 set num [lindex $minuses 0]
4277 } elseif {!$isbad && $pluses ne {} && $minuses eq {}} {
4278 # line appears in result, parents in $pluses don't have the line
4279 lappend tags mresult
4280 set num [lindex $spaces 0]
4283 if {$num >= $mergemax} {
4288 $ctext insert end "$line\n" $tags
4290 $ctext conf -state disabled
4291 if {[clock clicks -milliseconds] >= $nextupdate} {
4293 fileevent $mdf readable {}
4295 fileevent $mdf readable [list getmergediffline $mdf $id $np]
4299 proc startdiff {ids} {
4300 global treediffs diffids treepending diffmergeid
4303 catch {unset diffmergeid}
4304 if {![info exists treediffs($ids)]} {
4305 if {![info exists treepending]} {
4313 proc addtocflist {ids} {
4314 global treediffs cflist
4315 add_flist $treediffs($ids)
4319 proc gettreediffs {ids} {
4320 global treediff treepending
4321 set treepending $ids
4324 {set gdtf [open [concat | git diff-tree --no-commit-id -r $ids] r]} \
4326 fconfigure $gdtf -blocking 0
4327 fileevent $gdtf readable [list gettreediffline $gdtf $ids]
4330 proc gettreediffline {gdtf ids} {
4331 global treediff treediffs treepending diffids diffmergeid
4334 set n [gets $gdtf line]
4336 if {![eof $gdtf]} return
4338 set treediffs($ids) $treediff
4340 if {$cmitmode eq "tree"} {
4342 } elseif {$ids != $diffids} {
4343 if {![info exists diffmergeid]} {
4344 gettreediffs $diffids
4351 set file [lindex $line 5]
4352 lappend treediff $file
4355 proc getblobdiffs {ids} {
4356 global diffopts blobdifffd diffids env curdifftag curtagstart
4357 global nextupdate diffinhdr treediffs
4359 set env(GIT_DIFF_OPTS) $diffopts
4360 set cmd [concat | git diff-tree --no-commit-id -r -p -C $ids]
4361 if {[catch {set bdf [open $cmd r]} err]} {
4362 puts "error getting diffs: $err"
4366 fconfigure $bdf -blocking 0
4367 set blobdifffd($ids) $bdf
4368 set curdifftag Comments
4370 fileevent $bdf readable [list getblobdiffline $bdf $diffids]
4371 set nextupdate [expr {[clock clicks -milliseconds] + 100}]
4374 proc setinlist {var i val} {
4377 while {[llength [set $var]] < $i} {
4380 if {[llength [set $var]] == $i} {
4387 proc getblobdiffline {bdf ids} {
4388 global diffids blobdifffd ctext curdifftag curtagstart
4389 global diffnexthead diffnextnote difffilestart
4390 global nextupdate diffinhdr treediffs
4392 set n [gets $bdf line]
4396 if {$ids == $diffids && $bdf == $blobdifffd($ids)} {
4397 $ctext tag add $curdifftag $curtagstart end
4402 if {$ids != $diffids || $bdf != $blobdifffd($ids)} {
4405 $ctext conf -state normal
4406 if {[regexp {^diff --git a/(.*) b/(.*)} $line match fname newname]} {
4407 # start of a new file
4408 $ctext insert end "\n"
4409 $ctext tag add $curdifftag $curtagstart end
4410 set here [$ctext index "end - 1c"]
4411 set curtagstart $here
4413 set i [lsearch -exact $treediffs($ids) $fname]
4415 setinlist difffilestart $i $here
4417 if {$newname ne $fname} {
4418 set i [lsearch -exact $treediffs($ids) $newname]
4420 setinlist difffilestart $i $here
4423 set curdifftag "f:$fname"
4424 $ctext tag delete $curdifftag
4425 set l [expr {(78 - [string length $header]) / 2}]
4426 set pad [string range "----------------------------------------" 1 $l]
4427 $ctext insert end "$pad $header $pad\n" filesep
4429 } elseif {$diffinhdr && [string compare -length 3 $line "---"] == 0} {
4431 } elseif {$diffinhdr && [string compare -length 3 $line "+++"] == 0} {
4433 } elseif {[regexp {^@@ -([0-9]+),([0-9]+) \+([0-9]+),([0-9]+) @@(.*)} \
4434 $line match f1l f1c f2l f2c rest]} {
4435 $ctext insert end "$line\n" hunksep
4438 set x [string range $line 0 0]
4439 if {$x == "-" || $x == "+"} {
4440 set tag [expr {$x == "+"}]
4441 $ctext insert end "$line\n" d$tag
4442 } elseif {$x == " "} {
4443 $ctext insert end "$line\n"
4444 } elseif {$diffinhdr || $x == "\\"} {
4445 # e.g. "\ No newline at end of file"
4446 $ctext insert end "$line\n" filesep
4448 # Something else we don't recognize
4449 if {$curdifftag != "Comments"} {
4450 $ctext insert end "\n"
4451 $ctext tag add $curdifftag $curtagstart end
4452 set curtagstart [$ctext index "end - 1c"]
4453 set curdifftag Comments
4455 $ctext insert end "$line\n" filesep
4458 $ctext conf -state disabled
4459 if {[clock clicks -milliseconds] >= $nextupdate} {
4461 fileevent $bdf readable {}
4463 fileevent $bdf readable "getblobdiffline $bdf {$ids}"
4468 global difffilestart ctext
4469 set prev [lindex $difffilestart 0]
4470 set here [$ctext index @0,0]
4471 foreach loc $difffilestart {
4472 if {[$ctext compare $loc >= $here]} {
4482 global difffilestart ctext
4483 set here [$ctext index @0,0]
4484 foreach loc $difffilestart {
4485 if {[$ctext compare $loc > $here]} {
4492 proc clear_ctext {{first 1.0}} {
4493 global ctext smarktop smarkbot
4495 set l [lindex [split $first .] 0]
4496 if {![info exists smarktop] || [$ctext compare $first < $smarktop.0]} {
4499 if {![info exists smarkbot] || [$ctext compare $first < $smarkbot.0]} {
4502 $ctext delete $first end
4505 proc incrsearch {name ix op} {
4506 global ctext searchstring searchdirn
4508 $ctext tag remove found 1.0 end
4509 if {[catch {$ctext index anchor}]} {
4510 # no anchor set, use start of selection, or of visible area
4511 set sel [$ctext tag ranges sel]
4513 $ctext mark set anchor [lindex $sel 0]
4514 } elseif {$searchdirn eq "-forwards"} {
4515 $ctext mark set anchor @0,0
4517 $ctext mark set anchor @0,[winfo height $ctext]
4520 if {$searchstring ne {}} {
4521 set here [$ctext search $searchdirn -- $searchstring anchor]
4530 global sstring ctext searchstring searchdirn
4533 $sstring icursor end
4534 set searchdirn -forwards
4535 if {$searchstring ne {}} {
4536 set sel [$ctext tag ranges sel]
4538 set start "[lindex $sel 0] + 1c"
4539 } elseif {[catch {set start [$ctext index anchor]}]} {
4542 set match [$ctext search -count mlen -- $searchstring $start]
4543 $ctext tag remove sel 1.0 end
4549 set mend "$match + $mlen c"
4550 $ctext tag add sel $match $mend
4551 $ctext mark unset anchor
4555 proc dosearchback {} {
4556 global sstring ctext searchstring searchdirn
4559 $sstring icursor end
4560 set searchdirn -backwards
4561 if {$searchstring ne {}} {
4562 set sel [$ctext tag ranges sel]
4564 set start [lindex $sel 0]
4565 } elseif {[catch {set start [$ctext index anchor]}]} {
4566 set start @0,[winfo height $ctext]
4568 set match [$ctext search -backwards -count ml -- $searchstring $start]
4569 $ctext tag remove sel 1.0 end
4575 set mend "$match + $ml c"
4576 $ctext tag add sel $match $mend
4577 $ctext mark unset anchor
4581 proc searchmark {first last} {
4582 global ctext searchstring
4586 set match [$ctext search -count mlen -- $searchstring $mend $last.end]
4587 if {$match eq {}} break
4588 set mend "$match + $mlen c"
4589 $ctext tag add found $match $mend
4593 proc searchmarkvisible {doall} {
4594 global ctext smarktop smarkbot
4596 set topline [lindex [split [$ctext index @0,0] .] 0]
4597 set botline [lindex [split [$ctext index @0,[winfo height $ctext]] .] 0]
4598 if {$doall || $botline < $smarktop || $topline > $smarkbot} {
4599 # no overlap with previous
4600 searchmark $topline $botline
4601 set smarktop $topline
4602 set smarkbot $botline
4604 if {$topline < $smarktop} {
4605 searchmark $topline [expr {$smarktop-1}]
4606 set smarktop $topline
4608 if {$botline > $smarkbot} {
4609 searchmark [expr {$smarkbot+1}] $botline
4610 set smarkbot $botline
4615 proc scrolltext {f0 f1} {
4618 .bleft.sb set $f0 $f1
4619 if {$searchstring ne {}} {
4625 global linespc charspc canvx0 canvy0 mainfont
4626 global xspc1 xspc2 lthickness
4628 set linespc [font metrics $mainfont -linespace]
4629 set charspc [font measure $mainfont "m"]
4630 set canvy0 [expr {int(3 + 0.5 * $linespc)}]
4631 set canvx0 [expr {int(3 + 0.5 * $linespc)}]
4632 set lthickness [expr {int($linespc / 9) + 1}]
4633 set xspc1(0) $linespc
4641 set ymax [lindex [$canv cget -scrollregion] 3]
4642 if {$ymax eq {} || $ymax == 0} return
4643 set span [$canv yview]
4646 allcanvs yview moveto [lindex $span 0]
4648 if {[info exists selectedline]} {
4649 selectline $selectedline 0
4650 allcanvs yview moveto [lindex $span 0]
4654 proc incrfont {inc} {
4655 global mainfont textfont ctext canv phase
4656 global stopped entries
4658 set mainfont [lreplace $mainfont 1 1 [expr {[lindex $mainfont 1] + $inc}]]
4659 set textfont [lreplace $textfont 1 1 [expr {[lindex $textfont 1] + $inc}]]
4661 $ctext conf -font $textfont
4662 $ctext tag conf filesep -font [concat $textfont bold]
4663 foreach e $entries {
4664 $e conf -font $mainfont
4666 if {$phase eq "getcommits"} {
4667 $canv itemconf textitems -font $mainfont
4673 global sha1entry sha1string
4674 if {[string length $sha1string] == 40} {
4675 $sha1entry delete 0 end
4679 proc sha1change {n1 n2 op} {
4680 global sha1string currentid sha1but
4681 if {$sha1string == {}
4682 || ([info exists currentid] && $sha1string == $currentid)} {
4687 if {[$sha1but cget -state] == $state} return
4688 if {$state == "normal"} {
4689 $sha1but conf -state normal -relief raised -text "Goto: "
4691 $sha1but conf -state disabled -relief flat -text "SHA1 ID: "
4695 proc gotocommit {} {
4696 global sha1string currentid commitrow tagids headids
4697 global displayorder numcommits curview
4699 if {$sha1string == {}
4700 || ([info exists currentid] && $sha1string == $currentid)} return
4701 if {[info exists tagids($sha1string)]} {
4702 set id $tagids($sha1string)
4703 } elseif {[info exists headids($sha1string)]} {
4704 set id $headids($sha1string)
4706 set id [string tolower $sha1string]
4707 if {[regexp {^[0-9a-f]{4,39}$} $id]} {
4709 foreach i $displayorder {
4710 if {[string match $id* $i]} {
4714 if {$matches ne {}} {
4715 if {[llength $matches] > 1} {
4716 error_popup "Short SHA1 id $id is ambiguous"
4719 set id [lindex $matches 0]
4723 if {[info exists commitrow($curview,$id)]} {
4724 selectline $commitrow($curview,$id) 1
4727 if {[regexp {^[0-9a-fA-F]{4,}$} $sha1string]} {
4732 error_popup "$type $sha1string is not known"
4735 proc lineenter {x y id} {
4736 global hoverx hovery hoverid hovertimer
4737 global commitinfo canv
4739 if {![info exists commitinfo($id)] && ![getcommit $id]} return
4743 if {[info exists hovertimer]} {
4744 after cancel $hovertimer
4746 set hovertimer [after 500 linehover]
4750 proc linemotion {x y id} {
4751 global hoverx hovery hoverid hovertimer
4753 if {[info exists hoverid] && $id == $hoverid} {
4756 if {[info exists hovertimer]} {
4757 after cancel $hovertimer
4759 set hovertimer [after 500 linehover]
4763 proc lineleave {id} {
4764 global hoverid hovertimer canv
4766 if {[info exists hoverid] && $id == $hoverid} {
4768 if {[info exists hovertimer]} {
4769 after cancel $hovertimer
4777 global hoverx hovery hoverid hovertimer
4778 global canv linespc lthickness
4779 global commitinfo mainfont
4781 set text [lindex $commitinfo($hoverid) 0]
4782 set ymax [lindex [$canv cget -scrollregion] 3]
4783 if {$ymax == {}} return
4784 set yfrac [lindex [$canv yview] 0]
4785 set x [expr {$hoverx + 2 * $linespc}]
4786 set y [expr {$hovery + $yfrac * $ymax - $linespc / 2}]
4787 set x0 [expr {$x - 2 * $lthickness}]
4788 set y0 [expr {$y - 2 * $lthickness}]
4789 set x1 [expr {$x + [font measure $mainfont $text] + 2 * $lthickness}]
4790 set y1 [expr {$y + $linespc + 2 * $lthickness}]
4791 set t [$canv create rectangle $x0 $y0 $x1 $y1 \
4792 -fill \#ffff80 -outline black -width 1 -tags hover]
4794 set t [$canv create text $x $y -anchor nw -text $text -tags hover \
4799 proc clickisonarrow {id y} {
4802 set ranges [rowranges $id]
4803 set thresh [expr {2 * $lthickness + 6}]
4804 set n [expr {[llength $ranges] - 1}]
4805 for {set i 1} {$i < $n} {incr i} {
4806 set row [lindex $ranges $i]
4807 if {abs([yc $row] - $y) < $thresh} {
4814 proc arrowjump {id n y} {
4817 # 1 <-> 2, 3 <-> 4, etc...
4818 set n [expr {(($n - 1) ^ 1) + 1}]
4819 set row [lindex [rowranges $id] $n]
4821 set ymax [lindex [$canv cget -scrollregion] 3]
4822 if {$ymax eq {} || $ymax <= 0} return
4823 set view [$canv yview]
4824 set yspan [expr {[lindex $view 1] - [lindex $view 0]}]
4825 set yfrac [expr {$yt / $ymax - $yspan / 2}]
4829 allcanvs yview moveto $yfrac
4832 proc lineclick {x y id isnew} {
4833 global ctext commitinfo children canv thickerline curview
4835 if {![info exists commitinfo($id)] && ![getcommit $id]} return
4840 # draw this line thicker than normal
4844 set ymax [lindex [$canv cget -scrollregion] 3]
4845 if {$ymax eq {}} return
4846 set yfrac [lindex [$canv yview] 0]
4847 set y [expr {$y + $yfrac * $ymax}]
4849 set dirn [clickisonarrow $id $y]
4851 arrowjump $id $dirn $y
4856 addtohistory [list lineclick $x $y $id 0]
4858 # fill the details pane with info about this line
4859 $ctext conf -state normal
4861 $ctext tag conf link -foreground blue -underline 1
4862 $ctext tag bind link <Enter> { %W configure -cursor hand2 }
4863 $ctext tag bind link <Leave> { %W configure -cursor $curtextcursor }
4864 $ctext insert end "Parent:\t"
4865 $ctext insert end $id [list link link0]
4866 $ctext tag bind link0 <1> [list selbyid $id]
4867 set info $commitinfo($id)
4868 $ctext insert end "\n\t[lindex $info 0]\n"
4869 $ctext insert end "\tAuthor:\t[lindex $info 1]\n"
4870 set date [formatdate [lindex $info 2]]
4871 $ctext insert end "\tDate:\t$date\n"
4872 set kids $children($curview,$id)
4874 $ctext insert end "\nChildren:"
4876 foreach child $kids {
4878 if {![info exists commitinfo($child)] && ![getcommit $child]} continue
4879 set info $commitinfo($child)
4880 $ctext insert end "\n\t"
4881 $ctext insert end $child [list link link$i]
4882 $ctext tag bind link$i <1> [list selbyid $child]
4883 $ctext insert end "\n\t[lindex $info 0]"
4884 $ctext insert end "\n\tAuthor:\t[lindex $info 1]"
4885 set date [formatdate [lindex $info 2]]
4886 $ctext insert end "\n\tDate:\t$date\n"
4889 $ctext conf -state disabled
4893 proc normalline {} {
4895 if {[info exists thickerline]} {
4903 global commitrow curview
4904 if {[info exists commitrow($curview,$id)]} {
4905 selectline $commitrow($curview,$id) 1
4911 if {![info exists startmstime]} {
4912 set startmstime [clock clicks -milliseconds]
4914 return [format "%.3f" [expr {([clock click -milliseconds] - $startmstime) / 1000.0}]]
4917 proc rowmenu {x y id} {
4918 global rowctxmenu commitrow selectedline rowmenuid curview
4920 if {![info exists selectedline]
4921 || $commitrow($curview,$id) eq $selectedline} {
4926 $rowctxmenu entryconfigure "Diff this*" -state $state
4927 $rowctxmenu entryconfigure "Diff selected*" -state $state
4928 $rowctxmenu entryconfigure "Make patch" -state $state
4930 tk_popup $rowctxmenu $x $y
4933 proc diffvssel {dirn} {
4934 global rowmenuid selectedline displayorder
4936 if {![info exists selectedline]} return
4938 set oldid [lindex $displayorder $selectedline]
4939 set newid $rowmenuid
4941 set oldid $rowmenuid
4942 set newid [lindex $displayorder $selectedline]
4944 addtohistory [list doseldiff $oldid $newid]
4945 doseldiff $oldid $newid
4948 proc doseldiff {oldid newid} {
4952 $ctext conf -state normal
4955 $ctext insert end "From "
4956 $ctext tag conf link -foreground blue -underline 1
4957 $ctext tag bind link <Enter> { %W configure -cursor hand2 }
4958 $ctext tag bind link <Leave> { %W configure -cursor $curtextcursor }
4959 $ctext tag bind link0 <1> [list selbyid $oldid]
4960 $ctext insert end $oldid [list link link0]
4961 $ctext insert end "\n "
4962 $ctext insert end [lindex $commitinfo($oldid) 0]
4963 $ctext insert end "\n\nTo "
4964 $ctext tag bind link1 <1> [list selbyid $newid]
4965 $ctext insert end $newid [list link link1]
4966 $ctext insert end "\n "
4967 $ctext insert end [lindex $commitinfo($newid) 0]
4968 $ctext insert end "\n"
4969 $ctext conf -state disabled
4970 $ctext tag delete Comments
4971 $ctext tag remove found 1.0 end
4972 startdiff [list $oldid $newid]
4976 global rowmenuid currentid commitinfo patchtop patchnum
4978 if {![info exists currentid]} return
4979 set oldid $currentid
4980 set oldhead [lindex $commitinfo($oldid) 0]
4981 set newid $rowmenuid
4982 set newhead [lindex $commitinfo($newid) 0]
4985 catch {destroy $top}
4987 label $top.title -text "Generate patch"
4988 grid $top.title - -pady 10
4989 label $top.from -text "From:"
4990 entry $top.fromsha1 -width 40 -relief flat
4991 $top.fromsha1 insert 0 $oldid
4992 $top.fromsha1 conf -state readonly
4993 grid $top.from $top.fromsha1 -sticky w
4994 entry $top.fromhead -width 60 -relief flat
4995 $top.fromhead insert 0 $oldhead
4996 $top.fromhead conf -state readonly
4997 grid x $top.fromhead -sticky w
4998 label $top.to -text "To:"
4999 entry $top.tosha1 -width 40 -relief flat
5000 $top.tosha1 insert 0 $newid
5001 $top.tosha1 conf -state readonly
5002 grid $top.to $top.tosha1 -sticky w
5003 entry $top.tohead -width 60 -relief flat
5004 $top.tohead insert 0 $newhead
5005 $top.tohead conf -state readonly
5006 grid x $top.tohead -sticky w
5007 button $top.rev -text "Reverse" -command mkpatchrev -padx 5
5008 grid $top.rev x -pady 10
5009 label $top.flab -text "Output file:"
5010 entry $top.fname -width 60
5011 $top.fname insert 0 [file normalize "patch$patchnum.patch"]
5013 grid $top.flab $top.fname -sticky w
5015 button $top.buts.gen -text "Generate" -command mkpatchgo
5016 button $top.buts.can -text "Cancel" -command mkpatchcan
5017 grid $top.buts.gen $top.buts.can
5018 grid columnconfigure $top.buts 0 -weight 1 -uniform a
5019 grid columnconfigure $top.buts 1 -weight 1 -uniform a
5020 grid $top.buts - -pady 10 -sticky ew
5024 proc mkpatchrev {} {
5027 set oldid [$patchtop.fromsha1 get]
5028 set oldhead [$patchtop.fromhead get]
5029 set newid [$patchtop.tosha1 get]
5030 set newhead [$patchtop.tohead get]
5031 foreach e [list fromsha1 fromhead tosha1 tohead] \
5032 v [list $newid $newhead $oldid $oldhead] {
5033 $patchtop.$e conf -state normal
5034 $patchtop.$e delete 0 end
5035 $patchtop.$e insert 0 $v
5036 $patchtop.$e conf -state readonly
5043 set oldid [$patchtop.fromsha1 get]
5044 set newid [$patchtop.tosha1 get]
5045 set fname [$patchtop.fname get]
5046 if {[catch {exec git diff-tree -p $oldid $newid >$fname &} err]} {
5047 error_popup "Error creating patch: $err"
5049 catch {destroy $patchtop}
5053 proc mkpatchcan {} {
5056 catch {destroy $patchtop}
5061 global rowmenuid mktagtop commitinfo
5065 catch {destroy $top}
5067 label $top.title -text "Create tag"
5068 grid $top.title - -pady 10
5069 label $top.id -text "ID:"
5070 entry $top.sha1 -width 40 -relief flat
5071 $top.sha1 insert 0 $rowmenuid
5072 $top.sha1 conf -state readonly
5073 grid $top.id $top.sha1 -sticky w
5074 entry $top.head -width 60 -relief flat
5075 $top.head insert 0 [lindex $commitinfo($rowmenuid) 0]
5076 $top.head conf -state readonly
5077 grid x $top.head -sticky w
5078 label $top.tlab -text "Tag name:"
5079 entry $top.tag -width 60
5080 grid $top.tlab $top.tag -sticky w
5082 button $top.buts.gen -text "Create" -command mktaggo
5083 button $top.buts.can -text "Cancel" -command mktagcan
5084 grid $top.buts.gen $top.buts.can
5085 grid columnconfigure $top.buts 0 -weight 1 -uniform a
5086 grid columnconfigure $top.buts 1 -weight 1 -uniform a
5087 grid $top.buts - -pady 10 -sticky ew
5092 global mktagtop env tagids idtags
5094 set id [$mktagtop.sha1 get]
5095 set tag [$mktagtop.tag get]
5097 error_popup "No tag name specified"
5100 if {[info exists tagids($tag)]} {
5101 error_popup "Tag \"$tag\" already exists"
5106 set fname [file join $dir "refs/tags" $tag]
5107 set f [open $fname w]
5111 error_popup "Error creating tag: $err"
5115 set tagids($tag) $id
5116 lappend idtags($id) $tag
5121 proc redrawtags {id} {
5122 global canv linehtag commitrow idpos selectedline curview
5123 global mainfont canvxmax
5125 if {![info exists commitrow($curview,$id)]} return
5126 drawcmitrow $commitrow($curview,$id)
5127 $canv delete tag.$id
5128 set xt [eval drawtags $id $idpos($id)]
5129 $canv coords $linehtag($commitrow($curview,$id)) $xt [lindex $idpos($id) 2]
5130 set text [$canv itemcget $linehtag($commitrow($curview,$id)) -text]
5131 set xr [expr {$xt + [font measure $mainfont $text]}]
5132 if {$xr > $canvxmax} {
5136 if {[info exists selectedline]
5137 && $selectedline == $commitrow($curview,$id)} {
5138 selectline $selectedline 0
5145 catch {destroy $mktagtop}
5154 proc writecommit {} {
5155 global rowmenuid wrcomtop commitinfo wrcomcmd
5157 set top .writecommit
5159 catch {destroy $top}
5161 label $top.title -text "Write commit to file"
5162 grid $top.title - -pady 10
5163 label $top.id -text "ID:"
5164 entry $top.sha1 -width 40 -relief flat
5165 $top.sha1 insert 0 $rowmenuid
5166 $top.sha1 conf -state readonly
5167 grid $top.id $top.sha1 -sticky w
5168 entry $top.head -width 60 -relief flat
5169 $top.head insert 0 [lindex $commitinfo($rowmenuid) 0]
5170 $top.head conf -state readonly
5171 grid x $top.head -sticky w
5172 label $top.clab -text "Command:"
5173 entry $top.cmd -width 60 -textvariable wrcomcmd
5174 grid $top.clab $top.cmd -sticky w -pady 10
5175 label $top.flab -text "Output file:"
5176 entry $top.fname -width 60
5177 $top.fname insert 0 [file normalize "commit-[string range $rowmenuid 0 6]"]
5178 grid $top.flab $top.fname -sticky w
5180 button $top.buts.gen -text "Write" -command wrcomgo
5181 button $top.buts.can -text "Cancel" -command wrcomcan
5182 grid $top.buts.gen $top.buts.can
5183 grid columnconfigure $top.buts 0 -weight 1 -uniform a
5184 grid columnconfigure $top.buts 1 -weight 1 -uniform a
5185 grid $top.buts - -pady 10 -sticky ew
5192 set id [$wrcomtop.sha1 get]
5193 set cmd "echo $id | [$wrcomtop.cmd get]"
5194 set fname [$wrcomtop.fname get]
5195 if {[catch {exec sh -c $cmd >$fname &} err]} {
5196 error_popup "Error writing commit: $err"
5198 catch {destroy $wrcomtop}
5205 catch {destroy $wrcomtop}
5210 global rowmenuid mkbrtop
5213 catch {destroy $top}
5215 label $top.title -text "Create new branch"
5216 grid $top.title - -pady 10
5217 label $top.id -text "ID:"
5218 entry $top.sha1 -width 40 -relief flat
5219 $top.sha1 insert 0 $rowmenuid
5220 $top.sha1 conf -state readonly
5221 grid $top.id $top.sha1 -sticky w
5222 label $top.nlab -text "Name:"
5223 entry $top.name -width 40
5224 grid $top.nlab $top.name -sticky w
5226 button $top.buts.go -text "Create" -command [list mkbrgo $top]
5227 button $top.buts.can -text "Cancel" -command "catch {destroy $top}"
5228 grid $top.buts.go $top.buts.can
5229 grid columnconfigure $top.buts 0 -weight 1 -uniform a
5230 grid columnconfigure $top.buts 1 -weight 1 -uniform a
5231 grid $top.buts - -pady 10 -sticky ew
5236 global headids idheads
5238 set name [$top.name get]
5239 set id [$top.sha1 get]
5241 error_popup "Please specify a name for the new branch"
5244 catch {destroy $top}
5248 exec git branch $name $id
5254 # XXX should update list of heads displayed for selected commit
5260 proc cherrypick {} {
5261 global rowmenuid curview commitrow
5262 global mainhead desc_heads anc_tags desc_tags allparents allchildren
5264 if {[info exists desc_heads($rowmenuid)]
5265 && [lsearch -exact $desc_heads($rowmenuid) $mainhead] >= 0} {
5266 set ok [confirm_popup "Commit [string range $rowmenuid 0 7] is already\
5267 included in branch $mainhead -- really re-apply it?"]
5272 set oldhead [exec git rev-parse HEAD]
5273 # Unfortunately git-cherry-pick writes stuff to stderr even when
5274 # no error occurs, and exec takes that as an indication of error...
5275 if {[catch {exec sh -c "git cherry-pick -r $rowmenuid 2>&1"} err]} {
5280 set newhead [exec git rev-parse HEAD]
5281 if {$newhead eq $oldhead} {
5283 error_popup "No changes committed"
5286 set allparents($newhead) $oldhead
5287 lappend allchildren($oldhead) $newhead
5288 set desc_heads($newhead) $mainhead
5289 if {[info exists anc_tags($oldhead)]} {
5290 set anc_tags($newhead) $anc_tags($oldhead)
5292 set desc_tags($newhead) {}
5293 if {[info exists commitrow($curview,$oldhead)]} {
5294 insertrow $commitrow($curview,$oldhead) $newhead
5295 if {$mainhead ne {}} {
5296 movedhead $newhead $mainhead
5304 # context menu for a head
5305 proc headmenu {x y id head} {
5306 global headmenuid headmenuhead headctxmenu
5309 set headmenuhead $head
5310 tk_popup $headctxmenu $x $y
5314 global headmenuid headmenuhead mainhead headids
5316 # check the tree is clean first??
5317 set oldmainhead $mainhead
5321 exec git checkout $headmenuhead
5327 set mainhead $headmenuhead
5328 if {[info exists headids($oldmainhead)]} {
5329 redrawtags $headids($oldmainhead)
5331 redrawtags $headmenuid
5336 global desc_heads headmenuid headmenuhead mainhead
5337 global headids idheads
5339 set head $headmenuhead
5341 if {$head eq $mainhead} {
5342 error_popup "Cannot delete the currently checked-out branch"
5345 if {$desc_heads($id) eq $head} {
5346 # the stuff on this branch isn't on any other branch
5347 if {![confirm_popup "The commits on branch $head aren't on any other\
5348 branch.\nReally delete branch $head?"]} return
5352 if {[catch {exec git branch -D $head} err]} {
5357 removedhead $id $head
5362 # Stuff for finding nearby tags
5363 proc getallcommits {} {
5364 global allcstart allcommits allcfd allids
5367 set fd [open [concat | git rev-list --all --topo-order --parents] r]
5369 fconfigure $fd -blocking 0
5370 set allcommits "reading"
5375 proc discardallcommits {} {
5376 global allparents allchildren allcommits allcfd
5377 global desc_tags anc_tags alldtags tagisdesc allids desc_heads
5379 if {![info exists allcommits]} return
5380 if {$allcommits eq "reading"} {
5381 catch {close $allcfd}
5383 foreach v {allcommits allchildren allparents allids desc_tags anc_tags
5384 alldtags tagisdesc desc_heads} {
5389 proc restartgetall {fd} {
5392 fileevent $fd readable [list getallclines $fd]
5393 set allcstart [clock clicks -milliseconds]
5396 proc combine_dtags {l1 l2} {
5397 global tagisdesc notfirstd
5399 set res [lsort -unique [concat $l1 $l2]]
5400 for {set i 0} {$i < [llength $res]} {incr i} {
5401 set x [lindex $res $i]
5402 for {set j [expr {$i+1}]} {$j < [llength $res]} {} {
5403 set y [lindex $res $j]
5404 if {[info exists tagisdesc($x,$y)]} {
5405 if {$tagisdesc($x,$y) > 0} {
5406 # x is a descendent of y, exclude x
5407 set res [lreplace $res $i $i]
5411 # y is a descendent of x, exclude y
5412 set res [lreplace $res $j $j]
5415 # no relation, keep going
5423 proc combine_atags {l1 l2} {
5426 set res [lsort -unique [concat $l1 $l2]]
5427 for {set i 0} {$i < [llength $res]} {incr i} {
5428 set x [lindex $res $i]
5429 for {set j [expr {$i+1}]} {$j < [llength $res]} {} {
5430 set y [lindex $res $j]
5431 if {[info exists tagisdesc($x,$y)]} {
5432 if {$tagisdesc($x,$y) < 0} {
5433 # x is an ancestor of y, exclude x
5434 set res [lreplace $res $i $i]
5438 # y is an ancestor of x, exclude y
5439 set res [lreplace $res $j $j]
5442 # no relation, keep going
5450 proc forward_pass {id children} {
5451 global idtags desc_tags idheads desc_heads alldtags tagisdesc
5455 foreach child $children {
5456 if {[info exists idtags($child)]} {
5457 set ctags [list $child]
5459 set ctags $desc_tags($child)
5463 } elseif {$ctags ne $dtags} {
5464 set dtags [combine_dtags $dtags $ctags]
5466 set cheads $desc_heads($child)
5467 if {$dheads eq {}} {
5469 } elseif {$cheads ne $dheads} {
5470 set dheads [lsort -unique [concat $dheads $cheads]]
5473 set desc_tags($id) $dtags
5474 if {[info exists idtags($id)]} {
5476 foreach tag $dtags {
5477 set adt [concat $adt $alldtags($tag)]
5479 set adt [lsort -unique $adt]
5480 set alldtags($id) $adt
5482 set tagisdesc($id,$tag) -1
5483 set tagisdesc($tag,$id) 1
5486 if {[info exists idheads($id)]} {
5487 set dheads [concat $dheads $idheads($id)]
5489 set desc_heads($id) $dheads
5492 proc getallclines {fd} {
5493 global allparents allchildren allcommits allcstart
5494 global desc_tags anc_tags idtags tagisdesc allids
5495 global idheads travindex
5497 while {[gets $fd line] >= 0} {
5498 set id [lindex $line 0]
5500 set olds [lrange $line 1 end]
5501 set allparents($id) $olds
5502 if {![info exists allchildren($id)]} {
5503 set allchildren($id) {}
5506 lappend allchildren($p) $id
5508 # compute nearest tagged descendents as we go
5509 # also compute descendent heads
5510 forward_pass $id $allchildren($id)
5511 if {[clock clicks -milliseconds] - $allcstart >= 50} {
5512 fileevent $fd readable {}
5513 after idle restartgetall $fd
5518 set travindex [llength $allids]
5519 set allcommits "traversing"
5520 after idle restartatags
5521 if {[catch {close $fd} err]} {
5522 error_popup "Error reading full commit graph: $err.\n\
5523 Results may be incomplete."
5528 # walk backward through the tree and compute nearest tagged ancestors
5529 proc restartatags {} {
5530 global allids allparents idtags anc_tags travindex
5532 set t0 [clock clicks -milliseconds]
5534 while {[incr i -1] >= 0} {
5535 set id [lindex $allids $i]
5537 foreach p $allparents($id) {
5538 if {[info exists idtags($p)]} {
5541 set ptags $anc_tags($p)
5545 } elseif {$ptags ne $atags} {
5546 set atags [combine_atags $atags $ptags]
5549 set anc_tags($id) $atags
5550 if {[clock clicks -milliseconds] - $t0 >= 50} {
5552 after idle restartatags
5556 set allcommits "done"
5562 # update the desc_tags and anc_tags arrays for a new tag just added
5563 proc addedtag {id} {
5564 global desc_tags anc_tags allparents allchildren allcommits
5565 global idtags tagisdesc alldtags
5567 if {![info exists desc_tags($id)]} return
5568 set adt $desc_tags($id)
5569 foreach t $desc_tags($id) {
5570 set adt [concat $adt $alldtags($t)]
5572 set adt [lsort -unique $adt]
5573 set alldtags($id) $adt
5575 set tagisdesc($id,$t) -1
5576 set tagisdesc($t,$id) 1
5578 if {[info exists anc_tags($id)]} {
5579 set todo $anc_tags($id)
5580 while {$todo ne {}} {
5581 set do [lindex $todo 0]
5582 set todo [lrange $todo 1 end]
5583 if {[info exists tagisdesc($id,$do)]} continue
5584 set tagisdesc($do,$id) -1
5585 set tagisdesc($id,$do) 1
5586 if {[info exists anc_tags($do)]} {
5587 set todo [concat $todo $anc_tags($do)]
5592 set lastold $desc_tags($id)
5593 set lastnew [list $id]
5596 set todo $allparents($id)
5597 while {$todo ne {}} {
5598 set do [lindex $todo 0]
5599 set todo [lrange $todo 1 end]
5600 if {![info exists desc_tags($do)]} continue
5601 if {$desc_tags($do) ne $lastold} {
5602 set lastold $desc_tags($do)
5603 set lastnew [combine_dtags $lastold [list $id]]
5606 if {$lastold eq $lastnew} continue
5607 set desc_tags($do) $lastnew
5609 if {![info exists idtags($do)]} {
5610 set todo [concat $todo $allparents($do)]
5614 if {![info exists anc_tags($id)]} return
5615 set lastold $anc_tags($id)
5616 set lastnew [list $id]
5619 set todo $allchildren($id)
5620 while {$todo ne {}} {
5621 set do [lindex $todo 0]
5622 set todo [lrange $todo 1 end]
5623 if {![info exists anc_tags($do)]} continue
5624 if {$anc_tags($do) ne $lastold} {
5625 set lastold $anc_tags($do)
5626 set lastnew [combine_atags $lastold [list $id]]
5629 if {$lastold eq $lastnew} continue
5630 set anc_tags($do) $lastnew
5632 if {![info exists idtags($do)]} {
5633 set todo [concat $todo $allchildren($do)]
5638 # update the desc_heads array for a new head just added
5639 proc addedhead {hid head} {
5640 global desc_heads allparents headids idheads
5642 set headids($head) $hid
5643 lappend idheads($hid) $head
5645 set todo [list $hid]
5646 while {$todo ne {}} {
5647 set do [lindex $todo 0]
5648 set todo [lrange $todo 1 end]
5649 if {![info exists desc_heads($do)] ||
5650 [lsearch -exact $desc_heads($do) $head] >= 0} continue
5651 set oldheads $desc_heads($do)
5652 lappend desc_heads($do) $head
5653 set heads $desc_heads($do)
5655 set p $allparents($do)
5656 if {[llength $p] != 1 || ![info exists desc_heads($p)] ||
5657 $desc_heads($p) ne $oldheads} break
5659 set desc_heads($do) $heads
5661 set todo [concat $todo $p]
5665 # update the desc_heads array for a head just removed
5666 proc removedhead {hid head} {
5667 global desc_heads allparents headids idheads
5669 unset headids($head)
5670 if {$idheads($hid) eq $head} {
5673 set i [lsearch -exact $idheads($hid) $head]
5675 set idheads($hid) [lreplace $idheads($hid) $i $i]
5679 set todo [list $hid]
5680 while {$todo ne {}} {
5681 set do [lindex $todo 0]
5682 set todo [lrange $todo 1 end]
5683 if {![info exists desc_heads($do)]} continue
5684 set i [lsearch -exact $desc_heads($do) $head]
5685 if {$i < 0} continue
5686 set oldheads $desc_heads($do)
5687 set heads [lreplace $desc_heads($do) $i $i]
5689 set desc_heads($do) $heads
5690 set p $allparents($do)
5691 if {[llength $p] != 1 || ![info exists desc_heads($p)] ||
5692 $desc_heads($p) ne $oldheads} break
5695 set todo [concat $todo $p]
5699 # update things for a head moved to a child of its previous location
5700 proc movedhead {id name} {
5701 global headids idheads
5703 set oldid $headids($name)
5704 set headids($name) $id
5705 if {$idheads($oldid) eq $name} {
5706 unset idheads($oldid)
5708 set i [lsearch -exact $idheads($oldid) $name]
5710 set idheads($oldid) [lreplace $idheads($oldid) $i $i]
5713 lappend idheads($id) $name
5716 proc changedrefs {} {
5717 global desc_heads desc_tags anc_tags allcommits allids
5718 global allchildren allparents idtags travindex
5720 if {![info exists allcommits]} return
5721 catch {unset desc_heads}
5722 catch {unset desc_tags}
5723 catch {unset anc_tags}
5724 catch {unset alldtags}
5725 catch {unset tagisdesc}
5726 foreach id $allids {
5727 forward_pass $id $allchildren($id)
5729 if {$allcommits ne "reading"} {
5730 set travindex [llength $allids]
5731 if {$allcommits ne "traversing"} {
5732 set allcommits "traversing"
5733 after idle restartatags
5738 proc rereadrefs {} {
5739 global idtags idheads idotherrefs mainhead
5741 set refids [concat [array names idtags] \
5742 [array names idheads] [array names idotherrefs]]
5743 foreach id $refids {
5744 if {![info exists ref($id)]} {
5745 set ref($id) [listrefs $id]
5748 set oldmainhead $mainhead
5751 set refids [lsort -unique [concat $refids [array names idtags] \
5752 [array names idheads] [array names idotherrefs]]]
5753 foreach id $refids {
5754 set v [listrefs $id]
5755 if {![info exists ref($id)] || $ref($id) != $v ||
5756 ($id eq $oldmainhead && $id ne $mainhead) ||
5757 ($id eq $mainhead && $id ne $oldmainhead)} {
5763 proc listrefs {id} {
5764 global idtags idheads idotherrefs
5767 if {[info exists idtags($id)]} {
5771 if {[info exists idheads($id)]} {
5775 if {[info exists idotherrefs($id)]} {
5776 set z $idotherrefs($id)
5778 return [list $x $y $z]
5781 proc showtag {tag isnew} {
5782 global ctext tagcontents tagids linknum
5785 addtohistory [list showtag $tag 0]
5787 $ctext conf -state normal
5790 if {[info exists tagcontents($tag)]} {
5791 set text $tagcontents($tag)
5793 set text "Tag: $tag\nId: $tagids($tag)"
5795 appendwithlinks $text {}
5796 $ctext conf -state disabled
5808 global maxwidth maxgraphpct diffopts
5809 global oldprefs prefstop showneartags
5810 global bgcolor fgcolor ctext diffcolors
5814 if {[winfo exists $top]} {
5818 foreach v {maxwidth maxgraphpct diffopts showneartags} {
5819 set oldprefs($v) [set $v]
5822 wm title $top "Gitk preferences"
5823 label $top.ldisp -text "Commit list display options"
5824 grid $top.ldisp - -sticky w -pady 10
5825 label $top.spacer -text " "
5826 label $top.maxwidthl -text "Maximum graph width (lines)" \
5828 spinbox $top.maxwidth -from 0 -to 100 -width 4 -textvariable maxwidth
5829 grid $top.spacer $top.maxwidthl $top.maxwidth -sticky w
5830 label $top.maxpctl -text "Maximum graph width (% of pane)" \
5832 spinbox $top.maxpct -from 1 -to 100 -width 4 -textvariable maxgraphpct
5833 grid x $top.maxpctl $top.maxpct -sticky w
5835 label $top.ddisp -text "Diff display options"
5836 grid $top.ddisp - -sticky w -pady 10
5837 label $top.diffoptl -text "Options for diff program" \
5839 entry $top.diffopt -width 20 -textvariable diffopts
5840 grid x $top.diffoptl $top.diffopt -sticky w
5842 label $top.ntag.l -text "Display nearby tags" -font optionfont
5843 checkbutton $top.ntag.b -variable showneartags
5844 pack $top.ntag.b $top.ntag.l -side left
5845 grid x $top.ntag -sticky w
5847 label $top.cdisp -text "Colors: press to choose"
5848 grid $top.cdisp - -sticky w -pady 10
5849 label $top.bg -padx 40 -relief sunk -background $bgcolor
5850 button $top.bgbut -text "Background" -font optionfont \
5851 -command [list choosecolor bgcolor 0 $top.bg background setbg]
5852 grid x $top.bgbut $top.bg -sticky w
5853 label $top.fg -padx 40 -relief sunk -background $fgcolor
5854 button $top.fgbut -text "Foreground" -font optionfont \
5855 -command [list choosecolor fgcolor 0 $top.fg foreground setfg]
5856 grid x $top.fgbut $top.fg -sticky w
5857 label $top.diffold -padx 40 -relief sunk -background [lindex $diffcolors 0]
5858 button $top.diffoldbut -text "Diff: old lines" -font optionfont \
5859 -command [list choosecolor diffcolors 0 $top.diffold "diff old lines" \
5860 [list $ctext tag conf d0 -foreground]]
5861 grid x $top.diffoldbut $top.diffold -sticky w
5862 label $top.diffnew -padx 40 -relief sunk -background [lindex $diffcolors 1]
5863 button $top.diffnewbut -text "Diff: new lines" -font optionfont \
5864 -command [list choosecolor diffcolors 1 $top.diffnew "diff new lines" \
5865 [list $ctext tag conf d1 -foreground]]
5866 grid x $top.diffnewbut $top.diffnew -sticky w
5867 label $top.hunksep -padx 40 -relief sunk -background [lindex $diffcolors 2]
5868 button $top.hunksepbut -text "Diff: hunk header" -font optionfont \
5869 -command [list choosecolor diffcolors 2 $top.hunksep \
5870 "diff hunk header" \
5871 [list $ctext tag conf hunksep -foreground]]
5872 grid x $top.hunksepbut $top.hunksep -sticky w
5875 button $top.buts.ok -text "OK" -command prefsok
5876 button $top.buts.can -text "Cancel" -command prefscan
5877 grid $top.buts.ok $top.buts.can
5878 grid columnconfigure $top.buts 0 -weight 1 -uniform a
5879 grid columnconfigure $top.buts 1 -weight 1 -uniform a
5880 grid $top.buts - - -pady 10 -sticky ew
5883 proc choosecolor {v vi w x cmd} {
5886 set c [tk_chooseColor -initialcolor [lindex [set $v] $vi] \
5887 -title "Gitk: choose color for $x"]
5888 if {$c eq {}} return
5889 $w conf -background $c
5898 $w conf -background $c
5906 $w conf -foreground $c
5908 allcanvs itemconf text -fill $c
5909 $canv itemconf circle -outline $c
5913 global maxwidth maxgraphpct diffopts
5914 global oldprefs prefstop showneartags
5916 foreach v {maxwidth maxgraphpct diffopts showneartags} {
5917 set $v $oldprefs($v)
5919 catch {destroy $prefstop}
5924 global maxwidth maxgraphpct
5925 global oldprefs prefstop showneartags
5927 catch {destroy $prefstop}
5929 if {$maxwidth != $oldprefs(maxwidth)
5930 || $maxgraphpct != $oldprefs(maxgraphpct)} {
5932 } elseif {$showneartags != $oldprefs(showneartags)} {
5937 proc formatdate {d} {
5938 return [clock format $d -format "%Y-%m-%d %H:%M:%S"]
5941 # This list of encoding names and aliases is distilled from
5942 # http://www.iana.org/assignments/character-sets.
5943 # Not all of them are supported by Tcl.
5944 set encoding_aliases {
5945 { ANSI_X3.4-1968 iso-ir-6 ANSI_X3.4-1986 ISO_646.irv:1991 ASCII
5946 ISO646-US US-ASCII us IBM367 cp367 csASCII }
5947 { ISO-10646-UTF-1 csISO10646UTF1 }
5948 { ISO_646.basic:1983 ref csISO646basic1983 }
5949 { INVARIANT csINVARIANT }
5950 { ISO_646.irv:1983 iso-ir-2 irv csISO2IntlRefVersion }
5951 { BS_4730 iso-ir-4 ISO646-GB gb uk csISO4UnitedKingdom }
5952 { NATS-SEFI iso-ir-8-1 csNATSSEFI }
5953 { NATS-SEFI-ADD iso-ir-8-2 csNATSSEFIADD }
5954 { NATS-DANO iso-ir-9-1 csNATSDANO }
5955 { NATS-DANO-ADD iso-ir-9-2 csNATSDANOADD }
5956 { SEN_850200_B iso-ir-10 FI ISO646-FI ISO646-SE se csISO10Swedish }
5957 { SEN_850200_C iso-ir-11 ISO646-SE2 se2 csISO11SwedishForNames }
5958 { KS_C_5601-1987 iso-ir-149 KS_C_5601-1989 KSC_5601 korean csKSC56011987 }
5959 { ISO-2022-KR csISO2022KR }
5961 { ISO-2022-JP csISO2022JP }
5962 { ISO-2022-JP-2 csISO2022JP2 }
5963 { JIS_C6220-1969-jp JIS_C6220-1969 iso-ir-13 katakana x0201-7
5965 { JIS_C6220-1969-ro iso-ir-14 jp ISO646-JP csISO14JISC6220ro }
5966 { IT iso-ir-15 ISO646-IT csISO15Italian }
5967 { PT iso-ir-16 ISO646-PT csISO16Portuguese }
5968 { ES iso-ir-17 ISO646-ES csISO17Spanish }
5969 { greek7-old iso-ir-18 csISO18Greek7Old }
5970 { latin-greek iso-ir-19 csISO19LatinGreek }
5971 { DIN_66003 iso-ir-21 de ISO646-DE csISO21German }
5972 { NF_Z_62-010_(1973) iso-ir-25 ISO646-FR1 csISO25French }
5973 { Latin-greek-1 iso-ir-27 csISO27LatinGreek1 }
5974 { ISO_5427 iso-ir-37 csISO5427Cyrillic }
5975 { JIS_C6226-1978 iso-ir-42 csISO42JISC62261978 }
5976 { BS_viewdata iso-ir-47 csISO47BSViewdata }
5977 { INIS iso-ir-49 csISO49INIS }
5978 { INIS-8 iso-ir-50 csISO50INIS8 }
5979 { INIS-cyrillic iso-ir-51 csISO51INISCyrillic }
5980 { ISO_5427:1981 iso-ir-54 ISO5427Cyrillic1981 }
5981 { ISO_5428:1980 iso-ir-55 csISO5428Greek }
5982 { GB_1988-80 iso-ir-57 cn ISO646-CN csISO57GB1988 }
5983 { GB_2312-80 iso-ir-58 chinese csISO58GB231280 }
5984 { NS_4551-1 iso-ir-60 ISO646-NO no csISO60DanishNorwegian
5986 { NS_4551-2 ISO646-NO2 iso-ir-61 no2 csISO61Norwegian2 }
5987 { NF_Z_62-010 iso-ir-69 ISO646-FR fr csISO69French }
5988 { videotex-suppl iso-ir-70 csISO70VideotexSupp1 }
5989 { PT2 iso-ir-84 ISO646-PT2 csISO84Portuguese2 }
5990 { ES2 iso-ir-85 ISO646-ES2 csISO85Spanish2 }
5991 { MSZ_7795.3 iso-ir-86 ISO646-HU hu csISO86Hungarian }
5992 { JIS_C6226-1983 iso-ir-87 x0208 JIS_X0208-1983 csISO87JISX0208 }
5993 { greek7 iso-ir-88 csISO88Greek7 }
5994 { ASMO_449 ISO_9036 arabic7 iso-ir-89 csISO89ASMO449 }
5995 { iso-ir-90 csISO90 }
5996 { JIS_C6229-1984-a iso-ir-91 jp-ocr-a csISO91JISC62291984a }
5997 { JIS_C6229-1984-b iso-ir-92 ISO646-JP-OCR-B jp-ocr-b
5998 csISO92JISC62991984b }
5999 { JIS_C6229-1984-b-add iso-ir-93 jp-ocr-b-add csISO93JIS62291984badd }
6000 { JIS_C6229-1984-hand iso-ir-94 jp-ocr-hand csISO94JIS62291984hand }
6001 { JIS_C6229-1984-hand-add iso-ir-95 jp-ocr-hand-add
6002 csISO95JIS62291984handadd }
6003 { JIS_C6229-1984-kana iso-ir-96 csISO96JISC62291984kana }
6004 { ISO_2033-1983 iso-ir-98 e13b csISO2033 }
6005 { ANSI_X3.110-1983 iso-ir-99 CSA_T500-1983 NAPLPS csISO99NAPLPS }
6006 { ISO_8859-1:1987 iso-ir-100 ISO_8859-1 ISO-8859-1 latin1 l1 IBM819
6008 { ISO_8859-2:1987 iso-ir-101 ISO_8859-2 ISO-8859-2 latin2 l2 csISOLatin2 }
6009 { T.61-7bit iso-ir-102 csISO102T617bit }
6010 { T.61-8bit T.61 iso-ir-103 csISO103T618bit }
6011 { ISO_8859-3:1988 iso-ir-109 ISO_8859-3 ISO-8859-3 latin3 l3 csISOLatin3 }
6012 { ISO_8859-4:1988 iso-ir-110 ISO_8859-4 ISO-8859-4 latin4 l4 csISOLatin4 }
6013 { ECMA-cyrillic iso-ir-111 KOI8-E csISO111ECMACyrillic }
6014 { CSA_Z243.4-1985-1 iso-ir-121 ISO646-CA csa7-1 ca csISO121Canadian1 }
6015 { CSA_Z243.4-1985-2 iso-ir-122 ISO646-CA2 csa7-2 csISO122Canadian2 }
6016 { CSA_Z243.4-1985-gr iso-ir-123 csISO123CSAZ24341985gr }
6017 { ISO_8859-6:1987 iso-ir-127 ISO_8859-6 ISO-8859-6 ECMA-114 ASMO-708
6018 arabic csISOLatinArabic }
6019 { ISO_8859-6-E csISO88596E ISO-8859-6-E }
6020 { ISO_8859-6-I csISO88596I ISO-8859-6-I }
6021 { ISO_8859-7:1987 iso-ir-126 ISO_8859-7 ISO-8859-7 ELOT_928 ECMA-118
6022 greek greek8 csISOLatinGreek }
6023 { T.101-G2 iso-ir-128 csISO128T101G2 }
6024 { ISO_8859-8:1988 iso-ir-138 ISO_8859-8 ISO-8859-8 hebrew
6026 { ISO_8859-8-E csISO88598E ISO-8859-8-E }
6027 { ISO_8859-8-I csISO88598I ISO-8859-8-I }
6028 { CSN_369103 iso-ir-139 csISO139CSN369103 }
6029 { JUS_I.B1.002 iso-ir-141 ISO646-YU js yu csISO141JUSIB1002 }
6030 { ISO_6937-2-add iso-ir-142 csISOTextComm }
6031 { IEC_P27-1 iso-ir-143 csISO143IECP271 }
6032 { ISO_8859-5:1988 iso-ir-144 ISO_8859-5 ISO-8859-5 cyrillic
6033 csISOLatinCyrillic }
6034 { JUS_I.B1.003-serb iso-ir-146 serbian csISO146Serbian }
6035 { JUS_I.B1.003-mac macedonian iso-ir-147 csISO147Macedonian }
6036 { ISO_8859-9:1989 iso-ir-148 ISO_8859-9 ISO-8859-9 latin5 l5 csISOLatin5 }
6037 { greek-ccitt iso-ir-150 csISO150 csISO150GreekCCITT }
6038 { NC_NC00-10:81 cuba iso-ir-151 ISO646-CU csISO151Cuba }
6039 { ISO_6937-2-25 iso-ir-152 csISO6937Add }
6040 { GOST_19768-74 ST_SEV_358-88 iso-ir-153 csISO153GOST1976874 }
6041 { ISO_8859-supp iso-ir-154 latin1-2-5 csISO8859Supp }
6042 { ISO_10367-box iso-ir-155 csISO10367Box }
6043 { ISO-8859-10 iso-ir-157 l6 ISO_8859-10:1992 csISOLatin6 latin6 }
6044 { latin-lap lap iso-ir-158 csISO158Lap }
6045 { JIS_X0212-1990 x0212 iso-ir-159 csISO159JISX02121990 }
6046 { DS_2089 DS2089 ISO646-DK dk csISO646Danish }
6049 { JIS_X0201 X0201 csHalfWidthKatakana }
6050 { KSC5636 ISO646-KR csKSC5636 }
6051 { ISO-10646-UCS-2 csUnicode }
6052 { ISO-10646-UCS-4 csUCS4 }
6053 { DEC-MCS dec csDECMCS }
6054 { hp-roman8 roman8 r8 csHPRoman8 }
6055 { macintosh mac csMacintosh }
6056 { IBM037 cp037 ebcdic-cp-us ebcdic-cp-ca ebcdic-cp-wt ebcdic-cp-nl
6058 { IBM038 EBCDIC-INT cp038 csIBM038 }
6059 { IBM273 CP273 csIBM273 }
6060 { IBM274 EBCDIC-BE CP274 csIBM274 }
6061 { IBM275 EBCDIC-BR cp275 csIBM275 }
6062 { IBM277 EBCDIC-CP-DK EBCDIC-CP-NO csIBM277 }
6063 { IBM278 CP278 ebcdic-cp-fi ebcdic-cp-se csIBM278 }
6064 { IBM280 CP280 ebcdic-cp-it csIBM280 }
6065 { IBM281 EBCDIC-JP-E cp281 csIBM281 }
6066 { IBM284 CP284 ebcdic-cp-es csIBM284 }
6067 { IBM285 CP285 ebcdic-cp-gb csIBM285 }
6068 { IBM290 cp290 EBCDIC-JP-kana csIBM290 }
6069 { IBM297 cp297 ebcdic-cp-fr csIBM297 }
6070 { IBM420 cp420 ebcdic-cp-ar1 csIBM420 }
6071 { IBM423 cp423 ebcdic-cp-gr csIBM423 }
6072 { IBM424 cp424 ebcdic-cp-he csIBM424 }
6073 { IBM437 cp437 437 csPC8CodePage437 }
6074 { IBM500 CP500 ebcdic-cp-be ebcdic-cp-ch csIBM500 }
6075 { IBM775 cp775 csPC775Baltic }
6076 { IBM850 cp850 850 csPC850Multilingual }
6077 { IBM851 cp851 851 csIBM851 }
6078 { IBM852 cp852 852 csPCp852 }
6079 { IBM855 cp855 855 csIBM855 }
6080 { IBM857 cp857 857 csIBM857 }
6081 { IBM860 cp860 860 csIBM860 }
6082 { IBM861 cp861 861 cp-is csIBM861 }
6083 { IBM862 cp862 862 csPC862LatinHebrew }
6084 { IBM863 cp863 863 csIBM863 }
6085 { IBM864 cp864 csIBM864 }
6086 { IBM865 cp865 865 csIBM865 }
6087 { IBM866 cp866 866 csIBM866 }
6088 { IBM868 CP868 cp-ar csIBM868 }
6089 { IBM869 cp869 869 cp-gr csIBM869 }
6090 { IBM870 CP870 ebcdic-cp-roece ebcdic-cp-yu csIBM870 }
6091 { IBM871 CP871 ebcdic-cp-is csIBM871 }
6092 { IBM880 cp880 EBCDIC-Cyrillic csIBM880 }
6093 { IBM891 cp891 csIBM891 }
6094 { IBM903 cp903 csIBM903 }
6095 { IBM904 cp904 904 csIBBM904 }
6096 { IBM905 CP905 ebcdic-cp-tr csIBM905 }
6097 { IBM918 CP918 ebcdic-cp-ar2 csIBM918 }
6098 { IBM1026 CP1026 csIBM1026 }
6099 { EBCDIC-AT-DE csIBMEBCDICATDE }
6100 { EBCDIC-AT-DE-A csEBCDICATDEA }
6101 { EBCDIC-CA-FR csEBCDICCAFR }
6102 { EBCDIC-DK-NO csEBCDICDKNO }
6103 { EBCDIC-DK-NO-A csEBCDICDKNOA }
6104 { EBCDIC-FI-SE csEBCDICFISE }
6105 { EBCDIC-FI-SE-A csEBCDICFISEA }
6106 { EBCDIC-FR csEBCDICFR }
6107 { EBCDIC-IT csEBCDICIT }
6108 { EBCDIC-PT csEBCDICPT }
6109 { EBCDIC-ES csEBCDICES }
6110 { EBCDIC-ES-A csEBCDICESA }
6111 { EBCDIC-ES-S csEBCDICESS }
6112 { EBCDIC-UK csEBCDICUK }
6113 { EBCDIC-US csEBCDICUS }
6114 { UNKNOWN-8BIT csUnknown8BiT }
6115 { MNEMONIC csMnemonic }
6120 { IBM00858 CCSID00858 CP00858 PC-Multilingual-850+euro }
6121 { IBM00924 CCSID00924 CP00924 ebcdic-Latin9--euro }
6122 { IBM01140 CCSID01140 CP01140 ebcdic-us-37+euro }
6123 { IBM01141 CCSID01141 CP01141 ebcdic-de-273+euro }
6124 { IBM01142 CCSID01142 CP01142 ebcdic-dk-277+euro ebcdic-no-277+euro }
6125 { IBM01143 CCSID01143 CP01143 ebcdic-fi-278+euro ebcdic-se-278+euro }
6126 { IBM01144 CCSID01144 CP01144 ebcdic-it-280+euro }
6127 { IBM01145 CCSID01145 CP01145 ebcdic-es-284+euro }
6128 { IBM01146 CCSID01146 CP01146 ebcdic-gb-285+euro }
6129 { IBM01147 CCSID01147 CP01147 ebcdic-fr-297+euro }
6130 { IBM01148 CCSID01148 CP01148 ebcdic-international-500+euro }
6131 { IBM01149 CCSID01149 CP01149 ebcdic-is-871+euro }
6132 { IBM1047 IBM-1047 }
6133 { PTCP154 csPTCP154 PT154 CP154 Cyrillic-Asian }
6134 { Amiga-1251 Ami1251 Amiga1251 Ami-1251 }
6135 { UNICODE-1-1 csUnicode11 }
6138 { UNICODE-1-1-UTF-7 csUnicode11UTF7 }
6139 { ISO-8859-14 iso-ir-199 ISO_8859-14:1998 ISO_8859-14 latin8 iso-celtic
6141 { ISO-8859-15 ISO_8859-15 Latin-9 }
6142 { ISO-8859-16 iso-ir-226 ISO_8859-16:2001 ISO_8859-16 latin10 l10 }
6143 { GBK CP936 MS936 windows-936 }
6144 { JIS_Encoding csJISEncoding }
6145 { Shift_JIS MS_Kanji csShiftJIS }
6146 { Extended_UNIX_Code_Packed_Format_for_Japanese csEUCPkdFmtJapanese
6148 { Extended_UNIX_Code_Fixed_Width_for_Japanese csEUCFixWidJapanese }
6149 { ISO-10646-UCS-Basic csUnicodeASCII }
6150 { ISO-10646-Unicode-Latin1 csUnicodeLatin1 ISO-10646 }
6151 { ISO-Unicode-IBM-1261 csUnicodeIBM1261 }
6152 { ISO-Unicode-IBM-1268 csUnicodeIBM1268 }
6153 { ISO-Unicode-IBM-1276 csUnicodeIBM1276 }
6154 { ISO-Unicode-IBM-1264 csUnicodeIBM1264 }
6155 { ISO-Unicode-IBM-1265 csUnicodeIBM1265 }
6156 { ISO-8859-1-Windows-3.0-Latin-1 csWindows30Latin1 }
6157 { ISO-8859-1-Windows-3.1-Latin-1 csWindows31Latin1 }
6158 { ISO-8859-2-Windows-Latin-2 csWindows31Latin2 }
6159 { ISO-8859-9-Windows-Latin-5 csWindows31Latin5 }
6160 { Adobe-Standard-Encoding csAdobeStandardEncoding }
6161 { Ventura-US csVenturaUS }
6162 { Ventura-International csVenturaInternational }
6163 { PC8-Danish-Norwegian csPC8DanishNorwegian }
6164 { PC8-Turkish csPC8Turkish }
6165 { IBM-Symbols csIBMSymbols }
6166 { IBM-Thai csIBMThai }
6167 { HP-Legal csHPLegal }
6168 { HP-Pi-font csHPPiFont }
6169 { HP-Math8 csHPMath8 }
6170 { Adobe-Symbol-Encoding csHPPSMath }
6171 { HP-DeskTop csHPDesktop }
6172 { Ventura-Math csVenturaMath }
6173 { Microsoft-Publishing csMicrosoftPublishing }
6174 { Windows-31J csWindows31J }
6179 proc tcl_encoding {enc} {
6180 global encoding_aliases
6181 set names [encoding names]
6182 set lcnames [string tolower $names]
6183 set enc [string tolower $enc]
6184 set i [lsearch -exact $lcnames $enc]
6186 # look for "isonnn" instead of "iso-nnn" or "iso_nnn"
6187 if {[regsub {^iso[-_]} $enc iso encx]} {
6188 set i [lsearch -exact $lcnames $encx]
6192 foreach l $encoding_aliases {
6193 set ll [string tolower $l]
6194 if {[lsearch -exact $ll $enc] < 0} continue
6195 # look through the aliases for one that tcl knows about
6197 set i [lsearch -exact $lcnames $e]
6199 if {[regsub {^iso[-_]} $e iso ex]} {
6200 set i [lsearch -exact $lcnames $ex]
6209 return [lindex $names $i]
6216 set diffopts "-U 5 -p"
6217 set wrcomcmd "git diff-tree --stdin -p --pretty"
6221 set gitencoding [exec git config --get i18n.commitencoding]
6223 if {$gitencoding == ""} {
6224 set gitencoding "utf-8"
6226 set tclencoding [tcl_encoding $gitencoding]
6227 if {$tclencoding == {}} {
6228 puts stderr "Warning: encoding $gitencoding is not supported by Tcl/Tk"
6231 set mainfont {Helvetica 9}
6232 set textfont {Courier 9}
6233 set uifont {Helvetica 9 bold}
6234 set findmergefiles 0
6242 set cmitmode "patch"
6243 set wrapcomment "none"
6246 set colors {green red blue magenta darkgrey brown orange}
6249 set diffcolors {red "#00a000" blue}
6251 catch {source ~/.gitk}
6253 font create optionfont -family sans-serif -size -12
6257 switch -regexp -- $arg {
6259 "^-d" { set datemode 1 }
6261 lappend revtreeargs $arg
6266 # check that we can find a .git directory somewhere...
6268 if {![file isdirectory $gitdir]} {
6269 show_error {} . "Cannot find the git directory \"$gitdir\"."
6273 set cmdline_files {}
6274 set i [lsearch -exact $revtreeargs "--"]
6276 set cmdline_files [lrange $revtreeargs [expr {$i + 1}] end]
6277 set revtreeargs [lrange $revtreeargs 0 [expr {$i - 1}]]
6278 } elseif {$revtreeargs ne {}} {
6280 set f [eval exec git rev-parse --no-revs --no-flags $revtreeargs]
6281 set cmdline_files [split $f "\n"]
6282 set n [llength $cmdline_files]
6283 set revtreeargs [lrange $revtreeargs 0 end-$n]
6285 # unfortunately we get both stdout and stderr in $err,
6286 # so look for "fatal:".
6287 set i [string first "fatal:" $err]
6289 set err [string range $err [expr {$i + 6}] end]
6291 show_error {} . "Bad arguments to gitk:\n$err"
6300 set highlight_paths {}
6301 set searchdirn -forwards
6310 set selectedhlview None
6321 wm title . "[file tail $argv0]: [file tail [pwd]]"
6324 if {$cmdline_files ne {} || $revtreeargs ne {}} {
6325 # create a view for the files/dirs specified on the command line
6329 set viewname(1) "Command line"
6330 set viewfiles(1) $cmdline_files
6331 set viewargs(1) $revtreeargs
6334 .bar.view entryconf Edit* -state normal
6335 .bar.view entryconf Delete* -state normal
6338 if {[info exists permviews]} {
6339 foreach v $permviews {
6342 set viewname($n) [lindex $v 0]
6343 set viewfiles($n) [lindex $v 1]
6344 set viewargs($n) [lindex $v 2]