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(pwsash0)]} {
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(pwsash0) "[expr {40 * $charspc}] 2"
448 set geometry(pwsash1) "[expr {60 * $charspc}] 2"
451 # the upper half will have a paned window, a scroll bar to the right, and some stuff below
452 frame .tf -height $geometry(topheight) -width $geometry(topwidth)
454 panedwindow .tf.histframe.pwclist -orient horizontal -sashpad 0 -handlesize 4
456 # create three canvases
457 set cscroll .tf.histframe.csb
458 set canv .tf.histframe.pwclist.canv
460 -background $bgcolor -bd 0 \
461 -yscrollincr $linespc -yscrollcommand "scrollcanv $cscroll"
462 .tf.histframe.pwclist add $canv
463 set canv2 .tf.histframe.pwclist.canv2
465 -background $bgcolor -bd 0 -yscrollincr $linespc
466 .tf.histframe.pwclist add $canv2
467 set canv3 .tf.histframe.pwclist.canv3
469 -background $bgcolor -bd 0 -yscrollincr $linespc
470 .tf.histframe.pwclist add $canv3
471 eval .tf.histframe.pwclist sash place 0 $geometry(pwsash0)
472 eval .tf.histframe.pwclist sash place 1 $geometry(pwsash1)
474 # a scroll bar to rule them
475 scrollbar $cscroll -command {allcanvs yview} -highlightthickness 0
476 pack $cscroll -side right -fill y
477 bind .tf.histframe.pwclist <Configure> {resizeclistpanes %W %w}
478 lappend bglist $canv $canv2 $canv3
479 pack .tf.histframe.pwclist -fill both -expand 1 -side left
481 # we have two button bars at bottom of top frame. Bar 1
483 frame .tf.lbar -height 15
485 set sha1entry .tf.bar.sha1
486 set entries $sha1entry
487 set sha1but .tf.bar.sha1label
488 button $sha1but -text "SHA1 ID: " -state disabled -relief flat \
489 -command gotocommit -width 8 -font $uifont
490 $sha1but conf -disabledforeground [$sha1but cget -foreground]
491 pack .tf.bar.sha1label -side left
492 entry $sha1entry -width 40 -font $textfont -textvariable sha1string
493 trace add variable sha1string write sha1change
494 pack $sha1entry -side left -pady 2
496 image create bitmap bm-left -data {
497 #define left_width 16
498 #define left_height 16
499 static unsigned char left_bits[] = {
500 0x00, 0x00, 0xc0, 0x01, 0xe0, 0x00, 0x70, 0x00, 0x38, 0x00, 0x1c, 0x00,
501 0x0e, 0x00, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0x0e, 0x00, 0x1c, 0x00,
502 0x38, 0x00, 0x70, 0x00, 0xe0, 0x00, 0xc0, 0x01};
504 image create bitmap bm-right -data {
505 #define right_width 16
506 #define right_height 16
507 static unsigned char right_bits[] = {
508 0x00, 0x00, 0xc0, 0x01, 0x80, 0x03, 0x00, 0x07, 0x00, 0x0e, 0x00, 0x1c,
509 0x00, 0x38, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0x00, 0x38, 0x00, 0x1c,
510 0x00, 0x0e, 0x00, 0x07, 0x80, 0x03, 0xc0, 0x01};
512 button .tf.bar.leftbut -image bm-left -command goback \
513 -state disabled -width 26
514 pack .tf.bar.leftbut -side left -fill y
515 button .tf.bar.rightbut -image bm-right -command goforw \
516 -state disabled -width 26
517 pack .tf.bar.rightbut -side left -fill y
519 button .tf.bar.findbut -text "Find" -command dofind -font $uifont
520 pack .tf.bar.findbut -side left
522 set fstring .tf.bar.findstring
523 lappend entries $fstring
524 entry $fstring -width 30 -font $textfont -textvariable findstring
525 trace add variable findstring write find_change
526 pack $fstring -side left -expand 1 -fill x -in .tf.bar
528 set findtypemenu [tk_optionMenu .tf.bar.findtype \
529 findtype Exact IgnCase Regexp]
530 trace add variable findtype write find_change
531 .tf.bar.findtype configure -font $uifont
532 .tf.bar.findtype.menu configure -font $uifont
533 set findloc "All fields"
534 tk_optionMenu .tf.bar.findloc findloc "All fields" Headline \
535 Comments Author Committer
536 trace add variable findloc write find_change
537 .tf.bar.findloc configure -font $uifont
538 .tf.bar.findloc.menu configure -font $uifont
539 pack .tf.bar.findloc -side right
540 pack .tf.bar.findtype -side right
542 # build up the bottom bar of upper window
543 label .tf.lbar.flabel -text "Highlight: Commits " \
545 pack .tf.lbar.flabel -side left -fill y
546 set gdttype "touching paths:"
547 set gm [tk_optionMenu .tf.lbar.gdttype gdttype "touching paths:" \
548 "adding/removing string:"]
549 trace add variable gdttype write hfiles_change
550 $gm conf -font $uifont
551 .tf.lbar.gdttype conf -font $uifont
552 pack .tf.lbar.gdttype -side left -fill y
553 entry .tf.lbar.fent -width 25 -font $textfont \
554 -textvariable highlight_files
555 trace add variable highlight_files write hfiles_change
556 lappend entries .tf.lbar.fent
557 pack .tf.lbar.fent -side left -fill x -expand 1
558 label .tf.lbar.vlabel -text " OR in view" -font $uifont
559 pack .tf.lbar.vlabel -side left -fill y
560 global viewhlmenu selectedhlview
561 set viewhlmenu [tk_optionMenu .tf.lbar.vhl selectedhlview None]
562 $viewhlmenu entryconf None -command delvhighlight
563 $viewhlmenu conf -font $uifont
564 .tf.lbar.vhl conf -font $uifont
565 pack .tf.lbar.vhl -side left -fill y
566 label .tf.lbar.rlabel -text " OR " -font $uifont
567 pack .tf.lbar.rlabel -side left -fill y
568 global highlight_related
569 set m [tk_optionMenu .tf.lbar.relm highlight_related None \
570 "Descendent" "Not descendent" "Ancestor" "Not ancestor"]
571 $m conf -font $uifont
572 .tf.lbar.relm conf -font $uifont
573 trace add variable highlight_related write vrel_change
574 pack .tf.lbar.relm -side left -fill y
576 # Finish putting the upper half of the viewer together
577 pack .tf.lbar -in .tf -side bottom -fill x
578 pack .tf.bar -in .tf -side bottom -fill x
579 pack .tf.histframe -fill both -side top -expand 1
581 .ctop paneconfigure .tf -height $geometry(topheight)
582 .ctop paneconfigure .tf -width $geometry(topwidth)
584 # now build up the bottom
585 panedwindow .pwbottom -orient horizontal
587 # lower left, a text box over search bar, scroll bar to the right
588 # if we know window height, then that will set the lower text height, otherwise
589 # we set lower text height which will drive window height
590 if {[info exists geometry(main)]} {
591 frame .bleft -width $geometry(botwidth)
593 frame .bleft -width $geometry(botwidth) -height $geometry(botheight)
598 button .bleft.top.search -text "Search" -command dosearch \
600 pack .bleft.top.search -side left -padx 5
601 set sstring .bleft.top.sstring
602 entry $sstring -width 20 -font $textfont -textvariable searchstring
603 lappend entries $sstring
604 trace add variable searchstring write incrsearch
605 pack $sstring -side left -expand 1 -fill x
606 radiobutton .bleft.mid.diff -text "Diff" \
607 -command changediffdisp -variable diffelide -value {0 0}
608 radiobutton .bleft.mid.old -text "Old version" \
609 -command changediffdisp -variable diffelide -value {0 1}
610 radiobutton .bleft.mid.new -text "New version" \
611 -command changediffdisp -variable diffelide -value {1 0}
612 pack .bleft.mid.diff .bleft.mid.old .bleft.mid.new -side left
613 set ctext .bleft.ctext
614 text $ctext -background $bgcolor -foreground $fgcolor \
615 -state disabled -font $textfont \
616 -yscrollcommand scrolltext -wrap none
617 scrollbar .bleft.sb -command "$ctext yview"
618 pack .bleft.top -side top -fill x
619 pack .bleft.mid -side top -fill x
620 pack .bleft.sb -side right -fill y
621 pack $ctext -side left -fill both -expand 1
622 lappend bglist $ctext
623 lappend fglist $ctext
625 $ctext tag conf comment -wrap $wrapcomment
626 $ctext tag conf filesep -font [concat $textfont bold] -back "#aaaaaa"
627 $ctext tag conf hunksep -fore [lindex $diffcolors 2]
628 $ctext tag conf d0 -fore [lindex $diffcolors 0]
629 $ctext tag conf d1 -fore [lindex $diffcolors 1]
630 $ctext tag conf m0 -fore red
631 $ctext tag conf m1 -fore blue
632 $ctext tag conf m2 -fore green
633 $ctext tag conf m3 -fore purple
634 $ctext tag conf m4 -fore brown
635 $ctext tag conf m5 -fore "#009090"
636 $ctext tag conf m6 -fore magenta
637 $ctext tag conf m7 -fore "#808000"
638 $ctext tag conf m8 -fore "#009000"
639 $ctext tag conf m9 -fore "#ff0080"
640 $ctext tag conf m10 -fore cyan
641 $ctext tag conf m11 -fore "#b07070"
642 $ctext tag conf m12 -fore "#70b0f0"
643 $ctext tag conf m13 -fore "#70f0b0"
644 $ctext tag conf m14 -fore "#f0b070"
645 $ctext tag conf m15 -fore "#ff70b0"
646 $ctext tag conf mmax -fore darkgrey
648 $ctext tag conf mresult -font [concat $textfont bold]
649 $ctext tag conf msep -font [concat $textfont bold]
650 $ctext tag conf found -back yellow
653 .pwbottom paneconfigure .bleft -width $geometry(botwidth)
658 radiobutton .bright.mode.patch -text "Patch" \
659 -command reselectline -variable cmitmode -value "patch"
660 .bright.mode.patch configure -font $uifont
661 radiobutton .bright.mode.tree -text "Tree" \
662 -command reselectline -variable cmitmode -value "tree"
663 .bright.mode.tree configure -font $uifont
664 grid .bright.mode.patch .bright.mode.tree -sticky ew
665 pack .bright.mode -side top -fill x
666 set cflist .bright.cfiles
667 set indent [font measure $mainfont "nn"]
669 -background $bgcolor -foreground $fgcolor \
671 -tabs [list $indent [expr {2 * $indent}]] \
672 -yscrollcommand ".bright.sb set" \
673 -cursor [. cget -cursor] \
674 -spacing1 1 -spacing3 1
675 lappend bglist $cflist
676 lappend fglist $cflist
677 scrollbar .bright.sb -command "$cflist yview"
678 pack .bright.sb -side right -fill y
679 pack $cflist -side left -fill both -expand 1
680 $cflist tag configure highlight \
681 -background [$cflist cget -selectbackground]
682 $cflist tag configure bold -font [concat $mainfont bold]
684 .pwbottom add .bright
687 # restore window position if known
688 if {[info exists geometry(main)]} {
689 wm geometry . "$geometry(main)"
692 bind .pwbottom <Configure> {resizecdetpanes %W %w}
693 pack .ctop -fill both -expand 1
694 bindall <1> {selcanvline %W %x %y}
695 #bindall <B1-Motion> {selcanvline %W %x %y}
696 bindall <ButtonRelease-4> "allcanvs yview scroll -5 units"
697 bindall <ButtonRelease-5> "allcanvs yview scroll 5 units"
698 bindall <2> "canvscan mark %W %x %y"
699 bindall <B2-Motion> "canvscan dragto %W %x %y"
700 bindkey <Home> selfirstline
701 bindkey <End> sellastline
702 bind . <Key-Up> "selnextline -1"
703 bind . <Key-Down> "selnextline 1"
704 bind . <Shift-Key-Up> "next_highlight -1"
705 bind . <Shift-Key-Down> "next_highlight 1"
706 bindkey <Key-Right> "goforw"
707 bindkey <Key-Left> "goback"
708 bind . <Key-Prior> "selnextpage -1"
709 bind . <Key-Next> "selnextpage 1"
710 bind . <Control-Home> "allcanvs yview moveto 0.0"
711 bind . <Control-End> "allcanvs yview moveto 1.0"
712 bind . <Control-Key-Up> "allcanvs yview scroll -1 units"
713 bind . <Control-Key-Down> "allcanvs yview scroll 1 units"
714 bind . <Control-Key-Prior> "allcanvs yview scroll -1 pages"
715 bind . <Control-Key-Next> "allcanvs yview scroll 1 pages"
716 bindkey <Key-Delete> "$ctext yview scroll -1 pages"
717 bindkey <Key-BackSpace> "$ctext yview scroll -1 pages"
718 bindkey <Key-space> "$ctext yview scroll 1 pages"
719 bindkey p "selnextline -1"
720 bindkey n "selnextline 1"
723 bindkey i "selnextline -1"
724 bindkey k "selnextline 1"
727 bindkey b "$ctext yview scroll -1 pages"
728 bindkey d "$ctext yview scroll 18 units"
729 bindkey u "$ctext yview scroll -18 units"
730 bindkey / {findnext 1}
731 bindkey <Key-Return> {findnext 0}
734 bindkey <F5> updatecommits
735 bind . <Control-q> doquit
736 bind . <Control-f> dofind
737 bind . <Control-g> {findnext 0}
738 bind . <Control-r> dosearchback
739 bind . <Control-s> dosearch
740 bind . <Control-equal> {incrfont 1}
741 bind . <Control-KP_Add> {incrfont 1}
742 bind . <Control-minus> {incrfont -1}
743 bind . <Control-KP_Subtract> {incrfont -1}
744 wm protocol . WM_DELETE_WINDOW doquit
745 bind . <Button-1> "click %W"
746 bind $fstring <Key-Return> dofind
747 bind $sha1entry <Key-Return> gotocommit
748 bind $sha1entry <<PasteSelection>> clearsha1
749 bind $cflist <1> {sel_flist %W %x %y; break}
750 bind $cflist <B1-Motion> {sel_flist %W %x %y; break}
751 bind $cflist <ButtonRelease-1> {treeclick %W %x %y}
753 set maincursor [. cget -cursor]
754 set textcursor [$ctext cget -cursor]
755 set curtextcursor $textcursor
757 set rowctxmenu .rowctxmenu
758 menu $rowctxmenu -tearoff 0
759 $rowctxmenu add command -label "Diff this -> selected" \
760 -command {diffvssel 0}
761 $rowctxmenu add command -label "Diff selected -> this" \
762 -command {diffvssel 1}
763 $rowctxmenu add command -label "Make patch" -command mkpatch
764 $rowctxmenu add command -label "Create tag" -command mktag
765 $rowctxmenu add command -label "Write commit to file" -command writecommit
766 $rowctxmenu add command -label "Create new branch" -command mkbranch
767 $rowctxmenu add command -label "Cherry-pick this commit" \
770 set headctxmenu .headctxmenu
771 menu $headctxmenu -tearoff 0
772 $headctxmenu add command -label "Check out this branch" \
774 $headctxmenu add command -label "Remove this branch" \
778 # mouse-2 makes all windows scan vertically, but only the one
779 # the cursor is in scans horizontally
780 proc canvscan {op w x y} {
781 global canv canv2 canv3
782 foreach c [list $canv $canv2 $canv3] {
791 proc scrollcanv {cscroll f0 f1} {
797 # when we make a key binding for the toplevel, make sure
798 # it doesn't get triggered when that key is pressed in the
799 # find string entry widget.
800 proc bindkey {ev script} {
803 set escript [bind Entry $ev]
804 if {$escript == {}} {
805 set escript [bind Entry <Key>]
808 bind $e $ev "$escript; break"
812 # set the focus back to the toplevel for any click outside
823 global canv canv2 canv3 ctext cflist mainfont textfont uifont
824 global stuffsaved findmergefiles maxgraphpct
825 global maxwidth showneartags
826 global viewname viewfiles viewargs viewperm nextviewnum
827 global cmitmode wrapcomment
828 global colors bgcolor fgcolor diffcolors
830 if {$stuffsaved} return
831 if {![winfo viewable .]} return
833 set f [open "~/.gitk-new" w]
834 puts $f [list set mainfont $mainfont]
835 puts $f [list set textfont $textfont]
836 puts $f [list set uifont $uifont]
837 puts $f [list set findmergefiles $findmergefiles]
838 puts $f [list set maxgraphpct $maxgraphpct]
839 puts $f [list set maxwidth $maxwidth]
840 puts $f [list set cmitmode $cmitmode]
841 puts $f [list set wrapcomment $wrapcomment]
842 puts $f [list set showneartags $showneartags]
843 puts $f [list set bgcolor $bgcolor]
844 puts $f [list set fgcolor $fgcolor]
845 puts $f [list set colors $colors]
846 puts $f [list set diffcolors $diffcolors]
848 puts $f "set geometry(main) [wm geometry .]"
849 puts $f "set geometry(topwidth) [winfo width .tf]"
850 puts $f "set geometry(topheight) [winfo height .tf]"
851 puts $f "set geometry(pwsash0) \"[.tf.histframe.pwclist sash coord 0]\""
852 puts $f "set geometry(pwsash1) \"[.tf.histframe.pwclist sash coord 1]\""
853 puts $f "set geometry(botwidth) [winfo width .bleft]"
854 puts $f "set geometry(botheight) [winfo height .bleft]"
856 puts -nonewline $f "set permviews {"
857 for {set v 0} {$v < $nextviewnum} {incr v} {
859 puts $f "{[list $viewname($v) $viewfiles($v) $viewargs($v)]}"
864 file rename -force "~/.gitk-new" "~/.gitk"
869 proc resizeclistpanes {win w} {
871 if {[info exists oldwidth($win)]} {
872 set s0 [$win sash coord 0]
873 set s1 [$win sash coord 1]
875 set sash0 [expr {int($w/2 - 2)}]
876 set sash1 [expr {int($w*5/6 - 2)}]
878 set factor [expr {1.0 * $w / $oldwidth($win)}]
879 set sash0 [expr {int($factor * [lindex $s0 0])}]
880 set sash1 [expr {int($factor * [lindex $s1 0])}]
884 if {$sash1 < $sash0 + 20} {
885 set sash1 [expr {$sash0 + 20}]
887 if {$sash1 > $w - 10} {
888 set sash1 [expr {$w - 10}]
889 if {$sash0 > $sash1 - 20} {
890 set sash0 [expr {$sash1 - 20}]
894 $win sash place 0 $sash0 [lindex $s0 1]
895 $win sash place 1 $sash1 [lindex $s1 1]
897 set oldwidth($win) $w
900 proc resizecdetpanes {win w} {
902 if {[info exists oldwidth($win)]} {
903 set s0 [$win sash coord 0]
905 set sash0 [expr {int($w*3/4 - 2)}]
907 set factor [expr {1.0 * $w / $oldwidth($win)}]
908 set sash0 [expr {int($factor * [lindex $s0 0])}]
912 if {$sash0 > $w - 15} {
913 set sash0 [expr {$w - 15}]
916 $win sash place 0 $sash0 [lindex $s0 1]
918 set oldwidth($win) $w
922 global canv canv2 canv3
928 proc bindall {event action} {
929 global canv canv2 canv3
930 bind $canv $event $action
931 bind $canv2 $event $action
932 bind $canv3 $event $action
938 if {[winfo exists $w]} {
943 wm title $w "About gitk"
945 Gitk - a commit viewer for git
947 Copyright © 2005-2006 Paul Mackerras
949 Use and redistribute under the terms of the GNU General Public License} \
950 -justify center -aspect 400 -border 2 -bg white -relief groove
951 pack $w.m -side top -fill x -padx 2 -pady 2
952 $w.m configure -font $uifont
953 button $w.ok -text Close -command "destroy $w" -default active
954 pack $w.ok -side bottom
955 $w.ok configure -font $uifont
956 bind $w <Visibility> "focus $w.ok"
957 bind $w <Key-Escape> "destroy $w"
958 bind $w <Key-Return> "destroy $w"
964 if {[winfo exists $w]} {
969 wm title $w "Gitk key bindings"
974 <Home> Move to first commit
975 <End> Move to last commit
976 <Up>, p, i Move up one commit
977 <Down>, n, k Move down one commit
978 <Left>, z, j Go back in history list
979 <Right>, x, l Go forward in history list
980 <PageUp> Move up one page in commit list
981 <PageDown> Move down one page in commit list
982 <Ctrl-Home> Scroll to top of commit list
983 <Ctrl-End> Scroll to bottom of commit list
984 <Ctrl-Up> Scroll commit list up one line
985 <Ctrl-Down> Scroll commit list down one line
986 <Ctrl-PageUp> Scroll commit list up one page
987 <Ctrl-PageDown> Scroll commit list down one page
988 <Shift-Up> Move to previous highlighted line
989 <Shift-Down> Move to next highlighted line
990 <Delete>, b Scroll diff view up one page
991 <Backspace> Scroll diff view up one page
992 <Space> Scroll diff view down one page
993 u Scroll diff view up 18 lines
994 d Scroll diff view down 18 lines
996 <Ctrl-G> Move to next find hit
997 <Return> Move to next find hit
998 / Move to next find hit, or redo find
999 ? Move to previous find hit
1000 f Scroll diff view to next file
1001 <Ctrl-S> Search for next hit in diff view
1002 <Ctrl-R> Search for previous hit in diff view
1003 <Ctrl-KP+> Increase font size
1004 <Ctrl-plus> Increase font size
1005 <Ctrl-KP-> Decrease font size
1006 <Ctrl-minus> Decrease font size
1009 -justify left -bg white -border 2 -relief groove
1010 pack $w.m -side top -fill both -padx 2 -pady 2
1011 $w.m configure -font $uifont
1012 button $w.ok -text Close -command "destroy $w" -default active
1013 pack $w.ok -side bottom
1014 $w.ok configure -font $uifont
1015 bind $w <Visibility> "focus $w.ok"
1016 bind $w <Key-Escape> "destroy $w"
1017 bind $w <Key-Return> "destroy $w"
1020 # Procedures for manipulating the file list window at the
1021 # bottom right of the overall window.
1023 proc treeview {w l openlevs} {
1024 global treecontents treediropen treeheight treeparent treeindex
1034 set treecontents() {}
1035 $w conf -state normal
1037 while {[string range $f 0 $prefixend] ne $prefix} {
1038 if {$lev <= $openlevs} {
1039 $w mark set e:$treeindex($prefix) "end -1c"
1040 $w mark gravity e:$treeindex($prefix) left
1042 set treeheight($prefix) $ht
1043 incr ht [lindex $htstack end]
1044 set htstack [lreplace $htstack end end]
1045 set prefixend [lindex $prefendstack end]
1046 set prefendstack [lreplace $prefendstack end end]
1047 set prefix [string range $prefix 0 $prefixend]
1050 set tail [string range $f [expr {$prefixend+1}] end]
1051 while {[set slash [string first "/" $tail]] >= 0} {
1054 lappend prefendstack $prefixend
1055 incr prefixend [expr {$slash + 1}]
1056 set d [string range $tail 0 $slash]
1057 lappend treecontents($prefix) $d
1058 set oldprefix $prefix
1060 set treecontents($prefix) {}
1061 set treeindex($prefix) [incr ix]
1062 set treeparent($prefix) $oldprefix
1063 set tail [string range $tail [expr {$slash+1}] end]
1064 if {$lev <= $openlevs} {
1066 set treediropen($prefix) [expr {$lev < $openlevs}]
1067 set bm [expr {$lev == $openlevs? "tri-rt": "tri-dn"}]
1068 $w mark set d:$ix "end -1c"
1069 $w mark gravity d:$ix left
1071 for {set i 0} {$i < $lev} {incr i} {append str "\t"}
1073 $w image create end -align center -image $bm -padx 1 \
1075 $w insert end $d [highlight_tag $prefix]
1076 $w mark set s:$ix "end -1c"
1077 $w mark gravity s:$ix left
1082 if {$lev <= $openlevs} {
1085 for {set i 0} {$i < $lev} {incr i} {append str "\t"}
1087 $w insert end $tail [highlight_tag $f]
1089 lappend treecontents($prefix) $tail
1092 while {$htstack ne {}} {
1093 set treeheight($prefix) $ht
1094 incr ht [lindex $htstack end]
1095 set htstack [lreplace $htstack end end]
1097 $w conf -state disabled
1100 proc linetoelt {l} {
1101 global treeheight treecontents
1106 foreach e $treecontents($prefix) {
1111 if {[string index $e end] eq "/"} {
1112 set n $treeheight($prefix$e)
1124 proc highlight_tree {y prefix} {
1125 global treeheight treecontents cflist
1127 foreach e $treecontents($prefix) {
1129 if {[highlight_tag $path] ne {}} {
1130 $cflist tag add bold $y.0 "$y.0 lineend"
1133 if {[string index $e end] eq "/" && $treeheight($path) > 1} {
1134 set y [highlight_tree $y $path]
1140 proc treeclosedir {w dir} {
1141 global treediropen treeheight treeparent treeindex
1143 set ix $treeindex($dir)
1144 $w conf -state normal
1145 $w delete s:$ix e:$ix
1146 set treediropen($dir) 0
1147 $w image configure a:$ix -image tri-rt
1148 $w conf -state disabled
1149 set n [expr {1 - $treeheight($dir)}]
1150 while {$dir ne {}} {
1151 incr treeheight($dir) $n
1152 set dir $treeparent($dir)
1156 proc treeopendir {w dir} {
1157 global treediropen treeheight treeparent treecontents treeindex
1159 set ix $treeindex($dir)
1160 $w conf -state normal
1161 $w image configure a:$ix -image tri-dn
1162 $w mark set e:$ix s:$ix
1163 $w mark gravity e:$ix right
1166 set n [llength $treecontents($dir)]
1167 for {set x $dir} {$x ne {}} {set x $treeparent($x)} {
1170 incr treeheight($x) $n
1172 foreach e $treecontents($dir) {
1174 if {[string index $e end] eq "/"} {
1175 set iy $treeindex($de)
1176 $w mark set d:$iy e:$ix
1177 $w mark gravity d:$iy left
1178 $w insert e:$ix $str
1179 set treediropen($de) 0
1180 $w image create e:$ix -align center -image tri-rt -padx 1 \
1182 $w insert e:$ix $e [highlight_tag $de]
1183 $w mark set s:$iy e:$ix
1184 $w mark gravity s:$iy left
1185 set treeheight($de) 1
1187 $w insert e:$ix $str
1188 $w insert e:$ix $e [highlight_tag $de]
1191 $w mark gravity e:$ix left
1192 $w conf -state disabled
1193 set treediropen($dir) 1
1194 set top [lindex [split [$w index @0,0] .] 0]
1195 set ht [$w cget -height]
1196 set l [lindex [split [$w index s:$ix] .] 0]
1199 } elseif {$l + $n + 1 > $top + $ht} {
1200 set top [expr {$l + $n + 2 - $ht}]
1208 proc treeclick {w x y} {
1209 global treediropen cmitmode ctext cflist cflist_top
1211 if {$cmitmode ne "tree"} return
1212 if {![info exists cflist_top]} return
1213 set l [lindex [split [$w index "@$x,$y"] "."] 0]
1214 $cflist tag remove highlight $cflist_top.0 "$cflist_top.0 lineend"
1215 $cflist tag add highlight $l.0 "$l.0 lineend"
1221 set e [linetoelt $l]
1222 if {[string index $e end] ne "/"} {
1224 } elseif {$treediropen($e)} {
1231 proc setfilelist {id} {
1232 global treefilelist cflist
1234 treeview $cflist $treefilelist($id) 0
1237 image create bitmap tri-rt -background black -foreground blue -data {
1238 #define tri-rt_width 13
1239 #define tri-rt_height 13
1240 static unsigned char tri-rt_bits[] = {
1241 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x30, 0x00, 0x70, 0x00, 0xf0, 0x00,
1242 0xf0, 0x01, 0xf0, 0x00, 0x70, 0x00, 0x30, 0x00, 0x10, 0x00, 0x00, 0x00,
1245 #define tri-rt-mask_width 13
1246 #define tri-rt-mask_height 13
1247 static unsigned char tri-rt-mask_bits[] = {
1248 0x08, 0x00, 0x18, 0x00, 0x38, 0x00, 0x78, 0x00, 0xf8, 0x00, 0xf8, 0x01,
1249 0xf8, 0x03, 0xf8, 0x01, 0xf8, 0x00, 0x78, 0x00, 0x38, 0x00, 0x18, 0x00,
1252 image create bitmap tri-dn -background black -foreground blue -data {
1253 #define tri-dn_width 13
1254 #define tri-dn_height 13
1255 static unsigned char tri-dn_bits[] = {
1256 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x07, 0xf8, 0x03,
1257 0xf0, 0x01, 0xe0, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1260 #define tri-dn-mask_width 13
1261 #define tri-dn-mask_height 13
1262 static unsigned char tri-dn-mask_bits[] = {
1263 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x1f, 0xfe, 0x0f, 0xfc, 0x07,
1264 0xf8, 0x03, 0xf0, 0x01, 0xe0, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
1268 proc init_flist {first} {
1269 global cflist cflist_top selectedline difffilestart
1271 $cflist conf -state normal
1272 $cflist delete 0.0 end
1274 $cflist insert end $first
1276 $cflist tag add highlight 1.0 "1.0 lineend"
1278 catch {unset cflist_top}
1280 $cflist conf -state disabled
1281 set difffilestart {}
1284 proc highlight_tag {f} {
1285 global highlight_paths
1287 foreach p $highlight_paths {
1288 if {[string match $p $f]} {
1295 proc highlight_filelist {} {
1296 global cmitmode cflist
1298 $cflist conf -state normal
1299 if {$cmitmode ne "tree"} {
1300 set end [lindex [split [$cflist index end] .] 0]
1301 for {set l 2} {$l < $end} {incr l} {
1302 set line [$cflist get $l.0 "$l.0 lineend"]
1303 if {[highlight_tag $line] ne {}} {
1304 $cflist tag add bold $l.0 "$l.0 lineend"
1310 $cflist conf -state disabled
1313 proc unhighlight_filelist {} {
1316 $cflist conf -state normal
1317 $cflist tag remove bold 1.0 end
1318 $cflist conf -state disabled
1321 proc add_flist {fl} {
1324 $cflist conf -state normal
1326 $cflist insert end "\n"
1327 $cflist insert end $f [highlight_tag $f]
1329 $cflist conf -state disabled
1332 proc sel_flist {w x y} {
1333 global ctext difffilestart cflist cflist_top cmitmode
1335 if {$cmitmode eq "tree"} return
1336 if {![info exists cflist_top]} return
1337 set l [lindex [split [$w index "@$x,$y"] "."] 0]
1338 $cflist tag remove highlight $cflist_top.0 "$cflist_top.0 lineend"
1339 $cflist tag add highlight $l.0 "$l.0 lineend"
1344 catch {$ctext yview [lindex $difffilestart [expr {$l - 2}]]}
1348 # Functions for adding and removing shell-type quoting
1350 proc shellquote {str} {
1351 if {![string match "*\['\"\\ \t]*" $str]} {
1354 if {![string match "*\['\"\\]*" $str]} {
1357 if {![string match "*'*" $str]} {
1360 return "\"[string map {\" \\\" \\ \\\\} $str]\""
1363 proc shellarglist {l} {
1369 append str [shellquote $a]
1374 proc shelldequote {str} {
1379 if {![regexp -start $used -indices "\['\"\\\\ \t]" $str first]} {
1380 append ret [string range $str $used end]
1381 set used [string length $str]
1384 set first [lindex $first 0]
1385 set ch [string index $str $first]
1386 if {$first > $used} {
1387 append ret [string range $str $used [expr {$first - 1}]]
1390 if {$ch eq " " || $ch eq "\t"} break
1393 set first [string first "'" $str $used]
1395 error "unmatched single-quote"
1397 append ret [string range $str $used [expr {$first - 1}]]
1402 if {$used >= [string length $str]} {
1403 error "trailing backslash"
1405 append ret [string index $str $used]
1410 if {![regexp -start $used -indices "\[\"\\\\]" $str first]} {
1411 error "unmatched double-quote"
1413 set first [lindex $first 0]
1414 set ch [string index $str $first]
1415 if {$first > $used} {
1416 append ret [string range $str $used [expr {$first - 1}]]
1419 if {$ch eq "\""} break
1421 append ret [string index $str $used]
1425 return [list $used $ret]
1428 proc shellsplit {str} {
1431 set str [string trimleft $str]
1432 if {$str eq {}} break
1433 set dq [shelldequote $str]
1434 set n [lindex $dq 0]
1435 set word [lindex $dq 1]
1436 set str [string range $str $n end]
1442 # Code to implement multiple views
1444 proc newview {ishighlight} {
1445 global nextviewnum newviewname newviewperm uifont newishighlight
1446 global newviewargs revtreeargs
1448 set newishighlight $ishighlight
1450 if {[winfo exists $top]} {
1454 set newviewname($nextviewnum) "View $nextviewnum"
1455 set newviewperm($nextviewnum) 0
1456 set newviewargs($nextviewnum) [shellarglist $revtreeargs]
1457 vieweditor $top $nextviewnum "Gitk view definition"
1462 global viewname viewperm newviewname newviewperm
1463 global viewargs newviewargs
1465 set top .gitkvedit-$curview
1466 if {[winfo exists $top]} {
1470 set newviewname($curview) $viewname($curview)
1471 set newviewperm($curview) $viewperm($curview)
1472 set newviewargs($curview) [shellarglist $viewargs($curview)]
1473 vieweditor $top $curview "Gitk: edit view $viewname($curview)"
1476 proc vieweditor {top n title} {
1477 global newviewname newviewperm viewfiles
1481 wm title $top $title
1482 label $top.nl -text "Name" -font $uifont
1483 entry $top.name -width 20 -textvariable newviewname($n) -font $uifont
1484 grid $top.nl $top.name -sticky w -pady 5
1485 checkbutton $top.perm -text "Remember this view" -variable newviewperm($n) \
1487 grid $top.perm - -pady 5 -sticky w
1488 message $top.al -aspect 1000 -font $uifont \
1489 -text "Commits to include (arguments to git rev-list):"
1490 grid $top.al - -sticky w -pady 5
1491 entry $top.args -width 50 -textvariable newviewargs($n) \
1492 -background white -font $uifont
1493 grid $top.args - -sticky ew -padx 5
1494 message $top.l -aspect 1000 -font $uifont \
1495 -text "Enter files and directories to include, one per line:"
1496 grid $top.l - -sticky w
1497 text $top.t -width 40 -height 10 -background white -font $uifont
1498 if {[info exists viewfiles($n)]} {
1499 foreach f $viewfiles($n) {
1500 $top.t insert end $f
1501 $top.t insert end "\n"
1503 $top.t delete {end - 1c} end
1504 $top.t mark set insert 0.0
1506 grid $top.t - -sticky ew -padx 5
1508 button $top.buts.ok -text "OK" -command [list newviewok $top $n] \
1510 button $top.buts.can -text "Cancel" -command [list destroy $top] \
1512 grid $top.buts.ok $top.buts.can
1513 grid columnconfigure $top.buts 0 -weight 1 -uniform a
1514 grid columnconfigure $top.buts 1 -weight 1 -uniform a
1515 grid $top.buts - -pady 10 -sticky ew
1519 proc doviewmenu {m first cmd op argv} {
1520 set nmenu [$m index end]
1521 for {set i $first} {$i <= $nmenu} {incr i} {
1522 if {[$m entrycget $i -command] eq $cmd} {
1523 eval $m $op $i $argv
1529 proc allviewmenus {n op args} {
1532 doviewmenu .bar.view 5 [list showview $n] $op $args
1533 doviewmenu $viewhlmenu 1 [list addvhighlight $n] $op $args
1536 proc newviewok {top n} {
1537 global nextviewnum newviewperm newviewname newishighlight
1538 global viewname viewfiles viewperm selectedview curview
1539 global viewargs newviewargs viewhlmenu
1542 set newargs [shellsplit $newviewargs($n)]
1544 error_popup "Error in commit selection arguments: $err"
1550 foreach f [split [$top.t get 0.0 end] "\n"] {
1551 set ft [string trim $f]
1556 if {![info exists viewfiles($n)]} {
1557 # creating a new view
1559 set viewname($n) $newviewname($n)
1560 set viewperm($n) $newviewperm($n)
1561 set viewfiles($n) $files
1562 set viewargs($n) $newargs
1564 if {!$newishighlight} {
1565 after idle showview $n
1567 after idle addvhighlight $n
1570 # editing an existing view
1571 set viewperm($n) $newviewperm($n)
1572 if {$newviewname($n) ne $viewname($n)} {
1573 set viewname($n) $newviewname($n)
1574 doviewmenu .bar.view 5 [list showview $n] \
1575 entryconf [list -label $viewname($n)]
1576 doviewmenu $viewhlmenu 1 [list addvhighlight $n] \
1577 entryconf [list -label $viewname($n) -value $viewname($n)]
1579 if {$files ne $viewfiles($n) || $newargs ne $viewargs($n)} {
1580 set viewfiles($n) $files
1581 set viewargs($n) $newargs
1582 if {$curview == $n} {
1583 after idle updatecommits
1587 catch {destroy $top}
1591 global curview viewdata viewperm hlview selectedhlview
1593 if {$curview == 0} return
1594 if {[info exists hlview] && $hlview == $curview} {
1595 set selectedhlview None
1598 allviewmenus $curview delete
1599 set viewdata($curview) {}
1600 set viewperm($curview) 0
1604 proc addviewmenu {n} {
1605 global viewname viewhlmenu
1607 .bar.view add radiobutton -label $viewname($n) \
1608 -command [list showview $n] -variable selectedview -value $n
1609 $viewhlmenu add radiobutton -label $viewname($n) \
1610 -command [list addvhighlight $n] -variable selectedhlview
1613 proc flatten {var} {
1617 foreach i [array names $var] {
1618 lappend ret $i [set $var\($i\)]
1623 proc unflatten {var l} {
1633 global curview viewdata viewfiles
1634 global displayorder parentlist childlist rowidlist rowoffsets
1635 global colormap rowtextx commitrow nextcolor canvxmax
1636 global numcommits rowrangelist commitlisted idrowranges
1637 global selectedline currentid canv canvy0
1638 global matchinglines treediffs
1639 global pending_select phase
1640 global commitidx rowlaidout rowoptim linesegends
1641 global commfd nextupdate
1643 global vparentlist vchildlist vdisporder vcmitlisted
1644 global hlview selectedhlview
1646 if {$n == $curview} return
1648 if {[info exists selectedline]} {
1649 set selid $currentid
1650 set y [yc $selectedline]
1651 set ymax [lindex [$canv cget -scrollregion] 3]
1652 set span [$canv yview]
1653 set ytop [expr {[lindex $span 0] * $ymax}]
1654 set ybot [expr {[lindex $span 1] * $ymax}]
1655 if {$ytop < $y && $y < $ybot} {
1656 set yscreen [expr {$y - $ytop}]
1658 set yscreen [expr {($ybot - $ytop) / 2}]
1664 if {$curview >= 0} {
1665 set vparentlist($curview) $parentlist
1666 set vchildlist($curview) $childlist
1667 set vdisporder($curview) $displayorder
1668 set vcmitlisted($curview) $commitlisted
1670 set viewdata($curview) \
1671 [list $phase $rowidlist $rowoffsets $rowrangelist \
1672 [flatten idrowranges] [flatten idinlist] \
1673 $rowlaidout $rowoptim $numcommits $linesegends]
1674 } elseif {![info exists viewdata($curview)]
1675 || [lindex $viewdata($curview) 0] ne {}} {
1676 set viewdata($curview) \
1677 [list {} $rowidlist $rowoffsets $rowrangelist]
1680 catch {unset matchinglines}
1681 catch {unset treediffs}
1683 if {[info exists hlview] && $hlview == $n} {
1685 set selectedhlview None
1690 .bar.view entryconf Edit* -state [expr {$n == 0? "disabled": "normal"}]
1691 .bar.view entryconf Delete* -state [expr {$n == 0? "disabled": "normal"}]
1693 if {![info exists viewdata($n)]} {
1694 set pending_select $selid
1700 set phase [lindex $v 0]
1701 set displayorder $vdisporder($n)
1702 set parentlist $vparentlist($n)
1703 set childlist $vchildlist($n)
1704 set commitlisted $vcmitlisted($n)
1705 set rowidlist [lindex $v 1]
1706 set rowoffsets [lindex $v 2]
1707 set rowrangelist [lindex $v 3]
1709 set numcommits [llength $displayorder]
1710 catch {unset idrowranges}
1712 unflatten idrowranges [lindex $v 4]
1713 unflatten idinlist [lindex $v 5]
1714 set rowlaidout [lindex $v 6]
1715 set rowoptim [lindex $v 7]
1716 set numcommits [lindex $v 8]
1717 set linesegends [lindex $v 9]
1720 catch {unset colormap}
1721 catch {unset rowtextx}
1723 set canvxmax [$canv cget -width]
1729 if {$selid ne {} && [info exists commitrow($n,$selid)]} {
1730 set row $commitrow($n,$selid)
1731 # try to get the selected row in the same position on the screen
1732 set ymax [lindex [$canv cget -scrollregion] 3]
1733 set ytop [expr {[yc $row] - $yscreen}]
1737 set yf [expr {$ytop * 1.0 / $ymax}]
1739 allcanvs yview moveto $yf
1743 if {$phase eq "getcommits"} {
1744 show_status "Reading commits..."
1746 if {[info exists commfd($n)]} {
1751 } elseif {$numcommits == 0} {
1752 show_status "No commits selected"
1756 # Stuff relating to the highlighting facility
1758 proc ishighlighted {row} {
1759 global vhighlights fhighlights nhighlights rhighlights
1761 if {[info exists nhighlights($row)] && $nhighlights($row) > 0} {
1762 return $nhighlights($row)
1764 if {[info exists vhighlights($row)] && $vhighlights($row) > 0} {
1765 return $vhighlights($row)
1767 if {[info exists fhighlights($row)] && $fhighlights($row) > 0} {
1768 return $fhighlights($row)
1770 if {[info exists rhighlights($row)] && $rhighlights($row) > 0} {
1771 return $rhighlights($row)
1776 proc bolden {row font} {
1777 global canv linehtag selectedline boldrows
1779 lappend boldrows $row
1780 $canv itemconf $linehtag($row) -font $font
1781 if {[info exists selectedline] && $row == $selectedline} {
1783 set t [eval $canv create rect [$canv bbox $linehtag($row)] \
1784 -outline {{}} -tags secsel \
1785 -fill [$canv cget -selectbackground]]
1790 proc bolden_name {row font} {
1791 global canv2 linentag selectedline boldnamerows
1793 lappend boldnamerows $row
1794 $canv2 itemconf $linentag($row) -font $font
1795 if {[info exists selectedline] && $row == $selectedline} {
1796 $canv2 delete secsel
1797 set t [eval $canv2 create rect [$canv2 bbox $linentag($row)] \
1798 -outline {{}} -tags secsel \
1799 -fill [$canv2 cget -selectbackground]]
1805 global mainfont boldrows
1808 foreach row $boldrows {
1809 if {![ishighlighted $row]} {
1810 bolden $row $mainfont
1812 lappend stillbold $row
1815 set boldrows $stillbold
1818 proc addvhighlight {n} {
1819 global hlview curview viewdata vhl_done vhighlights commitidx
1821 if {[info exists hlview]} {
1825 if {$n != $curview && ![info exists viewdata($n)]} {
1826 set viewdata($n) [list getcommits {{}} {{}} {} {} {} 0 0 0 {}]
1827 set vparentlist($n) {}
1828 set vchildlist($n) {}
1829 set vdisporder($n) {}
1830 set vcmitlisted($n) {}
1833 set vhl_done $commitidx($hlview)
1834 if {$vhl_done > 0} {
1839 proc delvhighlight {} {
1840 global hlview vhighlights
1842 if {![info exists hlview]} return
1844 catch {unset vhighlights}
1848 proc vhighlightmore {} {
1849 global hlview vhl_done commitidx vhighlights
1850 global displayorder vdisporder curview mainfont
1852 set font [concat $mainfont bold]
1853 set max $commitidx($hlview)
1854 if {$hlview == $curview} {
1855 set disp $displayorder
1857 set disp $vdisporder($hlview)
1859 set vr [visiblerows]
1860 set r0 [lindex $vr 0]
1861 set r1 [lindex $vr 1]
1862 for {set i $vhl_done} {$i < $max} {incr i} {
1863 set id [lindex $disp $i]
1864 if {[info exists commitrow($curview,$id)]} {
1865 set row $commitrow($curview,$id)
1866 if {$r0 <= $row && $row <= $r1} {
1867 if {![highlighted $row]} {
1870 set vhighlights($row) 1
1877 proc askvhighlight {row id} {
1878 global hlview vhighlights commitrow iddrawn mainfont
1880 if {[info exists commitrow($hlview,$id)]} {
1881 if {[info exists iddrawn($id)] && ![ishighlighted $row]} {
1882 bolden $row [concat $mainfont bold]
1884 set vhighlights($row) 1
1886 set vhighlights($row) 0
1890 proc hfiles_change {name ix op} {
1891 global highlight_files filehighlight fhighlights fh_serial
1892 global mainfont highlight_paths
1894 if {[info exists filehighlight]} {
1895 # delete previous highlights
1896 catch {close $filehighlight}
1898 catch {unset fhighlights}
1900 unhighlight_filelist
1902 set highlight_paths {}
1903 after cancel do_file_hl $fh_serial
1905 if {$highlight_files ne {}} {
1906 after 300 do_file_hl $fh_serial
1910 proc makepatterns {l} {
1913 set ee [string map {"*" "\\*" "?" "\\?" "\[" "\\\[" "\\" "\\\\"} $e]
1914 if {[string index $ee end] eq "/"} {
1924 proc do_file_hl {serial} {
1925 global highlight_files filehighlight highlight_paths gdttype fhl_list
1927 if {$gdttype eq "touching paths:"} {
1928 if {[catch {set paths [shellsplit $highlight_files]}]} return
1929 set highlight_paths [makepatterns $paths]
1931 set gdtargs [concat -- $paths]
1933 set gdtargs [list "-S$highlight_files"]
1935 set cmd [concat | git diff-tree -r -s --stdin $gdtargs]
1936 set filehighlight [open $cmd r+]
1937 fconfigure $filehighlight -blocking 0
1938 fileevent $filehighlight readable readfhighlight
1944 proc flushhighlights {} {
1945 global filehighlight fhl_list
1947 if {[info exists filehighlight]} {
1949 puts $filehighlight ""
1950 flush $filehighlight
1954 proc askfilehighlight {row id} {
1955 global filehighlight fhighlights fhl_list
1957 lappend fhl_list $id
1958 set fhighlights($row) -1
1959 puts $filehighlight $id
1962 proc readfhighlight {} {
1963 global filehighlight fhighlights commitrow curview mainfont iddrawn
1966 while {[gets $filehighlight line] >= 0} {
1967 set line [string trim $line]
1968 set i [lsearch -exact $fhl_list $line]
1969 if {$i < 0} continue
1970 for {set j 0} {$j < $i} {incr j} {
1971 set id [lindex $fhl_list $j]
1972 if {[info exists commitrow($curview,$id)]} {
1973 set fhighlights($commitrow($curview,$id)) 0
1976 set fhl_list [lrange $fhl_list [expr {$i+1}] end]
1977 if {$line eq {}} continue
1978 if {![info exists commitrow($curview,$line)]} continue
1979 set row $commitrow($curview,$line)
1980 if {[info exists iddrawn($line)] && ![ishighlighted $row]} {
1981 bolden $row [concat $mainfont bold]
1983 set fhighlights($row) 1
1985 if {[eof $filehighlight]} {
1987 puts "oops, git diff-tree died"
1988 catch {close $filehighlight}
1994 proc find_change {name ix op} {
1995 global nhighlights mainfont boldnamerows
1996 global findstring findpattern findtype
1998 # delete previous highlights, if any
1999 foreach row $boldnamerows {
2000 bolden_name $row $mainfont
2003 catch {unset nhighlights}
2005 if {$findtype ne "Regexp"} {
2006 set e [string map {"*" "\\*" "?" "\\?" "\[" "\\\[" "\\" "\\\\"} \
2008 set findpattern "*$e*"
2013 proc askfindhighlight {row id} {
2014 global nhighlights commitinfo iddrawn mainfont
2015 global findstring findtype findloc findpattern
2017 if {![info exists commitinfo($id)]} {
2020 set info $commitinfo($id)
2022 set fldtypes {Headline Author Date Committer CDate Comments}
2023 foreach f $info ty $fldtypes {
2024 if {$findloc ne "All fields" && $findloc ne $ty} {
2027 if {$findtype eq "Regexp"} {
2028 set doesmatch [regexp $findstring $f]
2029 } elseif {$findtype eq "IgnCase"} {
2030 set doesmatch [string match -nocase $findpattern $f]
2032 set doesmatch [string match $findpattern $f]
2035 if {$ty eq "Author"} {
2042 if {[info exists iddrawn($id)]} {
2043 if {$isbold && ![ishighlighted $row]} {
2044 bolden $row [concat $mainfont bold]
2047 bolden_name $row [concat $mainfont bold]
2050 set nhighlights($row) $isbold
2053 proc vrel_change {name ix op} {
2054 global highlight_related
2057 if {$highlight_related ne "None"} {
2058 after idle drawvisible
2062 # prepare for testing whether commits are descendents or ancestors of a
2063 proc rhighlight_sel {a} {
2064 global descendent desc_todo ancestor anc_todo
2065 global highlight_related rhighlights
2067 catch {unset descendent}
2068 set desc_todo [list $a]
2069 catch {unset ancestor}
2070 set anc_todo [list $a]
2071 if {$highlight_related ne "None"} {
2073 after idle drawvisible
2077 proc rhighlight_none {} {
2080 catch {unset rhighlights}
2084 proc is_descendent {a} {
2085 global curview children commitrow descendent desc_todo
2088 set la $commitrow($v,$a)
2092 for {set i 0} {$i < [llength $todo]} {incr i} {
2093 set do [lindex $todo $i]
2094 if {$commitrow($v,$do) < $la} {
2095 lappend leftover $do
2098 foreach nk $children($v,$do) {
2099 if {![info exists descendent($nk)]} {
2100 set descendent($nk) 1
2108 set desc_todo [concat $leftover [lrange $todo [expr {$i+1}] end]]
2112 set descendent($a) 0
2113 set desc_todo $leftover
2116 proc is_ancestor {a} {
2117 global curview parentlist commitrow ancestor anc_todo
2120 set la $commitrow($v,$a)
2124 for {set i 0} {$i < [llength $todo]} {incr i} {
2125 set do [lindex $todo $i]
2126 if {![info exists commitrow($v,$do)] || $commitrow($v,$do) > $la} {
2127 lappend leftover $do
2130 foreach np [lindex $parentlist $commitrow($v,$do)] {
2131 if {![info exists ancestor($np)]} {
2140 set anc_todo [concat $leftover [lrange $todo [expr {$i+1}] end]]
2145 set anc_todo $leftover
2148 proc askrelhighlight {row id} {
2149 global descendent highlight_related iddrawn mainfont rhighlights
2150 global selectedline ancestor
2152 if {![info exists selectedline]} return
2154 if {$highlight_related eq "Descendent" ||
2155 $highlight_related eq "Not descendent"} {
2156 if {![info exists descendent($id)]} {
2159 if {$descendent($id) == ($highlight_related eq "Descendent")} {
2162 } elseif {$highlight_related eq "Ancestor" ||
2163 $highlight_related eq "Not ancestor"} {
2164 if {![info exists ancestor($id)]} {
2167 if {$ancestor($id) == ($highlight_related eq "Ancestor")} {
2171 if {[info exists iddrawn($id)]} {
2172 if {$isbold && ![ishighlighted $row]} {
2173 bolden $row [concat $mainfont bold]
2176 set rhighlights($row) $isbold
2179 proc next_hlcont {} {
2180 global fhl_row fhl_dirn displayorder numcommits
2181 global vhighlights fhighlights nhighlights rhighlights
2182 global hlview filehighlight findstring highlight_related
2184 if {![info exists fhl_dirn] || $fhl_dirn == 0} return
2187 if {$row < 0 || $row >= $numcommits} {
2192 set id [lindex $displayorder $row]
2193 if {[info exists hlview]} {
2194 if {![info exists vhighlights($row)]} {
2195 askvhighlight $row $id
2197 if {$vhighlights($row) > 0} break
2199 if {$findstring ne {}} {
2200 if {![info exists nhighlights($row)]} {
2201 askfindhighlight $row $id
2203 if {$nhighlights($row) > 0} break
2205 if {$highlight_related ne "None"} {
2206 if {![info exists rhighlights($row)]} {
2207 askrelhighlight $row $id
2209 if {$rhighlights($row) > 0} break
2211 if {[info exists filehighlight]} {
2212 if {![info exists fhighlights($row)]} {
2213 # ask for a few more while we're at it...
2215 for {set n 0} {$n < 100} {incr n} {
2216 if {![info exists fhighlights($r)]} {
2217 askfilehighlight $r [lindex $displayorder $r]
2220 if {$r < 0 || $r >= $numcommits} break
2224 if {$fhighlights($row) < 0} {
2228 if {$fhighlights($row) > 0} break
2236 proc next_highlight {dirn} {
2237 global selectedline fhl_row fhl_dirn
2238 global hlview filehighlight findstring highlight_related
2240 if {![info exists selectedline]} return
2241 if {!([info exists hlview] || $findstring ne {} ||
2242 $highlight_related ne "None" || [info exists filehighlight])} return
2243 set fhl_row [expr {$selectedline + $dirn}]
2248 proc cancel_next_highlight {} {
2254 # Graph layout functions
2256 proc shortids {ids} {
2259 if {[llength $id] > 1} {
2260 lappend res [shortids $id]
2261 } elseif {[regexp {^[0-9a-f]{40}$} $id]} {
2262 lappend res [string range $id 0 7]
2270 proc incrange {l x o} {
2273 set e [lindex $l $x]
2275 lset l $x [expr {$e + $o}]
2284 for {} {$n > 0} {incr n -1} {
2290 proc usedinrange {id l1 l2} {
2291 global children commitrow childlist curview
2293 if {[info exists commitrow($curview,$id)]} {
2294 set r $commitrow($curview,$id)
2295 if {$l1 <= $r && $r <= $l2} {
2296 return [expr {$r - $l1 + 1}]
2298 set kids [lindex $childlist $r]
2300 set kids $children($curview,$id)
2303 set r $commitrow($curview,$c)
2304 if {$l1 <= $r && $r <= $l2} {
2305 return [expr {$r - $l1 + 1}]
2311 proc sanity {row {full 0}} {
2312 global rowidlist rowoffsets
2315 set ids [lindex $rowidlist $row]
2318 if {$id eq {}} continue
2319 if {$col < [llength $ids] - 1 &&
2320 [lsearch -exact -start [expr {$col+1}] $ids $id] >= 0} {
2321 puts "oops: [shortids $id] repeated in row $row col $col: {[shortids [lindex $rowidlist $row]]}"
2323 set o [lindex $rowoffsets $row $col]
2329 if {[lindex $rowidlist $y $x] != $id} {
2330 puts "oops: rowoffsets wrong at row [expr {$y+1}] col [expr {$x-$o}]"
2331 puts " id=[shortids $id] check started at row $row"
2332 for {set i $row} {$i >= $y} {incr i -1} {
2333 puts " row $i ids={[shortids [lindex $rowidlist $i]]} offs={[lindex $rowoffsets $i]}"
2338 set o [lindex $rowoffsets $y $x]
2343 proc makeuparrow {oid x y z} {
2344 global rowidlist rowoffsets uparrowlen idrowranges
2346 for {set i 1} {$i < $uparrowlen && $y > 1} {incr i} {
2349 set off0 [lindex $rowoffsets $y]
2350 for {set x0 $x} {1} {incr x0} {
2351 if {$x0 >= [llength $off0]} {
2352 set x0 [llength [lindex $rowoffsets [expr {$y-1}]]]
2355 set z [lindex $off0 $x0]
2361 set z [expr {$x0 - $x}]
2362 lset rowidlist $y [linsert [lindex $rowidlist $y] $x $oid]
2363 lset rowoffsets $y [linsert [lindex $rowoffsets $y] $x $z]
2365 set tmp [lreplace [lindex $rowoffsets $y] $x $x {}]
2366 lset rowoffsets $y [incrange $tmp [expr {$x+1}] -1]
2367 lappend idrowranges($oid) $y
2370 proc initlayout {} {
2371 global rowidlist rowoffsets displayorder commitlisted
2372 global rowlaidout rowoptim
2373 global idinlist rowchk rowrangelist idrowranges
2374 global numcommits canvxmax canv
2376 global parentlist childlist children
2377 global colormap rowtextx
2389 catch {unset idinlist}
2390 catch {unset rowchk}
2393 set canvxmax [$canv cget -width]
2394 catch {unset colormap}
2395 catch {unset rowtextx}
2396 catch {unset idrowranges}
2400 proc setcanvscroll {} {
2401 global canv canv2 canv3 numcommits linespc canvxmax canvy0
2403 set ymax [expr {$canvy0 + ($numcommits - 0.5) * $linespc + 2}]
2404 $canv conf -scrollregion [list 0 0 $canvxmax $ymax]
2405 $canv2 conf -scrollregion [list 0 0 0 $ymax]
2406 $canv3 conf -scrollregion [list 0 0 0 $ymax]
2409 proc visiblerows {} {
2410 global canv numcommits linespc
2412 set ymax [lindex [$canv cget -scrollregion] 3]
2413 if {$ymax eq {} || $ymax == 0} return
2415 set y0 [expr {int([lindex $f 0] * $ymax)}]
2416 set r0 [expr {int(($y0 - 3) / $linespc) - 1}]
2420 set y1 [expr {int([lindex $f 1] * $ymax)}]
2421 set r1 [expr {int(($y1 - 3) / $linespc) + 1}]
2422 if {$r1 >= $numcommits} {
2423 set r1 [expr {$numcommits - 1}]
2425 return [list $r0 $r1]
2428 proc layoutmore {tmax} {
2429 global rowlaidout rowoptim commitidx numcommits optim_delay
2430 global uparrowlen curview
2433 if {$rowoptim - $optim_delay > $numcommits} {
2434 showstuff [expr {$rowoptim - $optim_delay}]
2435 } elseif {$rowlaidout - $uparrowlen - 1 > $rowoptim} {
2436 set nr [expr {$rowlaidout - $uparrowlen - 1 - $rowoptim}]
2440 optimize_rows $rowoptim 0 [expr {$rowoptim + $nr}]
2442 } elseif {$commitidx($curview) > $rowlaidout} {
2443 set nr [expr {$commitidx($curview) - $rowlaidout}]
2444 # may need to increase this threshold if uparrowlen or
2445 # mingaplen are increased...
2450 set rowlaidout [layoutrows $row [expr {$row + $nr}] 0]
2451 if {$rowlaidout == $row} {
2457 if {$tmax ne {} && [clock clicks -milliseconds] >= $tmax} {
2463 proc showstuff {canshow} {
2464 global numcommits commitrow pending_select selectedline
2465 global linesegends idrowranges idrangedrawn curview
2467 if {$numcommits == 0} {
2469 set phase "incrdraw"
2473 set numcommits $canshow
2475 set rows [visiblerows]
2476 set r0 [lindex $rows 0]
2477 set r1 [lindex $rows 1]
2479 for {set r $row} {$r < $canshow} {incr r} {
2480 foreach id [lindex $linesegends [expr {$r+1}]] {
2482 foreach {s e} [rowranges $id] {
2484 if {$e ne {} && $e < $numcommits && $s <= $r1 && $e >= $r0
2485 && ![info exists idrangedrawn($id,$i)]} {
2487 set idrangedrawn($id,$i) 1
2492 if {$canshow > $r1} {
2495 while {$row < $canshow} {
2499 if {[info exists pending_select] &&
2500 [info exists commitrow($curview,$pending_select)] &&
2501 $commitrow($curview,$pending_select) < $numcommits} {
2502 selectline $commitrow($curview,$pending_select) 1
2504 if {![info exists selectedline] && ![info exists pending_select]} {
2509 proc layoutrows {row endrow last} {
2510 global rowidlist rowoffsets displayorder
2511 global uparrowlen downarrowlen maxwidth mingaplen
2512 global childlist parentlist
2513 global idrowranges linesegends
2514 global commitidx curview
2515 global idinlist rowchk rowrangelist
2517 set idlist [lindex $rowidlist $row]
2518 set offs [lindex $rowoffsets $row]
2519 while {$row < $endrow} {
2520 set id [lindex $displayorder $row]
2523 foreach p [lindex $parentlist $row] {
2524 if {![info exists idinlist($p)]} {
2526 } elseif {!$idinlist($p)} {
2531 set nev [expr {[llength $idlist] + [llength $newolds]
2532 + [llength $oldolds] - $maxwidth + 1}]
2535 $row + $uparrowlen + $mingaplen >= $commitidx($curview)} break
2536 for {set x [llength $idlist]} {[incr x -1] >= 0} {} {
2537 set i [lindex $idlist $x]
2538 if {![info exists rowchk($i)] || $row >= $rowchk($i)} {
2539 set r [usedinrange $i [expr {$row - $downarrowlen}] \
2540 [expr {$row + $uparrowlen + $mingaplen}]]
2542 set idlist [lreplace $idlist $x $x]
2543 set offs [lreplace $offs $x $x]
2544 set offs [incrange $offs $x 1]
2546 set rm1 [expr {$row - 1}]
2548 lappend idrowranges($i) $rm1
2549 if {[incr nev -1] <= 0} break
2552 set rowchk($id) [expr {$row + $r}]
2555 lset rowidlist $row $idlist
2556 lset rowoffsets $row $offs
2558 lappend linesegends $lse
2559 set col [lsearch -exact $idlist $id]
2561 set col [llength $idlist]
2563 lset rowidlist $row $idlist
2565 if {[lindex $childlist $row] ne {}} {
2566 set z [expr {[llength [lindex $rowidlist [expr {$row-1}]]] - $col}]
2570 lset rowoffsets $row $offs
2572 makeuparrow $id $col $row $z
2578 if {[info exists idrowranges($id)]} {
2579 set ranges $idrowranges($id)
2581 unset idrowranges($id)
2583 lappend rowrangelist $ranges
2585 set offs [ntimes [llength $idlist] 0]
2586 set l [llength $newolds]
2587 set idlist [eval lreplace \$idlist $col $col $newolds]
2590 set offs [lrange $offs 0 [expr {$col - 1}]]
2591 foreach x $newolds {
2596 set tmp [expr {[llength $idlist] - [llength $offs]}]
2598 set offs [concat $offs [ntimes $tmp $o]]
2603 foreach i $newolds {
2605 set idrowranges($i) $row
2608 foreach oid $oldolds {
2609 set idinlist($oid) 1
2610 set idlist [linsert $idlist $col $oid]
2611 set offs [linsert $offs $col $o]
2612 makeuparrow $oid $col $row $o
2615 lappend rowidlist $idlist
2616 lappend rowoffsets $offs
2621 proc addextraid {id row} {
2622 global displayorder commitrow commitinfo
2623 global commitidx commitlisted
2624 global parentlist childlist children curview
2626 incr commitidx($curview)
2627 lappend displayorder $id
2628 lappend commitlisted 0
2629 lappend parentlist {}
2630 set commitrow($curview,$id) $row
2632 if {![info exists commitinfo($id)]} {
2633 set commitinfo($id) {"No commit information available"}
2635 if {![info exists children($curview,$id)]} {
2636 set children($curview,$id) {}
2638 lappend childlist $children($curview,$id)
2641 proc layouttail {} {
2642 global rowidlist rowoffsets idinlist commitidx curview
2643 global idrowranges rowrangelist
2645 set row $commitidx($curview)
2646 set idlist [lindex $rowidlist $row]
2647 while {$idlist ne {}} {
2648 set col [expr {[llength $idlist] - 1}]
2649 set id [lindex $idlist $col]
2652 lappend idrowranges($id) $row
2653 lappend rowrangelist $idrowranges($id)
2654 unset idrowranges($id)
2656 set offs [ntimes $col 0]
2657 set idlist [lreplace $idlist $col $col]
2658 lappend rowidlist $idlist
2659 lappend rowoffsets $offs
2662 foreach id [array names idinlist] {
2664 lset rowidlist $row [list $id]
2665 lset rowoffsets $row 0
2666 makeuparrow $id 0 $row 0
2667 lappend idrowranges($id) $row
2668 lappend rowrangelist $idrowranges($id)
2669 unset idrowranges($id)
2671 lappend rowidlist {}
2672 lappend rowoffsets {}
2676 proc insert_pad {row col npad} {
2677 global rowidlist rowoffsets
2679 set pad [ntimes $npad {}]
2680 lset rowidlist $row [eval linsert [list [lindex $rowidlist $row]] $col $pad]
2681 set tmp [eval linsert [list [lindex $rowoffsets $row]] $col $pad]
2682 lset rowoffsets $row [incrange $tmp [expr {$col + $npad}] [expr {-$npad}]]
2685 proc optimize_rows {row col endrow} {
2686 global rowidlist rowoffsets idrowranges displayorder
2688 for {} {$row < $endrow} {incr row} {
2689 set idlist [lindex $rowidlist $row]
2690 set offs [lindex $rowoffsets $row]
2692 for {} {$col < [llength $offs]} {incr col} {
2693 if {[lindex $idlist $col] eq {}} {
2697 set z [lindex $offs $col]
2698 if {$z eq {}} continue
2700 set x0 [expr {$col + $z}]
2701 set y0 [expr {$row - 1}]
2702 set z0 [lindex $rowoffsets $y0 $x0]
2704 set id [lindex $idlist $col]
2705 set ranges [rowranges $id]
2706 if {$ranges ne {} && $y0 > [lindex $ranges 0]} {
2710 if {$z < -1 || ($z < 0 && $isarrow)} {
2711 set npad [expr {-1 - $z + $isarrow}]
2712 set offs [incrange $offs $col $npad]
2713 insert_pad $y0 $x0 $npad
2715 optimize_rows $y0 $x0 $row
2717 set z [lindex $offs $col]
2718 set x0 [expr {$col + $z}]
2719 set z0 [lindex $rowoffsets $y0 $x0]
2720 } elseif {$z > 1 || ($z > 0 && $isarrow)} {
2721 set npad [expr {$z - 1 + $isarrow}]
2722 set y1 [expr {$row + 1}]
2723 set offs2 [lindex $rowoffsets $y1]
2727 if {$z eq {} || $x1 + $z < $col} continue
2728 if {$x1 + $z > $col} {
2731 lset rowoffsets $y1 [incrange $offs2 $x1 $npad]
2734 set pad [ntimes $npad {}]
2735 set idlist [eval linsert \$idlist $col $pad]
2736 set tmp [eval linsert \$offs $col $pad]
2738 set offs [incrange $tmp $col [expr {-$npad}]]
2739 set z [lindex $offs $col]
2742 if {$z0 eq {} && !$isarrow} {
2743 # this line links to its first child on row $row-2
2744 set rm2 [expr {$row - 2}]
2745 set id [lindex $displayorder $rm2]
2746 set xc [lsearch -exact [lindex $rowidlist $rm2] $id]
2748 set z0 [expr {$xc - $x0}]
2751 if {$z0 ne {} && $z < 0 && $z0 > 0} {
2752 insert_pad $y0 $x0 1
2753 set offs [incrange $offs $col 1]
2754 optimize_rows $y0 [expr {$x0 + 1}] $row
2759 for {set col [llength $idlist]} {[incr col -1] >= 0} {} {
2760 set o [lindex $offs $col]
2762 # check if this is the link to the first child
2763 set id [lindex $idlist $col]
2764 set ranges [rowranges $id]
2765 if {$ranges ne {} && $row == [lindex $ranges 0]} {
2766 # it is, work out offset to child
2767 set y0 [expr {$row - 1}]
2768 set id [lindex $displayorder $y0]
2769 set x0 [lsearch -exact [lindex $rowidlist $y0] $id]
2771 set o [expr {$x0 - $col}]
2775 if {$o eq {} || $o <= 0} break
2777 if {$o ne {} && [incr col] < [llength $idlist]} {
2778 set y1 [expr {$row + 1}]
2779 set offs2 [lindex $rowoffsets $y1]
2783 if {$z eq {} || $x1 + $z < $col} continue
2784 lset rowoffsets $y1 [incrange $offs2 $x1 1]
2787 set idlist [linsert $idlist $col {}]
2788 set tmp [linsert $offs $col {}]
2790 set offs [incrange $tmp $col -1]
2793 lset rowidlist $row $idlist
2794 lset rowoffsets $row $offs
2800 global canvx0 linespc
2801 return [expr {$canvx0 + $col * $linespc}]
2805 global canvy0 linespc
2806 return [expr {$canvy0 + $row * $linespc}]
2809 proc linewidth {id} {
2810 global thickerline lthickness
2813 if {[info exists thickerline] && $id eq $thickerline} {
2814 set wid [expr {2 * $lthickness}]
2819 proc rowranges {id} {
2820 global phase idrowranges commitrow rowlaidout rowrangelist curview
2824 ([info exists commitrow($curview,$id)]
2825 && $commitrow($curview,$id) < $rowlaidout)} {
2826 set ranges [lindex $rowrangelist $commitrow($curview,$id)]
2827 } elseif {[info exists idrowranges($id)]} {
2828 set ranges $idrowranges($id)
2833 proc drawlineseg {id i} {
2834 global rowoffsets rowidlist
2836 global canv colormap linespc
2837 global numcommits commitrow curview
2839 set ranges [rowranges $id]
2841 if {[info exists commitrow($curview,$id)]
2842 && $commitrow($curview,$id) < $numcommits} {
2843 set downarrow [expr {$i < [llength $ranges] / 2 - 1}]
2847 set startrow [lindex $ranges [expr {2 * $i}]]
2848 set row [lindex $ranges [expr {2 * $i + 1}]]
2849 if {$startrow == $row} return
2852 set col [lsearch -exact [lindex $rowidlist $row] $id]
2854 puts "oops: drawline: id $id not on row $row"
2860 set o [lindex $rowoffsets $row $col]
2863 # changing direction
2864 set x [xc $row $col]
2866 lappend coords $x $y
2872 set x [xc $row $col]
2874 lappend coords $x $y
2876 # draw the link to the first child as part of this line
2878 set child [lindex $displayorder $row]
2879 set ccol [lsearch -exact [lindex $rowidlist $row] $child]
2881 set x [xc $row $ccol]
2883 if {$ccol < $col - 1} {
2884 lappend coords [xc $row [expr {$col - 1}]] [yc $row]
2885 } elseif {$ccol > $col + 1} {
2886 lappend coords [xc $row [expr {$col + 1}]] [yc $row]
2888 lappend coords $x $y
2891 if {[llength $coords] < 4} return
2893 # This line has an arrow at the lower end: check if the arrow is
2894 # on a diagonal segment, and if so, work around the Tk 8.4
2895 # refusal to draw arrows on diagonal lines.
2896 set x0 [lindex $coords 0]
2897 set x1 [lindex $coords 2]
2899 set y0 [lindex $coords 1]
2900 set y1 [lindex $coords 3]
2901 if {$y0 - $y1 <= 2 * $linespc && $x1 == [lindex $coords 4]} {
2902 # we have a nearby vertical segment, just trim off the diag bit
2903 set coords [lrange $coords 2 end]
2905 set slope [expr {($x0 - $x1) / ($y0 - $y1)}]
2906 set xi [expr {$x0 - $slope * $linespc / 2}]
2907 set yi [expr {$y0 - $linespc / 2}]
2908 set coords [lreplace $coords 0 1 $xi $y0 $xi $yi]
2912 set arrow [expr {2 * ($i > 0) + $downarrow}]
2913 set arrow [lindex {none first last both} $arrow]
2914 set t [$canv create line $coords -width [linewidth $id] \
2915 -fill $colormap($id) -tags lines.$id -arrow $arrow]
2920 proc drawparentlinks {id row col olds} {
2921 global rowidlist canv colormap
2923 set row2 [expr {$row + 1}]
2924 set x [xc $row $col]
2927 set ids [lindex $rowidlist $row2]
2928 # rmx = right-most X coord used
2931 set i [lsearch -exact $ids $p]
2933 puts "oops, parent $p of $id not in list"
2936 set x2 [xc $row2 $i]
2940 set ranges [rowranges $p]
2941 if {$ranges ne {} && $row2 == [lindex $ranges 0]
2942 && $row2 < [lindex $ranges 1]} {
2943 # drawlineseg will do this one for us
2947 # should handle duplicated parents here...
2948 set coords [list $x $y]
2949 if {$i < $col - 1} {
2950 lappend coords [xc $row [expr {$i + 1}]] $y
2951 } elseif {$i > $col + 1} {
2952 lappend coords [xc $row [expr {$i - 1}]] $y
2954 lappend coords $x2 $y2
2955 set t [$canv create line $coords -width [linewidth $p] \
2956 -fill $colormap($p) -tags lines.$p]
2963 proc drawlines {id} {
2964 global colormap canv
2966 global children iddrawn commitrow rowidlist curview
2968 $canv delete lines.$id
2969 set nr [expr {[llength [rowranges $id]] / 2}]
2970 for {set i 0} {$i < $nr} {incr i} {
2971 if {[info exists idrangedrawn($id,$i)]} {
2975 foreach child $children($curview,$id) {
2976 if {[info exists iddrawn($child)]} {
2977 set row $commitrow($curview,$child)
2978 set col [lsearch -exact [lindex $rowidlist $row] $child]
2980 drawparentlinks $child $row $col [list $id]
2986 proc drawcmittext {id row col rmx} {
2987 global linespc canv canv2 canv3 canvy0 fgcolor
2988 global commitlisted commitinfo rowidlist
2989 global rowtextx idpos idtags idheads idotherrefs
2990 global linehtag linentag linedtag
2991 global mainfont canvxmax boldrows boldnamerows fgcolor
2993 set ofill [expr {[lindex $commitlisted $row]? "blue": "white"}]
2994 set x [xc $row $col]
2996 set orad [expr {$linespc / 3}]
2997 set t [$canv create oval [expr {$x - $orad}] [expr {$y - $orad}] \
2998 [expr {$x + $orad - 1}] [expr {$y + $orad - 1}] \
2999 -fill $ofill -outline $fgcolor -width 1 -tags circle]
3001 $canv bind $t <1> {selcanvline {} %x %y}
3002 set xt [xc $row [llength [lindex $rowidlist $row]]]
3006 set rowtextx($row) $xt
3007 set idpos($id) [list $x $xt $y]
3008 if {[info exists idtags($id)] || [info exists idheads($id)]
3009 || [info exists idotherrefs($id)]} {
3010 set xt [drawtags $id $x $xt $y]
3012 set headline [lindex $commitinfo($id) 0]
3013 set name [lindex $commitinfo($id) 1]
3014 set date [lindex $commitinfo($id) 2]
3015 set date [formatdate $date]
3018 set isbold [ishighlighted $row]
3020 lappend boldrows $row
3023 lappend boldnamerows $row
3027 set linehtag($row) [$canv create text $xt $y -anchor w -fill $fgcolor \
3028 -text $headline -font $font -tags text]
3029 $canv bind $linehtag($row) <Button-3> "rowmenu %X %Y $id"
3030 set linentag($row) [$canv2 create text 3 $y -anchor w -fill $fgcolor \
3031 -text $name -font $nfont -tags text]
3032 set linedtag($row) [$canv3 create text 3 $y -anchor w -fill $fgcolor \
3033 -text $date -font $mainfont -tags text]
3034 set xr [expr {$xt + [font measure $mainfont $headline]}]
3035 if {$xr > $canvxmax} {
3041 proc drawcmitrow {row} {
3042 global displayorder rowidlist
3043 global idrangedrawn iddrawn
3044 global commitinfo parentlist numcommits
3045 global filehighlight fhighlights findstring nhighlights
3046 global hlview vhighlights
3047 global highlight_related rhighlights
3049 if {$row >= $numcommits} return
3050 foreach id [lindex $rowidlist $row] {
3051 if {$id eq {}} continue
3053 foreach {s e} [rowranges $id] {
3055 if {$row < $s} continue
3058 if {$e < $numcommits && ![info exists idrangedrawn($id,$i)]} {
3060 set idrangedrawn($id,$i) 1
3067 set id [lindex $displayorder $row]
3068 if {[info exists hlview] && ![info exists vhighlights($row)]} {
3069 askvhighlight $row $id
3071 if {[info exists filehighlight] && ![info exists fhighlights($row)]} {
3072 askfilehighlight $row $id
3074 if {$findstring ne {} && ![info exists nhighlights($row)]} {
3075 askfindhighlight $row $id
3077 if {$highlight_related ne "None" && ![info exists rhighlights($row)]} {
3078 askrelhighlight $row $id
3080 if {[info exists iddrawn($id)]} return
3081 set col [lsearch -exact [lindex $rowidlist $row] $id]
3083 puts "oops, row $row id $id not in list"
3086 if {![info exists commitinfo($id)]} {
3090 set olds [lindex $parentlist $row]
3092 set rmx [drawparentlinks $id $row $col $olds]
3096 drawcmittext $id $row $col $rmx
3100 proc drawfrac {f0 f1} {
3101 global numcommits canv
3104 set ymax [lindex [$canv cget -scrollregion] 3]
3105 if {$ymax eq {} || $ymax == 0} return
3106 set y0 [expr {int($f0 * $ymax)}]
3107 set row [expr {int(($y0 - 3) / $linespc) - 1}]
3111 set y1 [expr {int($f1 * $ymax)}]
3112 set endrow [expr {int(($y1 - 3) / $linespc) + 1}]
3113 if {$endrow >= $numcommits} {
3114 set endrow [expr {$numcommits - 1}]
3116 for {} {$row <= $endrow} {incr row} {
3121 proc drawvisible {} {
3123 eval drawfrac [$canv yview]
3126 proc clear_display {} {
3127 global iddrawn idrangedrawn
3128 global vhighlights fhighlights nhighlights rhighlights
3131 catch {unset iddrawn}
3132 catch {unset idrangedrawn}
3133 catch {unset vhighlights}
3134 catch {unset fhighlights}
3135 catch {unset nhighlights}
3136 catch {unset rhighlights}
3139 proc findcrossings {id} {
3140 global rowidlist parentlist numcommits rowoffsets displayorder
3144 foreach {s e} [rowranges $id] {
3145 if {$e >= $numcommits} {
3146 set e [expr {$numcommits - 1}]
3148 if {$e <= $s} continue
3149 set x [lsearch -exact [lindex $rowidlist $e] $id]
3151 puts "findcrossings: oops, no [shortids $id] in row $e"
3154 for {set row $e} {[incr row -1] >= $s} {} {
3155 set olds [lindex $parentlist $row]
3156 set kid [lindex $displayorder $row]
3157 set kidx [lsearch -exact [lindex $rowidlist $row] $kid]
3158 if {$kidx < 0} continue
3159 set nextrow [lindex $rowidlist [expr {$row + 1}]]
3161 set px [lsearch -exact $nextrow $p]
3162 if {$px < 0} continue
3163 if {($kidx < $x && $x < $px) || ($px < $x && $x < $kidx)} {
3164 if {[lsearch -exact $ccross $p] >= 0} continue
3165 if {$x == $px + ($kidx < $px? -1: 1)} {
3167 } elseif {[lsearch -exact $cross $p] < 0} {
3172 set inc [lindex $rowoffsets $row $x]
3173 if {$inc eq {}} break
3177 return [concat $ccross {{}} $cross]
3180 proc assigncolor {id} {
3181 global colormap colors nextcolor
3182 global commitrow parentlist children children curview
3184 if {[info exists colormap($id)]} return
3185 set ncolors [llength $colors]
3186 if {[info exists children($curview,$id)]} {
3187 set kids $children($curview,$id)
3191 if {[llength $kids] == 1} {
3192 set child [lindex $kids 0]
3193 if {[info exists colormap($child)]
3194 && [llength [lindex $parentlist $commitrow($curview,$child)]] == 1} {
3195 set colormap($id) $colormap($child)
3201 foreach x [findcrossings $id] {
3203 # delimiter between corner crossings and other crossings
3204 if {[llength $badcolors] >= $ncolors - 1} break
3205 set origbad $badcolors
3207 if {[info exists colormap($x)]
3208 && [lsearch -exact $badcolors $colormap($x)] < 0} {
3209 lappend badcolors $colormap($x)
3212 if {[llength $badcolors] >= $ncolors} {
3213 set badcolors $origbad
3215 set origbad $badcolors
3216 if {[llength $badcolors] < $ncolors - 1} {
3217 foreach child $kids {
3218 if {[info exists colormap($child)]
3219 && [lsearch -exact $badcolors $colormap($child)] < 0} {
3220 lappend badcolors $colormap($child)
3222 foreach p [lindex $parentlist $commitrow($curview,$child)] {
3223 if {[info exists colormap($p)]
3224 && [lsearch -exact $badcolors $colormap($p)] < 0} {
3225 lappend badcolors $colormap($p)
3229 if {[llength $badcolors] >= $ncolors} {
3230 set badcolors $origbad
3233 for {set i 0} {$i <= $ncolors} {incr i} {
3234 set c [lindex $colors $nextcolor]
3235 if {[incr nextcolor] >= $ncolors} {
3238 if {[lsearch -exact $badcolors $c]} break
3240 set colormap($id) $c
3243 proc bindline {t id} {
3246 $canv bind $t <Enter> "lineenter %x %y $id"
3247 $canv bind $t <Motion> "linemotion %x %y $id"
3248 $canv bind $t <Leave> "lineleave $id"
3249 $canv bind $t <Button-1> "lineclick %x %y $id 1"
3252 proc drawtags {id x xt y1} {
3253 global idtags idheads idotherrefs mainhead
3254 global linespc lthickness
3255 global canv mainfont commitrow rowtextx curview fgcolor bgcolor
3260 if {[info exists idtags($id)]} {
3261 set marks $idtags($id)
3262 set ntags [llength $marks]
3264 if {[info exists idheads($id)]} {
3265 set marks [concat $marks $idheads($id)]
3266 set nheads [llength $idheads($id)]
3268 if {[info exists idotherrefs($id)]} {
3269 set marks [concat $marks $idotherrefs($id)]
3275 set delta [expr {int(0.5 * ($linespc - $lthickness))}]
3276 set yt [expr {$y1 - 0.5 * $linespc}]
3277 set yb [expr {$yt + $linespc - 1}]
3281 foreach tag $marks {
3283 if {$i >= $ntags && $i < $ntags + $nheads && $tag eq $mainhead} {
3284 set wid [font measure [concat $mainfont bold] $tag]
3286 set wid [font measure $mainfont $tag]
3290 set xt [expr {$xt + $delta + $wid + $lthickness + $linespc}]
3292 set t [$canv create line $x $y1 [lindex $xvals end] $y1 \
3293 -width $lthickness -fill black -tags tag.$id]
3295 foreach tag $marks x $xvals wid $wvals {
3296 set xl [expr {$x + $delta}]
3297 set xr [expr {$x + $delta + $wid + $lthickness}]
3299 if {[incr ntags -1] >= 0} {
3301 set t [$canv create polygon $x [expr {$yt + $delta}] $xl $yt \
3302 $xr $yt $xr $yb $xl $yb $x [expr {$yb - $delta}] \
3303 -width 1 -outline black -fill yellow -tags tag.$id]
3304 $canv bind $t <1> [list showtag $tag 1]
3305 set rowtextx($commitrow($curview,$id)) [expr {$xr + $linespc}]
3307 # draw a head or other ref
3308 if {[incr nheads -1] >= 0} {
3310 if {$tag eq $mainhead} {
3316 set xl [expr {$xl - $delta/2}]
3317 $canv create polygon $x $yt $xr $yt $xr $yb $x $yb \
3318 -width 1 -outline black -fill $col -tags tag.$id
3319 if {[regexp {^(remotes/.*/|remotes/)} $tag match remoteprefix]} {
3320 set rwid [font measure $mainfont $remoteprefix]
3321 set xi [expr {$x + 1}]
3322 set yti [expr {$yt + 1}]
3323 set xri [expr {$x + $rwid}]
3324 $canv create polygon $xi $yti $xri $yti $xri $yb $xi $yb \
3325 -width 0 -fill "#ffddaa" -tags tag.$id
3328 set t [$canv create text $xl $y1 -anchor w -text $tag -fill $fgcolor \
3329 -font $font -tags [list tag.$id text]]
3331 $canv bind $t <1> [list showtag $tag 1]
3332 } elseif {$nheads >= 0} {
3333 $canv bind $t <Button-3> [list headmenu %X %Y $id $tag]
3339 proc xcoord {i level ln} {
3340 global canvx0 xspc1 xspc2
3342 set x [expr {$canvx0 + $i * $xspc1($ln)}]
3343 if {$i > 0 && $i == $level} {
3344 set x [expr {$x + 0.5 * ($xspc2 - $xspc1($ln))}]
3345 } elseif {$i > $level} {
3346 set x [expr {$x + $xspc2 - $xspc1($ln)}]
3351 proc show_status {msg} {
3352 global canv mainfont fgcolor
3355 $canv create text 3 3 -anchor nw -text $msg -font $mainfont \
3356 -tags text -fill $fgcolor
3359 proc finishcommits {} {
3360 global commitidx phase curview
3361 global pending_select
3363 if {$commitidx($curview) > 0} {
3366 show_status "No commits selected"
3369 catch {unset pending_select}
3372 # Insert a new commit as the child of the commit on row $row.
3373 # The new commit will be displayed on row $row and the commits
3374 # on that row and below will move down one row.
3375 proc insertrow {row newcmit} {
3376 global displayorder parentlist childlist commitlisted
3377 global commitrow curview rowidlist rowoffsets numcommits
3378 global rowrangelist idrowranges rowlaidout rowoptim numcommits
3379 global linesegends selectedline
3381 if {$row >= $numcommits} {
3382 puts "oops, inserting new row $row but only have $numcommits rows"
3385 set p [lindex $displayorder $row]
3386 set displayorder [linsert $displayorder $row $newcmit]
3387 set parentlist [linsert $parentlist $row $p]
3388 set kids [lindex $childlist $row]
3389 lappend kids $newcmit
3390 lset childlist $row $kids
3391 set childlist [linsert $childlist $row {}]
3392 set commitlisted [linsert $commitlisted $row 1]
3393 set l [llength $displayorder]
3394 for {set r $row} {$r < $l} {incr r} {
3395 set id [lindex $displayorder $r]
3396 set commitrow($curview,$id) $r
3399 set idlist [lindex $rowidlist $row]
3400 set offs [lindex $rowoffsets $row]
3403 if {$x eq {} || ($x eq $p && [llength $kids] == 1)} {
3409 if {[llength $kids] == 1} {
3410 set col [lsearch -exact $idlist $p]
3411 lset idlist $col $newcmit
3413 set col [llength $idlist]
3414 lappend idlist $newcmit
3416 lset rowoffsets $row $offs
3418 set rowidlist [linsert $rowidlist $row $idlist]
3419 set rowoffsets [linsert $rowoffsets [expr {$row+1}] $newoffs]
3421 set rowrangelist [linsert $rowrangelist $row {}]
3422 set l [llength $rowrangelist]
3423 for {set r 0} {$r < $l} {incr r} {
3424 set ranges [lindex $rowrangelist $r]
3425 if {$ranges ne {} && [lindex $ranges end] >= $row} {
3429 lappend newranges [expr {$x + 1}]
3431 lappend newranges $x
3434 lset rowrangelist $r $newranges
3437 if {[llength $kids] > 1} {
3438 set rp1 [expr {$row + 1}]
3439 set ranges [lindex $rowrangelist $rp1]
3440 if {$ranges eq {}} {
3441 set ranges [list $row $rp1]
3442 } elseif {[lindex $ranges end-1] == $rp1} {
3443 lset ranges end-1 $row
3445 lset rowrangelist $rp1 $ranges
3447 foreach id [array names idrowranges] {
3448 set ranges $idrowranges($id)
3449 if {$ranges ne {} && [lindex $ranges end] >= $row} {
3453 lappend newranges [expr {$x + 1}]
3455 lappend newranges $x
3458 set idrowranges($id) $newranges
3462 set linesegends [linsert $linesegends $row {}]
3468 if {[info exists selectedline] && $selectedline >= $row} {
3474 # Don't change the text pane cursor if it is currently the hand cursor,
3475 # showing that we are over a sha1 ID link.
3476 proc settextcursor {c} {
3477 global ctext curtextcursor
3479 if {[$ctext cget -cursor] == $curtextcursor} {
3480 $ctext config -cursor $c
3482 set curtextcursor $c
3485 proc nowbusy {what} {
3488 if {[array names isbusy] eq {}} {
3489 . config -cursor watch
3495 proc notbusy {what} {
3496 global isbusy maincursor textcursor
3498 catch {unset isbusy($what)}
3499 if {[array names isbusy] eq {}} {
3500 . config -cursor $maincursor
3501 settextcursor $textcursor
3507 global rowlaidout commitidx curview
3508 global pending_select
3511 layoutrows $rowlaidout $commitidx($curview) 1
3513 optimize_rows $row 0 $commitidx($curview)
3514 showstuff $commitidx($curview)
3515 if {[info exists pending_select]} {
3519 set drawmsecs [expr {[clock clicks -milliseconds] - $startmsecs}]
3521 #puts "overall $drawmsecs ms for $numcommits commits"
3524 proc findmatches {f} {
3525 global findtype foundstring foundstrlen
3526 if {$findtype == "Regexp"} {
3527 set matches [regexp -indices -all -inline $foundstring $f]
3529 if {$findtype == "IgnCase"} {
3530 set str [string tolower $f]
3536 while {[set j [string first $foundstring $str $i]] >= 0} {
3537 lappend matches [list $j [expr {$j+$foundstrlen-1}]]
3538 set i [expr {$j + $foundstrlen}]
3545 global findtype findloc findstring markedmatches commitinfo
3546 global numcommits displayorder linehtag linentag linedtag
3547 global mainfont canv canv2 canv3 selectedline
3548 global matchinglines foundstring foundstrlen matchstring
3553 cancel_next_highlight
3555 set matchinglines {}
3556 if {$findtype == "IgnCase"} {
3557 set foundstring [string tolower $findstring]
3559 set foundstring $findstring
3561 set foundstrlen [string length $findstring]
3562 if {$foundstrlen == 0} return
3563 regsub -all {[*?\[\\]} $foundstring {\\&} matchstring
3564 set matchstring "*$matchstring*"
3565 if {![info exists selectedline]} {
3568 set oldsel $selectedline
3571 set fldtypes {Headline Author Date Committer CDate Comments}
3573 foreach id $displayorder {
3574 set d $commitdata($id)
3576 if {$findtype == "Regexp"} {
3577 set doesmatch [regexp $foundstring $d]
3578 } elseif {$findtype == "IgnCase"} {
3579 set doesmatch [string match -nocase $matchstring $d]
3581 set doesmatch [string match $matchstring $d]
3583 if {!$doesmatch} continue
3584 if {![info exists commitinfo($id)]} {
3587 set info $commitinfo($id)
3589 foreach f $info ty $fldtypes {
3590 if {$findloc != "All fields" && $findloc != $ty} {
3593 set matches [findmatches $f]
3594 if {$matches == {}} continue
3596 if {$ty == "Headline"} {
3598 markmatches $canv $l $f $linehtag($l) $matches $mainfont
3599 } elseif {$ty == "Author"} {
3601 markmatches $canv2 $l $f $linentag($l) $matches $mainfont
3602 } elseif {$ty == "Date"} {
3604 markmatches $canv3 $l $f $linedtag($l) $matches $mainfont
3608 lappend matchinglines $l
3609 if {!$didsel && $l > $oldsel} {
3615 if {$matchinglines == {}} {
3617 } elseif {!$didsel} {
3618 findselectline [lindex $matchinglines 0]
3622 proc findselectline {l} {
3623 global findloc commentend ctext
3625 if {$findloc == "All fields" || $findloc == "Comments"} {
3626 # highlight the matches in the comments
3627 set f [$ctext get 1.0 $commentend]
3628 set matches [findmatches $f]
3629 foreach match $matches {
3630 set start [lindex $match 0]
3631 set end [expr {[lindex $match 1] + 1}]
3632 $ctext tag add found "1.0 + $start c" "1.0 + $end c"
3637 proc findnext {restart} {
3638 global matchinglines selectedline
3639 if {![info exists matchinglines]} {
3645 if {![info exists selectedline]} return
3646 foreach l $matchinglines {
3647 if {$l > $selectedline} {
3656 global matchinglines selectedline
3657 if {![info exists matchinglines]} {
3661 if {![info exists selectedline]} return
3663 foreach l $matchinglines {
3664 if {$l >= $selectedline} break
3668 findselectline $prev
3674 proc stopfindproc {{done 0}} {
3675 global findprocpid findprocfile findids
3676 global ctext findoldcursor phase maincursor textcursor
3677 global findinprogress
3679 catch {unset findids}
3680 if {[info exists findprocpid]} {
3682 catch {exec kill $findprocpid}
3684 catch {close $findprocfile}
3687 catch {unset findinprogress}
3691 # mark a commit as matching by putting a yellow background
3692 # behind the headline
3693 proc markheadline {l id} {
3694 global canv mainfont linehtag
3697 set bbox [$canv bbox $linehtag($l)]
3698 set t [$canv create rect $bbox -outline {} -tags matches -fill yellow]
3702 # mark the bits of a headline, author or date that match a find string
3703 proc markmatches {canv l str tag matches font} {
3704 set bbox [$canv bbox $tag]
3705 set x0 [lindex $bbox 0]
3706 set y0 [lindex $bbox 1]
3707 set y1 [lindex $bbox 3]
3708 foreach match $matches {
3709 set start [lindex $match 0]
3710 set end [lindex $match 1]
3711 if {$start > $end} continue
3712 set xoff [font measure $font [string range $str 0 [expr {$start-1}]]]
3713 set xlen [font measure $font [string range $str 0 [expr {$end}]]]
3714 set t [$canv create rect [expr {$x0+$xoff}] $y0 \
3715 [expr {$x0+$xlen+2}] $y1 \
3716 -outline {} -tags matches -fill yellow]
3721 proc unmarkmatches {} {
3722 global matchinglines findids
3723 allcanvs delete matches
3724 catch {unset matchinglines}
3725 catch {unset findids}
3728 proc selcanvline {w x y} {
3729 global canv canvy0 ctext linespc
3731 set ymax [lindex [$canv cget -scrollregion] 3]
3732 if {$ymax == {}} return
3733 set yfrac [lindex [$canv yview] 0]
3734 set y [expr {$y + $yfrac * $ymax}]
3735 set l [expr {int(($y - $canvy0) / $linespc + 0.5)}]
3740 if {![info exists rowtextx($l)] || $x < $rowtextx($l)} return
3746 proc commit_descriptor {p} {
3748 if {![info exists commitinfo($p)]} {
3752 if {[llength $commitinfo($p)] > 1} {
3753 set l [lindex $commitinfo($p) 0]
3758 # append some text to the ctext widget, and make any SHA1 ID
3759 # that we know about be a clickable link.
3760 proc appendwithlinks {text tags} {
3761 global ctext commitrow linknum curview
3763 set start [$ctext index "end - 1c"]
3764 $ctext insert end $text $tags
3765 set links [regexp -indices -all -inline {[0-9a-f]{40}} $text]
3769 set linkid [string range $text $s $e]
3770 if {![info exists commitrow($curview,$linkid)]} continue
3772 $ctext tag add link "$start + $s c" "$start + $e c"
3773 $ctext tag add link$linknum "$start + $s c" "$start + $e c"
3774 $ctext tag bind link$linknum <1> \
3775 [list selectline $commitrow($curview,$linkid) 1]
3778 $ctext tag conf link -foreground blue -underline 1
3779 $ctext tag bind link <Enter> { %W configure -cursor hand2 }
3780 $ctext tag bind link <Leave> { %W configure -cursor $curtextcursor }
3783 proc viewnextline {dir} {
3787 set ymax [lindex [$canv cget -scrollregion] 3]
3788 set wnow [$canv yview]
3789 set wtop [expr {[lindex $wnow 0] * $ymax}]
3790 set newtop [expr {$wtop + $dir * $linespc}]
3793 } elseif {$newtop > $ymax} {
3796 allcanvs yview moveto [expr {$newtop * 1.0 / $ymax}]
3799 # add a list of tag or branch names at position pos
3800 # returns the number of names inserted
3801 proc appendrefs {pos tags var} {
3802 global ctext commitrow linknum curview $var
3804 if {[catch {$ctext index $pos}]} {
3807 set tags [lsort $tags]
3810 set id [set $var\($tag\)]
3813 $ctext insert $pos $sep
3814 $ctext insert $pos $tag $lk
3815 $ctext tag conf $lk -foreground blue
3816 if {[info exists commitrow($curview,$id)]} {
3817 $ctext tag bind $lk <1> \
3818 [list selectline $commitrow($curview,$id) 1]
3819 $ctext tag conf $lk -underline 1
3820 $ctext tag bind $lk <Enter> { %W configure -cursor hand2 }
3821 $ctext tag bind $lk <Leave> { %W configure -cursor $curtextcursor }
3825 return [llength $tags]
3828 proc taglist {ids} {
3833 foreach tag $idtags($id) {
3840 # called when we have finished computing the nearby tags
3841 proc dispneartags {} {
3842 global selectedline currentid ctext anc_tags desc_tags showneartags
3845 if {![info exists selectedline] || !$showneartags} return
3847 $ctext conf -state normal
3848 if {[info exists desc_heads($id)]} {
3849 if {[appendrefs branch $desc_heads($id) headids] > 1} {
3850 $ctext insert "branch -2c" "es"
3853 if {[info exists anc_tags($id)]} {
3854 appendrefs follows [taglist $anc_tags($id)] tagids
3856 if {[info exists desc_tags($id)]} {
3857 appendrefs precedes [taglist $desc_tags($id)] tagids
3859 $ctext conf -state disabled
3862 proc selectline {l isnew} {
3863 global canv canv2 canv3 ctext commitinfo selectedline
3864 global displayorder linehtag linentag linedtag
3865 global canvy0 linespc parentlist childlist
3866 global currentid sha1entry
3867 global commentend idtags linknum
3868 global mergemax numcommits pending_select
3869 global cmitmode desc_tags anc_tags showneartags allcommits desc_heads
3871 catch {unset pending_select}
3874 cancel_next_highlight
3875 if {$l < 0 || $l >= $numcommits} return
3876 set y [expr {$canvy0 + $l * $linespc}]
3877 set ymax [lindex [$canv cget -scrollregion] 3]
3878 set ytop [expr {$y - $linespc - 1}]
3879 set ybot [expr {$y + $linespc + 1}]
3880 set wnow [$canv yview]
3881 set wtop [expr {[lindex $wnow 0] * $ymax}]
3882 set wbot [expr {[lindex $wnow 1] * $ymax}]
3883 set wh [expr {$wbot - $wtop}]
3885 if {$ytop < $wtop} {
3886 if {$ybot < $wtop} {
3887 set newtop [expr {$y - $wh / 2.0}]
3890 if {$newtop > $wtop - $linespc} {
3891 set newtop [expr {$wtop - $linespc}]
3894 } elseif {$ybot > $wbot} {
3895 if {$ytop > $wbot} {
3896 set newtop [expr {$y - $wh / 2.0}]
3898 set newtop [expr {$ybot - $wh}]
3899 if {$newtop < $wtop + $linespc} {
3900 set newtop [expr {$wtop + $linespc}]
3904 if {$newtop != $wtop} {
3908 allcanvs yview moveto [expr {$newtop * 1.0 / $ymax}]
3912 if {![info exists linehtag($l)]} return
3914 set t [eval $canv create rect [$canv bbox $linehtag($l)] -outline {{}} \
3915 -tags secsel -fill [$canv cget -selectbackground]]
3917 $canv2 delete secsel
3918 set t [eval $canv2 create rect [$canv2 bbox $linentag($l)] -outline {{}} \
3919 -tags secsel -fill [$canv2 cget -selectbackground]]
3921 $canv3 delete secsel
3922 set t [eval $canv3 create rect [$canv3 bbox $linedtag($l)] -outline {{}} \
3923 -tags secsel -fill [$canv3 cget -selectbackground]]
3927 addtohistory [list selectline $l 0]
3932 set id [lindex $displayorder $l]
3934 $sha1entry delete 0 end
3935 $sha1entry insert 0 $id
3936 $sha1entry selection from 0
3937 $sha1entry selection to end
3940 $ctext conf -state normal
3943 set info $commitinfo($id)
3944 set date [formatdate [lindex $info 2]]
3945 $ctext insert end "Author: [lindex $info 1] $date\n"
3946 set date [formatdate [lindex $info 4]]
3947 $ctext insert end "Committer: [lindex $info 3] $date\n"
3948 if {[info exists idtags($id)]} {
3949 $ctext insert end "Tags:"
3950 foreach tag $idtags($id) {
3951 $ctext insert end " $tag"
3953 $ctext insert end "\n"
3957 set olds [lindex $parentlist $l]
3958 if {[llength $olds] > 1} {
3961 if {$np >= $mergemax} {
3966 $ctext insert end "Parent: " $tag
3967 appendwithlinks [commit_descriptor $p] {}
3972 append headers "Parent: [commit_descriptor $p]"
3976 foreach c [lindex $childlist $l] {
3977 append headers "Child: [commit_descriptor $c]"
3980 # make anything that looks like a SHA1 ID be a clickable link
3981 appendwithlinks $headers {}
3982 if {$showneartags} {
3983 if {![info exists allcommits]} {
3986 $ctext insert end "Branch: "
3987 $ctext mark set branch "end -1c"
3988 $ctext mark gravity branch left
3989 if {[info exists desc_heads($id)]} {
3990 if {[appendrefs branch $desc_heads($id) headids] > 1} {
3991 # turn "Branch" into "Branches"
3992 $ctext insert "branch -2c" "es"
3995 $ctext insert end "\nFollows: "
3996 $ctext mark set follows "end -1c"
3997 $ctext mark gravity follows left
3998 if {[info exists anc_tags($id)]} {
3999 appendrefs follows [taglist $anc_tags($id)] tagids
4001 $ctext insert end "\nPrecedes: "
4002 $ctext mark set precedes "end -1c"
4003 $ctext mark gravity precedes left
4004 if {[info exists desc_tags($id)]} {
4005 appendrefs precedes [taglist $desc_tags($id)] tagids
4007 $ctext insert end "\n"
4009 $ctext insert end "\n"
4010 appendwithlinks [lindex $info 5] {comment}
4012 $ctext tag delete Comments
4013 $ctext tag remove found 1.0 end
4014 $ctext conf -state disabled
4015 set commentend [$ctext index "end - 1c"]
4017 init_flist "Comments"
4018 if {$cmitmode eq "tree"} {
4020 } elseif {[llength $olds] <= 1} {
4027 proc selfirstline {} {
4032 proc sellastline {} {
4035 set l [expr {$numcommits - 1}]
4039 proc selnextline {dir} {
4041 if {![info exists selectedline]} return
4042 set l [expr {$selectedline + $dir}]
4047 proc selnextpage {dir} {
4048 global canv linespc selectedline numcommits
4050 set lpp [expr {([winfo height $canv] - 2) / $linespc}]
4054 allcanvs yview scroll [expr {$dir * $lpp}] units
4056 if {![info exists selectedline]} return
4057 set l [expr {$selectedline + $dir * $lpp}]
4060 } elseif {$l >= $numcommits} {
4061 set l [expr $numcommits - 1]
4067 proc unselectline {} {
4068 global selectedline currentid
4070 catch {unset selectedline}
4071 catch {unset currentid}
4072 allcanvs delete secsel
4074 cancel_next_highlight
4077 proc reselectline {} {
4080 if {[info exists selectedline]} {
4081 selectline $selectedline 0
4085 proc addtohistory {cmd} {
4086 global history historyindex curview
4088 set elt [list $curview $cmd]
4089 if {$historyindex > 0
4090 && [lindex $history [expr {$historyindex - 1}]] == $elt} {
4094 if {$historyindex < [llength $history]} {
4095 set history [lreplace $history $historyindex end $elt]
4097 lappend history $elt
4100 if {$historyindex > 1} {
4101 .tf.bar.leftbut conf -state normal
4103 .tf.bar.leftbut conf -state disabled
4105 .tf.bar.rightbut conf -state disabled
4111 set view [lindex $elt 0]
4112 set cmd [lindex $elt 1]
4113 if {$curview != $view} {
4120 global history historyindex
4122 if {$historyindex > 1} {
4123 incr historyindex -1
4124 godo [lindex $history [expr {$historyindex - 1}]]
4125 .tf.bar.rightbut conf -state normal
4127 if {$historyindex <= 1} {
4128 .tf.bar.leftbut conf -state disabled
4133 global history historyindex
4135 if {$historyindex < [llength $history]} {
4136 set cmd [lindex $history $historyindex]
4139 .tf.bar.leftbut conf -state normal
4141 if {$historyindex >= [llength $history]} {
4142 .tf.bar.rightbut conf -state disabled
4147 global treefilelist treeidlist diffids diffmergeid treepending
4150 catch {unset diffmergeid}
4151 if {![info exists treefilelist($id)]} {
4152 if {![info exists treepending]} {
4153 if {[catch {set gtf [open [concat | git ls-tree -r $id] r]}]} {
4157 set treefilelist($id) {}
4158 set treeidlist($id) {}
4159 fconfigure $gtf -blocking 0
4160 fileevent $gtf readable [list gettreeline $gtf $id]
4167 proc gettreeline {gtf id} {
4168 global treefilelist treeidlist treepending cmitmode diffids
4170 while {[gets $gtf line] >= 0} {
4171 if {[lindex $line 1] ne "blob"} continue
4172 set sha1 [lindex $line 2]
4173 set fname [lindex $line 3]
4174 lappend treefilelist($id) $fname
4175 lappend treeidlist($id) $sha1
4177 if {![eof $gtf]} return
4180 if {$cmitmode ne "tree"} {
4181 if {![info exists diffmergeid]} {
4182 gettreediffs $diffids
4184 } elseif {$id ne $diffids} {
4192 global treefilelist treeidlist diffids
4193 global ctext commentend
4195 set i [lsearch -exact $treefilelist($diffids) $f]
4197 puts "oops, $f not in list for id $diffids"
4200 set blob [lindex $treeidlist($diffids) $i]
4201 if {[catch {set bf [open [concat | git cat-file blob $blob] r]} err]} {
4202 puts "oops, error reading blob $blob: $err"
4205 fconfigure $bf -blocking 0
4206 fileevent $bf readable [list getblobline $bf $diffids]
4207 $ctext config -state normal
4208 clear_ctext $commentend
4209 $ctext insert end "\n"
4210 $ctext insert end "$f\n" filesep
4211 $ctext config -state disabled
4212 $ctext yview $commentend
4215 proc getblobline {bf id} {
4216 global diffids cmitmode ctext
4218 if {$id ne $diffids || $cmitmode ne "tree"} {
4222 $ctext config -state normal
4223 while {[gets $bf line] >= 0} {
4224 $ctext insert end "$line\n"
4227 # delete last newline
4228 $ctext delete "end - 2c" "end - 1c"
4231 $ctext config -state disabled
4234 proc mergediff {id l} {
4235 global diffmergeid diffopts mdifffd
4241 # this doesn't seem to actually affect anything...
4242 set env(GIT_DIFF_OPTS) $diffopts
4243 set cmd [concat | git diff-tree --no-commit-id --cc $id]
4244 if {[catch {set mdf [open $cmd r]} err]} {
4245 error_popup "Error getting merge diffs: $err"
4248 fconfigure $mdf -blocking 0
4249 set mdifffd($id) $mdf
4250 set np [llength [lindex $parentlist $l]]
4251 fileevent $mdf readable [list getmergediffline $mdf $id $np]
4252 set nextupdate [expr {[clock clicks -milliseconds] + 100}]
4255 proc getmergediffline {mdf id np} {
4256 global diffmergeid ctext cflist nextupdate mergemax
4257 global difffilestart mdifffd
4259 set n [gets $mdf line]
4266 if {![info exists diffmergeid] || $id != $diffmergeid
4267 || $mdf != $mdifffd($id)} {
4270 $ctext conf -state normal
4271 if {[regexp {^diff --cc (.*)} $line match fname]} {
4272 # start of a new file
4273 $ctext insert end "\n"
4274 set here [$ctext index "end - 1c"]
4275 lappend difffilestart $here
4276 add_flist [list $fname]
4277 set l [expr {(78 - [string length $fname]) / 2}]
4278 set pad [string range "----------------------------------------" 1 $l]
4279 $ctext insert end "$pad $fname $pad\n" filesep
4280 } elseif {[regexp {^@@} $line]} {
4281 $ctext insert end "$line\n" hunksep
4282 } elseif {[regexp {^[0-9a-f]{40}$} $line] || [regexp {^index} $line]} {
4285 # parse the prefix - one ' ', '-' or '+' for each parent
4290 for {set j 0} {$j < $np} {incr j} {
4291 set c [string range $line $j $j]
4294 } elseif {$c == "-"} {
4296 } elseif {$c == "+"} {
4305 if {!$isbad && $minuses ne {} && $pluses eq {}} {
4306 # line doesn't appear in result, parents in $minuses have the line
4307 set num [lindex $minuses 0]
4308 } elseif {!$isbad && $pluses ne {} && $minuses eq {}} {
4309 # line appears in result, parents in $pluses don't have the line
4310 lappend tags mresult
4311 set num [lindex $spaces 0]
4314 if {$num >= $mergemax} {
4319 $ctext insert end "$line\n" $tags
4321 $ctext conf -state disabled
4322 if {[clock clicks -milliseconds] >= $nextupdate} {
4324 fileevent $mdf readable {}
4326 fileevent $mdf readable [list getmergediffline $mdf $id $np]
4330 proc startdiff {ids} {
4331 global treediffs diffids treepending diffmergeid
4334 catch {unset diffmergeid}
4335 if {![info exists treediffs($ids)]} {
4336 if {![info exists treepending]} {
4344 proc addtocflist {ids} {
4345 global treediffs cflist
4346 add_flist $treediffs($ids)
4350 proc gettreediffs {ids} {
4351 global treediff treepending
4352 set treepending $ids
4355 {set gdtf [open [concat | git diff-tree --no-commit-id -r $ids] r]} \
4357 fconfigure $gdtf -blocking 0
4358 fileevent $gdtf readable [list gettreediffline $gdtf $ids]
4361 proc gettreediffline {gdtf ids} {
4362 global treediff treediffs treepending diffids diffmergeid
4365 set n [gets $gdtf line]
4367 if {![eof $gdtf]} return
4369 set treediffs($ids) $treediff
4371 if {$cmitmode eq "tree"} {
4373 } elseif {$ids != $diffids} {
4374 if {![info exists diffmergeid]} {
4375 gettreediffs $diffids
4382 set file [lindex $line 5]
4383 lappend treediff $file
4386 proc getblobdiffs {ids} {
4387 global diffopts blobdifffd diffids env curdifftag curtagstart
4388 global nextupdate diffinhdr treediffs
4390 set env(GIT_DIFF_OPTS) $diffopts
4391 set cmd [concat | git diff-tree --no-commit-id -r -p -C $ids]
4392 if {[catch {set bdf [open $cmd r]} err]} {
4393 puts "error getting diffs: $err"
4397 fconfigure $bdf -blocking 0
4398 set blobdifffd($ids) $bdf
4399 set curdifftag Comments
4401 fileevent $bdf readable [list getblobdiffline $bdf $diffids]
4402 set nextupdate [expr {[clock clicks -milliseconds] + 100}]
4405 proc setinlist {var i val} {
4408 while {[llength [set $var]] < $i} {
4411 if {[llength [set $var]] == $i} {
4418 proc getblobdiffline {bdf ids} {
4419 global diffids blobdifffd ctext curdifftag curtagstart
4420 global diffnexthead diffnextnote difffilestart
4421 global nextupdate diffinhdr treediffs
4423 set n [gets $bdf line]
4427 if {$ids == $diffids && $bdf == $blobdifffd($ids)} {
4428 $ctext tag add $curdifftag $curtagstart end
4433 if {$ids != $diffids || $bdf != $blobdifffd($ids)} {
4436 $ctext conf -state normal
4437 if {[regexp {^diff --git a/(.*) b/(.*)} $line match fname newname]} {
4438 # start of a new file
4439 $ctext insert end "\n"
4440 $ctext tag add $curdifftag $curtagstart end
4441 set here [$ctext index "end - 1c"]
4442 set curtagstart $here
4444 set i [lsearch -exact $treediffs($ids) $fname]
4446 setinlist difffilestart $i $here
4448 if {$newname ne $fname} {
4449 set i [lsearch -exact $treediffs($ids) $newname]
4451 setinlist difffilestart $i $here
4454 set curdifftag "f:$fname"
4455 $ctext tag delete $curdifftag
4456 set l [expr {(78 - [string length $header]) / 2}]
4457 set pad [string range "----------------------------------------" 1 $l]
4458 $ctext insert end "$pad $header $pad\n" filesep
4460 } elseif {$diffinhdr && [string compare -length 3 $line "---"] == 0} {
4462 } elseif {$diffinhdr && [string compare -length 3 $line "+++"] == 0} {
4464 } elseif {[regexp {^@@ -([0-9]+),([0-9]+) \+([0-9]+),([0-9]+) @@(.*)} \
4465 $line match f1l f1c f2l f2c rest]} {
4466 $ctext insert end "$line\n" hunksep
4469 set x [string range $line 0 0]
4470 if {$x == "-" || $x == "+"} {
4471 set tag [expr {$x == "+"}]
4472 $ctext insert end "$line\n" d$tag
4473 } elseif {$x == " "} {
4474 $ctext insert end "$line\n"
4475 } elseif {$diffinhdr || $x == "\\"} {
4476 # e.g. "\ No newline at end of file"
4477 $ctext insert end "$line\n" filesep
4479 # Something else we don't recognize
4480 if {$curdifftag != "Comments"} {
4481 $ctext insert end "\n"
4482 $ctext tag add $curdifftag $curtagstart end
4483 set curtagstart [$ctext index "end - 1c"]
4484 set curdifftag Comments
4486 $ctext insert end "$line\n" filesep
4489 $ctext conf -state disabled
4490 if {[clock clicks -milliseconds] >= $nextupdate} {
4492 fileevent $bdf readable {}
4494 fileevent $bdf readable "getblobdiffline $bdf {$ids}"
4498 proc changediffdisp {} {
4499 global ctext diffelide
4501 $ctext tag conf d0 -elide [lindex $diffelide 0]
4502 $ctext tag conf d1 -elide [lindex $diffelide 1]
4506 global difffilestart ctext
4507 set prev [lindex $difffilestart 0]
4508 set here [$ctext index @0,0]
4509 foreach loc $difffilestart {
4510 if {[$ctext compare $loc >= $here]} {
4520 global difffilestart ctext
4521 set here [$ctext index @0,0]
4522 foreach loc $difffilestart {
4523 if {[$ctext compare $loc > $here]} {
4530 proc clear_ctext {{first 1.0}} {
4531 global ctext smarktop smarkbot
4533 set l [lindex [split $first .] 0]
4534 if {![info exists smarktop] || [$ctext compare $first < $smarktop.0]} {
4537 if {![info exists smarkbot] || [$ctext compare $first < $smarkbot.0]} {
4540 $ctext delete $first end
4543 proc incrsearch {name ix op} {
4544 global ctext searchstring searchdirn
4546 $ctext tag remove found 1.0 end
4547 if {[catch {$ctext index anchor}]} {
4548 # no anchor set, use start of selection, or of visible area
4549 set sel [$ctext tag ranges sel]
4551 $ctext mark set anchor [lindex $sel 0]
4552 } elseif {$searchdirn eq "-forwards"} {
4553 $ctext mark set anchor @0,0
4555 $ctext mark set anchor @0,[winfo height $ctext]
4558 if {$searchstring ne {}} {
4559 set here [$ctext search $searchdirn -- $searchstring anchor]
4568 global sstring ctext searchstring searchdirn
4571 $sstring icursor end
4572 set searchdirn -forwards
4573 if {$searchstring ne {}} {
4574 set sel [$ctext tag ranges sel]
4576 set start "[lindex $sel 0] + 1c"
4577 } elseif {[catch {set start [$ctext index anchor]}]} {
4580 set match [$ctext search -count mlen -- $searchstring $start]
4581 $ctext tag remove sel 1.0 end
4587 set mend "$match + $mlen c"
4588 $ctext tag add sel $match $mend
4589 $ctext mark unset anchor
4593 proc dosearchback {} {
4594 global sstring ctext searchstring searchdirn
4597 $sstring icursor end
4598 set searchdirn -backwards
4599 if {$searchstring ne {}} {
4600 set sel [$ctext tag ranges sel]
4602 set start [lindex $sel 0]
4603 } elseif {[catch {set start [$ctext index anchor]}]} {
4604 set start @0,[winfo height $ctext]
4606 set match [$ctext search -backwards -count ml -- $searchstring $start]
4607 $ctext tag remove sel 1.0 end
4613 set mend "$match + $ml c"
4614 $ctext tag add sel $match $mend
4615 $ctext mark unset anchor
4619 proc searchmark {first last} {
4620 global ctext searchstring
4624 set match [$ctext search -count mlen -- $searchstring $mend $last.end]
4625 if {$match eq {}} break
4626 set mend "$match + $mlen c"
4627 $ctext tag add found $match $mend
4631 proc searchmarkvisible {doall} {
4632 global ctext smarktop smarkbot
4634 set topline [lindex [split [$ctext index @0,0] .] 0]
4635 set botline [lindex [split [$ctext index @0,[winfo height $ctext]] .] 0]
4636 if {$doall || $botline < $smarktop || $topline > $smarkbot} {
4637 # no overlap with previous
4638 searchmark $topline $botline
4639 set smarktop $topline
4640 set smarkbot $botline
4642 if {$topline < $smarktop} {
4643 searchmark $topline [expr {$smarktop-1}]
4644 set smarktop $topline
4646 if {$botline > $smarkbot} {
4647 searchmark [expr {$smarkbot+1}] $botline
4648 set smarkbot $botline
4653 proc scrolltext {f0 f1} {
4656 .bleft.sb set $f0 $f1
4657 if {$searchstring ne {}} {
4663 global linespc charspc canvx0 canvy0 mainfont
4664 global xspc1 xspc2 lthickness
4666 set linespc [font metrics $mainfont -linespace]
4667 set charspc [font measure $mainfont "m"]
4668 set canvy0 [expr {int(3 + 0.5 * $linespc)}]
4669 set canvx0 [expr {int(3 + 0.5 * $linespc)}]
4670 set lthickness [expr {int($linespc / 9) + 1}]
4671 set xspc1(0) $linespc
4679 set ymax [lindex [$canv cget -scrollregion] 3]
4680 if {$ymax eq {} || $ymax == 0} return
4681 set span [$canv yview]
4684 allcanvs yview moveto [lindex $span 0]
4686 if {[info exists selectedline]} {
4687 selectline $selectedline 0
4688 allcanvs yview moveto [lindex $span 0]
4692 proc incrfont {inc} {
4693 global mainfont textfont ctext canv phase
4694 global stopped entries
4696 set mainfont [lreplace $mainfont 1 1 [expr {[lindex $mainfont 1] + $inc}]]
4697 set textfont [lreplace $textfont 1 1 [expr {[lindex $textfont 1] + $inc}]]
4699 $ctext conf -font $textfont
4700 $ctext tag conf filesep -font [concat $textfont bold]
4701 foreach e $entries {
4702 $e conf -font $mainfont
4704 if {$phase eq "getcommits"} {
4705 $canv itemconf textitems -font $mainfont
4711 global sha1entry sha1string
4712 if {[string length $sha1string] == 40} {
4713 $sha1entry delete 0 end
4717 proc sha1change {n1 n2 op} {
4718 global sha1string currentid sha1but
4719 if {$sha1string == {}
4720 || ([info exists currentid] && $sha1string == $currentid)} {
4725 if {[$sha1but cget -state] == $state} return
4726 if {$state == "normal"} {
4727 $sha1but conf -state normal -relief raised -text "Goto: "
4729 $sha1but conf -state disabled -relief flat -text "SHA1 ID: "
4733 proc gotocommit {} {
4734 global sha1string currentid commitrow tagids headids
4735 global displayorder numcommits curview
4737 if {$sha1string == {}
4738 || ([info exists currentid] && $sha1string == $currentid)} return
4739 if {[info exists tagids($sha1string)]} {
4740 set id $tagids($sha1string)
4741 } elseif {[info exists headids($sha1string)]} {
4742 set id $headids($sha1string)
4744 set id [string tolower $sha1string]
4745 if {[regexp {^[0-9a-f]{4,39}$} $id]} {
4747 foreach i $displayorder {
4748 if {[string match $id* $i]} {
4752 if {$matches ne {}} {
4753 if {[llength $matches] > 1} {
4754 error_popup "Short SHA1 id $id is ambiguous"
4757 set id [lindex $matches 0]
4761 if {[info exists commitrow($curview,$id)]} {
4762 selectline $commitrow($curview,$id) 1
4765 if {[regexp {^[0-9a-fA-F]{4,}$} $sha1string]} {
4770 error_popup "$type $sha1string is not known"
4773 proc lineenter {x y id} {
4774 global hoverx hovery hoverid hovertimer
4775 global commitinfo canv
4777 if {![info exists commitinfo($id)] && ![getcommit $id]} return
4781 if {[info exists hovertimer]} {
4782 after cancel $hovertimer
4784 set hovertimer [after 500 linehover]
4788 proc linemotion {x y id} {
4789 global hoverx hovery hoverid hovertimer
4791 if {[info exists hoverid] && $id == $hoverid} {
4794 if {[info exists hovertimer]} {
4795 after cancel $hovertimer
4797 set hovertimer [after 500 linehover]
4801 proc lineleave {id} {
4802 global hoverid hovertimer canv
4804 if {[info exists hoverid] && $id == $hoverid} {
4806 if {[info exists hovertimer]} {
4807 after cancel $hovertimer
4815 global hoverx hovery hoverid hovertimer
4816 global canv linespc lthickness
4817 global commitinfo mainfont
4819 set text [lindex $commitinfo($hoverid) 0]
4820 set ymax [lindex [$canv cget -scrollregion] 3]
4821 if {$ymax == {}} return
4822 set yfrac [lindex [$canv yview] 0]
4823 set x [expr {$hoverx + 2 * $linespc}]
4824 set y [expr {$hovery + $yfrac * $ymax - $linespc / 2}]
4825 set x0 [expr {$x - 2 * $lthickness}]
4826 set y0 [expr {$y - 2 * $lthickness}]
4827 set x1 [expr {$x + [font measure $mainfont $text] + 2 * $lthickness}]
4828 set y1 [expr {$y + $linespc + 2 * $lthickness}]
4829 set t [$canv create rectangle $x0 $y0 $x1 $y1 \
4830 -fill \#ffff80 -outline black -width 1 -tags hover]
4832 set t [$canv create text $x $y -anchor nw -text $text -tags hover \
4837 proc clickisonarrow {id y} {
4840 set ranges [rowranges $id]
4841 set thresh [expr {2 * $lthickness + 6}]
4842 set n [expr {[llength $ranges] - 1}]
4843 for {set i 1} {$i < $n} {incr i} {
4844 set row [lindex $ranges $i]
4845 if {abs([yc $row] - $y) < $thresh} {
4852 proc arrowjump {id n y} {
4855 # 1 <-> 2, 3 <-> 4, etc...
4856 set n [expr {(($n - 1) ^ 1) + 1}]
4857 set row [lindex [rowranges $id] $n]
4859 set ymax [lindex [$canv cget -scrollregion] 3]
4860 if {$ymax eq {} || $ymax <= 0} return
4861 set view [$canv yview]
4862 set yspan [expr {[lindex $view 1] - [lindex $view 0]}]
4863 set yfrac [expr {$yt / $ymax - $yspan / 2}]
4867 allcanvs yview moveto $yfrac
4870 proc lineclick {x y id isnew} {
4871 global ctext commitinfo children canv thickerline curview
4873 if {![info exists commitinfo($id)] && ![getcommit $id]} return
4878 # draw this line thicker than normal
4882 set ymax [lindex [$canv cget -scrollregion] 3]
4883 if {$ymax eq {}} return
4884 set yfrac [lindex [$canv yview] 0]
4885 set y [expr {$y + $yfrac * $ymax}]
4887 set dirn [clickisonarrow $id $y]
4889 arrowjump $id $dirn $y
4894 addtohistory [list lineclick $x $y $id 0]
4896 # fill the details pane with info about this line
4897 $ctext conf -state normal
4899 $ctext tag conf link -foreground blue -underline 1
4900 $ctext tag bind link <Enter> { %W configure -cursor hand2 }
4901 $ctext tag bind link <Leave> { %W configure -cursor $curtextcursor }
4902 $ctext insert end "Parent:\t"
4903 $ctext insert end $id [list link link0]
4904 $ctext tag bind link0 <1> [list selbyid $id]
4905 set info $commitinfo($id)
4906 $ctext insert end "\n\t[lindex $info 0]\n"
4907 $ctext insert end "\tAuthor:\t[lindex $info 1]\n"
4908 set date [formatdate [lindex $info 2]]
4909 $ctext insert end "\tDate:\t$date\n"
4910 set kids $children($curview,$id)
4912 $ctext insert end "\nChildren:"
4914 foreach child $kids {
4916 if {![info exists commitinfo($child)] && ![getcommit $child]} continue
4917 set info $commitinfo($child)
4918 $ctext insert end "\n\t"
4919 $ctext insert end $child [list link link$i]
4920 $ctext tag bind link$i <1> [list selbyid $child]
4921 $ctext insert end "\n\t[lindex $info 0]"
4922 $ctext insert end "\n\tAuthor:\t[lindex $info 1]"
4923 set date [formatdate [lindex $info 2]]
4924 $ctext insert end "\n\tDate:\t$date\n"
4927 $ctext conf -state disabled
4931 proc normalline {} {
4933 if {[info exists thickerline]} {
4941 global commitrow curview
4942 if {[info exists commitrow($curview,$id)]} {
4943 selectline $commitrow($curview,$id) 1
4949 if {![info exists startmstime]} {
4950 set startmstime [clock clicks -milliseconds]
4952 return [format "%.3f" [expr {([clock click -milliseconds] - $startmstime) / 1000.0}]]
4955 proc rowmenu {x y id} {
4956 global rowctxmenu commitrow selectedline rowmenuid curview
4958 if {![info exists selectedline]
4959 || $commitrow($curview,$id) eq $selectedline} {
4964 $rowctxmenu entryconfigure "Diff this*" -state $state
4965 $rowctxmenu entryconfigure "Diff selected*" -state $state
4966 $rowctxmenu entryconfigure "Make patch" -state $state
4968 tk_popup $rowctxmenu $x $y
4971 proc diffvssel {dirn} {
4972 global rowmenuid selectedline displayorder
4974 if {![info exists selectedline]} return
4976 set oldid [lindex $displayorder $selectedline]
4977 set newid $rowmenuid
4979 set oldid $rowmenuid
4980 set newid [lindex $displayorder $selectedline]
4982 addtohistory [list doseldiff $oldid $newid]
4983 doseldiff $oldid $newid
4986 proc doseldiff {oldid newid} {
4990 $ctext conf -state normal
4993 $ctext insert end "From "
4994 $ctext tag conf link -foreground blue -underline 1
4995 $ctext tag bind link <Enter> { %W configure -cursor hand2 }
4996 $ctext tag bind link <Leave> { %W configure -cursor $curtextcursor }
4997 $ctext tag bind link0 <1> [list selbyid $oldid]
4998 $ctext insert end $oldid [list link link0]
4999 $ctext insert end "\n "
5000 $ctext insert end [lindex $commitinfo($oldid) 0]
5001 $ctext insert end "\n\nTo "
5002 $ctext tag bind link1 <1> [list selbyid $newid]
5003 $ctext insert end $newid [list link link1]
5004 $ctext insert end "\n "
5005 $ctext insert end [lindex $commitinfo($newid) 0]
5006 $ctext insert end "\n"
5007 $ctext conf -state disabled
5008 $ctext tag delete Comments
5009 $ctext tag remove found 1.0 end
5010 startdiff [list $oldid $newid]
5014 global rowmenuid currentid commitinfo patchtop patchnum
5016 if {![info exists currentid]} return
5017 set oldid $currentid
5018 set oldhead [lindex $commitinfo($oldid) 0]
5019 set newid $rowmenuid
5020 set newhead [lindex $commitinfo($newid) 0]
5023 catch {destroy $top}
5025 label $top.title -text "Generate patch"
5026 grid $top.title - -pady 10
5027 label $top.from -text "From:"
5028 entry $top.fromsha1 -width 40 -relief flat
5029 $top.fromsha1 insert 0 $oldid
5030 $top.fromsha1 conf -state readonly
5031 grid $top.from $top.fromsha1 -sticky w
5032 entry $top.fromhead -width 60 -relief flat
5033 $top.fromhead insert 0 $oldhead
5034 $top.fromhead conf -state readonly
5035 grid x $top.fromhead -sticky w
5036 label $top.to -text "To:"
5037 entry $top.tosha1 -width 40 -relief flat
5038 $top.tosha1 insert 0 $newid
5039 $top.tosha1 conf -state readonly
5040 grid $top.to $top.tosha1 -sticky w
5041 entry $top.tohead -width 60 -relief flat
5042 $top.tohead insert 0 $newhead
5043 $top.tohead conf -state readonly
5044 grid x $top.tohead -sticky w
5045 button $top.rev -text "Reverse" -command mkpatchrev -padx 5
5046 grid $top.rev x -pady 10
5047 label $top.flab -text "Output file:"
5048 entry $top.fname -width 60
5049 $top.fname insert 0 [file normalize "patch$patchnum.patch"]
5051 grid $top.flab $top.fname -sticky w
5053 button $top.buts.gen -text "Generate" -command mkpatchgo
5054 button $top.buts.can -text "Cancel" -command mkpatchcan
5055 grid $top.buts.gen $top.buts.can
5056 grid columnconfigure $top.buts 0 -weight 1 -uniform a
5057 grid columnconfigure $top.buts 1 -weight 1 -uniform a
5058 grid $top.buts - -pady 10 -sticky ew
5062 proc mkpatchrev {} {
5065 set oldid [$patchtop.fromsha1 get]
5066 set oldhead [$patchtop.fromhead get]
5067 set newid [$patchtop.tosha1 get]
5068 set newhead [$patchtop.tohead get]
5069 foreach e [list fromsha1 fromhead tosha1 tohead] \
5070 v [list $newid $newhead $oldid $oldhead] {
5071 $patchtop.$e conf -state normal
5072 $patchtop.$e delete 0 end
5073 $patchtop.$e insert 0 $v
5074 $patchtop.$e conf -state readonly
5081 set oldid [$patchtop.fromsha1 get]
5082 set newid [$patchtop.tosha1 get]
5083 set fname [$patchtop.fname get]
5084 if {[catch {exec git diff-tree -p $oldid $newid >$fname &} err]} {
5085 error_popup "Error creating patch: $err"
5087 catch {destroy $patchtop}
5091 proc mkpatchcan {} {
5094 catch {destroy $patchtop}
5099 global rowmenuid mktagtop commitinfo
5103 catch {destroy $top}
5105 label $top.title -text "Create tag"
5106 grid $top.title - -pady 10
5107 label $top.id -text "ID:"
5108 entry $top.sha1 -width 40 -relief flat
5109 $top.sha1 insert 0 $rowmenuid
5110 $top.sha1 conf -state readonly
5111 grid $top.id $top.sha1 -sticky w
5112 entry $top.head -width 60 -relief flat
5113 $top.head insert 0 [lindex $commitinfo($rowmenuid) 0]
5114 $top.head conf -state readonly
5115 grid x $top.head -sticky w
5116 label $top.tlab -text "Tag name:"
5117 entry $top.tag -width 60
5118 grid $top.tlab $top.tag -sticky w
5120 button $top.buts.gen -text "Create" -command mktaggo
5121 button $top.buts.can -text "Cancel" -command mktagcan
5122 grid $top.buts.gen $top.buts.can
5123 grid columnconfigure $top.buts 0 -weight 1 -uniform a
5124 grid columnconfigure $top.buts 1 -weight 1 -uniform a
5125 grid $top.buts - -pady 10 -sticky ew
5130 global mktagtop env tagids idtags
5132 set id [$mktagtop.sha1 get]
5133 set tag [$mktagtop.tag get]
5135 error_popup "No tag name specified"
5138 if {[info exists tagids($tag)]} {
5139 error_popup "Tag \"$tag\" already exists"
5144 set fname [file join $dir "refs/tags" $tag]
5145 set f [open $fname w]
5149 error_popup "Error creating tag: $err"
5153 set tagids($tag) $id
5154 lappend idtags($id) $tag
5159 proc redrawtags {id} {
5160 global canv linehtag commitrow idpos selectedline curview
5161 global mainfont canvxmax
5163 if {![info exists commitrow($curview,$id)]} return
5164 drawcmitrow $commitrow($curview,$id)
5165 $canv delete tag.$id
5166 set xt [eval drawtags $id $idpos($id)]
5167 $canv coords $linehtag($commitrow($curview,$id)) $xt [lindex $idpos($id) 2]
5168 set text [$canv itemcget $linehtag($commitrow($curview,$id)) -text]
5169 set xr [expr {$xt + [font measure $mainfont $text]}]
5170 if {$xr > $canvxmax} {
5174 if {[info exists selectedline]
5175 && $selectedline == $commitrow($curview,$id)} {
5176 selectline $selectedline 0
5183 catch {destroy $mktagtop}
5192 proc writecommit {} {
5193 global rowmenuid wrcomtop commitinfo wrcomcmd
5195 set top .writecommit
5197 catch {destroy $top}
5199 label $top.title -text "Write commit to file"
5200 grid $top.title - -pady 10
5201 label $top.id -text "ID:"
5202 entry $top.sha1 -width 40 -relief flat
5203 $top.sha1 insert 0 $rowmenuid
5204 $top.sha1 conf -state readonly
5205 grid $top.id $top.sha1 -sticky w
5206 entry $top.head -width 60 -relief flat
5207 $top.head insert 0 [lindex $commitinfo($rowmenuid) 0]
5208 $top.head conf -state readonly
5209 grid x $top.head -sticky w
5210 label $top.clab -text "Command:"
5211 entry $top.cmd -width 60 -textvariable wrcomcmd
5212 grid $top.clab $top.cmd -sticky w -pady 10
5213 label $top.flab -text "Output file:"
5214 entry $top.fname -width 60
5215 $top.fname insert 0 [file normalize "commit-[string range $rowmenuid 0 6]"]
5216 grid $top.flab $top.fname -sticky w
5218 button $top.buts.gen -text "Write" -command wrcomgo
5219 button $top.buts.can -text "Cancel" -command wrcomcan
5220 grid $top.buts.gen $top.buts.can
5221 grid columnconfigure $top.buts 0 -weight 1 -uniform a
5222 grid columnconfigure $top.buts 1 -weight 1 -uniform a
5223 grid $top.buts - -pady 10 -sticky ew
5230 set id [$wrcomtop.sha1 get]
5231 set cmd "echo $id | [$wrcomtop.cmd get]"
5232 set fname [$wrcomtop.fname get]
5233 if {[catch {exec sh -c $cmd >$fname &} err]} {
5234 error_popup "Error writing commit: $err"
5236 catch {destroy $wrcomtop}
5243 catch {destroy $wrcomtop}
5248 global rowmenuid mkbrtop
5251 catch {destroy $top}
5253 label $top.title -text "Create new branch"
5254 grid $top.title - -pady 10
5255 label $top.id -text "ID:"
5256 entry $top.sha1 -width 40 -relief flat
5257 $top.sha1 insert 0 $rowmenuid
5258 $top.sha1 conf -state readonly
5259 grid $top.id $top.sha1 -sticky w
5260 label $top.nlab -text "Name:"
5261 entry $top.name -width 40
5262 grid $top.nlab $top.name -sticky w
5264 button $top.buts.go -text "Create" -command [list mkbrgo $top]
5265 button $top.buts.can -text "Cancel" -command "catch {destroy $top}"
5266 grid $top.buts.go $top.buts.can
5267 grid columnconfigure $top.buts 0 -weight 1 -uniform a
5268 grid columnconfigure $top.buts 1 -weight 1 -uniform a
5269 grid $top.buts - -pady 10 -sticky ew
5274 global headids idheads
5276 set name [$top.name get]
5277 set id [$top.sha1 get]
5279 error_popup "Please specify a name for the new branch"
5282 catch {destroy $top}
5286 exec git branch $name $id
5292 # XXX should update list of heads displayed for selected commit
5298 proc cherrypick {} {
5299 global rowmenuid curview commitrow
5300 global mainhead desc_heads anc_tags desc_tags allparents allchildren
5302 if {[info exists desc_heads($rowmenuid)]
5303 && [lsearch -exact $desc_heads($rowmenuid) $mainhead] >= 0} {
5304 set ok [confirm_popup "Commit [string range $rowmenuid 0 7] is already\
5305 included in branch $mainhead -- really re-apply it?"]
5310 set oldhead [exec git rev-parse HEAD]
5311 # Unfortunately git-cherry-pick writes stuff to stderr even when
5312 # no error occurs, and exec takes that as an indication of error...
5313 if {[catch {exec sh -c "git cherry-pick -r $rowmenuid 2>&1"} err]} {
5318 set newhead [exec git rev-parse HEAD]
5319 if {$newhead eq $oldhead} {
5321 error_popup "No changes committed"
5324 set allparents($newhead) $oldhead
5325 lappend allchildren($oldhead) $newhead
5326 set desc_heads($newhead) $mainhead
5327 if {[info exists anc_tags($oldhead)]} {
5328 set anc_tags($newhead) $anc_tags($oldhead)
5330 set desc_tags($newhead) {}
5331 if {[info exists commitrow($curview,$oldhead)]} {
5332 insertrow $commitrow($curview,$oldhead) $newhead
5333 if {$mainhead ne {}} {
5334 movedhead $newhead $mainhead
5342 # context menu for a head
5343 proc headmenu {x y id head} {
5344 global headmenuid headmenuhead headctxmenu
5347 set headmenuhead $head
5348 tk_popup $headctxmenu $x $y
5352 global headmenuid headmenuhead mainhead headids
5354 # check the tree is clean first??
5355 set oldmainhead $mainhead
5359 exec git checkout -q $headmenuhead
5365 set mainhead $headmenuhead
5366 if {[info exists headids($oldmainhead)]} {
5367 redrawtags $headids($oldmainhead)
5369 redrawtags $headmenuid
5374 global desc_heads headmenuid headmenuhead mainhead
5375 global headids idheads
5377 set head $headmenuhead
5379 if {$head eq $mainhead} {
5380 error_popup "Cannot delete the currently checked-out branch"
5383 if {$desc_heads($id) eq $head} {
5384 # the stuff on this branch isn't on any other branch
5385 if {![confirm_popup "The commits on branch $head aren't on any other\
5386 branch.\nReally delete branch $head?"]} return
5390 if {[catch {exec git branch -D $head} err]} {
5395 removedhead $id $head
5400 # Stuff for finding nearby tags
5401 proc getallcommits {} {
5402 global allcstart allcommits allcfd allids
5405 set fd [open [concat | git rev-list --all --topo-order --parents] r]
5407 fconfigure $fd -blocking 0
5408 set allcommits "reading"
5413 proc discardallcommits {} {
5414 global allparents allchildren allcommits allcfd
5415 global desc_tags anc_tags alldtags tagisdesc allids desc_heads
5417 if {![info exists allcommits]} return
5418 if {$allcommits eq "reading"} {
5419 catch {close $allcfd}
5421 foreach v {allcommits allchildren allparents allids desc_tags anc_tags
5422 alldtags tagisdesc desc_heads} {
5427 proc restartgetall {fd} {
5430 fileevent $fd readable [list getallclines $fd]
5431 set allcstart [clock clicks -milliseconds]
5434 proc combine_dtags {l1 l2} {
5435 global tagisdesc notfirstd
5437 set res [lsort -unique [concat $l1 $l2]]
5438 for {set i 0} {$i < [llength $res]} {incr i} {
5439 set x [lindex $res $i]
5440 for {set j [expr {$i+1}]} {$j < [llength $res]} {} {
5441 set y [lindex $res $j]
5442 if {[info exists tagisdesc($x,$y)]} {
5443 if {$tagisdesc($x,$y) > 0} {
5444 # x is a descendent of y, exclude x
5445 set res [lreplace $res $i $i]
5449 # y is a descendent of x, exclude y
5450 set res [lreplace $res $j $j]
5453 # no relation, keep going
5461 proc combine_atags {l1 l2} {
5464 set res [lsort -unique [concat $l1 $l2]]
5465 for {set i 0} {$i < [llength $res]} {incr i} {
5466 set x [lindex $res $i]
5467 for {set j [expr {$i+1}]} {$j < [llength $res]} {} {
5468 set y [lindex $res $j]
5469 if {[info exists tagisdesc($x,$y)]} {
5470 if {$tagisdesc($x,$y) < 0} {
5471 # x is an ancestor of y, exclude x
5472 set res [lreplace $res $i $i]
5476 # y is an ancestor of x, exclude y
5477 set res [lreplace $res $j $j]
5480 # no relation, keep going
5488 proc forward_pass {id children} {
5489 global idtags desc_tags idheads desc_heads alldtags tagisdesc
5493 foreach child $children {
5494 if {[info exists idtags($child)]} {
5495 set ctags [list $child]
5497 set ctags $desc_tags($child)
5501 } elseif {$ctags ne $dtags} {
5502 set dtags [combine_dtags $dtags $ctags]
5504 set cheads $desc_heads($child)
5505 if {$dheads eq {}} {
5507 } elseif {$cheads ne $dheads} {
5508 set dheads [lsort -unique [concat $dheads $cheads]]
5511 set desc_tags($id) $dtags
5512 if {[info exists idtags($id)]} {
5514 foreach tag $dtags {
5515 set adt [concat $adt $alldtags($tag)]
5517 set adt [lsort -unique $adt]
5518 set alldtags($id) $adt
5520 set tagisdesc($id,$tag) -1
5521 set tagisdesc($tag,$id) 1
5524 if {[info exists idheads($id)]} {
5525 set dheads [concat $dheads $idheads($id)]
5527 set desc_heads($id) $dheads
5530 proc getallclines {fd} {
5531 global allparents allchildren allcommits allcstart
5532 global desc_tags anc_tags idtags tagisdesc allids
5533 global idheads travindex
5535 while {[gets $fd line] >= 0} {
5536 set id [lindex $line 0]
5538 set olds [lrange $line 1 end]
5539 set allparents($id) $olds
5540 if {![info exists allchildren($id)]} {
5541 set allchildren($id) {}
5544 lappend allchildren($p) $id
5546 # compute nearest tagged descendents as we go
5547 # also compute descendent heads
5548 forward_pass $id $allchildren($id)
5549 if {[clock clicks -milliseconds] - $allcstart >= 50} {
5550 fileevent $fd readable {}
5551 after idle restartgetall $fd
5556 set travindex [llength $allids]
5557 set allcommits "traversing"
5558 after idle restartatags
5559 if {[catch {close $fd} err]} {
5560 error_popup "Error reading full commit graph: $err.\n\
5561 Results may be incomplete."
5566 # walk backward through the tree and compute nearest tagged ancestors
5567 proc restartatags {} {
5568 global allids allparents idtags anc_tags travindex
5570 set t0 [clock clicks -milliseconds]
5572 while {[incr i -1] >= 0} {
5573 set id [lindex $allids $i]
5575 foreach p $allparents($id) {
5576 if {[info exists idtags($p)]} {
5579 set ptags $anc_tags($p)
5583 } elseif {$ptags ne $atags} {
5584 set atags [combine_atags $atags $ptags]
5587 set anc_tags($id) $atags
5588 if {[clock clicks -milliseconds] - $t0 >= 50} {
5590 after idle restartatags
5594 set allcommits "done"
5600 # update the desc_tags and anc_tags arrays for a new tag just added
5601 proc addedtag {id} {
5602 global desc_tags anc_tags allparents allchildren allcommits
5603 global idtags tagisdesc alldtags
5605 if {![info exists desc_tags($id)]} return
5606 set adt $desc_tags($id)
5607 foreach t $desc_tags($id) {
5608 set adt [concat $adt $alldtags($t)]
5610 set adt [lsort -unique $adt]
5611 set alldtags($id) $adt
5613 set tagisdesc($id,$t) -1
5614 set tagisdesc($t,$id) 1
5616 if {[info exists anc_tags($id)]} {
5617 set todo $anc_tags($id)
5618 while {$todo ne {}} {
5619 set do [lindex $todo 0]
5620 set todo [lrange $todo 1 end]
5621 if {[info exists tagisdesc($id,$do)]} continue
5622 set tagisdesc($do,$id) -1
5623 set tagisdesc($id,$do) 1
5624 if {[info exists anc_tags($do)]} {
5625 set todo [concat $todo $anc_tags($do)]
5630 set lastold $desc_tags($id)
5631 set lastnew [list $id]
5634 set todo $allparents($id)
5635 while {$todo ne {}} {
5636 set do [lindex $todo 0]
5637 set todo [lrange $todo 1 end]
5638 if {![info exists desc_tags($do)]} continue
5639 if {$desc_tags($do) ne $lastold} {
5640 set lastold $desc_tags($do)
5641 set lastnew [combine_dtags $lastold [list $id]]
5644 if {$lastold eq $lastnew} continue
5645 set desc_tags($do) $lastnew
5647 if {![info exists idtags($do)]} {
5648 set todo [concat $todo $allparents($do)]
5652 if {![info exists anc_tags($id)]} return
5653 set lastold $anc_tags($id)
5654 set lastnew [list $id]
5657 set todo $allchildren($id)
5658 while {$todo ne {}} {
5659 set do [lindex $todo 0]
5660 set todo [lrange $todo 1 end]
5661 if {![info exists anc_tags($do)]} continue
5662 if {$anc_tags($do) ne $lastold} {
5663 set lastold $anc_tags($do)
5664 set lastnew [combine_atags $lastold [list $id]]
5667 if {$lastold eq $lastnew} continue
5668 set anc_tags($do) $lastnew
5670 if {![info exists idtags($do)]} {
5671 set todo [concat $todo $allchildren($do)]
5676 # update the desc_heads array for a new head just added
5677 proc addedhead {hid head} {
5678 global desc_heads allparents headids idheads
5680 set headids($head) $hid
5681 lappend idheads($hid) $head
5683 set todo [list $hid]
5684 while {$todo ne {}} {
5685 set do [lindex $todo 0]
5686 set todo [lrange $todo 1 end]
5687 if {![info exists desc_heads($do)] ||
5688 [lsearch -exact $desc_heads($do) $head] >= 0} continue
5689 set oldheads $desc_heads($do)
5690 lappend desc_heads($do) $head
5691 set heads $desc_heads($do)
5693 set p $allparents($do)
5694 if {[llength $p] != 1 || ![info exists desc_heads($p)] ||
5695 $desc_heads($p) ne $oldheads} break
5697 set desc_heads($do) $heads
5699 set todo [concat $todo $p]
5703 # update the desc_heads array for a head just removed
5704 proc removedhead {hid head} {
5705 global desc_heads allparents headids idheads
5707 unset headids($head)
5708 if {$idheads($hid) eq $head} {
5711 set i [lsearch -exact $idheads($hid) $head]
5713 set idheads($hid) [lreplace $idheads($hid) $i $i]
5717 set todo [list $hid]
5718 while {$todo ne {}} {
5719 set do [lindex $todo 0]
5720 set todo [lrange $todo 1 end]
5721 if {![info exists desc_heads($do)]} continue
5722 set i [lsearch -exact $desc_heads($do) $head]
5723 if {$i < 0} continue
5724 set oldheads $desc_heads($do)
5725 set heads [lreplace $desc_heads($do) $i $i]
5727 set desc_heads($do) $heads
5728 set p $allparents($do)
5729 if {[llength $p] != 1 || ![info exists desc_heads($p)] ||
5730 $desc_heads($p) ne $oldheads} break
5733 set todo [concat $todo $p]
5737 # update things for a head moved to a child of its previous location
5738 proc movedhead {id name} {
5739 global headids idheads
5741 set oldid $headids($name)
5742 set headids($name) $id
5743 if {$idheads($oldid) eq $name} {
5744 unset idheads($oldid)
5746 set i [lsearch -exact $idheads($oldid) $name]
5748 set idheads($oldid) [lreplace $idheads($oldid) $i $i]
5751 lappend idheads($id) $name
5754 proc changedrefs {} {
5755 global desc_heads desc_tags anc_tags allcommits allids
5756 global allchildren allparents idtags travindex
5758 if {![info exists allcommits]} return
5759 catch {unset desc_heads}
5760 catch {unset desc_tags}
5761 catch {unset anc_tags}
5762 catch {unset alldtags}
5763 catch {unset tagisdesc}
5764 foreach id $allids {
5765 forward_pass $id $allchildren($id)
5767 if {$allcommits ne "reading"} {
5768 set travindex [llength $allids]
5769 if {$allcommits ne "traversing"} {
5770 set allcommits "traversing"
5771 after idle restartatags
5776 proc rereadrefs {} {
5777 global idtags idheads idotherrefs mainhead
5779 set refids [concat [array names idtags] \
5780 [array names idheads] [array names idotherrefs]]
5781 foreach id $refids {
5782 if {![info exists ref($id)]} {
5783 set ref($id) [listrefs $id]
5786 set oldmainhead $mainhead
5789 set refids [lsort -unique [concat $refids [array names idtags] \
5790 [array names idheads] [array names idotherrefs]]]
5791 foreach id $refids {
5792 set v [listrefs $id]
5793 if {![info exists ref($id)] || $ref($id) != $v ||
5794 ($id eq $oldmainhead && $id ne $mainhead) ||
5795 ($id eq $mainhead && $id ne $oldmainhead)} {
5801 proc listrefs {id} {
5802 global idtags idheads idotherrefs
5805 if {[info exists idtags($id)]} {
5809 if {[info exists idheads($id)]} {
5813 if {[info exists idotherrefs($id)]} {
5814 set z $idotherrefs($id)
5816 return [list $x $y $z]
5819 proc showtag {tag isnew} {
5820 global ctext tagcontents tagids linknum
5823 addtohistory [list showtag $tag 0]
5825 $ctext conf -state normal
5828 if {[info exists tagcontents($tag)]} {
5829 set text $tagcontents($tag)
5831 set text "Tag: $tag\nId: $tagids($tag)"
5833 appendwithlinks $text {}
5834 $ctext conf -state disabled
5846 global maxwidth maxgraphpct diffopts
5847 global oldprefs prefstop showneartags
5848 global bgcolor fgcolor ctext diffcolors
5853 if {[winfo exists $top]} {
5857 foreach v {maxwidth maxgraphpct diffopts showneartags} {
5858 set oldprefs($v) [set $v]
5861 wm title $top "Gitk preferences"
5862 label $top.ldisp -text "Commit list display options"
5863 $top.ldisp configure -font $uifont
5864 grid $top.ldisp - -sticky w -pady 10
5865 label $top.spacer -text " "
5866 label $top.maxwidthl -text "Maximum graph width (lines)" \
5868 spinbox $top.maxwidth -from 0 -to 100 -width 4 -textvariable maxwidth
5869 grid $top.spacer $top.maxwidthl $top.maxwidth -sticky w
5870 label $top.maxpctl -text "Maximum graph width (% of pane)" \
5872 spinbox $top.maxpct -from 1 -to 100 -width 4 -textvariable maxgraphpct
5873 grid x $top.maxpctl $top.maxpct -sticky w
5875 label $top.ddisp -text "Diff display options"
5876 $top.ddisp configure -font $uifont
5877 grid $top.ddisp - -sticky w -pady 10
5878 label $top.diffoptl -text "Options for diff program" \
5880 entry $top.diffopt -width 20 -textvariable diffopts
5881 grid x $top.diffoptl $top.diffopt -sticky w
5883 label $top.ntag.l -text "Display nearby tags" -font optionfont
5884 checkbutton $top.ntag.b -variable showneartags
5885 pack $top.ntag.b $top.ntag.l -side left
5886 grid x $top.ntag -sticky w
5888 label $top.cdisp -text "Colors: press to choose"
5889 $top.cdisp configure -font $uifont
5890 grid $top.cdisp - -sticky w -pady 10
5891 label $top.bg -padx 40 -relief sunk -background $bgcolor
5892 button $top.bgbut -text "Background" -font optionfont \
5893 -command [list choosecolor bgcolor 0 $top.bg background setbg]
5894 grid x $top.bgbut $top.bg -sticky w
5895 label $top.fg -padx 40 -relief sunk -background $fgcolor
5896 button $top.fgbut -text "Foreground" -font optionfont \
5897 -command [list choosecolor fgcolor 0 $top.fg foreground setfg]
5898 grid x $top.fgbut $top.fg -sticky w
5899 label $top.diffold -padx 40 -relief sunk -background [lindex $diffcolors 0]
5900 button $top.diffoldbut -text "Diff: old lines" -font optionfont \
5901 -command [list choosecolor diffcolors 0 $top.diffold "diff old lines" \
5902 [list $ctext tag conf d0 -foreground]]
5903 grid x $top.diffoldbut $top.diffold -sticky w
5904 label $top.diffnew -padx 40 -relief sunk -background [lindex $diffcolors 1]
5905 button $top.diffnewbut -text "Diff: new lines" -font optionfont \
5906 -command [list choosecolor diffcolors 1 $top.diffnew "diff new lines" \
5907 [list $ctext tag conf d1 -foreground]]
5908 grid x $top.diffnewbut $top.diffnew -sticky w
5909 label $top.hunksep -padx 40 -relief sunk -background [lindex $diffcolors 2]
5910 button $top.hunksepbut -text "Diff: hunk header" -font optionfont \
5911 -command [list choosecolor diffcolors 2 $top.hunksep \
5912 "diff hunk header" \
5913 [list $ctext tag conf hunksep -foreground]]
5914 grid x $top.hunksepbut $top.hunksep -sticky w
5917 button $top.buts.ok -text "OK" -command prefsok -default active
5918 $top.buts.ok configure -font $uifont
5919 button $top.buts.can -text "Cancel" -command prefscan -default normal
5920 $top.buts.can configure -font $uifont
5921 grid $top.buts.ok $top.buts.can
5922 grid columnconfigure $top.buts 0 -weight 1 -uniform a
5923 grid columnconfigure $top.buts 1 -weight 1 -uniform a
5924 grid $top.buts - - -pady 10 -sticky ew
5925 bind $top <Visibility> "focus $top.buts.ok"
5928 proc choosecolor {v vi w x cmd} {
5931 set c [tk_chooseColor -initialcolor [lindex [set $v] $vi] \
5932 -title "Gitk: choose color for $x"]
5933 if {$c eq {}} return
5934 $w conf -background $c
5943 $w conf -background $c
5951 $w conf -foreground $c
5953 allcanvs itemconf text -fill $c
5954 $canv itemconf circle -outline $c
5958 global maxwidth maxgraphpct diffopts
5959 global oldprefs prefstop showneartags
5961 foreach v {maxwidth maxgraphpct diffopts showneartags} {
5962 set $v $oldprefs($v)
5964 catch {destroy $prefstop}
5969 global maxwidth maxgraphpct
5970 global oldprefs prefstop showneartags
5972 catch {destroy $prefstop}
5974 if {$maxwidth != $oldprefs(maxwidth)
5975 || $maxgraphpct != $oldprefs(maxgraphpct)} {
5977 } elseif {$showneartags != $oldprefs(showneartags)} {
5982 proc formatdate {d} {
5983 return [clock format $d -format "%Y-%m-%d %H:%M:%S"]
5986 # This list of encoding names and aliases is distilled from
5987 # http://www.iana.org/assignments/character-sets.
5988 # Not all of them are supported by Tcl.
5989 set encoding_aliases {
5990 { ANSI_X3.4-1968 iso-ir-6 ANSI_X3.4-1986 ISO_646.irv:1991 ASCII
5991 ISO646-US US-ASCII us IBM367 cp367 csASCII }
5992 { ISO-10646-UTF-1 csISO10646UTF1 }
5993 { ISO_646.basic:1983 ref csISO646basic1983 }
5994 { INVARIANT csINVARIANT }
5995 { ISO_646.irv:1983 iso-ir-2 irv csISO2IntlRefVersion }
5996 { BS_4730 iso-ir-4 ISO646-GB gb uk csISO4UnitedKingdom }
5997 { NATS-SEFI iso-ir-8-1 csNATSSEFI }
5998 { NATS-SEFI-ADD iso-ir-8-2 csNATSSEFIADD }
5999 { NATS-DANO iso-ir-9-1 csNATSDANO }
6000 { NATS-DANO-ADD iso-ir-9-2 csNATSDANOADD }
6001 { SEN_850200_B iso-ir-10 FI ISO646-FI ISO646-SE se csISO10Swedish }
6002 { SEN_850200_C iso-ir-11 ISO646-SE2 se2 csISO11SwedishForNames }
6003 { KS_C_5601-1987 iso-ir-149 KS_C_5601-1989 KSC_5601 korean csKSC56011987 }
6004 { ISO-2022-KR csISO2022KR }
6006 { ISO-2022-JP csISO2022JP }
6007 { ISO-2022-JP-2 csISO2022JP2 }
6008 { JIS_C6220-1969-jp JIS_C6220-1969 iso-ir-13 katakana x0201-7
6010 { JIS_C6220-1969-ro iso-ir-14 jp ISO646-JP csISO14JISC6220ro }
6011 { IT iso-ir-15 ISO646-IT csISO15Italian }
6012 { PT iso-ir-16 ISO646-PT csISO16Portuguese }
6013 { ES iso-ir-17 ISO646-ES csISO17Spanish }
6014 { greek7-old iso-ir-18 csISO18Greek7Old }
6015 { latin-greek iso-ir-19 csISO19LatinGreek }
6016 { DIN_66003 iso-ir-21 de ISO646-DE csISO21German }
6017 { NF_Z_62-010_(1973) iso-ir-25 ISO646-FR1 csISO25French }
6018 { Latin-greek-1 iso-ir-27 csISO27LatinGreek1 }
6019 { ISO_5427 iso-ir-37 csISO5427Cyrillic }
6020 { JIS_C6226-1978 iso-ir-42 csISO42JISC62261978 }
6021 { BS_viewdata iso-ir-47 csISO47BSViewdata }
6022 { INIS iso-ir-49 csISO49INIS }
6023 { INIS-8 iso-ir-50 csISO50INIS8 }
6024 { INIS-cyrillic iso-ir-51 csISO51INISCyrillic }
6025 { ISO_5427:1981 iso-ir-54 ISO5427Cyrillic1981 }
6026 { ISO_5428:1980 iso-ir-55 csISO5428Greek }
6027 { GB_1988-80 iso-ir-57 cn ISO646-CN csISO57GB1988 }
6028 { GB_2312-80 iso-ir-58 chinese csISO58GB231280 }
6029 { NS_4551-1 iso-ir-60 ISO646-NO no csISO60DanishNorwegian
6031 { NS_4551-2 ISO646-NO2 iso-ir-61 no2 csISO61Norwegian2 }
6032 { NF_Z_62-010 iso-ir-69 ISO646-FR fr csISO69French }
6033 { videotex-suppl iso-ir-70 csISO70VideotexSupp1 }
6034 { PT2 iso-ir-84 ISO646-PT2 csISO84Portuguese2 }
6035 { ES2 iso-ir-85 ISO646-ES2 csISO85Spanish2 }
6036 { MSZ_7795.3 iso-ir-86 ISO646-HU hu csISO86Hungarian }
6037 { JIS_C6226-1983 iso-ir-87 x0208 JIS_X0208-1983 csISO87JISX0208 }
6038 { greek7 iso-ir-88 csISO88Greek7 }
6039 { ASMO_449 ISO_9036 arabic7 iso-ir-89 csISO89ASMO449 }
6040 { iso-ir-90 csISO90 }
6041 { JIS_C6229-1984-a iso-ir-91 jp-ocr-a csISO91JISC62291984a }
6042 { JIS_C6229-1984-b iso-ir-92 ISO646-JP-OCR-B jp-ocr-b
6043 csISO92JISC62991984b }
6044 { JIS_C6229-1984-b-add iso-ir-93 jp-ocr-b-add csISO93JIS62291984badd }
6045 { JIS_C6229-1984-hand iso-ir-94 jp-ocr-hand csISO94JIS62291984hand }
6046 { JIS_C6229-1984-hand-add iso-ir-95 jp-ocr-hand-add
6047 csISO95JIS62291984handadd }
6048 { JIS_C6229-1984-kana iso-ir-96 csISO96JISC62291984kana }
6049 { ISO_2033-1983 iso-ir-98 e13b csISO2033 }
6050 { ANSI_X3.110-1983 iso-ir-99 CSA_T500-1983 NAPLPS csISO99NAPLPS }
6051 { ISO_8859-1:1987 iso-ir-100 ISO_8859-1 ISO-8859-1 latin1 l1 IBM819
6053 { ISO_8859-2:1987 iso-ir-101 ISO_8859-2 ISO-8859-2 latin2 l2 csISOLatin2 }
6054 { T.61-7bit iso-ir-102 csISO102T617bit }
6055 { T.61-8bit T.61 iso-ir-103 csISO103T618bit }
6056 { ISO_8859-3:1988 iso-ir-109 ISO_8859-3 ISO-8859-3 latin3 l3 csISOLatin3 }
6057 { ISO_8859-4:1988 iso-ir-110 ISO_8859-4 ISO-8859-4 latin4 l4 csISOLatin4 }
6058 { ECMA-cyrillic iso-ir-111 KOI8-E csISO111ECMACyrillic }
6059 { CSA_Z243.4-1985-1 iso-ir-121 ISO646-CA csa7-1 ca csISO121Canadian1 }
6060 { CSA_Z243.4-1985-2 iso-ir-122 ISO646-CA2 csa7-2 csISO122Canadian2 }
6061 { CSA_Z243.4-1985-gr iso-ir-123 csISO123CSAZ24341985gr }
6062 { ISO_8859-6:1987 iso-ir-127 ISO_8859-6 ISO-8859-6 ECMA-114 ASMO-708
6063 arabic csISOLatinArabic }
6064 { ISO_8859-6-E csISO88596E ISO-8859-6-E }
6065 { ISO_8859-6-I csISO88596I ISO-8859-6-I }
6066 { ISO_8859-7:1987 iso-ir-126 ISO_8859-7 ISO-8859-7 ELOT_928 ECMA-118
6067 greek greek8 csISOLatinGreek }
6068 { T.101-G2 iso-ir-128 csISO128T101G2 }
6069 { ISO_8859-8:1988 iso-ir-138 ISO_8859-8 ISO-8859-8 hebrew
6071 { ISO_8859-8-E csISO88598E ISO-8859-8-E }
6072 { ISO_8859-8-I csISO88598I ISO-8859-8-I }
6073 { CSN_369103 iso-ir-139 csISO139CSN369103 }
6074 { JUS_I.B1.002 iso-ir-141 ISO646-YU js yu csISO141JUSIB1002 }
6075 { ISO_6937-2-add iso-ir-142 csISOTextComm }
6076 { IEC_P27-1 iso-ir-143 csISO143IECP271 }
6077 { ISO_8859-5:1988 iso-ir-144 ISO_8859-5 ISO-8859-5 cyrillic
6078 csISOLatinCyrillic }
6079 { JUS_I.B1.003-serb iso-ir-146 serbian csISO146Serbian }
6080 { JUS_I.B1.003-mac macedonian iso-ir-147 csISO147Macedonian }
6081 { ISO_8859-9:1989 iso-ir-148 ISO_8859-9 ISO-8859-9 latin5 l5 csISOLatin5 }
6082 { greek-ccitt iso-ir-150 csISO150 csISO150GreekCCITT }
6083 { NC_NC00-10:81 cuba iso-ir-151 ISO646-CU csISO151Cuba }
6084 { ISO_6937-2-25 iso-ir-152 csISO6937Add }
6085 { GOST_19768-74 ST_SEV_358-88 iso-ir-153 csISO153GOST1976874 }
6086 { ISO_8859-supp iso-ir-154 latin1-2-5 csISO8859Supp }
6087 { ISO_10367-box iso-ir-155 csISO10367Box }
6088 { ISO-8859-10 iso-ir-157 l6 ISO_8859-10:1992 csISOLatin6 latin6 }
6089 { latin-lap lap iso-ir-158 csISO158Lap }
6090 { JIS_X0212-1990 x0212 iso-ir-159 csISO159JISX02121990 }
6091 { DS_2089 DS2089 ISO646-DK dk csISO646Danish }
6094 { JIS_X0201 X0201 csHalfWidthKatakana }
6095 { KSC5636 ISO646-KR csKSC5636 }
6096 { ISO-10646-UCS-2 csUnicode }
6097 { ISO-10646-UCS-4 csUCS4 }
6098 { DEC-MCS dec csDECMCS }
6099 { hp-roman8 roman8 r8 csHPRoman8 }
6100 { macintosh mac csMacintosh }
6101 { IBM037 cp037 ebcdic-cp-us ebcdic-cp-ca ebcdic-cp-wt ebcdic-cp-nl
6103 { IBM038 EBCDIC-INT cp038 csIBM038 }
6104 { IBM273 CP273 csIBM273 }
6105 { IBM274 EBCDIC-BE CP274 csIBM274 }
6106 { IBM275 EBCDIC-BR cp275 csIBM275 }
6107 { IBM277 EBCDIC-CP-DK EBCDIC-CP-NO csIBM277 }
6108 { IBM278 CP278 ebcdic-cp-fi ebcdic-cp-se csIBM278 }
6109 { IBM280 CP280 ebcdic-cp-it csIBM280 }
6110 { IBM281 EBCDIC-JP-E cp281 csIBM281 }
6111 { IBM284 CP284 ebcdic-cp-es csIBM284 }
6112 { IBM285 CP285 ebcdic-cp-gb csIBM285 }
6113 { IBM290 cp290 EBCDIC-JP-kana csIBM290 }
6114 { IBM297 cp297 ebcdic-cp-fr csIBM297 }
6115 { IBM420 cp420 ebcdic-cp-ar1 csIBM420 }
6116 { IBM423 cp423 ebcdic-cp-gr csIBM423 }
6117 { IBM424 cp424 ebcdic-cp-he csIBM424 }
6118 { IBM437 cp437 437 csPC8CodePage437 }
6119 { IBM500 CP500 ebcdic-cp-be ebcdic-cp-ch csIBM500 }
6120 { IBM775 cp775 csPC775Baltic }
6121 { IBM850 cp850 850 csPC850Multilingual }
6122 { IBM851 cp851 851 csIBM851 }
6123 { IBM852 cp852 852 csPCp852 }
6124 { IBM855 cp855 855 csIBM855 }
6125 { IBM857 cp857 857 csIBM857 }
6126 { IBM860 cp860 860 csIBM860 }
6127 { IBM861 cp861 861 cp-is csIBM861 }
6128 { IBM862 cp862 862 csPC862LatinHebrew }
6129 { IBM863 cp863 863 csIBM863 }
6130 { IBM864 cp864 csIBM864 }
6131 { IBM865 cp865 865 csIBM865 }
6132 { IBM866 cp866 866 csIBM866 }
6133 { IBM868 CP868 cp-ar csIBM868 }
6134 { IBM869 cp869 869 cp-gr csIBM869 }
6135 { IBM870 CP870 ebcdic-cp-roece ebcdic-cp-yu csIBM870 }
6136 { IBM871 CP871 ebcdic-cp-is csIBM871 }
6137 { IBM880 cp880 EBCDIC-Cyrillic csIBM880 }
6138 { IBM891 cp891 csIBM891 }
6139 { IBM903 cp903 csIBM903 }
6140 { IBM904 cp904 904 csIBBM904 }
6141 { IBM905 CP905 ebcdic-cp-tr csIBM905 }
6142 { IBM918 CP918 ebcdic-cp-ar2 csIBM918 }
6143 { IBM1026 CP1026 csIBM1026 }
6144 { EBCDIC-AT-DE csIBMEBCDICATDE }
6145 { EBCDIC-AT-DE-A csEBCDICATDEA }
6146 { EBCDIC-CA-FR csEBCDICCAFR }
6147 { EBCDIC-DK-NO csEBCDICDKNO }
6148 { EBCDIC-DK-NO-A csEBCDICDKNOA }
6149 { EBCDIC-FI-SE csEBCDICFISE }
6150 { EBCDIC-FI-SE-A csEBCDICFISEA }
6151 { EBCDIC-FR csEBCDICFR }
6152 { EBCDIC-IT csEBCDICIT }
6153 { EBCDIC-PT csEBCDICPT }
6154 { EBCDIC-ES csEBCDICES }
6155 { EBCDIC-ES-A csEBCDICESA }
6156 { EBCDIC-ES-S csEBCDICESS }
6157 { EBCDIC-UK csEBCDICUK }
6158 { EBCDIC-US csEBCDICUS }
6159 { UNKNOWN-8BIT csUnknown8BiT }
6160 { MNEMONIC csMnemonic }
6165 { IBM00858 CCSID00858 CP00858 PC-Multilingual-850+euro }
6166 { IBM00924 CCSID00924 CP00924 ebcdic-Latin9--euro }
6167 { IBM01140 CCSID01140 CP01140 ebcdic-us-37+euro }
6168 { IBM01141 CCSID01141 CP01141 ebcdic-de-273+euro }
6169 { IBM01142 CCSID01142 CP01142 ebcdic-dk-277+euro ebcdic-no-277+euro }
6170 { IBM01143 CCSID01143 CP01143 ebcdic-fi-278+euro ebcdic-se-278+euro }
6171 { IBM01144 CCSID01144 CP01144 ebcdic-it-280+euro }
6172 { IBM01145 CCSID01145 CP01145 ebcdic-es-284+euro }
6173 { IBM01146 CCSID01146 CP01146 ebcdic-gb-285+euro }
6174 { IBM01147 CCSID01147 CP01147 ebcdic-fr-297+euro }
6175 { IBM01148 CCSID01148 CP01148 ebcdic-international-500+euro }
6176 { IBM01149 CCSID01149 CP01149 ebcdic-is-871+euro }
6177 { IBM1047 IBM-1047 }
6178 { PTCP154 csPTCP154 PT154 CP154 Cyrillic-Asian }
6179 { Amiga-1251 Ami1251 Amiga1251 Ami-1251 }
6180 { UNICODE-1-1 csUnicode11 }
6183 { UNICODE-1-1-UTF-7 csUnicode11UTF7 }
6184 { ISO-8859-14 iso-ir-199 ISO_8859-14:1998 ISO_8859-14 latin8 iso-celtic
6186 { ISO-8859-15 ISO_8859-15 Latin-9 }
6187 { ISO-8859-16 iso-ir-226 ISO_8859-16:2001 ISO_8859-16 latin10 l10 }
6188 { GBK CP936 MS936 windows-936 }
6189 { JIS_Encoding csJISEncoding }
6190 { Shift_JIS MS_Kanji csShiftJIS }
6191 { Extended_UNIX_Code_Packed_Format_for_Japanese csEUCPkdFmtJapanese
6193 { Extended_UNIX_Code_Fixed_Width_for_Japanese csEUCFixWidJapanese }
6194 { ISO-10646-UCS-Basic csUnicodeASCII }
6195 { ISO-10646-Unicode-Latin1 csUnicodeLatin1 ISO-10646 }
6196 { ISO-Unicode-IBM-1261 csUnicodeIBM1261 }
6197 { ISO-Unicode-IBM-1268 csUnicodeIBM1268 }
6198 { ISO-Unicode-IBM-1276 csUnicodeIBM1276 }
6199 { ISO-Unicode-IBM-1264 csUnicodeIBM1264 }
6200 { ISO-Unicode-IBM-1265 csUnicodeIBM1265 }
6201 { ISO-8859-1-Windows-3.0-Latin-1 csWindows30Latin1 }
6202 { ISO-8859-1-Windows-3.1-Latin-1 csWindows31Latin1 }
6203 { ISO-8859-2-Windows-Latin-2 csWindows31Latin2 }
6204 { ISO-8859-9-Windows-Latin-5 csWindows31Latin5 }
6205 { Adobe-Standard-Encoding csAdobeStandardEncoding }
6206 { Ventura-US csVenturaUS }
6207 { Ventura-International csVenturaInternational }
6208 { PC8-Danish-Norwegian csPC8DanishNorwegian }
6209 { PC8-Turkish csPC8Turkish }
6210 { IBM-Symbols csIBMSymbols }
6211 { IBM-Thai csIBMThai }
6212 { HP-Legal csHPLegal }
6213 { HP-Pi-font csHPPiFont }
6214 { HP-Math8 csHPMath8 }
6215 { Adobe-Symbol-Encoding csHPPSMath }
6216 { HP-DeskTop csHPDesktop }
6217 { Ventura-Math csVenturaMath }
6218 { Microsoft-Publishing csMicrosoftPublishing }
6219 { Windows-31J csWindows31J }
6224 proc tcl_encoding {enc} {
6225 global encoding_aliases
6226 set names [encoding names]
6227 set lcnames [string tolower $names]
6228 set enc [string tolower $enc]
6229 set i [lsearch -exact $lcnames $enc]
6231 # look for "isonnn" instead of "iso-nnn" or "iso_nnn"
6232 if {[regsub {^iso[-_]} $enc iso encx]} {
6233 set i [lsearch -exact $lcnames $encx]
6237 foreach l $encoding_aliases {
6238 set ll [string tolower $l]
6239 if {[lsearch -exact $ll $enc] < 0} continue
6240 # look through the aliases for one that tcl knows about
6242 set i [lsearch -exact $lcnames $e]
6244 if {[regsub {^iso[-_]} $e iso ex]} {
6245 set i [lsearch -exact $lcnames $ex]
6254 return [lindex $names $i]
6261 set diffopts "-U 5 -p"
6262 set wrcomcmd "git diff-tree --stdin -p --pretty"
6266 set gitencoding [exec git config --get i18n.commitencoding]
6268 if {$gitencoding == ""} {
6269 set gitencoding "utf-8"
6271 set tclencoding [tcl_encoding $gitencoding]
6272 if {$tclencoding == {}} {
6273 puts stderr "Warning: encoding $gitencoding is not supported by Tcl/Tk"
6276 set mainfont {Helvetica 9}
6277 set textfont {Courier 9}
6278 set uifont {Helvetica 9 bold}
6279 set findmergefiles 0
6287 set cmitmode "patch"
6288 set wrapcomment "none"
6291 set colors {green red blue magenta darkgrey brown orange}
6294 set diffcolors {red "#00a000" blue}
6296 catch {source ~/.gitk}
6298 font create optionfont -family sans-serif -size -12
6302 switch -regexp -- $arg {
6304 "^-d" { set datemode 1 }
6306 lappend revtreeargs $arg
6311 # check that we can find a .git directory somewhere...
6313 if {![file isdirectory $gitdir]} {
6314 show_error {} . "Cannot find the git directory \"$gitdir\"."
6318 set cmdline_files {}
6319 set i [lsearch -exact $revtreeargs "--"]
6321 set cmdline_files [lrange $revtreeargs [expr {$i + 1}] end]
6322 set revtreeargs [lrange $revtreeargs 0 [expr {$i - 1}]]
6323 } elseif {$revtreeargs ne {}} {
6325 set f [eval exec git rev-parse --no-revs --no-flags $revtreeargs]
6326 set cmdline_files [split $f "\n"]
6327 set n [llength $cmdline_files]
6328 set revtreeargs [lrange $revtreeargs 0 end-$n]
6330 # unfortunately we get both stdout and stderr in $err,
6331 # so look for "fatal:".
6332 set i [string first "fatal:" $err]
6334 set err [string range $err [expr {$i + 6}] end]
6336 show_error {} . "Bad arguments to gitk:\n$err"
6345 set highlight_paths {}
6346 set searchdirn -forwards
6356 set selectedhlview None
6367 wm title . "[file tail $argv0]: [file tail [pwd]]"
6370 if {$cmdline_files ne {} || $revtreeargs ne {}} {
6371 # create a view for the files/dirs specified on the command line
6375 set viewname(1) "Command line"
6376 set viewfiles(1) $cmdline_files
6377 set viewargs(1) $revtreeargs
6380 .bar.view entryconf Edit* -state normal
6381 .bar.view entryconf Delete* -state normal
6384 if {[info exists permviews]} {
6385 foreach v $permviews {
6388 set viewname($n) [lindex $v 0]
6389 set viewfiles($n) [lindex $v 1]
6390 set viewargs($n) [lindex $v 2]