Skip to content

Commit 93d2a60

Browse files
peffgitster
authored andcommitted
cat-file: add --batch-check=<format>
The `cat-file --batch-check` command can be used to quickly get information about a large number of objects. However, it provides a fixed set of information. This patch adds an optional <format> option to --batch-check to allow a caller to specify which items they are interested in, and in which order to output them. This is not very exciting for now, since we provide the same limited set that you could already get. However, it opens the door to adding new format items in the future without breaking backwards compatibility (or forcing callers to pay the cost to calculate uninteresting items). Since the --batch option shares code with --batch-check, it receives the same feature, though it is less likely to be of interest there. The format atom names are chosen to match their counterparts in for-each-ref. Though we do not (yet) share any code with for-each-ref's formatter, this keeps the interface as consistent as possible, and may help later on if the implementations are unified. Signed-off-by: Jeff King <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent b71bd48 commit 93d2a60

File tree

3 files changed

+142
-26
lines changed

3 files changed

+142
-26
lines changed

Documentation/git-cat-file.txt

Lines changed: 44 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -58,12 +58,16 @@ OPTIONS
5858
to apply the filter to the content recorded in the index at <path>.
5959

6060
--batch::
61-
Print the SHA-1, type, size, and contents of each object provided on
62-
stdin. May not be combined with any other options or arguments.
61+
--batch=<format>::
62+
Print object information and contents for each object provided
63+
on stdin. May not be combined with any other options or arguments.
64+
See the section `BATCH OUTPUT` below for details.
6365

6466
--batch-check::
65-
Print the SHA-1, type, and size of each object provided on stdin. May not
66-
be combined with any other options or arguments.
67+
--batch-check=<format>::
68+
Print object information for each object provided on stdin. May
69+
not be combined with any other options or arguments. See the
70+
section `BATCH OUTPUT` below for details.
6771

6872
OUTPUT
6973
------
@@ -78,23 +82,52 @@ If '-p' is specified, the contents of <object> are pretty-printed.
7882
If <type> is specified, the raw (though uncompressed) contents of the <object>
7983
will be returned.
8084

81-
If '--batch' is specified, output of the following form is printed for each
82-
object specified on stdin:
85+
BATCH OUTPUT
86+
------------
87+
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].
93+
94+
You can specify the information shown for each object by using a custom
95+
`<format>`. The `<format>` is copied literally to stdout for each
96+
object, with placeholders of the form `%(atom)` expanded, followed by a
97+
newline. The available atoms are:
98+
99+
`objectname`::
100+
The 40-hex object name of the object.
101+
102+
`objecttype`::
103+
The type of of the object (the same as `cat-file -t` reports).
104+
105+
`objectsize`::
106+
The size, in bytes, of the object (the same as `cat-file -s`
107+
reports).
108+
109+
If no format is specified, the default format is `%(objectname)
110+
%(objecttype) %(objectsize)`.
111+
112+
If `--batch` is specified, the object information is followed by the
113+
object contents (consisting of `%(objectsize)` bytes), followed by a
114+
newline.
115+
116+
For example, `--batch` without a custom format would produce:
83117

84118
------------
85119
<sha1> SP <type> SP <size> LF
86120
<contents> LF
87121
------------
88122

89-
If '--batch-check' is specified, output of the following form is printed for
90-
each object specified on stdin:
123+
Whereas `--batch-check='%(objectname) %(objecttype)'` would produce:
91124

92125
------------
93-
<sha1> SP <type> SP <size> LF
126+
<sha1> SP <type> LF
94127
------------
95128

96-
For both '--batch' and '--batch-check', output of the following form is printed
97-
for each object specified on stdin that does not exist in the repository:
129+
If a name is specified on stdin that cannot be resolved to an object in
130+
the repository, then `cat-file` will ignore any custom format and print:
98131

99132
------------
100133
<object> SP missing LF

builtin/cat-file.c

Lines changed: 92 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,66 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name)
114114
return 0;
115115
}
116116

