@matheusbsbs / matheus-bagliano / commits / a0e46390d3
Fork 0

filter-branch: fix ref rewriting with --subdirectory-filter

The previous ancestor discovery code failed on any refs that are (pre-rewrite) ancestors of commits marked for rewriting. This means that in a situation A -- B(topic) -- C(master) where B is dropped by --subdirectory-filter pruning, the 'topic' was not moved up to A as intended, but left unrewritten because we asked about 'git rev-list ^master topic', which does not return anything. Instead, we use the straightforward git rev-list -1 $ref -- $filter_subdir to find the right ancestor. To justify this, note that the nearest ancestor is unique: We use the output of git rev-list --parents -- $filter_subdir to rewrite commits in the first pass, before any ref rewriting. If B is a non-merge commit, the only candidate is its parent. If it is a merge, there are two cases: - All sides of the merge bring the same subdirectory contents. Then rev-list already pruned away the merge in favour for just one of its parents, so there is only one candidate. - Some merge sides, or the merge outcome, differ. Then the merge is not pruned and can be rewritten directly. So it is always safe to use rev-list -1. Signed-off-by: Thomas Rast <trast@student.ethz.ch> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Thomas Rast committed Aug 12, 2008 at 10:45 UTC a0e46390d397e71182e42930b98b6b59a1a84898
2 files changed +12 -17
git-filter-branch.sh
+11 -16
@@ -317,24 +317,19 @@ done <../revs
317
318 # In case of a subdirectory filter, it is possible that a specified head
319 # is not in the set of rewritten commits, because it was pruned by the
320 -# revision walker. Fix it by mapping these heads to the next rewritten
321 -# ancestor(s), i.e. the boundaries in the set of rewritten commits.
320 +# revision walker. Fix it by mapping these heads to the unique nearest
321 +# ancestor that survived the pruning.
322
323 -# NEEDSWORK: we should sort the unmapped refs topologically first
324 -while read ref
325 -do
326 - sha1=$(git rev-parse "$ref"^0)
327 - test -f "$workdir"/../map/$sha1 && continue
328 - # Assign the boundarie(s) in the set of rewritten commits
329 - # as the replacement commit(s).
330 - # (This would look a bit nicer if --not --stdin worked.)
331 - for p in $( (cd "$workdir"/../map; ls | sed "s/^/^/") |
332 - git rev-list $ref --boundary --stdin |
333 - sed -n "s/^-//p")
323 +if test "$filter_subdir"
324 +then
325 + while read ref
326 do
335 - map $p >> "$workdir"/../map/$sha1
336 - done
337 -done < "$tempdir"/heads
327 + sha1=$(git rev-parse "$ref"^0)
328 + test -f "$workdir"/../map/$sha1 && continue
329 + ancestor=$(git rev-list -1 $ref -- "$filter_subdir")
330 + test "$ancestor" && echo $(map $ancestor) >> "$workdir"/../map/$sha1
331 + done < "$tempdir"/heads
332 +fi
333
334 # Finally update the refs
335
t/t7003-filter-branch.sh
+1 -1
@@ -101,7 +101,7 @@ test_expect_success 'filter subdirectory only' '
101 refs/heads/sub refs/heads/sub-earlier
102 '
103
104 -test_expect_failure 'subdirectory filter result looks okay' '
104 +test_expect_success 'subdirectory filter result looks okay' '
105 test 2 = $(git rev-list sub | wc -l) &&
106 git show sub:new &&
107 test_must_fail git show sub:subdir &&