cat-file: split --batch input lines on whitespace

If we get an input line to --batch or --batch-check that looks like "HEAD foo bar", we will currently feed the whole thing to get_sha1(). This means that to use --batch-check with `rev-list --objects`, one must pre-process the input, like: git rev-list --objects HEAD | cut -d' ' -f1 | git cat-file --batch-check Besides being more typing and slightly less efficient to invoke `cut`, the result loses information: we no longer know which path each object was found at. This patch teaches cat-file to split input lines at the first whitespace. Everything to the left of the whitespace is considered an object name, and everything to the right is made available as the %(reset) atom. So you can now do: git rev-list --objects HEAD | git cat-file --batch-check='%(objectsize) %(rest)' to collect object sizes at particular paths. Even if %(rest) is not used, we always do the whitespace split (which means you can simply eliminate the `cut` command from the first example above). This whitespace split is backwards compatible for any reasonable input. Object names cannot contain spaces, so any input with spaces would have resulted in a "missing" line. The only input hurt is if somebody really expected input of the form "HEAD is a fine-looking ref!" to fail; it will now parse HEAD, and make "is a fine-looking ref!" available as %(rest). Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Jeff King committed Jul 11, 2013 at 16:45 UTC c334b87b30c1464a1ab563fe1fb8de5eaf0e5bac
3 files changed +34 -3
Documentation/git-cat-file.txt
+8 -2
@@ -88,8 +88,10 @@ BATCH OUTPUT
88 If `--batch` or `--batch-check` is given, `cat-file` will read objects
89 from stdin, one per line, and print information about them.
90
91 -Each line is considered as a whole object name, and is parsed as if
92 -given to linkgit:git-rev-parse[1].
91 +Each line is split at the first whitespace boundary. All characters
92 +before that whitespace are considered as a whole object name, and are
93 +parsed as if given to linkgit:git-rev-parse[1]. Characters after that
94 +whitespace can be accessed using the `%(rest)` atom (see below).
95
96 You can specify the information shown for each object by using a custom
97 `<format>`. The `<format>` is copied literally to stdout for each
@@ -110,6 +112,10 @@ newline. The available atoms are:
112 The size, in bytes, that the object takes up on disk. See the
113 note about on-disk sizes in the `CAVEATS` section below.
114
115 +`rest`::
116 + The text (if any) found after the first run of whitespace on the
117 + input line (i.e., the "rest" of the line).
118 +
119 If no format is specified, the default format is `%(objectname)
120 %(objecttype) %(objectsize)`.
121
builtin/cat-file.c
+19 -1
@@ -119,6 +119,7 @@ struct expand_data {
119 enum object_type type;
120 unsigned long size;
121 unsigned long disk_size;
122 + const char *rest;
123
124 /*
125 * If mark_query is true, we do not expand anything, but rather
@@ -161,6 +162,9 @@ static void expand_atom(struct strbuf *sb, const char *atom, int len,
162 data->info.disk_sizep = &data->disk_size;
163 else
164 strbuf_addf(sb, "%lu", data->disk_size);
165 + } else if (is_atom("rest", atom, len)) {
166 + if (!data->mark_query && data->rest)
167 + strbuf_addstr(sb, data->rest);
168 } else
169 die("unknown format element: %.*s", len, atom);
170 }
@@ -263,7 +267,21 @@ static int batch_objects(struct batch_options *opt)
267 data.mark_query = 0;
268
269 while (strbuf_getline(&buf, stdin, '\n') != EOF) {
266 - int error = batch_one_object(buf.buf, opt, &data);
270 + char *p;
271 + int error;
272 +
273 + /*
274 + * Split at first whitespace, tying off the beginning of the
275 + * string and saving the remainder (or NULL) in data.rest.
276 + */
277 + p = strpbrk(buf.buf, " \t");
278 + if (p) {
279 + while (*p && strchr(" \t", *p))
280 + *p++ = '\0';
281 + }
282 + data.rest = p;
283 +
284 + error = batch_one_object(buf.buf, opt, &data);
285 if (error)
286 return error;
287 }
t/t1006-cat-file.sh
+7
@@ -78,6 +78,13 @@ $content"
78 echo $sha1 | git cat-file --batch-check="%(objecttype) %(objectname)" >actual &&
79 test_cmp expect actual
80 '
81 +
82 + test_expect_success '--batch-check with %(rest)' '
83 + echo "$type this is some extra content" >expect &&
84 + echo "$sha1 this is some extra content" |
85 + git cat-file --batch-check="%(objecttype) %(rest)" >actual &&
86 + test_cmp expect actual
87 + '
88 }
89
90 hello_content="Hello World"