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 <pw@padd.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Pete Wyckoff committed
Dec 22, 2010 at 06:40 UTC
a2b665de4b694b04959f6371f9887f7ad5cae404
3 files changed
+74
-1
Documentation/gitattributes.txt
+10
@@ -335,6 +335,16 @@ input that is already correctly indented. In this case, the lack of a
335
smudge filter means that the clean filter _must_ accept its own output
336
without modifying it.
337
338
+Sequence "%f" on the filter command line is replaced with the name of
339
+the file the filter is working on. A filter might use this in keyword
340
+substitution. For example:
341
+
342
+------------------------
343
+[filter "p4"]
344
+ clean = git-p4-filter --clean %f
345
+ smudge = git-p4-filter --smudge %f
346
+------------------------
347
+
348
349
Interaction between checkin/checkout attributes
350
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
convert.c
+22
-1
@@ -1,6 +1,7 @@
1
#include "cache.h"
2
#include "attr.h"
3
#include "run-command.h"
4
+#include "quote.h"
5
6
/*
7
* convert.c - convert a file when checking it out and checking it in.
@@ -318,6 +319,7 @@ struct filter_params {
319
const char *src;
320
unsigned long size;
321
const char *cmd;
322
+ const char *path;
323
};
324
325
static int filter_buffer(int in, int out, void *data)
@@ -330,7 +332,23 @@ static int filter_buffer(int in, int out, void *data)
332
int write_err, status;
333
const char *argv[] = { NULL, NULL };
334
333
- argv[0] = params->cmd;
335
+ /* apply % substitution to cmd */
336
+ struct strbuf cmd = STRBUF_INIT;
337
+ struct strbuf path = STRBUF_INIT;
338
+ struct strbuf_expand_dict_entry dict[] = {
339
+ { "f", NULL, },
340
+ { NULL, NULL, },
341
+ };
342
+
343
+ /* quote the path to preserve spaces, etc. */
344
+ sq_quote_buf(&path, params->path);
345
+ dict[0].value = path.buf;
346
+
347
+ /* expand all %f with the quoted path */
348
+ strbuf_expand(&cmd, params->cmd, strbuf_expand_dict_cb, &dict);
349
+ strbuf_release(&path);
350
+
351
+ argv[0] = cmd.buf;
352
353
memset(&child_process, 0, sizeof(child_process));
354
child_process.argv = argv;
@@ -350,6 +368,8 @@ static int filter_buffer(int in, int out, void *data)
368
status = finish_command(&child_process);
369
if (status)
370
error("external filter %s failed %d", params->cmd, status);
371
+
372
+ strbuf_release(&cmd);
373
return (write_err || status);
374
}
375
@@ -377,6 +397,7 @@ static int apply_filter(const char *path, const char *src, size_t len,
397
params.src = src;
398
params.size = len;
399
params.cmd = cmd;
400
+ params.path = path;
401
402
fflush(NULL);
403
if (start_async(&async))
t/t0021-conversion.sh
+42
@@ -93,4 +93,46 @@ test_expect_success expanded_in_repo '
93
cmp expanded-keywords expected-output
94
'
95
96
+# The use of %f in a filter definition is expanded to the path to
97
+# the filename being smudged or cleaned. It must be shell escaped.
98
+# First, set up some interesting file names and pet them in
99
+# .gitattributes.
100
+test_expect_success 'filter shell-escaped filenames' '
101
+ cat >argc.sh <<-EOF &&
102
+ #!$SHELL_PATH
103
+ echo argc: \$# "\$@"
104
+ EOF
105
+ normal=name-no-magic &&
106
+ special="name with '\''sq'\'' and \$x" &&
107
+ echo some test text >"$normal" &&
108
+ echo some test text >"$special" &&
109
+ git add "$normal" "$special" &&
110
+ git commit -q -m "add files" &&
111
+ echo "name* filter=argc" >.gitattributes &&
112
+
113
+ # delete the files and check them out again, using a smudge filter
114
+ # that will count the args and echo the command-line back to us
115
+ git config filter.argc.smudge "sh ./argc.sh %f" &&
116
+ rm "$normal" "$special" &&
117
+ git checkout -- "$normal" "$special" &&
118
+
119
+ # make sure argc.sh counted the right number of args
120
+ echo "argc: 1 $normal" >expect &&
121
+ test_cmp expect "$normal" &&
122
+ echo "argc: 1 $special" >expect &&
123
+ test_cmp expect "$special" &&
124
+
125
+ # do the same thing, but with more args in the filter expression
126
+ git config filter.argc.smudge "sh ./argc.sh %f --my-extra-arg" &&
127
+ rm "$normal" "$special" &&
128
+ git checkout -- "$normal" "$special" &&
129
+
130
+ # make sure argc.sh counted the right number of args
131
+ echo "argc: 2 $normal --my-extra-arg" >expect &&
132
+ test_cmp expect "$normal" &&
133
+ echo "argc: 2 $special --my-extra-arg" >expect &&
134
+ test_cmp expect "$special" &&
135
+ :
136
+'
137
+
138
test_done