From f806f0fba8ce62f2c74f21419e2bd1e7ce9e565b Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Sun, 24 Feb 2008 12:16:56 +1100 Subject: [PATCH] gitk: Handle updating with path limiting better When updating the graph, gitk uses a git log command with commit limiting in order to get just the new commits. When path limiting is also in effect, git log rewrites the parents of the commits it outputs in order to represent just the subgraph that modifies the listed paths, but it doesn't rewrite the parents on the boundary of the graph. The result is that when updating, git log does not give gitk the information about where the new commits join in to the existing graph. This solves the problem by explicitly rewriting boundary parents when updating. If we are updating and are doing path limiting, then when gitk finds an unlisted commit (one where git log puts a "-" in front of the commit ID to indicate that it isn't actually part of the graph), then gitk will execute: git rev-list --first-parent --max-count=1 $id -- paths... which returns the first ancestor that affects the listed paths. (Currently gitk executes this synchronously; it could do it asynchronously, which would be more complex but would avoid the possibility of the UI freezing up if git rev-list takes a long time.) Then, if the result is a commit that we know about, we rewrite the parents of the children of the original commit to point to the new commit. That is mostly a matter of adjusting the parents and children arrays and calling fix_reversal to fix up the graph. Signed-off-by: Paul Mackerras --- gitk | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 55 insertions(+), 4 deletions(-) diff --git a/gitk b/gitk index a50ef79479..5925ced55b 100755 --- a/gitk +++ b/gitk @@ -133,7 +133,7 @@ proc start_rev_list {view} { if {$tclencoding != {}} { fconfigure $fd -encoding $tclencoding } - filerun $fd [list getcommitlines $fd $i $view] + filerun $fd [list getcommitlines $fd $i $view 0] nowbusy $view [mc "Reading"] if {$view == $curview} { set progressdirn 1 @@ -227,7 +227,7 @@ proc updatecommits {} { if {$tclencoding != {}} { fconfigure $fd -encoding $tclencoding } - filerun $fd [list getcommitlines $fd $i $view] + filerun $fd [list getcommitlines $fd $i $view 1] incr viewactive($view) set viewcomplete($view) 0 set pending_select $mainheadid @@ -555,6 +555,9 @@ proc renumbervarc {a v} { #puts "renumbervarc did [llength $todo] of $ntot arcs in [expr {$t2-$t1}]ms" } +# Fix up the graph after we have found out that in view $v, +# $p (a commit that we have already seen) is actually the parent +# of the last commit in arc $a. proc fix_reversal {p a v} { global varcid varcstart varctok vupptr @@ -964,12 +967,40 @@ proc closevarcs {v} { } } -proc getcommitlines {fd inst view} { +# Use $rwid as a substitute for $id, i.e. reparent $id's children to $rwid +# Assumes we already have an arc for $rwid. +proc rewrite_commit {v id rwid} { + global children parents varcid varctok vtokmod varccommits + + foreach ch $children($v,$id) { + # make $rwid be $ch's parent in place of $id + set i [lsearch -exact $parents($v,$ch) $id] + if {$i < 0} { + puts "oops rewrite_commit didn't find $id in parent list for $ch" + } + set parents($v,$ch) [lreplace $parents($v,$ch) $i $i $rwid] + # add $ch to $rwid's children and sort the list if necessary + if {[llength [lappend children($v,$rwid) $ch]] > 1} { + set children($v,$rwid) [lsort -command [list vtokcmp $v] \ + $children($v,$rwid)] + } + # fix the graph after joining $id to $rwid + set a $varcid($v,$ch) + fix_reversal $rwid $a $v + if {[string compare [lindex $varctok($v) $a] $vtokmod($v)] < 0} { + # parentlist is wrong for the last element of arc $a + # even if displayorder is right, hence the 3rd arg here + modify_arc $v $a [expr {[llength $varccommits($v,$a)] - 1}] + } + } +} + +proc getcommitlines {fd inst view updating} { global cmitlisted commitinterest leftover global commitidx commitdata datemode global parents children curview hlview global vnextroot idpending ordertok - global varccommits varcid varctok vtokmod + global varccommits varcid varctok vtokmod viewfiles set stuff [read $fd 500000] # git log doesn't terminate the last commit with a null... @@ -1070,6 +1101,26 @@ proc getcommitlines {fd inst view} { } set id [lindex $ids 0] set vid $view,$id + + if {!$listed && $updating && ![info exists varcid($vid)] && + $viewfiles($view) ne {}} { + # git log doesn't rewrite parents for unlisted commits + # when doing path limiting, so work around that here + # by working out the rewritten parent with git rev-list + # and if we already know about it, using the rewritten + # parent as a substitute parent for $id's children. + if {![catch { + set rwid [exec git rev-list --first-parent --max-count=1 \ + $id -- $viewfiles($view)] + }]} { + if {$rwid ne {} && [info exists varcid($view,$rwid)]} { + # use $rwid in place of $id + rewrite_commit $view $id $rwid + continue + } + } + } + set a 0 if {[info exists varcid($vid)]} { if {$cmitlisted($vid) || !$listed} continue -- 2.11.4.GIT