2 # Tcl ignores the next line -*- tcl -*- \
5 # Copyright (C) 2005 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)]} {
19 proc start_rev_list {view} {
20 global startmsecs nextupdate ncmupdate
21 global commfd leftover tclencoding datemode
22 global viewargs viewfiles commitidx
24 set startmsecs [clock clicks -milliseconds]
25 set nextupdate [expr {$startmsecs + 100}]
27 set commitidx($view) 0
28 set args $viewargs($view)
29 if {$viewfiles($view) ne {}} {
30 set args [concat $args "--" $viewfiles($view)]
32 set order "--topo-order"
34 set order "--date-order"
37 set fd [open [concat | git-rev-list --header $order \
38 --parents --boundary --default HEAD $args] r]
40 puts stderr "Error executing git-rev-list: $err"
44 set leftover($view) {}
45 fconfigure $fd -blocking 0 -translation lf
46 if {$tclencoding != {}} {
47 fconfigure $fd -encoding $tclencoding
49 fileevent $fd readable [list getcommitlines $fd $view]
53 proc stop_rev_list {} {
56 if {![info exists commfd($curview)]} return
57 set fd $commfd($curview)
63 unset commfd($curview)
67 global phase canv mainfont curview
71 start_rev_list $curview
72 show_status "Reading commits..."
75 proc getcommitlines {fd view} {
76 global commitlisted nextupdate
77 global leftover commfd
78 global displayorder commitidx commitrow commitdata
79 global parentlist childlist children curview hlview
80 global vparentlist vchildlist vdisporder vcmitlisted
84 if {![eof $fd]} return
88 # set it blocking so we wait for the process to terminate
89 fconfigure $fd -blocking 1
90 if {[catch {close $fd} err]} {
92 if {$view != $curview} {
93 set fv " for the \"$viewname($view)\" view"
95 if {[string range $err 0 4] == "usage"} {
96 set err "Gitk: error reading commits$fv:\
97 bad arguments to git-rev-list."
98 if {$viewname($view) eq "Command line"} {
100 " (Note: arguments to gitk are passed to git-rev-list\
101 to allow selection of commits to be displayed.)"
104 set err "Error reading commits$fv: $err"
108 if {$view == $curview} {
109 after idle finishcommits
116 set i [string first "\0" $stuff $start]
118 append leftover($view) [string range $stuff $start end]
122 set cmit $leftover($view)
123 append cmit [string range $stuff 0 [expr {$i - 1}]]
124 set leftover($view) {}
126 set cmit [string range $stuff $start [expr {$i - 1}]]
128 set start [expr {$i + 1}]
129 set j [string first "\n" $cmit]
133 set ids [string range $cmit 0 [expr {$j - 1}]]
134 if {[string range $ids 0 0] == "-"} {
136 set ids [string range $ids 1 end]
140 if {[string length $id] != 40} {
148 if {[string length $shortcmit] > 80} {
149 set shortcmit "[string range $shortcmit 0 80]..."
151 error_popup "Can't parse git-rev-list output: {$shortcmit}"
154 set id [lindex $ids 0]
156 set olds [lrange $ids 1 end]
159 if {$i == 0 || [lsearch -exact $olds $p] >= $i} {
160 lappend children($view,$p) $id
167 if {![info exists children($view,$id)]} {
168 set children($view,$id) {}
170 set commitdata($id) [string range $cmit [expr {$j + 1}] end]
171 set commitrow($view,$id) $commitidx($view)
172 incr commitidx($view)
173 if {$view == $curview} {
174 lappend parentlist $olds
175 lappend childlist $children($view,$id)
176 lappend displayorder $id
177 lappend commitlisted $listed
179 lappend vparentlist($view) $olds
180 lappend vchildlist($view) $children($view,$id)
181 lappend vdisporder($view) $id
182 lappend vcmitlisted($view) $listed
187 if {$view == $curview} {
189 } elseif {[info exists hlview] && $view == $hlview} {
193 if {[clock clicks -milliseconds] >= $nextupdate} {
199 global commfd nextupdate numcommits ncmupdate
201 foreach v [array names commfd] {
202 fileevent $commfd($v) readable {}
205 set nextupdate [expr {[clock clicks -milliseconds] + 100}]
206 if {$numcommits < 100} {
207 set ncmupdate [expr {$numcommits + 1}]
208 } elseif {$numcommits < 10000} {
209 set ncmupdate [expr {$numcommits + 10}]
211 set ncmupdate [expr {$numcommits + 100}]
213 foreach v [array names commfd] {
215 fileevent $fd readable [list getcommitlines $fd $v]
219 proc readcommit {id} {
220 if {[catch {set contents [exec git-cat-file commit $id]}]} return
221 parsecommit $id $contents 0
224 proc updatecommits {} {
225 global viewdata curview phase displayorder
226 global children commitrow
233 foreach id $displayorder {
234 catch {unset children($n,$id)}
235 catch {unset commitrow($n,$id)}
238 catch {unset viewdata($n)}
243 proc parsecommit {id contents listed} {
244 global commitinfo cdate
253 set hdrend [string first "\n\n" $contents]
255 # should never happen...
256 set hdrend [string length $contents]
258 set header [string range $contents 0 [expr {$hdrend - 1}]]
259 set comment [string range $contents [expr {$hdrend + 2}] end]
260 foreach line [split $header "\n"] {
261 set tag [lindex $line 0]
262 if {$tag == "author"} {
263 set audate [lindex $line end-1]
264 set auname [lrange $line 1 end-2]
265 } elseif {$tag == "committer"} {
266 set comdate [lindex $line end-1]
267 set comname [lrange $line 1 end-2]
271 # take the first line of the comment as the headline
272 set i [string first "\n" $comment]
274 set headline [string trim [string range $comment 0 $i]]
276 set headline $comment
279 # git-rev-list indents the comment by 4 spaces;
280 # if we got this via git-cat-file, add the indentation
282 foreach line [split $comment "\n"] {
283 append newcomment " "
284 append newcomment $line
285 append newcomment "\n"
287 set comment $newcomment
289 if {$comdate != {}} {
290 set cdate($id) $comdate
292 set commitinfo($id) [list $headline $auname $audate \
293 $comname $comdate $comment]
296 proc getcommit {id} {
297 global commitdata commitinfo
299 if {[info exists commitdata($id)]} {
300 parsecommit $id $commitdata($id) 1
303 if {![info exists commitinfo($id)]} {
304 set commitinfo($id) {"No commit information available"}
311 global tagids idtags headids idheads tagcontents
312 global otherrefids idotherrefs
314 foreach v {tagids idtags headids idheads otherrefids idotherrefs} {
317 set refd [open [list | git ls-remote [gitdir]] r]
318 while {0 <= [set n [gets $refd line]]} {
319 if {![regexp {^([0-9a-f]{40}) refs/([^^]*)$} $line \
323 if {[regexp {^remotes/.*/HEAD$} $path match]} {
326 if {![regexp {^(tags|heads)/(.*)$} $path match type name]} {
330 if {[regexp {^remotes/} $path match]} {
333 if {$type == "tags"} {
334 set tagids($name) $id
335 lappend idtags($id) $name
340 set commit [exec git-rev-parse "$id^0"]
341 if {"$commit" != "$id"} {
342 set tagids($name) $commit
343 lappend idtags($commit) $name
347 set tagcontents($name) [exec git-cat-file tag "$id"]
349 } elseif { $type == "heads" } {
350 set headids($name) $id
351 lappend idheads($id) $name
353 set otherrefids($name) $id
354 lappend idotherrefs($id) $name
360 proc show_error {w msg} {
361 message $w.m -text $msg -justify center -aspect 400
362 pack $w.m -side top -fill x -padx 20 -pady 20
363 button $w.ok -text OK -command "destroy $w"
364 pack $w.ok -side bottom -fill x
365 bind $w <Visibility> "grab $w; focus $w"
366 bind $w <Key-Return> "destroy $w"
370 proc error_popup msg {
378 global canv canv2 canv3 linespc charspc ctext cflist
379 global textfont mainfont uifont
380 global findtype findtypemenu findloc findstring fstring geometry
381 global entries sha1entry sha1string sha1but
382 global maincursor textcursor curtextcursor
383 global rowctxmenu mergemax
386 .bar add cascade -label "File" -menu .bar.file
387 .bar configure -font $uifont
389 .bar.file add command -label "Update" -command updatecommits
390 .bar.file add command -label "Reread references" -command rereadrefs
391 .bar.file add command -label "Quit" -command doquit
392 .bar.file configure -font $uifont
394 .bar add cascade -label "Edit" -menu .bar.edit
395 .bar.edit add command -label "Preferences" -command doprefs
396 .bar.edit configure -font $uifont
398 menu .bar.view -font $uifont
399 menu .bar.view.hl -font $uifont -tearoff 0
400 .bar add cascade -label "View" -menu .bar.view
401 .bar.view add command -label "New view..." -command {newview 0}
402 .bar.view add command -label "Edit view..." -command editview \
404 .bar.view add command -label "Delete view" -command delview -state disabled
405 .bar.view add cascade -label "Highlight" -menu .bar.view.hl
406 .bar.view add separator
407 .bar.view add radiobutton -label "All files" -command {showview 0} \
408 -variable selectedview -value 0
409 .bar.view.hl add command -label "New view..." -command {newview 1}
410 .bar.view.hl add command -label "Remove" -command delhighlight \
412 .bar.view.hl add separator
415 .bar add cascade -label "Help" -menu .bar.help
416 .bar.help add command -label "About gitk" -command about
417 .bar.help add command -label "Key bindings" -command keys
418 .bar.help configure -font $uifont
419 . configure -menu .bar
421 if {![info exists geometry(canv1)]} {
422 set geometry(canv1) [expr {45 * $charspc}]
423 set geometry(canv2) [expr {30 * $charspc}]
424 set geometry(canv3) [expr {15 * $charspc}]
425 set geometry(canvh) [expr {25 * $linespc + 4}]
426 set geometry(ctextw) 80
427 set geometry(ctexth) 30
428 set geometry(cflistw) 30
430 panedwindow .ctop -orient vertical
431 if {[info exists geometry(width)]} {
432 .ctop conf -width $geometry(width) -height $geometry(height)
433 set texth [expr {$geometry(height) - $geometry(canvh) - 56}]
434 set geometry(ctexth) [expr {($texth - 8) /
435 [font metrics $textfont -linespace]}]
439 pack .ctop.top.bar -side bottom -fill x
440 set cscroll .ctop.top.csb
441 scrollbar $cscroll -command {allcanvs yview} -highlightthickness 0
442 pack $cscroll -side right -fill y
443 panedwindow .ctop.top.clist -orient horizontal -sashpad 0 -handlesize 4
444 pack .ctop.top.clist -side top -fill both -expand 1
446 set canv .ctop.top.clist.canv
447 canvas $canv -height $geometry(canvh) -width $geometry(canv1) \
449 -yscrollincr $linespc -yscrollcommand "scrollcanv $cscroll"
450 .ctop.top.clist add $canv
451 set canv2 .ctop.top.clist.canv2
452 canvas $canv2 -height $geometry(canvh) -width $geometry(canv2) \
453 -bg white -bd 0 -yscrollincr $linespc
454 .ctop.top.clist add $canv2
455 set canv3 .ctop.top.clist.canv3
456 canvas $canv3 -height $geometry(canvh) -width $geometry(canv3) \
457 -bg white -bd 0 -yscrollincr $linespc
458 .ctop.top.clist add $canv3
459 bind .ctop.top.clist <Configure> {resizeclistpanes %W %w}
461 set sha1entry .ctop.top.bar.sha1
462 set entries $sha1entry
463 set sha1but .ctop.top.bar.sha1label
464 button $sha1but -text "SHA1 ID: " -state disabled -relief flat \
465 -command gotocommit -width 8 -font $uifont
466 $sha1but conf -disabledforeground [$sha1but cget -foreground]
467 pack .ctop.top.bar.sha1label -side left
468 entry $sha1entry -width 40 -font $textfont -textvariable sha1string
469 trace add variable sha1string write sha1change
470 pack $sha1entry -side left -pady 2
472 image create bitmap bm-left -data {
473 #define left_width 16
474 #define left_height 16
475 static unsigned char left_bits[] = {
476 0x00, 0x00, 0xc0, 0x01, 0xe0, 0x00, 0x70, 0x00, 0x38, 0x00, 0x1c, 0x00,
477 0x0e, 0x00, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0x0e, 0x00, 0x1c, 0x00,
478 0x38, 0x00, 0x70, 0x00, 0xe0, 0x00, 0xc0, 0x01};
480 image create bitmap bm-right -data {
481 #define right_width 16
482 #define right_height 16
483 static unsigned char right_bits[] = {
484 0x00, 0x00, 0xc0, 0x01, 0x80, 0x03, 0x00, 0x07, 0x00, 0x0e, 0x00, 0x1c,
485 0x00, 0x38, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0x00, 0x38, 0x00, 0x1c,
486 0x00, 0x0e, 0x00, 0x07, 0x80, 0x03, 0xc0, 0x01};
488 button .ctop.top.bar.leftbut -image bm-left -command goback \
489 -state disabled -width 26
490 pack .ctop.top.bar.leftbut -side left -fill y
491 button .ctop.top.bar.rightbut -image bm-right -command goforw \
492 -state disabled -width 26
493 pack .ctop.top.bar.rightbut -side left -fill y
495 button .ctop.top.bar.findbut -text "Find" -command dofind -font $uifont
496 pack .ctop.top.bar.findbut -side left
498 set fstring .ctop.top.bar.findstring
499 lappend entries $fstring
500 entry $fstring -width 30 -font $textfont -textvariable findstring -font $textfont
501 pack $fstring -side left -expand 1 -fill x
503 set findtypemenu [tk_optionMenu .ctop.top.bar.findtype \
504 findtype Exact IgnCase Regexp]
505 .ctop.top.bar.findtype configure -font $uifont
506 .ctop.top.bar.findtype.menu configure -font $uifont
507 set findloc "All fields"
508 tk_optionMenu .ctop.top.bar.findloc findloc "All fields" Headline \
509 Comments Author Committer Files Pickaxe
510 .ctop.top.bar.findloc configure -font $uifont
511 .ctop.top.bar.findloc.menu configure -font $uifont
513 pack .ctop.top.bar.findloc -side right
514 pack .ctop.top.bar.findtype -side right
515 # for making sure type==Exact whenever loc==Pickaxe
516 trace add variable findloc write findlocchange
518 panedwindow .ctop.cdet -orient horizontal
520 frame .ctop.cdet.left
521 set ctext .ctop.cdet.left.ctext
522 text $ctext -bg white -state disabled -font $textfont \
523 -width $geometry(ctextw) -height $geometry(ctexth) \
524 -yscrollcommand {.ctop.cdet.left.sb set} -wrap none
525 scrollbar .ctop.cdet.left.sb -command "$ctext yview"
526 pack .ctop.cdet.left.sb -side right -fill y
527 pack $ctext -side left -fill both -expand 1
528 .ctop.cdet add .ctop.cdet.left
530 $ctext tag conf filesep -font [concat $textfont bold] -back "#aaaaaa"
531 $ctext tag conf hunksep -fore blue
532 $ctext tag conf d0 -fore red
533 $ctext tag conf d1 -fore "#00a000"
534 $ctext tag conf m0 -fore red
535 $ctext tag conf m1 -fore blue
536 $ctext tag conf m2 -fore green
537 $ctext tag conf m3 -fore purple
538 $ctext tag conf m4 -fore brown
539 $ctext tag conf m5 -fore "#009090"
540 $ctext tag conf m6 -fore magenta
541 $ctext tag conf m7 -fore "#808000"
542 $ctext tag conf m8 -fore "#009000"
543 $ctext tag conf m9 -fore "#ff0080"
544 $ctext tag conf m10 -fore cyan
545 $ctext tag conf m11 -fore "#b07070"
546 $ctext tag conf m12 -fore "#70b0f0"
547 $ctext tag conf m13 -fore "#70f0b0"
548 $ctext tag conf m14 -fore "#f0b070"
549 $ctext tag conf m15 -fore "#ff70b0"
550 $ctext tag conf mmax -fore darkgrey
552 $ctext tag conf mresult -font [concat $textfont bold]
553 $ctext tag conf msep -font [concat $textfont bold]
554 $ctext tag conf found -back yellow
556 frame .ctop.cdet.right
557 frame .ctop.cdet.right.mode
558 radiobutton .ctop.cdet.right.mode.patch -text "Patch" \
559 -command reselectline -variable cmitmode -value "patch"
560 radiobutton .ctop.cdet.right.mode.tree -text "Tree" \
561 -command reselectline -variable cmitmode -value "tree"
562 grid .ctop.cdet.right.mode.patch .ctop.cdet.right.mode.tree -sticky ew
563 pack .ctop.cdet.right.mode -side top -fill x
564 set cflist .ctop.cdet.right.cfiles
565 set indent [font measure $mainfont "nn"]
566 text $cflist -width $geometry(cflistw) -background white -font $mainfont \
567 -tabs [list $indent [expr {2 * $indent}]] \
568 -yscrollcommand ".ctop.cdet.right.sb set" \
569 -cursor [. cget -cursor] \
570 -spacing1 1 -spacing3 1
571 scrollbar .ctop.cdet.right.sb -command "$cflist yview"
572 pack .ctop.cdet.right.sb -side right -fill y
573 pack $cflist -side left -fill both -expand 1
574 $cflist tag configure highlight \
575 -background [$cflist cget -selectbackground]
576 .ctop.cdet add .ctop.cdet.right
577 bind .ctop.cdet <Configure> {resizecdetpanes %W %w}
579 pack .ctop -side top -fill both -expand 1
581 bindall <1> {selcanvline %W %x %y}
582 #bindall <B1-Motion> {selcanvline %W %x %y}
583 bindall <ButtonRelease-4> "allcanvs yview scroll -5 units"
584 bindall <ButtonRelease-5> "allcanvs yview scroll 5 units"
585 bindall <2> "canvscan mark %W %x %y"
586 bindall <B2-Motion> "canvscan dragto %W %x %y"
587 bindkey <Home> selfirstline
588 bindkey <End> sellastline
589 bind . <Key-Up> "selnextline -1"
590 bind . <Key-Down> "selnextline 1"
591 bindkey <Key-Right> "goforw"
592 bindkey <Key-Left> "goback"
593 bind . <Key-Prior> "selnextpage -1"
594 bind . <Key-Next> "selnextpage 1"
595 bind . <Control-Home> "allcanvs yview moveto 0.0"
596 bind . <Control-End> "allcanvs yview moveto 1.0"
597 bind . <Control-Key-Up> "allcanvs yview scroll -1 units"
598 bind . <Control-Key-Down> "allcanvs yview scroll 1 units"
599 bind . <Control-Key-Prior> "allcanvs yview scroll -1 pages"
600 bind . <Control-Key-Next> "allcanvs yview scroll 1 pages"
601 bindkey <Key-Delete> "$ctext yview scroll -1 pages"
602 bindkey <Key-BackSpace> "$ctext yview scroll -1 pages"
603 bindkey <Key-space> "$ctext yview scroll 1 pages"
604 bindkey p "selnextline -1"
605 bindkey n "selnextline 1"
608 bindkey i "selnextline -1"
609 bindkey k "selnextline 1"
612 bindkey b "$ctext yview scroll -1 pages"
613 bindkey d "$ctext yview scroll 18 units"
614 bindkey u "$ctext yview scroll -18 units"
615 bindkey / {findnext 1}
616 bindkey <Key-Return> {findnext 0}
619 bind . <Control-q> doquit
620 bind . <Control-f> dofind
621 bind . <Control-g> {findnext 0}
622 bind . <Control-r> findprev
623 bind . <Control-equal> {incrfont 1}
624 bind . <Control-KP_Add> {incrfont 1}
625 bind . <Control-minus> {incrfont -1}
626 bind . <Control-KP_Subtract> {incrfont -1}
627 bind . <Destroy> {savestuff %W}
628 bind . <Button-1> "click %W"
629 bind $fstring <Key-Return> dofind
630 bind $sha1entry <Key-Return> gotocommit
631 bind $sha1entry <<PasteSelection>> clearsha1
632 bind $cflist <1> {sel_flist %W %x %y; break}
633 bind $cflist <B1-Motion> {sel_flist %W %x %y; break}
634 bind $cflist <ButtonRelease-1> {treeclick %W %x %y}
636 set maincursor [. cget -cursor]
637 set textcursor [$ctext cget -cursor]
638 set curtextcursor $textcursor
640 set rowctxmenu .rowctxmenu
641 menu $rowctxmenu -tearoff 0
642 $rowctxmenu add command -label "Diff this -> selected" \
643 -command {diffvssel 0}
644 $rowctxmenu add command -label "Diff selected -> this" \
645 -command {diffvssel 1}
646 $rowctxmenu add command -label "Make patch" -command mkpatch
647 $rowctxmenu add command -label "Create tag" -command mktag
648 $rowctxmenu add command -label "Write commit to file" -command writecommit
651 # mouse-2 makes all windows scan vertically, but only the one
652 # the cursor is in scans horizontally
653 proc canvscan {op w x y} {
654 global canv canv2 canv3
655 foreach c [list $canv $canv2 $canv3] {
664 proc scrollcanv {cscroll f0 f1} {
669 # when we make a key binding for the toplevel, make sure
670 # it doesn't get triggered when that key is pressed in the
671 # find string entry widget.
672 proc bindkey {ev script} {
675 set escript [bind Entry $ev]
676 if {$escript == {}} {
677 set escript [bind Entry <Key>]
680 bind $e $ev "$escript; break"
684 # set the focus back to the toplevel for any click outside
695 global canv canv2 canv3 ctext cflist mainfont textfont uifont
696 global stuffsaved findmergefiles maxgraphpct
698 global viewname viewfiles viewargs viewperm nextviewnum
701 if {$stuffsaved} return
702 if {![winfo viewable .]} return
704 set f [open "~/.gitk-new" w]
705 puts $f [list set mainfont $mainfont]
706 puts $f [list set textfont $textfont]
707 puts $f [list set uifont $uifont]
708 puts $f [list set findmergefiles $findmergefiles]
709 puts $f [list set maxgraphpct $maxgraphpct]
710 puts $f [list set maxwidth $maxwidth]
711 puts $f [list set cmitmode $cmitmode]
712 puts $f "set geometry(width) [winfo width .ctop]"
713 puts $f "set geometry(height) [winfo height .ctop]"
714 puts $f "set geometry(canv1) [expr {[winfo width $canv]-2}]"
715 puts $f "set geometry(canv2) [expr {[winfo width $canv2]-2}]"
716 puts $f "set geometry(canv3) [expr {[winfo width $canv3]-2}]"
717 puts $f "set geometry(canvh) [expr {[winfo height $canv]-2}]"
718 set wid [expr {([winfo width $ctext] - 8) \
719 / [font measure $textfont "0"]}]
720 puts $f "set geometry(ctextw) $wid"
721 set wid [expr {([winfo width $cflist] - 11) \
722 / [font measure [$cflist cget -font] "0"]}]
723 puts $f "set geometry(cflistw) $wid"
724 puts -nonewline $f "set permviews {"
725 for {set v 0} {$v < $nextviewnum} {incr v} {
727 puts $f "{[list $viewname($v) $viewfiles($v) $viewargs($v)]}"
732 file rename -force "~/.gitk-new" "~/.gitk"
737 proc resizeclistpanes {win w} {
739 if {[info exists oldwidth($win)]} {
740 set s0 [$win sash coord 0]
741 set s1 [$win sash coord 1]
743 set sash0 [expr {int($w/2 - 2)}]
744 set sash1 [expr {int($w*5/6 - 2)}]
746 set factor [expr {1.0 * $w / $oldwidth($win)}]
747 set sash0 [expr {int($factor * [lindex $s0 0])}]
748 set sash1 [expr {int($factor * [lindex $s1 0])}]
752 if {$sash1 < $sash0 + 20} {
753 set sash1 [expr {$sash0 + 20}]
755 if {$sash1 > $w - 10} {
756 set sash1 [expr {$w - 10}]
757 if {$sash0 > $sash1 - 20} {
758 set sash0 [expr {$sash1 - 20}]
762 $win sash place 0 $sash0 [lindex $s0 1]
763 $win sash place 1 $sash1 [lindex $s1 1]
765 set oldwidth($win) $w
768 proc resizecdetpanes {win w} {
770 if {[info exists oldwidth($win)]} {
771 set s0 [$win sash coord 0]
773 set sash0 [expr {int($w*3/4 - 2)}]
775 set factor [expr {1.0 * $w / $oldwidth($win)}]
776 set sash0 [expr {int($factor * [lindex $s0 0])}]
780 if {$sash0 > $w - 15} {
781 set sash0 [expr {$w - 15}]
784 $win sash place 0 $sash0 [lindex $s0 1]
786 set oldwidth($win) $w
790 global canv canv2 canv3
796 proc bindall {event action} {
797 global canv canv2 canv3
798 bind $canv $event $action
799 bind $canv2 $event $action
800 bind $canv3 $event $action
805 if {[winfo exists $w]} {
810 wm title $w "About gitk"
812 Gitk - a commit viewer for git
814 Copyright © 2005-2006 Paul Mackerras
816 Use and redistribute under the terms of the GNU General Public License} \
817 -justify center -aspect 400
818 pack $w.m -side top -fill x -padx 20 -pady 20
819 button $w.ok -text Close -command "destroy $w"
820 pack $w.ok -side bottom
825 if {[winfo exists $w]} {
830 wm title $w "Gitk key bindings"
835 <Home> Move to first commit
836 <End> Move to last commit
837 <Up>, p, i Move up one commit
838 <Down>, n, k Move down one commit
839 <Left>, z, j Go back in history list
840 <Right>, x, l Go forward in history list
841 <PageUp> Move up one page in commit list
842 <PageDown> Move down one page in commit list
843 <Ctrl-Home> Scroll to top of commit list
844 <Ctrl-End> Scroll to bottom of commit list
845 <Ctrl-Up> Scroll commit list up one line
846 <Ctrl-Down> Scroll commit list down one line
847 <Ctrl-PageUp> Scroll commit list up one page
848 <Ctrl-PageDown> Scroll commit list down one page
849 <Delete>, b Scroll diff view up one page
850 <Backspace> Scroll diff view up one page
851 <Space> Scroll diff view down one page
852 u Scroll diff view up 18 lines
853 d Scroll diff view down 18 lines
855 <Ctrl-G> Move to next find hit
856 <Ctrl-R> Move to previous find hit
857 <Return> Move to next find hit
858 / Move to next find hit, or redo find
859 ? Move to previous find hit
860 f Scroll diff view to next file
861 <Ctrl-KP+> Increase font size
862 <Ctrl-plus> Increase font size
863 <Ctrl-KP-> Decrease font size
864 <Ctrl-minus> Decrease font size
866 -justify left -bg white -border 2 -relief sunken
867 pack $w.m -side top -fill both
868 button $w.ok -text Close -command "destroy $w"
869 pack $w.ok -side bottom
872 # Procedures for manipulating the file list window at the
873 # bottom right of the overall window.
875 proc treeview {w l openlevs} {
876 global treecontents treediropen treeheight treeparent treeindex
886 set treecontents() {}
887 $w conf -state normal
889 while {[string range $f 0 $prefixend] ne $prefix} {
890 if {$lev <= $openlevs} {
891 $w mark set e:$treeindex($prefix) "end -1c"
892 $w mark gravity e:$treeindex($prefix) left
894 set treeheight($prefix) $ht
895 incr ht [lindex $htstack end]
896 set htstack [lreplace $htstack end end]
897 set prefixend [lindex $prefendstack end]
898 set prefendstack [lreplace $prefendstack end end]
899 set prefix [string range $prefix 0 $prefixend]
902 set tail [string range $f [expr {$prefixend+1}] end]
903 while {[set slash [string first "/" $tail]] >= 0} {
906 lappend prefendstack $prefixend
907 incr prefixend [expr {$slash + 1}]
908 set d [string range $tail 0 $slash]
909 lappend treecontents($prefix) $d
910 set oldprefix $prefix
912 set treecontents($prefix) {}
913 set treeindex($prefix) [incr ix]
914 set treeparent($prefix) $oldprefix
915 set tail [string range $tail [expr {$slash+1}] end]
916 if {$lev <= $openlevs} {
918 set treediropen($prefix) [expr {$lev < $openlevs}]
919 set bm [expr {$lev == $openlevs? "tri-rt": "tri-dn"}]
920 $w mark set d:$ix "end -1c"
921 $w mark gravity d:$ix left
923 for {set i 0} {$i < $lev} {incr i} {append str "\t"}
925 $w image create end -align center -image $bm -padx 1 \
928 $w mark set s:$ix "end -1c"
929 $w mark gravity s:$ix left
934 if {$lev <= $openlevs} {
937 for {set i 0} {$i < $lev} {incr i} {append str "\t"}
941 lappend treecontents($prefix) $tail
944 while {$htstack ne {}} {
945 set treeheight($prefix) $ht
946 incr ht [lindex $htstack end]
947 set htstack [lreplace $htstack end end]
949 $w conf -state disabled
953 global treeheight treecontents
958 foreach e $treecontents($prefix) {
963 if {[string index $e end] eq "/"} {
964 set n $treeheight($prefix$e)
976 proc treeclosedir {w dir} {
977 global treediropen treeheight treeparent treeindex
979 set ix $treeindex($dir)
980 $w conf -state normal
981 $w delete s:$ix e:$ix
982 set treediropen($dir) 0
983 $w image configure a:$ix -image tri-rt
984 $w conf -state disabled
985 set n [expr {1 - $treeheight($dir)}]
987 incr treeheight($dir) $n
988 set dir $treeparent($dir)
992 proc treeopendir {w dir} {
993 global treediropen treeheight treeparent treecontents treeindex
995 set ix $treeindex($dir)
996 $w conf -state normal
997 $w image configure a:$ix -image tri-dn
998 $w mark set e:$ix s:$ix
999 $w mark gravity e:$ix right
1002 set n [llength $treecontents($dir)]
1003 for {set x $dir} {$x ne {}} {set x $treeparent($x)} {
1006 incr treeheight($x) $n
1008 foreach e $treecontents($dir) {
1009 if {[string index $e end] eq "/"} {
1011 set iy $treeindex($de)
1012 $w mark set d:$iy e:$ix
1013 $w mark gravity d:$iy left
1014 $w insert e:$ix $str
1015 set treediropen($de) 0
1016 $w image create e:$ix -align center -image tri-rt -padx 1 \
1019 $w mark set s:$iy e:$ix
1020 $w mark gravity s:$iy left
1021 set treeheight($de) 1
1023 $w insert e:$ix $str
1027 $w mark gravity e:$ix left
1028 $w conf -state disabled
1029 set treediropen($dir) 1
1030 set top [lindex [split [$w index @0,0] .] 0]
1031 set ht [$w cget -height]
1032 set l [lindex [split [$w index s:$ix] .] 0]
1035 } elseif {$l + $n + 1 > $top + $ht} {
1036 set top [expr {$l + $n + 2 - $ht}]
1044 proc treeclick {w x y} {
1045 global treediropen cmitmode ctext cflist cflist_top
1047 if {$cmitmode ne "tree"} return
1048 if {![info exists cflist_top]} return
1049 set l [lindex [split [$w index "@$x,$y"] "."] 0]
1050 $cflist tag remove highlight $cflist_top.0 "$cflist_top.0 lineend"
1051 $cflist tag add highlight $l.0 "$l.0 lineend"
1057 set e [linetoelt $l]
1058 if {[string index $e end] ne "/"} {
1060 } elseif {$treediropen($e)} {
1067 proc setfilelist {id} {
1068 global treefilelist cflist
1070 treeview $cflist $treefilelist($id) 0
1073 image create bitmap tri-rt -background black -foreground blue -data {
1074 #define tri-rt_width 13
1075 #define tri-rt_height 13
1076 static unsigned char tri-rt_bits[] = {
1077 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x30, 0x00, 0x70, 0x00, 0xf0, 0x00,
1078 0xf0, 0x01, 0xf0, 0x00, 0x70, 0x00, 0x30, 0x00, 0x10, 0x00, 0x00, 0x00,
1081 #define tri-rt-mask_width 13
1082 #define tri-rt-mask_height 13
1083 static unsigned char tri-rt-mask_bits[] = {
1084 0x08, 0x00, 0x18, 0x00, 0x38, 0x00, 0x78, 0x00, 0xf8, 0x00, 0xf8, 0x01,
1085 0xf8, 0x03, 0xf8, 0x01, 0xf8, 0x00, 0x78, 0x00, 0x38, 0x00, 0x18, 0x00,
1088 image create bitmap tri-dn -background black -foreground blue -data {
1089 #define tri-dn_width 13
1090 #define tri-dn_height 13
1091 static unsigned char tri-dn_bits[] = {
1092 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x07, 0xf8, 0x03,
1093 0xf0, 0x01, 0xe0, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1096 #define tri-dn-mask_width 13
1097 #define tri-dn-mask_height 13
1098 static unsigned char tri-dn-mask_bits[] = {
1099 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x1f, 0xfe, 0x0f, 0xfc, 0x07,
1100 0xf8, 0x03, 0xf0, 0x01, 0xe0, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
1104 proc init_flist {first} {
1105 global cflist cflist_top selectedline difffilestart
1107 $cflist conf -state normal
1108 $cflist delete 0.0 end
1110 $cflist insert end $first
1112 $cflist tag add highlight 1.0 "1.0 lineend"
1114 catch {unset cflist_top}
1116 $cflist conf -state disabled
1117 set difffilestart {}
1120 proc add_flist {fl} {
1121 global flistmode cflist
1123 $cflist conf -state normal
1124 if {$flistmode eq "flat"} {
1126 $cflist insert end "\n$f"
1129 $cflist conf -state disabled
1132 proc sel_flist {w x y} {
1133 global flistmode ctext difffilestart cflist cflist_top cmitmode
1135 if {$cmitmode eq "tree"} return
1136 if {![info exists cflist_top]} return
1137 set l [lindex [split [$w index "@$x,$y"] "."] 0]
1138 $cflist tag remove highlight $cflist_top.0 "$cflist_top.0 lineend"
1139 $cflist tag add highlight $l.0 "$l.0 lineend"
1144 catch {$ctext yview [lindex $difffilestart [expr {$l - 2}]]}
1148 # Functions for adding and removing shell-type quoting
1150 proc shellquote {str} {
1151 if {![string match "*\['\"\\ \t]*" $str]} {
1154 if {![string match "*\['\"\\]*" $str]} {
1157 if {![string match "*'*" $str]} {
1160 return "\"[string map {\" \\\" \\ \\\\} $str]\""
1163 proc shellarglist {l} {
1169 append str [shellquote $a]
1174 proc shelldequote {str} {
1179 if {![regexp -start $used -indices "\['\"\\\\ \t]" $str first]} {
1180 append ret [string range $str $used end]
1181 set used [string length $str]
1184 set first [lindex $first 0]
1185 set ch [string index $str $first]
1186 if {$first > $used} {
1187 append ret [string range $str $used [expr {$first - 1}]]
1190 if {$ch eq " " || $ch eq "\t"} break
1193 set first [string first "'" $str $used]
1195 error "unmatched single-quote"
1197 append ret [string range $str $used [expr {$first - 1}]]
1202 if {$used >= [string length $str]} {
1203 error "trailing backslash"
1205 append ret [string index $str $used]
1210 if {![regexp -start $used -indices "\[\"\\\\]" $str first]} {
1211 error "unmatched double-quote"
1213 set first [lindex $first 0]
1214 set ch [string index $str $first]
1215 if {$first > $used} {
1216 append ret [string range $str $used [expr {$first - 1}]]
1219 if {$ch eq "\""} break
1221 append ret [string index $str $used]
1225 return [list $used $ret]
1228 proc shellsplit {str} {
1231 set str [string trimleft $str]
1232 if {$str eq {}} break
1233 set dq [shelldequote $str]
1234 set n [lindex $dq 0]
1235 set word [lindex $dq 1]
1236 set str [string range $str $n end]
1242 # Code to implement multiple views
1244 proc newview {ishighlight} {
1245 global nextviewnum newviewname newviewperm uifont newishighlight
1246 global newviewargs revtreeargs
1248 set newishighlight $ishighlight
1250 if {[winfo exists $top]} {
1254 set newviewname($nextviewnum) "View $nextviewnum"
1255 set newviewperm($nextviewnum) 0
1256 set newviewargs($nextviewnum) [shellarglist $revtreeargs]
1257 vieweditor $top $nextviewnum "Gitk view definition"
1262 global viewname viewperm newviewname newviewperm
1263 global viewargs newviewargs
1265 set top .gitkvedit-$curview
1266 if {[winfo exists $top]} {
1270 set newviewname($curview) $viewname($curview)
1271 set newviewperm($curview) $viewperm($curview)
1272 set newviewargs($curview) [shellarglist $viewargs($curview)]
1273 vieweditor $top $curview "Gitk: edit view $viewname($curview)"
1276 proc vieweditor {top n title} {
1277 global newviewname newviewperm viewfiles
1281 wm title $top $title
1282 label $top.nl -text "Name" -font $uifont
1283 entry $top.name -width 20 -textvariable newviewname($n)
1284 grid $top.nl $top.name -sticky w -pady 5
1285 checkbutton $top.perm -text "Remember this view" -variable newviewperm($n)
1286 grid $top.perm - -pady 5 -sticky w
1287 message $top.al -aspect 1000 -font $uifont \
1288 -text "Commits to include (arguments to git-rev-list):"
1289 grid $top.al - -sticky w -pady 5
1290 entry $top.args -width 50 -textvariable newviewargs($n) \
1292 grid $top.args - -sticky ew -padx 5
1293 message $top.l -aspect 1000 -font $uifont \
1294 -text "Enter files and directories to include, one per line:"
1295 grid $top.l - -sticky w
1296 text $top.t -width 40 -height 10 -background white
1297 if {[info exists viewfiles($n)]} {
1298 foreach f $viewfiles($n) {
1299 $top.t insert end $f
1300 $top.t insert end "\n"
1302 $top.t delete {end - 1c} end
1303 $top.t mark set insert 0.0
1305 grid $top.t - -sticky ew -padx 5
1307 button $top.buts.ok -text "OK" -command [list newviewok $top $n]
1308 button $top.buts.can -text "Cancel" -command [list destroy $top]
1309 grid $top.buts.ok $top.buts.can
1310 grid columnconfigure $top.buts 0 -weight 1 -uniform a
1311 grid columnconfigure $top.buts 1 -weight 1 -uniform a
1312 grid $top.buts - -pady 10 -sticky ew
1316 proc doviewmenu {m first cmd op args} {
1317 set nmenu [$m index end]
1318 for {set i $first} {$i <= $nmenu} {incr i} {
1319 if {[$m entrycget $i -command] eq $cmd} {
1320 eval $m $op $i $args
1326 proc allviewmenus {n op args} {
1327 doviewmenu .bar.view 7 [list showview $n] $op $args
1328 doviewmenu .bar.view.hl 3 [list addhighlight $n] $op $args
1331 proc newviewok {top n} {
1332 global nextviewnum newviewperm newviewname newishighlight
1333 global viewname viewfiles viewperm selectedview curview
1334 global viewargs newviewargs
1337 set newargs [shellsplit $newviewargs($n)]
1339 error_popup "Error in commit selection arguments: $err"
1345 foreach f [split [$top.t get 0.0 end] "\n"] {
1346 set ft [string trim $f]
1351 if {![info exists viewfiles($n)]} {
1352 # creating a new view
1354 set viewname($n) $newviewname($n)
1355 set viewperm($n) $newviewperm($n)
1356 set viewfiles($n) $files
1357 set viewargs($n) $newargs
1359 if {!$newishighlight} {
1360 after idle showview $n
1362 after idle addhighlight $n
1365 # editing an existing view
1366 set viewperm($n) $newviewperm($n)
1367 if {$newviewname($n) ne $viewname($n)} {
1368 set viewname($n) $newviewname($n)
1369 allviewmenus $n entryconf -label $viewname($n)
1371 if {$files ne $viewfiles($n) || $newargs ne $viewargs($n)} {
1372 set viewfiles($n) $files
1373 set viewargs($n) $newargs
1374 if {$curview == $n} {
1375 after idle updatecommits
1379 catch {destroy $top}
1383 global curview viewdata viewperm
1385 if {$curview == 0} return
1386 allviewmenus $curview delete
1387 set viewdata($curview) {}
1388 set viewperm($curview) 0
1392 proc addviewmenu {n} {
1395 .bar.view add radiobutton -label $viewname($n) \
1396 -command [list showview $n] -variable selectedview -value $n
1397 .bar.view.hl add radiobutton -label $viewname($n) \
1398 -command [list addhighlight $n] -variable selectedhlview -value $n
1401 proc flatten {var} {
1405 foreach i [array names $var] {
1406 lappend ret $i [set $var\($i\)]
1411 proc unflatten {var l} {
1421 global curview viewdata viewfiles
1422 global displayorder parentlist childlist rowidlist rowoffsets
1423 global colormap rowtextx commitrow nextcolor canvxmax
1424 global numcommits rowrangelist commitlisted idrowranges
1425 global selectedline currentid canv canvy0
1426 global matchinglines treediffs
1427 global pending_select phase
1428 global commitidx rowlaidout rowoptim linesegends
1429 global commfd nextupdate
1430 global selectedview hlview selectedhlview
1431 global vparentlist vchildlist vdisporder vcmitlisted
1433 if {$n == $curview} return
1435 if {[info exists selectedline]} {
1436 set selid $currentid
1437 set y [yc $selectedline]
1438 set ymax [lindex [$canv cget -scrollregion] 3]
1439 set span [$canv yview]
1440 set ytop [expr {[lindex $span 0] * $ymax}]
1441 set ybot [expr {[lindex $span 1] * $ymax}]
1442 if {$ytop < $y && $y < $ybot} {
1443 set yscreen [expr {$y - $ytop}]
1445 set yscreen [expr {($ybot - $ytop) / 2}]
1451 if {$curview >= 0} {
1452 set vparentlist($curview) $parentlist
1453 set vchildlist($curview) $childlist
1454 set vdisporder($curview) $displayorder
1455 set vcmitlisted($curview) $commitlisted
1457 set viewdata($curview) \
1458 [list $phase $rowidlist $rowoffsets $rowrangelist \
1459 [flatten idrowranges] [flatten idinlist] \
1460 $rowlaidout $rowoptim $numcommits $linesegends]
1461 } elseif {![info exists viewdata($curview)]
1462 || [lindex $viewdata($curview) 0] ne {}} {
1463 set viewdata($curview) \
1464 [list {} $rowidlist $rowoffsets $rowrangelist]
1467 catch {unset matchinglines}
1468 catch {unset treediffs}
1473 set selectedhlview -1
1474 .bar.view entryconf 2 -state [expr {$n == 0? "disabled": "normal"}]
1475 .bar.view entryconf 3 -state [expr {$n == 0? "disabled": "normal"}]
1476 catch {unset hlview}
1477 .bar.view.hl entryconf 1 -state disabled
1479 if {![info exists viewdata($n)]} {
1480 set pending_select $selid
1486 set phase [lindex $v 0]
1487 set displayorder $vdisporder($n)
1488 set parentlist $vparentlist($n)
1489 set childlist $vchildlist($n)
1490 set commitlisted $vcmitlisted($n)
1491 set rowidlist [lindex $v 1]
1492 set rowoffsets [lindex $v 2]
1493 set rowrangelist [lindex $v 3]
1495 set numcommits [llength $displayorder]
1496 catch {unset idrowranges}
1498 unflatten idrowranges [lindex $v 4]
1499 unflatten idinlist [lindex $v 5]
1500 set rowlaidout [lindex $v 6]
1501 set rowoptim [lindex $v 7]
1502 set numcommits [lindex $v 8]
1503 set linesegends [lindex $v 9]
1506 catch {unset colormap}
1507 catch {unset rowtextx}
1509 set canvxmax [$canv cget -width]
1515 if {$selid ne {} && [info exists commitrow($n,$selid)]} {
1516 set row $commitrow($n,$selid)
1517 # try to get the selected row in the same position on the screen
1518 set ymax [lindex [$canv cget -scrollregion] 3]
1519 set ytop [expr {[yc $row] - $yscreen}]
1523 set yf [expr {$ytop * 1.0 / $ymax}]
1525 allcanvs yview moveto $yf
1529 if {$phase eq "getcommits"} {
1530 show_status "Reading commits..."
1532 if {[info exists commfd($n)]} {
1537 } elseif {$numcommits == 0} {
1538 show_status "No commits selected"
1542 proc addhighlight {n} {
1543 global hlview curview viewdata highlighted highlightedrows
1544 global selectedhlview
1546 if {[info exists hlview]} {
1550 set selectedhlview $n
1551 .bar.view.hl entryconf 1 -state normal
1552 set highlighted($n) 0
1553 set highlightedrows {}
1554 if {$n != $curview && ![info exists viewdata($n)]} {
1555 set viewdata($n) [list getcommits {{}} {{}} {} {} {} 0 0 0 {}]
1556 set vparentlist($n) {}
1557 set vchildlist($n) {}
1558 set vdisporder($n) {}
1559 set vcmitlisted($n) {}
1566 proc delhighlight {} {
1567 global hlview highlightedrows canv linehtag mainfont
1568 global selectedhlview selectedline
1570 if {![info exists hlview]} return
1572 set selectedhlview {}
1573 .bar.view.hl entryconf 1 -state disabled
1574 foreach l $highlightedrows {
1575 $canv itemconf $linehtag($l) -font $mainfont
1576 if {$l == $selectedline} {
1578 set t [eval $canv create rect [$canv bbox $linehtag($l)] \
1579 -outline {{}} -tags secsel \
1580 -fill [$canv cget -selectbackground]]
1586 proc highlightmore {} {
1587 global hlview highlighted commitidx highlightedrows linehtag mainfont
1588 global displayorder vdisporder curview canv commitrow selectedline
1590 set font [concat $mainfont bold]
1591 set max $commitidx($hlview)
1592 if {$hlview == $curview} {
1593 set disp $displayorder
1595 set disp $vdisporder($hlview)
1597 for {set i $highlighted($hlview)} {$i < $max} {incr i} {
1598 set id [lindex $disp $i]
1599 if {[info exists commitrow($curview,$id)]} {
1600 set row $commitrow($curview,$id)
1601 if {[info exists linehtag($row)]} {
1602 $canv itemconf $linehtag($row) -font $font
1603 lappend highlightedrows $row
1604 if {$row == $selectedline} {
1606 set t [eval $canv create rect \
1607 [$canv bbox $linehtag($row)] \
1608 -outline {{}} -tags secsel \
1609 -fill [$canv cget -selectbackground]]
1615 set highlighted($hlview) $max
1618 # Graph layout functions
1620 proc shortids {ids} {
1623 if {[llength $id] > 1} {
1624 lappend res [shortids $id]
1625 } elseif {[regexp {^[0-9a-f]{40}$} $id]} {
1626 lappend res [string range $id 0 7]
1634 proc incrange {l x o} {
1637 set e [lindex $l $x]
1639 lset l $x [expr {$e + $o}]
1648 for {} {$n > 0} {incr n -1} {
1654 proc usedinrange {id l1 l2} {
1655 global children commitrow childlist curview
1657 if {[info exists commitrow($curview,$id)]} {
1658 set r $commitrow($curview,$id)
1659 if {$l1 <= $r && $r <= $l2} {
1660 return [expr {$r - $l1 + 1}]
1662 set kids [lindex $childlist $r]
1664 set kids $children($curview,$id)
1667 set r $commitrow($curview,$c)
1668 if {$l1 <= $r && $r <= $l2} {
1669 return [expr {$r - $l1 + 1}]
1675 proc sanity {row {full 0}} {
1676 global rowidlist rowoffsets
1679 set ids [lindex $rowidlist $row]
1682 if {$id eq {}} continue
1683 if {$col < [llength $ids] - 1 &&
1684 [lsearch -exact -start [expr {$col+1}] $ids $id] >= 0} {
1685 puts "oops: [shortids $id] repeated in row $row col $col: {[shortids [lindex $rowidlist $row]]}"
1687 set o [lindex $rowoffsets $row $col]
1693 if {[lindex $rowidlist $y $x] != $id} {
1694 puts "oops: rowoffsets wrong at row [expr {$y+1}] col [expr {$x-$o}]"
1695 puts " id=[shortids $id] check started at row $row"
1696 for {set i $row} {$i >= $y} {incr i -1} {
1697 puts " row $i ids={[shortids [lindex $rowidlist $i]]} offs={[lindex $rowoffsets $i]}"
1702 set o [lindex $rowoffsets $y $x]
1707 proc makeuparrow {oid x y z} {
1708 global rowidlist rowoffsets uparrowlen idrowranges
1710 for {set i 1} {$i < $uparrowlen && $y > 1} {incr i} {
1713 set off0 [lindex $rowoffsets $y]
1714 for {set x0 $x} {1} {incr x0} {
1715 if {$x0 >= [llength $off0]} {
1716 set x0 [llength [lindex $rowoffsets [expr {$y-1}]]]
1719 set z [lindex $off0 $x0]
1725 set z [expr {$x0 - $x}]
1726 lset rowidlist $y [linsert [lindex $rowidlist $y] $x $oid]
1727 lset rowoffsets $y [linsert [lindex $rowoffsets $y] $x $z]
1729 set tmp [lreplace [lindex $rowoffsets $y] $x $x {}]
1730 lset rowoffsets $y [incrange $tmp [expr {$x+1}] -1]
1731 lappend idrowranges($oid) $y
1734 proc initlayout {} {
1735 global rowidlist rowoffsets displayorder commitlisted
1736 global rowlaidout rowoptim
1737 global idinlist rowchk rowrangelist idrowranges
1738 global numcommits canvxmax canv
1740 global parentlist childlist children
1741 global colormap rowtextx
1753 catch {unset idinlist}
1754 catch {unset rowchk}
1757 set canvxmax [$canv cget -width]
1758 catch {unset colormap}
1759 catch {unset rowtextx}
1760 catch {unset idrowranges}
1764 proc setcanvscroll {} {
1765 global canv canv2 canv3 numcommits linespc canvxmax canvy0
1767 set ymax [expr {$canvy0 + ($numcommits - 0.5) * $linespc + 2}]
1768 $canv conf -scrollregion [list 0 0 $canvxmax $ymax]
1769 $canv2 conf -scrollregion [list 0 0 0 $ymax]
1770 $canv3 conf -scrollregion [list 0 0 0 $ymax]
1773 proc visiblerows {} {
1774 global canv numcommits linespc
1776 set ymax [lindex [$canv cget -scrollregion] 3]
1777 if {$ymax eq {} || $ymax == 0} return
1779 set y0 [expr {int([lindex $f 0] * $ymax)}]
1780 set r0 [expr {int(($y0 - 3) / $linespc) - 1}]
1784 set y1 [expr {int([lindex $f 1] * $ymax)}]
1785 set r1 [expr {int(($y1 - 3) / $linespc) + 1}]
1786 if {$r1 >= $numcommits} {
1787 set r1 [expr {$numcommits - 1}]
1789 return [list $r0 $r1]
1792 proc layoutmore {} {
1793 global rowlaidout rowoptim commitidx numcommits optim_delay
1794 global uparrowlen curview
1797 set rowlaidout [layoutrows $row $commitidx($curview) 0]
1798 set orow [expr {$rowlaidout - $uparrowlen - 1}]
1799 if {$orow > $rowoptim} {
1800 optimize_rows $rowoptim 0 $orow
1803 set canshow [expr {$rowoptim - $optim_delay}]
1804 if {$canshow > $numcommits} {
1809 proc showstuff {canshow} {
1810 global numcommits commitrow pending_select selectedline
1811 global linesegends idrowranges idrangedrawn curview
1813 if {$numcommits == 0} {
1815 set phase "incrdraw"
1819 set numcommits $canshow
1821 set rows [visiblerows]
1822 set r0 [lindex $rows 0]
1823 set r1 [lindex $rows 1]
1825 for {set r $row} {$r < $canshow} {incr r} {
1826 foreach id [lindex $linesegends [expr {$r+1}]] {
1828 foreach {s e} [rowranges $id] {
1830 if {$e ne {} && $e < $numcommits && $s <= $r1 && $e >= $r0
1831 && ![info exists idrangedrawn($id,$i)]} {
1833 set idrangedrawn($id,$i) 1
1838 if {$canshow > $r1} {
1841 while {$row < $canshow} {
1845 if {[info exists pending_select] &&
1846 [info exists commitrow($curview,$pending_select)] &&
1847 $commitrow($curview,$pending_select) < $numcommits} {
1848 selectline $commitrow($curview,$pending_select) 1
1850 if {![info exists selectedline] && ![info exists pending_select]} {
1855 proc layoutrows {row endrow last} {
1856 global rowidlist rowoffsets displayorder
1857 global uparrowlen downarrowlen maxwidth mingaplen
1858 global childlist parentlist
1859 global idrowranges linesegends
1860 global commitidx curview
1861 global idinlist rowchk rowrangelist
1863 set idlist [lindex $rowidlist $row]
1864 set offs [lindex $rowoffsets $row]
1865 while {$row < $endrow} {
1866 set id [lindex $displayorder $row]
1869 foreach p [lindex $parentlist $row] {
1870 if {![info exists idinlist($p)]} {
1872 } elseif {!$idinlist($p)} {
1877 set nev [expr {[llength $idlist] + [llength $newolds]
1878 + [llength $oldolds] - $maxwidth + 1}]
1881 $row + $uparrowlen + $mingaplen >= $commitidx($curview)} break
1882 for {set x [llength $idlist]} {[incr x -1] >= 0} {} {
1883 set i [lindex $idlist $x]
1884 if {![info exists rowchk($i)] || $row >= $rowchk($i)} {
1885 set r [usedinrange $i [expr {$row - $downarrowlen}] \
1886 [expr {$row + $uparrowlen + $mingaplen}]]
1888 set idlist [lreplace $idlist $x $x]
1889 set offs [lreplace $offs $x $x]
1890 set offs [incrange $offs $x 1]
1892 set rm1 [expr {$row - 1}]
1894 lappend idrowranges($i) $rm1
1895 if {[incr nev -1] <= 0} break
1898 set rowchk($id) [expr {$row + $r}]
1901 lset rowidlist $row $idlist
1902 lset rowoffsets $row $offs
1904 lappend linesegends $lse
1905 set col [lsearch -exact $idlist $id]
1907 set col [llength $idlist]
1909 lset rowidlist $row $idlist
1911 if {[lindex $childlist $row] ne {}} {
1912 set z [expr {[llength [lindex $rowidlist [expr {$row-1}]]] - $col}]
1916 lset rowoffsets $row $offs
1918 makeuparrow $id $col $row $z
1924 if {[info exists idrowranges($id)]} {
1925 set ranges $idrowranges($id)
1927 unset idrowranges($id)
1929 lappend rowrangelist $ranges
1931 set offs [ntimes [llength $idlist] 0]
1932 set l [llength $newolds]
1933 set idlist [eval lreplace \$idlist $col $col $newolds]
1936 set offs [lrange $offs 0 [expr {$col - 1}]]
1937 foreach x $newolds {
1942 set tmp [expr {[llength $idlist] - [llength $offs]}]
1944 set offs [concat $offs [ntimes $tmp $o]]
1949 foreach i $newolds {
1951 set idrowranges($i) $row
1954 foreach oid $oldolds {
1955 set idinlist($oid) 1
1956 set idlist [linsert $idlist $col $oid]
1957 set offs [linsert $offs $col $o]
1958 makeuparrow $oid $col $row $o
1961 lappend rowidlist $idlist
1962 lappend rowoffsets $offs
1967 proc addextraid {id row} {
1968 global displayorder commitrow commitinfo
1969 global commitidx commitlisted
1970 global parentlist childlist children curview
1972 incr commitidx($curview)
1973 lappend displayorder $id
1974 lappend commitlisted 0
1975 lappend parentlist {}
1976 set commitrow($curview,$id) $row
1978 if {![info exists commitinfo($id)]} {
1979 set commitinfo($id) {"No commit information available"}
1981 if {![info exists children($curview,$id)]} {
1982 set children($curview,$id) {}
1984 lappend childlist $children($curview,$id)
1987 proc layouttail {} {
1988 global rowidlist rowoffsets idinlist commitidx curview
1989 global idrowranges rowrangelist
1991 set row $commitidx($curview)
1992 set idlist [lindex $rowidlist $row]
1993 while {$idlist ne {}} {
1994 set col [expr {[llength $idlist] - 1}]
1995 set id [lindex $idlist $col]
1998 lappend idrowranges($id) $row
1999 lappend rowrangelist $idrowranges($id)
2000 unset idrowranges($id)
2002 set offs [ntimes $col 0]
2003 set idlist [lreplace $idlist $col $col]
2004 lappend rowidlist $idlist
2005 lappend rowoffsets $offs
2008 foreach id [array names idinlist] {
2010 lset rowidlist $row [list $id]
2011 lset rowoffsets $row 0
2012 makeuparrow $id 0 $row 0
2013 lappend idrowranges($id) $row
2014 lappend rowrangelist $idrowranges($id)
2015 unset idrowranges($id)
2017 lappend rowidlist {}
2018 lappend rowoffsets {}
2022 proc insert_pad {row col npad} {
2023 global rowidlist rowoffsets
2025 set pad [ntimes $npad {}]
2026 lset rowidlist $row [eval linsert [list [lindex $rowidlist $row]] $col $pad]
2027 set tmp [eval linsert [list [lindex $rowoffsets $row]] $col $pad]
2028 lset rowoffsets $row [incrange $tmp [expr {$col + $npad}] [expr {-$npad}]]
2031 proc optimize_rows {row col endrow} {
2032 global rowidlist rowoffsets idrowranges displayorder
2034 for {} {$row < $endrow} {incr row} {
2035 set idlist [lindex $rowidlist $row]
2036 set offs [lindex $rowoffsets $row]
2038 for {} {$col < [llength $offs]} {incr col} {
2039 if {[lindex $idlist $col] eq {}} {
2043 set z [lindex $offs $col]
2044 if {$z eq {}} continue
2046 set x0 [expr {$col + $z}]
2047 set y0 [expr {$row - 1}]
2048 set z0 [lindex $rowoffsets $y0 $x0]
2050 set id [lindex $idlist $col]
2051 set ranges [rowranges $id]
2052 if {$ranges ne {} && $y0 > [lindex $ranges 0]} {
2056 if {$z < -1 || ($z < 0 && $isarrow)} {
2057 set npad [expr {-1 - $z + $isarrow}]
2058 set offs [incrange $offs $col $npad]
2059 insert_pad $y0 $x0 $npad
2061 optimize_rows $y0 $x0 $row
2063 set z [lindex $offs $col]
2064 set x0 [expr {$col + $z}]
2065 set z0 [lindex $rowoffsets $y0 $x0]
2066 } elseif {$z > 1 || ($z > 0 && $isarrow)} {
2067 set npad [expr {$z - 1 + $isarrow}]
2068 set y1 [expr {$row + 1}]
2069 set offs2 [lindex $rowoffsets $y1]
2073 if {$z eq {} || $x1 + $z < $col} continue
2074 if {$x1 + $z > $col} {
2077 lset rowoffsets $y1 [incrange $offs2 $x1 $npad]
2080 set pad [ntimes $npad {}]
2081 set idlist [eval linsert \$idlist $col $pad]
2082 set tmp [eval linsert \$offs $col $pad]
2084 set offs [incrange $tmp $col [expr {-$npad}]]
2085 set z [lindex $offs $col]
2088 if {$z0 eq {} && !$isarrow} {
2089 # this line links to its first child on row $row-2
2090 set rm2 [expr {$row - 2}]
2091 set id [lindex $displayorder $rm2]
2092 set xc [lsearch -exact [lindex $rowidlist $rm2] $id]
2094 set z0 [expr {$xc - $x0}]
2097 if {$z0 ne {} && $z < 0 && $z0 > 0} {
2098 insert_pad $y0 $x0 1
2099 set offs [incrange $offs $col 1]
2100 optimize_rows $y0 [expr {$x0 + 1}] $row
2105 for {set col [llength $idlist]} {[incr col -1] >= 0} {} {
2106 set o [lindex $offs $col]
2108 # check if this is the link to the first child
2109 set id [lindex $idlist $col]
2110 set ranges [rowranges $id]
2111 if {$ranges ne {} && $row == [lindex $ranges 0]} {
2112 # it is, work out offset to child
2113 set y0 [expr {$row - 1}]
2114 set id [lindex $displayorder $y0]
2115 set x0 [lsearch -exact [lindex $rowidlist $y0] $id]
2117 set o [expr {$x0 - $col}]
2121 if {$o eq {} || $o <= 0} break
2123 if {$o ne {} && [incr col] < [llength $idlist]} {
2124 set y1 [expr {$row + 1}]
2125 set offs2 [lindex $rowoffsets $y1]
2129 if {$z eq {} || $x1 + $z < $col} continue
2130 lset rowoffsets $y1 [incrange $offs2 $x1 1]
2133 set idlist [linsert $idlist $col {}]
2134 set tmp [linsert $offs $col {}]
2136 set offs [incrange $tmp $col -1]
2139 lset rowidlist $row $idlist
2140 lset rowoffsets $row $offs
2146 global canvx0 linespc
2147 return [expr {$canvx0 + $col * $linespc}]
2151 global canvy0 linespc
2152 return [expr {$canvy0 + $row * $linespc}]
2155 proc linewidth {id} {
2156 global thickerline lthickness
2159 if {[info exists thickerline] && $id eq $thickerline} {
2160 set wid [expr {2 * $lthickness}]
2165 proc rowranges {id} {
2166 global phase idrowranges commitrow rowlaidout rowrangelist curview
2170 ([info exists commitrow($curview,$id)]
2171 && $commitrow($curview,$id) < $rowlaidout)} {
2172 set ranges [lindex $rowrangelist $commitrow($curview,$id)]
2173 } elseif {[info exists idrowranges($id)]} {
2174 set ranges $idrowranges($id)
2179 proc drawlineseg {id i} {
2180 global rowoffsets rowidlist
2182 global canv colormap linespc
2183 global numcommits commitrow curview
2185 set ranges [rowranges $id]
2187 if {[info exists commitrow($curview,$id)]
2188 && $commitrow($curview,$id) < $numcommits} {
2189 set downarrow [expr {$i < [llength $ranges] / 2 - 1}]
2193 set startrow [lindex $ranges [expr {2 * $i}]]
2194 set row [lindex $ranges [expr {2 * $i + 1}]]
2195 if {$startrow == $row} return
2198 set col [lsearch -exact [lindex $rowidlist $row] $id]
2200 puts "oops: drawline: id $id not on row $row"
2206 set o [lindex $rowoffsets $row $col]
2209 # changing direction
2210 set x [xc $row $col]
2212 lappend coords $x $y
2218 set x [xc $row $col]
2220 lappend coords $x $y
2222 # draw the link to the first child as part of this line
2224 set child [lindex $displayorder $row]
2225 set ccol [lsearch -exact [lindex $rowidlist $row] $child]
2227 set x [xc $row $ccol]
2229 if {$ccol < $col - 1} {
2230 lappend coords [xc $row [expr {$col - 1}]] [yc $row]
2231 } elseif {$ccol > $col + 1} {
2232 lappend coords [xc $row [expr {$col + 1}]] [yc $row]
2234 lappend coords $x $y
2237 if {[llength $coords] < 4} return
2239 # This line has an arrow at the lower end: check if the arrow is
2240 # on a diagonal segment, and if so, work around the Tk 8.4
2241 # refusal to draw arrows on diagonal lines.
2242 set x0 [lindex $coords 0]
2243 set x1 [lindex $coords 2]
2245 set y0 [lindex $coords 1]
2246 set y1 [lindex $coords 3]
2247 if {$y0 - $y1 <= 2 * $linespc && $x1 == [lindex $coords 4]} {
2248 # we have a nearby vertical segment, just trim off the diag bit
2249 set coords [lrange $coords 2 end]
2251 set slope [expr {($x0 - $x1) / ($y0 - $y1)}]
2252 set xi [expr {$x0 - $slope * $linespc / 2}]
2253 set yi [expr {$y0 - $linespc / 2}]
2254 set coords [lreplace $coords 0 1 $xi $y0 $xi $yi]
2258 set arrow [expr {2 * ($i > 0) + $downarrow}]
2259 set arrow [lindex {none first last both} $arrow]
2260 set t [$canv create line $coords -width [linewidth $id] \
2261 -fill $colormap($id) -tags lines.$id -arrow $arrow]
2266 proc drawparentlinks {id row col olds} {
2267 global rowidlist canv colormap
2269 set row2 [expr {$row + 1}]
2270 set x [xc $row $col]
2273 set ids [lindex $rowidlist $row2]
2274 # rmx = right-most X coord used
2277 set i [lsearch -exact $ids $p]
2279 puts "oops, parent $p of $id not in list"
2282 set x2 [xc $row2 $i]
2286 set ranges [rowranges $p]
2287 if {$ranges ne {} && $row2 == [lindex $ranges 0]
2288 && $row2 < [lindex $ranges 1]} {
2289 # drawlineseg will do this one for us
2293 # should handle duplicated parents here...
2294 set coords [list $x $y]
2295 if {$i < $col - 1} {
2296 lappend coords [xc $row [expr {$i + 1}]] $y
2297 } elseif {$i > $col + 1} {
2298 lappend coords [xc $row [expr {$i - 1}]] $y
2300 lappend coords $x2 $y2
2301 set t [$canv create line $coords -width [linewidth $p] \
2302 -fill $colormap($p) -tags lines.$p]
2309 proc drawlines {id} {
2310 global colormap canv
2312 global children iddrawn commitrow rowidlist curview
2314 $canv delete lines.$id
2315 set nr [expr {[llength [rowranges $id]] / 2}]
2316 for {set i 0} {$i < $nr} {incr i} {
2317 if {[info exists idrangedrawn($id,$i)]} {
2321 foreach child $children($curview,$id) {
2322 if {[info exists iddrawn($child)]} {
2323 set row $commitrow($curview,$child)
2324 set col [lsearch -exact [lindex $rowidlist $row] $child]
2326 drawparentlinks $child $row $col [list $id]
2332 proc drawcmittext {id row col rmx} {
2333 global linespc canv canv2 canv3 canvy0
2334 global commitlisted commitinfo rowidlist
2335 global rowtextx idpos idtags idheads idotherrefs
2336 global linehtag linentag linedtag
2337 global mainfont canvxmax
2338 global hlview commitrow highlightedrows
2340 set ofill [expr {[lindex $commitlisted $row]? "blue": "white"}]
2341 set x [xc $row $col]
2343 set orad [expr {$linespc / 3}]
2344 set t [$canv create oval [expr {$x - $orad}] [expr {$y - $orad}] \
2345 [expr {$x + $orad - 1}] [expr {$y + $orad - 1}] \
2346 -fill $ofill -outline black -width 1]
2348 $canv bind $t <1> {selcanvline {} %x %y}
2349 set xt [xc $row [llength [lindex $rowidlist $row]]]
2353 set rowtextx($row) $xt
2354 set idpos($id) [list $x $xt $y]
2355 if {[info exists idtags($id)] || [info exists idheads($id)]
2356 || [info exists idotherrefs($id)]} {
2357 set xt [drawtags $id $x $xt $y]
2359 set headline [lindex $commitinfo($id) 0]
2360 set name [lindex $commitinfo($id) 1]
2361 set date [lindex $commitinfo($id) 2]
2362 set date [formatdate $date]
2364 if {[info exists hlview] && [info exists commitrow($hlview,$id)]} {
2366 lappend highlightedrows $row
2368 set linehtag($row) [$canv create text $xt $y -anchor w \
2369 -text $headline -font $font]
2370 $canv bind $linehtag($row) <Button-3> "rowmenu %X %Y $id"
2371 set linentag($row) [$canv2 create text 3 $y -anchor w \
2372 -text $name -font $mainfont]
2373 set linedtag($row) [$canv3 create text 3 $y -anchor w \
2374 -text $date -font $mainfont]
2375 set xr [expr {$xt + [font measure $mainfont $headline]}]
2376 if {$xr > $canvxmax} {
2382 proc drawcmitrow {row} {
2383 global displayorder rowidlist
2384 global idrangedrawn iddrawn
2385 global commitinfo parentlist numcommits
2387 if {$row >= $numcommits} return
2388 foreach id [lindex $rowidlist $row] {
2389 if {$id eq {}} continue
2391 foreach {s e} [rowranges $id] {
2393 if {$row < $s} continue
2396 if {$e < $numcommits && ![info exists idrangedrawn($id,$i)]} {
2398 set idrangedrawn($id,$i) 1
2405 set id [lindex $displayorder $row]
2406 if {[info exists iddrawn($id)]} return
2407 set col [lsearch -exact [lindex $rowidlist $row] $id]
2409 puts "oops, row $row id $id not in list"
2412 if {![info exists commitinfo($id)]} {
2416 set olds [lindex $parentlist $row]
2418 set rmx [drawparentlinks $id $row $col $olds]
2422 drawcmittext $id $row $col $rmx
2426 proc drawfrac {f0 f1} {
2427 global numcommits canv
2430 set ymax [lindex [$canv cget -scrollregion] 3]
2431 if {$ymax eq {} || $ymax == 0} return
2432 set y0 [expr {int($f0 * $ymax)}]
2433 set row [expr {int(($y0 - 3) / $linespc) - 1}]
2437 set y1 [expr {int($f1 * $ymax)}]
2438 set endrow [expr {int(($y1 - 3) / $linespc) + 1}]
2439 if {$endrow >= $numcommits} {
2440 set endrow [expr {$numcommits - 1}]
2442 for {} {$row <= $endrow} {incr row} {
2447 proc drawvisible {} {
2449 eval drawfrac [$canv yview]
2452 proc clear_display {} {
2453 global iddrawn idrangedrawn
2456 catch {unset iddrawn}
2457 catch {unset idrangedrawn}
2460 proc findcrossings {id} {
2461 global rowidlist parentlist numcommits rowoffsets displayorder
2465 foreach {s e} [rowranges $id] {
2466 if {$e >= $numcommits} {
2467 set e [expr {$numcommits - 1}]
2469 if {$e <= $s} continue
2470 set x [lsearch -exact [lindex $rowidlist $e] $id]
2472 puts "findcrossings: oops, no [shortids $id] in row $e"
2475 for {set row $e} {[incr row -1] >= $s} {} {
2476 set olds [lindex $parentlist $row]
2477 set kid [lindex $displayorder $row]
2478 set kidx [lsearch -exact [lindex $rowidlist $row] $kid]
2479 if {$kidx < 0} continue
2480 set nextrow [lindex $rowidlist [expr {$row + 1}]]
2482 set px [lsearch -exact $nextrow $p]
2483 if {$px < 0} continue
2484 if {($kidx < $x && $x < $px) || ($px < $x && $x < $kidx)} {
2485 if {[lsearch -exact $ccross $p] >= 0} continue
2486 if {$x == $px + ($kidx < $px? -1: 1)} {
2488 } elseif {[lsearch -exact $cross $p] < 0} {
2493 set inc [lindex $rowoffsets $row $x]
2494 if {$inc eq {}} break
2498 return [concat $ccross {{}} $cross]
2501 proc assigncolor {id} {
2502 global colormap colors nextcolor
2503 global commitrow parentlist children children curview
2505 if {[info exists colormap($id)]} return
2506 set ncolors [llength $colors]
2507 if {[info exists children($curview,$id)]} {
2508 set kids $children($curview,$id)
2512 if {[llength $kids] == 1} {
2513 set child [lindex $kids 0]
2514 if {[info exists colormap($child)]
2515 && [llength [lindex $parentlist $commitrow($curview,$child)]] == 1} {
2516 set colormap($id) $colormap($child)
2522 foreach x [findcrossings $id] {
2524 # delimiter between corner crossings and other crossings
2525 if {[llength $badcolors] >= $ncolors - 1} break
2526 set origbad $badcolors
2528 if {[info exists colormap($x)]
2529 && [lsearch -exact $badcolors $colormap($x)] < 0} {
2530 lappend badcolors $colormap($x)
2533 if {[llength $badcolors] >= $ncolors} {
2534 set badcolors $origbad
2536 set origbad $badcolors
2537 if {[llength $badcolors] < $ncolors - 1} {
2538 foreach child $kids {
2539 if {[info exists colormap($child)]
2540 && [lsearch -exact $badcolors $colormap($child)] < 0} {
2541 lappend badcolors $colormap($child)
2543 foreach p [lindex $parentlist $commitrow($curview,$child)] {
2544 if {[info exists colormap($p)]
2545 && [lsearch -exact $badcolors $colormap($p)] < 0} {
2546 lappend badcolors $colormap($p)
2550 if {[llength $badcolors] >= $ncolors} {
2551 set badcolors $origbad
2554 for {set i 0} {$i <= $ncolors} {incr i} {
2555 set c [lindex $colors $nextcolor]
2556 if {[incr nextcolor] >= $ncolors} {
2559 if {[lsearch -exact $badcolors $c]} break
2561 set colormap($id) $c
2564 proc bindline {t id} {
2567 $canv bind $t <Enter> "lineenter %x %y $id"
2568 $canv bind $t <Motion> "linemotion %x %y $id"
2569 $canv bind $t <Leave> "lineleave $id"
2570 $canv bind $t <Button-1> "lineclick %x %y $id 1"
2573 proc drawtags {id x xt y1} {
2574 global idtags idheads idotherrefs
2575 global linespc lthickness
2576 global canv mainfont commitrow rowtextx curview
2581 if {[info exists idtags($id)]} {
2582 set marks $idtags($id)
2583 set ntags [llength $marks]
2585 if {[info exists idheads($id)]} {
2586 set marks [concat $marks $idheads($id)]
2587 set nheads [llength $idheads($id)]
2589 if {[info exists idotherrefs($id)]} {
2590 set marks [concat $marks $idotherrefs($id)]
2596 set delta [expr {int(0.5 * ($linespc - $lthickness))}]
2597 set yt [expr {$y1 - 0.5 * $linespc}]
2598 set yb [expr {$yt + $linespc - 1}]
2601 foreach tag $marks {
2602 set wid [font measure $mainfont $tag]
2605 set xt [expr {$xt + $delta + $wid + $lthickness + $linespc}]
2607 set t [$canv create line $x $y1 [lindex $xvals end] $y1 \
2608 -width $lthickness -fill black -tags tag.$id]
2610 foreach tag $marks x $xvals wid $wvals {
2611 set xl [expr {$x + $delta}]
2612 set xr [expr {$x + $delta + $wid + $lthickness}]
2613 if {[incr ntags -1] >= 0} {
2615 set t [$canv create polygon $x [expr {$yt + $delta}] $xl $yt \
2616 $xr $yt $xr $yb $xl $yb $x [expr {$yb - $delta}] \
2617 -width 1 -outline black -fill yellow -tags tag.$id]
2618 $canv bind $t <1> [list showtag $tag 1]
2619 set rowtextx($commitrow($curview,$id)) [expr {$xr + $linespc}]
2621 # draw a head or other ref
2622 if {[incr nheads -1] >= 0} {
2627 set xl [expr {$xl - $delta/2}]
2628 $canv create polygon $x $yt $xr $yt $xr $yb $x $yb \
2629 -width 1 -outline black -fill $col -tags tag.$id
2630 if {[regexp {^(remotes/.*/|remotes/)} $tag match remoteprefix]} {
2631 set rwid [font measure $mainfont $remoteprefix]
2632 set xi [expr {$x + 1}]
2633 set yti [expr {$yt + 1}]
2634 set xri [expr {$x + $rwid}]
2635 $canv create polygon $xi $yti $xri $yti $xri $yb $xi $yb \
2636 -width 0 -fill "#ffddaa" -tags tag.$id
2639 set t [$canv create text $xl $y1 -anchor w -text $tag \
2640 -font $mainfont -tags tag.$id]
2642 $canv bind $t <1> [list showtag $tag 1]
2648 proc xcoord {i level ln} {
2649 global canvx0 xspc1 xspc2
2651 set x [expr {$canvx0 + $i * $xspc1($ln)}]
2652 if {$i > 0 && $i == $level} {
2653 set x [expr {$x + 0.5 * ($xspc2 - $xspc1($ln))}]
2654 } elseif {$i > $level} {
2655 set x [expr {$x + $xspc2 - $xspc1($ln)}]
2660 proc show_status {msg} {
2661 global canv mainfont
2664 $canv create text 3 3 -anchor nw -text $msg -font $mainfont -tags textitems
2667 proc finishcommits {} {
2668 global commitidx phase curview
2669 global canv mainfont ctext maincursor textcursor
2670 global findinprogress pending_select
2672 if {$commitidx($curview) > 0} {
2675 show_status "No commits selected"
2678 catch {unset pending_select}
2681 # Don't change the text pane cursor if it is currently the hand cursor,
2682 # showing that we are over a sha1 ID link.
2683 proc settextcursor {c} {
2684 global ctext curtextcursor
2686 if {[$ctext cget -cursor] == $curtextcursor} {
2687 $ctext config -cursor $c
2689 set curtextcursor $c
2692 proc nowbusy {what} {
2695 if {[array names isbusy] eq {}} {
2696 . config -cursor watch
2702 proc notbusy {what} {
2703 global isbusy maincursor textcursor
2705 catch {unset isbusy($what)}
2706 if {[array names isbusy] eq {}} {
2707 . config -cursor $maincursor
2708 settextcursor $textcursor
2715 global canvy0 numcommits linespc
2716 global rowlaidout commitidx curview
2717 global pending_select
2720 layoutrows $rowlaidout $commitidx($curview) 1
2722 optimize_rows $row 0 $commitidx($curview)
2723 showstuff $commitidx($curview)
2724 if {[info exists pending_select]} {
2728 set drawmsecs [expr {[clock clicks -milliseconds] - $startmsecs}]
2729 #puts "overall $drawmsecs ms for $numcommits commits"
2732 proc findmatches {f} {
2733 global findtype foundstring foundstrlen
2734 if {$findtype == "Regexp"} {
2735 set matches [regexp -indices -all -inline $foundstring $f]
2737 if {$findtype == "IgnCase"} {
2738 set str [string tolower $f]
2744 while {[set j [string first $foundstring $str $i]] >= 0} {
2745 lappend matches [list $j [expr {$j+$foundstrlen-1}]]
2746 set i [expr {$j + $foundstrlen}]
2753 global findtype findloc findstring markedmatches commitinfo
2754 global numcommits displayorder linehtag linentag linedtag
2755 global mainfont canv canv2 canv3 selectedline
2756 global matchinglines foundstring foundstrlen matchstring
2762 set matchinglines {}
2763 if {$findloc == "Pickaxe"} {
2767 if {$findtype == "IgnCase"} {
2768 set foundstring [string tolower $findstring]
2770 set foundstring $findstring
2772 set foundstrlen [string length $findstring]
2773 if {$foundstrlen == 0} return
2774 regsub -all {[*?\[\\]} $foundstring {\\&} matchstring
2775 set matchstring "*$matchstring*"
2776 if {$findloc == "Files"} {
2780 if {![info exists selectedline]} {
2783 set oldsel $selectedline
2786 set fldtypes {Headline Author Date Committer CDate Comment}
2788 foreach id $displayorder {
2789 set d $commitdata($id)
2791 if {$findtype == "Regexp"} {
2792 set doesmatch [regexp $foundstring $d]
2793 } elseif {$findtype == "IgnCase"} {
2794 set doesmatch [string match -nocase $matchstring $d]
2796 set doesmatch [string match $matchstring $d]
2798 if {!$doesmatch} continue
2799 if {![info exists commitinfo($id)]} {
2802 set info $commitinfo($id)
2804 foreach f $info ty $fldtypes {
2805 if {$findloc != "All fields" && $findloc != $ty} {
2808 set matches [findmatches $f]
2809 if {$matches == {}} continue
2811 if {$ty == "Headline"} {
2813 markmatches $canv $l $f $linehtag($l) $matches $mainfont
2814 } elseif {$ty == "Author"} {
2816 markmatches $canv2 $l $f $linentag($l) $matches $mainfont
2817 } elseif {$ty == "Date"} {
2819 markmatches $canv3 $l $f $linedtag($l) $matches $mainfont
2823 lappend matchinglines $l
2824 if {!$didsel && $l > $oldsel} {
2830 if {$matchinglines == {}} {
2832 } elseif {!$didsel} {
2833 findselectline [lindex $matchinglines 0]
2837 proc findselectline {l} {
2838 global findloc commentend ctext
2840 if {$findloc == "All fields" || $findloc == "Comments"} {
2841 # highlight the matches in the comments
2842 set f [$ctext get 1.0 $commentend]
2843 set matches [findmatches $f]
2844 foreach match $matches {
2845 set start [lindex $match 0]
2846 set end [expr {[lindex $match 1] + 1}]
2847 $ctext tag add found "1.0 + $start c" "1.0 + $end c"
2852 proc findnext {restart} {
2853 global matchinglines selectedline
2854 if {![info exists matchinglines]} {
2860 if {![info exists selectedline]} return
2861 foreach l $matchinglines {
2862 if {$l > $selectedline} {
2871 global matchinglines selectedline
2872 if {![info exists matchinglines]} {
2876 if {![info exists selectedline]} return
2878 foreach l $matchinglines {
2879 if {$l >= $selectedline} break
2883 findselectline $prev
2889 proc findlocchange {name ix op} {
2890 global findloc findtype findtypemenu
2891 if {$findloc == "Pickaxe"} {
2897 $findtypemenu entryconf 1 -state $state
2898 $findtypemenu entryconf 2 -state $state
2901 proc stopfindproc {{done 0}} {
2902 global findprocpid findprocfile findids
2903 global ctext findoldcursor phase maincursor textcursor
2904 global findinprogress
2906 catch {unset findids}
2907 if {[info exists findprocpid]} {
2909 catch {exec kill $findprocpid}
2911 catch {close $findprocfile}
2914 catch {unset findinprogress}
2918 proc findpatches {} {
2919 global findstring selectedline numcommits
2920 global findprocpid findprocfile
2921 global finddidsel ctext displayorder findinprogress
2922 global findinsertpos
2924 if {$numcommits == 0} return
2926 # make a list of all the ids to search, starting at the one
2927 # after the selected line (if any)
2928 if {[info exists selectedline]} {
2934 for {set i 0} {$i < $numcommits} {incr i} {
2935 if {[incr l] >= $numcommits} {
2938 append inputids [lindex $displayorder $l] "\n"
2942 set f [open [list | git-diff-tree --stdin -s -r -S$findstring \
2945 error_popup "Error starting search process: $err"
2949 set findinsertpos end
2951 set findprocpid [pid $f]
2952 fconfigure $f -blocking 0
2953 fileevent $f readable readfindproc
2956 set findinprogress 1
2959 proc readfindproc {} {
2960 global findprocfile finddidsel
2961 global commitrow matchinglines findinsertpos curview
2963 set n [gets $findprocfile line]
2965 if {[eof $findprocfile]} {
2973 if {![regexp {^[0-9a-f]{40}} $line id]} {
2974 error_popup "Can't parse git-diff-tree output: $line"
2978 if {![info exists commitrow($curview,$id)]} {
2979 puts stderr "spurious id: $id"
2982 set l $commitrow($curview,$id)
2986 proc insertmatch {l id} {
2987 global matchinglines findinsertpos finddidsel
2989 if {$findinsertpos == "end"} {
2990 if {$matchinglines != {} && $l < [lindex $matchinglines 0]} {
2991 set matchinglines [linsert $matchinglines 0 $l]
2994 lappend matchinglines $l
2997 set matchinglines [linsert $matchinglines $findinsertpos $l]
3008 global selectedline numcommits displayorder ctext
3009 global ffileline finddidsel parentlist
3010 global findinprogress findstartline findinsertpos
3011 global treediffs fdiffid fdiffsneeded fdiffpos
3012 global findmergefiles
3014 if {$numcommits == 0} return
3016 if {[info exists selectedline]} {
3017 set l [expr {$selectedline + 1}]
3022 set findstartline $l
3026 set id [lindex $displayorder $l]
3027 if {$findmergefiles || [llength [lindex $parentlist $l]] == 1} {
3028 if {![info exists treediffs($id)]} {
3029 append diffsneeded "$id\n"
3030 lappend fdiffsneeded $id
3033 if {[incr l] >= $numcommits} {
3036 if {$l == $findstartline} break
3039 # start off a git-diff-tree process if needed
3040 if {$diffsneeded ne {}} {
3042 set df [open [list | git-diff-tree -r --stdin << $diffsneeded] r]
3044 error_popup "Error starting search process: $err"
3047 catch {unset fdiffid}
3049 fconfigure $df -blocking 0
3050 fileevent $df readable [list readfilediffs $df]
3054 set findinsertpos end
3055 set id [lindex $displayorder $l]
3057 set findinprogress 1
3062 proc readfilediffs {df} {
3063 global findid fdiffid fdiffs
3065 set n [gets $df line]
3069 if {[catch {close $df} err]} {
3072 error_popup "Error in git-diff-tree: $err"
3073 } elseif {[info exists findid]} {
3077 error_popup "Couldn't find diffs for $id"
3082 if {[regexp {^([0-9a-f]{40})$} $line match id]} {
3083 # start of a new string of diffs
3087 } elseif {[string match ":*" $line]} {
3088 lappend fdiffs [lindex $line 5]
3092 proc donefilediff {} {
3093 global fdiffid fdiffs treediffs findid
3094 global fdiffsneeded fdiffpos
3096 if {[info exists fdiffid]} {
3097 while {[lindex $fdiffsneeded $fdiffpos] ne $fdiffid
3098 && $fdiffpos < [llength $fdiffsneeded]} {
3099 # git-diff-tree doesn't output anything for a commit
3100 # which doesn't change anything
3101 set nullid [lindex $fdiffsneeded $fdiffpos]
3102 set treediffs($nullid) {}
3103 if {[info exists findid] && $nullid eq $findid} {
3111 if {![info exists treediffs($fdiffid)]} {
3112 set treediffs($fdiffid) $fdiffs
3114 if {[info exists findid] && $fdiffid eq $findid} {
3122 global findid treediffs parentlist
3123 global ffileline findstartline finddidsel
3124 global displayorder numcommits matchinglines findinprogress
3125 global findmergefiles
3129 set id [lindex $displayorder $l]
3130 if {$findmergefiles || [llength [lindex $parentlist $l]] == 1} {
3131 if {![info exists treediffs($id)]} {
3137 foreach f $treediffs($id) {
3138 set x [findmatches $f]
3148 if {[incr l] >= $numcommits} {
3151 if {$l == $findstartline} break
3159 # mark a commit as matching by putting a yellow background
3160 # behind the headline
3161 proc markheadline {l id} {
3162 global canv mainfont linehtag
3165 set bbox [$canv bbox $linehtag($l)]
3166 set t [$canv create rect $bbox -outline {} -tags matches -fill yellow]
3170 # mark the bits of a headline, author or date that match a find string
3171 proc markmatches {canv l str tag matches font} {
3172 set bbox [$canv bbox $tag]
3173 set x0 [lindex $bbox 0]
3174 set y0 [lindex $bbox 1]
3175 set y1 [lindex $bbox 3]
3176 foreach match $matches {
3177 set start [lindex $match 0]
3178 set end [lindex $match 1]
3179 if {$start > $end} continue
3180 set xoff [font measure $font [string range $str 0 [expr {$start-1}]]]
3181 set xlen [font measure $font [string range $str 0 [expr {$end}]]]
3182 set t [$canv create rect [expr {$x0+$xoff}] $y0 \
3183 [expr {$x0+$xlen+2}] $y1 \
3184 -outline {} -tags matches -fill yellow]
3189 proc unmarkmatches {} {
3190 global matchinglines findids
3191 allcanvs delete matches
3192 catch {unset matchinglines}
3193 catch {unset findids}
3196 proc selcanvline {w x y} {
3197 global canv canvy0 ctext linespc
3199 set ymax [lindex [$canv cget -scrollregion] 3]
3200 if {$ymax == {}} return
3201 set yfrac [lindex [$canv yview] 0]
3202 set y [expr {$y + $yfrac * $ymax}]
3203 set l [expr {int(($y - $canvy0) / $linespc + 0.5)}]
3208 if {![info exists rowtextx($l)] || $x < $rowtextx($l)} return
3214 proc commit_descriptor {p} {
3217 if {[info exists commitinfo($p)]} {
3218 set l [lindex $commitinfo($p) 0]
3223 # append some text to the ctext widget, and make any SHA1 ID
3224 # that we know about be a clickable link.
3225 proc appendwithlinks {text} {
3226 global ctext commitrow linknum curview
3228 set start [$ctext index "end - 1c"]
3229 $ctext insert end $text
3230 $ctext insert end "\n"
3231 set links [regexp -indices -all -inline {[0-9a-f]{40}} $text]
3235 set linkid [string range $text $s $e]
3236 if {![info exists commitrow($curview,$linkid)]} continue
3238 $ctext tag add link "$start + $s c" "$start + $e c"
3239 $ctext tag add link$linknum "$start + $s c" "$start + $e c"
3240 $ctext tag bind link$linknum <1> \
3241 [list selectline $commitrow($curview,$linkid) 1]
3244 $ctext tag conf link -foreground blue -underline 1
3245 $ctext tag bind link <Enter> { %W configure -cursor hand2 }
3246 $ctext tag bind link <Leave> { %W configure -cursor $curtextcursor }
3249 proc viewnextline {dir} {
3253 set ymax [lindex [$canv cget -scrollregion] 3]
3254 set wnow [$canv yview]
3255 set wtop [expr {[lindex $wnow 0] * $ymax}]
3256 set newtop [expr {$wtop + $dir * $linespc}]
3259 } elseif {$newtop > $ymax} {
3262 allcanvs yview moveto [expr {$newtop * 1.0 / $ymax}]
3265 proc selectline {l isnew} {
3266 global canv canv2 canv3 ctext commitinfo selectedline
3267 global displayorder linehtag linentag linedtag
3268 global canvy0 linespc parentlist childlist
3269 global currentid sha1entry
3270 global commentend idtags linknum
3271 global mergemax numcommits pending_select
3274 catch {unset pending_select}
3277 if {$l < 0 || $l >= $numcommits} return
3278 set y [expr {$canvy0 + $l * $linespc}]
3279 set ymax [lindex [$canv cget -scrollregion] 3]
3280 set ytop [expr {$y - $linespc - 1}]
3281 set ybot [expr {$y + $linespc + 1}]
3282 set wnow [$canv yview]
3283 set wtop [expr {[lindex $wnow 0] * $ymax}]
3284 set wbot [expr {[lindex $wnow 1] * $ymax}]
3285 set wh [expr {$wbot - $wtop}]
3287 if {$ytop < $wtop} {
3288 if {$ybot < $wtop} {
3289 set newtop [expr {$y - $wh / 2.0}]
3292 if {$newtop > $wtop - $linespc} {
3293 set newtop [expr {$wtop - $linespc}]
3296 } elseif {$ybot > $wbot} {
3297 if {$ytop > $wbot} {
3298 set newtop [expr {$y - $wh / 2.0}]
3300 set newtop [expr {$ybot - $wh}]
3301 if {$newtop < $wtop + $linespc} {
3302 set newtop [expr {$wtop + $linespc}]
3306 if {$newtop != $wtop} {
3310 allcanvs yview moveto [expr {$newtop * 1.0 / $ymax}]
3314 if {![info exists linehtag($l)]} return
3316 set t [eval $canv create rect [$canv bbox $linehtag($l)] -outline {{}} \
3317 -tags secsel -fill [$canv cget -selectbackground]]
3319 $canv2 delete secsel
3320 set t [eval $canv2 create rect [$canv2 bbox $linentag($l)] -outline {{}} \
3321 -tags secsel -fill [$canv2 cget -selectbackground]]
3323 $canv3 delete secsel
3324 set t [eval $canv3 create rect [$canv3 bbox $linedtag($l)] -outline {{}} \
3325 -tags secsel -fill [$canv3 cget -selectbackground]]
3329 addtohistory [list selectline $l 0]
3334 set id [lindex $displayorder $l]
3336 $sha1entry delete 0 end
3337 $sha1entry insert 0 $id
3338 $sha1entry selection from 0
3339 $sha1entry selection to end
3341 $ctext conf -state normal
3342 $ctext delete 0.0 end
3344 set info $commitinfo($id)
3345 set date [formatdate [lindex $info 2]]
3346 $ctext insert end "Author: [lindex $info 1] $date\n"
3347 set date [formatdate [lindex $info 4]]
3348 $ctext insert end "Committer: [lindex $info 3] $date\n"
3349 if {[info exists idtags($id)]} {
3350 $ctext insert end "Tags:"
3351 foreach tag $idtags($id) {
3352 $ctext insert end " $tag"
3354 $ctext insert end "\n"
3358 set olds [lindex $parentlist $l]
3359 if {[llength $olds] > 1} {
3362 if {$np >= $mergemax} {
3367 $ctext insert end "Parent: " $tag
3368 appendwithlinks [commit_descriptor $p]
3373 append comment "Parent: [commit_descriptor $p]\n"
3377 foreach c [lindex $childlist $l] {
3378 append comment "Child: [commit_descriptor $c]\n"
3381 append comment [lindex $info 5]
3383 # make anything that looks like a SHA1 ID be a clickable link
3384 appendwithlinks $comment
3386 $ctext tag delete Comments
3387 $ctext tag remove found 1.0 end
3388 $ctext conf -state disabled
3389 set commentend [$ctext index "end - 1c"]
3391 init_flist "Comments"
3392 if {$cmitmode eq "tree"} {
3394 } elseif {[llength $olds] <= 1} {
3401 proc selfirstline {} {
3406 proc sellastline {} {
3409 set l [expr {$numcommits - 1}]
3413 proc selnextline {dir} {
3415 if {![info exists selectedline]} return
3416 set l [expr {$selectedline + $dir}]
3421 proc selnextpage {dir} {
3422 global canv linespc selectedline numcommits
3424 set lpp [expr {([winfo height $canv] - 2) / $linespc}]
3428 allcanvs yview scroll [expr {$dir * $lpp}] units
3429 if {![info exists selectedline]} return
3430 set l [expr {$selectedline + $dir * $lpp}]
3433 } elseif {$l >= $numcommits} {
3434 set l [expr $numcommits - 1]
3440 proc unselectline {} {
3441 global selectedline currentid
3443 catch {unset selectedline}
3444 catch {unset currentid}
3445 allcanvs delete secsel
3448 proc reselectline {} {
3451 if {[info exists selectedline]} {
3452 selectline $selectedline 0
3456 proc addtohistory {cmd} {
3457 global history historyindex curview
3459 set elt [list $curview $cmd]
3460 if {$historyindex > 0
3461 && [lindex $history [expr {$historyindex - 1}]] == $elt} {
3465 if {$historyindex < [llength $history]} {
3466 set history [lreplace $history $historyindex end $elt]
3468 lappend history $elt
3471 if {$historyindex > 1} {
3472 .ctop.top.bar.leftbut conf -state normal
3474 .ctop.top.bar.leftbut conf -state disabled
3476 .ctop.top.bar.rightbut conf -state disabled
3482 set view [lindex $elt 0]
3483 set cmd [lindex $elt 1]
3484 if {$curview != $view} {
3491 global history historyindex
3493 if {$historyindex > 1} {
3494 incr historyindex -1
3495 godo [lindex $history [expr {$historyindex - 1}]]
3496 .ctop.top.bar.rightbut conf -state normal
3498 if {$historyindex <= 1} {
3499 .ctop.top.bar.leftbut conf -state disabled
3504 global history historyindex
3506 if {$historyindex < [llength $history]} {
3507 set cmd [lindex $history $historyindex]
3510 .ctop.top.bar.leftbut conf -state normal
3512 if {$historyindex >= [llength $history]} {
3513 .ctop.top.bar.rightbut conf -state disabled
3518 global treefilelist treeidlist diffids diffmergeid treepending
3521 catch {unset diffmergeid}
3522 if {![info exists treefilelist($id)]} {
3523 if {![info exists treepending]} {
3524 if {[catch {set gtf [open [concat | git-ls-tree -r $id] r]}]} {
3528 set treefilelist($id) {}
3529 set treeidlist($id) {}
3530 fconfigure $gtf -blocking 0
3531 fileevent $gtf readable [list gettreeline $gtf $id]
3538 proc gettreeline {gtf id} {
3539 global treefilelist treeidlist treepending cmitmode diffids
3541 while {[gets $gtf line] >= 0} {
3542 if {[lindex $line 1] ne "blob"} continue
3543 set sha1 [lindex $line 2]
3544 set fname [lindex $line 3]
3545 lappend treefilelist($id) $fname
3546 lappend treeidlist($id) $sha1
3548 if {![eof $gtf]} return
3551 if {$cmitmode ne "tree"} {
3552 if {![info exists diffmergeid]} {
3553 gettreediffs $diffids
3555 } elseif {$id ne $diffids} {
3563 global treefilelist treeidlist diffids
3564 global ctext commentend
3566 set i [lsearch -exact $treefilelist($diffids) $f]
3568 puts "oops, $f not in list for id $diffids"
3571 set blob [lindex $treeidlist($diffids) $i]
3572 if {[catch {set bf [open [concat | git-cat-file blob $blob] r]} err]} {
3573 puts "oops, error reading blob $blob: $err"
3576 fconfigure $bf -blocking 0
3577 fileevent $bf readable [list getblobline $bf $diffids]
3578 $ctext config -state normal
3579 $ctext delete $commentend end
3580 $ctext insert end "\n"
3581 $ctext insert end "$f\n" filesep
3582 $ctext config -state disabled
3583 $ctext yview $commentend
3586 proc getblobline {bf id} {
3587 global diffids cmitmode ctext
3589 if {$id ne $diffids || $cmitmode ne "tree"} {
3593 $ctext config -state normal
3594 while {[gets $bf line] >= 0} {
3595 $ctext insert end "$line\n"
3598 # delete last newline
3599 $ctext delete "end - 2c" "end - 1c"
3602 $ctext config -state disabled
3605 proc mergediff {id l} {
3606 global diffmergeid diffopts mdifffd
3612 # this doesn't seem to actually affect anything...
3613 set env(GIT_DIFF_OPTS) $diffopts
3614 set cmd [concat | git-diff-tree --no-commit-id --cc $id]
3615 if {[catch {set mdf [open $cmd r]} err]} {
3616 error_popup "Error getting merge diffs: $err"
3619 fconfigure $mdf -blocking 0
3620 set mdifffd($id) $mdf
3621 set np [llength [lindex $parentlist $l]]
3622 fileevent $mdf readable [list getmergediffline $mdf $id $np]
3623 set nextupdate [expr {[clock clicks -milliseconds] + 100}]
3626 proc getmergediffline {mdf id np} {
3627 global diffmergeid ctext cflist nextupdate mergemax
3628 global difffilestart mdifffd
3630 set n [gets $mdf line]
3637 if {![info exists diffmergeid] || $id != $diffmergeid
3638 || $mdf != $mdifffd($id)} {
3641 $ctext conf -state normal
3642 if {[regexp {^diff --cc (.*)} $line match fname]} {
3643 # start of a new file
3644 $ctext insert end "\n"
3645 set here [$ctext index "end - 1c"]
3646 lappend difffilestart $here
3647 add_flist [list $fname]
3648 set l [expr {(78 - [string length $fname]) / 2}]
3649 set pad [string range "----------------------------------------" 1 $l]
3650 $ctext insert end "$pad $fname $pad\n" filesep
3651 } elseif {[regexp {^@@} $line]} {
3652 $ctext insert end "$line\n" hunksep
3653 } elseif {[regexp {^[0-9a-f]{40}$} $line] || [regexp {^index} $line]} {
3656 # parse the prefix - one ' ', '-' or '+' for each parent
3661 for {set j 0} {$j < $np} {incr j} {
3662 set c [string range $line $j $j]
3665 } elseif {$c == "-"} {
3667 } elseif {$c == "+"} {
3676 if {!$isbad && $minuses ne {} && $pluses eq {}} {
3677 # line doesn't appear in result, parents in $minuses have the line
3678 set num [lindex $minuses 0]
3679 } elseif {!$isbad && $pluses ne {} && $minuses eq {}} {
3680 # line appears in result, parents in $pluses don't have the line
3681 lappend tags mresult
3682 set num [lindex $spaces 0]
3685 if {$num >= $mergemax} {
3690 $ctext insert end "$line\n" $tags
3692 $ctext conf -state disabled
3693 if {[clock clicks -milliseconds] >= $nextupdate} {
3695 fileevent $mdf readable {}
3697 fileevent $mdf readable [list getmergediffline $mdf $id $np]
3701 proc startdiff {ids} {
3702 global treediffs diffids treepending diffmergeid
3705 catch {unset diffmergeid}
3706 if {![info exists treediffs($ids)]} {
3707 if {![info exists treepending]} {
3715 proc addtocflist {ids} {
3716 global treediffs cflist
3717 add_flist $treediffs($ids)
3721 proc gettreediffs {ids} {
3722 global treediff treepending
3723 set treepending $ids
3726 {set gdtf [open [concat | git-diff-tree --no-commit-id -r $ids] r]} \
3728 fconfigure $gdtf -blocking 0
3729 fileevent $gdtf readable [list gettreediffline $gdtf $ids]
3732 proc gettreediffline {gdtf ids} {
3733 global treediff treediffs treepending diffids diffmergeid
3736 set n [gets $gdtf line]
3738 if {![eof $gdtf]} return
3740 set treediffs($ids) $treediff
3742 if {$cmitmode eq "tree"} {
3744 } elseif {$ids != $diffids} {
3745 if {![info exists diffmergeid]} {
3746 gettreediffs $diffids
3753 set file [lindex $line 5]
3754 lappend treediff $file
3757 proc getblobdiffs {ids} {
3758 global diffopts blobdifffd diffids env curdifftag curtagstart
3759 global nextupdate diffinhdr treediffs
3761 set env(GIT_DIFF_OPTS) $diffopts
3762 set cmd [concat | git-diff-tree --no-commit-id -r -p -C $ids]
3763 if {[catch {set bdf [open $cmd r]} err]} {
3764 puts "error getting diffs: $err"
3768 fconfigure $bdf -blocking 0
3769 set blobdifffd($ids) $bdf
3770 set curdifftag Comments
3772 fileevent $bdf readable [list getblobdiffline $bdf $diffids]
3773 set nextupdate [expr {[clock clicks -milliseconds] + 100}]
3776 proc setinlist {var i val} {
3779 while {[llength [set $var]] < $i} {
3782 if {[llength [set $var]] == $i} {
3789 proc getblobdiffline {bdf ids} {
3790 global diffids blobdifffd ctext curdifftag curtagstart
3791 global diffnexthead diffnextnote difffilestart
3792 global nextupdate diffinhdr treediffs
3794 set n [gets $bdf line]
3798 if {$ids == $diffids && $bdf == $blobdifffd($ids)} {
3799 $ctext tag add $curdifftag $curtagstart end
3804 if {$ids != $diffids || $bdf != $blobdifffd($ids)} {
3807 $ctext conf -state normal
3808 if {[regexp {^diff --git a/(.*) b/(.*)} $line match fname newname]} {
3809 # start of a new file
3810 $ctext insert end "\n"
3811 $ctext tag add $curdifftag $curtagstart end
3812 set here [$ctext index "end - 1c"]
3813 set curtagstart $here
3815 set i [lsearch -exact $treediffs($ids) $fname]
3817 setinlist difffilestart $i $here
3819 if {$newname ne $fname} {
3820 set i [lsearch -exact $treediffs($ids) $newname]
3822 setinlist difffilestart $i $here
3825 set curdifftag "f:$fname"
3826 $ctext tag delete $curdifftag
3827 set l [expr {(78 - [string length $header]) / 2}]
3828 set pad [string range "----------------------------------------" 1 $l]
3829 $ctext insert end "$pad $header $pad\n" filesep
3831 } elseif {$diffinhdr && [string compare -length 3 $line "---"] == 0} {
3833 } elseif {$diffinhdr && [string compare -length 3 $line "+++"] == 0} {
3835 } elseif {[regexp {^@@ -([0-9]+),([0-9]+) \+([0-9]+),([0-9]+) @@(.*)} \
3836 $line match f1l f1c f2l f2c rest]} {
3837 $ctext insert end "$line\n" hunksep
3840 set x [string range $line 0 0]
3841 if {$x == "-" || $x == "+"} {
3842 set tag [expr {$x == "+"}]
3843 $ctext insert end "$line\n" d$tag
3844 } elseif {$x == " "} {
3845 $ctext insert end "$line\n"
3846 } elseif {$diffinhdr || $x == "\\"} {
3847 # e.g. "\ No newline at end of file"
3848 $ctext insert end "$line\n" filesep
3850 # Something else we don't recognize
3851 if {$curdifftag != "Comments"} {
3852 $ctext insert end "\n"
3853 $ctext tag add $curdifftag $curtagstart end
3854 set curtagstart [$ctext index "end - 1c"]
3855 set curdifftag Comments
3857 $ctext insert end "$line\n" filesep
3860 $ctext conf -state disabled
3861 if {[clock clicks -milliseconds] >= $nextupdate} {
3863 fileevent $bdf readable {}
3865 fileevent $bdf readable "getblobdiffline $bdf {$ids}"
3870 global difffilestart ctext
3871 set here [$ctext index @0,0]
3872 foreach loc $difffilestart {
3873 if {[$ctext compare $loc > $here]} {
3880 global linespc charspc canvx0 canvy0 mainfont
3881 global xspc1 xspc2 lthickness
3883 set linespc [font metrics $mainfont -linespace]
3884 set charspc [font measure $mainfont "m"]
3885 set canvy0 [expr {int(3 + 0.5 * $linespc)}]
3886 set canvx0 [expr {int(3 + 0.5 * $linespc)}]
3887 set lthickness [expr {int($linespc / 9) + 1}]
3888 set xspc1(0) $linespc
3896 set ymax [lindex [$canv cget -scrollregion] 3]
3897 if {$ymax eq {} || $ymax == 0} return
3898 set span [$canv yview]
3901 allcanvs yview moveto [lindex $span 0]
3903 if {[info exists selectedline]} {
3904 selectline $selectedline 0
3908 proc incrfont {inc} {
3909 global mainfont textfont ctext canv phase
3910 global stopped entries
3912 set mainfont [lreplace $mainfont 1 1 [expr {[lindex $mainfont 1] + $inc}]]
3913 set textfont [lreplace $textfont 1 1 [expr {[lindex $textfont 1] + $inc}]]
3915 $ctext conf -font $textfont
3916 $ctext tag conf filesep -font [concat $textfont bold]
3917 foreach e $entries {
3918 $e conf -font $mainfont
3920 if {$phase eq "getcommits"} {
3921 $canv itemconf textitems -font $mainfont
3927 global sha1entry sha1string
3928 if {[string length $sha1string] == 40} {
3929 $sha1entry delete 0 end
3933 proc sha1change {n1 n2 op} {
3934 global sha1string currentid sha1but
3935 if {$sha1string == {}
3936 || ([info exists currentid] && $sha1string == $currentid)} {
3941 if {[$sha1but cget -state] == $state} return
3942 if {$state == "normal"} {
3943 $sha1but conf -state normal -relief raised -text "Goto: "
3945 $sha1but conf -state disabled -relief flat -text "SHA1 ID: "
3949 proc gotocommit {} {
3950 global sha1string currentid commitrow tagids headids
3951 global displayorder numcommits curview
3953 if {$sha1string == {}
3954 || ([info exists currentid] && $sha1string == $currentid)} return
3955 if {[info exists tagids($sha1string)]} {
3956 set id $tagids($sha1string)
3957 } elseif {[info exists headids($sha1string)]} {
3958 set id $headids($sha1string)
3960 set id [string tolower $sha1string]
3961 if {[regexp {^[0-9a-f]{4,39}$} $id]} {
3963 foreach i $displayorder {
3964 if {[string match $id* $i]} {
3968 if {$matches ne {}} {
3969 if {[llength $matches] > 1} {
3970 error_popup "Short SHA1 id $id is ambiguous"
3973 set id [lindex $matches 0]
3977 if {[info exists commitrow($curview,$id)]} {
3978 selectline $commitrow($curview,$id) 1
3981 if {[regexp {^[0-9a-fA-F]{4,}$} $sha1string]} {
3986 error_popup "$type $sha1string is not known"
3989 proc lineenter {x y id} {
3990 global hoverx hovery hoverid hovertimer
3991 global commitinfo canv
3993 if {![info exists commitinfo($id)] && ![getcommit $id]} return
3997 if {[info exists hovertimer]} {
3998 after cancel $hovertimer
4000 set hovertimer [after 500 linehover]
4004 proc linemotion {x y id} {
4005 global hoverx hovery hoverid hovertimer
4007 if {[info exists hoverid] && $id == $hoverid} {
4010 if {[info exists hovertimer]} {
4011 after cancel $hovertimer
4013 set hovertimer [after 500 linehover]
4017 proc lineleave {id} {
4018 global hoverid hovertimer canv
4020 if {[info exists hoverid] && $id == $hoverid} {
4022 if {[info exists hovertimer]} {
4023 after cancel $hovertimer
4031 global hoverx hovery hoverid hovertimer
4032 global canv linespc lthickness
4033 global commitinfo mainfont
4035 set text [lindex $commitinfo($hoverid) 0]
4036 set ymax [lindex [$canv cget -scrollregion] 3]
4037 if {$ymax == {}} return
4038 set yfrac [lindex [$canv yview] 0]
4039 set x [expr {$hoverx + 2 * $linespc}]
4040 set y [expr {$hovery + $yfrac * $ymax - $linespc / 2}]
4041 set x0 [expr {$x - 2 * $lthickness}]
4042 set y0 [expr {$y - 2 * $lthickness}]
4043 set x1 [expr {$x + [font measure $mainfont $text] + 2 * $lthickness}]
4044 set y1 [expr {$y + $linespc + 2 * $lthickness}]
4045 set t [$canv create rectangle $x0 $y0 $x1 $y1 \
4046 -fill \#ffff80 -outline black -width 1 -tags hover]
4048 set t [$canv create text $x $y -anchor nw -text $text -tags hover -font $mainfont]
4052 proc clickisonarrow {id y} {
4055 set ranges [rowranges $id]
4056 set thresh [expr {2 * $lthickness + 6}]
4057 set n [expr {[llength $ranges] - 1}]
4058 for {set i 1} {$i < $n} {incr i} {
4059 set row [lindex $ranges $i]
4060 if {abs([yc $row] - $y) < $thresh} {
4067 proc arrowjump {id n y} {
4070 # 1 <-> 2, 3 <-> 4, etc...
4071 set n [expr {(($n - 1) ^ 1) + 1}]
4072 set row [lindex [rowranges $id] $n]
4074 set ymax [lindex [$canv cget -scrollregion] 3]
4075 if {$ymax eq {} || $ymax <= 0} return
4076 set view [$canv yview]
4077 set yspan [expr {[lindex $view 1] - [lindex $view 0]}]
4078 set yfrac [expr {$yt / $ymax - $yspan / 2}]
4082 allcanvs yview moveto $yfrac
4085 proc lineclick {x y id isnew} {
4086 global ctext commitinfo children canv thickerline curview
4088 if {![info exists commitinfo($id)] && ![getcommit $id]} return
4093 # draw this line thicker than normal
4097 set ymax [lindex [$canv cget -scrollregion] 3]
4098 if {$ymax eq {}} return
4099 set yfrac [lindex [$canv yview] 0]
4100 set y [expr {$y + $yfrac * $ymax}]
4102 set dirn [clickisonarrow $id $y]
4104 arrowjump $id $dirn $y
4109 addtohistory [list lineclick $x $y $id 0]
4111 # fill the details pane with info about this line
4112 $ctext conf -state normal
4113 $ctext delete 0.0 end
4114 $ctext tag conf link -foreground blue -underline 1
4115 $ctext tag bind link <Enter> { %W configure -cursor hand2 }
4116 $ctext tag bind link <Leave> { %W configure -cursor $curtextcursor }
4117 $ctext insert end "Parent:\t"
4118 $ctext insert end $id [list link link0]
4119 $ctext tag bind link0 <1> [list selbyid $id]
4120 set info $commitinfo($id)
4121 $ctext insert end "\n\t[lindex $info 0]\n"
4122 $ctext insert end "\tAuthor:\t[lindex $info 1]\n"
4123 set date [formatdate [lindex $info 2]]
4124 $ctext insert end "\tDate:\t$date\n"
4125 set kids $children($curview,$id)
4127 $ctext insert end "\nChildren:"
4129 foreach child $kids {
4131 if {![info exists commitinfo($child)] && ![getcommit $child]} continue
4132 set info $commitinfo($child)
4133 $ctext insert end "\n\t"
4134 $ctext insert end $child [list link link$i]
4135 $ctext tag bind link$i <1> [list selbyid $child]
4136 $ctext insert end "\n\t[lindex $info 0]"
4137 $ctext insert end "\n\tAuthor:\t[lindex $info 1]"
4138 set date [formatdate [lindex $info 2]]
4139 $ctext insert end "\n\tDate:\t$date\n"
4142 $ctext conf -state disabled
4146 proc normalline {} {
4148 if {[info exists thickerline]} {
4156 global commitrow curview
4157 if {[info exists commitrow($curview,$id)]} {
4158 selectline $commitrow($curview,$id) 1
4164 if {![info exists startmstime]} {
4165 set startmstime [clock clicks -milliseconds]
4167 return [format "%.3f" [expr {([clock click -milliseconds] - $startmstime) / 1000.0}]]
4170 proc rowmenu {x y id} {
4171 global rowctxmenu commitrow selectedline rowmenuid curview
4173 if {![info exists selectedline]
4174 || $commitrow($curview,$id) eq $selectedline} {
4179 $rowctxmenu entryconfigure 0 -state $state
4180 $rowctxmenu entryconfigure 1 -state $state
4181 $rowctxmenu entryconfigure 2 -state $state
4183 tk_popup $rowctxmenu $x $y
4186 proc diffvssel {dirn} {
4187 global rowmenuid selectedline displayorder
4189 if {![info exists selectedline]} return
4191 set oldid [lindex $displayorder $selectedline]
4192 set newid $rowmenuid
4194 set oldid $rowmenuid
4195 set newid [lindex $displayorder $selectedline]
4197 addtohistory [list doseldiff $oldid $newid]
4198 doseldiff $oldid $newid
4201 proc doseldiff {oldid newid} {
4205 $ctext conf -state normal
4206 $ctext delete 0.0 end
4208 $ctext insert end "From "
4209 $ctext tag conf link -foreground blue -underline 1
4210 $ctext tag bind link <Enter> { %W configure -cursor hand2 }
4211 $ctext tag bind link <Leave> { %W configure -cursor $curtextcursor }
4212 $ctext tag bind link0 <1> [list selbyid $oldid]
4213 $ctext insert end $oldid [list link link0]
4214 $ctext insert end "\n "
4215 $ctext insert end [lindex $commitinfo($oldid) 0]
4216 $ctext insert end "\n\nTo "
4217 $ctext tag bind link1 <1> [list selbyid $newid]
4218 $ctext insert end $newid [list link link1]
4219 $ctext insert end "\n "
4220 $ctext insert end [lindex $commitinfo($newid) 0]
4221 $ctext insert end "\n"
4222 $ctext conf -state disabled
4223 $ctext tag delete Comments
4224 $ctext tag remove found 1.0 end
4225 startdiff [list $oldid $newid]
4229 global rowmenuid currentid commitinfo patchtop patchnum
4231 if {![info exists currentid]} return
4232 set oldid $currentid
4233 set oldhead [lindex $commitinfo($oldid) 0]
4234 set newid $rowmenuid
4235 set newhead [lindex $commitinfo($newid) 0]
4238 catch {destroy $top}
4240 label $top.title -text "Generate patch"
4241 grid $top.title - -pady 10
4242 label $top.from -text "From:"
4243 entry $top.fromsha1 -width 40 -relief flat
4244 $top.fromsha1 insert 0 $oldid
4245 $top.fromsha1 conf -state readonly
4246 grid $top.from $top.fromsha1 -sticky w
4247 entry $top.fromhead -width 60 -relief flat
4248 $top.fromhead insert 0 $oldhead
4249 $top.fromhead conf -state readonly
4250 grid x $top.fromhead -sticky w
4251 label $top.to -text "To:"
4252 entry $top.tosha1 -width 40 -relief flat
4253 $top.tosha1 insert 0 $newid
4254 $top.tosha1 conf -state readonly
4255 grid $top.to $top.tosha1 -sticky w
4256 entry $top.tohead -width 60 -relief flat
4257 $top.tohead insert 0 $newhead
4258 $top.tohead conf -state readonly
4259 grid x $top.tohead -sticky w
4260 button $top.rev -text "Reverse" -command mkpatchrev -padx 5
4261 grid $top.rev x -pady 10
4262 label $top.flab -text "Output file:"
4263 entry $top.fname -width 60
4264 $top.fname insert 0 [file normalize "patch$patchnum.patch"]
4266 grid $top.flab $top.fname -sticky w
4268 button $top.buts.gen -text "Generate" -command mkpatchgo
4269 button $top.buts.can -text "Cancel" -command mkpatchcan
4270 grid $top.buts.gen $top.buts.can
4271 grid columnconfigure $top.buts 0 -weight 1 -uniform a
4272 grid columnconfigure $top.buts 1 -weight 1 -uniform a
4273 grid $top.buts - -pady 10 -sticky ew
4277 proc mkpatchrev {} {
4280 set oldid [$patchtop.fromsha1 get]
4281 set oldhead [$patchtop.fromhead get]
4282 set newid [$patchtop.tosha1 get]
4283 set newhead [$patchtop.tohead get]
4284 foreach e [list fromsha1 fromhead tosha1 tohead] \
4285 v [list $newid $newhead $oldid $oldhead] {
4286 $patchtop.$e conf -state normal
4287 $patchtop.$e delete 0 end
4288 $patchtop.$e insert 0 $v
4289 $patchtop.$e conf -state readonly
4296 set oldid [$patchtop.fromsha1 get]
4297 set newid [$patchtop.tosha1 get]
4298 set fname [$patchtop.fname get]
4299 if {[catch {exec git-diff-tree -p $oldid $newid >$fname &} err]} {
4300 error_popup "Error creating patch: $err"
4302 catch {destroy $patchtop}
4306 proc mkpatchcan {} {
4309 catch {destroy $patchtop}
4314 global rowmenuid mktagtop commitinfo
4318 catch {destroy $top}
4320 label $top.title -text "Create tag"
4321 grid $top.title - -pady 10
4322 label $top.id -text "ID:"
4323 entry $top.sha1 -width 40 -relief flat
4324 $top.sha1 insert 0 $rowmenuid
4325 $top.sha1 conf -state readonly
4326 grid $top.id $top.sha1 -sticky w
4327 entry $top.head -width 60 -relief flat
4328 $top.head insert 0 [lindex $commitinfo($rowmenuid) 0]
4329 $top.head conf -state readonly
4330 grid x $top.head -sticky w
4331 label $top.tlab -text "Tag name:"
4332 entry $top.tag -width 60
4333 grid $top.tlab $top.tag -sticky w
4335 button $top.buts.gen -text "Create" -command mktaggo
4336 button $top.buts.can -text "Cancel" -command mktagcan
4337 grid $top.buts.gen $top.buts.can
4338 grid columnconfigure $top.buts 0 -weight 1 -uniform a
4339 grid columnconfigure $top.buts 1 -weight 1 -uniform a
4340 grid $top.buts - -pady 10 -sticky ew
4345 global mktagtop env tagids idtags
4347 set id [$mktagtop.sha1 get]
4348 set tag [$mktagtop.tag get]
4350 error_popup "No tag name specified"
4353 if {[info exists tagids($tag)]} {
4354 error_popup "Tag \"$tag\" already exists"
4359 set fname [file join $dir "refs/tags" $tag]
4360 set f [open $fname w]
4364 error_popup "Error creating tag: $err"
4368 set tagids($tag) $id
4369 lappend idtags($id) $tag
4373 proc redrawtags {id} {
4374 global canv linehtag commitrow idpos selectedline curview
4376 if {![info exists commitrow($curview,$id)]} return
4377 drawcmitrow $commitrow($curview,$id)
4378 $canv delete tag.$id
4379 set xt [eval drawtags $id $idpos($id)]
4380 $canv coords $linehtag($commitrow($curview,$id)) $xt [lindex $idpos($id) 2]
4381 if {[info exists selectedline]
4382 && $selectedline == $commitrow($curview,$id)} {
4383 selectline $selectedline 0
4390 catch {destroy $mktagtop}
4399 proc writecommit {} {
4400 global rowmenuid wrcomtop commitinfo wrcomcmd
4402 set top .writecommit
4404 catch {destroy $top}
4406 label $top.title -text "Write commit to file"
4407 grid $top.title - -pady 10
4408 label $top.id -text "ID:"
4409 entry $top.sha1 -width 40 -relief flat
4410 $top.sha1 insert 0 $rowmenuid
4411 $top.sha1 conf -state readonly
4412 grid $top.id $top.sha1 -sticky w
4413 entry $top.head -width 60 -relief flat
4414 $top.head insert 0 [lindex $commitinfo($rowmenuid) 0]
4415 $top.head conf -state readonly
4416 grid x $top.head -sticky w
4417 label $top.clab -text "Command:"
4418 entry $top.cmd -width 60 -textvariable wrcomcmd
4419 grid $top.clab $top.cmd -sticky w -pady 10
4420 label $top.flab -text "Output file:"
4421 entry $top.fname -width 60
4422 $top.fname insert 0 [file normalize "commit-[string range $rowmenuid 0 6]"]
4423 grid $top.flab $top.fname -sticky w
4425 button $top.buts.gen -text "Write" -command wrcomgo
4426 button $top.buts.can -text "Cancel" -command wrcomcan
4427 grid $top.buts.gen $top.buts.can
4428 grid columnconfigure $top.buts 0 -weight 1 -uniform a
4429 grid columnconfigure $top.buts 1 -weight 1 -uniform a
4430 grid $top.buts - -pady 10 -sticky ew
4437 set id [$wrcomtop.sha1 get]
4438 set cmd "echo $id | [$wrcomtop.cmd get]"
4439 set fname [$wrcomtop.fname get]
4440 if {[catch {exec sh -c $cmd >$fname &} err]} {
4441 error_popup "Error writing commit: $err"
4443 catch {destroy $wrcomtop}
4450 catch {destroy $wrcomtop}
4454 proc listrefs {id} {
4455 global idtags idheads idotherrefs
4458 if {[info exists idtags($id)]} {
4462 if {[info exists idheads($id)]} {
4466 if {[info exists idotherrefs($id)]} {
4467 set z $idotherrefs($id)
4469 return [list $x $y $z]
4472 proc rereadrefs {} {
4473 global idtags idheads idotherrefs
4475 set refids [concat [array names idtags] \
4476 [array names idheads] [array names idotherrefs]]
4477 foreach id $refids {
4478 if {![info exists ref($id)]} {
4479 set ref($id) [listrefs $id]
4483 set refids [lsort -unique [concat $refids [array names idtags] \
4484 [array names idheads] [array names idotherrefs]]]
4485 foreach id $refids {
4486 set v [listrefs $id]
4487 if {![info exists ref($id)] || $ref($id) != $v} {
4493 proc showtag {tag isnew} {
4494 global ctext tagcontents tagids linknum
4497 addtohistory [list showtag $tag 0]
4499 $ctext conf -state normal
4500 $ctext delete 0.0 end
4502 if {[info exists tagcontents($tag)]} {
4503 set text $tagcontents($tag)
4505 set text "Tag: $tag\nId: $tagids($tag)"
4507 appendwithlinks $text
4508 $ctext conf -state disabled
4519 global maxwidth maxgraphpct diffopts findmergefiles
4520 global oldprefs prefstop
4524 if {[winfo exists $top]} {
4528 foreach v {maxwidth maxgraphpct diffopts findmergefiles} {
4529 set oldprefs($v) [set $v]
4532 wm title $top "Gitk preferences"
4533 label $top.ldisp -text "Commit list display options"
4534 grid $top.ldisp - -sticky w -pady 10
4535 label $top.spacer -text " "
4536 label $top.maxwidthl -text "Maximum graph width (lines)" \
4538 spinbox $top.maxwidth -from 0 -to 100 -width 4 -textvariable maxwidth
4539 grid $top.spacer $top.maxwidthl $top.maxwidth -sticky w
4540 label $top.maxpctl -text "Maximum graph width (% of pane)" \
4542 spinbox $top.maxpct -from 1 -to 100 -width 4 -textvariable maxgraphpct
4543 grid x $top.maxpctl $top.maxpct -sticky w
4544 checkbutton $top.findm -variable findmergefiles
4545 label $top.findml -text "Include merges for \"Find\" in \"Files\"" \
4547 grid $top.findm $top.findml - -sticky w
4548 label $top.ddisp -text "Diff display options"
4549 grid $top.ddisp - -sticky w -pady 10
4550 label $top.diffoptl -text "Options for diff program" \
4552 entry $top.diffopt -width 20 -textvariable diffopts
4553 grid x $top.diffoptl $top.diffopt -sticky w
4555 button $top.buts.ok -text "OK" -command prefsok
4556 button $top.buts.can -text "Cancel" -command prefscan
4557 grid $top.buts.ok $top.buts.can
4558 grid columnconfigure $top.buts 0 -weight 1 -uniform a
4559 grid columnconfigure $top.buts 1 -weight 1 -uniform a
4560 grid $top.buts - - -pady 10 -sticky ew
4564 global maxwidth maxgraphpct diffopts findmergefiles
4565 global oldprefs prefstop
4567 foreach v {maxwidth maxgraphpct diffopts findmergefiles} {
4568 set $v $oldprefs($v)
4570 catch {destroy $prefstop}
4575 global maxwidth maxgraphpct
4576 global oldprefs prefstop
4578 catch {destroy $prefstop}
4580 if {$maxwidth != $oldprefs(maxwidth)
4581 || $maxgraphpct != $oldprefs(maxgraphpct)} {
4586 proc formatdate {d} {
4587 return [clock format $d -format "%Y-%m-%d %H:%M:%S"]
4590 # This list of encoding names and aliases is distilled from
4591 # http://www.iana.org/assignments/character-sets.
4592 # Not all of them are supported by Tcl.
4593 set encoding_aliases {
4594 { ANSI_X3.4-1968 iso-ir-6 ANSI_X3.4-1986 ISO_646.irv:1991 ASCII
4595 ISO646-US US-ASCII us IBM367 cp367 csASCII }
4596 { ISO-10646-UTF-1 csISO10646UTF1 }
4597 { ISO_646.basic:1983 ref csISO646basic1983 }
4598 { INVARIANT csINVARIANT }
4599 { ISO_646.irv:1983 iso-ir-2 irv csISO2IntlRefVersion }
4600 { BS_4730 iso-ir-4 ISO646-GB gb uk csISO4UnitedKingdom }
4601 { NATS-SEFI iso-ir-8-1 csNATSSEFI }
4602 { NATS-SEFI-ADD iso-ir-8-2 csNATSSEFIADD }
4603 { NATS-DANO iso-ir-9-1 csNATSDANO }
4604 { NATS-DANO-ADD iso-ir-9-2 csNATSDANOADD }
4605 { SEN_850200_B iso-ir-10 FI ISO646-FI ISO646-SE se csISO10Swedish }
4606 { SEN_850200_C iso-ir-11 ISO646-SE2 se2 csISO11SwedishForNames }
4607 { KS_C_5601-1987 iso-ir-149 KS_C_5601-1989 KSC_5601 korean csKSC56011987 }
4608 { ISO-2022-KR csISO2022KR }
4610 { ISO-2022-JP csISO2022JP }
4611 { ISO-2022-JP-2 csISO2022JP2 }
4612 { JIS_C6220-1969-jp JIS_C6220-1969 iso-ir-13 katakana x0201-7
4614 { JIS_C6220-1969-ro iso-ir-14 jp ISO646-JP csISO14JISC6220ro }
4615 { IT iso-ir-15 ISO646-IT csISO15Italian }
4616 { PT iso-ir-16 ISO646-PT csISO16Portuguese }
4617 { ES iso-ir-17 ISO646-ES csISO17Spanish }
4618 { greek7-old iso-ir-18 csISO18Greek7Old }
4619 { latin-greek iso-ir-19 csISO19LatinGreek }
4620 { DIN_66003 iso-ir-21 de ISO646-DE csISO21German }
4621 { NF_Z_62-010_(1973) iso-ir-25 ISO646-FR1 csISO25French }
4622 { Latin-greek-1 iso-ir-27 csISO27LatinGreek1 }
4623 { ISO_5427 iso-ir-37 csISO5427Cyrillic }
4624 { JIS_C6226-1978 iso-ir-42 csISO42JISC62261978 }
4625 { BS_viewdata iso-ir-47 csISO47BSViewdata }
4626 { INIS iso-ir-49 csISO49INIS }
4627 { INIS-8 iso-ir-50 csISO50INIS8 }
4628 { INIS-cyrillic iso-ir-51 csISO51INISCyrillic }
4629 { ISO_5427:1981 iso-ir-54 ISO5427Cyrillic1981 }
4630 { ISO_5428:1980 iso-ir-55 csISO5428Greek }
4631 { GB_1988-80 iso-ir-57 cn ISO646-CN csISO57GB1988 }
4632 { GB_2312-80 iso-ir-58 chinese csISO58GB231280 }
4633 { NS_4551-1 iso-ir-60 ISO646-NO no csISO60DanishNorwegian
4635 { NS_4551-2 ISO646-NO2 iso-ir-61 no2 csISO61Norwegian2 }
4636 { NF_Z_62-010 iso-ir-69 ISO646-FR fr csISO69French }
4637 { videotex-suppl iso-ir-70 csISO70VideotexSupp1 }
4638 { PT2 iso-ir-84 ISO646-PT2 csISO84Portuguese2 }
4639 { ES2 iso-ir-85 ISO646-ES2 csISO85Spanish2 }
4640 { MSZ_7795.3 iso-ir-86 ISO646-HU hu csISO86Hungarian }
4641 { JIS_C6226-1983 iso-ir-87 x0208 JIS_X0208-1983 csISO87JISX0208 }
4642 { greek7 iso-ir-88 csISO88Greek7 }
4643 { ASMO_449 ISO_9036 arabic7 iso-ir-89 csISO89ASMO449 }
4644 { iso-ir-90 csISO90 }
4645 { JIS_C6229-1984-a iso-ir-91 jp-ocr-a csISO91JISC62291984a }
4646 { JIS_C6229-1984-b iso-ir-92 ISO646-JP-OCR-B jp-ocr-b
4647 csISO92JISC62991984b }
4648 { JIS_C6229-1984-b-add iso-ir-93 jp-ocr-b-add csISO93JIS62291984badd }
4649 { JIS_C6229-1984-hand iso-ir-94 jp-ocr-hand csISO94JIS62291984hand }
4650 { JIS_C6229-1984-hand-add iso-ir-95 jp-ocr-hand-add
4651 csISO95JIS62291984handadd }
4652 { JIS_C6229-1984-kana iso-ir-96 csISO96JISC62291984kana }
4653 { ISO_2033-1983 iso-ir-98 e13b csISO2033 }
4654 { ANSI_X3.110-1983 iso-ir-99 CSA_T500-1983 NAPLPS csISO99NAPLPS }
4655 { ISO_8859-1:1987 iso-ir-100 ISO_8859-1 ISO-8859-1 latin1 l1 IBM819
4657 { ISO_8859-2:1987 iso-ir-101 ISO_8859-2 ISO-8859-2 latin2 l2 csISOLatin2 }
4658 { T.61-7bit iso-ir-102 csISO102T617bit }
4659 { T.61-8bit T.61 iso-ir-103 csISO103T618bit }
4660 { ISO_8859-3:1988 iso-ir-109 ISO_8859-3 ISO-8859-3 latin3 l3 csISOLatin3 }
4661 { ISO_8859-4:1988 iso-ir-110 ISO_8859-4 ISO-8859-4 latin4 l4 csISOLatin4 }
4662 { ECMA-cyrillic iso-ir-111 KOI8-E csISO111ECMACyrillic }
4663 { CSA_Z243.4-1985-1 iso-ir-121 ISO646-CA csa7-1 ca csISO121Canadian1 }
4664 { CSA_Z243.4-1985-2 iso-ir-122 ISO646-CA2 csa7-2 csISO122Canadian2 }
4665 { CSA_Z243.4-1985-gr iso-ir-123 csISO123CSAZ24341985gr }
4666 { ISO_8859-6:1987 iso-ir-127 ISO_8859-6 ISO-8859-6 ECMA-114 ASMO-708
4667 arabic csISOLatinArabic }
4668 { ISO_8859-6-E csISO88596E ISO-8859-6-E }
4669 { ISO_8859-6-I csISO88596I ISO-8859-6-I }
4670 { ISO_8859-7:1987 iso-ir-126 ISO_8859-7 ISO-8859-7 ELOT_928 ECMA-118
4671 greek greek8 csISOLatinGreek }
4672 { T.101-G2 iso-ir-128 csISO128T101G2 }
4673 { ISO_8859-8:1988 iso-ir-138 ISO_8859-8 ISO-8859-8 hebrew
4675 { ISO_8859-8-E csISO88598E ISO-8859-8-E }
4676 { ISO_8859-8-I csISO88598I ISO-8859-8-I }
4677 { CSN_369103 iso-ir-139 csISO139CSN369103 }
4678 { JUS_I.B1.002 iso-ir-141 ISO646-YU js yu csISO141JUSIB1002 }
4679 { ISO_6937-2-add iso-ir-142 csISOTextComm }
4680 { IEC_P27-1 iso-ir-143 csISO143IECP271 }
4681 { ISO_8859-5:1988 iso-ir-144 ISO_8859-5 ISO-8859-5 cyrillic
4682 csISOLatinCyrillic }
4683 { JUS_I.B1.003-serb iso-ir-146 serbian csISO146Serbian }
4684 { JUS_I.B1.003-mac macedonian iso-ir-147 csISO147Macedonian }
4685 { ISO_8859-9:1989 iso-ir-148 ISO_8859-9 ISO-8859-9 latin5 l5 csISOLatin5 }
4686 { greek-ccitt iso-ir-150 csISO150 csISO150GreekCCITT }
4687 { NC_NC00-10:81 cuba iso-ir-151 ISO646-CU csISO151Cuba }
4688 { ISO_6937-2-25 iso-ir-152 csISO6937Add }
4689 { GOST_19768-74 ST_SEV_358-88 iso-ir-153 csISO153GOST1976874 }
4690 { ISO_8859-supp iso-ir-154 latin1-2-5 csISO8859Supp }
4691 { ISO_10367-box iso-ir-155 csISO10367Box }
4692 { ISO-8859-10 iso-ir-157 l6 ISO_8859-10:1992 csISOLatin6 latin6 }
4693 { latin-lap lap iso-ir-158 csISO158Lap }
4694 { JIS_X0212-1990 x0212 iso-ir-159 csISO159JISX02121990 }
4695 { DS_2089 DS2089 ISO646-DK dk csISO646Danish }
4698 { JIS_X0201 X0201 csHalfWidthKatakana }
4699 { KSC5636 ISO646-KR csKSC5636 }
4700 { ISO-10646-UCS-2 csUnicode }
4701 { ISO-10646-UCS-4 csUCS4 }
4702 { DEC-MCS dec csDECMCS }
4703 { hp-roman8 roman8 r8 csHPRoman8 }
4704 { macintosh mac csMacintosh }
4705 { IBM037 cp037 ebcdic-cp-us ebcdic-cp-ca ebcdic-cp-wt ebcdic-cp-nl
4707 { IBM038 EBCDIC-INT cp038 csIBM038 }
4708 { IBM273 CP273 csIBM273 }
4709 { IBM274 EBCDIC-BE CP274 csIBM274 }
4710 { IBM275 EBCDIC-BR cp275 csIBM275 }
4711 { IBM277 EBCDIC-CP-DK EBCDIC-CP-NO csIBM277 }
4712 { IBM278 CP278 ebcdic-cp-fi ebcdic-cp-se csIBM278 }
4713 { IBM280 CP280 ebcdic-cp-it csIBM280 }
4714 { IBM281 EBCDIC-JP-E cp281 csIBM281 }
4715 { IBM284 CP284 ebcdic-cp-es csIBM284 }
4716 { IBM285 CP285 ebcdic-cp-gb csIBM285 }
4717 { IBM290 cp290 EBCDIC-JP-kana csIBM290 }
4718 { IBM297 cp297 ebcdic-cp-fr csIBM297 }
4719 { IBM420 cp420 ebcdic-cp-ar1 csIBM420 }
4720 { IBM423 cp423 ebcdic-cp-gr csIBM423 }
4721 { IBM424 cp424 ebcdic-cp-he csIBM424 }
4722 { IBM437 cp437 437 csPC8CodePage437 }
4723 { IBM500 CP500 ebcdic-cp-be ebcdic-cp-ch csIBM500 }
4724 { IBM775 cp775 csPC775Baltic }
4725 { IBM850 cp850 850 csPC850Multilingual }
4726 { IBM851 cp851 851 csIBM851 }
4727 { IBM852 cp852 852 csPCp852 }
4728 { IBM855 cp855 855 csIBM855 }
4729 { IBM857 cp857 857 csIBM857 }
4730 { IBM860 cp860 860 csIBM860 }
4731 { IBM861 cp861 861 cp-is csIBM861 }
4732 { IBM862 cp862 862 csPC862LatinHebrew }
4733 { IBM863 cp863 863 csIBM863 }
4734 { IBM864 cp864 csIBM864 }
4735 { IBM865 cp865 865 csIBM865 }
4736 { IBM866 cp866 866 csIBM866 }
4737 { IBM868 CP868 cp-ar csIBM868 }
4738 { IBM869 cp869 869 cp-gr csIBM869 }
4739 { IBM870 CP870 ebcdic-cp-roece ebcdic-cp-yu csIBM870 }
4740 { IBM871 CP871 ebcdic-cp-is csIBM871 }
4741 { IBM880 cp880 EBCDIC-Cyrillic csIBM880 }
4742 { IBM891 cp891 csIBM891 }
4743 { IBM903 cp903 csIBM903 }
4744 { IBM904 cp904 904 csIBBM904 }
4745 { IBM905 CP905 ebcdic-cp-tr csIBM905 }
4746 { IBM918 CP918 ebcdic-cp-ar2 csIBM918 }
4747 { IBM1026 CP1026 csIBM1026 }
4748 { EBCDIC-AT-DE csIBMEBCDICATDE }
4749 { EBCDIC-AT-DE-A csEBCDICATDEA }
4750 { EBCDIC-CA-FR csEBCDICCAFR }
4751 { EBCDIC-DK-NO csEBCDICDKNO }
4752 { EBCDIC-DK-NO-A csEBCDICDKNOA }
4753 { EBCDIC-FI-SE csEBCDICFISE }
4754 { EBCDIC-FI-SE-A csEBCDICFISEA }
4755 { EBCDIC-FR csEBCDICFR }
4756 { EBCDIC-IT csEBCDICIT }
4757 { EBCDIC-PT csEBCDICPT }
4758 { EBCDIC-ES csEBCDICES }
4759 { EBCDIC-ES-A csEBCDICESA }
4760 { EBCDIC-ES-S csEBCDICESS }
4761 { EBCDIC-UK csEBCDICUK }
4762 { EBCDIC-US csEBCDICUS }
4763 { UNKNOWN-8BIT csUnknown8BiT }
4764 { MNEMONIC csMnemonic }
4769 { IBM00858 CCSID00858 CP00858 PC-Multilingual-850+euro }
4770 { IBM00924 CCSID00924 CP00924 ebcdic-Latin9--euro }
4771 { IBM01140 CCSID01140 CP01140 ebcdic-us-37+euro }
4772 { IBM01141 CCSID01141 CP01141 ebcdic-de-273+euro }
4773 { IBM01142 CCSID01142 CP01142 ebcdic-dk-277+euro ebcdic-no-277+euro }
4774 { IBM01143 CCSID01143 CP01143 ebcdic-fi-278+euro ebcdic-se-278+euro }
4775 { IBM01144 CCSID01144 CP01144 ebcdic-it-280+euro }
4776 { IBM01145 CCSID01145 CP01145 ebcdic-es-284+euro }
4777 { IBM01146 CCSID01146 CP01146 ebcdic-gb-285+euro }
4778 { IBM01147 CCSID01147 CP01147 ebcdic-fr-297+euro }
4779 { IBM01148 CCSID01148 CP01148 ebcdic-international-500+euro }
4780 { IBM01149 CCSID01149 CP01149 ebcdic-is-871+euro }
4781 { IBM1047 IBM-1047 }
4782 { PTCP154 csPTCP154 PT154 CP154 Cyrillic-Asian }
4783 { Amiga-1251 Ami1251 Amiga1251 Ami-1251 }
4784 { UNICODE-1-1 csUnicode11 }
4787 { UNICODE-1-1-UTF-7 csUnicode11UTF7 }
4788 { ISO-8859-14 iso-ir-199 ISO_8859-14:1998 ISO_8859-14 latin8 iso-celtic
4790 { ISO-8859-15 ISO_8859-15 Latin-9 }
4791 { ISO-8859-16 iso-ir-226 ISO_8859-16:2001 ISO_8859-16 latin10 l10 }
4792 { GBK CP936 MS936 windows-936 }
4793 { JIS_Encoding csJISEncoding }
4794 { Shift_JIS MS_Kanji csShiftJIS }
4795 { Extended_UNIX_Code_Packed_Format_for_Japanese csEUCPkdFmtJapanese
4797 { Extended_UNIX_Code_Fixed_Width_for_Japanese csEUCFixWidJapanese }
4798 { ISO-10646-UCS-Basic csUnicodeASCII }
4799 { ISO-10646-Unicode-Latin1 csUnicodeLatin1 ISO-10646 }
4800 { ISO-Unicode-IBM-1261 csUnicodeIBM1261 }
4801 { ISO-Unicode-IBM-1268 csUnicodeIBM1268 }
4802 { ISO-Unicode-IBM-1276 csUnicodeIBM1276 }
4803 { ISO-Unicode-IBM-1264 csUnicodeIBM1264 }
4804 { ISO-Unicode-IBM-1265 csUnicodeIBM1265 }
4805 { ISO-8859-1-Windows-3.0-Latin-1 csWindows30Latin1 }
4806 { ISO-8859-1-Windows-3.1-Latin-1 csWindows31Latin1 }
4807 { ISO-8859-2-Windows-Latin-2 csWindows31Latin2 }
4808 { ISO-8859-9-Windows-Latin-5 csWindows31Latin5 }
4809 { Adobe-Standard-Encoding csAdobeStandardEncoding }
4810 { Ventura-US csVenturaUS }
4811 { Ventura-International csVenturaInternational }
4812 { PC8-Danish-Norwegian csPC8DanishNorwegian }
4813 { PC8-Turkish csPC8Turkish }
4814 { IBM-Symbols csIBMSymbols }
4815 { IBM-Thai csIBMThai }
4816 { HP-Legal csHPLegal }
4817 { HP-Pi-font csHPPiFont }
4818 { HP-Math8 csHPMath8 }
4819 { Adobe-Symbol-Encoding csHPPSMath }
4820 { HP-DeskTop csHPDesktop }
4821 { Ventura-Math csVenturaMath }
4822 { Microsoft-Publishing csMicrosoftPublishing }
4823 { Windows-31J csWindows31J }
4828 proc tcl_encoding {enc} {
4829 global encoding_aliases
4830 set names [encoding names]
4831 set lcnames [string tolower $names]
4832 set enc [string tolower $enc]
4833 set i [lsearch -exact $lcnames $enc]
4835 # look for "isonnn" instead of "iso-nnn" or "iso_nnn"
4836 if {[regsub {^iso[-_]} $enc iso encx]} {
4837 set i [lsearch -exact $lcnames $encx]
4841 foreach l $encoding_aliases {
4842 set ll [string tolower $l]
4843 if {[lsearch -exact $ll $enc] < 0} continue
4844 # look through the aliases for one that tcl knows about
4846 set i [lsearch -exact $lcnames $e]
4848 if {[regsub {^iso[-_]} $e iso ex]} {
4849 set i [lsearch -exact $lcnames $ex]
4858 return [lindex $names $i]
4865 set diffopts "-U 5 -p"
4866 set wrcomcmd "git-diff-tree --stdin -p --pretty"
4870 set gitencoding [exec git-repo-config --get i18n.commitencoding]
4872 if {$gitencoding == ""} {
4873 set gitencoding "utf-8"
4875 set tclencoding [tcl_encoding $gitencoding]
4876 if {$tclencoding == {}} {
4877 puts stderr "Warning: encoding $gitencoding is not supported by Tcl/Tk"
4880 set mainfont {Helvetica 9}
4881 set textfont {Courier 9}
4882 set uifont {Helvetica 9 bold}
4883 set findmergefiles 0
4891 set flistmode "flat"
4892 set cmitmode "patch"
4894 set colors {green red blue magenta darkgrey brown orange}
4896 catch {source ~/.gitk}
4898 font create optionfont -family sans-serif -size -12
4902 switch -regexp -- $arg {
4904 "^-d" { set datemode 1 }
4906 lappend revtreeargs $arg
4911 # check that we can find a .git directory somewhere...
4913 if {![file isdirectory $gitdir]} {
4914 show_error . "Cannot find the git directory \"$gitdir\"."
4918 set cmdline_files {}
4919 set i [lsearch -exact $revtreeargs "--"]
4921 set cmdline_files [lrange $revtreeargs [expr {$i + 1}] end]
4922 set revtreeargs [lrange $revtreeargs 0 [expr {$i - 1}]]
4923 } elseif {$revtreeargs ne {}} {
4925 set f [eval exec git-rev-parse --no-revs --no-flags $revtreeargs]
4926 set cmdline_files [split $f "\n"]
4927 set n [llength $cmdline_files]
4928 set revtreeargs [lrange $revtreeargs 0 end-$n]
4930 # unfortunately we get both stdout and stderr in $err,
4931 # so look for "fatal:".
4932 set i [string first "fatal:" $err]
4934 set err [string range [expr {$i + 6}] end]
4936 show_error . "Bad arguments to gitk:\n$err"
4949 set selectedhlview {}
4962 if {$cmdline_files ne {} || $revtreeargs ne {}} {
4963 # create a view for the files/dirs specified on the command line
4967 set viewname(1) "Command line"
4968 set viewfiles(1) $cmdline_files
4969 set viewargs(1) $revtreeargs
4972 .bar.view entryconf 2 -state normal
4973 .bar.view entryconf 3 -state normal
4976 if {[info exists permviews]} {
4977 foreach v $permviews {
4980 set viewname($n) [lindex $v 0]
4981 set viewfiles($n) [lindex $v 1]
4982 set viewargs($n) [lindex $v 2]