Skip to content

Commit

Permalink
filter-branch: nearest-ancestor rewriting outside subdir filter
Browse files Browse the repository at this point in the history
Since a0e4639 (filter-branch: fix ref rewriting with
--subdirectory-filter, 2008-08-12) git-filter-branch has done
nearest-ancestor rewriting when using a --subdirectory-filter.

However, that rewriting strategy is also a useful building block in
other tasks.  For example, if you want to split out a subset of files
from your history, you would typically call

  git filter-branch -- <refs> -- <files>

But this fails for all refs that do not point directly to a commit
that affects <files>, because their referenced commit will not be
rewritten and the ref remains untouched.

The code was already there for the --subdirectory-filter case, so just
introduce an option that enables it independently.

Signed-off-by: Thomas Rast <trast@student.ethz.ch>
Signed-off-by: Johannes Sixt <j6t@kdbg.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
  • Loading branch information
trast authored and gitster committed Nov 13, 2009
1 parent 2c1d2d8 commit f2f3a6b
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 6 deletions.
13 changes: 12 additions & 1 deletion Documentation/git-filter-branch.txt
Expand Up @@ -159,7 +159,18 @@ to other tags will be rewritten to point to the underlying commit.
--subdirectory-filter <directory>::
Only look at the history which touches the given subdirectory.
The result will contain that directory (and only that) as its
project root.
project root. Implies --remap-to-ancestor.

--remap-to-ancestor::
Rewrite refs to the nearest rewritten ancestor instead of
ignoring them.
+
Normally, positive refs on the command line are only changed if the
commit they point to was rewritten. However, you can limit the extent
of this rewriting by using linkgit:rev-list[1] arguments, e.g., path
limiters. Refs pointing to such excluded commits would then normally
be ignored. With this option, they are instead rewritten to point at
the nearest ancestor that was not excluded.

--prune-empty::
Some kind of filters will generate empty commits, that left the tree
Expand Down
18 changes: 13 additions & 5 deletions git-filter-branch.sh
Expand Up @@ -125,6 +125,7 @@ filter_subdir=
orig_namespace=refs/original/
force=
prune_empty=
remap_to_ancestor=
while :
do
case "$1" in
Expand All @@ -137,6 +138,11 @@ do
force=t
continue
;;
--remap-to-ancestor)
shift
remap_to_ancestor=t
continue
;;
--prune-empty)
shift
prune_empty=t
Expand Down Expand Up @@ -182,6 +188,7 @@ do
;;
--subdirectory-filter)
filter_subdir="$OPTARG"
remap_to_ancestor=t
;;
--original)
orig_namespace=$(expr "$OPTARG/" : '\(.*[^/]\)/*$')/
Expand Down Expand Up @@ -354,12 +361,13 @@ while read commit parents; do
die "could not write rewritten commit"
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 unique nearest
# ancestor that survived the pruning.
# If we are filtering for paths, as in the 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.
# Ancestor remapping fixes this by mapping these heads to the unique
# nearest ancestor that survived the pruning.

if test "$filter_subdir"
if test "$remap_to_ancestor" = t
then
while read ref
do
Expand Down
18 changes: 18 additions & 0 deletions t/t7003-filter-branch.sh
Expand Up @@ -288,4 +288,22 @@ test_expect_success 'Prune empty commits' '
test_cmp expect actual
'

test_expect_success '--remap-to-ancestor with filename filters' '
git checkout master &&
git reset --hard A &&
test_commit add-foo foo 1 &&
git branch moved-foo &&
test_commit add-bar bar a &&
git branch invariant &&
orig_invariant=$(git rev-parse invariant) &&
git branch moved-bar &&
test_commit change-foo foo 2 &&
git filter-branch -f --remap-to-ancestor \
moved-foo moved-bar A..master \
-- -- foo &&
test $(git rev-parse moved-foo) = $(git rev-parse moved-bar) &&
test $(git rev-parse moved-foo) = $(git rev-parse master^) &&
test $orig_invariant = $(git rev-parse invariant)
'

test_done

0 comments on commit f2f3a6b

Please sign in to comment.