hgk
3654 lines
| 95.3 KiB
| text/plain
|
TextLexer
/ contrib / hgk
|
r1361 | #!/usr/bin/env wish | ||
|
r267 | |||
# Copyright (C) 2005 Paul Mackerras. All rights reserved. | ||||
# This program is free software; it may be used, copied, modified | ||||
# and distributed under the terms of the GNU General Public Licence, | ||||
# either version 2, or (at your option) any later version. | ||||
|
r1240 | proc gitdir {} { | ||
global env | ||||
if {[info exists env(GIT_DIR)]} { | ||||
return $env(GIT_DIR) | ||||
} else { | ||||
return ".hg" | ||||
|
r267 | } | ||
|
r1240 | } | ||
proc getcommits {rargs} { | ||||
global commits commfd phase canv mainfont env | ||||
global startmsecs nextupdate ncmupdate | ||||
global ctext maincursor textcursor leftover | ||||
# check that we can find a .git directory somewhere... | ||||
set gitdir [gitdir] | ||||
if {![file isdirectory $gitdir]} { | ||||
error_popup "Cannot find the git directory \"$gitdir\"." | ||||
|
r267 | exit 1 | ||
} | ||||
|
r1240 | set commits {} | ||
set phase getcommits | ||||
set startmsecs [clock clicks -milliseconds] | ||||
set nextupdate [expr $startmsecs + 100] | ||||
set ncmupdate 1 | ||||
if [catch { | ||||
set parse_args [concat --default HEAD $rargs] | ||||
|
r1278 | set parsed_args [split [eval exec hg debug-rev-parse $parse_args] "\n"] | ||
|
r1240 | }] { | ||
# if git-rev-parse failed for some reason... | ||||
if {$rargs == {}} { | ||||
set rargs HEAD | ||||
} | ||||
set parsed_args $rargs | ||||
} | ||||
if [catch { | ||||
|
r1278 | set commfd [open "|hg debug-rev-list --header --topo-order --parents $parsed_args" r] | ||
|
r1240 | } err] { | ||
|
r1278 | puts stderr "Error executing hg debug-rev-list: $err" | ||
|
r1240 | exit 1 | ||
} | ||||
set leftover {} | ||||
fconfigure $commfd -blocking 0 -translation lf | ||||
fileevent $commfd readable [list getcommitlines $commfd] | ||||
|
r267 | $canv delete all | ||
|
r1240 | $canv create text 3 3 -anchor nw -text "Reading commits..." \ | ||
|
r267 | -font $mainfont -tags textitems | ||
|
r1240 | . config -cursor watch | ||
settextcursor watch | ||||
|
r267 | } | ||
|
r1240 | proc getcommitlines {commfd} { | ||
global commits parents cdate children | ||||
global commitlisted phase commitinfo nextupdate | ||||
global stopped redisplaying leftover | ||||
set stuff [read $commfd] | ||||
if {$stuff == {}} { | ||||
|
r267 | if {![eof $commfd]} return | ||
|
r1240 | # set it blocking so we wait for the process to terminate | ||
|
r267 | fconfigure $commfd -blocking 1 | ||
if {![catch {close $commfd} err]} { | ||||
|
r1240 | after idle finishcommits | ||
|
r267 | return | ||
} | ||||
if {[string range $err 0 4] == "usage"} { | ||||
|
r1240 | set err \ | ||
{Gitk: error reading commits: bad arguments to git-rev-list. | ||||
(Note: arguments to gitk are passed to git-rev-list | ||||
to allow selection of commits to be displayed.)} | ||||
|
r267 | } else { | ||
set err "Error reading commits: $err" | ||||
} | ||||
error_popup $err | ||||
exit 1 | ||||
} | ||||
|
r1240 | set start 0 | ||
while 1 { | ||||
set i [string first "\0" $stuff $start] | ||||
if {$i < 0} { | ||||
append leftover [string range $stuff $start end] | ||||
return | ||||
} | ||||
set cmit [string range $stuff $start [expr {$i - 1}]] | ||||
if {$start == 0} { | ||||
set cmit "$leftover$cmit" | ||||
set leftover {} | ||||
} | ||||
set start [expr {$i + 1}] | ||||
set j [string first "\n" $cmit] | ||||
set ok 0 | ||||
if {$j >= 0} { | ||||
set ids [string range $cmit 0 [expr {$j - 1}]] | ||||
set ok 1 | ||||
foreach id $ids { | ||||
if {![regexp {^[0-9a-f]{40}$} $id]} { | ||||
set ok 0 | ||||
break | ||||
} | ||||
} | ||||
} | ||||
if {!$ok} { | ||||
set shortcmit $cmit | ||||
if {[string length $shortcmit] > 80} { | ||||
set shortcmit "[string range $shortcmit 0 80]..." | ||||
} | ||||
|
r1278 | error_popup "Can't parse hg debug-rev-list output: {$shortcmit}" | ||
|
r1240 | exit 1 | ||
} | ||||
set id [lindex $ids 0] | ||||
set olds [lrange $ids 1 end] | ||||
set cmit [string range $cmit [expr {$j + 1}] end] | ||||
lappend commits $id | ||||
set commitlisted($id) 1 | ||||
parsecommit $id $cmit 1 [lrange $ids 1 end] | ||||
drawcommit $id | ||||
if {[clock clicks -milliseconds] >= $nextupdate} { | ||||
doupdate 1 | ||||
} | ||||
while {$redisplaying} { | ||||
set redisplaying 0 | ||||
if {$stopped == 1} { | ||||
set stopped 0 | ||||
set phase "getcommits" | ||||
foreach id $commits { | ||||
drawcommit $id | ||||
if {$stopped} break | ||||
if {[clock clicks -milliseconds] >= $nextupdate} { | ||||
doupdate 1 | ||||
} | ||||
} | ||||
} | ||||
} | ||||
|
r267 | } | ||
} | ||||
|
r1240 | proc doupdate {reading} { | ||
global commfd nextupdate numcommits ncmupdate | ||||
if {$reading} { | ||||
fileevent $commfd readable {} | ||||
|
r267 | } | ||
|
r1240 | update | ||
set nextupdate [expr {[clock clicks -milliseconds] + 100}] | ||||
if {$numcommits < 100} { | ||||
set ncmupdate [expr {$numcommits + 1}] | ||||
} elseif {$numcommits < 10000} { | ||||
set ncmupdate [expr {$numcommits + 10}] | ||||
} else { | ||||
set ncmupdate [expr {$numcommits + 100}] | ||||
} | ||||
if {$reading} { | ||||
fileevent $commfd readable [list getcommitlines $commfd] | ||||
} | ||||
|
r267 | } | ||
|
r1240 | proc readcommit {id} { | ||
|
r1278 | if [catch {set contents [exec hg debug-cat-file commit $id]}] return | ||
|
r1240 | parsecommit $id $contents 0 {} | ||
} | ||||
proc parsecommit {id contents listed olds} { | ||||
global commitinfo children nchildren parents nparents cdate ncleft | ||||
|
r267 | set inhdr 1 | ||
set comment {} | ||||
set headline {} | ||||
set auname {} | ||||
set audate {} | ||||
set comname {} | ||||
set comdate {} | ||||
if {![info exists nchildren($id)]} { | ||||
set children($id) {} | ||||
set nchildren($id) 0 | ||||
|
r1240 | set ncleft($id) 0 | ||
|
r267 | } | ||
|
r1240 | set parents($id) $olds | ||
set nparents($id) [llength $olds] | ||||
foreach p $olds { | ||||
if {![info exists nchildren($p)]} { | ||||
set children($p) [list $id] | ||||
set nchildren($p) 1 | ||||
set ncleft($p) 1 | ||||
} elseif {[lsearch -exact $children($p) $id] < 0} { | ||||
lappend children($p) $id | ||||
incr nchildren($p) | ||||
incr ncleft($p) | ||||
} | ||||
} | ||||
|
r267 | foreach line [split $contents "\n"] { | ||
if {$inhdr} { | ||||
|
r2297 | set line [split $line] | ||
|
r267 | if {$line == {}} { | ||
set inhdr 0 | ||||
} else { | ||||
set tag [lindex $line 0] | ||||
|
r1240 | if {$tag == "author"} { | ||
|
r267 | set x [expr {[llength $line] - 2}] | ||
set audate [lindex $line $x] | ||||
|
r2297 | set auname [join [lrange $line 1 [expr {$x - 1}]]] | ||
|
r267 | } elseif {$tag == "committer"} { | ||
set x [expr {[llength $line] - 2}] | ||||
set comdate [lindex $line $x] | ||||
|
r2297 | set comname [join [lrange $line 1 [expr {$x - 1}]]] | ||
|
r267 | } | ||
} | ||||
} else { | ||||
if {$comment == {}} { | ||||
|
r1240 | set headline [string trim $line] | ||
|
r267 | } else { | ||
append comment "\n" | ||||
} | ||||
|
r1240 | if {!$listed} { | ||
# git-rev-list indents the comment by 4 spaces; | ||||
# if we got this via git-cat-file, add the indentation | ||||
append comment " " | ||||
} | ||||
|
r267 | append comment $line | ||
} | ||||
} | ||||
if {$audate != {}} { | ||||
set audate [clock format $audate -format "%Y-%m-%d %H:%M:%S"] | ||||
} | ||||
if {$comdate != {}} { | ||||
set cdate($id) $comdate | ||||
set comdate [clock format $comdate -format "%Y-%m-%d %H:%M:%S"] | ||||
} | ||||
set commitinfo($id) [list $headline $auname $audate \ | ||||
$comname $comdate $comment] | ||||
} | ||||
|
r1240 | proc readrefs {} { | ||
global tagids idtags headids idheads tagcontents | ||||
set tags [exec hg tags] | ||||
set lines [split $tags '\n'] | ||||
foreach f $lines { | ||||
set f [regexp -all -inline {\S+} $f] | ||||
set direct [lindex $f 0] | ||||
set full [lindex $f 1] | ||||
set sha [split $full ':'] | ||||
set tag [lindex $sha 1] | ||||
lappend tagids($direct) $tag | ||||
lappend idtags($tag) $direct | ||||
|
r267 | } | ||
} | ||||
|
r1240 | proc readotherrefs {base dname excl} { | ||
global otherrefids idotherrefs | ||||
|
r267 | |||
|
r1240 | set git [gitdir] | ||
set files [glob -nocomplain -types f [file join $git $base *]] | ||||
foreach f $files { | ||||
|
r267 | catch { | ||
set fd [open $f r] | ||||
|
r1240 | set line [read $fd 40] | ||
|
r267 | if {[regexp {^[0-9a-f]{40}} $line id]} { | ||
|
r1240 | set name "$dname[file tail $f]" | ||
set otherrefids($name) $id | ||||
lappend idotherrefs($id) $name | ||||
|
r267 | } | ||
|
r1240 | close $fd | ||
|
r267 | } | ||
} | ||||
|
r1240 | set dirs [glob -nocomplain -types d [file join $git $base *]] | ||
foreach d $dirs { | ||||
set dir [file tail $d] | ||||
if {[lsearch -exact $excl $dir] >= 0} continue | ||||
readotherrefs [file join $base $dir] "$dname$dir/" {} | ||||
} | ||||
|
r267 | } | ||
proc error_popup msg { | ||||
set w .error | ||||
toplevel $w | ||||
wm transient $w . | ||||
message $w.m -text $msg -justify center -aspect 400 | ||||
pack $w.m -side top -fill x -padx 20 -pady 20 | ||||
button $w.ok -text OK -command "destroy $w" | ||||
pack $w.ok -side bottom -fill x | ||||
bind $w <Visibility> "grab $w; focus $w" | ||||
tkwait window $w | ||||
} | ||||
proc makewindow {} { | ||||
global canv canv2 canv3 linespc charspc ctext cflist textfont | ||||
|
r1240 | global findtype findtypemenu findloc findstring fstring geometry | ||
|
r267 | global entries sha1entry sha1string sha1but | ||
|
r1240 | global maincursor textcursor curtextcursor | ||
global rowctxmenu gaudydiff mergemax | ||||
|
r267 | |||
menu .bar | ||||
.bar add cascade -label "File" -menu .bar.file | ||||
menu .bar.file | ||||
|
r1240 | .bar.file add command -label "Reread references" -command rereadrefs | ||
|
r267 | .bar.file add command -label "Quit" -command doquit | ||
menu .bar.help | ||||
.bar add cascade -label "Help" -menu .bar.help | ||||
.bar.help add command -label "About gitk" -command about | ||||
. configure -menu .bar | ||||
if {![info exists geometry(canv1)]} { | ||||
set geometry(canv1) [expr 45 * $charspc] | ||||
set geometry(canv2) [expr 30 * $charspc] | ||||
set geometry(canv3) [expr 15 * $charspc] | ||||
set geometry(canvh) [expr 25 * $linespc + 4] | ||||
set geometry(ctextw) 80 | ||||
set geometry(ctexth) 30 | ||||
set geometry(cflistw) 30 | ||||
} | ||||
panedwindow .ctop -orient vertical | ||||
if {[info exists geometry(width)]} { | ||||
.ctop conf -width $geometry(width) -height $geometry(height) | ||||
set texth [expr {$geometry(height) - $geometry(canvh) - 56}] | ||||
set geometry(ctexth) [expr {($texth - 8) / | ||||
[font metrics $textfont -linespace]}] | ||||
} | ||||
frame .ctop.top | ||||
frame .ctop.top.bar | ||||
pack .ctop.top.bar -side bottom -fill x | ||||
set cscroll .ctop.top.csb | ||||
scrollbar $cscroll -command {allcanvs yview} -highlightthickness 0 | ||||
pack $cscroll -side right -fill y | ||||
panedwindow .ctop.top.clist -orient horizontal -sashpad 0 -handlesize 4 | ||||
pack .ctop.top.clist -side top -fill both -expand 1 | ||||
.ctop add .ctop.top | ||||
set canv .ctop.top.clist.canv | ||||
canvas $canv -height $geometry(canvh) -width $geometry(canv1) \ | ||||
-bg white -bd 0 \ | ||||
-yscrollincr $linespc -yscrollcommand "$cscroll set" | ||||
.ctop.top.clist add $canv | ||||
set canv2 .ctop.top.clist.canv2 | ||||
canvas $canv2 -height $geometry(canvh) -width $geometry(canv2) \ | ||||
-bg white -bd 0 -yscrollincr $linespc | ||||
.ctop.top.clist add $canv2 | ||||
set canv3 .ctop.top.clist.canv3 | ||||
canvas $canv3 -height $geometry(canvh) -width $geometry(canv3) \ | ||||
-bg white -bd 0 -yscrollincr $linespc | ||||
.ctop.top.clist add $canv3 | ||||
bind .ctop.top.clist <Configure> {resizeclistpanes %W %w} | ||||
set sha1entry .ctop.top.bar.sha1 | ||||
set entries $sha1entry | ||||
set sha1but .ctop.top.bar.sha1label | ||||
button $sha1but -text "SHA1 ID: " -state disabled -relief flat \ | ||||
-command gotocommit -width 8 | ||||
$sha1but conf -disabledforeground [$sha1but cget -foreground] | ||||
pack .ctop.top.bar.sha1label -side left | ||||
entry $sha1entry -width 40 -font $textfont -textvariable sha1string | ||||
trace add variable sha1string write sha1change | ||||
pack $sha1entry -side left -pady 2 | ||||
|
r1240 | |||
image create bitmap bm-left -data { | ||||
#define left_width 16 | ||||
#define left_height 16 | ||||
static unsigned char left_bits[] = { | ||||
0x00, 0x00, 0xc0, 0x01, 0xe0, 0x00, 0x70, 0x00, 0x38, 0x00, 0x1c, 0x00, | ||||
0x0e, 0x00, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0x0e, 0x00, 0x1c, 0x00, | ||||
0x38, 0x00, 0x70, 0x00, 0xe0, 0x00, 0xc0, 0x01}; | ||||
} | ||||
image create bitmap bm-right -data { | ||||
#define right_width 16 | ||||
#define right_height 16 | ||||
static unsigned char right_bits[] = { | ||||
0x00, 0x00, 0xc0, 0x01, 0x80, 0x03, 0x00, 0x07, 0x00, 0x0e, 0x00, 0x1c, | ||||
0x00, 0x38, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0x00, 0x38, 0x00, 0x1c, | ||||
0x00, 0x0e, 0x00, 0x07, 0x80, 0x03, 0xc0, 0x01}; | ||||
} | ||||
button .ctop.top.bar.leftbut -image bm-left -command goback \ | ||||
-state disabled -width 26 | ||||
pack .ctop.top.bar.leftbut -side left -fill y | ||||
button .ctop.top.bar.rightbut -image bm-right -command goforw \ | ||||
-state disabled -width 26 | ||||
pack .ctop.top.bar.rightbut -side left -fill y | ||||
|
r267 | button .ctop.top.bar.findbut -text "Find" -command dofind | ||
pack .ctop.top.bar.findbut -side left | ||||
set findstring {} | ||||
set fstring .ctop.top.bar.findstring | ||||
lappend entries $fstring | ||||
entry $fstring -width 30 -font $textfont -textvariable findstring | ||||
pack $fstring -side left -expand 1 -fill x | ||||
set findtype Exact | ||||
|
r1240 | set findtypemenu [tk_optionMenu .ctop.top.bar.findtype \ | ||
findtype Exact IgnCase Regexp] | ||||
|
r267 | set findloc "All fields" | ||
tk_optionMenu .ctop.top.bar.findloc findloc "All fields" Headline \ | ||||
|
r1240 | Comments Author Committer Files Pickaxe | ||
|
r267 | pack .ctop.top.bar.findloc -side right | ||
pack .ctop.top.bar.findtype -side right | ||||
|
r1240 | # for making sure type==Exact whenever loc==Pickaxe | ||
trace add variable findloc write findlocchange | ||||
|
r267 | |||
panedwindow .ctop.cdet -orient horizontal | ||||
.ctop add .ctop.cdet | ||||
frame .ctop.cdet.left | ||||
set ctext .ctop.cdet.left.ctext | ||||
text $ctext -bg white -state disabled -font $textfont \ | ||||
-width $geometry(ctextw) -height $geometry(ctexth) \ | ||||
|
r1430 | -yscrollcommand ".ctop.cdet.left.sb set" \ | ||
-xscrollcommand ".ctop.cdet.left.hb set" -wrap none | ||||
|
r267 | scrollbar .ctop.cdet.left.sb -command "$ctext yview" | ||
|
r1430 | scrollbar .ctop.cdet.left.hb -orient horizontal -command "$ctext xview" | ||
|
r267 | pack .ctop.cdet.left.sb -side right -fill y | ||
|
r1430 | pack .ctop.cdet.left.hb -side bottom -fill x | ||
|
r267 | pack $ctext -side left -fill both -expand 1 | ||
.ctop.cdet add .ctop.cdet.left | ||||
|
r1240 | $ctext tag conf filesep -font [concat $textfont bold] -back "#aaaaaa" | ||
if {$gaudydiff} { | ||||
$ctext tag conf hunksep -back blue -fore white | ||||
$ctext tag conf d0 -back "#ff8080" | ||||
$ctext tag conf d1 -back green | ||||
} else { | ||||
$ctext tag conf hunksep -fore blue | ||||
$ctext tag conf d0 -fore red | ||||
$ctext tag conf d1 -fore "#00a000" | ||||
$ctext tag conf m0 -fore red | ||||
$ctext tag conf m1 -fore blue | ||||
$ctext tag conf m2 -fore green | ||||
$ctext tag conf m3 -fore purple | ||||
$ctext tag conf m4 -fore brown | ||||
$ctext tag conf mmax -fore darkgrey | ||||
set mergemax 5 | ||||
$ctext tag conf mresult -font [concat $textfont bold] | ||||
$ctext tag conf msep -font [concat $textfont bold] | ||||
$ctext tag conf found -back yellow | ||||
} | ||||
|
r267 | |||
frame .ctop.cdet.right | ||||
set cflist .ctop.cdet.right.cfiles | ||||
listbox $cflist -bg white -selectmode extended -width $geometry(cflistw) \ | ||||
-yscrollcommand ".ctop.cdet.right.sb set" | ||||
scrollbar .ctop.cdet.right.sb -command "$cflist yview" | ||||
pack .ctop.cdet.right.sb -side right -fill y | ||||
pack $cflist -side left -fill both -expand 1 | ||||
.ctop.cdet add .ctop.cdet.right | ||||
bind .ctop.cdet <Configure> {resizecdetpanes %W %w} | ||||
pack .ctop -side top -fill both -expand 1 | ||||
|
r1240 | bindall <1> {selcanvline %W %x %y} | ||
#bindall <B1-Motion> {selcanvline %W %x %y} | ||||
|
r267 | bindall <ButtonRelease-4> "allcanvs yview scroll -5 units" | ||
bindall <ButtonRelease-5> "allcanvs yview scroll 5 units" | ||||
bindall <2> "allcanvs scan mark 0 %y" | ||||
bindall <B2-Motion> "allcanvs scan dragto 0 %y" | ||||
bind . <Key-Up> "selnextline -1" | ||||
bind . <Key-Down> "selnextline 1" | ||||
bind . <Key-Prior> "allcanvs yview scroll -1 pages" | ||||
bind . <Key-Next> "allcanvs yview scroll 1 pages" | ||||
bindkey <Key-Delete> "$ctext yview scroll -1 pages" | ||||
bindkey <Key-BackSpace> "$ctext yview scroll -1 pages" | ||||
bindkey <Key-space> "$ctext yview scroll 1 pages" | ||||
bindkey p "selnextline -1" | ||||
bindkey n "selnextline 1" | ||||
bindkey b "$ctext yview scroll -1 pages" | ||||
bindkey d "$ctext yview scroll 18 units" | ||||
bindkey u "$ctext yview scroll -18 units" | ||||
|
r1240 | bindkey / {findnext 1} | ||
bindkey <Key-Return> {findnext 0} | ||||
|
r267 | bindkey ? findprev | ||
bindkey f nextfile | ||||
bind . <Control-q> doquit | ||||
|
r1429 | bind . <Control-w> doquit | ||
|
r267 | bind . <Control-f> dofind | ||
|
r1240 | bind . <Control-g> {findnext 0} | ||
|
r267 | bind . <Control-r> findprev | ||
bind . <Control-equal> {incrfont 1} | ||||
bind . <Control-KP_Add> {incrfont 1} | ||||
bind . <Control-minus> {incrfont -1} | ||||
bind . <Control-KP_Subtract> {incrfont -1} | ||||
bind $cflist <<ListboxSelect>> listboxsel | ||||
bind . <Destroy> {savestuff %W} | ||||
bind . <Button-1> "click %W" | ||||
bind $fstring <Key-Return> dofind | ||||
bind $sha1entry <Key-Return> gotocommit | ||||
|
r1240 | bind $sha1entry <<PasteSelection>> clearsha1 | ||
set maincursor [. cget -cursor] | ||||
set textcursor [$ctext cget -cursor] | ||||
set curtextcursor $textcursor | ||||
set rowctxmenu .rowctxmenu | ||||
menu $rowctxmenu -tearoff 0 | ||||
$rowctxmenu add command -label "Diff this -> selected" \ | ||||
-command {diffvssel 0} | ||||
$rowctxmenu add command -label "Diff selected -> this" \ | ||||
-command {diffvssel 1} | ||||
$rowctxmenu add command -label "Make patch" -command mkpatch | ||||
$rowctxmenu add command -label "Create tag" -command mktag | ||||
$rowctxmenu add command -label "Write commit to file" -command writecommit | ||||
|
r267 | } | ||
# when we make a key binding for the toplevel, make sure | ||||
# it doesn't get triggered when that key is pressed in the | ||||
# find string entry widget. | ||||
proc bindkey {ev script} { | ||||
global entries | ||||
bind . $ev $script | ||||
set escript [bind Entry $ev] | ||||
if {$escript == {}} { | ||||
set escript [bind Entry <Key>] | ||||
} | ||||
foreach e $entries { | ||||
bind $e $ev "$escript; break" | ||||
} | ||||
} | ||||
# set the focus back to the toplevel for any click outside | ||||
# the entry widgets | ||||
proc click {w} { | ||||
global entries | ||||
foreach e $entries { | ||||
if {$w == $e} return | ||||
} | ||||
focus . | ||||
} | ||||
proc savestuff {w} { | ||||
global canv canv2 canv3 ctext cflist mainfont textfont | ||||
|
r1240 | global stuffsaved findmergefiles gaudydiff maxgraphpct | ||
global maxwidth | ||||
|
r267 | if {$stuffsaved} return | ||
if {![winfo viewable .]} return | ||||
catch { | ||||
set f [open "~/.gitk-new" w] | ||||
|
r1240 | puts $f [list set mainfont $mainfont] | ||
puts $f [list set textfont $textfont] | ||||
puts $f [list set findmergefiles $findmergefiles] | ||||
puts $f [list set gaudydiff $gaudydiff] | ||||
puts $f [list set maxgraphpct $maxgraphpct] | ||||
puts $f [list set maxwidth $maxwidth] | ||||
|
r267 | puts $f "set geometry(width) [winfo width .ctop]" | ||
puts $f "set geometry(height) [winfo height .ctop]" | ||||
puts $f "set geometry(canv1) [expr [winfo width $canv]-2]" | ||||
puts $f "set geometry(canv2) [expr [winfo width $canv2]-2]" | ||||
puts $f "set geometry(canv3) [expr [winfo width $canv3]-2]" | ||||
puts $f "set geometry(canvh) [expr [winfo height $canv]-2]" | ||||
set wid [expr {([winfo width $ctext] - 8) \ | ||||
/ [font measure $textfont "0"]}] | ||||
puts $f "set geometry(ctextw) $wid" | ||||
set wid [expr {([winfo width $cflist] - 11) \ | ||||
/ [font measure [$cflist cget -font] "0"]}] | ||||
puts $f "set geometry(cflistw) $wid" | ||||
close $f | ||||
file rename -force "~/.gitk-new" "~/.gitk" | ||||
} | ||||
set stuffsaved 1 | ||||
} | ||||
proc resizeclistpanes {win w} { | ||||
global oldwidth | ||||
if [info exists oldwidth($win)] { | ||||
set s0 [$win sash coord 0] | ||||
set s1 [$win sash coord 1] | ||||
if {$w < 60} { | ||||
set sash0 [expr {int($w/2 - 2)}] | ||||
set sash1 [expr {int($w*5/6 - 2)}] | ||||
} else { | ||||
set factor [expr {1.0 * $w / $oldwidth($win)}] | ||||
set sash0 [expr {int($factor * [lindex $s0 0])}] | ||||
set sash1 [expr {int($factor * [lindex $s1 0])}] | ||||
if {$sash0 < 30} { | ||||
set sash0 30 | ||||
} | ||||
if {$sash1 < $sash0 + 20} { | ||||
set sash1 [expr $sash0 + 20] | ||||
} | ||||
if {$sash1 > $w - 10} { | ||||
set sash1 [expr $w - 10] | ||||
if {$sash0 > $sash1 - 20} { | ||||
set sash0 [expr $sash1 - 20] | ||||
} | ||||
} | ||||
} | ||||
$win sash place 0 $sash0 [lindex $s0 1] | ||||
$win sash place 1 $sash1 [lindex $s1 1] | ||||
} | ||||
set oldwidth($win) $w | ||||
} | ||||
proc resizecdetpanes {win w} { | ||||
global oldwidth | ||||
if [info exists oldwidth($win)] { | ||||
set s0 [$win sash coord 0] | ||||
if {$w < 60} { | ||||
set sash0 [expr {int($w*3/4 - 2)}] | ||||
} else { | ||||
set factor [expr {1.0 * $w / $oldwidth($win)}] | ||||
set sash0 [expr {int($factor * [lindex $s0 0])}] | ||||
if {$sash0 < 45} { | ||||
set sash0 45 | ||||
} | ||||
if {$sash0 > $w - 15} { | ||||
set sash0 [expr $w - 15] | ||||
} | ||||
} | ||||
$win sash place 0 $sash0 [lindex $s0 1] | ||||
} | ||||
set oldwidth($win) $w | ||||
} | ||||
proc allcanvs args { | ||||
global canv canv2 canv3 | ||||
eval $canv $args | ||||
eval $canv2 $args | ||||
eval $canv3 $args | ||||
} | ||||
proc bindall {event action} { | ||||
global canv canv2 canv3 | ||||
bind $canv $event $action | ||||
bind $canv2 $event $action | ||||
bind $canv3 $event $action | ||||
} | ||||
proc about {} { | ||||
set w .about | ||||
if {[winfo exists $w]} { | ||||
raise $w | ||||
return | ||||
} | ||||
toplevel $w | ||||
wm title $w "About gitk" | ||||
message $w.m -text { | ||||
|
r1240 | Gitk version 1.2 | ||
|
r267 | |||
Copyright � 2005 Paul Mackerras | ||||
|
r1240 | Use and redistribute under the terms of the GNU General Public License} \ | ||
|
r267 | -justify center -aspect 400 | ||
pack $w.m -side top -fill x -padx 20 -pady 20 | ||||
button $w.ok -text Close -command "destroy $w" | ||||
pack $w.ok -side bottom | ||||
} | ||||
proc assigncolor {id} { | ||||
global commitinfo colormap commcolors colors nextcolor | ||||
global parents nparents children nchildren | ||||
|
r1240 | global cornercrossings crossings | ||
|
r267 | if [info exists colormap($id)] return | ||
set ncolors [llength $colors] | ||||
|
r1240 | if {$nparents($id) <= 1 && $nchildren($id) == 1} { | ||
set child [lindex $children($id) 0] | ||||
if {[info exists colormap($child)] | ||||
&& $nparents($child) == 1} { | ||||
set colormap($id) $colormap($child) | ||||
return | ||||
|
r267 | } | ||
|
r1240 | } | ||
set badcolors {} | ||||
if {[info exists cornercrossings($id)]} { | ||||
foreach x $cornercrossings($id) { | ||||
if {[info exists colormap($x)] | ||||
&& [lsearch -exact $badcolors $colormap($x)] < 0} { | ||||
lappend badcolors $colormap($x) | ||||
|
r267 | } | ||
} | ||||
|
r1240 | if {[llength $badcolors] >= $ncolors} { | ||
set badcolors {} | ||||
} | ||||
} | ||||
set origbad $badcolors | ||||
if {[llength $badcolors] < $ncolors - 1} { | ||||
if {[info exists crossings($id)]} { | ||||
foreach x $crossings($id) { | ||||
if {[info exists colormap($x)] | ||||
&& [lsearch -exact $badcolors $colormap($x)] < 0} { | ||||
lappend badcolors $colormap($x) | ||||
} | ||||
} | ||||
if {[llength $badcolors] >= $ncolors} { | ||||
set badcolors $origbad | ||||
|
r267 | } | ||
} | ||||
|
r1240 | set origbad $badcolors | ||
} | ||||
if {[llength $badcolors] < $ncolors - 1} { | ||||
|
r267 | foreach child $children($id) { | ||
if {[info exists colormap($child)] | ||||
&& [lsearch -exact $badcolors $colormap($child)] < 0} { | ||||
lappend badcolors $colormap($child) | ||||
} | ||||
if {[info exists parents($child)]} { | ||||
foreach p $parents($child) { | ||||
if {[info exists colormap($p)] | ||||
&& [lsearch -exact $badcolors $colormap($p)] < 0} { | ||||
lappend badcolors $colormap($p) | ||||
} | ||||
} | ||||
} | ||||
} | ||||
if {[llength $badcolors] >= $ncolors} { | ||||
|
r1240 | set badcolors $origbad | ||
} | ||||
} | ||||
for {set i 0} {$i <= $ncolors} {incr i} { | ||||
set c [lindex $colors $nextcolor] | ||||
if {[incr nextcolor] >= $ncolors} { | ||||
set nextcolor 0 | ||||
|
r267 | } | ||
|
r1240 | if {[lsearch -exact $badcolors $c]} break | ||
} | ||||
set colormap($id) $c | ||||
} | ||||
proc initgraph {} { | ||||
global canvy canvy0 lineno numcommits nextcolor linespc | ||||
global mainline mainlinearrow sidelines | ||||
global nchildren ncleft | ||||
global displist nhyperspace | ||||
allcanvs delete all | ||||
set nextcolor 0 | ||||
set canvy $canvy0 | ||||
set lineno -1 | ||||
set numcommits 0 | ||||
catch {unset mainline} | ||||
catch {unset mainlinearrow} | ||||
catch {unset sidelines} | ||||
foreach id [array names nchildren] { | ||||
set ncleft($id) $nchildren($id) | ||||
} | ||||
set displist {} | ||||
set nhyperspace 0 | ||||
} | ||||
proc bindline {t id} { | ||||
global canv | ||||
$canv bind $t <Enter> "lineenter %x %y $id" | ||||
$canv bind $t <Motion> "linemotion %x %y $id" | ||||
$canv bind $t <Leave> "lineleave $id" | ||||
$canv bind $t <Button-1> "lineclick %x %y $id 1" | ||||
} | ||||
proc drawlines {id xtra} { | ||||
global mainline mainlinearrow sidelines lthickness colormap canv | ||||
$canv delete lines.$id | ||||
if {[info exists mainline($id)]} { | ||||
set t [$canv create line $mainline($id) \ | ||||
-width [expr {($xtra + 1) * $lthickness}] \ | ||||
-fill $colormap($id) -tags lines.$id \ | ||||
-arrow $mainlinearrow($id)] | ||||
$canv lower $t | ||||
bindline $t $id | ||||
} | ||||
if {[info exists sidelines($id)]} { | ||||
foreach ls $sidelines($id) { | ||||
set coords [lindex $ls 0] | ||||
set thick [lindex $ls 1] | ||||
set arrow [lindex $ls 2] | ||||
set t [$canv create line $coords -fill $colormap($id) \ | ||||
-width [expr {($thick + $xtra) * $lthickness}] \ | ||||
-arrow $arrow -tags lines.$id] | ||||
$canv lower $t | ||||
bindline $t $id | ||||
|
r267 | } | ||
} | ||||
} | ||||
|
r1240 | # level here is an index in displist | ||
proc drawcommitline {level} { | ||||
global parents children nparents displist | ||||
global canv canv2 canv3 mainfont namefont canvy linespc | ||||
|
r267 | global lineid linehtag linentag linedtag commitinfo | ||
|
r1240 | global colormap numcommits currentparents dupparents | ||
global idtags idline idheads idotherrefs | ||||
global lineno lthickness mainline mainlinearrow sidelines | ||||
global commitlisted rowtextx idpos lastuse displist | ||||
global oldnlines olddlevel olddisplist | ||||
|
r267 | |||
|
r1240 | incr numcommits | ||
incr lineno | ||||
set id [lindex $displist $level] | ||||
set lastuse($id) $lineno | ||||
set lineid($lineno) $id | ||||
set idline($id) $lineno | ||||
set ofill [expr {[info exists commitlisted($id)]? "blue": "white"}] | ||||
if {![info exists commitinfo($id)]} { | ||||
readcommit $id | ||||
if {![info exists commitinfo($id)]} { | ||||
set commitinfo($id) {"No commit information available"} | ||||
|
r267 | set nparents($id) 0 | ||
} | ||||
} | ||||
|
r1240 | assigncolor $id | ||
set currentparents {} | ||||
set dupparents {} | ||||
if {[info exists commitlisted($id)] && [info exists parents($id)]} { | ||||
foreach p $parents($id) { | ||||
if {[lsearch -exact $currentparents $p] < 0} { | ||||
lappend currentparents $p | ||||
} else { | ||||
# remember that this parent was listed twice | ||||
lappend dupparents $p | ||||
} | ||||
} | ||||
} | ||||
set x [xcoord $level $level $lineno] | ||||
set y1 $canvy | ||||
set canvy [expr $canvy + $linespc] | ||||
allcanvs conf -scrollregion \ | ||||
[list 0 0 0 [expr $y1 + 0.5 * $linespc + 2]] | ||||
if {[info exists mainline($id)]} { | ||||
lappend mainline($id) $x $y1 | ||||
if {$mainlinearrow($id) ne "none"} { | ||||
set mainline($id) [trimdiagstart $mainline($id)] | ||||
} | ||||
} | ||||
drawlines $id 0 | ||||
set orad [expr {$linespc / 3}] | ||||
set t [$canv create oval [expr $x - $orad] [expr $y1 - $orad] \ | ||||
[expr $x + $orad - 1] [expr $y1 + $orad - 1] \ | ||||
-fill $ofill -outline black -width 1] | ||||
$canv raise $t | ||||
$canv bind $t <1> {selcanvline {} %x %y} | ||||
set xt [xcoord [llength $displist] $level $lineno] | ||||
if {[llength $currentparents] > 2} { | ||||
set xt [expr {$xt + ([llength $currentparents] - 2) * $linespc}] | ||||
} | ||||
set rowtextx($lineno) $xt | ||||
set idpos($id) [list $x $xt $y1] | ||||
if {[info exists idtags($id)] || [info exists idheads($id)] | ||||
|| [info exists idotherrefs($id)]} { | ||||
set xt [drawtags $id $x $xt $y1] | ||||
} | ||||
set headline [lindex $commitinfo($id) 0] | ||||
set name [lindex $commitinfo($id) 1] | ||||
set date [lindex $commitinfo($id) 2] | ||||
set linehtag($lineno) [$canv create text $xt $y1 -anchor w \ | ||||
-text $headline -font $mainfont ] | ||||
$canv bind $linehtag($lineno) <Button-3> "rowmenu %X %Y $id" | ||||
set linentag($lineno) [$canv2 create text 3 $y1 -anchor w \ | ||||
-text $name -font $namefont] | ||||
set linedtag($lineno) [$canv3 create text 3 $y1 -anchor w \ | ||||
-text $date -font $mainfont] | ||||
set olddlevel $level | ||||
set olddisplist $displist | ||||
set oldnlines [llength $displist] | ||||
} | ||||
proc drawtags {id x xt y1} { | ||||
global idtags idheads idotherrefs | ||||
global linespc lthickness | ||||
global canv mainfont idline rowtextx | ||||
set marks {} | ||||
set ntags 0 | ||||
set nheads 0 | ||||
if {[info exists idtags($id)]} { | ||||
set marks $idtags($id) | ||||
set ntags [llength $marks] | ||||
} | ||||
if {[info exists idheads($id)]} { | ||||
set marks [concat $marks $idheads($id)] | ||||
set nheads [llength $idheads($id)] | ||||
} | ||||
if {[info exists idotherrefs($id)]} { | ||||
set marks [concat $marks $idotherrefs($id)] | ||||
} | ||||
if {$marks eq {}} { | ||||
return $xt | ||||
} | ||||
set delta [expr {int(0.5 * ($linespc - $lthickness))}] | ||||
set yt [expr $y1 - 0.5 * $linespc] | ||||
set yb [expr $yt + $linespc - 1] | ||||
set xvals {} | ||||
set wvals {} | ||||
foreach tag $marks { | ||||
set wid [font measure $mainfont $tag] | ||||
lappend xvals $xt | ||||
lappend wvals $wid | ||||
set xt [expr {$xt + $delta + $wid + $lthickness + $linespc}] | ||||
} | ||||
set t [$canv create line $x $y1 [lindex $xvals end] $y1 \ | ||||
-width $lthickness -fill black -tags tag.$id] | ||||
$canv lower $t | ||||
foreach tag $marks x $xvals wid $wvals { | ||||
set xl [expr $x + $delta] | ||||
set xr [expr $x + $delta + $wid + $lthickness] | ||||
if {[incr ntags -1] >= 0} { | ||||
# draw a tag | ||||
set t [$canv create polygon $x [expr $yt + $delta] $xl $yt \ | ||||
$xr $yt $xr $yb $xl $yb $x [expr $yb - $delta] \ | ||||
-width 1 -outline black -fill yellow -tags tag.$id] | ||||
$canv bind $t <1> [list showtag $tag 1] | ||||
set rowtextx($idline($id)) [expr {$xr + $linespc}] | ||||
} else { | ||||
# draw a head or other ref | ||||
if {[incr nheads -1] >= 0} { | ||||
set col green | ||||
} else { | ||||
set col "#ddddff" | ||||
} | ||||
set xl [expr $xl - $delta/2] | ||||
$canv create polygon $x $yt $xr $yt $xr $yb $x $yb \ | ||||
-width 1 -outline black -fill $col -tags tag.$id | ||||
} | ||||
set t [$canv create text $xl $y1 -anchor w -text $tag \ | ||||
-font $mainfont -tags tag.$id] | ||||
if {$ntags >= 0} { | ||||
$canv bind $t <1> [list showtag $tag 1] | ||||
} | ||||
} | ||||
return $xt | ||||
} | ||||
proc notecrossings {id lo hi corner} { | ||||
global olddisplist crossings cornercrossings | ||||
for {set i $lo} {[incr i] < $hi} {} { | ||||
set p [lindex $olddisplist $i] | ||||
if {$p == {}} continue | ||||
if {$i == $corner} { | ||||
if {![info exists cornercrossings($id)] | ||||
|| [lsearch -exact $cornercrossings($id) $p] < 0} { | ||||
lappend cornercrossings($id) $p | ||||
} | ||||
if {![info exists cornercrossings($p)] | ||||
|| [lsearch -exact $cornercrossings($p) $id] < 0} { | ||||
lappend cornercrossings($p) $id | ||||
} | ||||
} else { | ||||
if {![info exists crossings($id)] | ||||
|| [lsearch -exact $crossings($id) $p] < 0} { | ||||
lappend crossings($id) $p | ||||
} | ||||
if {![info exists crossings($p)] | ||||
|| [lsearch -exact $crossings($p) $id] < 0} { | ||||
lappend crossings($p) $id | ||||
} | ||||
} | ||||
} | ||||
} | ||||
proc xcoord {i level ln} { | ||||
global canvx0 xspc1 xspc2 | ||||
set x [expr {$canvx0 + $i * $xspc1($ln)}] | ||||
if {$i > 0 && $i == $level} { | ||||
set x [expr {$x + 0.5 * ($xspc2 - $xspc1($ln))}] | ||||
} elseif {$i > $level} { | ||||
set x [expr {$x + $xspc2 - $xspc1($ln)}] | ||||
} | ||||
return $x | ||||
} | ||||
# it seems Tk can't draw arrows on the end of diagonal line segments... | ||||
proc trimdiagend {line} { | ||||
while {[llength $line] > 4} { | ||||
set x1 [lindex $line end-3] | ||||
set y1 [lindex $line end-2] | ||||
set x2 [lindex $line end-1] | ||||
set y2 [lindex $line end] | ||||
if {($x1 == $x2) != ($y1 == $y2)} break | ||||
set line [lreplace $line end-1 end] | ||||
} | ||||
return $line | ||||
} | ||||
proc trimdiagstart {line} { | ||||
while {[llength $line] > 4} { | ||||
set x1 [lindex $line 0] | ||||
set y1 [lindex $line 1] | ||||
set x2 [lindex $line 2] | ||||
set y2 [lindex $line 3] | ||||
if {($x1 == $x2) != ($y1 == $y2)} break | ||||
set line [lreplace $line 0 1] | ||||
} | ||||
return $line | ||||
} | ||||
proc drawslants {id needonscreen nohs} { | ||||
global canv mainline mainlinearrow sidelines | ||||
global canvx0 canvy xspc1 xspc2 lthickness | ||||
global currentparents dupparents | ||||
global lthickness linespc canvy colormap lineno geometry | ||||
global maxgraphpct maxwidth | ||||
global displist onscreen lastuse | ||||
global parents commitlisted | ||||
global oldnlines olddlevel olddisplist | ||||
global nhyperspace numcommits nnewparents | ||||
if {$lineno < 0} { | ||||
lappend displist $id | ||||
set onscreen($id) 1 | ||||
return 0 | ||||
} | ||||
set y1 [expr {$canvy - $linespc}] | ||||
set y2 $canvy | ||||
# work out what we need to get back on screen | ||||
set reins {} | ||||
if {$onscreen($id) < 0} { | ||||
# next to do isn't displayed, better get it on screen... | ||||
lappend reins [list $id 0] | ||||
} | ||||
# make sure all the previous commits's parents are on the screen | ||||
foreach p $currentparents { | ||||
if {$onscreen($p) < 0} { | ||||
lappend reins [list $p 0] | ||||
} | ||||
} | ||||
# bring back anything requested by caller | ||||
if {$needonscreen ne {}} { | ||||
lappend reins $needonscreen | ||||
|
r267 | } | ||
|
r1240 | # try the shortcut | ||
if {$currentparents == $id && $onscreen($id) == 0 && $reins eq {}} { | ||||
set dlevel $olddlevel | ||||
set x [xcoord $dlevel $dlevel $lineno] | ||||
set mainline($id) [list $x $y1] | ||||
set mainlinearrow($id) none | ||||
set lastuse($id) $lineno | ||||
set displist [lreplace $displist $dlevel $dlevel $id] | ||||
set onscreen($id) 1 | ||||
set xspc1([expr {$lineno + 1}]) $xspc1($lineno) | ||||
return $dlevel | ||||
} | ||||
# update displist | ||||
set displist [lreplace $displist $olddlevel $olddlevel] | ||||
set j $olddlevel | ||||
foreach p $currentparents { | ||||
set lastuse($p) $lineno | ||||
if {$onscreen($p) == 0} { | ||||
set displist [linsert $displist $j $p] | ||||
set onscreen($p) 1 | ||||
incr j | ||||
} | ||||
} | ||||
if {$onscreen($id) == 0} { | ||||
lappend displist $id | ||||
set onscreen($id) 1 | ||||
} | ||||
# remove the null entry if present | ||||
set nullentry [lsearch -exact $displist {}] | ||||
if {$nullentry >= 0} { | ||||
set displist [lreplace $displist $nullentry $nullentry] | ||||
} | ||||
# bring back the ones we need now (if we did it earlier | ||||
# it would change displist and invalidate olddlevel) | ||||
foreach pi $reins { | ||||
# test again in case of duplicates in reins | ||||
set p [lindex $pi 0] | ||||
if {$onscreen($p) < 0} { | ||||
set onscreen($p) 1 | ||||
set lastuse($p) $lineno | ||||
set displist [linsert $displist [lindex $pi 1] $p] | ||||
incr nhyperspace -1 | ||||
} | ||||
} | ||||
set lastuse($id) $lineno | ||||
# see if we need to make any lines jump off into hyperspace | ||||
set displ [llength $displist] | ||||
if {$displ > $maxwidth} { | ||||
set ages {} | ||||
foreach x $displist { | ||||
lappend ages [list $lastuse($x) $x] | ||||
} | ||||
set ages [lsort -integer -index 0 $ages] | ||||
set k 0 | ||||
while {$displ > $maxwidth} { | ||||
set use [lindex $ages $k 0] | ||||
set victim [lindex $ages $k 1] | ||||
if {$use >= $lineno - 5} break | ||||
incr k | ||||
if {[lsearch -exact $nohs $victim] >= 0} continue | ||||
set i [lsearch -exact $displist $victim] | ||||
set displist [lreplace $displist $i $i] | ||||
set onscreen($victim) -1 | ||||
incr nhyperspace | ||||
incr displ -1 | ||||
if {$i < $nullentry} { | ||||
incr nullentry -1 | ||||
} | ||||
set x [lindex $mainline($victim) end-1] | ||||
lappend mainline($victim) $x $y1 | ||||
set line [trimdiagend $mainline($victim)] | ||||
set arrow "last" | ||||
if {$mainlinearrow($victim) ne "none"} { | ||||
set line [trimdiagstart $line] | ||||
set arrow "both" | ||||
} | ||||
lappend sidelines($victim) [list $line 1 $arrow] | ||||
unset mainline($victim) | ||||
} | ||||
|
r267 | } | ||
|
r1240 | |||
set dlevel [lsearch -exact $displist $id] | ||||
# If we are reducing, put in a null entry | ||||
if {$displ < $oldnlines} { | ||||
# does the next line look like a merge? | ||||
# i.e. does it have > 1 new parent? | ||||
if {$nnewparents($id) > 1} { | ||||
set i [expr {$dlevel + 1}] | ||||
} elseif {$nnewparents([lindex $olddisplist $olddlevel]) == 0} { | ||||
set i $olddlevel | ||||
if {$nullentry >= 0 && $nullentry < $i} { | ||||
incr i -1 | ||||
} | ||||
} elseif {$nullentry >= 0} { | ||||
set i $nullentry | ||||
while {$i < $displ | ||||
&& [lindex $olddisplist $i] == [lindex $displist $i]} { | ||||
incr i | ||||
} | ||||
} else { | ||||
set i $olddlevel | ||||
if {$dlevel >= $i} { | ||||
incr i | ||||
} | ||||
} | ||||
if {$i < $displ} { | ||||
set displist [linsert $displist $i {}] | ||||
incr displ | ||||
if {$dlevel >= $i} { | ||||
incr dlevel | ||||
} | ||||
} | ||||
} | ||||
# decide on the line spacing for the next line | ||||
set lj [expr {$lineno + 1}] | ||||
set maxw [expr {$maxgraphpct * $geometry(canv1) / 100}] | ||||
if {$displ <= 1 || $canvx0 + $displ * $xspc2 <= $maxw} { | ||||
set xspc1($lj) $xspc2 | ||||
} else { | ||||
set xspc1($lj) [expr {($maxw - $canvx0 - $xspc2) / ($displ - 1)}] | ||||
if {$xspc1($lj) < $lthickness} { | ||||
set xspc1($lj) $lthickness | ||||
} | ||||
} | ||||
foreach idi $reins { | ||||
set id [lindex $idi 0] | ||||
set j [lsearch -exact $displist $id] | ||||
set xj [xcoord $j $dlevel $lj] | ||||
set mainline($id) [list $xj $y2] | ||||
set mainlinearrow($id) first | ||||
} | ||||
set i -1 | ||||
foreach id $olddisplist { | ||||
incr i | ||||
if {$id == {}} continue | ||||
if {$onscreen($id) <= 0} continue | ||||
set xi [xcoord $i $olddlevel $lineno] | ||||
if {$i == $olddlevel} { | ||||
foreach p $currentparents { | ||||
set j [lsearch -exact $displist $p] | ||||
set coords [list $xi $y1] | ||||
set xj [xcoord $j $dlevel $lj] | ||||
if {$xj < $xi - $linespc} { | ||||
lappend coords [expr {$xj + $linespc}] $y1 | ||||
notecrossings $p $j $i [expr {$j + 1}] | ||||
} elseif {$xj > $xi + $linespc} { | ||||
lappend coords [expr {$xj - $linespc}] $y1 | ||||
notecrossings $p $i $j [expr {$j - 1}] | ||||
} | ||||
if {[lsearch -exact $dupparents $p] >= 0} { | ||||
# draw a double-width line to indicate the doubled parent | ||||
lappend coords $xj $y2 | ||||
lappend sidelines($p) [list $coords 2 none] | ||||
if {![info exists mainline($p)]} { | ||||
set mainline($p) [list $xj $y2] | ||||
set mainlinearrow($p) none | ||||
|
r267 | } | ||
|
r1240 | } else { | ||
# normal case, no parent duplicated | ||||
set yb $y2 | ||||
set dx [expr {abs($xi - $xj)}] | ||||
if {0 && $dx < $linespc} { | ||||
set yb [expr {$y1 + $dx}] | ||||
} | ||||
if {![info exists mainline($p)]} { | ||||
if {$xi != $xj} { | ||||
lappend coords $xj $yb | ||||
} | ||||
set mainline($p) $coords | ||||
set mainlinearrow($p) none | ||||
} else { | ||||
lappend coords $xj $yb | ||||
if {$yb < $y2} { | ||||
lappend coords $xj $y2 | ||||
} | ||||
lappend sidelines($p) [list $coords 1 none] | ||||
} | ||||
} | ||||
} | ||||
} else { | ||||
set j $i | ||||
if {[lindex $displist $i] != $id} { | ||||
set j [lsearch -exact $displist $id] | ||||
} | ||||
if {$j != $i || $xspc1($lineno) != $xspc1($lj) | ||||
|| ($olddlevel < $i && $i < $dlevel) | ||||
|| ($dlevel < $i && $i < $olddlevel)} { | ||||
set xj [xcoord $j $dlevel $lj] | ||||
lappend mainline($id) $xi $y1 $xj $y2 | ||||
} | ||||
} | ||||
} | ||||
return $dlevel | ||||
} | ||||
# search for x in a list of lists | ||||
proc llsearch {llist x} { | ||||
set i 0 | ||||
foreach l $llist { | ||||
if {$l == $x || [lsearch -exact $l $x] >= 0} { | ||||
return $i | ||||
} | ||||
incr i | ||||
} | ||||
return -1 | ||||
} | ||||
proc drawmore {reading} { | ||||
global displayorder numcommits ncmupdate nextupdate | ||||
global stopped nhyperspace parents commitlisted | ||||
global maxwidth onscreen displist currentparents olddlevel | ||||
set n [llength $displayorder] | ||||
while {$numcommits < $n} { | ||||
set id [lindex $displayorder $numcommits] | ||||
set ctxend [expr {$numcommits + 10}] | ||||
if {!$reading && $ctxend > $n} { | ||||
set ctxend $n | ||||
} | ||||
set dlist {} | ||||
if {$numcommits > 0} { | ||||
set dlist [lreplace $displist $olddlevel $olddlevel] | ||||
set i $olddlevel | ||||
foreach p $currentparents { | ||||
if {$onscreen($p) == 0} { | ||||
set dlist [linsert $dlist $i $p] | ||||
incr i | ||||
|
r267 | } | ||
} | ||||
} | ||||
|
r1240 | set nohs {} | ||
set reins {} | ||||
set isfat [expr {[llength $dlist] > $maxwidth}] | ||||
if {$nhyperspace > 0 || $isfat} { | ||||
if {$ctxend > $n} break | ||||
# work out what to bring back and | ||||
# what we want to don't want to send into hyperspace | ||||
set room 1 | ||||
for {set k $numcommits} {$k < $ctxend} {incr k} { | ||||
set x [lindex $displayorder $k] | ||||
set i [llsearch $dlist $x] | ||||
if {$i < 0} { | ||||
set i [llength $dlist] | ||||
lappend dlist $x | ||||
} | ||||
if {[lsearch -exact $nohs $x] < 0} { | ||||
lappend nohs $x | ||||
} | ||||
if {$reins eq {} && $onscreen($x) < 0 && $room} { | ||||
set reins [list $x $i] | ||||
} | ||||
set newp {} | ||||
if {[info exists commitlisted($x)]} { | ||||
set right 0 | ||||
foreach p $parents($x) { | ||||
if {[llsearch $dlist $p] < 0} { | ||||
lappend newp $p | ||||
if {[lsearch -exact $nohs $p] < 0} { | ||||
lappend nohs $p | ||||
} | ||||
if {$reins eq {} && $onscreen($p) < 0 && $room} { | ||||
set reins [list $p [expr {$i + $right}]] | ||||
} | ||||
} | ||||
set right 1 | ||||
} | ||||
} | ||||
set l [lindex $dlist $i] | ||||
if {[llength $l] == 1} { | ||||
set l $newp | ||||
} else { | ||||
set j [lsearch -exact $l $x] | ||||
set l [concat [lreplace $l $j $j] $newp] | ||||
} | ||||
set dlist [lreplace $dlist $i $i $l] | ||||
if {$room && $isfat && [llength $newp] <= 1} { | ||||
set room 0 | ||||
} | ||||
|
r267 | } | ||
} | ||||
|
r1240 | set dlevel [drawslants $id $reins $nohs] | ||
drawcommitline $dlevel | ||||
if {[clock clicks -milliseconds] >= $nextupdate | ||||
&& $numcommits >= $ncmupdate} { | ||||
doupdate $reading | ||||
if {$stopped} break | ||||
|
r267 | } | ||
|
r1240 | } | ||
} | ||||
|
r267 | |||
|
r1240 | # level here is an index in todo | ||
proc updatetodo {level noshortcut} { | ||||
global ncleft todo nnewparents | ||||
global commitlisted parents onscreen | ||||
set id [lindex $todo $level] | ||||
set olds {} | ||||
if {[info exists commitlisted($id)]} { | ||||
foreach p $parents($id) { | ||||
if {[lsearch -exact $olds $p] < 0} { | ||||
lappend olds $p | ||||
|
r267 | } | ||
} | ||||
|
r1240 | } | ||
if {!$noshortcut && [llength $olds] == 1} { | ||||
set p [lindex $olds 0] | ||||
if {$ncleft($p) == 1 && [lsearch -exact $todo $p] < 0} { | ||||
set ncleft($p) 0 | ||||
set todo [lreplace $todo $level $level $p] | ||||
set onscreen($p) 0 | ||||
set nnewparents($id) 1 | ||||
return 0 | ||||
} | ||||
} | ||||
set todo [lreplace $todo $level $level] | ||||
set i $level | ||||
set n 0 | ||||
foreach p $olds { | ||||
incr ncleft($p) -1 | ||||
set k [lsearch -exact $todo $p] | ||||
if {$k < 0} { | ||||
set todo [linsert $todo $i $p] | ||||
set onscreen($p) 0 | ||||
incr i | ||||
incr n | ||||
|
r267 | } | ||
|
r1240 | } | ||
set nnewparents($id) $n | ||||
|
r267 | |||
|
r1240 | return 1 | ||
} | ||||
proc decidenext {{noread 0}} { | ||||
global ncleft todo | ||||
global datemode cdate | ||||
global commitinfo | ||||
# choose which one to do next time around | ||||
set todol [llength $todo] | ||||
set level -1 | ||||
set latest {} | ||||
for {set k $todol} {[incr k -1] >= 0} {} { | ||||
set p [lindex $todo $k] | ||||
if {$ncleft($p) == 0} { | ||||
if {$datemode} { | ||||
if {![info exists commitinfo($p)]} { | ||||
if {$noread} { | ||||
return {} | ||||
} | ||||
readcommit $p | ||||
} | ||||
if {$latest == {} || $cdate($p) > $latest} { | ||||
set level $k | ||||
set latest $cdate($p) | ||||
|
r267 | } | ||
} else { | ||||
|
r1240 | set level $k | ||
break | ||||
|
r267 | } | ||
} | ||||
} | ||||
|
r1240 | if {$level < 0} { | ||
if {$todo != {}} { | ||||
puts "ERROR: none of the pending commits can be done yet:" | ||||
foreach p $todo { | ||||
puts " $p ($ncleft($p))" | ||||
} | ||||
} | ||||
return -1 | ||||
} | ||||
return $level | ||||
} | ||||
proc drawcommit {id} { | ||||
global phase todo nchildren datemode nextupdate | ||||
global numcommits ncmupdate displayorder todo onscreen | ||||
if {$phase != "incrdraw"} { | ||||
set phase incrdraw | ||||
set displayorder {} | ||||
set todo {} | ||||
initgraph | ||||
} | ||||
if {$nchildren($id) == 0} { | ||||
lappend todo $id | ||||
set onscreen($id) 0 | ||||
} | ||||
set level [decidenext 1] | ||||
if {$level == {} || $id != [lindex $todo $level]} { | ||||
return | ||||
} | ||||
while 1 { | ||||
lappend displayorder [lindex $todo $level] | ||||
if {[updatetodo $level $datemode]} { | ||||
set level [decidenext 1] | ||||
if {$level == {}} break | ||||
} | ||||
set id [lindex $todo $level] | ||||
if {![info exists commitlisted($id)]} { | ||||
break | ||||
} | ||||
} | ||||
drawmore 1 | ||||
} | ||||
proc finishcommits {} { | ||||
global phase | ||||
global canv mainfont ctext maincursor textcursor | ||||
if {$phase != "incrdraw"} { | ||||
$canv delete all | ||||
$canv create text 3 3 -anchor nw -text "No commits selected" \ | ||||
-font $mainfont -tags textitems | ||||
set phase {} | ||||
} else { | ||||
drawrest | ||||
} | ||||
. config -cursor $maincursor | ||||
settextcursor $textcursor | ||||
} | ||||
# Don't change the text pane cursor if it is currently the hand cursor, | ||||
# showing that we are over a sha1 ID link. | ||||
proc settextcursor {c} { | ||||
global ctext curtextcursor | ||||
if {[$ctext cget -cursor] == $curtextcursor} { | ||||
$ctext config -cursor $c | ||||
} | ||||
set curtextcursor $c | ||||
} | ||||
proc drawgraph {} { | ||||
global nextupdate startmsecs ncmupdate | ||||
global displayorder onscreen | ||||
if {$displayorder == {}} return | ||||
set startmsecs [clock clicks -milliseconds] | ||||
set nextupdate [expr $startmsecs + 100] | ||||
set ncmupdate 1 | ||||
initgraph | ||||
foreach id $displayorder { | ||||
set onscreen($id) 0 | ||||
} | ||||
drawmore 0 | ||||
} | ||||
proc drawrest {} { | ||||
global phase stopped redisplaying selectedline | ||||
global datemode todo displayorder | ||||
global numcommits ncmupdate | ||||
global nextupdate startmsecs | ||||
set level [decidenext] | ||||
if {$level >= 0} { | ||||
set phase drawgraph | ||||
while 1 { | ||||
lappend displayorder [lindex $todo $level] | ||||
set hard [updatetodo $level $datemode] | ||||
if {$hard} { | ||||
set level [decidenext] | ||||
if {$level < 0} break | ||||
} | ||||
} | ||||
drawmore 0 | ||||
} | ||||
|
r267 | set phase {} | ||
|
r1240 | set drawmsecs [expr [clock clicks -milliseconds] - $startmsecs] | ||
#puts "overall $drawmsecs ms for $numcommits commits" | ||||
|
r267 | if {$redisplaying} { | ||
if {$stopped == 0 && [info exists selectedline]} { | ||||
|
r1240 | selectline $selectedline 0 | ||
|
r267 | } | ||
if {$stopped == 1} { | ||||
set stopped 0 | ||||
after idle drawgraph | ||||
} else { | ||||
set redisplaying 0 | ||||
} | ||||
} | ||||
} | ||||
proc findmatches {f} { | ||||
global findtype foundstring foundstrlen | ||||
if {$findtype == "Regexp"} { | ||||
set matches [regexp -indices -all -inline $foundstring $f] | ||||
} else { | ||||
if {$findtype == "IgnCase"} { | ||||
set str [string tolower $f] | ||||
} else { | ||||
set str $f | ||||
} | ||||
set matches {} | ||||
set i 0 | ||||
while {[set j [string first $foundstring $str $i]] >= 0} { | ||||
lappend matches [list $j [expr $j+$foundstrlen-1]] | ||||
set i [expr $j + $foundstrlen] | ||||
} | ||||
} | ||||
return $matches | ||||
} | ||||
proc dofind {} { | ||||
global findtype findloc findstring markedmatches commitinfo | ||||
global numcommits lineid linehtag linentag linedtag | ||||
global mainfont namefont canv canv2 canv3 selectedline | ||||
|
r1240 | global matchinglines foundstring foundstrlen | ||
stopfindproc | ||||
|
r267 | unmarkmatches | ||
focus . | ||||
set matchinglines {} | ||||
|
r1240 | if {$findloc == "Pickaxe"} { | ||
findpatches | ||||
return | ||||
} | ||||
|
r267 | if {$findtype == "IgnCase"} { | ||
set foundstring [string tolower $findstring] | ||||
} else { | ||||
set foundstring $findstring | ||||
} | ||||
set foundstrlen [string length $findstring] | ||||
if {$foundstrlen == 0} return | ||||
|
r1240 | if {$findloc == "Files"} { | ||
findfiles | ||||
return | ||||
} | ||||
|
r267 | if {![info exists selectedline]} { | ||
set oldsel -1 | ||||
} else { | ||||
set oldsel $selectedline | ||||
} | ||||
set didsel 0 | ||||
|
r1240 | set fldtypes {Headline Author Date Committer CDate Comment} | ||
|
r267 | for {set l 0} {$l < $numcommits} {incr l} { | ||
set id $lineid($l) | ||||
set info $commitinfo($id) | ||||
set doesmatch 0 | ||||
foreach f $info ty $fldtypes { | ||||
if {$findloc != "All fields" && $findloc != $ty} { | ||||
continue | ||||
} | ||||
set matches [findmatches $f] | ||||
if {$matches == {}} continue | ||||
set doesmatch 1 | ||||
if {$ty == "Headline"} { | ||||
markmatches $canv $l $f $linehtag($l) $matches $mainfont | ||||
} elseif {$ty == "Author"} { | ||||
markmatches $canv2 $l $f $linentag($l) $matches $namefont | ||||
} elseif {$ty == "Date"} { | ||||
markmatches $canv3 $l $f $linedtag($l) $matches $mainfont | ||||
} | ||||
} | ||||
if {$doesmatch} { | ||||
lappend matchinglines $l | ||||
if {!$didsel && $l > $oldsel} { | ||||
findselectline $l | ||||
set didsel 1 | ||||
} | ||||
} | ||||
} | ||||
if {$matchinglines == {}} { | ||||
bell | ||||
} elseif {!$didsel} { | ||||
findselectline [lindex $matchinglines 0] | ||||
} | ||||
} | ||||
proc findselectline {l} { | ||||
global findloc commentend ctext | ||||
|
r1240 | selectline $l 1 | ||
|
r267 | if {$findloc == "All fields" || $findloc == "Comments"} { | ||
# highlight the matches in the comments | ||||
set f [$ctext get 1.0 $commentend] | ||||
set matches [findmatches $f] | ||||
foreach match $matches { | ||||
set start [lindex $match 0] | ||||
set end [expr [lindex $match 1] + 1] | ||||
$ctext tag add found "1.0 + $start c" "1.0 + $end c" | ||||
} | ||||
} | ||||
} | ||||
|
r1240 | proc findnext {restart} { | ||
|
r267 | global matchinglines selectedline | ||
if {![info exists matchinglines]} { | ||||
|
r1240 | if {$restart} { | ||
dofind | ||||
} | ||||
|
r267 | return | ||
} | ||||
if {![info exists selectedline]} return | ||||
foreach l $matchinglines { | ||||
if {$l > $selectedline} { | ||||
findselectline $l | ||||
return | ||||
} | ||||
} | ||||
bell | ||||
} | ||||
proc findprev {} { | ||||
global matchinglines selectedline | ||||
if {![info exists matchinglines]} { | ||||
dofind | ||||
return | ||||
} | ||||
if {![info exists selectedline]} return | ||||
set prev {} | ||||
foreach l $matchinglines { | ||||
if {$l >= $selectedline} break | ||||
set prev $l | ||||
} | ||||
if {$prev != {}} { | ||||
findselectline $prev | ||||
} else { | ||||
bell | ||||
} | ||||
} | ||||
|
r1240 | proc findlocchange {name ix op} { | ||
global findloc findtype findtypemenu | ||||
if {$findloc == "Pickaxe"} { | ||||
set findtype Exact | ||||
set state disabled | ||||
} else { | ||||
set state normal | ||||
} | ||||
$findtypemenu entryconf 1 -state $state | ||||
$findtypemenu entryconf 2 -state $state | ||||
} | ||||
proc stopfindproc {{done 0}} { | ||||
global findprocpid findprocfile findids | ||||
global ctext findoldcursor phase maincursor textcursor | ||||
global findinprogress | ||||
catch {unset findids} | ||||
if {[info exists findprocpid]} { | ||||
if {!$done} { | ||||
catch {exec kill $findprocpid} | ||||
} | ||||
catch {close $findprocfile} | ||||
unset findprocpid | ||||
} | ||||
if {[info exists findinprogress]} { | ||||
unset findinprogress | ||||
if {$phase != "incrdraw"} { | ||||
. config -cursor $maincursor | ||||
settextcursor $textcursor | ||||
} | ||||
} | ||||
} | ||||
proc findpatches {} { | ||||
global findstring selectedline numcommits | ||||
global findprocpid findprocfile | ||||
global finddidsel ctext lineid findinprogress | ||||
global findinsertpos | ||||
if {$numcommits == 0} return | ||||
# make a list of all the ids to search, starting at the one | ||||
# after the selected line (if any) | ||||
if {[info exists selectedline]} { | ||||
set l $selectedline | ||||
} else { | ||||
set l -1 | ||||
} | ||||
set inputids {} | ||||
for {set i 0} {$i < $numcommits} {incr i} { | ||||
if {[incr l] >= $numcommits} { | ||||
set l 0 | ||||
} | ||||
append inputids $lineid($l) "\n" | ||||
} | ||||
if {[catch { | ||||
|
r1278 | set f [open [list | hg debug-diff-tree --stdin -s -r -S$findstring \ | ||
|
r1240 | << $inputids] r] | ||
} err]} { | ||||
error_popup "Error starting search process: $err" | ||||
return | ||||
} | ||||
set findinsertpos end | ||||
set findprocfile $f | ||||
set findprocpid [pid $f] | ||||
fconfigure $f -blocking 0 | ||||
fileevent $f readable readfindproc | ||||
set finddidsel 0 | ||||
. config -cursor watch | ||||
settextcursor watch | ||||
set findinprogress 1 | ||||
} | ||||
proc readfindproc {} { | ||||
global findprocfile finddidsel | ||||
global idline matchinglines findinsertpos | ||||
set n [gets $findprocfile line] | ||||
if {$n < 0} { | ||||
if {[eof $findprocfile]} { | ||||
stopfindproc 1 | ||||
if {!$finddidsel} { | ||||
bell | ||||
} | ||||
} | ||||
return | ||||
} | ||||
if {![regexp {^[0-9a-f]{40}} $line id]} { | ||||
error_popup "Can't parse git-diff-tree output: $line" | ||||
stopfindproc | ||||
return | ||||
} | ||||
if {![info exists idline($id)]} { | ||||
puts stderr "spurious id: $id" | ||||
return | ||||
} | ||||
set l $idline($id) | ||||
insertmatch $l $id | ||||
} | ||||
proc insertmatch {l id} { | ||||
global matchinglines findinsertpos finddidsel | ||||
if {$findinsertpos == "end"} { | ||||
if {$matchinglines != {} && $l < [lindex $matchinglines 0]} { | ||||
set matchinglines [linsert $matchinglines 0 $l] | ||||
set findinsertpos 1 | ||||
} else { | ||||
lappend matchinglines $l | ||||
} | ||||
} else { | ||||
set matchinglines [linsert $matchinglines $findinsertpos $l] | ||||
incr findinsertpos | ||||
} | ||||
markheadline $l $id | ||||
if {!$finddidsel} { | ||||
findselectline $l | ||||
set finddidsel 1 | ||||
} | ||||
} | ||||
proc findfiles {} { | ||||
global selectedline numcommits lineid ctext | ||||
global ffileline finddidsel parents nparents | ||||
global findinprogress findstartline findinsertpos | ||||
global treediffs fdiffids fdiffsneeded fdiffpos | ||||
global findmergefiles | ||||
if {$numcommits == 0} return | ||||
if {[info exists selectedline]} { | ||||
set l [expr {$selectedline + 1}] | ||||
} else { | ||||
set l 0 | ||||
} | ||||
set ffileline $l | ||||
set findstartline $l | ||||
set diffsneeded {} | ||||
set fdiffsneeded {} | ||||
while 1 { | ||||
set id $lineid($l) | ||||
if {$findmergefiles || $nparents($id) == 1} { | ||||
foreach p $parents($id) { | ||||
if {![info exists treediffs([list $id $p])]} { | ||||
append diffsneeded "$id $p\n" | ||||
lappend fdiffsneeded [list $id $p] | ||||
} | ||||
} | ||||
} | ||||
if {[incr l] >= $numcommits} { | ||||
set l 0 | ||||
} | ||||
if {$l == $findstartline} break | ||||
} | ||||
# start off a git-diff-tree process if needed | ||||
if {$diffsneeded ne {}} { | ||||
if {[catch { | ||||
|
r1278 | set df [open [list | hg debug-diff-tree -r --stdin << $diffsneeded] r] | ||
|
r1240 | } err ]} { | ||
error_popup "Error starting search process: $err" | ||||
return | ||||
} | ||||
catch {unset fdiffids} | ||||
set fdiffpos 0 | ||||
fconfigure $df -blocking 0 | ||||
fileevent $df readable [list readfilediffs $df] | ||||
} | ||||
set finddidsel 0 | ||||
set findinsertpos end | ||||
set id $lineid($l) | ||||
set p [lindex $parents($id) 0] | ||||
. config -cursor watch | ||||
settextcursor watch | ||||
set findinprogress 1 | ||||
findcont [list $id $p] | ||||
update | ||||
} | ||||
proc readfilediffs {df} { | ||||
global findids fdiffids fdiffs | ||||
set n [gets $df line] | ||||
if {$n < 0} { | ||||
if {[eof $df]} { | ||||
donefilediff | ||||
if {[catch {close $df} err]} { | ||||
stopfindproc | ||||
bell | ||||
|
r1278 | error_popup "Error in hg debug-diff-tree: $err" | ||
|
r1240 | } elseif {[info exists findids]} { | ||
set ids $findids | ||||
stopfindproc | ||||
bell | ||||
error_popup "Couldn't find diffs for {$ids}" | ||||
} | ||||
} | ||||
return | ||||
} | ||||
if {[regexp {^([0-9a-f]{40}) \(from ([0-9a-f]{40})\)} $line match id p]} { | ||||
# start of a new string of diffs | ||||
donefilediff | ||||
set fdiffids [list $id $p] | ||||
set fdiffs {} | ||||
} elseif {[string match ":*" $line]} { | ||||
lappend fdiffs [lindex $line 5] | ||||
} | ||||
} | ||||
proc donefilediff {} { | ||||
global fdiffids fdiffs treediffs findids | ||||
global fdiffsneeded fdiffpos | ||||
if {[info exists fdiffids]} { | ||||
while {[lindex $fdiffsneeded $fdiffpos] ne $fdiffids | ||||
&& $fdiffpos < [llength $fdiffsneeded]} { | ||||
# git-diff-tree doesn't output anything for a commit | ||||
# which doesn't change anything | ||||
set nullids [lindex $fdiffsneeded $fdiffpos] | ||||
set treediffs($nullids) {} | ||||
if {[info exists findids] && $nullids eq $findids} { | ||||
unset findids | ||||
findcont $nullids | ||||
} | ||||
incr fdiffpos | ||||
} | ||||
incr fdiffpos | ||||
if {![info exists treediffs($fdiffids)]} { | ||||
set treediffs($fdiffids) $fdiffs | ||||
} | ||||
if {[info exists findids] && $fdiffids eq $findids} { | ||||
unset findids | ||||
findcont $fdiffids | ||||
} | ||||
} | ||||
} | ||||
proc findcont {ids} { | ||||
global findids treediffs parents nparents | ||||
global ffileline findstartline finddidsel | ||||
global lineid numcommits matchinglines findinprogress | ||||
global findmergefiles | ||||
set id [lindex $ids 0] | ||||
set p [lindex $ids 1] | ||||
set pi [lsearch -exact $parents($id) $p] | ||||
set l $ffileline | ||||
while 1 { | ||||
if {$findmergefiles || $nparents($id) == 1} { | ||||
if {![info exists treediffs($ids)]} { | ||||
set findids $ids | ||||
set ffileline $l | ||||
return | ||||
} | ||||
set doesmatch 0 | ||||
foreach f $treediffs($ids) { | ||||
set x [findmatches $f] | ||||
if {$x != {}} { | ||||
set doesmatch 1 | ||||
break | ||||
} | ||||
} | ||||
if {$doesmatch} { | ||||
insertmatch $l $id | ||||
set pi $nparents($id) | ||||
} | ||||
} else { | ||||
set pi $nparents($id) | ||||
} | ||||
if {[incr pi] >= $nparents($id)} { | ||||
set pi 0 | ||||
if {[incr l] >= $numcommits} { | ||||
set l 0 | ||||
} | ||||
if {$l == $findstartline} break | ||||
set id $lineid($l) | ||||
} | ||||
set p [lindex $parents($id) $pi] | ||||
set ids [list $id $p] | ||||
} | ||||
stopfindproc | ||||
if {!$finddidsel} { | ||||
bell | ||||
} | ||||
} | ||||
# mark a commit as matching by putting a yellow background | ||||
# behind the headline | ||||
proc markheadline {l id} { | ||||
global canv mainfont linehtag commitinfo | ||||
set bbox [$canv bbox $linehtag($l)] | ||||
set t [$canv create rect $bbox -outline {} -tags matches -fill yellow] | ||||
$canv lower $t | ||||
} | ||||
# mark the bits of a headline, author or date that match a find string | ||||
|
r267 | proc markmatches {canv l str tag matches font} { | ||
set bbox [$canv bbox $tag] | ||||
set x0 [lindex $bbox 0] | ||||
set y0 [lindex $bbox 1] | ||||
set y1 [lindex $bbox 3] | ||||
foreach match $matches { | ||||
set start [lindex $match 0] | ||||
set end [lindex $match 1] | ||||
if {$start > $end} continue | ||||
set xoff [font measure $font [string range $str 0 [expr $start-1]]] | ||||
set xlen [font measure $font [string range $str 0 [expr $end]]] | ||||
set t [$canv create rect [expr $x0+$xoff] $y0 [expr $x0+$xlen+2] $y1 \ | ||||
-outline {} -tags matches -fill yellow] | ||||
$canv lower $t | ||||
} | ||||
} | ||||
proc unmarkmatches {} { | ||||
|
r1240 | global matchinglines findids | ||
|
r267 | allcanvs delete matches | ||
catch {unset matchinglines} | ||||
|
r1240 | catch {unset findids} | ||
|
r267 | } | ||
|
r1240 | proc selcanvline {w x y} { | ||
global canv canvy0 ctext linespc | ||||
global lineid linehtag linentag linedtag rowtextx | ||||
|
r267 | set ymax [lindex [$canv cget -scrollregion] 3] | ||
if {$ymax == {}} return | ||||
set yfrac [lindex [$canv yview] 0] | ||||
set y [expr {$y + $yfrac * $ymax}] | ||||
set l [expr {int(($y - $canvy0) / $linespc + 0.5)}] | ||||
if {$l < 0} { | ||||
set l 0 | ||||
} | ||||
|
r1240 | if {$w eq $canv} { | ||
if {![info exists rowtextx($l)] || $x < $rowtextx($l)} return | ||||
} | ||||
|
r267 | unmarkmatches | ||
|
r1240 | selectline $l 1 | ||
} | ||||
proc commit_descriptor {p} { | ||||
global commitinfo | ||||
set l "..." | ||||
if {[info exists commitinfo($p)]} { | ||||
set l [lindex $commitinfo($p) 0] | ||||
} | ||||
return "$p ($l)" | ||||
|
r267 | } | ||
|
r1240 | # append some text to the ctext widget, and make any SHA1 ID | ||
# that we know about be a clickable link. | ||||
proc appendwithlinks {text} { | ||||
global ctext idline linknum | ||||
set start [$ctext index "end - 1c"] | ||||
$ctext insert end $text | ||||
$ctext insert end "\n" | ||||
set links [regexp -indices -all -inline {[0-9a-f]{40}} $text] | ||||
foreach l $links { | ||||
set s [lindex $l 0] | ||||
set e [lindex $l 1] | ||||
set linkid [string range $text $s $e] | ||||
if {![info exists idline($linkid)]} continue | ||||
incr e | ||||
$ctext tag add link "$start + $s c" "$start + $e c" | ||||
$ctext tag add link$linknum "$start + $s c" "$start + $e c" | ||||
$ctext tag bind link$linknum <1> [list selectline $idline($linkid) 1] | ||||
incr linknum | ||||
} | ||||
$ctext tag conf link -foreground blue -underline 1 | ||||
$ctext tag bind link <Enter> { %W configure -cursor hand2 } | ||||
$ctext tag bind link <Leave> { %W configure -cursor $curtextcursor } | ||||
} | ||||
proc selectline {l isnew} { | ||||
|
r267 | global canv canv2 canv3 ctext commitinfo selectedline | ||
global lineid linehtag linentag linedtag | ||||
|
r1240 | global canvy0 linespc parents nparents children | ||
global cflist currentid sha1entry | ||||
global commentend idtags idline linknum | ||||
$canv delete hover | ||||
normalline | ||||
|
r267 | if {![info exists lineid($l)] || ![info exists linehtag($l)]} return | ||
$canv delete secsel | ||||
set t [eval $canv create rect [$canv bbox $linehtag($l)] -outline {{}} \ | ||||
-tags secsel -fill [$canv cget -selectbackground]] | ||||
$canv lower $t | ||||
$canv2 delete secsel | ||||
set t [eval $canv2 create rect [$canv2 bbox $linentag($l)] -outline {{}} \ | ||||
-tags secsel -fill [$canv2 cget -selectbackground]] | ||||
$canv2 lower $t | ||||
$canv3 delete secsel | ||||
set t [eval $canv3 create rect [$canv3 bbox $linedtag($l)] -outline {{}} \ | ||||
-tags secsel -fill [$canv3 cget -selectbackground]] | ||||
$canv3 lower $t | ||||
set y [expr {$canvy0 + $l * $linespc}] | ||||
set ymax [lindex [$canv cget -scrollregion] 3] | ||||
set ytop [expr {$y - $linespc - 1}] | ||||
set ybot [expr {$y + $linespc + 1}] | ||||
set wnow [$canv yview] | ||||
set wtop [expr [lindex $wnow 0] * $ymax] | ||||
set wbot [expr [lindex $wnow 1] * $ymax] | ||||
set wh [expr {$wbot - $wtop}] | ||||
set newtop $wtop | ||||
if {$ytop < $wtop} { | ||||
if {$ybot < $wtop} { | ||||
set newtop [expr {$y - $wh / 2.0}] | ||||
} else { | ||||
set newtop $ytop | ||||
if {$newtop > $wtop - $linespc} { | ||||
set newtop [expr {$wtop - $linespc}] | ||||
} | ||||
} | ||||
} elseif {$ybot > $wbot} { | ||||
if {$ytop > $wbot} { | ||||
set newtop [expr {$y - $wh / 2.0}] | ||||
} else { | ||||
set newtop [expr {$ybot - $wh}] | ||||
if {$newtop < $wtop + $linespc} { | ||||
set newtop [expr {$wtop + $linespc}] | ||||
} | ||||
} | ||||
} | ||||
if {$newtop != $wtop} { | ||||
if {$newtop < 0} { | ||||
set newtop 0 | ||||
} | ||||
allcanvs yview moveto [expr $newtop * 1.0 / $ymax] | ||||
} | ||||
|
r1240 | |||
if {$isnew} { | ||||
addtohistory [list selectline $l 0] | ||||
} | ||||
|
r267 | set selectedline $l | ||
set id $lineid($l) | ||||
set currentid $id | ||||
$sha1entry delete 0 end | ||||
$sha1entry insert 0 $id | ||||
$sha1entry selection from 0 | ||||
$sha1entry selection to end | ||||
$ctext conf -state normal | ||||
$ctext delete 0.0 end | ||||
|
r1240 | set linknum 0 | ||
$ctext mark set fmark.0 0.0 | ||||
$ctext mark gravity fmark.0 left | ||||
|
r267 | set info $commitinfo($id) | ||
$ctext insert end "Author: [lindex $info 1] [lindex $info 2]\n" | ||||
$ctext insert end "Committer: [lindex $info 3] [lindex $info 4]\n" | ||||
if {[info exists idtags($id)]} { | ||||
$ctext insert end "Tags:" | ||||
foreach tag $idtags($id) { | ||||
$ctext insert end " $tag" | ||||
} | ||||
$ctext insert end "\n" | ||||
} | ||||
|
r1308 | |||
|
r1240 | set comment {} | ||
if {[info exists parents($id)]} { | ||||
foreach p $parents($id) { | ||||
append comment "Parent: [commit_descriptor $p]\n" | ||||
} | ||||
} | ||||
if {[info exists children($id)]} { | ||||
foreach c $children($id) { | ||||
append comment "Child: [commit_descriptor $c]\n" | ||||
} | ||||
} | ||||
append comment "\n" | ||||
append comment [lindex $info 5] | ||||
# make anything that looks like a SHA1 ID be a clickable link | ||||
appendwithlinks $comment | ||||
|
r267 | $ctext tag delete Comments | ||
$ctext tag remove found 1.0 end | ||||
$ctext conf -state disabled | ||||
set commentend [$ctext index "end - 1c"] | ||||
$cflist delete 0 end | ||||
|
r1240 | $cflist insert end "Comments" | ||
|
r267 | if {$nparents($id) == 1} { | ||
|
r1240 | startdiff [concat $id $parents($id)] | ||
} elseif {$nparents($id) > 1} { | ||||
mergediff $id | ||||
|
r267 | } | ||
} | ||||
proc selnextline {dir} { | ||||
global selectedline | ||||
if {![info exists selectedline]} return | ||||
set l [expr $selectedline + $dir] | ||||
unmarkmatches | ||||
|
r1240 | selectline $l 1 | ||
} | ||||
proc unselectline {} { | ||||
global selectedline | ||||
catch {unset selectedline} | ||||
allcanvs delete secsel | ||||
} | ||||
proc addtohistory {cmd} { | ||||
global history historyindex | ||||
if {$historyindex > 0 | ||||
&& [lindex $history [expr {$historyindex - 1}]] == $cmd} { | ||||
return | ||||
} | ||||
if {$historyindex < [llength $history]} { | ||||
set history [lreplace $history $historyindex end $cmd] | ||||
} else { | ||||
lappend history $cmd | ||||
} | ||||
incr historyindex | ||||
if {$historyindex > 1} { | ||||
.ctop.top.bar.leftbut conf -state normal | ||||
} else { | ||||
.ctop.top.bar.leftbut conf -state disabled | ||||
} | ||||
.ctop.top.bar.rightbut conf -state disabled | ||||
} | ||||
proc goback {} { | ||||
global history historyindex | ||||
if {$historyindex > 1} { | ||||
incr historyindex -1 | ||||
set cmd [lindex $history [expr {$historyindex - 1}]] | ||||
eval $cmd | ||||
.ctop.top.bar.rightbut conf -state normal | ||||
} | ||||
if {$historyindex <= 1} { | ||||
.ctop.top.bar.leftbut conf -state disabled | ||||
} | ||||
} | ||||
proc goforw {} { | ||||
global history historyindex | ||||
if {$historyindex < [llength $history]} { | ||||
set cmd [lindex $history $historyindex] | ||||
incr historyindex | ||||
eval $cmd | ||||
.ctop.top.bar.leftbut conf -state normal | ||||
} | ||||
if {$historyindex >= [llength $history]} { | ||||
.ctop.top.bar.rightbut conf -state disabled | ||||
} | ||||
} | ||||
proc mergediff {id} { | ||||
global parents diffmergeid diffmergegca mergefilelist diffpindex | ||||
set diffmergeid $id | ||||
set diffpindex -1 | ||||
set diffmergegca [findgca $parents($id)] | ||||
if {[info exists mergefilelist($id)]} { | ||||
if {$mergefilelist($id) ne {}} { | ||||
showmergediff | ||||
} | ||||
} else { | ||||
contmergediff {} | ||||
} | ||||
|
r267 | } | ||
|
r1240 | proc findgca {ids} { | ||
set gca {} | ||||
foreach id $ids { | ||||
if {$gca eq {}} { | ||||
set gca $id | ||||
} else { | ||||
if {[catch { | ||||
|
r1278 | set gca [exec hg debug-merge-base $gca $id] | ||
|
r1240 | } err]} { | ||
return {} | ||||
} | ||||
} | ||||
} | ||||
return $gca | ||||
} | ||||
proc contmergediff {ids} { | ||||
global diffmergeid diffpindex parents nparents diffmergegca | ||||
global treediffs mergefilelist diffids treepending | ||||
# diff the child against each of the parents, and diff | ||||
# each of the parents against the GCA. | ||||
while 1 { | ||||
if {[lindex $ids 0] == $diffmergeid && $diffmergegca ne {}} { | ||||
set ids [list [lindex $ids 1] $diffmergegca] | ||||
} else { | ||||
if {[incr diffpindex] >= $nparents($diffmergeid)} break | ||||
set p [lindex $parents($diffmergeid) $diffpindex] | ||||
set ids [list $diffmergeid $p] | ||||
} | ||||
if {![info exists treediffs($ids)]} { | ||||
set diffids $ids | ||||
if {![info exists treepending]} { | ||||
gettreediffs $ids | ||||
} | ||||
return | ||||
} | ||||
|
r267 | } | ||
|
r1240 | |||
# If a file in some parent is different from the child and also | ||||
# different from the GCA, then it's interesting. | ||||
# If we don't have a GCA, then a file is interesting if it is | ||||
# different from the child in all the parents. | ||||
if {$diffmergegca ne {}} { | ||||
set files {} | ||||
foreach p $parents($diffmergeid) { | ||||
set gcadiffs $treediffs([list $p $diffmergegca]) | ||||
foreach f $treediffs([list $diffmergeid $p]) { | ||||
if {[lsearch -exact $files $f] < 0 | ||||
&& [lsearch -exact $gcadiffs $f] >= 0} { | ||||
lappend files $f | ||||
} | ||||
} | ||||
} | ||||
set files [lsort $files] | ||||
} else { | ||||
set p [lindex $parents($diffmergeid) 0] | ||||
set files $treediffs([list $diffmergeid $p]) | ||||
for {set i 1} {$i < $nparents($diffmergeid) && $files ne {}} {incr i} { | ||||
set p [lindex $parents($diffmergeid) $i] | ||||
set df $treediffs([list $diffmergeid $p]) | ||||
set nf {} | ||||
foreach f $files { | ||||
if {[lsearch -exact $df $f] >= 0} { | ||||
lappend nf $f | ||||
} | ||||
} | ||||
set files $nf | ||||
} | ||||
} | ||||
set mergefilelist($diffmergeid) $files | ||||
if {$files ne {}} { | ||||
showmergediff | ||||
} | ||||
} | ||||
proc showmergediff {} { | ||||
global cflist diffmergeid mergefilelist parents | ||||
global diffopts diffinhunk currentfile currenthunk filelines | ||||
global diffblocked groupfilelast mergefds groupfilenum grouphunks | ||||
set files $mergefilelist($diffmergeid) | ||||
foreach f $files { | ||||
|
r267 | $cflist insert end $f | ||
} | ||||
|
r1240 | set env(GIT_DIFF_OPTS) $diffopts | ||
set flist {} | ||||
catch {unset currentfile} | ||||
catch {unset currenthunk} | ||||
catch {unset filelines} | ||||
catch {unset groupfilenum} | ||||
catch {unset grouphunks} | ||||
set groupfilelast -1 | ||||
foreach p $parents($diffmergeid) { | ||||
|
r1278 | set cmd [list | hg debug-diff-tree -p $p $diffmergeid] | ||
|
r1240 | set cmd [concat $cmd $mergefilelist($diffmergeid)] | ||
if {[catch {set f [open $cmd r]} err]} { | ||||
error_popup "Error getting diffs: $err" | ||||
foreach f $flist { | ||||
catch {close $f} | ||||
} | ||||
return | ||||
} | ||||
lappend flist $f | ||||
set ids [list $diffmergeid $p] | ||||
set mergefds($ids) $f | ||||
set diffinhunk($ids) 0 | ||||
set diffblocked($ids) 0 | ||||
fconfigure $f -blocking 0 | ||||
fileevent $f readable [list getmergediffline $f $ids $diffmergeid] | ||||
} | ||||
} | ||||
proc getmergediffline {f ids id} { | ||||
global diffmergeid diffinhunk diffoldlines diffnewlines | ||||
global currentfile currenthunk | ||||
global diffoldstart diffnewstart diffoldlno diffnewlno | ||||
global diffblocked mergefilelist | ||||
global noldlines nnewlines difflcounts filelines | ||||
set n [gets $f line] | ||||
if {$n < 0} { | ||||
if {![eof $f]} return | ||||
} | ||||
if {!([info exists diffmergeid] && $diffmergeid == $id)} { | ||||
if {$n < 0} { | ||||
close $f | ||||
} | ||||
return | ||||
} | ||||
if {$diffinhunk($ids) != 0} { | ||||
set fi $currentfile($ids) | ||||
if {$n > 0 && [regexp {^[-+ \\]} $line match]} { | ||||
# continuing an existing hunk | ||||
set line [string range $line 1 end] | ||||
set p [lindex $ids 1] | ||||
if {$match eq "-" || $match eq " "} { | ||||
set filelines($p,$fi,$diffoldlno($ids)) $line | ||||
incr diffoldlno($ids) | ||||
} | ||||
if {$match eq "+" || $match eq " "} { | ||||
set filelines($id,$fi,$diffnewlno($ids)) $line | ||||
incr diffnewlno($ids) | ||||
} | ||||
if {$match eq " "} { | ||||
if {$diffinhunk($ids) == 2} { | ||||
lappend difflcounts($ids) \ | ||||
[list $noldlines($ids) $nnewlines($ids)] | ||||
set noldlines($ids) 0 | ||||
set diffinhunk($ids) 1 | ||||
} | ||||
incr noldlines($ids) | ||||
} elseif {$match eq "-" || $match eq "+"} { | ||||
if {$diffinhunk($ids) == 1} { | ||||
lappend difflcounts($ids) [list $noldlines($ids)] | ||||
set noldlines($ids) 0 | ||||
set nnewlines($ids) 0 | ||||
set diffinhunk($ids) 2 | ||||
} | ||||
if {$match eq "-"} { | ||||
incr noldlines($ids) | ||||
} else { | ||||
incr nnewlines($ids) | ||||
} | ||||
} | ||||
# and if it's \ No newline at end of line, then what? | ||||
return | ||||
} | ||||
# end of a hunk | ||||
if {$diffinhunk($ids) == 1 && $noldlines($ids) != 0} { | ||||
lappend difflcounts($ids) [list $noldlines($ids)] | ||||
} elseif {$diffinhunk($ids) == 2 | ||||
&& ($noldlines($ids) != 0 || $nnewlines($ids) != 0)} { | ||||
lappend difflcounts($ids) [list $noldlines($ids) $nnewlines($ids)] | ||||
} | ||||
set currenthunk($ids) [list $currentfile($ids) \ | ||||
$diffoldstart($ids) $diffnewstart($ids) \ | ||||
$diffoldlno($ids) $diffnewlno($ids) \ | ||||
$difflcounts($ids)] | ||||
set diffinhunk($ids) 0 | ||||
# -1 = need to block, 0 = unblocked, 1 = is blocked | ||||
set diffblocked($ids) -1 | ||||
processhunks | ||||
if {$diffblocked($ids) == -1} { | ||||
fileevent $f readable {} | ||||
set diffblocked($ids) 1 | ||||
} | ||||
} | ||||
if {$n < 0} { | ||||
# eof | ||||
if {!$diffblocked($ids)} { | ||||
close $f | ||||
set currentfile($ids) [llength $mergefilelist($diffmergeid)] | ||||
set currenthunk($ids) [list $currentfile($ids) 0 0 0 0 {}] | ||||
processhunks | ||||
} | ||||
} elseif {[regexp {^diff --git a/(.*) b/} $line match fname]} { | ||||
# start of a new file | ||||
set currentfile($ids) \ | ||||
[lsearch -exact $mergefilelist($diffmergeid) $fname] | ||||
} elseif {[regexp {^@@ -([0-9]+),([0-9]+) \+([0-9]+),([0-9]+) @@(.*)} \ | ||||
$line match f1l f1c f2l f2c rest]} { | ||||
if {[info exists currentfile($ids)] && $currentfile($ids) >= 0} { | ||||
# start of a new hunk | ||||
if {$f1l == 0 && $f1c == 0} { | ||||
set f1l 1 | ||||
} | ||||
if {$f2l == 0 && $f2c == 0} { | ||||
set f2l 1 | ||||
} | ||||
set diffinhunk($ids) 1 | ||||
set diffoldstart($ids) $f1l | ||||
set diffnewstart($ids) $f2l | ||||
set diffoldlno($ids) $f1l | ||||
set diffnewlno($ids) $f2l | ||||
set difflcounts($ids) {} | ||||
set noldlines($ids) 0 | ||||
set nnewlines($ids) 0 | ||||
} | ||||
} | ||||
} | ||||
proc processhunks {} { | ||||
global diffmergeid parents nparents currenthunk | ||||
global mergefilelist diffblocked mergefds | ||||
global grouphunks grouplinestart grouplineend groupfilenum | ||||
set nfiles [llength $mergefilelist($diffmergeid)] | ||||
while 1 { | ||||
set fi $nfiles | ||||
set lno 0 | ||||
# look for the earliest hunk | ||||
foreach p $parents($diffmergeid) { | ||||
set ids [list $diffmergeid $p] | ||||
if {![info exists currenthunk($ids)]} return | ||||
set i [lindex $currenthunk($ids) 0] | ||||
set l [lindex $currenthunk($ids) 2] | ||||
if {$i < $fi || ($i == $fi && $l < $lno)} { | ||||
set fi $i | ||||
set lno $l | ||||
set pi $p | ||||
} | ||||
} | ||||
if {$fi < $nfiles} { | ||||
set ids [list $diffmergeid $pi] | ||||
set hunk $currenthunk($ids) | ||||
unset currenthunk($ids) | ||||
if {$diffblocked($ids) > 0} { | ||||
fileevent $mergefds($ids) readable \ | ||||
[list getmergediffline $mergefds($ids) $ids $diffmergeid] | ||||
} | ||||
set diffblocked($ids) 0 | ||||
if {[info exists groupfilenum] && $groupfilenum == $fi | ||||
&& $lno <= $grouplineend} { | ||||
# add this hunk to the pending group | ||||
lappend grouphunks($pi) $hunk | ||||
set endln [lindex $hunk 4] | ||||
if {$endln > $grouplineend} { | ||||
set grouplineend $endln | ||||
} | ||||
continue | ||||
} | ||||
} | ||||
# succeeding stuff doesn't belong in this group, so | ||||
# process the group now | ||||
if {[info exists groupfilenum]} { | ||||
processgroup | ||||
unset groupfilenum | ||||
unset grouphunks | ||||
} | ||||
if {$fi >= $nfiles} break | ||||
# start a new group | ||||
set groupfilenum $fi | ||||
set grouphunks($pi) [list $hunk] | ||||
set grouplinestart $lno | ||||
set grouplineend [lindex $hunk 4] | ||||
} | ||||
|
r267 | } | ||
|
r1240 | proc processgroup {} { | ||
global groupfilelast groupfilenum difffilestart | ||||
global mergefilelist diffmergeid ctext filelines | ||||
global parents diffmergeid diffoffset | ||||
global grouphunks grouplinestart grouplineend nparents | ||||
global mergemax | ||||
$ctext conf -state normal | ||||
set id $diffmergeid | ||||
set f $groupfilenum | ||||
if {$groupfilelast != $f} { | ||||
$ctext insert end "\n" | ||||
set here [$ctext index "end - 1c"] | ||||
set difffilestart($f) $here | ||||
set mark fmark.[expr {$f + 1}] | ||||
$ctext mark set $mark $here | ||||
$ctext mark gravity $mark left | ||||
set header [lindex $mergefilelist($id) $f] | ||||
set l [expr {(78 - [string length $header]) / 2}] | ||||
set pad [string range "----------------------------------------" 1 $l] | ||||
$ctext insert end "$pad $header $pad\n" filesep | ||||
set groupfilelast $f | ||||
foreach p $parents($id) { | ||||
set diffoffset($p) 0 | ||||
} | ||||
} | ||||
$ctext insert end "@@" msep | ||||
set nlines [expr {$grouplineend - $grouplinestart}] | ||||
set events {} | ||||
set pnum 0 | ||||
foreach p $parents($id) { | ||||
set startline [expr {$grouplinestart + $diffoffset($p)}] | ||||
set ol $startline | ||||
set nl $grouplinestart | ||||
if {[info exists grouphunks($p)]} { | ||||
foreach h $grouphunks($p) { | ||||
set l [lindex $h 2] | ||||
if {$nl < $l} { | ||||
for {} {$nl < $l} {incr nl} { | ||||
set filelines($p,$f,$ol) $filelines($id,$f,$nl) | ||||
incr ol | ||||
} | ||||
} | ||||
foreach chunk [lindex $h 5] { | ||||
if {[llength $chunk] == 2} { | ||||
set olc [lindex $chunk 0] | ||||
set nlc [lindex $chunk 1] | ||||
set nnl [expr {$nl + $nlc}] | ||||
lappend events [list $nl $nnl $pnum $olc $nlc] | ||||
incr ol $olc | ||||
set nl $nnl | ||||
} else { | ||||
incr ol [lindex $chunk 0] | ||||
incr nl [lindex $chunk 0] | ||||
} | ||||
} | ||||
} | ||||
} | ||||
if {$nl < $grouplineend} { | ||||
for {} {$nl < $grouplineend} {incr nl} { | ||||
set filelines($p,$f,$ol) $filelines($id,$f,$nl) | ||||
incr ol | ||||
} | ||||
} | ||||
set nlines [expr {$ol - $startline}] | ||||
$ctext insert end " -$startline,$nlines" msep | ||||
incr pnum | ||||
} | ||||
set nlines [expr {$grouplineend - $grouplinestart}] | ||||
$ctext insert end " +$grouplinestart,$nlines @@\n" msep | ||||
set events [lsort -integer -index 0 $events] | ||||
set nevents [llength $events] | ||||
set nmerge $nparents($diffmergeid) | ||||
set l $grouplinestart | ||||
for {set i 0} {$i < $nevents} {set i $j} { | ||||
set nl [lindex $events $i 0] | ||||
while {$l < $nl} { | ||||
$ctext insert end " $filelines($id,$f,$l)\n" | ||||
incr l | ||||
} | ||||
set e [lindex $events $i] | ||||
set enl [lindex $e 1] | ||||
set j $i | ||||
set active {} | ||||
while 1 { | ||||
set pnum [lindex $e 2] | ||||
set olc [lindex $e 3] | ||||
set nlc [lindex $e 4] | ||||
if {![info exists delta($pnum)]} { | ||||
set delta($pnum) [expr {$olc - $nlc}] | ||||
lappend active $pnum | ||||
} else { | ||||
incr delta($pnum) [expr {$olc - $nlc}] | ||||
} | ||||
if {[incr j] >= $nevents} break | ||||
set e [lindex $events $j] | ||||
if {[lindex $e 0] >= $enl} break | ||||
if {[lindex $e 1] > $enl} { | ||||
set enl [lindex $e 1] | ||||
} | ||||
} | ||||
set nlc [expr {$enl - $l}] | ||||
set ncol mresult | ||||
set bestpn -1 | ||||
if {[llength $active] == $nmerge - 1} { | ||||
# no diff for one of the parents, i.e. it's identical | ||||
for {set pnum 0} {$pnum < $nmerge} {incr pnum} { | ||||
if {![info exists delta($pnum)]} { | ||||
if {$pnum < $mergemax} { | ||||
lappend ncol m$pnum | ||||
} else { | ||||
lappend ncol mmax | ||||
} | ||||
break | ||||
} | ||||
} | ||||
} elseif {[llength $active] == $nmerge} { | ||||
# all parents are different, see if one is very similar | ||||
set bestsim 30 | ||||
for {set pnum 0} {$pnum < $nmerge} {incr pnum} { | ||||
set sim [similarity $pnum $l $nlc $f \ | ||||
[lrange $events $i [expr {$j-1}]]] | ||||
if {$sim > $bestsim} { | ||||
set bestsim $sim | ||||
set bestpn $pnum | ||||
} | ||||
} | ||||
if {$bestpn >= 0} { | ||||
lappend ncol m$bestpn | ||||
} | ||||
} | ||||
set pnum -1 | ||||
foreach p $parents($id) { | ||||
incr pnum | ||||
if {![info exists delta($pnum)] || $pnum == $bestpn} continue | ||||
set olc [expr {$nlc + $delta($pnum)}] | ||||
set ol [expr {$l + $diffoffset($p)}] | ||||
incr diffoffset($p) $delta($pnum) | ||||
unset delta($pnum) | ||||
for {} {$olc > 0} {incr olc -1} { | ||||
$ctext insert end "-$filelines($p,$f,$ol)\n" m$pnum | ||||
incr ol | ||||
} | ||||
} | ||||
set endl [expr {$l + $nlc}] | ||||
if {$bestpn >= 0} { | ||||
# show this pretty much as a normal diff | ||||
set p [lindex $parents($id) $bestpn] | ||||
set ol [expr {$l + $diffoffset($p)}] | ||||
incr diffoffset($p) $delta($bestpn) | ||||
unset delta($bestpn) | ||||
for {set k $i} {$k < $j} {incr k} { | ||||
set e [lindex $events $k] | ||||
if {[lindex $e 2] != $bestpn} continue | ||||
set nl [lindex $e 0] | ||||
set ol [expr {$ol + $nl - $l}] | ||||
for {} {$l < $nl} {incr l} { | ||||
$ctext insert end "+$filelines($id,$f,$l)\n" $ncol | ||||
} | ||||
set c [lindex $e 3] | ||||
for {} {$c > 0} {incr c -1} { | ||||
$ctext insert end "-$filelines($p,$f,$ol)\n" m$bestpn | ||||
incr ol | ||||
} | ||||
set nl [lindex $e 1] | ||||
for {} {$l < $nl} {incr l} { | ||||
$ctext insert end "+$filelines($id,$f,$l)\n" mresult | ||||
} | ||||
} | ||||
} | ||||
for {} {$l < $endl} {incr l} { | ||||
$ctext insert end "+$filelines($id,$f,$l)\n" $ncol | ||||
} | ||||
} | ||||
while {$l < $grouplineend} { | ||||
$ctext insert end " $filelines($id,$f,$l)\n" | ||||
incr l | ||||
} | ||||
$ctext conf -state disabled | ||||
|
r267 | } | ||
|
r1240 | proc similarity {pnum l nlc f events} { | ||
global diffmergeid parents diffoffset filelines | ||||
set id $diffmergeid | ||||
set p [lindex $parents($id) $pnum] | ||||
set ol [expr {$l + $diffoffset($p)}] | ||||
set endl [expr {$l + $nlc}] | ||||
set same 0 | ||||
set diff 0 | ||||
foreach e $events { | ||||
if {[lindex $e 2] != $pnum} continue | ||||
set nl [lindex $e 0] | ||||
set ol [expr {$ol + $nl - $l}] | ||||
for {} {$l < $nl} {incr l} { | ||||
incr same [string length $filelines($id,$f,$l)] | ||||
incr same | ||||
} | ||||
set oc [lindex $e 3] | ||||
for {} {$oc > 0} {incr oc -1} { | ||||
incr diff [string length $filelines($p,$f,$ol)] | ||||
incr diff | ||||
incr ol | ||||
} | ||||
set nl [lindex $e 1] | ||||
for {} {$l < $nl} {incr l} { | ||||
incr diff [string length $filelines($id,$f,$l)] | ||||
incr diff | ||||
} | ||||
} | ||||
for {} {$l < $endl} {incr l} { | ||||
incr same [string length $filelines($id,$f,$l)] | ||||
incr same | ||||
} | ||||
if {$same == 0} { | ||||
return 0 | ||||
} | ||||
return [expr {200 * $same / (2 * $same + $diff)}] | ||||
} | ||||
proc startdiff {ids} { | ||||
global treediffs diffids treepending diffmergeid | ||||
set diffids $ids | ||||
catch {unset diffmergeid} | ||||
if {![info exists treediffs($ids)]} { | ||||
if {![info exists treepending]} { | ||||
gettreediffs $ids | ||||
} | ||||
} else { | ||||
addtocflist $ids | ||||
} | ||||
} | ||||
proc addtocflist {ids} { | ||||
global treediffs cflist | ||||
foreach f $treediffs($ids) { | ||||
$cflist insert end $f | ||||
} | ||||
getblobdiffs $ids | ||||
} | ||||
proc gettreediffs {ids} { | ||||
global treediff parents treepending | ||||
set treepending $ids | ||||
set treediff {} | ||||
set id [lindex $ids 0] | ||||
set p [lindex $ids 1] | ||||
|
r1278 | if [catch {set gdtf [open "|hg debug-diff-tree -r $p $id" r]}] return | ||
|
r1240 | fconfigure $gdtf -blocking 0 | ||
fileevent $gdtf readable [list gettreediffline $gdtf $ids] | ||||
} | ||||
proc gettreediffline {gdtf ids} { | ||||
global treediff treediffs treepending diffids diffmergeid | ||||
|
r267 | set n [gets $gdtf line] | ||
if {$n < 0} { | ||||
if {![eof $gdtf]} return | ||||
close $gdtf | ||||
|
r1240 | set treediffs($ids) $treediff | ||
|
r267 | unset treepending | ||
|
r1240 | if {$ids != $diffids} { | ||
gettreediffs $diffids | ||||
} else { | ||||
if {[info exists diffmergeid]} { | ||||
contmergediff $ids | ||||
} else { | ||||
addtocflist $ids | ||||
} | ||||
} | ||||
|
r267 | return | ||
} | ||||
set file [lindex $line 5] | ||||
|
r1240 | lappend treediff $file | ||
|
r267 | } | ||
|
r1240 | proc getblobdiffs {ids} { | ||
global diffopts blobdifffd diffids env curdifftag curtagstart | ||||
global difffilestart nextupdate diffinhdr treediffs | ||||
set id [lindex $ids 0] | ||||
set p [lindex $ids 1] | ||||
|
r267 | set env(GIT_DIFF_OPTS) $diffopts | ||
|
r1278 | set cmd [list | hg debug-diff-tree -r -p -C $p $id] | ||
|
r1240 | if {[catch {set bdf [open $cmd r]} err]} { | ||
|
r267 | puts "error getting diffs: $err" | ||
return | ||||
} | ||||
|
r1240 | set diffinhdr 0 | ||
|
r267 | fconfigure $bdf -blocking 0 | ||
|
r1240 | set blobdifffd($ids) $bdf | ||
|
r267 | set curdifftag Comments | ||
set curtagstart 0.0 | ||||
catch {unset difffilestart} | ||||
|
r1240 | fileevent $bdf readable [list getblobdiffline $bdf $diffids] | ||
set nextupdate [expr {[clock clicks -milliseconds] + 100}] | ||||
|
r267 | } | ||
|
r1240 | proc getblobdiffline {bdf ids} { | ||
global diffids blobdifffd ctext curdifftag curtagstart | ||||
global diffnexthead diffnextnote difffilestart | ||||
global nextupdate diffinhdr treediffs | ||||
global gaudydiff | ||||
|
r267 | set n [gets $bdf line] | ||
if {$n < 0} { | ||||
if {[eof $bdf]} { | ||||
close $bdf | ||||
|
r1240 | if {$ids == $diffids && $bdf == $blobdifffd($ids)} { | ||
|
r267 | $ctext tag add $curdifftag $curtagstart end | ||
} | ||||
} | ||||
return | ||||
} | ||||
|
r1240 | if {$ids != $diffids || $bdf != $blobdifffd($ids)} { | ||
|
r267 | return | ||
} | ||||
$ctext conf -state normal | ||||
|
r1240 | if {[regexp {^diff --git a/(.*) b/(.*)} $line match fname newname]} { | ||
|
r267 | # start of a new file | ||
$ctext insert end "\n" | ||||
$ctext tag add $curdifftag $curtagstart end | ||||
set curtagstart [$ctext index "end - 1c"] | ||||
|
r1240 | set header $newname | ||
set here [$ctext index "end - 1c"] | ||||
set i [lsearch -exact $treediffs($diffids) $fname] | ||||
if {$i >= 0} { | ||||
set difffilestart($i) $here | ||||
incr i | ||||
$ctext mark set fmark.$i $here | ||||
$ctext mark gravity fmark.$i left | ||||
|
r267 | } | ||
|
r1240 | if {$newname != $fname} { | ||
set i [lsearch -exact $treediffs($diffids) $newname] | ||||
if {$i >= 0} { | ||||
set difffilestart($i) $here | ||||
incr i | ||||
$ctext mark set fmark.$i $here | ||||
$ctext mark gravity fmark.$i left | ||||
} | ||||
} | ||||
|
r267 | set curdifftag "f:$fname" | ||
$ctext tag delete $curdifftag | ||||
set l [expr {(78 - [string length $header]) / 2}] | ||||
set pad [string range "----------------------------------------" 1 $l] | ||||
$ctext insert end "$pad $header $pad\n" filesep | ||||
|
r1240 | set diffinhdr 1 | ||
} elseif {[regexp {^(---|\+\+\+)} $line]} { | ||||
set diffinhdr 0 | ||||
|
r267 | } elseif {[regexp {^@@ -([0-9]+),([0-9]+) \+([0-9]+),([0-9]+) @@(.*)} \ | ||
$line match f1l f1c f2l f2c rest]} { | ||||
|
r1240 | if {$gaudydiff} { | ||
$ctext insert end "\t" hunksep | ||||
$ctext insert end " $f1l " d0 " $f2l " d1 | ||||
$ctext insert end " $rest \n" hunksep | ||||
} else { | ||||
$ctext insert end "$line\n" hunksep | ||||
} | ||||
set diffinhdr 0 | ||||
|
r267 | } else { | ||
set x [string range $line 0 0] | ||||
if {$x == "-" || $x == "+"} { | ||||
set tag [expr {$x == "+"}] | ||||
|
r1240 | if {$gaudydiff} { | ||
set line [string range $line 1 end] | ||||
} | ||||
|
r267 | $ctext insert end "$line\n" d$tag | ||
} elseif {$x == " "} { | ||||
|
r1240 | if {$gaudydiff} { | ||
set line [string range $line 1 end] | ||||
} | ||||
|
r267 | $ctext insert end "$line\n" | ||
|
r1240 | } elseif {$diffinhdr || $x == "\\"} { | ||
|
r267 | # e.g. "\ No newline at end of file" | ||
$ctext insert end "$line\n" filesep | ||||
} else { | ||||
# Something else we don't recognize | ||||
if {$curdifftag != "Comments"} { | ||||
$ctext insert end "\n" | ||||
$ctext tag add $curdifftag $curtagstart end | ||||
set curtagstart [$ctext index "end - 1c"] | ||||
set curdifftag Comments | ||||
} | ||||
$ctext insert end "$line\n" filesep | ||||
} | ||||
} | ||||
$ctext conf -state disabled | ||||
|
r1240 | if {[clock clicks -milliseconds] >= $nextupdate} { | ||
incr nextupdate 100 | ||||
fileevent $bdf readable {} | ||||
update | ||||
fileevent $bdf readable "getblobdiffline $bdf {$ids}" | ||||
} | ||||
|
r267 | } | ||
proc nextfile {} { | ||||
global difffilestart ctext | ||||
set here [$ctext index @0,0] | ||||
for {set i 0} {[info exists difffilestart($i)]} {incr i} { | ||||
if {[$ctext compare $difffilestart($i) > $here]} { | ||||
|
r1240 | if {![info exists pos] | ||
|| [$ctext compare $difffilestart($i) < $pos]} { | ||||
set pos $difffilestart($i) | ||||
} | ||||
|
r267 | } | ||
} | ||||
|
r1240 | if {[info exists pos]} { | ||
$ctext yview $pos | ||||
} | ||||
|
r267 | } | ||
proc listboxsel {} { | ||||
|
r1240 | global ctext cflist currentid | ||
|
r267 | if {![info exists currentid]} return | ||
|
r1240 | set sel [lsort [$cflist curselection]] | ||
if {$sel eq {}} return | ||||
set first [lindex $sel 0] | ||||
catch {$ctext yview fmark.$first} | ||||
|
r267 | } | ||
proc setcoords {} { | ||||
global linespc charspc canvx0 canvy0 mainfont | ||||
|
r1240 | global xspc1 xspc2 lthickness | ||
|
r267 | set linespc [font metrics $mainfont -linespace] | ||
set charspc [font measure $mainfont "m"] | ||||
set canvy0 [expr 3 + 0.5 * $linespc] | ||||
set canvx0 [expr 3 + 0.5 * $linespc] | ||||
|
r1240 | set lthickness [expr {int($linespc / 9) + 1}] | ||
set xspc1(0) $linespc | ||||
set xspc2 $linespc | ||||
|
r267 | } | ||
proc redisplay {} { | ||||
|
r1240 | global stopped redisplaying phase | ||
|
r267 | if {$stopped > 1} return | ||
if {$phase == "getcommits"} return | ||||
set redisplaying 1 | ||||
|
r1240 | if {$phase == "drawgraph" || $phase == "incrdraw"} { | ||
|
r267 | set stopped 1 | ||
} else { | ||||
drawgraph | ||||
} | ||||
} | ||||
proc incrfont {inc} { | ||||
|
r1240 | global mainfont namefont textfont ctext canv phase | ||
|
r267 | global stopped entries | ||
unmarkmatches | ||||
set mainfont [lreplace $mainfont 1 1 [expr {[lindex $mainfont 1] + $inc}]] | ||||
set namefont [lreplace $namefont 1 1 [expr {[lindex $namefont 1] + $inc}]] | ||||
set textfont [lreplace $textfont 1 1 [expr {[lindex $textfont 1] + $inc}]] | ||||
setcoords | ||||
$ctext conf -font $textfont | ||||
$ctext tag conf filesep -font [concat $textfont bold] | ||||
foreach e $entries { | ||||
$e conf -font $mainfont | ||||
} | ||||
if {$phase == "getcommits"} { | ||||
$canv itemconf textitems -font $mainfont | ||||
} | ||||
redisplay | ||||
} | ||||
|
r1240 | proc clearsha1 {} { | ||
global sha1entry sha1string | ||||
if {[string length $sha1string] == 40} { | ||||
$sha1entry delete 0 end | ||||
} | ||||
} | ||||
|
r267 | proc sha1change {n1 n2 op} { | ||
global sha1string currentid sha1but | ||||
if {$sha1string == {} | ||||
|| ([info exists currentid] && $sha1string == $currentid)} { | ||||
set state disabled | ||||
} else { | ||||
set state normal | ||||
} | ||||
if {[$sha1but cget -state] == $state} return | ||||
if {$state == "normal"} { | ||||
$sha1but conf -state normal -relief raised -text "Goto: " | ||||
} else { | ||||
$sha1but conf -state disabled -relief flat -text "SHA1 ID: " | ||||
} | ||||
} | ||||
proc gotocommit {} { | ||||
global sha1string currentid idline tagids | ||||
|
r1240 | global lineid numcommits | ||
|
r267 | if {$sha1string == {} | ||
|| ([info exists currentid] && $sha1string == $currentid)} return | ||||
if {[info exists tagids($sha1string)]} { | ||||
set id $tagids($sha1string) | ||||
} else { | ||||
set id [string tolower $sha1string] | ||||
|
r1240 | if {[regexp {^[0-9a-f]{4,39}$} $id]} { | ||
set matches {} | ||||
for {set l 0} {$l < $numcommits} {incr l} { | ||||
if {[string match $id* $lineid($l)]} { | ||||
lappend matches $lineid($l) | ||||
} | ||||
} | ||||
if {$matches ne {}} { | ||||
if {[llength $matches] > 1} { | ||||
error_popup "Short SHA1 id $id is ambiguous" | ||||
return | ||||
} | ||||
set id [lindex $matches 0] | ||||
} | ||||
} | ||||
|
r267 | } | ||
if {[info exists idline($id)]} { | ||||
|
r1240 | selectline $idline($id) 1 | ||
|
r267 | return | ||
} | ||||
|
r1240 | if {[regexp {^[0-9a-fA-F]{4,}$} $sha1string]} { | ||
|
r267 | set type "SHA1 id" | ||
} else { | ||||
set type "Tag" | ||||
} | ||||
error_popup "$type $sha1string is not known" | ||||
} | ||||
|
r1240 | proc lineenter {x y id} { | ||
global hoverx hovery hoverid hovertimer | ||||
global commitinfo canv | ||||
if {![info exists commitinfo($id)]} return | ||||
set hoverx $x | ||||
set hovery $y | ||||
set hoverid $id | ||||
if {[info exists hovertimer]} { | ||||
after cancel $hovertimer | ||||
} | ||||
set hovertimer [after 500 linehover] | ||||
$canv delete hover | ||||
} | ||||
proc linemotion {x y id} { | ||||
global hoverx hovery hoverid hovertimer | ||||
if {[info exists hoverid] && $id == $hoverid} { | ||||
set hoverx $x | ||||
set hovery $y | ||||
if {[info exists hovertimer]} { | ||||
after cancel $hovertimer | ||||
} | ||||
set hovertimer [after 500 linehover] | ||||
} | ||||
} | ||||
proc lineleave {id} { | ||||
global hoverid hovertimer canv | ||||
if {[info exists hoverid] && $id == $hoverid} { | ||||
$canv delete hover | ||||
if {[info exists hovertimer]} { | ||||
after cancel $hovertimer | ||||
unset hovertimer | ||||
} | ||||
unset hoverid | ||||
} | ||||
} | ||||
proc linehover {} { | ||||
global hoverx hovery hoverid hovertimer | ||||
global canv linespc lthickness | ||||
global commitinfo mainfont | ||||
set text [lindex $commitinfo($hoverid) 0] | ||||
set ymax [lindex [$canv cget -scrollregion] 3] | ||||
if {$ymax == {}} return | ||||
set yfrac [lindex [$canv yview] 0] | ||||
set x [expr {$hoverx + 2 * $linespc}] | ||||
set y [expr {$hovery + $yfrac * $ymax - $linespc / 2}] | ||||
set x0 [expr {$x - 2 * $lthickness}] | ||||
set y0 [expr {$y - 2 * $lthickness}] | ||||
set x1 [expr {$x + [font measure $mainfont $text] + 2 * $lthickness}] | ||||
set y1 [expr {$y + $linespc + 2 * $lthickness}] | ||||
set t [$canv create rectangle $x0 $y0 $x1 $y1 \ | ||||
-fill \#ffff80 -outline black -width 1 -tags hover] | ||||
$canv raise $t | ||||
set t [$canv create text $x $y -anchor nw -text $text -tags hover] | ||||
$canv raise $t | ||||
} | ||||
proc clickisonarrow {id y} { | ||||
global mainline mainlinearrow sidelines lthickness | ||||
set thresh [expr {2 * $lthickness + 6}] | ||||
if {[info exists mainline($id)]} { | ||||
if {$mainlinearrow($id) ne "none"} { | ||||
if {abs([lindex $mainline($id) 1] - $y) < $thresh} { | ||||
return "up" | ||||
} | ||||
} | ||||
} | ||||
if {[info exists sidelines($id)]} { | ||||
foreach ls $sidelines($id) { | ||||
set coords [lindex $ls 0] | ||||
set arrow [lindex $ls 2] | ||||
if {$arrow eq "first" || $arrow eq "both"} { | ||||
if {abs([lindex $coords 1] - $y) < $thresh} { | ||||
return "up" | ||||
} | ||||
} | ||||
if {$arrow eq "last" || $arrow eq "both"} { | ||||
if {abs([lindex $coords end] - $y) < $thresh} { | ||||
return "down" | ||||
} | ||||
} | ||||
} | ||||
} | ||||
return {} | ||||
} | ||||
proc arrowjump {id dirn y} { | ||||
global mainline sidelines canv | ||||
set yt {} | ||||
if {$dirn eq "down"} { | ||||
if {[info exists mainline($id)]} { | ||||
set y1 [lindex $mainline($id) 1] | ||||
if {$y1 > $y} { | ||||
set yt $y1 | ||||
} | ||||
} | ||||
if {[info exists sidelines($id)]} { | ||||
foreach ls $sidelines($id) { | ||||
set y1 [lindex $ls 0 1] | ||||
if {$y1 > $y && ($yt eq {} || $y1 < $yt)} { | ||||
set yt $y1 | ||||
} | ||||
} | ||||
} | ||||
} else { | ||||
if {[info exists sidelines($id)]} { | ||||
foreach ls $sidelines($id) { | ||||
set y1 [lindex $ls 0 end] | ||||
if {$y1 < $y && ($yt eq {} || $y1 > $yt)} { | ||||
set yt $y1 | ||||
} | ||||
} | ||||
} | ||||
} | ||||
if {$yt eq {}} return | ||||
set ymax [lindex [$canv cget -scrollregion] 3] | ||||
if {$ymax eq {} || $ymax <= 0} return | ||||
set view [$canv yview] | ||||
set yspan [expr {[lindex $view 1] - [lindex $view 0]}] | ||||
set yfrac [expr {$yt / $ymax - $yspan / 2}] | ||||
if {$yfrac < 0} { | ||||
set yfrac 0 | ||||
} | ||||
$canv yview moveto $yfrac | ||||
} | ||||
proc lineclick {x y id isnew} { | ||||
global ctext commitinfo children cflist canv thickerline | ||||
unmarkmatches | ||||
unselectline | ||||
normalline | ||||
$canv delete hover | ||||
# draw this line thicker than normal | ||||
drawlines $id 1 | ||||
set thickerline $id | ||||
if {$isnew} { | ||||
set ymax [lindex [$canv cget -scrollregion] 3] | ||||
if {$ymax eq {}} return | ||||
set yfrac [lindex [$canv yview] 0] | ||||
set y [expr {$y + $yfrac * $ymax}] | ||||
} | ||||
set dirn [clickisonarrow $id $y] | ||||
if {$dirn ne {}} { | ||||
arrowjump $id $dirn $y | ||||
return | ||||
} | ||||
if {$isnew} { | ||||
addtohistory [list lineclick $x $y $id 0] | ||||
} | ||||
# fill the details pane with info about this line | ||||
$ctext conf -state normal | ||||
$ctext delete 0.0 end | ||||
$ctext tag conf link -foreground blue -underline 1 | ||||
$ctext tag bind link <Enter> { %W configure -cursor hand2 } | ||||
$ctext tag bind link <Leave> { %W configure -cursor $curtextcursor } | ||||
$ctext insert end "Parent:\t" | ||||
$ctext insert end $id [list link link0] | ||||
$ctext tag bind link0 <1> [list selbyid $id] | ||||
set info $commitinfo($id) | ||||
$ctext insert end "\n\t[lindex $info 0]\n" | ||||
$ctext insert end "\tAuthor:\t[lindex $info 1]\n" | ||||
$ctext insert end "\tDate:\t[lindex $info 2]\n" | ||||
if {[info exists children($id)]} { | ||||
$ctext insert end "\nChildren:" | ||||
set i 0 | ||||
foreach child $children($id) { | ||||
incr i | ||||
set info $commitinfo($child) | ||||
$ctext insert end "\n\t" | ||||
$ctext insert end $child [list link link$i] | ||||
$ctext tag bind link$i <1> [list selbyid $child] | ||||
$ctext insert end "\n\t[lindex $info 0]" | ||||
$ctext insert end "\n\tAuthor:\t[lindex $info 1]" | ||||
$ctext insert end "\n\tDate:\t[lindex $info 2]\n" | ||||
} | ||||
} | ||||
$ctext conf -state disabled | ||||
$cflist delete 0 end | ||||
} | ||||
proc normalline {} { | ||||
global thickerline | ||||
if {[info exists thickerline]} { | ||||
drawlines $thickerline 0 | ||||
unset thickerline | ||||
} | ||||
} | ||||
proc selbyid {id} { | ||||
global idline | ||||
if {[info exists idline($id)]} { | ||||
selectline $idline($id) 1 | ||||
} | ||||
} | ||||
proc mstime {} { | ||||
global startmstime | ||||
if {![info exists startmstime]} { | ||||
set startmstime [clock clicks -milliseconds] | ||||
} | ||||
return [format "%.3f" [expr {([clock click -milliseconds] - $startmstime) / 1000.0}]] | ||||
} | ||||
proc rowmenu {x y id} { | ||||
global rowctxmenu idline selectedline rowmenuid | ||||
if {![info exists selectedline] || $idline($id) eq $selectedline} { | ||||
set state disabled | ||||
} else { | ||||
set state normal | ||||
} | ||||
$rowctxmenu entryconfigure 0 -state $state | ||||
$rowctxmenu entryconfigure 1 -state $state | ||||
$rowctxmenu entryconfigure 2 -state $state | ||||
set rowmenuid $id | ||||
tk_popup $rowctxmenu $x $y | ||||
} | ||||
proc diffvssel {dirn} { | ||||
global rowmenuid selectedline lineid | ||||
if {![info exists selectedline]} return | ||||
if {$dirn} { | ||||
set oldid $lineid($selectedline) | ||||
set newid $rowmenuid | ||||
} else { | ||||
set oldid $rowmenuid | ||||
set newid $lineid($selectedline) | ||||
} | ||||
addtohistory [list doseldiff $oldid $newid] | ||||
doseldiff $oldid $newid | ||||
} | ||||
proc doseldiff {oldid newid} { | ||||
global ctext cflist | ||||
global commitinfo | ||||
$ctext conf -state normal | ||||
$ctext delete 0.0 end | ||||
$ctext mark set fmark.0 0.0 | ||||
$ctext mark gravity fmark.0 left | ||||
$cflist delete 0 end | ||||
$cflist insert end "Top" | ||||
$ctext insert end "From " | ||||
$ctext tag conf link -foreground blue -underline 1 | ||||
$ctext tag bind link <Enter> { %W configure -cursor hand2 } | ||||
$ctext tag bind link <Leave> { %W configure -cursor $curtextcursor } | ||||
$ctext tag bind link0 <1> [list selbyid $oldid] | ||||
$ctext insert end $oldid [list link link0] | ||||
$ctext insert end "\n " | ||||
$ctext insert end [lindex $commitinfo($oldid) 0] | ||||
$ctext insert end "\n\nTo " | ||||
$ctext tag bind link1 <1> [list selbyid $newid] | ||||
$ctext insert end $newid [list link link1] | ||||
$ctext insert end "\n " | ||||
$ctext insert end [lindex $commitinfo($newid) 0] | ||||
$ctext insert end "\n" | ||||
$ctext conf -state disabled | ||||
$ctext tag delete Comments | ||||
$ctext tag remove found 1.0 end | ||||
startdiff [list $newid $oldid] | ||||
} | ||||
proc mkpatch {} { | ||||
global rowmenuid currentid commitinfo patchtop patchnum | ||||
if {![info exists currentid]} return | ||||
set oldid $currentid | ||||
set oldhead [lindex $commitinfo($oldid) 0] | ||||
set newid $rowmenuid | ||||
set newhead [lindex $commitinfo($newid) 0] | ||||
set top .patch | ||||
set patchtop $top | ||||
catch {destroy $top} | ||||
toplevel $top | ||||
label $top.title -text "Generate patch" | ||||
grid $top.title - -pady 10 | ||||
label $top.from -text "From:" | ||||
entry $top.fromsha1 -width 40 -relief flat | ||||
$top.fromsha1 insert 0 $oldid | ||||
$top.fromsha1 conf -state readonly | ||||
grid $top.from $top.fromsha1 -sticky w | ||||
entry $top.fromhead -width 60 -relief flat | ||||
$top.fromhead insert 0 $oldhead | ||||
$top.fromhead conf -state readonly | ||||
grid x $top.fromhead -sticky w | ||||
label $top.to -text "To:" | ||||
entry $top.tosha1 -width 40 -relief flat | ||||
$top.tosha1 insert 0 $newid | ||||
$top.tosha1 conf -state readonly | ||||
grid $top.to $top.tosha1 -sticky w | ||||
entry $top.tohead -width 60 -relief flat | ||||
$top.tohead insert 0 $newhead | ||||
$top.tohead conf -state readonly | ||||
grid x $top.tohead -sticky w | ||||
button $top.rev -text "Reverse" -command mkpatchrev -padx 5 | ||||
grid $top.rev x -pady 10 | ||||
label $top.flab -text "Output file:" | ||||
entry $top.fname -width 60 | ||||
$top.fname insert 0 [file normalize "patch$patchnum.patch"] | ||||
incr patchnum | ||||
grid $top.flab $top.fname -sticky w | ||||
frame $top.buts | ||||
button $top.buts.gen -text "Generate" -command mkpatchgo | ||||
button $top.buts.can -text "Cancel" -command mkpatchcan | ||||
grid $top.buts.gen $top.buts.can | ||||
grid columnconfigure $top.buts 0 -weight 1 -uniform a | ||||
grid columnconfigure $top.buts 1 -weight 1 -uniform a | ||||
grid $top.buts - -pady 10 -sticky ew | ||||
focus $top.fname | ||||
} | ||||
proc mkpatchrev {} { | ||||
global patchtop | ||||
set oldid [$patchtop.fromsha1 get] | ||||
set oldhead [$patchtop.fromhead get] | ||||
set newid [$patchtop.tosha1 get] | ||||
set newhead [$patchtop.tohead get] | ||||
foreach e [list fromsha1 fromhead tosha1 tohead] \ | ||||
v [list $newid $newhead $oldid $oldhead] { | ||||
$patchtop.$e conf -state normal | ||||
$patchtop.$e delete 0 end | ||||
$patchtop.$e insert 0 $v | ||||
$patchtop.$e conf -state readonly | ||||
} | ||||
} | ||||
proc mkpatchgo {} { | ||||
global patchtop | ||||
set oldid [$patchtop.fromsha1 get] | ||||
set newid [$patchtop.tosha1 get] | ||||
set fname [$patchtop.fname get] | ||||
|
r1278 | if {[catch {exec hg debug-diff-tree -p $oldid $newid >$fname &} err]} { | ||
|
r1240 | error_popup "Error creating patch: $err" | ||
} | ||||
catch {destroy $patchtop} | ||||
unset patchtop | ||||
} | ||||
proc mkpatchcan {} { | ||||
global patchtop | ||||
catch {destroy $patchtop} | ||||
unset patchtop | ||||
} | ||||
proc mktag {} { | ||||
global rowmenuid mktagtop commitinfo | ||||
set top .maketag | ||||
set mktagtop $top | ||||
catch {destroy $top} | ||||
toplevel $top | ||||
label $top.title -text "Create tag" | ||||
grid $top.title - -pady 10 | ||||
label $top.id -text "ID:" | ||||
entry $top.sha1 -width 40 -relief flat | ||||
$top.sha1 insert 0 $rowmenuid | ||||
$top.sha1 conf -state readonly | ||||
grid $top.id $top.sha1 -sticky w | ||||
entry $top.head -width 60 -relief flat | ||||
$top.head insert 0 [lindex $commitinfo($rowmenuid) 0] | ||||
$top.head conf -state readonly | ||||
grid x $top.head -sticky w | ||||
label $top.tlab -text "Tag name:" | ||||
entry $top.tag -width 60 | ||||
grid $top.tlab $top.tag -sticky w | ||||
frame $top.buts | ||||
button $top.buts.gen -text "Create" -command mktaggo | ||||
button $top.buts.can -text "Cancel" -command mktagcan | ||||
grid $top.buts.gen $top.buts.can | ||||
grid columnconfigure $top.buts 0 -weight 1 -uniform a | ||||
grid columnconfigure $top.buts 1 -weight 1 -uniform a | ||||
grid $top.buts - -pady 10 -sticky ew | ||||
focus $top.tag | ||||
} | ||||
proc domktag {} { | ||||
global mktagtop env tagids idtags | ||||
set id [$mktagtop.sha1 get] | ||||
set tag [$mktagtop.tag get] | ||||
if {$tag == {}} { | ||||
error_popup "No tag name specified" | ||||
return | ||||
} | ||||
if {[info exists tagids($tag)]} { | ||||
error_popup "Tag \"$tag\" already exists" | ||||
return | ||||
} | ||||
if {[catch { | ||||
|
r2058 | set out [exec hg tag -r $id $tag] | ||
|
r1240 | } err]} { | ||
error_popup "Error creating tag: $err" | ||||
return | ||||
} | ||||
set tagids($tag) $id | ||||
lappend idtags($id) $tag | ||||
redrawtags $id | ||||
} | ||||
proc redrawtags {id} { | ||||
global canv linehtag idline idpos selectedline | ||||
if {![info exists idline($id)]} return | ||||
$canv delete tag.$id | ||||
set xt [eval drawtags $id $idpos($id)] | ||||
$canv coords $linehtag($idline($id)) $xt [lindex $idpos($id) 2] | ||||
if {[info exists selectedline] && $selectedline == $idline($id)} { | ||||
selectline $selectedline 0 | ||||
} | ||||
} | ||||
proc mktagcan {} { | ||||
global mktagtop | ||||
catch {destroy $mktagtop} | ||||
unset mktagtop | ||||
} | ||||
proc mktaggo {} { | ||||
domktag | ||||
mktagcan | ||||
} | ||||
proc writecommit {} { | ||||
global rowmenuid wrcomtop commitinfo wrcomcmd | ||||
set top .writecommit | ||||
set wrcomtop $top | ||||
catch {destroy $top} | ||||
toplevel $top | ||||
label $top.title -text "Write commit to file" | ||||
grid $top.title - -pady 10 | ||||
label $top.id -text "ID:" | ||||
entry $top.sha1 -width 40 -relief flat | ||||
$top.sha1 insert 0 $rowmenuid | ||||
$top.sha1 conf -state readonly | ||||
grid $top.id $top.sha1 -sticky w | ||||
entry $top.head -width 60 -relief flat | ||||
$top.head insert 0 [lindex $commitinfo($rowmenuid) 0] | ||||
$top.head conf -state readonly | ||||
grid x $top.head -sticky w | ||||
label $top.clab -text "Command:" | ||||
entry $top.cmd -width 60 -textvariable wrcomcmd | ||||
grid $top.clab $top.cmd -sticky w -pady 10 | ||||
label $top.flab -text "Output file:" | ||||
entry $top.fname -width 60 | ||||
$top.fname insert 0 [file normalize "commit-[string range $rowmenuid 0 6]"] | ||||
grid $top.flab $top.fname -sticky w | ||||
frame $top.buts | ||||
button $top.buts.gen -text "Write" -command wrcomgo | ||||
button $top.buts.can -text "Cancel" -command wrcomcan | ||||
grid $top.buts.gen $top.buts.can | ||||
grid columnconfigure $top.buts 0 -weight 1 -uniform a | ||||
grid columnconfigure $top.buts 1 -weight 1 -uniform a | ||||
grid $top.buts - -pady 10 -sticky ew | ||||
focus $top.fname | ||||
} | ||||
proc wrcomgo {} { | ||||
global wrcomtop | ||||
set id [$wrcomtop.sha1 get] | ||||
set cmd "echo $id | [$wrcomtop.cmd get]" | ||||
set fname [$wrcomtop.fname get] | ||||
if {[catch {exec sh -c $cmd >$fname &} err]} { | ||||
error_popup "Error writing commit: $err" | ||||
} | ||||
catch {destroy $wrcomtop} | ||||
unset wrcomtop | ||||
} | ||||
proc wrcomcan {} { | ||||
global wrcomtop | ||||
catch {destroy $wrcomtop} | ||||
unset wrcomtop | ||||
} | ||||
proc listrefs {id} { | ||||
global idtags idheads idotherrefs | ||||
set x {} | ||||
if {[info exists idtags($id)]} { | ||||
set x $idtags($id) | ||||
} | ||||
set y {} | ||||
if {[info exists idheads($id)]} { | ||||
set y $idheads($id) | ||||
} | ||||
set z {} | ||||
if {[info exists idotherrefs($id)]} { | ||||
set z $idotherrefs($id) | ||||
} | ||||
return [list $x $y $z] | ||||
} | ||||
proc rereadrefs {} { | ||||
global idtags idheads idotherrefs | ||||
global tagids headids otherrefids | ||||
set refids [concat [array names idtags] \ | ||||
[array names idheads] [array names idotherrefs]] | ||||
foreach id $refids { | ||||
if {![info exists ref($id)]} { | ||||
set ref($id) [listrefs $id] | ||||
} | ||||
} | ||||
foreach v {tagids idtags headids idheads otherrefids idotherrefs} { | ||||
catch {unset $v} | ||||
} | ||||
readrefs | ||||
set refids [lsort -unique [concat $refids [array names idtags] \ | ||||
[array names idheads] [array names idotherrefs]]] | ||||
foreach id $refids { | ||||
set v [listrefs $id] | ||||
if {![info exists ref($id)] || $ref($id) != $v} { | ||||
redrawtags $id | ||||
} | ||||
} | ||||
} | ||||
proc showtag {tag isnew} { | ||||
global ctext cflist tagcontents tagids linknum | ||||
if {$isnew} { | ||||
addtohistory [list showtag $tag 0] | ||||
} | ||||
$ctext conf -state normal | ||||
$ctext delete 0.0 end | ||||
set linknum 0 | ||||
if {[info exists tagcontents($tag)]} { | ||||
set text $tagcontents($tag) | ||||
} else { | ||||
set text "Tag: $tag\nId: $tagids($tag)" | ||||
} | ||||
appendwithlinks $text | ||||
$ctext conf -state disabled | ||||
$cflist delete 0 end | ||||
} | ||||
|
r267 | proc doquit {} { | ||
global stopped | ||||
set stopped 100 | ||||
destroy . | ||||
} | ||||
# defaults... | ||||
set datemode 0 | ||||
set boldnames 0 | ||||
set diffopts "-U 5 -p" | ||||
|
r1278 | set wrcomcmd "hg debug-diff-tree --stdin -p --pretty" | ||
|
r267 | |||
set mainfont {Helvetica 9} | ||||
set textfont {Courier 9} | ||||
|
r1240 | set findmergefiles 0 | ||
set gaudydiff 0 | ||||
set maxgraphpct 50 | ||||
set maxwidth 16 | ||||
|
r267 | |||
set colors {green red blue magenta darkgrey brown orange} | ||||
catch {source ~/.gitk} | ||||
set namefont $mainfont | ||||
if {$boldnames} { | ||||
lappend namefont bold | ||||
} | ||||
set revtreeargs {} | ||||
foreach arg $argv { | ||||
switch -regexp -- $arg { | ||||
"^$" { } | ||||
"^-b" { set boldnames 1 } | ||||
"^-d" { set datemode 1 } | ||||
default { | ||||
lappend revtreeargs $arg | ||||
} | ||||
} | ||||
} | ||||
|
r1240 | set history {} | ||
set historyindex 0 | ||||
|
r267 | set stopped 0 | ||
set redisplaying 0 | ||||
set stuffsaved 0 | ||||
|
r1240 | set patchnum 0 | ||
|
r267 | setcoords | ||
makewindow | ||||
readrefs | ||||
|
r1240 | getcommits $revtreeargs | ||