From: Johannes Schindelin <hidden> Date: 2016-06-15 22:43:19
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 <redacted>
---
It is slightly ugly that the output of msg-filter is written
to a temporary file. But I do not know a better method to
catch a failing msg-filter. Help?
git-filter-branch.sh | 39 +++++++++++++++++++++++++++++----------
t/t7003-filter-branch.sh | 8 +++++++-
2 files changed, 36 insertions(+), 11 deletions(-)
@@ -20,6 +20,16 @@ map()cat"$workdir/../map/$1"}+# override die(): this version puts in an extra line break, so that+# the progress is still visible++die()+{+echo>&2+echo"$*">&2+exit1+}+# When piped a commit, output a script to set the ident of either# "author" or "committer
@@ -173,23 +183,29 @@ while read commit parents; doexportGIT_COMMIT=$commitgitcat-filecommit"$commit">../commit-eval"$(set_identAUTHOR<../commit)"-eval"$(set_identCOMMITTER<../commit)"-eval"$filter_env"</dev/null+eval"$(set_identAUTHOR<../commit)"||+die"setting author failed for commit $commit"+eval"$(set_identCOMMITTER<../commit)"||+die"setting committer failed for commit $commit"+eval"$filter_env"</dev/null||+die"env filter failed: $filter_env"if["$filter_tree"];thengitcheckout-index-f-u-a# files that $commit removed are now still in the working tree;# remove them, else they would be added againgitls-files-z--others|xargs-0rm-f-eval"$filter_tree"</dev/null+eval"$filter_tree"</dev/null||+die"tree filter failed: $filter_tree"+gitdiff-index-r$commit|cut-f2-|tr'\n''\0'|\xargs-0gitupdate-index--add--replace--removegitls-files-z--others|\xargs-0gitupdate-index--add--replace--removefi-eval"$filter_index"</dev/null+eval"$filter_index"</dev/null||+die"index filter failed: $filter_index"parentstr=forparentin$parents;do
@@ -107,13 +107,19 @@ test_expect_success 'use index-filter to move into a subdirectory' 'mv\$GIT_INDEX_FILE.new\$GIT_INDEX_FILE" directorymoved &&test-z"$(gitdiffHEADdirectorymoved:newsubdir)"'+test_expect_success'stops when msg filter fails''+!git-filter-branch--msg-filterfalsenonono&&+rm-rf.git-rewrite&&+!gitrev-parsenonono+'+ test_expect_success'author information is preserved'':>i&&gitaddi&&test_tick&&GIT_AUTHOR_NAME="B V Uips"gitcommit-mbvuips&&git-filter-branch--msg-filter"cat; \-test\$GIT_COMMIT=$(gitrev-parsemaster)&&\+test\$GIT_COMMIT!=$(gitrev-parsemaster)||\echoHallo" \preserved-author&&test1=$(gitrev-list--author="B V Uips"preserved-author|wc-l)
From: Jeff King <hidden> Date: 2016-06-15 22:43:19
On Wed, Jul 04, 2007 at 03:36:01PM +0100, Johannes Schindelin wrote:
It is slightly ugly that the output of msg-filter is written
to a temporary file. But I do not know a better method to
catch a failing msg-filter. Help?
If you mean, in general, to catch the exit code of the first part of a
pipe, you have to do something like this:
status=`((cmd1; echo $? >&3) | cmd2) 3>&1`
which is pretty ugly in itself, and if you want the stdout of cmd2, then
you have to add even more redirection. I'm not sure it's worth it.
-Peff
From: Johannes Schindelin <hidden> Date: 2016-06-15 22:43:19
Hi,
On Thu, 5 Jul 2007, Jeff King wrote:
status=`((cmd1; echo $? >&3) | cmd2) 3>&1`
Cute.
This is the replacement patch, then (I guess there are still some nits to
be had, so I did not redo the proper patch yet):
git-filter-branch.sh | 43 ++++++++++++++++++++++++++++++++-----------
1 files changed, 32 insertions(+), 11 deletions(-)
@@ -28,6 +28,16 @@ map()fi}+# override die(): this version puts in an extra line break, so that+# the progress is still visible++die()+{+echo\ >&2+echo"$*">&2+exit1+}+# When piped a commit, output a script to set the ident of either# "author" or "committer
@@ -181,23 +191,29 @@ while read commit parents; doexportGIT_COMMIT=$commitgitcat-filecommit"$commit">../commit-eval"$(set_identAUTHOR<../commit)"-eval"$(set_identCOMMITTER<../commit)"-eval"$filter_env"</dev/null+eval"$(set_identAUTHOR<../commit)"||+die"setting author failed for commit $commit"+eval"$(set_identCOMMITTER<../commit)"||+die"setting committer failed for commit $commit"+eval"$filter_env"</dev/null||+die"env filter failed: $filter_env"if["$filter_tree"];thengitcheckout-index-f-u-a# files that $commit removed are now still in the working tree;# remove them, else they would be added againgitls-files-z--others|xargs-0rm-f-eval"$filter_tree"</dev/null+eval"$filter_tree"</dev/null||+die"tree filter failed: $filter_tree"+gitdiff-index-r$commit|cut-f2-|tr'\n''\0'|\xargs-0gitupdate-index--add--replace--removegitls-files-z--others|\xargs-0gitupdate-index--add--replace--removefi-eval"$filter_index"</dev/null+eval"$filter_index"</dev/null||+die"index filter failed: $filter_index"parentstr=forparentin$parents;do
On Wed, Jul 04, 2007 at 03:36:01PM +0100, Johannes Schindelin wrote:
quoted
It is slightly ugly that the output of msg-filter is written
to a temporary file. But I do not know a better method to
catch a failing msg-filter. Help?
If you mean, in general, to catch the exit code of the first part of a
pipe, you have to do something like this:
status=`((cmd1; echo $? >&3) | cmd2) 3>&1`
which is pretty ugly in itself, and if you want the stdout of cmd2, then
you have to add even more redirection. I'm not sure it's worth it.
bash has "set -o pipefail", but that would require bash. However, you could
try setting pipefail, and ignoring any failure to set it; that would give the
more friendly behavior with bash, while still allowing any /bin/sh in general.
- Josh Triplett
From: Johannes Schindelin <hidden> Date: 2016-06-15 22:43:19
Hi,
On Thu, 5 Jul 2007, Josh Triplett wrote:
bash has "set -o pipefail", but that would require bash. However, you
could try setting pipefail, and ignoring any failure to set it; that
would give the more friendly behavior with bash, while still allowing
any /bin/sh in general.
I was aware of pipefail when I wrote that patch. However, I have zero
interest in a "solution" which works on bash, but fails on other shells.
That is like allowing a precious few to overstep some serious line (and
commuting them), but severely punish all others. And that's wrong. And
to allow it to happen is wrong, too.
Ciao,
Dscho
You introduce a handful of new forks and an exec. Isn't an intermediate
file much cheaper?
The number of forks can be reduced by using { ...; } instead of (
... ) here (though it is possible the shell optimizes them away).
grep . should likely redirect its output with >&2 so that it ends up
on stderr. I'd probably prefer grep ^ or grep '' since that matches
empty lines as well. When done that way, I don't see a "handful of
new forks".
Instead of "grep ." one could also do something like
if read line then
while echo "$line" && read line; do :; done
die
fi
which is fork-less.
--
David Kastrup