path: root/
diff options
authorThomas Rast <>2008-08-12 08:45:58 (GMT)
committerJunio C Hamano <>2008-08-13 00:27:17 (GMT)
commita0e46390d397e71182e42930b98b6b59a1a84898 (patch)
tree9c38ceb70da87ef412ea2ce5eca887549f482a90 /
parent6e84b712373d343ea3531740d8eb048e84240a39 (diff)
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 <> Signed-off-by: Junio C Hamano <>
Diffstat (limited to '')
1 files changed, 11 insertions, 16 deletions
diff --git a/ b/
index a324cf0..a140337 100755
--- a/
+++ b/
@@ -317,24 +317,19 @@ done <../revs
# In case of a subdirectory filter, it is possible that a specified head
# is not in the set of rewritten commits, because it was pruned by the
-# revision walker. Fix it by mapping these heads to the next rewritten
-# ancestor(s), i.e. the boundaries in the set of rewritten commits.
+# revision walker. Fix it by mapping these heads to the unique nearest
+# ancestor that survived the pruning.
-# NEEDSWORK: we should sort the unmapped refs topologically first
-while read ref
- sha1=$(git rev-parse "$ref"^0)
- test -f "$workdir"/../map/$sha1 && continue
- # Assign the boundarie(s) in the set of rewritten commits
- # as the replacement commit(s).
- # (This would look a bit nicer if --not --stdin worked.)
- for p in $( (cd "$workdir"/../map; ls | sed "s/^/^/") |
- git rev-list $ref --boundary --stdin |
- sed -n "s/^-//p")
+if test "$filter_subdir"
+ while read ref
- map $p >> "$workdir"/../map/$sha1
- done
-done < "$tempdir"/heads
+ sha1=$(git rev-parse "$ref"^0)
+ test -f "$workdir"/../map/$sha1 && continue
+ ancestor=$(git rev-list -1 $ref -- "$filter_subdir")
+ test "$ancestor" && echo $(map $ancestor) >> "$workdir"/../map/$sha1
+ done < "$tempdir"/heads
# Finally update the refs