Skip to content

Commit d169d51

Browse files
committed
Merge branch 'jc/cat-file-batch-commands'
"git cat-file" learns "--batch-command" mode, which is a more flexible interface than the existing "--batch" or "--batch-check" modes, to allow different kinds of inquiries made. * jc/cat-file-batch-commands: cat-file: add --batch-command mode cat-file: add remove_timestamp helper cat-file: introduce batch_mode enum to replace print_contents cat-file: rename cmdmode to transform_mode
2 parents 47be28e + 440c705 commit d169d51

File tree

3 files changed

+333
-22
lines changed

3 files changed

+333
-22
lines changed

Documentation/git-cat-file.txt

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,33 @@ OPTIONS
9696
need to specify the path, separated by whitespace. See the
9797
section `BATCH OUTPUT` below for details.
9898

99+
--batch-command::
100+
--batch-command=<format>::
101+
Enter a command mode that reads commands and arguments from stdin. May
102+
only be combined with `--buffer`, `--textconv` or `--filters`. In the
103+
case of `--textconv` or `--filters`, the input lines also need to specify
104+
the path, separated by whitespace. See the section `BATCH OUTPUT` below
105+
for details.
106+
+
107+
`--batch-command` recognizes the following commands:
108+
+
109+
--
110+
contents <object>::
111+
Print object contents for object reference `<object>`. This corresponds to
112+
the output of `--batch`.
113+
114+
info <object>::
115+
Print object info for object reference `<object>`. This corresponds to the
116+
output of `--batch-check`.
117+
118+
flush::
119+
Used with `--buffer` to execute all preceding commands that were issued
120+
since the beginning or since the last flush was issued. When `--buffer`
121+
is used, no output will come until a `flush` is issued. When `--buffer`
122+
is not used, commands are flushed each time without issuing `flush`.
123+
--
124+
+
125+
99126
--batch-all-objects::
100127
Instead of reading a list of objects on stdin, perform the
101128
requested batch operation on all objects in the repository and
@@ -110,7 +137,7 @@ OPTIONS
110137
that a process can interactively read and write from
111138
`cat-file`. With this option, the output uses normal stdio
112139
buffering; this is much more efficient when invoking
113-
`--batch-check` on a large number of objects.
140+
`--batch-check` or `--batch-command` on a large number of objects.
114141

115142
--unordered::
116143
When `--batch-all-objects` is in use, visit objects in an
@@ -202,6 +229,13 @@ from stdin, one per line, and print information about them. By default,
202229
the whole line is considered as an object, as if it were fed to
203230
linkgit:git-rev-parse[1].
204231

232+
When `--batch-command` is given, `cat-file` will read commands from stdin,
233+
one per line, and print information based on the command given. With
234+
`--batch-command`, the `info` command followed by an object will print
235+
information about the object the same way `--batch-check` would, and the
236+
`contents` command followed by an object prints contents in the same way
237+
`--batch` would.
238+
205239
You can specify the information shown for each object by using a custom
206240
`<format>`. The `<format>` is copied literally to stdout for each
207241
object, with placeholders of the form `%(atom)` expanded, followed by a
@@ -237,9 +271,9 @@ newline. The available atoms are:
237271
If no format is specified, the default format is `%(objectname)
238272
%(objecttype) %(objectsize)`.
239273

240-
If `--batch` is specified, the object information is followed by the
241-
object contents (consisting of `%(objectsize)` bytes), followed by a
242-
newline.
274+
If `--batch` is specified, or if `--batch-command` is used with the `contents`
275+
command, the object information is followed by the object contents (consisting
276+
of `%(objectsize)` bytes), followed by a newline.
243277

244278
For example, `--batch` without a custom format would produce:
245279

builtin/cat-file.c

Lines changed: 166 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,20 @@
1717
#include "object-store.h"
1818
#include "promisor-remote.h"
1919

20+
enum batch_mode {
21+
BATCH_MODE_CONTENTS,
22+
BATCH_MODE_INFO,
23+
BATCH_MODE_QUEUE_AND_DISPATCH,
24+
};
25+
2026
struct batch_options {
2127
int enabled;
2228
int follow_symlinks;
23-
int print_contents;
29+
enum batch_mode batch_mode;
2430
int buffer_output;
2531
int all_objects;
2632
int unordered;
27-
int cmdmode; /* may be 'w' or 'c' for --filters or --textconv */
33+
int transform_mode; /* may be 'w' or 'c' for --filters or --textconv */
2834
const char *format;
2935
};
3036

