filter-branch: fail gracefully when a filter fails

A common mistake is to provide a filter which fails unwantedly. For
example, this will stop in the middle:

	git filter-branch --env-filter '
		test $GIT_COMMITTER_EMAIL = xyz &&
		export GIT_COMMITTER_EMAIL = abc' rewritten

When $GIT_COMMITTER_EMAIL is not "xyz", the test fails, and consequently
the whole filter has a non-zero exit status. However, as demonstrated
in this example, filter-branch would just stop, and the user would be
none the wiser.

Also, a failing msg-filter would not have been caught, as was the
case with one of the tests.

This patch fixes both issues, by paying attention to the exit status
of msg-filter, and by saying what failed before exiting.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Johannes Schindelin 2007-07-04 15:36:01 +01:00 committed by Junio C Hamano
parent 09ff69bb39
commit 8c1ce0f46b
2 changed files with 36 additions and 11 deletions

39
git-filter-branch.sh Normal file → Executable file
View File

@ -28,6 +28,16 @@ map()
fi fi
} }
# override die(): this version puts in an extra line break, so that
# the progress is still visible
die()
{
echo >&2
echo "$*" >&2
exit 1
}
# When piped a commit, output a script to set the ident of either # When piped a commit, output a script to set the ident of either
# "author" or "committer # "author" or "committer
@ -181,23 +191,29 @@ while read commit parents; do
export GIT_COMMIT=$commit export GIT_COMMIT=$commit
git cat-file commit "$commit" >../commit git cat-file commit "$commit" >../commit
eval "$(set_ident AUTHOR <../commit)" eval "$(set_ident AUTHOR <../commit)" ||
eval "$(set_ident COMMITTER <../commit)" die "setting author failed for commit $commit"
eval "$filter_env" < /dev/null eval "$(set_ident COMMITTER <../commit)" ||
die "setting committer failed for commit $commit"
eval "$filter_env" < /dev/null ||
die "env filter failed: $filter_env"
if [ "$filter_tree" ]; then if [ "$filter_tree" ]; then
git checkout-index -f -u -a git checkout-index -f -u -a
# files that $commit removed are now still in the working tree; # files that $commit removed are now still in the working tree;
# remove them, else they would be added again # remove them, else they would be added again
git ls-files -z --others | xargs -0 rm -f git ls-files -z --others | xargs -0 rm -f
eval "$filter_tree" < /dev/null eval "$filter_tree" < /dev/null ||
die "tree filter failed: $filter_tree"
git diff-index -r $commit | cut -f 2- | tr '\n' '\0' | \ git diff-index -r $commit | cut -f 2- | tr '\n' '\0' | \
xargs -0 git update-index --add --replace --remove xargs -0 git update-index --add --replace --remove
git ls-files -z --others | \ git ls-files -z --others | \
xargs -0 git update-index --add --replace --remove xargs -0 git update-index --add --replace --remove
fi fi
eval "$filter_index" < /dev/null eval "$filter_index" < /dev/null ||
die "index filter failed: $filter_index"
parentstr= parentstr=
for parent in $parents; do for parent in $parents; do
@ -206,13 +222,15 @@ while read commit parents; do
done done
done done
if [ "$filter_parent" ]; then if [ "$filter_parent" ]; then
parentstr="$(echo "$parentstr" | eval "$filter_parent")" parentstr="$(echo "$parentstr" | eval "$filter_parent")" ||
die "parent filter failed: $filter_parent"
fi fi
sed -e '1,/^$/d' <../commit | \ sed -e '1,/^$/d' <../commit | \
eval "$filter_msg" | \ eval "$filter_msg" > ../message ||
sh -c "$filter_commit" "git commit-tree" $(git write-tree) \ die "msg filter failed: $filter_msg"
$parentstr > ../map/$commit sh -c "$filter_commit" "git commit-tree" \
$(git write-tree) $parentstr < ../message > ../map/$commit
done <../revs done <../revs
src_head=$(tail -n 1 ../revs | sed -e 's/ .*//') src_head=$(tail -n 1 ../revs | sed -e 's/ .*//')
@ -249,7 +267,8 @@ if [ "$filter_tag_name" ]; then
[ -f "../map/$sha1" ] || continue [ -f "../map/$sha1" ] || continue
new_sha1="$(cat "../map/$sha1")" new_sha1="$(cat "../map/$sha1")"
export GIT_COMMIT="$sha1" export GIT_COMMIT="$sha1"
new_ref="$(echo "$ref" | eval "$filter_tag_name")" new_ref="$(echo "$ref" | eval "$filter_tag_name")" ||
die "tag name filter failed: $filter_tag_name"
echo "$ref -> $new_ref ($sha1 -> $new_sha1)" echo "$ref -> $new_ref ($sha1 -> $new_sha1)"

View File

@ -107,13 +107,19 @@ test_expect_success 'use index-filter to move into a subdirectory' '
mv \$GIT_INDEX_FILE.new \$GIT_INDEX_FILE" directorymoved && mv \$GIT_INDEX_FILE.new \$GIT_INDEX_FILE" directorymoved &&
test -z "$(git diff HEAD directorymoved:newsubdir)"' test -z "$(git diff HEAD directorymoved:newsubdir)"'
test_expect_success 'stops when msg filter fails' '
! git-filter-branch --msg-filter false nonono &&
rm -rf .git-rewrite &&
! git rev-parse nonono
'
test_expect_success 'author information is preserved' ' test_expect_success 'author information is preserved' '
: > i && : > i &&
git add i && git add i &&
test_tick && test_tick &&
GIT_AUTHOR_NAME="B V Uips" git commit -m bvuips && GIT_AUTHOR_NAME="B V Uips" git commit -m bvuips &&
git-filter-branch --msg-filter "cat; \ git-filter-branch --msg-filter "cat; \
test \$GIT_COMMIT = $(git rev-parse master) && \ test \$GIT_COMMIT != $(git rev-parse master) || \
echo Hallo" \ echo Hallo" \
preserved-author && preserved-author &&
test 1 = $(git rev-list --author="B V Uips" preserved-author | wc -l) test 1 = $(git rev-list --author="B V Uips" preserved-author | wc -l)