From a2b665de4b694b04959f6371f9887f7ad5cae404 Mon Sep 17 00:00:00 2001 From: Pete Wyckoff Date: Wed, 22 Dec 2010 06:40:13 -0800 Subject: convert filter: supply path to external driver Filtering to support keyword expansion may need the name of the file being filtered. In particular, to support p4 keywords like $File: //depot/product/dir/script.sh $ the smudge filter needs to know the name of the file it is smudging. Allow "%f" in the custom filter command line specified in the configuration. This will be substituted by the filename inside a single-quote pair to be passed to the shell. Signed-off-by: Pete Wyckoff Signed-off-by: Junio C Hamano --- Documentation/gitattributes.txt | 10 ++++++++++ convert.c | 23 +++++++++++++++++++++- t/t0021-conversion.sh | 42 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 74 insertions(+), 1 deletion(-) diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt index 5a7f93642..22b85825a 100644 --- a/Documentation/gitattributes.txt +++ b/Documentation/gitattributes.txt @@ -335,6 +335,16 @@ input that is already correctly indented. In this case, the lack of a smudge filter means that the clean filter _must_ accept its own output without modifying it. +Sequence "%f" on the filter command line is replaced with the name of +the file the filter is working on. A filter might use this in keyword +substitution. For example: + +------------------------ +[filter "p4"] + clean = git-p4-filter --clean %f + smudge = git-p4-filter --smudge %f +------------------------ + Interaction between checkin/checkout attributes ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/convert.c b/convert.c index 01de9a84c..d5aebed48 100644 --- a/convert.c +++ b/convert.c @@ -1,6 +1,7 @@ #include "cache.h" #include "attr.h" #include "run-command.h" +#include "quote.h" /* * convert.c - convert a file when checking it out and checking it in. @@ -318,6 +319,7 @@ struct filter_params { const char *src; unsigned long size; const char *cmd; + const char *path; }; static int filter_buffer(int in, int out, void *data) @@ -330,7 +332,23 @@ static int filter_buffer(int in, int out, void *data) int write_err, status; const char *argv[] = { NULL, NULL }; - argv[0] = params->cmd; + /* apply % substitution to cmd */ + struct strbuf cmd = STRBUF_INIT; + struct strbuf path = STRBUF_INIT; + struct strbuf_expand_dict_entry dict[] = { + { "f", NULL, }, + { NULL, NULL, }, + }; + + /* quote the path to preserve spaces, etc. */ + sq_quote_buf(&path, params->path); + dict[0].value = path.buf; + + /* expand all %f with the quoted path */ + strbuf_expand(&cmd, params->cmd, strbuf_expand_dict_cb, &dict); + strbuf_release(&path); + + argv[0] = cmd.buf; memset(&child_process, 0, sizeof(child_process)); child_process.argv = argv; @@ -350,6 +368,8 @@ static int filter_buffer(int in, int out, void *data) status = finish_command(&child_process); if (status) error("external filter %s failed %d", params->cmd, status); + + strbuf_release(&cmd); return (write_err || status); } @@ -377,6 +397,7 @@ static int apply_filter(const char *path, const char *src, size_t len, params.src = src; params.size = len; params.cmd = cmd; + params.path = path; fflush(NULL); if (start_async(&async)) diff --git a/t/t0021-conversion.sh b/t/t0021-conversion.sh index 828e35baf..aacfd004b 100755 --- a/t/t0021-conversion.sh +++ b/t/t0021-conversion.sh @@ -93,4 +93,46 @@ test_expect_success expanded_in_repo ' cmp expanded-keywords expected-output ' +# The use of %f in a filter definition is expanded to the path to +# the filename being smudged or cleaned. It must be shell escaped. +# First, set up some interesting file names and pet them in +# .gitattributes. +test_expect_success 'filter shell-escaped filenames' ' + cat >argc.sh <<-EOF && + #!$SHELL_PATH + echo argc: \$# "\$@" + EOF + normal=name-no-magic && + special="name with '\''sq'\'' and \$x" && + echo some test text >"$normal" && + echo some test text >"$special" && + git add "$normal" "$special" && + git commit -q -m "add files" && + echo "name* filter=argc" >.gitattributes && + + # delete the files and check them out again, using a smudge filter + # that will count the args and echo the command-line back to us + git config filter.argc.smudge "sh ./argc.sh %f" && + rm "$normal" "$special" && + git checkout -- "$normal" "$special" && + + # make sure argc.sh counted the right number of args + echo "argc: 1 $normal" >expect && + test_cmp expect "$normal" && + echo "argc: 1 $special" >expect && + test_cmp expect "$special" && + + # do the same thing, but with more args in the filter expression + git config filter.argc.smudge "sh ./argc.sh %f --my-extra-arg" && + rm "$normal" "$special" && + git checkout -- "$normal" "$special" && + + # make sure argc.sh counted the right number of args + echo "argc: 2 $normal --my-extra-arg" >expect && + test_cmp expect "$normal" && + echo "argc: 2 $special --my-extra-arg" >expect && + test_cmp expect "$special" && + : +' + test_done -- cgit v1.2.1 From 4290f690676bc14c49c7cddbcda1c09017948718 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 22 Dec 2010 15:18:47 -0800 Subject: t0021: avoid getting filter killed with SIGPIPE The fake filter did not read from the standard input at all, which caused the calling side to die with SIGPIPE, depending on the timing. Signed-off-by: Junio C Hamano --- t/t0021-conversion.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/t/t0021-conversion.sh b/t/t0021-conversion.sh index aacfd004b..9078b84ae 100755 --- a/t/t0021-conversion.sh +++ b/t/t0021-conversion.sh @@ -100,6 +100,7 @@ test_expect_success expanded_in_repo ' test_expect_success 'filter shell-escaped filenames' ' cat >argc.sh <<-EOF && #!$SHELL_PATH + cat >/dev/null echo argc: \$# "\$@" EOF normal=name-no-magic && -- cgit v1.2.1