117+
struct expand_data {
118+
unsigned char sha1[20];
119+
enum object_type type;
120+
unsigned long size;
121+
122+
/*
123+
* If mark_query is true, we do not expand anything, but rather
124+
* just mark the object_info with items we wish to query.
125+
*/
126+
int mark_query;
127+
128+
/*
129+
* After a mark_query run, this object_info is set up to be
130+
* passed to sha1_object_info_extended. It will point to the data
131+
* elements above, so you can retrieve the response from there.
132+
*/
133+
struct object_info info;
134+
};
135+
136+
static int is_atom(const char *atom, const char *s, int slen)
137+
{
138+
int alen = strlen(atom);
139+
return alen == slen && !memcmp(atom, s, alen);
140+
}
141+
142+
static void expand_atom(struct strbuf *sb, const char *atom, int len,
143+
void *vdata)
144+
{
145+
struct expand_data *data = vdata;
146+
147+
if (is_atom("objectname", atom, len)) {
148+
if (!data->mark_query)
149+
strbuf_addstr(sb, sha1_to_hex(data->sha1));
150+
} else if (is_atom("objecttype", atom, len)) {
151+
if (!data->mark_query)
152+
strbuf_addstr(sb, typename(data->type));
153+
} else if (is_atom("objectsize", atom, len)) {
154+
if (data->mark_query)
155+
data->info.sizep = &data->size;
156+
else
157+
strbuf_addf(sb, "%lu", data->size);
158+
} else
159+
die("unknown format element: %.*s", len, atom);
160+
}
161+
162+
static size_t expand_format(struct strbuf *sb, const char *start, void *data)
163+
{
164+
const char *end;
165+
166+
if (*start != '(')
167+
return 0;
168+
end = strchr(start + 1, ')');
169+
if (!end)
170+
die("format element '%s' does not end in ')'", start);
171+
172+
expand_atom(sb, start + 1, end - start - 1, data);
173+
174+
return end - start + 1;
175+
}
176+
117177
static void print_object_or_die(int fd, const unsigned char *sha1,
118178
enum object_type type, unsigned long size)
119179
{
@@ -142,35 +202,37 @@ static void print_object_or_die(int fd, const unsigned char *sha1,
142202
struct batch_options {
143203
int enabled;
144204
int print_contents;
205+
const char *format;
145206
};
146207

147-
static int batch_one_object(const char *obj_name, struct batch_options *opt)
208+
static int batch_one_object(const char *obj_name, struct batch_options *opt,
209+
struct expand_data *data)
148210
{
149-
unsigned char sha1[20];
150-
enum object_type type = 0;
151-
unsigned long size;
211+
struct strbuf buf = STRBUF_INIT;
152212

153213
if (!obj_name)
154214
return 1;
155215

156-
if (get_sha1(obj_name, sha1)) {
216+
if (get_sha1(obj_name, data->sha1)) {
157217
printf("%s missing\n", obj_name);
158218
fflush(stdout);
159219
return 0;
160220
}
161221

162-
type = sha1_object_info(sha1, &size);
163-
if (type <= 0) {
222+
data->type = sha1_object_info_extended(data->sha1, &data->info);
223+
if (data->type <= 0) {
164224
printf("%s missing\n", obj_name);
165225
fflush(stdout);
166226
return 0;
167227
}
168228

169-
printf("%s %s %lu\n", sha1_to_hex(sha1), typename(type), size);
170-
fflush(stdout);
229+
strbuf_expand(&buf, opt->format, expand_format, data);
230+
strbuf_addch(&buf, '\n');
231+
write_or_die(1, buf.buf, buf.len);
232+
strbuf_release(&buf);
171233

172234
if (opt->print_contents) {
173-
print_object_or_die(1, sha1, type, size);
235+
print_object_or_die(1, data->sha1, data->type, data->size);
174236
write_or_die(1, "\n", 1);
175237
}
176238
return 0;
@@ -179,9 +241,23 @@ static int batch_one_object(const char *obj_name, struct batch_options *opt)
179241
static int batch_objects(struct batch_options *opt)
180242
{
181243
struct strbuf buf = STRBUF_INIT;
244+
struct expand_data data;
245+
246+
if (!opt->format)
247+
opt->format = "%(objectname) %(objecttype) %(objectsize)";
248+
249+
/*
250+
* Expand once with our special mark_query flag, which will prime the
251+
* object_info to be handed to sha1_object_info_extended for each
252+
* object.
253+
*/
254+
memset(&data, 0, sizeof(data));
255+
data.mark_query = 1;
256+
strbuf_expand(&buf, opt->format, expand_format, &data);
257+
data.mark_query = 0;
182258

183259
while (strbuf_getline(&buf, stdin, '\n') != EOF) {
184-
int error = batch_one_object(buf.buf, opt);
260+
int error = batch_one_object(buf.buf, opt, &data);
185261
if (error)
186262
return error;
187263
}
@@ -216,6 +292,7 @@ static int batch_option_callback(const struct option *opt,
216292

217293
bo->enabled = 1;
218294
bo->print_contents = !strcmp(opt->long_name, "batch");
295+
bo->format = arg;
219296

220297
return 0;
221298
}
@@ -235,12 +312,12 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix)
235312
OPT_SET_INT('p', NULL, &opt, N_("pretty-print object's content"), 'p'),
236313
OPT_SET_INT(0, "textconv", &opt,
237314
N_("for blob objects, run textconv on object's content"), 'c'),
238-
{ OPTION_CALLBACK, 0, "batch", &batch, NULL,
315+
{ OPTION_CALLBACK, 0, "batch", &batch, "format",
239316
N_("show info and content of objects fed from the standard input"),
240-
PARSE_OPT_NOARG, batch_option_callback },
241-
{ OPTION_CALLBACK, 0, "batch-check", &batch, NULL,
317+
PARSE_OPT_OPTARG, batch_option_callback },
318+
{ OPTION_CALLBACK, 0, "batch-check", &batch, "format",
242319
N_("show info about objects fed from the standard input"),
243-
PARSE_OPT_NOARG, batch_option_callback },
320+
PARSE_OPT_OPTARG, batch_option_callback },
244321
OPT_END()
245322
};
246323

t/t1006-cat-file.sh

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,12 @@ $content"
7272
echo_without_newline $sha1 | git cat-file --batch-check >actual &&
7373
test_cmp expect actual
7474
'
75+
76+
test_expect_success "custom --batch-check format" '
77+
echo "$type $sha1" >expect &&
78+
echo $sha1 | git cat-file --batch-check="%(objecttype) %(objectname)" >actual &&
79+
test_cmp expect actual
80+
'
7581
}
7682

7783
hello_content="Hello World"

0 commit comments

Comments
 (0)