fast-export: quote paths in output

Many pathnames in a fast-import stream need to be quoted. In particular: 1. Pathnames at the end of an "M" or "D" line need quoting if they contain a LF or start with double-quote. 2. Pathnames on a "C" or "R" line need quoting as above, but also if they contain spaces. For (1), we weren't quoting at all. For (2), we put double-quotes around the paths to handle spaces, but ignored the possibility that they would need further quoting. This patch checks whether each pathname needs c-style quoting, and uses it. This is slightly overkill for (1), which doesn't actually need to quote many characters that vanilla c-style quoting does. However, it shouldn't hurt, as any implementation needs to be ready to handle quoted strings anyway. In addition to adding a test, we have to tweak a test which blindly assumed that case (2) would always use double-quotes, whether it needed to or not. Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Jeff King committed Aug 5, 2011 at 16:36 UTC 6280dfdc3b22aa821521182781e5a247b19fb515
2 files changed +51 -8
builtin/fast-export.c
+24 -7
@@ -16,6 +16,7 @@
16 #include "string-list.h"
17 #include "utf8.h"
18 #include "parse-options.h"
19 +#include "quote.h"
20
21 static const char *fast_export_usage[] = {
22 "git fast-export [rev-list-opts]",
@@ -178,6 +179,15 @@ static int depth_first(const void *a_, const void *b_)
179 return (a->status == 'R') - (b->status == 'R');
180 }
181
182 +static void print_path(const char *path)
183 +{
184 + int need_quote = quote_c_style(path, NULL, NULL, 0);
185 + if (need_quote)
186 + quote_c_style(path, NULL, stdout, 0);
187 + else
188 + printf("%s", path);
189 +}
190 +
191 static void show_filemodify(struct diff_queue_struct *q,
192 struct diff_options *options, void *data)
193 {
@@ -195,13 +205,18 @@ static void show_filemodify(struct diff_queue_struct *q,
205
206 switch (q->queue[i]->status) {
207 case DIFF_STATUS_DELETED:
198 - printf("D %s\n", spec->path);
208 + printf("D ");
209 + print_path(spec->path);
210 + putchar('\n');
211 break;
212
213 case DIFF_STATUS_COPIED:
214 case DIFF_STATUS_RENAMED:
203 - printf("%c \"%s\" \"%s\"\n", q->queue[i]->status,
204 - ospec->path, spec->path);
215 + printf("%c ", q->queue[i]->status);
216 + print_path(ospec->path);
217 + putchar(' ');
218 + print_path(spec->path);
219 + putchar('\n');
220
221 if (!hashcmp(ospec->sha1, spec->sha1) &&
222 ospec->mode == spec->mode)
@@ -216,13 +231,15 @@ static void show_filemodify(struct diff_queue_struct *q,
231 * output the SHA-1 verbatim.
232 */
233 if (no_data || S_ISGITLINK(spec->mode))
219 - printf("M %06o %s %s\n", spec->mode,
220 - sha1_to_hex(spec->sha1), spec->path);
234 + printf("M %06o %s ", spec->mode,
235 + sha1_to_hex(spec->sha1));
236 else {
237 struct object *object = lookup_object(spec->sha1);
223 - printf("M %06o :%d %s\n", spec->mode,
224 - get_object_mark(object), spec->path);
238 + printf("M %06o :%d ", spec->mode,
239 + get_object_mark(object));
240 }
241 + print_path(spec->path);
242 + putchar('\n');
243 break;
244
245 default:
t/t9350-fast-export.sh
+27 -1
@@ -228,7 +228,7 @@ test_expect_success 'fast-export -C -C | fast-import' '
228 mkdir new &&
229 git --git-dir=new/.git init &&
230 git fast-export -C -C --signed-tags=strip --all > output &&
231 - grep "^C \"file6\" \"file7\"\$" output &&
231 + grep "^C file6 file7\$" output &&
232 cat output |
233 (cd new &&
234 git fast-import &&
@@ -414,4 +414,30 @@ test_expect_success SYMLINKS 'directory becomes symlink' '
414 (cd result && git show master:foo)
415 '
416
417 +test_expect_success 'fast-export quotes pathnames' '
418 + git init crazy-paths &&
419 + (cd crazy-paths &&
420 + blob=`echo foo | git hash-object -w --stdin` &&
421 + git update-index --add \
422 + --cacheinfo 100644 $blob "$(printf "path with\\nnewline")" \
423 + --cacheinfo 100644 $blob "path with \"quote\"" \
424 + --cacheinfo 100644 $blob "path with \\backslash" \
425 + --cacheinfo 100644 $blob "path with space" &&
426 + git commit -m addition &&
427 + git ls-files -z -s | perl -0pe "s{\\t}{$&subdir/}" >index &&
428 + git read-tree --empty &&
429 + git update-index -z --index-info <index &&
430 + git commit -m rename &&
431 + git read-tree --empty &&
432 + git commit -m deletion &&
433 + git fast-export HEAD >export.out &&
434 + git rev-list HEAD >expect &&
435 + git init result &&
436 + cd result &&
437 + git fast-import <../export.out &&
438 + git rev-list HEAD >actual &&
439 + test_cmp ../expect actual
440 + )
441 +'
442 +
443 test_done