filter-branch: nearest-ancestor rewriting outside subdir filter

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>
This commit is contained in:
Thomas Rast 2009-11-10 22:04:51 +01:00 committed by Junio C Hamano
parent 2c1d2d8188
commit f2f3a6b802
3 changed files with 43 additions and 6 deletions

View File

@ -159,7 +159,18 @@ to other tags will be rewritten to point to the underlying commit.
--subdirectory-filter <directory>:: --subdirectory-filter <directory>::
Only look at the history which touches the given subdirectory. Only look at the history which touches the given subdirectory.
The result will contain that directory (and only that) as its 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:: --prune-empty::
Some kind of filters will generate empty commits, that left the tree Some kind of filters will generate empty commits, that left the tree

View File

@ -125,6 +125,7 @@ filter_subdir=
orig_namespace=refs/original/ orig_namespace=refs/original/
force= force=
prune_empty= prune_empty=
remap_to_ancestor=
while : while :
do do
case "$1" in case "$1" in
@ -137,6 +138,11 @@ do
force=t force=t
continue continue
;; ;;
--remap-to-ancestor)
shift
remap_to_ancestor=t
continue
;;
--prune-empty) --prune-empty)
shift shift
prune_empty=t prune_empty=t
@ -182,6 +188,7 @@ do
;; ;;
--subdirectory-filter) --subdirectory-filter)
filter_subdir="$OPTARG" filter_subdir="$OPTARG"
remap_to_ancestor=t
;; ;;
--original) --original)
orig_namespace=$(expr "$OPTARG/" : '\(.*[^/]\)/*$')/ orig_namespace=$(expr "$OPTARG/" : '\(.*[^/]\)/*$')/
@ -354,12 +361,13 @@ while read commit parents; do
die "could not write rewritten commit" die "could not write rewritten commit"
done <../revs done <../revs
# In case of a subdirectory filter, it is possible that a specified head # If we are filtering for paths, as in the case of a subdirectory
# is not in the set of rewritten commits, because it was pruned by the # filter, it is possible that a specified head is not in the set of
# revision walker. Fix it by mapping these heads to the unique nearest # rewritten commits, because it was pruned by the revision walker.
# ancestor that survived the pruning. # 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 then
while read ref while read ref
do do

View File

@ -288,4 +288,22 @@ test_expect_success 'Prune empty commits' '
test_cmp expect actual 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 test_done