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 getcommits
{rargs
} {
20 global commits commfd phase canv mainfont env
21 global startmsecs nextupdate ncmupdate
22 global ctext maincursor textcursor leftover gitencoding
24 # check that we can find a .git directory somewhere...
26 if {![file isdirectory
$gitdir]} {
27 error_popup
"Cannot find the git directory \"$gitdir\"."
32 set startmsecs
[clock clicks
-milliseconds]
33 set nextupdate
[expr {$startmsecs + 100}]
36 set parse_args
[concat
--default HEAD
$rargs]
37 set parsed_args
[split [eval exec git-rev-parse
$parse_args] "\n"]
39 # if git-rev-parse failed for some reason...
43 set parsed_args
$rargs
46 set commfd
[open
"|git-rev-list --header --topo-order --parents $parsed_args" r
]
48 puts stderr
"Error executing git-rev-list: $err"
52 fconfigure
$commfd -blocking 0 -translation lf
-encoding $gitencoding
53 fileevent
$commfd readable
[list getcommitlines
$commfd]
55 $canv create text
3 3 -anchor nw
-text "Reading commits..." \
56 -font $mainfont -tags textitems
57 . config
-cursor watch
61 proc getcommitlines
{commfd
} {
62 global commits parents cdate children
63 global commitlisted phase nextupdate
64 global stopped redisplaying leftover
66 set stuff
[read $commfd]
68 if {![eof
$commfd]} return
69 # set it blocking so we wait for the process to terminate
70 fconfigure
$commfd -blocking 1
71 if {![catch
{close
$commfd} err
]} {
72 after idle finishcommits
75 if {[string range
$err 0 4] == "usage"} {
77 "Gitk: error reading commits: bad arguments to git-rev-list.\
78 (Note: arguments to gitk are passed to git-rev-list\
79 to allow selection of commits to be displayed.)"
81 set err
"Error reading commits: $err"
88 set i
[string first
"\0" $stuff $start]
90 append leftover
[string range
$stuff $start end
]
93 set cmit
[string range
$stuff $start [expr {$i - 1}]]
95 set cmit
"$leftover$cmit"
98 set start
[expr {$i + 1}]
99 set j
[string first
"\n" $cmit]
102 set ids
[string range
$cmit 0 [expr {$j - 1}]]
105 if {![regexp
{^
[0-9a-f]{40}$
} $id]} {
113 if {[string length
$shortcmit] > 80} {
114 set shortcmit
"[string range $shortcmit 0 80]..."
116 error_popup
"Can't parse git-rev-list output: {$shortcmit}"
119 set id
[lindex
$ids 0]
120 set olds
[lrange
$ids 1 end
]
121 set cmit
[string range
$cmit [expr {$j + 1}] end
]
123 set commitlisted
($id) 1
124 parsecommit
$id $cmit 1 [lrange
$ids 1 end
]
126 if {[clock clicks
-milliseconds] >= $nextupdate} {
129 while {$redisplaying} {
133 set phase
"getcommits"
134 foreach id
$commits {
137 if {[clock clicks
-milliseconds] >= $nextupdate} {
146 proc doupdate
{reading
} {
147 global commfd nextupdate numcommits ncmupdate
150 fileevent
$commfd readable
{}
153 set nextupdate
[expr {[clock clicks
-milliseconds] + 100}]
154 if {$numcommits < 100} {
155 set ncmupdate
[expr {$numcommits + 1}]
156 } elseif
{$numcommits < 10000} {
157 set ncmupdate
[expr {$numcommits + 10}]
159 set ncmupdate
[expr {$numcommits + 100}]
162 fileevent
$commfd readable
[list getcommitlines
$commfd]
166 proc readcommit
{id
} {
167 if [catch
{set contents
[exec git-cat-file commit
$id]}] return
168 parsecommit
$id $contents 0 {}
171 proc parsecommit
{id contents listed olds
} {
172 global commitinfo children nchildren parents nparents cdate ncleft
181 if {![info exists nchildren
($id)]} {
186 set parents
($id) $olds
187 set nparents
($id) [llength
$olds]
189 if {![info exists nchildren
($p)]} {
190 set children
($p) [list
$id]
193 } elseif
{[lsearch
-exact $children($p) $id] < 0} {
194 lappend children
($p) $id
199 set hdrend
[string first
"\n\n" $contents]
201 # should never happen...
202 set hdrend
[string length
$contents]
204 set header
[string range
$contents 0 [expr {$hdrend - 1}]]
205 set comment
[string range
$contents [expr {$hdrend + 2}] end
]
206 foreach line
[split $header "\n"] {
207 set tag
[lindex
$line 0]
208 if {$tag == "author"} {
209 set audate
[lindex
$line end-1
]
210 set auname
[lrange
$line 1 end-2
]
211 } elseif
{$tag == "committer"} {
212 set comdate
[lindex
$line end-1
]
213 set comname
[lrange
$line 1 end-2
]
217 # take the first line of the comment as the headline
218 set i
[string first
"\n" $comment]
220 set headline
[string trim
[string range
$comment 0 $i]]
222 set headline
$comment
225 # git-rev-list indents the comment by 4 spaces;
226 # if we got this via git-cat-file, add the indentation
228 foreach line
[split $comment "\n"] {
229 append newcomment
" "
230 append newcomment
$line
231 append newcomment
"\n"
233 set comment
$newcomment
235 if {$comdate != {}} {
236 set cdate
($id) $comdate
238 set commitinfo
($id) [list
$headline $auname $audate \
239 $comname $comdate $comment]
243 global tagids idtags headids idheads tagcontents
244 global otherrefids idotherrefs
246 set refd
[open
[list | git-ls-remote
[gitdir
]] r
]
247 while {0 <= [set n
[gets
$refd line
]]} {
248 if {![regexp
{^
([0-9a-f]{40}) refs
/([^^
]*)$
} $line \
252 if {![regexp
{^
(tags|heads
)/(.
*)$
} $path match
type name
]} {
256 if {$type == "tags"} {
257 set tagids
($name) $id
258 lappend idtags
($id) $name
263 set commit
[exec git-rev-parse
"$id^0"]
264 if {"$commit" != "$id"} {
265 set tagids
($name) $commit
266 lappend idtags
($commit) $name
270 set tagcontents
($name) [exec git-cat-file tag
"$id"]
272 } elseif
{ $type == "heads" } {
273 set headids
($name) $id
274 lappend idheads
($id) $name
276 set otherrefids
($name) $id
277 lappend idotherrefs
($id) $name
283 proc error_popup msg
{
287 message
$w.m
-text $msg -justify center
-aspect 400
288 pack
$w.m
-side top
-fill x
-padx 20 -pady 20
289 button
$w.ok
-text OK
-command "destroy $w"
290 pack
$w.ok
-side bottom
-fill x
291 bind $w <Visibility
> "grab $w; focus $w"
296 global canv canv2 canv3 linespc charspc ctext cflist textfont
297 global findtype findtypemenu findloc findstring fstring geometry
298 global entries sha1entry sha1string sha1but
299 global maincursor textcursor curtextcursor
300 global rowctxmenu mergemax
303 .bar add cascade
-label "File" -menu .bar.
file
305 .bar.
file add
command -label "Reread references" -command rereadrefs
306 .bar.
file add
command -label "Quit" -command doquit
308 .bar add cascade
-label "Edit" -menu .bar.edit
309 .bar.edit add
command -label "Preferences" -command doprefs
311 .bar add cascade
-label "Help" -menu .bar.
help
312 .bar.
help add
command -label "About gitk" -command about
313 . configure
-menu .bar
315 if {![info exists geometry
(canv1
)]} {
316 set geometry
(canv1
) [expr {45 * $charspc}]
317 set geometry
(canv2
) [expr {30 * $charspc}]
318 set geometry
(canv3
) [expr {15 * $charspc}]
319 set geometry
(canvh
) [expr {25 * $linespc + 4}]
320 set geometry
(ctextw
) 80
321 set geometry
(ctexth
) 30
322 set geometry
(cflistw
) 30
324 panedwindow .ctop
-orient vertical
325 if {[info exists geometry
(width
)]} {
326 .ctop conf
-width $geometry(width
) -height $geometry(height
)
327 set texth
[expr {$geometry(height
) - $geometry(canvh
) - 56}]
328 set geometry
(ctexth
) [expr {($texth - 8) /
329 [font metrics
$textfont -linespace]}]
333 pack .ctop.top.bar
-side bottom
-fill x
334 set cscroll .ctop.top.csb
335 scrollbar
$cscroll -command {allcanvs yview
} -highlightthickness 0
336 pack
$cscroll -side right
-fill y
337 panedwindow .ctop.top.clist
-orient horizontal
-sashpad 0 -handlesize 4
338 pack .ctop.top.clist
-side top
-fill both
-expand 1
340 set canv .ctop.top.clist.canv
341 canvas
$canv -height $geometry(canvh
) -width $geometry(canv1
) \
343 -yscrollincr $linespc -yscrollcommand "$cscroll set"
344 .ctop.top.clist add
$canv
345 set canv2 .ctop.top.clist.canv2
346 canvas
$canv2 -height $geometry(canvh
) -width $geometry(canv2
) \
347 -bg white
-bd 0 -yscrollincr $linespc
348 .ctop.top.clist add
$canv2
349 set canv3 .ctop.top.clist.canv3
350 canvas
$canv3 -height $geometry(canvh
) -width $geometry(canv3
) \
351 -bg white
-bd 0 -yscrollincr $linespc
352 .ctop.top.clist add
$canv3
353 bind .ctop.top.clist
<Configure
> {resizeclistpanes
%W
%w
}
355 set sha1entry .ctop.top.bar.sha1
356 set entries
$sha1entry
357 set sha1but .ctop.top.bar.sha1label
358 button
$sha1but -text "SHA1 ID: " -state disabled
-relief flat \
359 -command gotocommit
-width 8
360 $sha1but conf
-disabledforeground [$sha1but cget
-foreground]
361 pack .ctop.top.bar.sha1label
-side left
362 entry
$sha1entry -width 40 -font $textfont -textvariable sha1string
363 trace add variable sha1string
write sha1change
364 pack
$sha1entry -side left
-pady 2
366 image create bitmap bm-left
-data {
367 #define left_width 16
368 #define left_height 16
369 static unsigned char left_bits
[] = {
370 0x00, 0x00, 0xc0, 0x01, 0xe0, 0x00, 0x70, 0x00, 0x38, 0x00, 0x1c, 0x00,
371 0x0e, 0x00, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0x0e, 0x00, 0x1c, 0x00,
372 0x38, 0x00, 0x70, 0x00, 0xe0, 0x00, 0xc0, 0x01};
374 image create bitmap bm-right
-data {
375 #define right_width 16
376 #define right_height 16
377 static unsigned char right_bits
[] = {
378 0x00, 0x00, 0xc0, 0x01, 0x80, 0x03, 0x00, 0x07, 0x00, 0x0e, 0x00, 0x1c,
379 0x00, 0x38, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0x00, 0x38, 0x00, 0x1c,
380 0x00, 0x0e, 0x00, 0x07, 0x80, 0x03, 0xc0, 0x01};
382 button .ctop.top.bar.leftbut
-image bm-left
-command goback \
383 -state disabled
-width 26
384 pack .ctop.top.bar.leftbut
-side left
-fill y
385 button .ctop.top.bar.rightbut
-image bm-right
-command goforw \
386 -state disabled
-width 26
387 pack .ctop.top.bar.rightbut
-side left
-fill y
389 button .ctop.top.bar.findbut
-text "Find" -command dofind
390 pack .ctop.top.bar.findbut
-side left
392 set fstring .ctop.top.bar.findstring
393 lappend entries
$fstring
394 entry
$fstring -width 30 -font $textfont -textvariable findstring
395 pack
$fstring -side left
-expand 1 -fill x
397 set findtypemenu
[tk_optionMenu .ctop.top.bar.findtype \
398 findtype Exact IgnCase Regexp
]
399 set findloc
"All fields"
400 tk_optionMenu .ctop.top.bar.findloc findloc
"All fields" Headline \
401 Comments Author Committer Files Pickaxe
402 pack .ctop.top.bar.findloc
-side right
403 pack .ctop.top.bar.findtype
-side right
404 # for making sure type==Exact whenever loc==Pickaxe
405 trace add variable findloc
write findlocchange
407 panedwindow .ctop.cdet
-orient horizontal
409 frame .ctop.cdet.left
410 set ctext .ctop.cdet.left.ctext
411 text
$ctext -bg white
-state disabled
-font $textfont \
412 -width $geometry(ctextw
) -height $geometry(ctexth
) \
413 -yscrollcommand ".ctop.cdet.left.sb set" -wrap none
414 scrollbar .ctop.cdet.left.sb
-command "$ctext yview"
415 pack .ctop.cdet.left.sb
-side right
-fill y
416 pack
$ctext -side left
-fill both
-expand 1
417 .ctop.cdet add .ctop.cdet.left
419 $ctext tag conf filesep
-font [concat
$textfont bold
] -back "#aaaaaa"
420 $ctext tag conf hunksep
-fore blue
421 $ctext tag conf d0
-fore red
422 $ctext tag conf d1
-fore "#00a000"
423 $ctext tag conf m0
-fore red
424 $ctext tag conf m1
-fore blue
425 $ctext tag conf m2
-fore green
426 $ctext tag conf m3
-fore purple
427 $ctext tag conf
m4 -fore brown
428 $ctext tag conf mmax
-fore darkgrey
430 $ctext tag conf mresult
-font [concat
$textfont bold
]
431 $ctext tag conf msep
-font [concat
$textfont bold
]
432 $ctext tag conf found
-back yellow
434 frame .ctop.cdet.right
435 set cflist .ctop.cdet.right.cfiles
436 listbox
$cflist -bg white
-selectmode extended
-width $geometry(cflistw
) \
437 -yscrollcommand ".ctop.cdet.right.sb set"
438 scrollbar .ctop.cdet.right.sb
-command "$cflist yview"
439 pack .ctop.cdet.right.sb
-side right
-fill y
440 pack
$cflist -side left
-fill both
-expand 1
441 .ctop.cdet add .ctop.cdet.right
442 bind .ctop.cdet
<Configure
> {resizecdetpanes
%W
%w
}
444 pack .ctop
-side top
-fill both
-expand 1
446 bindall
<1> {selcanvline
%W
%x
%y
}
447 #bindall <B1-Motion> {selcanvline %W %x %y}
448 bindall
<ButtonRelease-4
> "allcanvs yview scroll -5 units"
449 bindall
<ButtonRelease-5
> "allcanvs yview scroll 5 units"
450 bindall
<2> "allcanvs scan mark 0 %y"
451 bindall
<B2-Motion
> "allcanvs scan dragto 0 %y"
452 bind .
<Key-Up
> "selnextline -1"
453 bind .
<Key-Down
> "selnextline 1"
454 bind .
<Key-Right
> "goforw"
455 bind .
<Key-Left
> "goback"
456 bind .
<Key-Prior
> "allcanvs yview scroll -1 pages"
457 bind .
<Key-Next
> "allcanvs yview scroll 1 pages"
458 bindkey
<Key-Delete
> "$ctext yview scroll -1 pages"
459 bindkey
<Key-BackSpace
> "$ctext yview scroll -1 pages"
460 bindkey
<Key-space
> "$ctext yview scroll 1 pages"
461 bindkey p
"selnextline -1"
462 bindkey n
"selnextline 1"
465 bindkey i
"selnextline -1"
466 bindkey k
"selnextline 1"
469 bindkey b
"$ctext yview scroll -1 pages"
470 bindkey d
"$ctext yview scroll 18 units"
471 bindkey u
"$ctext yview scroll -18 units"
472 bindkey
/ {findnext
1}
473 bindkey
<Key-Return
> {findnext
0}
476 bind .
<Control-q
> doquit
477 bind .
<Control-f
> dofind
478 bind .
<Control-g
> {findnext
0}
479 bind .
<Control-r
> findprev
480 bind .
<Control-equal
> {incrfont
1}
481 bind .
<Control-KP_Add
> {incrfont
1}
482 bind .
<Control-minus
> {incrfont
-1}
483 bind .
<Control-KP_Subtract
> {incrfont
-1}
484 bind $cflist <<ListboxSelect>> listboxsel
485 bind . <Destroy> {savestuff %W}
486 bind . <Button-1> "click %W"
487 bind $fstring <Key-Return> dofind
488 bind $sha1entry <Key-Return> gotocommit
489 bind $sha1entry <<PasteSelection>> clearsha1
491 set maincursor [. cget -cursor]
492 set textcursor [$ctext cget -cursor]
493 set curtextcursor $textcursor
495 set rowctxmenu .rowctxmenu
496 menu $rowctxmenu -tearoff 0
497 $rowctxmenu add command -label "Diff this -> selected" \
498 -command {diffvssel 0}
499 $rowctxmenu add command -label "Diff selected -> this" \
500 -command {diffvssel 1}
501 $rowctxmenu add command -label "Make patch" -command mkpatch
502 $rowctxmenu add command -label "Create tag" -command mktag
503 $rowctxmenu add command -label "Write commit to file" -command writecommit
506 # when we make a key binding for the toplevel, make sure
507 # it doesn't get triggered when that key is pressed in the
508 # find string entry widget.
509 proc bindkey {ev script} {
512 set escript [bind Entry $ev]
513 if {$escript == {}} {
514 set escript [bind Entry <Key>]
517 bind $e $ev "$escript; break"
521 # set the focus back to the toplevel for any click outside
532 global canv canv2 canv3 ctext cflist mainfont textfont
533 global stuffsaved findmergefiles maxgraphpct
536 if {$stuffsaved} return
537 if {![winfo viewable .]} return
539 set f [open "~/.gitk-new" w]
540 puts $f [list set mainfont $mainfont]
541 puts $f [list set textfont $textfont]
542 puts $f [list set findmergefiles $findmergefiles]
543 puts $f [list set maxgraphpct $maxgraphpct]
544 puts $f [list set maxwidth $maxwidth]
545 puts $f "set geometry(width) [winfo width .ctop]"
546 puts $f "set geometry(height) [winfo height .ctop]"
547 puts $f "set geometry(canv1) [expr {[winfo width $canv]-2}]"
548 puts $f "set geometry(canv2) [expr {[winfo width $canv2]-2}]"
549 puts $f "set geometry(canv3) [expr {[winfo width $canv3]-2}]"
550 puts $f "set geometry(canvh) [expr {[winfo height $canv]-2}]"
551 set wid [expr {([winfo width $ctext] - 8) \
552 / [font measure $textfont "0"]}]
553 puts $f "set geometry(ctextw) $wid"
554 set wid [expr {([winfo width $cflist] - 11) \
555 / [font measure [$cflist cget -font] "0"]}]
556 puts $f "set geometry(cflistw) $wid"
558 file rename -force "~/.gitk-new" "~/.gitk"
563 proc resizeclistpanes {win w} {
565 if [info exists oldwidth($win)] {
566 set s0 [$win sash coord 0]
567 set s1 [$win sash coord 1]
569 set sash0 [expr {int($w/2 - 2)}]
570 set sash1 [expr {int($w*5/6 - 2)}]
572 set factor [expr {1.0 * $w / $oldwidth($win)}]
573 set sash0 [expr {int($factor * [lindex $s0 0])}]
574 set sash1 [expr {int($factor * [lindex $s1 0])}]
578 if {$sash1 < $sash0 + 20} {
579 set sash1 [expr {$sash0 + 20}]
581 if {$sash1 > $w - 10} {
582 set sash1 [expr {$w - 10}]
583 if {$sash0 > $sash1 - 20} {
584 set sash0 [expr {$sash1 - 20}]
588 $win sash place 0 $sash0 [lindex $s0 1]
589 $win sash place 1 $sash1 [lindex $s1 1]
591 set oldwidth($win) $w
594 proc resizecdetpanes {win w} {
596 if [info exists oldwidth($win)] {
597 set s0 [$win sash coord 0]
599 set sash0 [expr {int($w*3/4 - 2)}]
601 set factor [expr {1.0 * $w / $oldwidth($win)}]
602 set sash0 [expr {int($factor * [lindex $s0 0])}]
606 if {$sash0 > $w - 15} {
607 set sash0 [expr {$w - 15}]
610 $win sash place 0 $sash0 [lindex $s0 1]
612 set oldwidth($win) $w
616 global canv canv2 canv3
622 proc bindall {event action} {
623 global canv canv2 canv3
624 bind $canv $event $action
625 bind $canv2 $event $action
626 bind $canv3 $event $action
631 if {[winfo exists $w]} {
636 wm title $w "About gitk"
640 Copyright © 2005 Paul Mackerras
642 Use and redistribute under the terms of the GNU General Public License} \
643 -justify center -aspect 400
644 pack $w.m -side top -fill x -padx 20 -pady 20
645 button $w.ok -text Close -command "destroy $w"
646 pack $w.ok -side bottom
649 proc assigncolor {id} {
650 global colormap commcolors colors nextcolor
651 global parents nparents children nchildren
652 global cornercrossings crossings
654 if [info exists colormap($id)] return
655 set ncolors [llength $colors]
656 if {$nparents($id) <= 1 && $nchildren($id) == 1} {
657 set child [lindex $children($id) 0]
658 if {[info exists colormap($child)]
659 && $nparents($child) == 1} {
660 set colormap($id) $colormap($child)
665 if {[info exists cornercrossings($id)]} {
666 foreach x $cornercrossings($id) {
667 if {[info exists colormap($x)]
668 && [lsearch -exact $badcolors $colormap($x)] < 0} {
669 lappend badcolors $colormap($x)
672 if {[llength $badcolors] >= $ncolors} {
676 set origbad $badcolors
677 if {[llength $badcolors] < $ncolors - 1} {
678 if {[info exists crossings($id)]} {
679 foreach x $crossings($id) {
680 if {[info exists colormap($x)]
681 && [lsearch -exact $badcolors $colormap($x)] < 0} {
682 lappend badcolors $colormap($x)
685 if {[llength $badcolors] >= $ncolors} {
686 set badcolors $origbad
689 set origbad $badcolors
691 if {[llength $badcolors] < $ncolors - 1} {
692 foreach child $children($id) {
693 if {[info exists colormap($child)]
694 && [lsearch -exact $badcolors $colormap($child)] < 0} {
695 lappend badcolors $colormap($child)
697 if {[info exists parents($child)]} {
698 foreach p $parents($child) {
699 if {[info exists colormap($p)]
700 && [lsearch -exact $badcolors $colormap($p)] < 0} {
701 lappend badcolors $colormap($p)
706 if {[llength $badcolors] >= $ncolors} {
707 set badcolors $origbad
710 for {set i 0} {$i <= $ncolors} {incr i} {
711 set c [lindex $colors $nextcolor]
712 if {[incr nextcolor] >= $ncolors} {
715 if {[lsearch -exact $badcolors $c]} break
721 global canvy canvy0 lineno numcommits nextcolor linespc
722 global mainline mainlinearrow sidelines
723 global nchildren ncleft
724 global displist nhyperspace
731 catch {unset mainline}
732 catch {unset mainlinearrow}
733 catch {unset sidelines}
734 foreach id [array names nchildren] {
735 set ncleft($id) $nchildren($id)
741 proc bindline {t id} {
744 $canv bind $t <Enter> "lineenter %x %y $id"
745 $canv bind $t <Motion> "linemotion %x %y $id"
746 $canv bind $t <Leave> "lineleave $id"
747 $canv bind $t <Button-1> "lineclick %x %y $id 1"
750 proc drawlines {id xtra delold} {
751 global mainline mainlinearrow sidelines lthickness colormap canv
754 $canv delete lines.$id
756 if {[info exists mainline($id)]} {
757 set t [$canv create line $mainline($id) \
758 -width [expr {($xtra + 1) * $lthickness}] \
759 -fill $colormap($id) -tags lines.$id \
760 -arrow $mainlinearrow($id)]
764 if {[info exists sidelines($id)]} {
765 foreach ls $sidelines($id) {
766 set coords [lindex $ls 0]
767 set thick [lindex $ls 1]
768 set arrow [lindex $ls 2]
769 set t [$canv create line $coords -fill $colormap($id) \
770 -width [expr {($thick + $xtra) * $lthickness}] \
771 -arrow $arrow -tags lines.$id]
778 # level here is an index in displist
779 proc drawcommitline {level} {
780 global parents children nparents displist
781 global canv canv2 canv3 mainfont namefont canvy linespc
782 global lineid linehtag linentag linedtag commitinfo
783 global colormap numcommits currentparents dupparents
784 global idtags idline idheads idotherrefs
785 global lineno lthickness mainline mainlinearrow sidelines
786 global commitlisted rowtextx idpos lastuse displist
787 global oldnlines olddlevel olddisplist
791 set id [lindex $displist $level]
792 set lastuse($id) $lineno
793 set lineid($lineno) $id
794 set idline($id) $lineno
795 set ofill [expr {[info exists commitlisted($id)]? "blue": "white"}]
796 if {![info exists commitinfo($id)]} {
798 if {![info exists commitinfo($id)]} {
799 set commitinfo($id) {"No commit information available"}
804 set currentparents {}
806 if {[info exists commitlisted($id)] && [info exists parents($id)]} {
807 foreach p $parents($id) {
808 if {[lsearch -exact $currentparents $p] < 0} {
809 lappend currentparents $p
811 # remember that this parent was listed twice
812 lappend dupparents $p
816 set x [xcoord $level $level $lineno]
818 set canvy [expr {$canvy + $linespc}]
819 allcanvs conf -scrollregion \
820 [list 0 0 0 [expr {$y1 + 0.5 * $linespc + 2}]]
821 if {[info exists mainline($id)]} {
822 lappend mainline($id) $x $y1
823 if {$mainlinearrow($id) ne "none"} {
824 set mainline($id) [trimdiagstart $mainline($id)]
828 set orad [expr {$linespc / 3}]
829 set t [$canv create oval [expr {$x - $orad}] [expr {$y1 - $orad}] \
830 [expr {$x + $orad - 1}] [expr {$y1 + $orad - 1}] \
831 -fill $ofill -outline black -width 1]
833 $canv bind $t <1> {selcanvline {} %x %y}
834 set xt [xcoord [llength $displist] $level $lineno]
835 if {[llength $currentparents] > 2} {
836 set xt [expr {$xt + ([llength $currentparents] - 2) * $linespc}]
838 set rowtextx($lineno) $xt
839 set idpos($id) [list $x $xt $y1]
840 if {[info exists idtags($id)] || [info exists idheads($id)]
841 || [info exists idotherrefs($id)]} {
842 set xt [drawtags $id $x $xt $y1]
844 set headline [lindex $commitinfo($id) 0]
845 set name [lindex $commitinfo($id) 1]
846 set date [lindex $commitinfo($id) 2]
847 set date [formatdate $date]
848 set linehtag($lineno) [$canv create text $xt $y1 -anchor w \
849 -text $headline -font $mainfont ]
850 $canv bind $linehtag($lineno) <Button-3> "rowmenu %X %Y $id"
851 set linentag($lineno) [$canv2 create text 3 $y1 -anchor w \
852 -text $name -font $namefont]
853 set linedtag($lineno) [$canv3 create text 3 $y1 -anchor w \
854 -text $date -font $mainfont]
857 set olddisplist $displist
858 set oldnlines [llength $displist]
861 proc drawtags {id x xt y1} {
862 global idtags idheads idotherrefs
863 global linespc lthickness
864 global canv mainfont idline rowtextx
869 if {[info exists idtags($id)]} {
870 set marks $idtags($id)
871 set ntags [llength $marks]
873 if {[info exists idheads($id)]} {
874 set marks [concat $marks $idheads($id)]
875 set nheads [llength $idheads($id)]
877 if {[info exists idotherrefs($id)]} {
878 set marks [concat $marks $idotherrefs($id)]
884 set delta [expr {int(0.5 * ($linespc - $lthickness))}]
885 set yt [expr {$y1 - 0.5 * $linespc}]
886 set yb [expr {$yt + $linespc - 1}]
890 set wid [font measure $mainfont $tag]
893 set xt [expr {$xt + $delta + $wid + $lthickness + $linespc}]
895 set t [$canv create line $x $y1 [lindex $xvals end] $y1 \
896 -width $lthickness -fill black -tags tag.$id]
898 foreach tag $marks x $xvals wid $wvals {
899 set xl [expr {$x + $delta}]
900 set xr [expr {$x + $delta + $wid + $lthickness}]
901 if {[incr ntags -1] >= 0} {
903 set t [$canv create polygon $x [expr {$yt + $delta}] $xl $yt \
904 $xr $yt $xr $yb $xl $yb $x [expr {$yb - $delta}] \
905 -width 1 -outline black -fill yellow -tags tag.$id]
906 $canv bind $t <1> [list showtag $tag 1]
907 set rowtextx($idline($id)) [expr {$xr + $linespc}]
909 # draw a head or other ref
910 if {[incr nheads -1] >= 0} {
915 set xl [expr {$xl - $delta/2}]
916 $canv create polygon $x $yt $xr $yt $xr $yb $x $yb \
917 -width 1 -outline black -fill $col -tags tag.$id
919 set t [$canv create text $xl $y1 -anchor w -text $tag \
920 -font $mainfont -tags tag.$id]
922 $canv bind $t <1> [list showtag $tag 1]
928 proc notecrossings {id lo hi corner} {
929 global olddisplist crossings cornercrossings
931 for {set i $lo} {[incr i] < $hi} {} {
932 set p [lindex $olddisplist $i]
933 if {$p == {}} continue
935 if {![info exists cornercrossings($id)]
936 || [lsearch -exact $cornercrossings($id) $p] < 0} {
937 lappend cornercrossings($id) $p
939 if {![info exists cornercrossings($p)]
940 || [lsearch -exact $cornercrossings($p) $id] < 0} {
941 lappend cornercrossings($p) $id
944 if {![info exists crossings($id)]
945 || [lsearch -exact $crossings($id) $p] < 0} {
946 lappend crossings($id) $p
948 if {![info exists crossings($p)]
949 || [lsearch -exact $crossings($p) $id] < 0} {
950 lappend crossings($p) $id
956 proc xcoord {i level ln} {
957 global canvx0 xspc1 xspc2
959 set x [expr {$canvx0 + $i * $xspc1($ln)}]
960 if {$i > 0 && $i == $level} {
961 set x [expr {$x + 0.5 * ($xspc2 - $xspc1($ln))}]
962 } elseif {$i > $level} {
963 set x [expr {$x + $xspc2 - $xspc1($ln)}]
968 # it seems Tk can't draw arrows on the end of diagonal line segments...
969 proc trimdiagend {line} {
970 while {[llength $line] > 4} {
971 set x1 [lindex $line end-3]
972 set y1 [lindex $line end-2]
973 set x2 [lindex $line end-1]
974 set y2 [lindex $line end]
975 if {($x1 == $x2) != ($y1 == $y2)} break
976 set line [lreplace $line end-1 end]
981 proc trimdiagstart {line} {
982 while {[llength $line] > 4} {
983 set x1 [lindex $line 0]
984 set y1 [lindex $line 1]
985 set x2 [lindex $line 2]
986 set y2 [lindex $line 3]
987 if {($x1 == $x2) != ($y1 == $y2)} break
988 set line [lreplace $line 0 1]
993 proc drawslants {id needonscreen nohs} {
994 global canv mainline mainlinearrow sidelines
995 global canvx0 canvy xspc1 xspc2 lthickness
996 global currentparents dupparents
997 global lthickness linespc canvy colormap lineno geometry
998 global maxgraphpct maxwidth
999 global displist onscreen lastuse
1000 global parents commitlisted
1001 global oldnlines olddlevel olddisplist
1002 global nhyperspace numcommits nnewparents
1005 lappend displist $id
1010 set y1 [expr {$canvy - $linespc}]
1013 # work out what we need to get back on screen
1015 if {$onscreen($id) < 0} {
1016 # next to do isn't displayed, better get it on screen...
1017 lappend reins [list $id 0]
1019 # make sure all the previous commits's parents are on the screen
1020 foreach p $currentparents {
1021 if {$onscreen($p) < 0} {
1022 lappend reins [list $p 0]
1025 # bring back anything requested by caller
1026 if {$needonscreen ne {}} {
1027 lappend reins $needonscreen
1031 if {$currentparents == $id && $onscreen($id) == 0 && $reins eq {}} {
1032 set dlevel $olddlevel
1033 set x [xcoord $dlevel $dlevel $lineno]
1034 set mainline($id) [list $x $y1]
1035 set mainlinearrow($id) none
1036 set lastuse($id) $lineno
1037 set displist [lreplace $displist $dlevel $dlevel $id]
1039 set xspc1([expr {$lineno + 1}]) $xspc1($lineno)
1044 set displist [lreplace $displist $olddlevel $olddlevel]
1046 foreach p $currentparents {
1047 set lastuse($p) $lineno
1048 if {$onscreen($p) == 0} {
1049 set displist [linsert $displist $j $p]
1054 if {$onscreen($id) == 0} {
1055 lappend displist $id
1059 # remove the null entry if present
1060 set nullentry [lsearch -exact $displist {}]
1061 if {$nullentry >= 0} {
1062 set displist [lreplace $displist $nullentry $nullentry]
1065 # bring back the ones we need now (if we did it earlier
1066 # it would change displist and invalidate olddlevel)
1068 # test again in case of duplicates in reins
1069 set p [lindex $pi 0]
1070 if {$onscreen($p) < 0} {
1072 set lastuse($p) $lineno
1073 set displist [linsert $displist [lindex $pi 1] $p]
1078 set lastuse($id) $lineno
1080 # see if we need to make any lines jump off into hyperspace
1081 set displ [llength $displist]
1082 if {$displ > $maxwidth} {
1084 foreach x $displist {
1085 lappend ages [list $lastuse($x) $x]
1087 set ages [lsort -integer -index 0 $ages]
1089 while {$displ > $maxwidth} {
1090 set use [lindex $ages $k 0]
1091 set victim [lindex $ages $k 1]
1092 if {$use >= $lineno - 5} break
1094 if {[lsearch -exact $nohs $victim] >= 0} continue
1095 set i [lsearch -exact $displist $victim]
1096 set displist [lreplace $displist $i $i]
1097 set onscreen($victim) -1
1100 if {$i < $nullentry} {
1103 set x [lindex $mainline($victim) end-1]
1104 lappend mainline($victim) $x $y1
1105 set line [trimdiagend $mainline($victim)]
1107 if {$mainlinearrow($victim) ne "none"} {
1108 set line [trimdiagstart $line]
1111 lappend sidelines($victim) [list $line 1 $arrow]
1112 unset mainline($victim)
1116 set dlevel [lsearch -exact $displist $id]
1118 # If we are reducing, put in a null entry
1119 if {$displ < $oldnlines} {
1120 # does the next line look like a merge?
1121 # i.e. does it have > 1 new parent?
1122 if {$nnewparents($id) > 1} {
1123 set i [expr {$dlevel + 1}]
1124 } elseif {$nnewparents([lindex $olddisplist $olddlevel]) == 0} {
1126 if {$nullentry >= 0 && $nullentry < $i} {
1129 } elseif {$nullentry >= 0} {
1132 && [lindex $olddisplist $i] == [lindex $displist $i]} {
1137 if {$dlevel >= $i} {
1142 set displist [linsert $displist $i {}]
1144 if {$dlevel >= $i} {
1150 # decide on the line spacing for the next line
1151 set lj [expr {$lineno + 1}]
1152 set maxw [expr {$maxgraphpct * $geometry(canv1) / 100}]
1153 if {$displ <= 1 || $canvx0 + $displ * $xspc2 <= $maxw} {
1154 set xspc1($lj) $xspc2
1156 set xspc1($lj) [expr {($maxw - $canvx0 - $xspc2) / ($displ - 1)}]
1157 if {$xspc1($lj) < $lthickness} {
1158 set xspc1($lj) $lthickness
1162 foreach idi $reins {
1163 set id [lindex $idi 0]
1164 set j [lsearch -exact $displist $id]
1165 set xj [xcoord $j $dlevel $lj]
1166 set mainline($id) [list $xj $y2]
1167 set mainlinearrow($id) first
1171 foreach id $olddisplist {
1173 if {$id == {}} continue
1174 if {$onscreen($id) <= 0} continue
1175 set xi [xcoord $i $olddlevel $lineno]
1176 if {$i == $olddlevel} {
1177 foreach p $currentparents {
1178 set j [lsearch -exact $displist $p]
1179 set coords [list $xi $y1]
1180 set xj [xcoord $j $dlevel $lj]
1181 if {$xj < $xi - $linespc} {
1182 lappend coords [expr {$xj + $linespc}] $y1
1183 notecrossings $p $j $i [expr {$j + 1}]
1184 } elseif {$xj > $xi + $linespc} {
1185 lappend coords [expr {$xj - $linespc}] $y1
1186 notecrossings $p $i $j [expr {$j - 1}]
1188 if {[lsearch -exact $dupparents $p] >= 0} {
1189 # draw a double-width line to indicate the doubled parent
1190 lappend coords $xj $y2
1191 lappend sidelines($p) [list $coords 2 none]
1192 if {![info exists mainline($p)]} {
1193 set mainline($p) [list $xj $y2]
1194 set mainlinearrow($p) none
1197 # normal case, no parent duplicated
1199 set dx [expr {abs($xi - $xj)}]
1200 if {0 && $dx < $linespc} {
1201 set yb [expr {$y1 + $dx}]
1203 if {![info exists mainline($p)]} {
1205 lappend coords $xj $yb
1207 set mainline($p) $coords
1208 set mainlinearrow($p) none
1210 lappend coords $xj $yb
1212 lappend coords $xj $y2
1214 lappend sidelines($p) [list $coords 1 none]
1220 if {[lindex $displist $i] != $id} {
1221 set j [lsearch -exact $displist $id]
1223 if {$j != $i || $xspc1($lineno) != $xspc1($lj)
1224 || ($olddlevel < $i && $i < $dlevel)
1225 || ($dlevel < $i && $i < $olddlevel)} {
1226 set xj [xcoord $j $dlevel $lj]
1227 lappend mainline($id) $xi $y1 $xj $y2
1234 # search for x in a list of lists
1235 proc llsearch {llist x} {
1238 if {$l == $x || [lsearch -exact $l $x] >= 0} {
1246 proc drawmore {reading} {
1247 global displayorder numcommits ncmupdate nextupdate
1248 global stopped nhyperspace parents commitlisted
1249 global maxwidth onscreen displist currentparents olddlevel
1251 set n [llength $displayorder]
1252 while {$numcommits < $n} {
1253 set id [lindex $displayorder $numcommits]
1254 set ctxend [expr {$numcommits + 10}]
1255 if {!$reading && $ctxend > $n} {
1259 if {$numcommits > 0} {
1260 set dlist [lreplace $displist $olddlevel $olddlevel]
1262 foreach p $currentparents {
1263 if {$onscreen($p) == 0} {
1264 set dlist [linsert $dlist $i $p]
1271 set isfat [expr {[llength $dlist] > $maxwidth}]
1272 if {$nhyperspace > 0 || $isfat} {
1273 if {$ctxend > $n} break
1274 # work out what to bring back and
1275 # what we want to don't want to send into hyperspace
1277 for {set k $numcommits} {$k < $ctxend} {incr k} {
1278 set x [lindex $displayorder $k]
1279 set i [llsearch $dlist $x]
1281 set i [llength $dlist]
1284 if {[lsearch -exact $nohs $x] < 0} {
1287 if {$reins eq {} && $onscreen($x) < 0 && $room} {
1288 set reins [list $x $i]
1291 if {[info exists commitlisted($x)]} {
1293 foreach p $parents($x) {
1294 if {[llsearch $dlist $p] < 0} {
1296 if {[lsearch -exact $nohs $p] < 0} {
1299 if {$reins eq {} && $onscreen($p) < 0 && $room} {
1300 set reins [list $p [expr {$i + $right}]]
1306 set l [lindex $dlist $i]
1307 if {[llength $l] == 1} {
1310 set j [lsearch -exact $l $x]
1311 set l [concat [lreplace $l $j $j] $newp]
1313 set dlist [lreplace $dlist $i $i $l]
1314 if {$room && $isfat && [llength $newp] <= 1} {
1320 set dlevel [drawslants $id $reins $nohs]
1321 drawcommitline $dlevel
1322 if {[clock clicks -milliseconds] >= $nextupdate
1323 && $numcommits >= $ncmupdate} {
1330 # level here is an index in todo
1331 proc updatetodo {level noshortcut} {
1332 global ncleft todo nnewparents
1333 global commitlisted parents onscreen
1335 set id [lindex $todo $level]
1337 if {[info exists commitlisted($id)]} {
1338 foreach p $parents($id) {
1339 if {[lsearch -exact $olds $p] < 0} {
1344 if {!$noshortcut && [llength $olds] == 1} {
1345 set p [lindex $olds 0]
1346 if {$ncleft($p) == 1 && [lsearch -exact $todo $p] < 0} {
1348 set todo [lreplace $todo $level $level $p]
1350 set nnewparents($id) 1
1355 set todo [lreplace $todo $level $level]
1360 set k [lsearch -exact $todo $p]
1362 set todo [linsert $todo $i $p]
1368 set nnewparents($id) $n
1373 proc decidenext {{noread 0}} {
1375 global datemode cdate
1378 # choose which one to do next time around
1379 set todol [llength $todo]
1382 for {set k $todol} {[incr k -1] >= 0} {} {
1383 set p [lindex $todo $k]
1384 if {$ncleft($p) == 0} {
1386 if {![info exists commitinfo($p)]} {
1392 if {$latest == {} || $cdate($p) > $latest} {
1394 set latest $cdate($p)
1404 puts "ERROR: none of the pending commits can be done yet:"
1406 puts " $p ($ncleft($p))"
1415 proc drawcommit {id} {
1416 global phase todo nchildren datemode nextupdate revlistorder
1417 global numcommits ncmupdate displayorder todo onscreen parents
1419 if {$phase != "incrdraw"} {
1425 if {$nchildren($id) == 0} {
1429 if {$revlistorder} {
1430 set level [lsearch -exact $todo $id]
1432 error_popup "oops, $id isn't in todo"
1435 lappend displayorder $id
1438 set level [decidenext 1]
1439 if {$level == {} || $id != [lindex $todo $level]} {
1443 lappend displayorder [lindex $todo $level]
1444 if {[updatetodo $level $datemode]} {
1445 set level [decidenext 1]
1446 if {$level == {}} break
1448 set id [lindex $todo $level]
1449 if {![info exists commitlisted($id)]} {
1457 proc finishcommits {} {
1459 global canv mainfont ctext maincursor textcursor
1461 if {$phase != "incrdraw"} {
1463 $canv create text 3 3 -anchor nw -text "No commits selected" \
1464 -font $mainfont -tags textitems
1469 . config -cursor $maincursor
1470 settextcursor $textcursor
1473 # Don't change the text pane cursor if it is currently the hand cursor,
1474 # showing that we are over a sha1 ID link.
1475 proc settextcursor {c} {
1476 global ctext curtextcursor
1478 if {[$ctext cget -cursor] == $curtextcursor} {
1479 $ctext config -cursor $c
1481 set curtextcursor $c
1485 global nextupdate startmsecs ncmupdate
1486 global displayorder onscreen
1488 if {$displayorder == {}} return
1489 set startmsecs [clock clicks -milliseconds]
1490 set nextupdate [expr {$startmsecs + 100}]
1493 foreach id $displayorder {
1500 global phase stopped redisplaying selectedline
1501 global datemode todo displayorder
1502 global numcommits ncmupdate
1503 global nextupdate startmsecs revlistorder
1505 set level [decidenext]
1509 lappend displayorder [lindex $todo $level]
1510 set hard [updatetodo $level $datemode]
1512 set level [decidenext]
1513 if {$level < 0} break
1519 set drawmsecs [expr {[clock clicks -milliseconds] - $startmsecs}]
1520 #puts "overall $drawmsecs ms for $numcommits commits"
1521 if {$redisplaying} {
1522 if {$stopped == 0 && [info exists selectedline]} {
1523 selectline $selectedline 0
1525 if {$stopped == 1} {
1527 after idle drawgraph
1534 proc findmatches {f} {
1535 global findtype foundstring foundstrlen
1536 if {$findtype == "Regexp"} {
1537 set matches [regexp -indices -all -inline $foundstring $f]
1539 if {$findtype == "IgnCase"} {
1540 set str [string tolower $f]
1546 while {[set j [string first $foundstring $str $i]] >= 0} {
1547 lappend matches [list $j [expr {$j+$foundstrlen-1}]]
1548 set i [expr {$j + $foundstrlen}]
1555 global findtype findloc findstring markedmatches commitinfo
1556 global numcommits lineid linehtag linentag linedtag
1557 global mainfont namefont canv canv2 canv3 selectedline
1558 global matchinglines foundstring foundstrlen
1563 set matchinglines {}
1564 if {$findloc == "Pickaxe"} {
1568 if {$findtype == "IgnCase"} {
1569 set foundstring [string tolower $findstring]
1571 set foundstring $findstring
1573 set foundstrlen [string length $findstring]
1574 if {$foundstrlen == 0} return
1575 if {$findloc == "Files"} {
1579 if {![info exists selectedline]} {
1582 set oldsel $selectedline
1585 set fldtypes {Headline Author Date Committer CDate Comment}
1586 for {set l 0} {$l < $numcommits} {incr l} {
1588 set info $commitinfo($id)
1590 foreach f $info ty $fldtypes {
1591 if {$findloc != "All fields" && $findloc != $ty} {
1594 set matches [findmatches $f]
1595 if {$matches == {}} continue
1597 if {$ty == "Headline"} {
1598 markmatches $canv $l $f $linehtag($l) $matches $mainfont
1599 } elseif {$ty == "Author"} {
1600 markmatches $canv2 $l $f $linentag($l) $matches $namefont
1601 } elseif {$ty == "Date"} {
1602 markmatches $canv3 $l $f $linedtag($l) $matches $mainfont
1606 lappend matchinglines $l
1607 if {!$didsel && $l > $oldsel} {
1613 if {$matchinglines == {}} {
1615 } elseif {!$didsel} {
1616 findselectline [lindex $matchinglines 0]
1620 proc findselectline {l} {
1621 global findloc commentend ctext
1623 if {$findloc == "All fields" || $findloc == "Comments"} {
1624 # highlight the matches in the comments
1625 set f [$ctext get 1.0 $commentend]
1626 set matches [findmatches $f]
1627 foreach match $matches {
1628 set start [lindex $match 0]
1629 set end [expr {[lindex $match 1] + 1}]
1630 $ctext tag add found "1.0 + $start c" "1.0 + $end c"
1635 proc findnext {restart} {
1636 global matchinglines selectedline
1637 if {![info exists matchinglines]} {
1643 if {![info exists selectedline]} return
1644 foreach l $matchinglines {
1645 if {$l > $selectedline} {
1654 global matchinglines selectedline
1655 if {![info exists matchinglines]} {
1659 if {![info exists selectedline]} return
1661 foreach l $matchinglines {
1662 if {$l >= $selectedline} break
1666 findselectline $prev
1672 proc findlocchange {name ix op} {
1673 global findloc findtype findtypemenu
1674 if {$findloc == "Pickaxe"} {
1680 $findtypemenu entryconf 1 -state $state
1681 $findtypemenu entryconf 2 -state $state
1684 proc stopfindproc {{done 0}} {
1685 global findprocpid findprocfile findids
1686 global ctext findoldcursor phase maincursor textcursor
1687 global findinprogress
1689 catch {unset findids}
1690 if {[info exists findprocpid]} {
1692 catch {exec kill $findprocpid}
1694 catch {close $findprocfile}
1697 if {[info exists findinprogress]} {
1698 unset findinprogress
1699 if {$phase != "incrdraw"} {
1700 . config -cursor $maincursor
1701 settextcursor $textcursor
1706 proc findpatches {} {
1707 global findstring selectedline numcommits
1708 global findprocpid findprocfile
1709 global finddidsel ctext lineid findinprogress
1710 global findinsertpos
1712 if {$numcommits == 0} return
1714 # make a list of all the ids to search, starting at the one
1715 # after the selected line (if any)
1716 if {[info exists selectedline]} {
1722 for {set i 0} {$i < $numcommits} {incr i} {
1723 if {[incr l] >= $numcommits} {
1726 append inputids $lineid($l) "\n"
1730 set f [open [list | git-diff-tree --stdin -s -r -S$findstring \
1733 error_popup "Error starting search process: $err"
1737 set findinsertpos end
1739 set findprocpid [pid $f]
1740 fconfigure $f -blocking 0
1741 fileevent $f readable readfindproc
1743 . config -cursor watch
1745 set findinprogress 1
1748 proc readfindproc {} {
1749 global findprocfile finddidsel
1750 global idline matchinglines findinsertpos
1752 set n [gets $findprocfile line]
1754 if {[eof $findprocfile]} {
1762 if {![regexp {^[0-9a-f]{40}} $line id]} {
1763 error_popup "Can't parse git-diff-tree output: $line"
1767 if {![info exists idline($id)]} {
1768 puts stderr "spurious id: $id"
1775 proc insertmatch {l id} {
1776 global matchinglines findinsertpos finddidsel
1778 if {$findinsertpos == "end"} {
1779 if {$matchinglines != {} && $l < [lindex $matchinglines 0]} {
1780 set matchinglines [linsert $matchinglines 0 $l]
1783 lappend matchinglines $l
1786 set matchinglines [linsert $matchinglines $findinsertpos $l]
1797 global selectedline numcommits lineid ctext
1798 global ffileline finddidsel parents nparents
1799 global findinprogress findstartline findinsertpos
1800 global treediffs fdiffids fdiffsneeded fdiffpos
1801 global findmergefiles
1803 if {$numcommits == 0} return
1805 if {[info exists selectedline]} {
1806 set l [expr {$selectedline + 1}]
1811 set findstartline $l
1816 if {$findmergefiles || $nparents($id) == 1} {
1817 foreach p $parents($id) {
1818 if {![info exists treediffs([list $id $p])]} {
1819 append diffsneeded "$id $p\n"
1820 lappend fdiffsneeded [list $id $p]
1824 if {[incr l] >= $numcommits} {
1827 if {$l == $findstartline} break
1830 # start off a git-diff-tree process if needed
1831 if {$diffsneeded ne {}} {
1833 set df [open [list | git-diff-tree -r --stdin << $diffsneeded] r]
1835 error_popup "Error starting search process: $err"
1838 catch {unset fdiffids}
1840 fconfigure $df -blocking 0
1841 fileevent $df readable [list readfilediffs $df]
1845 set findinsertpos end
1847 set p [lindex $parents($id) 0]
1848 . config -cursor watch
1850 set findinprogress 1
1851 findcont [list $id $p]
1855 proc readfilediffs {df} {
1856 global findids fdiffids fdiffs
1858 set n [gets $df line]
1862 if {[catch {close $df} err]} {
1865 error_popup "Error in git-diff-tree: $err"
1866 } elseif {[info exists findids]} {
1870 error_popup "Couldn't find diffs for {$ids}"
1875 if {[regexp {^([0-9a-f]{40}) \(from ([0-9a-f]{40})\)} $line match id p]} {
1876 # start of a new string of diffs
1878 set fdiffids [list $id $p]
1880 } elseif {[string match ":*" $line]} {
1881 lappend fdiffs [lindex $line 5]
1885 proc donefilediff {} {
1886 global fdiffids fdiffs treediffs findids
1887 global fdiffsneeded fdiffpos
1889 if {[info exists fdiffids]} {
1890 while {[lindex $fdiffsneeded $fdiffpos] ne $fdiffids
1891 && $fdiffpos < [llength $fdiffsneeded]} {
1892 # git-diff-tree doesn't output anything for a commit
1893 # which doesn't change anything
1894 set nullids [lindex $fdiffsneeded $fdiffpos]
1895 set treediffs($nullids) {}
1896 if {[info exists findids] && $nullids eq $findids} {
1904 if {![info exists treediffs($fdiffids)]} {
1905 set treediffs($fdiffids) $fdiffs
1907 if {[info exists findids] && $fdiffids eq $findids} {
1914 proc findcont {ids} {
1915 global findids treediffs parents nparents
1916 global ffileline findstartline finddidsel
1917 global lineid numcommits matchinglines findinprogress
1918 global findmergefiles
1920 set id [lindex $ids 0]
1921 set p [lindex $ids 1]
1922 set pi [lsearch -exact $parents($id) $p]
1925 if {$findmergefiles || $nparents($id) == 1} {
1926 if {![info exists treediffs($ids)]} {
1932 foreach f $treediffs($ids) {
1933 set x [findmatches $f]
1941 set pi $nparents($id)
1944 set pi $nparents($id)
1946 if {[incr pi] >= $nparents($id)} {
1948 if {[incr l] >= $numcommits} {
1951 if {$l == $findstartline} break
1954 set p [lindex $parents($id) $pi]
1955 set ids [list $id $p]
1963 # mark a commit as matching by putting a yellow background
1964 # behind the headline
1965 proc markheadline {l id} {
1966 global canv mainfont linehtag commitinfo
1968 set bbox [$canv bbox $linehtag($l)]
1969 set t [$canv create rect $bbox -outline {} -tags matches -fill yellow]
1973 # mark the bits of a headline, author or date that match a find string
1974 proc markmatches {canv l str tag matches font} {
1975 set bbox [$canv bbox $tag]
1976 set x0 [lindex $bbox 0]
1977 set y0 [lindex $bbox 1]
1978 set y1 [lindex $bbox 3]
1979 foreach match $matches {
1980 set start [lindex $match 0]
1981 set end [lindex $match 1]
1982 if {$start > $end} continue
1983 set xoff [font measure $font [string range $str 0 [expr {$start-1}]]]
1984 set xlen [font measure $font [string range $str 0 [expr {$end}]]]
1985 set t [$canv create rect [expr {$x0+$xoff}] $y0 \
1986 [expr {$x0+$xlen+2}] $y1 \
1987 -outline {} -tags matches -fill yellow]
1992 proc unmarkmatches {} {
1993 global matchinglines findids
1994 allcanvs delete matches
1995 catch {unset matchinglines}
1996 catch {unset findids}
1999 proc selcanvline {w x y} {
2000 global canv canvy0 ctext linespc
2001 global lineid linehtag linentag linedtag rowtextx
2002 set ymax [lindex [$canv cget -scrollregion] 3]
2003 if {$ymax == {}} return
2004 set yfrac [lindex [$canv yview] 0]
2005 set y [expr {$y + $yfrac * $ymax}]
2006 set l [expr {int(($y - $canvy0) / $linespc + 0.5)}]
2011 if {![info exists rowtextx($l)] || $x < $rowtextx($l)} return
2017 proc commit_descriptor {p} {
2020 if {[info exists commitinfo($p)]} {
2021 set l [lindex $commitinfo($p) 0]
2026 # append some text to the ctext widget, and make any SHA1 ID
2027 # that we know about be a clickable link.
2028 proc appendwithlinks {text} {
2029 global ctext idline linknum
2031 set start [$ctext index "end - 1c"]
2032 $ctext insert end $text
2033 $ctext insert end "\n"
2034 set links [regexp -indices -all -inline {[0-9a-f]{40}} $text]
2038 set linkid [string range $text $s $e]
2039 if {![info exists idline($linkid)]} continue
2041 $ctext tag add link "$start + $s c" "$start + $e c"
2042 $ctext tag add link$linknum "$start + $s c" "$start + $e c"
2043 $ctext tag bind link$linknum <1> [list selectline $idline($linkid) 1]
2046 $ctext tag conf link -foreground blue -underline 1
2047 $ctext tag bind link <Enter> { %W configure -cursor hand2 }
2048 $ctext tag bind link <Leave> { %W configure -cursor $curtextcursor }
2051 proc selectline {l isnew} {
2052 global canv canv2 canv3 ctext commitinfo selectedline
2053 global lineid linehtag linentag linedtag
2054 global canvy0 linespc parents nparents children
2055 global cflist currentid sha1entry
2056 global commentend idtags idline linknum
2060 if {![info exists lineid($l)] || ![info exists linehtag($l)]} return
2062 set t [eval $canv create rect [$canv bbox $linehtag($l)] -outline {{}} \
2063 -tags secsel -fill [$canv cget -selectbackground]]
2065 $canv2 delete secsel
2066 set t [eval $canv2 create rect [$canv2 bbox $linentag($l)] -outline {{}} \
2067 -tags secsel -fill [$canv2 cget -selectbackground]]
2069 $canv3 delete secsel
2070 set t [eval $canv3 create rect [$canv3 bbox $linedtag($l)] -outline {{}} \
2071 -tags secsel -fill [$canv3 cget -selectbackground]]
2073 set y [expr {$canvy0 + $l * $linespc}]
2074 set ymax [lindex [$canv cget -scrollregion] 3]
2075 set ytop [expr {$y - $linespc - 1}]
2076 set ybot [expr {$y + $linespc + 1}]
2077 set wnow [$canv yview]
2078 set wtop [expr {[lindex $wnow 0] * $ymax}]
2079 set wbot [expr {[lindex $wnow 1] * $ymax}]
2080 set wh [expr {$wbot - $wtop}]
2082 if {$ytop < $wtop} {
2083 if {$ybot < $wtop} {
2084 set newtop [expr {$y - $wh / 2.0}]
2087 if {$newtop > $wtop - $linespc} {
2088 set newtop [expr {$wtop - $linespc}]
2091 } elseif {$ybot > $wbot} {
2092 if {$ytop > $wbot} {
2093 set newtop [expr {$y - $wh / 2.0}]
2095 set newtop [expr {$ybot - $wh}]
2096 if {$newtop < $wtop + $linespc} {
2097 set newtop [expr {$wtop + $linespc}]
2101 if {$newtop != $wtop} {
2105 allcanvs yview moveto [expr {$newtop * 1.0 / $ymax}]
2109 addtohistory [list selectline $l 0]
2116 $sha1entry delete 0 end
2117 $sha1entry insert 0 $id
2118 $sha1entry selection from 0
2119 $sha1entry selection to end
2121 $ctext conf -state normal
2122 $ctext delete 0.0 end
2124 $ctext mark set fmark.0 0.0
2125 $ctext mark gravity fmark.0 left
2126 set info $commitinfo($id)
2127 set date [formatdate [lindex $info 2]]
2128 $ctext insert end "Author: [lindex $info 1] $date\n"
2129 set date [formatdate [lindex $info 4]]
2130 $ctext insert end "Committer: [lindex $info 3] $date\n"
2131 if {[info exists idtags($id)]} {
2132 $ctext insert end "Tags:"
2133 foreach tag $idtags($id) {
2134 $ctext insert end " $tag"
2136 $ctext insert end "\n"
2140 if {[info exists parents($id)]} {
2141 foreach p $parents($id) {
2142 append comment "Parent: [commit_descriptor $p]\n"
2145 if {[info exists children($id)]} {
2146 foreach c $children($id) {
2147 append comment "Child: [commit_descriptor $c]\n"
2151 append comment [lindex $info 5]
2153 # make anything that looks like a SHA1 ID be a clickable link
2154 appendwithlinks $comment
2156 $ctext tag delete Comments
2157 $ctext tag remove found 1.0 end
2158 $ctext conf -state disabled
2159 set commentend [$ctext index "end - 1c"]
2161 $cflist delete 0 end
2162 $cflist insert end "Comments"
2163 if {$nparents($id) == 1} {
2165 } elseif {$nparents($id) > 1} {
2170 proc selnextline {dir} {
2172 if {![info exists selectedline]} return
2173 set l [expr {$selectedline + $dir}]
2178 proc unselectline {} {
2181 catch {unset selectedline}
2182 allcanvs delete secsel
2185 proc addtohistory {cmd} {
2186 global history historyindex
2188 if {$historyindex > 0
2189 && [lindex $history [expr {$historyindex - 1}]] == $cmd} {
2193 if {$historyindex < [llength $history]} {
2194 set history [lreplace $history $historyindex end $cmd]
2196 lappend history $cmd
2199 if {$historyindex > 1} {
2200 .ctop.top.bar.leftbut conf -state normal
2202 .ctop.top.bar.leftbut conf -state disabled
2204 .ctop.top.bar.rightbut conf -state disabled
2208 global history historyindex
2210 if {$historyindex > 1} {
2211 incr historyindex -1
2212 set cmd [lindex $history [expr {$historyindex - 1}]]
2214 .ctop.top.bar.rightbut conf -state normal
2216 if {$historyindex <= 1} {
2217 .ctop.top.bar.leftbut conf -state disabled
2222 global history historyindex
2224 if {$historyindex < [llength $history]} {
2225 set cmd [lindex $history $historyindex]
2228 .ctop.top.bar.leftbut conf -state normal
2230 if {$historyindex >= [llength $history]} {
2231 .ctop.top.bar.rightbut conf -state disabled
2235 proc mergediff {id} {
2236 global parents diffmergeid diffmergegca mergefilelist diffpindex
2240 set diffmergegca [findgca $parents($id)]
2241 if {[info exists mergefilelist($id)]} {
2242 if {$mergefilelist($id) ne {}} {
2250 proc findgca {ids} {
2257 set gca [exec git-merge-base $gca $id]
2266 proc contmergediff {ids} {
2267 global diffmergeid diffpindex parents nparents diffmergegca
2268 global treediffs mergefilelist diffids treepending
2270 # diff the child against each of the parents, and diff
2271 # each of the parents against the GCA.
2273 if {[lindex $ids 1] == $diffmergeid && $diffmergegca ne {}} {
2274 set ids [list $diffmergegca [lindex $ids 0]]
2276 if {[incr diffpindex] >= $nparents($diffmergeid)} break
2277 set p [lindex $parents($diffmergeid) $diffpindex]
2278 set ids [list $p $diffmergeid]
2280 if {![info exists treediffs($ids)]} {
2282 if {![info exists treepending]} {
2289 # If a file in some parent is different from the child and also
2290 # different from the GCA, then it's interesting.
2291 # If we don't have a GCA, then a file is interesting if it is
2292 # different from the child in all the parents.
2293 if {$diffmergegca ne {}} {
2295 foreach p $parents($diffmergeid) {
2296 set gcadiffs $treediffs([list $diffmergegca $p])
2297 foreach f $treediffs([list $p $diffmergeid]) {
2298 if {[lsearch -exact $files $f] < 0
2299 && [lsearch -exact $gcadiffs $f] >= 0} {
2304 set files [lsort $files]
2306 set p [lindex $parents($diffmergeid) 0]
2307 set files $treediffs([list $diffmergeid $p])
2308 for {set i 1} {$i < $nparents($diffmergeid) && $files ne {}} {incr i} {
2309 set p [lindex $parents($diffmergeid) $i]
2310 set df $treediffs([list $p $diffmergeid])
2313 if {[lsearch -exact $df $f] >= 0} {
2321 set mergefilelist($diffmergeid) $files
2327 proc showmergediff {} {
2328 global cflist diffmergeid mergefilelist parents
2329 global diffopts diffinhunk currentfile currenthunk filelines
2330 global diffblocked groupfilelast mergefds groupfilenum grouphunks
2332 set files $mergefilelist($diffmergeid)
2334 $cflist insert end $f
2336 set env(GIT_DIFF_OPTS) $diffopts
2338 catch {unset currentfile}
2339 catch {unset currenthunk}
2340 catch {unset filelines}
2341 catch {unset groupfilenum}
2342 catch {unset grouphunks}
2343 set groupfilelast -1
2344 foreach p $parents($diffmergeid) {
2345 set cmd [list | git-diff-tree -p $p $diffmergeid]
2346 set cmd [concat $cmd $mergefilelist($diffmergeid)]
2347 if {[catch {set f [open $cmd r]} err]} {
2348 error_popup "Error getting diffs: $err"
2355 set ids [list $diffmergeid $p]
2356 set mergefds($ids) $f
2357 set diffinhunk($ids) 0
2358 set diffblocked($ids) 0
2359 fconfigure $f -blocking 0
2360 fileevent $f readable [list getmergediffline $f $ids $diffmergeid]
2364 proc getmergediffline {f ids id} {
2365 global diffmergeid diffinhunk diffoldlines diffnewlines
2366 global currentfile currenthunk
2367 global diffoldstart diffnewstart diffoldlno diffnewlno
2368 global diffblocked mergefilelist
2369 global noldlines nnewlines difflcounts filelines
2371 set n [gets $f line]
2373 if {![eof $f]} return
2376 if {!([info exists diffmergeid] && $diffmergeid == $id)} {
2383 if {$diffinhunk($ids) != 0} {
2384 set fi $currentfile($ids)
2385 if {$n > 0 && [regexp {^[-+ \\]} $line match]} {
2386 # continuing an existing hunk
2387 set line [string range $line 1 end]
2388 set p [lindex $ids 1]
2389 if {$match eq "-" || $match eq " "} {
2390 set filelines($p,$fi,$diffoldlno($ids)) $line
2391 incr diffoldlno($ids)
2393 if {$match eq "+" || $match eq " "} {
2394 set filelines($id,$fi,$diffnewlno($ids)) $line
2395 incr diffnewlno($ids)
2397 if {$match eq " "} {
2398 if {$diffinhunk($ids) == 2} {
2399 lappend difflcounts($ids) \
2400 [list $noldlines($ids) $nnewlines($ids)]
2401 set noldlines($ids) 0
2402 set diffinhunk($ids) 1
2404 incr noldlines($ids)
2405 } elseif {$match eq "-" || $match eq "+"} {
2406 if {$diffinhunk($ids) == 1} {
2407 lappend difflcounts($ids) [list $noldlines($ids)]
2408 set noldlines($ids) 0
2409 set nnewlines($ids) 0
2410 set diffinhunk($ids) 2
2412 if {$match eq "-"} {
2413 incr noldlines($ids)
2415 incr nnewlines($ids)
2418 # and if it's \ No newline at end of line, then what?
2422 if {$diffinhunk($ids) == 1 && $noldlines($ids) != 0} {
2423 lappend difflcounts($ids) [list $noldlines($ids)]
2424 } elseif {$diffinhunk($ids) == 2
2425 && ($noldlines($ids) != 0 || $nnewlines($ids) != 0)} {
2426 lappend difflcounts($ids) [list $noldlines($ids) $nnewlines($ids)]
2428 set currenthunk($ids) [list $currentfile($ids) \
2429 $diffoldstart($ids) $diffnewstart($ids) \
2430 $diffoldlno($ids) $diffnewlno($ids) \
2432 set diffinhunk($ids) 0
2433 # -1 = need to block, 0 = unblocked, 1 = is blocked
2434 set diffblocked($ids) -1
2436 if {$diffblocked($ids) == -1} {
2437 fileevent $f readable {}
2438 set diffblocked($ids) 1
2444 if {!$diffblocked($ids)} {
2446 set currentfile($ids) [llength $mergefilelist($diffmergeid)]
2447 set currenthunk($ids) [list $currentfile($ids) 0 0 0 0 {}]
2450 } elseif {[regexp {^diff --git a/(.*) b/} $line match fname]} {
2451 # start of a new file
2452 set currentfile($ids) \
2453 [lsearch -exact $mergefilelist($diffmergeid) $fname]
2454 } elseif {[regexp {^@@ -([0-9]+),([0-9]+) \+([0-9]+),([0-9]+) @@(.*)} \
2455 $line match f1l f1c f2l f2c rest]} {
2456 if {[info exists currentfile($ids)] && $currentfile($ids) >= 0} {
2457 # start of a new hunk
2458 if {$f1l == 0 && $f1c == 0} {
2461 if {$f2l == 0 && $f2c == 0} {
2464 set diffinhunk($ids) 1
2465 set diffoldstart($ids) $f1l
2466 set diffnewstart($ids) $f2l
2467 set diffoldlno($ids) $f1l
2468 set diffnewlno($ids) $f2l
2469 set difflcounts($ids) {}
2470 set noldlines($ids) 0
2471 set nnewlines($ids) 0
2476 proc processhunks {} {
2477 global diffmergeid parents nparents currenthunk
2478 global mergefilelist diffblocked mergefds
2479 global grouphunks grouplinestart grouplineend groupfilenum
2481 set nfiles [llength $mergefilelist($diffmergeid)]
2485 # look for the earliest hunk
2486 foreach p $parents($diffmergeid) {
2487 set ids [list $diffmergeid $p]
2488 if {![info exists currenthunk($ids)]} return
2489 set i [lindex $currenthunk($ids) 0]
2490 set l [lindex $currenthunk($ids) 2]
2491 if {$i < $fi || ($i == $fi && $l < $lno)} {
2498 if {$fi < $nfiles} {
2499 set ids [list $diffmergeid $pi]
2500 set hunk $currenthunk($ids)
2501 unset currenthunk($ids)
2502 if {$diffblocked($ids) > 0} {
2503 fileevent $mergefds($ids) readable \
2504 [list getmergediffline $mergefds($ids) $ids $diffmergeid]
2506 set diffblocked($ids) 0
2508 if {[info exists groupfilenum] && $groupfilenum == $fi
2509 && $lno <= $grouplineend} {
2510 # add this hunk to the pending group
2511 lappend grouphunks($pi) $hunk
2512 set endln [lindex $hunk 4]
2513 if {$endln > $grouplineend} {
2514 set grouplineend $endln
2520 # succeeding stuff doesn't belong in this group, so
2521 # process the group now
2522 if {[info exists groupfilenum]} {
2528 if {$fi >= $nfiles} break
2531 set groupfilenum $fi
2532 set grouphunks($pi) [list $hunk]
2533 set grouplinestart $lno
2534 set grouplineend [lindex $hunk 4]
2538 proc processgroup {} {
2539 global groupfilelast groupfilenum difffilestart
2540 global mergefilelist diffmergeid ctext filelines
2541 global parents diffmergeid diffoffset
2542 global grouphunks grouplinestart grouplineend nparents
2545 $ctext conf -state normal
2548 if {$groupfilelast != $f} {
2549 $ctext insert end "\n"
2550 set here [$ctext index "end - 1c"]
2551 set difffilestart($f) $here
2552 set mark fmark.[expr {$f + 1}]
2553 $ctext mark set $mark $here
2554 $ctext mark gravity $mark left
2555 set header [lindex $mergefilelist($id) $f]
2556 set l [expr {(78 - [string length $header]) / 2}]
2557 set pad [string range "----------------------------------------" 1 $l]
2558 $ctext insert end "$pad $header $pad\n" filesep
2559 set groupfilelast $f
2560 foreach p $parents($id) {
2561 set diffoffset($p) 0
2565 $ctext insert end "@@" msep
2566 set nlines [expr {$grouplineend - $grouplinestart}]
2569 foreach p $parents($id) {
2570 set startline [expr {$grouplinestart + $diffoffset($p)}]
2572 set nl $grouplinestart
2573 if {[info exists grouphunks($p)]} {
2574 foreach h $grouphunks($p) {
2577 for {} {$nl < $l} {incr nl} {
2578 set filelines($p,$f,$ol) $filelines($id,$f,$nl)
2582 foreach chunk [lindex $h 5] {
2583 if {[llength $chunk] == 2} {
2584 set olc [lindex $chunk 0]
2585 set nlc [lindex $chunk 1]
2586 set nnl [expr {$nl + $nlc}]
2587 lappend events [list $nl $nnl $pnum $olc $nlc]
2591 incr ol [lindex $chunk 0]
2592 incr nl [lindex $chunk 0]
2597 if {$nl < $grouplineend} {
2598 for {} {$nl < $grouplineend} {incr nl} {
2599 set filelines($p,$f,$ol) $filelines($id,$f,$nl)
2603 set nlines [expr {$ol - $startline}]
2604 $ctext insert end " -$startline,$nlines" msep
2608 set nlines [expr {$grouplineend - $grouplinestart}]
2609 $ctext insert end " +$grouplinestart,$nlines @@\n" msep
2611 set events [lsort -integer -index 0 $events]
2612 set nevents [llength $events]
2613 set nmerge $nparents($diffmergeid)
2614 set l $grouplinestart
2615 for {set i 0} {$i < $nevents} {set i $j} {
2616 set nl [lindex $events $i 0]
2618 $ctext insert end " $filelines($id,$f,$l)\n"
2621 set e [lindex $events $i]
2622 set enl [lindex $e 1]
2626 set pnum [lindex $e 2]
2627 set olc [lindex $e 3]
2628 set nlc [lindex $e 4]
2629 if {![info exists delta($pnum)]} {
2630 set delta($pnum) [expr {$olc - $nlc}]
2631 lappend active $pnum
2633 incr delta($pnum) [expr {$olc - $nlc}]
2635 if {[incr j] >= $nevents} break
2636 set e [lindex $events $j]
2637 if {[lindex $e 0] >= $enl} break
2638 if {[lindex $e 1] > $enl} {
2639 set enl [lindex $e 1]
2642 set nlc [expr {$enl - $l}]
2645 if {[llength $active] == $nmerge - 1} {
2646 # no diff for one of the parents, i.e. it's identical
2647 for {set pnum 0} {$pnum < $nmerge} {incr pnum} {
2648 if {![info exists delta($pnum)]} {
2649 if {$pnum < $mergemax} {
2657 } elseif {[llength $active] == $nmerge} {
2658 # all parents are different, see if one is very similar
2660 for {set pnum 0} {$pnum < $nmerge} {incr pnum} {
2661 set sim [similarity $pnum $l $nlc $f \
2662 [lrange $events $i [expr {$j-1}]]]
2663 if {$sim > $bestsim} {
2669 lappend ncol m$bestpn
2673 foreach p $parents($id) {
2675 if {![info exists delta($pnum)] || $pnum == $bestpn} continue
2676 set olc [expr {$nlc + $delta($pnum)}]
2677 set ol [expr {$l + $diffoffset($p)}]
2678 incr diffoffset($p) $delta($pnum)
2680 for {} {$olc > 0} {incr olc -1} {
2681 $ctext insert end "-$filelines($p,$f,$ol)\n" m$pnum
2685 set endl [expr {$l + $nlc}]
2687 # show this pretty much as a normal diff
2688 set p [lindex $parents($id) $bestpn]
2689 set ol [expr {$l + $diffoffset($p)}]
2690 incr diffoffset($p) $delta($bestpn)
2691 unset delta($bestpn)
2692 for {set k $i} {$k < $j} {incr k} {
2693 set e [lindex $events $k]
2694 if {[lindex $e 2] != $bestpn} continue
2695 set nl [lindex $e 0]
2696 set ol [expr {$ol + $nl - $l}]
2697 for {} {$l < $nl} {incr l} {
2698 $ctext insert end "+$filelines($id,$f,$l)\n" $ncol
2701 for {} {$c > 0} {incr c -1} {
2702 $ctext insert end "-$filelines($p,$f,$ol)\n" m$bestpn
2705 set nl [lindex $e 1]
2706 for {} {$l < $nl} {incr l} {
2707 $ctext insert end "+$filelines($id,$f,$l)\n" mresult
2711 for {} {$l < $endl} {incr l} {
2712 $ctext insert end "+$filelines($id,$f,$l)\n" $ncol
2715 while {$l < $grouplineend} {
2716 $ctext insert end " $filelines($id,$f,$l)\n"
2719 $ctext conf -state disabled
2722 proc similarity {pnum l nlc f events} {
2723 global diffmergeid parents diffoffset filelines
2726 set p [lindex $parents($id) $pnum]
2727 set ol [expr {$l + $diffoffset($p)}]
2728 set endl [expr {$l + $nlc}]
2732 if {[lindex $e 2] != $pnum} continue
2733 set nl [lindex $e 0]
2734 set ol [expr {$ol + $nl - $l}]
2735 for {} {$l < $nl} {incr l} {
2736 incr same [string length $filelines($id,$f,$l)]
2739 set oc [lindex $e 3]
2740 for {} {$oc > 0} {incr oc -1} {
2741 incr diff [string length $filelines($p,$f,$ol)]
2745 set nl [lindex $e 1]
2746 for {} {$l < $nl} {incr l} {
2747 incr diff [string length $filelines($id,$f,$l)]
2751 for {} {$l < $endl} {incr l} {
2752 incr same [string length $filelines($id,$f,$l)]
2758 return [expr {200 * $same / (2 * $same + $diff)}]
2761 proc startdiff {ids} {
2762 global treediffs diffids treepending diffmergeid
2765 catch {unset diffmergeid}
2766 if {![info exists treediffs($ids)]} {
2767 if {![info exists treepending]} {
2775 proc addtocflist {ids} {
2776 global treediffs cflist
2777 foreach f $treediffs($ids) {
2778 $cflist insert end $f
2783 proc gettreediffs {ids} {
2784 global treediff parents treepending
2785 set treepending $ids
2787 if [catch {set gdtf [open [concat | git-diff-tree --no-commit-id -r $ids] r]}] return
2788 fconfigure $gdtf -blocking 0
2789 fileevent $gdtf readable [list gettreediffline $gdtf $ids]
2792 proc gettreediffline {gdtf ids} {
2793 global treediff treediffs treepending diffids diffmergeid
2795 set n [gets $gdtf line]
2797 if {![eof $gdtf]} return
2799 set treediffs($ids) $treediff
2801 if {$ids != $diffids} {
2802 gettreediffs $diffids
2804 if {[info exists diffmergeid]} {
2812 set file [lindex $line 5]
2813 lappend treediff $file
2816 proc getblobdiffs {ids} {
2817 global diffopts blobdifffd diffids env curdifftag curtagstart
2818 global difffilestart nextupdate diffinhdr treediffs
2820 set env(GIT_DIFF_OPTS) $diffopts
2821 set cmd [concat | git-diff-tree --no-commit-id -r -p -C $ids]
2822 if {[catch {set bdf [open $cmd r]} err]} {
2823 puts "error getting diffs: $err"
2827 fconfigure $bdf -blocking 0
2828 set blobdifffd($ids) $bdf
2829 set curdifftag Comments
2831 catch {unset difffilestart}
2832 fileevent $bdf readable [list getblobdiffline $bdf $diffids]
2833 set nextupdate [expr {[clock clicks -milliseconds] + 100}]
2836 proc getblobdiffline {bdf ids} {
2837 global diffids blobdifffd ctext curdifftag curtagstart
2838 global diffnexthead diffnextnote difffilestart
2839 global nextupdate diffinhdr treediffs
2841 set n [gets $bdf line]
2845 if {$ids == $diffids && $bdf == $blobdifffd($ids)} {
2846 $ctext tag add $curdifftag $curtagstart end
2851 if {$ids != $diffids || $bdf != $blobdifffd($ids)} {
2854 $ctext conf -state normal
2855 if {[regexp {^diff --git a/(.*) b/(.*)} $line match fname newname]} {
2856 # start of a new file
2857 $ctext insert end "\n"
2858 $ctext tag add $curdifftag $curtagstart end
2859 set curtagstart [$ctext index "end - 1c"]
2861 set here [$ctext index "end - 1c"]
2862 set i [lsearch -exact $treediffs($diffids) $fname]
2864 set difffilestart($i) $here
2866 $ctext mark set fmark.$i $here
2867 $ctext mark gravity fmark.$i left
2869 if {$newname != $fname} {
2870 set i [lsearch -exact $treediffs($diffids) $newname]
2872 set difffilestart($i) $here
2874 $ctext mark set fmark.$i $here
2875 $ctext mark gravity fmark.$i left
2878 set curdifftag "f:$fname"
2879 $ctext tag delete $curdifftag
2880 set l [expr {(78 - [string length $header]) / 2}]
2881 set pad [string range "----------------------------------------" 1 $l]
2882 $ctext insert end "$pad $header $pad\n" filesep
2884 } elseif {[regexp {^(---|\+\+\+)} $line]} {
2886 } elseif {[regexp {^@@ -([0-9]+),([0-9]+) \+([0-9]+),([0-9]+) @@(.*)} \
2887 $line match f1l f1c f2l f2c rest]} {
2888 $ctext insert end "$line\n" hunksep
2891 set x [string range $line 0 0]
2892 if {$x == "-" || $x == "+"} {
2893 set tag [expr {$x == "+"}]
2894 $ctext insert end "$line\n" d$tag
2895 } elseif {$x == " "} {
2896 $ctext insert end "$line\n"
2897 } elseif {$diffinhdr || $x == "\\"} {
2898 # e.g. "\ No newline at end of file"
2899 $ctext insert end "$line\n" filesep
2901 # Something else we don't recognize
2902 if {$curdifftag != "Comments"} {
2903 $ctext insert end "\n"
2904 $ctext tag add $curdifftag $curtagstart end
2905 set curtagstart [$ctext index "end - 1c"]
2906 set curdifftag Comments
2908 $ctext insert end "$line\n" filesep
2911 $ctext conf -state disabled
2912 if {[clock clicks -milliseconds] >= $nextupdate} {
2914 fileevent $bdf readable {}
2916 fileevent $bdf readable "getblobdiffline $bdf {$ids}"
2921 global difffilestart ctext
2922 set here [$ctext index @0,0]
2923 for {set i 0} {[info exists difffilestart($i)]} {incr i} {
2924 if {[$ctext compare $difffilestart($i) > $here]} {
2925 if {![info exists pos]
2926 || [$ctext compare $difffilestart($i) < $pos]} {
2927 set pos $difffilestart($i)
2931 if {[info exists pos]} {
2936 proc listboxsel {} {
2937 global ctext cflist currentid
2938 if {![info exists currentid]} return
2939 set sel [lsort [$cflist curselection]]
2940 if {$sel eq {}} return
2941 set first [lindex $sel 0]
2942 catch {$ctext yview fmark.$first}
2946 global linespc charspc canvx0 canvy0 mainfont
2947 global xspc1 xspc2 lthickness
2949 set linespc [font metrics $mainfont -linespace]
2950 set charspc [font measure $mainfont "m"]
2951 set canvy0 [expr {3 + 0.5 * $linespc}]
2952 set canvx0 [expr {3 + 0.5 * $linespc}]
2953 set lthickness [expr {int($linespc / 9) + 1}]
2954 set xspc1(0) $linespc
2959 global stopped redisplaying phase
2960 if {$stopped > 1} return
2961 if {$phase == "getcommits"} return
2963 if {$phase == "drawgraph" || $phase == "incrdraw"} {
2970 proc incrfont {inc} {
2971 global mainfont namefont textfont ctext canv phase
2972 global stopped entries
2974 set mainfont [lreplace $mainfont 1 1 [expr {[lindex $mainfont 1] + $inc}]]
2975 set namefont [lreplace $namefont 1 1 [expr {[lindex $namefont 1] + $inc}]]
2976 set textfont [lreplace $textfont 1 1 [expr {[lindex $textfont 1] + $inc}]]
2978 $ctext conf -font $textfont
2979 $ctext tag conf filesep -font [concat $textfont bold]
2980 foreach e $entries {
2981 $e conf -font $mainfont
2983 if {$phase == "getcommits"} {
2984 $canv itemconf textitems -font $mainfont
2990 global sha1entry sha1string
2991 if {[string length $sha1string] == 40} {
2992 $sha1entry delete 0 end
2996 proc sha1change {n1 n2 op} {
2997 global sha1string currentid sha1but
2998 if {$sha1string == {}
2999 || ([info exists currentid] && $sha1string == $currentid)} {
3004 if {[$sha1but cget -state] == $state} return
3005 if {$state == "normal"} {
3006 $sha1but conf -state normal -relief raised -text "Goto: "
3008 $sha1but conf -state disabled -relief flat -text "SHA1 ID: "
3012 proc gotocommit {} {
3013 global sha1string currentid idline tagids
3014 global lineid numcommits
3016 if {$sha1string == {}
3017 || ([info exists currentid] && $sha1string == $currentid)} return
3018 if {[info exists tagids($sha1string)]} {
3019 set id $tagids($sha1string)
3021 set id [string tolower $sha1string]
3022 if {[regexp {^[0-9a-f]{4,39}$} $id]} {
3024 for {set l 0} {$l < $numcommits} {incr l} {
3025 if {[string match $id* $lineid($l)]} {
3026 lappend matches $lineid($l)
3029 if {$matches ne {}} {
3030 if {[llength $matches] > 1} {
3031 error_popup "Short SHA1 id $id is ambiguous"
3034 set id [lindex $matches 0]
3038 if {[info exists idline($id)]} {
3039 selectline $idline($id) 1
3042 if {[regexp {^[0-9a-fA-F]{4,}$} $sha1string]} {
3047 error_popup "$type $sha1string is not known"
3050 proc lineenter {x y id} {
3051 global hoverx hovery hoverid hovertimer
3052 global commitinfo canv
3054 if {![info exists commitinfo($id)]} return
3058 if {[info exists hovertimer]} {
3059 after cancel $hovertimer
3061 set hovertimer [after 500 linehover]
3065 proc linemotion {x y id} {
3066 global hoverx hovery hoverid hovertimer
3068 if {[info exists hoverid] && $id == $hoverid} {
3071 if {[info exists hovertimer]} {
3072 after cancel $hovertimer
3074 set hovertimer [after 500 linehover]
3078 proc lineleave {id} {
3079 global hoverid hovertimer canv
3081 if {[info exists hoverid] && $id == $hoverid} {
3083 if {[info exists hovertimer]} {
3084 after cancel $hovertimer
3092 global hoverx hovery hoverid hovertimer
3093 global canv linespc lthickness
3094 global commitinfo mainfont
3096 set text [lindex $commitinfo($hoverid) 0]
3097 set ymax [lindex [$canv cget -scrollregion] 3]
3098 if {$ymax == {}} return
3099 set yfrac [lindex [$canv yview] 0]
3100 set x [expr {$hoverx + 2 * $linespc}]
3101 set y [expr {$hovery + $yfrac * $ymax - $linespc / 2}]
3102 set x0 [expr {$x - 2 * $lthickness}]
3103 set y0 [expr {$y - 2 * $lthickness}]
3104 set x1 [expr {$x + [font measure $mainfont $text] + 2 * $lthickness}]
3105 set y1 [expr {$y + $linespc + 2 * $lthickness}]
3106 set t [$canv create rectangle $x0 $y0 $x1 $y1 \
3107 -fill \#ffff80 -outline black -width 1 -tags hover]
3109 set t [$canv create text $x $y -anchor nw -text $text -tags hover -font $mainfont]
3113 proc clickisonarrow {id y} {
3114 global mainline mainlinearrow sidelines lthickness
3116 set thresh [expr {2 * $lthickness + 6}]
3117 if {[info exists mainline($id)]} {
3118 if {$mainlinearrow($id) ne "none"} {
3119 if {abs([lindex $mainline($id) 1] - $y) < $thresh} {
3124 if {[info exists sidelines($id)]} {
3125 foreach ls $sidelines($id) {
3126 set coords [lindex $ls 0]
3127 set arrow [lindex $ls 2]
3128 if {$arrow eq "first" || $arrow eq "both"} {
3129 if {abs([lindex $coords 1] - $y) < $thresh} {
3133 if {$arrow eq "last" || $arrow eq "both"} {
3134 if {abs([lindex $coords end] - $y) < $thresh} {
3143 proc arrowjump {id dirn y} {
3144 global mainline sidelines canv canv2 canv3
3147 if {$dirn eq "down"} {
3148 if {[info exists mainline($id)]} {
3149 set y1 [lindex $mainline($id) 1]
3154 if {[info exists sidelines($id)]} {
3155 foreach ls $sidelines($id) {
3156 set y1 [lindex $ls 0 1]
3157 if {$y1 > $y && ($yt eq {} || $y1 < $yt)} {
3163 if {[info exists sidelines($id)]} {
3164 foreach ls $sidelines($id) {
3165 set y1 [lindex $ls 0 end]
3166 if {$y1 < $y && ($yt eq {} || $y1 > $yt)} {
3172 if {$yt eq {}} return
3173 set ymax [lindex [$canv cget -scrollregion] 3]
3174 if {$ymax eq {} || $ymax <= 0} return
3175 set view [$canv yview]
3176 set yspan [expr {[lindex $view 1] - [lindex $view 0]}]
3177 set yfrac [expr {$yt / $ymax - $yspan / 2}]
3181 $canv yview moveto $yfrac
3182 $canv2 yview moveto $yfrac
3183 $canv3 yview moveto $yfrac
3186 proc lineclick {x y id isnew} {
3187 global ctext commitinfo children cflist canv thickerline
3193 # draw this line thicker than normal
3197 set ymax [lindex [$canv cget -scrollregion] 3]
3198 if {$ymax eq {}} return
3199 set yfrac [lindex [$canv yview] 0]
3200 set y [expr {$y + $yfrac * $ymax}]
3202 set dirn [clickisonarrow $id $y]
3204 arrowjump $id $dirn $y
3209 addtohistory [list lineclick $x $y $id 0]
3211 # fill the details pane with info about this line
3212 $ctext conf -state normal
3213 $ctext delete 0.0 end
3214 $ctext tag conf link -foreground blue -underline 1
3215 $ctext tag bind link <Enter> { %W configure -cursor hand2 }
3216 $ctext tag bind link <Leave> { %W configure -cursor $curtextcursor }
3217 $ctext insert end "Parent:\t"
3218 $ctext insert end $id [list link link0]
3219 $ctext tag bind link0 <1> [list selbyid $id]
3220 set info $commitinfo($id)
3221 $ctext insert end "\n\t[lindex $info 0]\n"
3222 $ctext insert end "\tAuthor:\t[lindex $info 1]\n"
3223 set date [formatdate [lindex $info 2]]
3224 $ctext insert end "\tDate:\t$date\n"
3225 if {[info exists children($id)]} {
3226 $ctext insert end "\nChildren:"
3228 foreach child $children($id) {
3230 set info $commitinfo($child)
3231 $ctext insert end "\n\t"
3232 $ctext insert end $child [list link link$i]
3233 $ctext tag bind link$i <1> [list selbyid $child]
3234 $ctext insert end "\n\t[lindex $info 0]"
3235 $ctext insert end "\n\tAuthor:\t[lindex $info 1]"
3236 set date [formatdate [lindex $info 2]]
3237 $ctext insert end "\n\tDate:\t$date\n"
3240 $ctext conf -state disabled
3242 $cflist delete 0 end
3245 proc normalline {} {
3247 if {[info exists thickerline]} {
3248 drawlines $thickerline 0 1
3255 if {[info exists idline($id)]} {
3256 selectline $idline($id) 1
3262 if {![info exists startmstime]} {
3263 set startmstime [clock clicks -milliseconds]
3265 return [format "%.3f" [expr {([clock click -milliseconds] - $startmstime) / 1000.0}]]
3268 proc rowmenu {x y id} {
3269 global rowctxmenu idline selectedline rowmenuid
3271 if {![info exists selectedline] || $idline($id) eq $selectedline} {
3276 $rowctxmenu entryconfigure 0 -state $state
3277 $rowctxmenu entryconfigure 1 -state $state
3278 $rowctxmenu entryconfigure 2 -state $state
3280 tk_popup $rowctxmenu $x $y
3283 proc diffvssel {dirn} {
3284 global rowmenuid selectedline lineid
3286 if {![info exists selectedline]} return
3288 set oldid $lineid($selectedline)
3289 set newid $rowmenuid
3291 set oldid $rowmenuid
3292 set newid $lineid($selectedline)
3294 addtohistory [list doseldiff $oldid $newid]
3295 doseldiff $oldid $newid
3298 proc doseldiff {oldid newid} {
3302 $ctext conf -state normal
3303 $ctext delete 0.0 end
3304 $ctext mark set fmark.0 0.0
3305 $ctext mark gravity fmark.0 left
3306 $cflist delete 0 end
3307 $cflist insert end "Top"
3308 $ctext insert end "From "
3309 $ctext tag conf link -foreground blue -underline 1
3310 $ctext tag bind link <Enter> { %W configure -cursor hand2 }
3311 $ctext tag bind link <Leave> { %W configure -cursor $curtextcursor }
3312 $ctext tag bind link0 <1> [list selbyid $oldid]
3313 $ctext insert end $oldid [list link link0]
3314 $ctext insert end "\n "
3315 $ctext insert end [lindex $commitinfo($oldid) 0]
3316 $ctext insert end "\n\nTo "
3317 $ctext tag bind link1 <1> [list selbyid $newid]
3318 $ctext insert end $newid [list link link1]
3319 $ctext insert end "\n "
3320 $ctext insert end [lindex $commitinfo($newid) 0]
3321 $ctext insert end "\n"
3322 $ctext conf -state disabled
3323 $ctext tag delete Comments
3324 $ctext tag remove found 1.0 end
3325 startdiff [list $oldid $newid]
3329 global rowmenuid currentid commitinfo patchtop patchnum
3331 if {![info exists currentid]} return
3332 set oldid $currentid
3333 set oldhead [lindex $commitinfo($oldid) 0]
3334 set newid $rowmenuid
3335 set newhead [lindex $commitinfo($newid) 0]
3338 catch {destroy $top}
3340 label $top.title -text "Generate patch"
3341 grid $top.title - -pady 10
3342 label $top.from -text "From:"
3343 entry $top.fromsha1 -width 40 -relief flat
3344 $top.fromsha1 insert 0 $oldid
3345 $top.fromsha1 conf -state readonly
3346 grid $top.from $top.fromsha1 -sticky w
3347 entry $top.fromhead -width 60 -relief flat
3348 $top.fromhead insert 0 $oldhead
3349 $top.fromhead conf -state readonly
3350 grid x $top.fromhead -sticky w
3351 label $top.to -text "To:"
3352 entry $top.tosha1 -width 40 -relief flat
3353 $top.tosha1 insert 0 $newid
3354 $top.tosha1 conf -state readonly
3355 grid $top.to $top.tosha1 -sticky w
3356 entry $top.tohead -width 60 -relief flat
3357 $top.tohead insert 0 $newhead
3358 $top.tohead conf -state readonly
3359 grid x $top.tohead -sticky w
3360 button $top.rev -text "Reverse" -command mkpatchrev -padx 5
3361 grid $top.rev x -pady 10
3362 label $top.flab -text "Output file:"
3363 entry $top.fname -width 60
3364 $top.fname insert 0 [file normalize "patch$patchnum.patch"]
3366 grid $top.flab $top.fname -sticky w
3368 button $top.buts.gen -text "Generate" -command mkpatchgo
3369 button $top.buts.can -text "Cancel" -command mkpatchcan
3370 grid $top.buts.gen $top.buts.can
3371 grid columnconfigure $top.buts 0 -weight 1 -uniform a
3372 grid columnconfigure $top.buts 1 -weight 1 -uniform a
3373 grid $top.buts - -pady 10 -sticky ew
3377 proc mkpatchrev {} {
3380 set oldid [$patchtop.fromsha1 get]
3381 set oldhead [$patchtop.fromhead get]
3382 set newid [$patchtop.tosha1 get]
3383 set newhead [$patchtop.tohead get]
3384 foreach e [list fromsha1 fromhead tosha1 tohead] \
3385 v [list $newid $newhead $oldid $oldhead] {
3386 $patchtop.$e conf -state normal
3387 $patchtop.$e delete 0 end
3388 $patchtop.$e insert 0 $v
3389 $patchtop.$e conf -state readonly
3396 set oldid [$patchtop.fromsha1 get]
3397 set newid [$patchtop.tosha1 get]
3398 set fname [$patchtop.fname get]
3399 if {[catch {exec git-diff-tree -p $oldid $newid >$fname &} err]} {
3400 error_popup "Error creating patch: $err"
3402 catch {destroy $patchtop}
3406 proc mkpatchcan {} {
3409 catch {destroy $patchtop}
3414 global rowmenuid mktagtop commitinfo
3418 catch {destroy $top}
3420 label $top.title -text "Create tag"
3421 grid $top.title - -pady 10
3422 label $top.id -text "ID:"
3423 entry $top.sha1 -width 40 -relief flat
3424 $top.sha1 insert 0 $rowmenuid
3425 $top.sha1 conf -state readonly
3426 grid $top.id $top.sha1 -sticky w
3427 entry $top.head -width 60 -relief flat
3428 $top.head insert 0 [lindex $commitinfo($rowmenuid) 0]
3429 $top.head conf -state readonly
3430 grid x $top.head -sticky w
3431 label $top.tlab -text "Tag name:"
3432 entry $top.tag -width 60
3433 grid $top.tlab $top.tag -sticky w
3435 button $top.buts.gen -text "Create" -command mktaggo
3436 button $top.buts.can -text "Cancel" -command mktagcan
3437 grid $top.buts.gen $top.buts.can
3438 grid columnconfigure $top.buts 0 -weight 1 -uniform a
3439 grid columnconfigure $top.buts 1 -weight 1 -uniform a
3440 grid $top.buts - -pady 10 -sticky ew
3445 global mktagtop env tagids idtags
3447 set id [$mktagtop.sha1 get]
3448 set tag [$mktagtop.tag get]
3450 error_popup "No tag name specified"
3453 if {[info exists tagids($tag)]} {
3454 error_popup "Tag \"$tag\" already exists"
3459 set fname [file join $dir "refs/tags" $tag]
3460 set f [open $fname w]
3464 error_popup "Error creating tag: $err"
3468 set tagids($tag) $id
3469 lappend idtags($id) $tag
3473 proc redrawtags {id} {
3474 global canv linehtag idline idpos selectedline
3476 if {![info exists idline($id)]} return
3477 $canv delete tag.$id
3478 set xt [eval drawtags $id $idpos($id)]
3479 $canv coords $linehtag($idline($id)) $xt [lindex $idpos($id) 2]
3480 if {[info exists selectedline] && $selectedline == $idline($id)} {
3481 selectline $selectedline 0
3488 catch {destroy $mktagtop}
3497 proc writecommit {} {
3498 global rowmenuid wrcomtop commitinfo wrcomcmd
3500 set top .writecommit
3502 catch {destroy $top}
3504 label $top.title -text "Write commit to file"
3505 grid $top.title - -pady 10
3506 label $top.id -text "ID:"
3507 entry $top.sha1 -width 40 -relief flat
3508 $top.sha1 insert 0 $rowmenuid
3509 $top.sha1 conf -state readonly
3510 grid $top.id $top.sha1 -sticky w
3511 entry $top.head -width 60 -relief flat
3512 $top.head insert 0 [lindex $commitinfo($rowmenuid) 0]
3513 $top.head conf -state readonly
3514 grid x $top.head -sticky w
3515 label $top.clab -text "Command:"
3516 entry $top.cmd -width 60 -textvariable wrcomcmd
3517 grid $top.clab $top.cmd -sticky w -pady 10
3518 label $top.flab -text "Output file:"
3519 entry $top.fname -width 60
3520 $top.fname insert 0 [file normalize "commit-[string range $rowmenuid 0 6]"]
3521 grid $top.flab $top.fname -sticky w
3523 button $top.buts.gen -text "Write" -command wrcomgo
3524 button $top.buts.can -text "Cancel" -command wrcomcan
3525 grid $top.buts.gen $top.buts.can
3526 grid columnconfigure $top.buts 0 -weight 1 -uniform a
3527 grid columnconfigure $top.buts 1 -weight 1 -uniform a
3528 grid $top.buts - -pady 10 -sticky ew
3535 set id [$wrcomtop.sha1 get]
3536 set cmd "echo $id | [$wrcomtop.cmd get]"
3537 set fname [$wrcomtop.fname get]
3538 if {[catch {exec sh -c $cmd >$fname &} err]} {
3539 error_popup "Error writing commit: $err"
3541 catch {destroy $wrcomtop}
3548 catch {destroy $wrcomtop}
3552 proc listrefs {id} {
3553 global idtags idheads idotherrefs
3556 if {[info exists idtags($id)]} {
3560 if {[info exists idheads($id)]} {
3564 if {[info exists idotherrefs($id)]} {
3565 set z $idotherrefs($id)
3567 return [list $x $y $z]
3570 proc rereadrefs {} {
3571 global idtags idheads idotherrefs
3572 global tagids headids otherrefids
3574 set refids [concat [array names idtags] \
3575 [array names idheads] [array names idotherrefs]]
3576 foreach id $refids {
3577 if {![info exists ref($id)]} {
3578 set ref($id) [listrefs $id]
3581 foreach v {tagids idtags headids idheads otherrefids idotherrefs} {
3585 set refids [lsort -unique [concat $refids [array names idtags] \
3586 [array names idheads] [array names idotherrefs]]]
3587 foreach id $refids {
3588 set v [listrefs $id]
3589 if {![info exists ref($id)] || $ref($id) != $v} {
3595 proc showtag {tag isnew} {
3596 global ctext cflist tagcontents tagids linknum
3599 addtohistory [list showtag $tag 0]
3601 $ctext conf -state normal
3602 $ctext delete 0.0 end
3604 if {[info exists tagcontents($tag)]} {
3605 set text $tagcontents($tag)
3607 set text "Tag: $tag\nId: $tagids($tag)"
3609 appendwithlinks $text
3610 $ctext conf -state disabled
3611 $cflist delete 0 end
3621 global maxwidth maxgraphpct diffopts findmergefiles
3622 global oldprefs prefstop
3626 if {[winfo exists $top]} {
3630 foreach v {maxwidth maxgraphpct diffopts findmergefiles} {
3631 set oldprefs($v) [set $v]
3634 wm title $top "Gitk preferences"
3635 label $top.ldisp -text "Commit list display options"
3636 grid $top.ldisp - -sticky w -pady 10
3637 label $top.spacer -text " "
3638 label $top.maxwidthl -text "Maximum graph width (lines)" \
3640 spinbox $top.maxwidth -from 0 -to 100 -width 4 -textvariable maxwidth
3641 grid $top.spacer $top.maxwidthl $top.maxwidth -sticky w
3642 label $top.maxpctl -text "Maximum graph width (% of pane)" \
3644 spinbox $top.maxpct -from 1 -to 100 -width 4 -textvariable maxgraphpct
3645 grid x $top.maxpctl $top.maxpct -sticky w
3646 checkbutton $top.findm -variable findmergefiles
3647 label $top.findml -text "Include merges for \"Find\" in \"Files\"" \
3649 grid $top.findm $top.findml - -sticky w
3650 label $top.ddisp -text "Diff display options"
3651 grid $top.ddisp - -sticky w -pady 10
3652 label $top.diffoptl -text "Options for diff program" \
3654 entry $top.diffopt -width 20 -textvariable diffopts
3655 grid x $top.diffoptl $top.diffopt -sticky w
3657 button $top.buts.ok -text "OK" -command prefsok
3658 button $top.buts.can -text "Cancel" -command prefscan
3659 grid $top.buts.ok $top.buts.can
3660 grid columnconfigure $top.buts 0 -weight 1 -uniform a
3661 grid columnconfigure $top.buts 1 -weight 1 -uniform a
3662 grid $top.buts - - -pady 10 -sticky ew
3666 global maxwidth maxgraphpct diffopts findmergefiles
3667 global oldprefs prefstop
3669 foreach v {maxwidth maxgraphpct diffopts findmergefiles} {
3670 set $v $oldprefs($v)
3672 catch {destroy $prefstop}
3677 global maxwidth maxgraphpct
3678 global oldprefs prefstop
3680 catch {destroy $prefstop}
3682 if {$maxwidth != $oldprefs(maxwidth)
3683 || $maxgraphpct != $oldprefs(maxgraphpct)} {
3688 proc formatdate {d} {
3689 return [clock format $d -format "%Y-%m-%d %H:%M:%S"]
3694 set diffopts "-U 5 -p"
3695 set wrcomcmd "git-diff-tree --stdin -p --pretty"
3699 set gitencoding [exec git-repo-config --get i18n.commitencoding]
3701 if {$gitencoding == ""} {
3702 set gitencoding "utf-8"
3705 set mainfont {Helvetica 9}
3706 set textfont {Courier 9}
3707 set findmergefiles 0
3713 set colors {green red blue magenta darkgrey brown orange}
3715 catch {source ~/.gitk}
3717 set namefont $mainfont
3719 font create optionfont -family sans-serif -size -12
3723 switch -regexp -- $arg {
3725 "^-d" { set datemode 1 }
3726 "^-r" { set revlistorder 1 }
3728 lappend revtreeargs $arg
3743 getcommits $revtreeargs