@@ -302,19 +308,19 @@ static void print_object_or_die(struct batch_options *opt, struct expand_data *d
302308
if (data->type == OBJ_BLOB) {
303309
if (opt->buffer_output)
304310
fflush(stdout);
305-
if (opt->cmdmode) {
311+
if (opt->transform_mode) {
306312
char *contents;
307313
unsigned long size;
308314

309315
if (!data->rest)
310316
die("missing path for '%s'", oid_to_hex(oid));
311317

312-
if (opt->cmdmode == 'w') {
318+
if (opt->transform_mode == 'w') {
313319
if (filter_object(data->rest, 0100644, oid,
314320
&contents, &size))
315321
die("could not convert '%s' %s",
316322
oid_to_hex(oid), data->rest);
317-
} else if (opt->cmdmode == 'c') {
323+
} else if (opt->transform_mode == 'c') {
318324
enum object_type type;
319325
if (!textconv_object(the_repository,
320326
data->rest, 0100644, oid,
@@ -326,7 +332,7 @@ static void print_object_or_die(struct batch_options *opt, struct expand_data *d
326332
die("could not convert '%s' %s",
327333
oid_to_hex(oid), data->rest);
328334
} else
329-
BUG("invalid cmdmode: %c", opt->cmdmode);
335+
BUG("invalid transform_mode: %c", opt->transform_mode);
330336
batch_write(opt, contents, size);
331337
free(contents);
332338
} else {
@@ -386,7 +392,7 @@ static void batch_object_write(const char *obj_name,
386392
strbuf_addch(scratch, '\n');
387393
batch_write(opt, scratch->buf, scratch->len);
388394

389-
if (opt->print_contents) {
395+
if (opt->batch_mode == BATCH_MODE_CONTENTS) {
390396
print_object_or_die(opt, data);
391397
batch_write(opt, "\n", 1);
392398
}
@@ -508,6 +514,135 @@ static int batch_unordered_packed(const struct object_id *oid,
508514
data);
509515
}
510516

517+
typedef void (*parse_cmd_fn_t)(struct batch_options *, const char *,
518+
struct strbuf *, struct expand_data *);
519+
520+
struct queued_cmd {
521+
parse_cmd_fn_t fn;
522+
char *line;
523+
};
524+
525+
static void parse_cmd_contents(struct batch_options *opt,
526+
const char *line,
527+
struct strbuf *output,
528+
struct expand_data *data)
529+
{
530+
opt->batch_mode = BATCH_MODE_CONTENTS;
531+
batch_one_object(line, output, opt, data);
532+
}
533+
534+
static void parse_cmd_info(struct batch_options *opt,
535+
const char *line,
536+
struct strbuf *output,
537+
struct expand_data *data)
538+
{
539+
opt->batch_mode = BATCH_MODE_INFO;
540+
batch_one_object(line, output, opt, data);
541+
}
542+
543+
static void dispatch_calls(struct batch_options *opt,
544+
struct strbuf *output,
545+
struct expand_data *data,
546+
struct queued_cmd *cmd,
547+
int nr)
548+
{
549+
int i;
550+
551+
if (!opt->buffer_output)
552+
die(_("flush is only for --buffer mode"));
553+
554+
for (i = 0; i < nr; i++)
555+
cmd[i].fn(opt, cmd[i].line, output, data);
556+
557+
fflush(stdout);
558+
}
559+
560+
static void free_cmds(struct queued_cmd *cmd, size_t *nr)
561+
{
562+
size_t i;
563+
564+
for (i = 0; i < *nr; i++)
565+
FREE_AND_NULL(cmd[i].line);
566+
567+
*nr = 0;
568+
}
569+
570+
571+
static const struct parse_cmd {
572+
const char *name;
573+
parse_cmd_fn_t fn;
574+
unsigned takes_args;
575+
} commands[] = {
576+
{ "contents", parse_cmd_contents, 1},
577+
{ "info", parse_cmd_info, 1},
578+
{ "flush", NULL, 0},
579+
};
580+
581+
static void batch_objects_command(struct batch_options *opt,
582+
struct strbuf *output,
583+
struct expand_data *data)
584+
{
585+
struct strbuf input = STRBUF_INIT;
586+
struct queued_cmd *queued_cmd = NULL;
587+
size_t alloc = 0, nr = 0;
588+
589+
while (!strbuf_getline(&input, stdin)) {
590+
int i;
591+
const struct parse_cmd *cmd = NULL;
592+
const char *p = NULL, *cmd_end;
593+
struct queued_cmd call = {0};
594+
595+
if (!input.len)
596+
die(_("empty command in input"));
597+
if (isspace(*input.buf))
598+
die(_("whitespace before command: '%s'"), input.buf);
599+
600+
for (i = 0; i < ARRAY_SIZE(commands); i++) {
601+
if (!skip_prefix(input.buf, commands[i].name, &cmd_end))
602+
continue;
603+
604+
cmd = &commands[i];
605+
if (cmd->takes_args) {
606+
if (*cmd_end != ' ')
607+
die(_("%s requires arguments"),
608+
commands[i].name);
609+
610+
p = cmd_end + 1;
611+
} else if (*cmd_end) {
612+
die(_("%s takes no arguments"),
613+
commands[i].name);
614+
}
615+
616+
break;
617+
}
618+
619+
if (!cmd)
620+
die(_("unknown command: '%s'"), input.buf);
621+
622+
if (!strcmp(cmd->name, "flush")) {
623+
dispatch_calls(opt, output, data, queued_cmd, nr);
624+
free_cmds(queued_cmd, &nr);
625+
} else if (!opt->buffer_output) {
626+
cmd->fn(opt, p, output, data);
627+
} else {
628+
ALLOC_GROW(queued_cmd, nr + 1, alloc);
629+
call.fn = cmd->fn;
630+
call.line = xstrdup_or_null(p);
631+
queued_cmd[nr++] = call;
632+
}
633+
}
634+
635+
if (opt->buffer_output &&
636+
nr &&
637+
!git_env_bool("GIT_TEST_CAT_FILE_NO_FLUSH_ON_EXIT", 0)) {
638+
dispatch_calls(opt, output, data, queued_cmd, nr);
639+
free_cmds(queued_cmd, &nr);
640+
}
641+
642+
free(queued_cmd);
643+
strbuf_release(&input);
644+
}
645+
511646
static int batch_objects(struct batch_options *opt)
512647
{
513648
struct strbuf input = STRBUF_INIT;
@@ -529,14 +664,14 @@ static int batch_objects(struct batch_options *opt)
529664
strbuf_expand(&output, opt->format, expand_format, &data);
530665
data.mark_query = 0;
531666
strbuf_release(&output);
532-
if (opt->cmdmode)
667+
if (opt->transform_mode)
533668
data.split_on_whitespace = 1;
534669

535670
/*
536671
* If we are printing out the object, then always fill in the type,
537672
* since we will want to decide whether or not to stream.
538673
*/
539-
if (opt->print_contents)
674+
if (opt->batch_mode == BATCH_MODE_CONTENTS)
540675
data.info.typep = &data.type;
541676

542677
if (opt->all_objects) {
@@ -590,6 +725,11 @@ static int batch_objects(struct batch_options *opt)
590725
save_warning = warn_on_object_refname_ambiguity;
591726
warn_on_object_refname_ambiguity = 0;
592727

728+
if (opt->batch_mode == BATCH_MODE_QUEUE_AND_DISPATCH) {
729+
batch_objects_command(opt, &output, &data);
730+
goto cleanup;
731+
}
732+
593733
while (strbuf_getline(&input, stdin) != EOF) {
594734
if (data.split_on_whitespace) {
595735
/*
@@ -608,6 +748,7 @@ static int batch_objects(struct batch_options *opt)
608748
batch_one_object(input.buf, &output, opt, &data);
609749
}
610750

751+
cleanup:
611752
strbuf_release(&input);
612753
strbuf_release(&output);
613754
warn_on_object_refname_ambiguity = save_warning;
@@ -635,7 +776,16 @@ static int batch_option_callback(const struct option *opt,
635776
}
636777

637778
bo->enabled = 1;
638-
bo->print_contents = !strcmp(opt->long_name, "batch");
779+
780+
if (!strcmp(opt->long_name, "batch"))
781+
bo->batch_mode = BATCH_MODE_CONTENTS;
782+
else if (!strcmp(opt->long_name, "batch-check"))
783+
bo->batch_mode = BATCH_MODE_INFO;
784+
else if (!strcmp(opt->long_name, "batch-command"))
785+
bo->batch_mode = BATCH_MODE_QUEUE_AND_DISPATCH;
786+
else
787+
BUG("%s given to batch-option-callback", opt->long_name);
788+
639789
bo->format = arg;
640790

641791
return 0;
@@ -654,7 +804,7 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix)
654804
N_("git cat-file <type> <object>"),
655805
N_("git cat-file (-e | -p) <object>"),
656806
N_("git cat-file (-t | -s) [--allow-unknown-type] <object>"),
657-
N_("git cat-file (--batch | --batch-check) [--batch-all-objects]\n"
807+
N_("git cat-file (--batch | --batch-check | --batch-command) [--batch-all-objects]\n"
658808
" [--buffer] [--follow-symlinks] [--unordered]\n"
659809
" [--textconv | --filters]"),
660810
N_("git cat-file (--textconv | --filters)\n"
@@ -683,6 +833,10 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix)
683833
N_("like --batch, but don't emit <contents>"),
684834
PARSE_OPT_OPTARG | PARSE_OPT_NONEG,
685835
batch_option_callback),
836+
OPT_CALLBACK_F(0, "batch-command", &batch, N_("format"),
837+
N_("read commands from stdin"),
838+
PARSE_OPT_OPTARG | PARSE_OPT_NONEG,
839+
batch_option_callback),
686840
OPT_CMDMODE(0, "batch-all-objects", &opt,
687841
N_("with --batch[-check]: ignores stdin, batches all known objects"), 'b'),
688842
/* Batch-specific options */
@@ -742,7 +896,7 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix)
742896
/* Return early if we're in batch mode? */
743897
if (batch.enabled) {
744898
if (opt_cw)
745-
batch.cmdmode = opt;
899+
batch.transform_mode = opt;
746900
else if (opt && opt != 'b')
747901
usage_msg_optf(_("'-%c' is incompatible with batch mode"),
748902
usage, options, opt);

0 commit comments

Comments
 (0)