Skip to content

Commit dd781e3

Browse files
pks-tgitster
authored andcommitted
fetch: introduce machine-parseable "porcelain" output format
The output of git-fetch(1) is obviously designed for consumption by users, only: we neatly columnize data, we abbreviate reference names, we print neat arrows and we don't provide information about actual object IDs that have changed. This makes the output format basically unusable in the context of scripted invocations of git-fetch(1) that want to learn about the exact changes that the command performs. Introduce a new machine-parseable "porcelain" output format that is supposed to fix this shortcoming. This output format is intended to provide information about every reference that is about to be updated, the old object ID that the reference has been pointing to and the new object ID it will be updated to. Furthermore, the output format provides the same flags as the human-readable format to indicate basic conditions for each reference update like whether it was a fast-forward update, a branch deletion, a rejected update or others. The output format is quite simple: ``` <flag> <old-object-id> <new-object-id> <local-reference>\n ``` We assume two conditions which are generally true: - The old and new object IDs have fixed known widths and cannot contain spaces. - References cannot contain newlines. With these assumptions, the output format becomes unambiguously parseable. Furthermore, given that this output is designed to be consumed by scripts, the machine-readable data is printed to stdout instead of stderr like the human-readable output is. This is mostly done so that other data printed to stderr, like error messages or progress meters, don't interfere with the parseable data. A notable ommission here is that the output format does not include the remote from which a reference was fetched, which might be important information especially in the context of multi-remote fetches. But as such a format would require us to print the remote for every single reference update due to parallelizable fetches it feels wasteful for the most likely usecase, which is when fetching from a single remote. In a similar spirit, a second restriction is that this cannot be used with `--recurse-submodules`. This is because any reference updates would be ambiguous without also printing the repository in which the update happens. Considering that both multi-remote and submodule fetches are user-facing features, using them in conjunction with `--porcelain` that is intended for scripting purposes is likely not going to be useful in the majority of cases. With that in mind these restrictions feel acceptable. If usecases for either of these come up in the future though it is easy enough to add a new "porcelain-v2" format that adds this information. Signed-off-by: Patrick Steinhardt <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent cdc034a commit dd781e3

File tree

4 files changed

+244
-19
lines changed

4 files changed

+244
-19
lines changed

Documentation/fetch-options.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,13 @@ linkgit:git-config[1].
7878
--dry-run::
7979
Show what would be done, without making any changes.
8080

81+
--porcelain::
82+
Print the output to standard output in an easy-to-parse format for
83+
scripts. See section OUTPUT in linkgit:git-fetch[1] for details.
84+
+
85+
This is incompatible with `--recurse-submodules=[yes|on-demand]` and takes
86+
precedence over the `fetch.output` config option.
87+
8188
ifndef::git-pull[]
8289
--[no-]write-fetch-head::
8390
Write the list of remote refs fetched in the `FETCH_HEAD`

Documentation/git-fetch.txt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,15 @@ representing the status of a single ref. Each line is of the form:
204204
<flag> <summary> <from> -> <to> [<reason>]
205205
-------------------------------
206206

207+
When using `--porcelain`, the output format is intended to be
208+
machine-parseable. In contrast to the human-readable output formats it
209+
thus prints to standard output instead of standard error. Each line is
210+
of the form:
211+
212+
-------------------------------
213+
<flag> <old-object-id> <new-object-id> <local-reference>
214+
-------------------------------
215+
207216
The status of up-to-date refs is shown only if the --verbose option is
208217
used.
209218

builtin/fetch.c

Lines changed: 69 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ enum display_format {
5454
DISPLAY_FORMAT_UNKNOWN = 0,
5555
DISPLAY_FORMAT_FULL,
5656
DISPLAY_FORMAT_COMPACT,
57+
DISPLAY_FORMAT_PORCELAIN,
5758
};
5859

5960
struct display_state {
@@ -756,6 +757,9 @@ static void display_state_init(struct display_state *display_state, struct ref *
756757
display_state->refcol_width = refcol_width(ref_map,
757758
display_state->format == DISPLAY_FORMAT_COMPACT);
758759
break;
760+
case DISPLAY_FORMAT_PORCELAIN:
761+
/* We don't need to precompute anything here. */
762+
break;
759763
default:
760764
BUG("unexpected display format %d", display_state->format);
761765
}
@@ -826,8 +830,12 @@ static void print_compact(struct display_state *display_state,
826830
static void display_ref_update(struct display_state *display_state, char code,
827831
const char *summary, const char *error,
828832
const char *remote, const char *local,
833+
const struct object_id *old_oid,
834+
const struct object_id *new_oid,
829835
int summary_width)
830836
{
837+
FILE *f = stderr;
838+
831839
if (verbosity < 0)
832840
return;
833841

@@ -860,12 +868,17 @@ static void display_ref_update(struct display_state *display_state, char code,
860868

861869
break;
862870
}
871+
case DISPLAY_FORMAT_PORCELAIN:
872+
strbuf_addf(&display_state->buf, "%c %s %s %s", code,
873+
oid_to_hex(old_oid), oid_to_hex(new_oid), local);
874+
f = stdout;
875+
break;
863876
default:
864877
BUG("unexpected display format %d", display_state->format);
865878
};
866879
strbuf_addch(&display_state->buf, '\n');
867880

868-
fputs(display_state->buf.buf, stderr);
881+
fputs(display_state->buf.buf, f);
869882
}
870883

871884
static int update_local_ref(struct ref *ref,
@@ -883,7 +896,8 @@ static int update_local_ref(struct ref *ref,
883896
if (oideq(&ref->old_oid, &ref->new_oid)) {
884897
if (verbosity > 0)
885898
display_ref_update(display_state, '=', _("[up to date]"), NULL,
886-
remote_ref->name, ref->name, summary_width);
899+
remote_ref->name, ref->name,
900+
&ref->old_oid, &ref->new_oid, summary_width);
887901
return 0;
888902
}
889903

@@ -896,7 +910,8 @@ static int update_local_ref(struct ref *ref,
896910
*/
897911
display_ref_update(display_state, '!', _("[rejected]"),
898912
_("can't fetch into checked-out branch"),
899-
remote_ref->name, ref->name, summary_width);
913+
remote_ref->name, ref->name,
914+
&ref->old_oid, &ref->new_oid, summary_width);
900915
return 1;
901916
}
902917

@@ -907,12 +922,14 @@ static int update_local_ref(struct ref *ref,
907922
r = s_update_ref("updating tag", ref, transaction, 0);
908923
display_ref_update(display_state, r ? '!' : 't', _("[tag update]"),
909924
r ? _("unable to update local ref") : NULL,
910-
remote_ref->name, ref->name, summary_width);
925+
remote_ref->name, ref->name,
926+
&ref->old_oid, &ref->new_oid, summary_width);
911927
return r;
912928
} else {
913929
display_ref_update(display_state, '!', _("[rejected]"),
914930
_("would clobber existing tag"),
915-
remote_ref->name, ref->name, summary_width);
931+
remote_ref->name, ref->name,
932+
&ref->old_oid, &ref->new_oid, summary_width);
916933
return 1;
917934
}
918935
}
@@ -945,7 +962,8 @@ static int update_local_ref(struct ref *ref,
945962
r = s_update_ref(msg, ref, transaction, 0);
946963
display_ref_update(display_state, r ? '!' : '*', what,
947964
r ? _("unable to update local ref") : NULL,
948-
remote_ref->name, ref->name, summary_width);
965+
remote_ref->name, ref->name,
966+
&ref->old_oid, &ref->new_oid, summary_width);
949967
return r;
950968
}
951969

@@ -968,7 +986,8 @@ static int update_local_ref(struct ref *ref,
968986
r = s_update_ref("fast-forward", ref, transaction, 1);
969987
display_ref_update(display_state, r ? '!' : ' ', quickref.buf,
970988
r ? _("unable to update local ref") : NULL,
971-
remote_ref->name, ref->name, summary_width);
989+
remote_ref->name, ref->name,
990+
&ref->old_oid, &ref->new_oid, summary_width);
972991
strbuf_release(&quickref);
973992
return r;
974993
} else if (force || ref->force) {
@@ -980,12 +999,14 @@ static int update_local_ref(struct ref *ref,
980999
r = s_update_ref("forced-update", ref, transaction, 1);
9811000
display_ref_update(display_state, r ? '!' : '+', quickref.buf,
9821001
r ? _("unable to update local ref") : _("forced update"),
983-
remote_ref->name, ref->name, summary_width);
1002+
remote_ref->name, ref->name,
1003+
&ref->old_oid, &ref->new_oid, summary_width);
9841004
strbuf_release(&quickref);
9851005
return r;
9861006
} else {
9871007
display_ref_update(display_state, '!', _("[rejected]"), _("non-fast-forward"),
988-
remote_ref->name, ref->name, summary_width);
1008+
remote_ref->name, ref->name,
1009+
&ref->old_oid, &ref->new_oid, summary_width);
9891010
return 1;
9901011
}
9911012
}
@@ -1226,7 +1247,9 @@ static int store_updated_refs(struct display_state *display_state,
12261247
display_ref_update(display_state, '*',
12271248
*kind ? kind : "branch", NULL,
12281249
rm->name,
1229-
"FETCH_HEAD", summary_width);
1250+
"FETCH_HEAD",
1251+
&rm->new_oid, &rm->old_oid,
1252+
summary_width);
12301253
}
12311254
}
12321255
}
@@ -1366,6 +1389,7 @@ static int prune_refs(struct display_state *display_state,
13661389
for (ref = stale_refs; ref; ref = ref->next) {
13671390
display_ref_update(display_state, '-', _("[deleted]"), NULL,
13681391
_("(none)"), ref->name,
1392+
&ref->new_oid, &ref->old_oid,
13691393
summary_width);
13701394
warn_dangling_symref(stderr, dangling_msg, ref->name);
13711395
}
@@ -1798,7 +1822,8 @@ static int add_remote_or_group(const char *name, struct string_list *list)
17981822
return 1;
17991823
}
18001824

1801-
static void add_options_to_argv(struct strvec *argv)
1825+
static void add_options_to_argv(struct strvec *argv,
1826+
enum display_format format)
18021827
{
18031828
if (dry_run)
18041829
strvec_push(argv, "--dry-run");
@@ -1834,6 +1859,8 @@ static void add_options_to_argv(struct strvec *argv)
18341859
strvec_push(argv, "--ipv6");
18351860
if (!write_fetch_head)
18361861
strvec_push(argv, "--no-write-fetch-head");
1862+
if (format == DISPLAY_FORMAT_PORCELAIN)
1863+
strvec_pushf(argv, "--porcelain");
18371864
}
18381865

18391866
/* Fetch multiple remotes in parallel */
@@ -1842,6 +1869,7 @@ struct parallel_fetch_state {
18421869
const char **argv;
18431870
struct string_list *remotes;
18441871
int next, result;
1872+
enum display_format format;
18451873
};
18461874

18471875
static int fetch_next_remote(struct child_process *cp,
@@ -1861,7 +1889,7 @@ static int fetch_next_remote(struct child_process *cp,
18611889
strvec_push(&cp->args, remote);
18621890
cp->git_cmd = 1;
18631891

1864-
if (verbosity >= 0)
1892+
if (verbosity >= 0 && state->format != DISPLAY_FORMAT_PORCELAIN)
18651893
printf(_("Fetching %s\n"), remote);
18661894

18671895
return 1;
@@ -1893,7 +1921,8 @@ static int fetch_finished(int result, struct strbuf *out,
18931921
return 0;
18941922
}
18951923

1896-
static int fetch_multiple(struct string_list *list, int max_children)
1924+
static int fetch_multiple(struct string_list *list, int max_children,
1925+
enum display_format format)
18971926
{
18981927
int i, result = 0;
18991928
struct strvec argv = STRVEC_INIT;
@@ -1911,10 +1940,10 @@ static int fetch_multiple(struct string_list *list, int max_children)
19111940
strvec_pushl(&argv, "-c", "fetch.bundleURI=",
19121941
"fetch", "--append", "--no-auto-gc",
19131942
"--no-write-commit-graph", NULL);
1914-
add_options_to_argv(&argv);
1943+
add_options_to_argv(&argv, format);
19151944

19161945
if (max_children != 1 && list->nr != 1) {
1917-
struct parallel_fetch_state state = { argv.v, list, 0, 0 };
1946+
struct parallel_fetch_state state = { argv.v, list, 0, 0, format };
19181947
const struct run_process_parallel_opts opts = {
19191948
.tr2_category = "fetch",
19201949
.tr2_label = "parallel/fetch",
@@ -1938,7 +1967,7 @@ static int fetch_multiple(struct string_list *list, int max_children)
19381967

19391968
strvec_pushv(&cmd.args, argv.v);
19401969
strvec_push(&cmd.args, name);
1941-
if (verbosity >= 0)
1970+
if (verbosity >= 0 && format != DISPLAY_FORMAT_PORCELAIN)
19421971
printf(_("Fetching %s\n"), name);
19431972
cmd.git_cmd = 1;
19441973
if (run_command(&cmd)) {
@@ -2089,6 +2118,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
20892118
int fetch_write_commit_graph = -1;
20902119
int stdin_refspecs = 0;
20912120
int negotiate_only = 0;
2121+
int porcelain = 0;
20922122
int i;
20932123

20942124
struct option builtin_fetch_options[] = {
@@ -2123,6 +2153,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
21232153
PARSE_OPT_OPTARG, option_fetch_parse_recurse_submodules),
21242154
OPT_BOOL(0, "dry-run", &dry_run,
21252155
N_("dry run")),
2156+
OPT_BOOL(0, "porcelain", &porcelain, N_("machine-readable output")),
21262157
OPT_BOOL(0, "write-fetch-head", &write_fetch_head,
21272158
N_("write fetched references to the FETCH_HEAD file")),
21282159
OPT_BOOL('k', "keep", &keep, N_("keep downloaded pack")),
@@ -2228,6 +2259,26 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
22282259
fetch_config_from_gitmodules(sfjc, rs);
22292260
}
22302261

2262+
2263+
if (porcelain) {
2264+
switch (recurse_submodules_cli) {
2265+
case RECURSE_SUBMODULES_OFF:
2266+
case RECURSE_SUBMODULES_DEFAULT:
2267+
/*
2268+
* Reference updates in submodules would be ambiguous
2269+
* in porcelain mode, so we reject this combination.
2270+
*/
2271+
recurse_submodules = RECURSE_SUBMODULES_OFF;
2272+
break;
2273+
2274+
default:
2275+
die(_("options '%s' and '%s' cannot be used together"),
2276+
"--porcelain", "--recurse-submodules");
2277+
}
2278+
2279+
config.display_format = DISPLAY_FORMAT_PORCELAIN;
2280+
}
2281+
22312282
if (negotiate_only && !negotiation_tip.nr)
22322283
die(_("--negotiate-only needs one or more --negotiation-tip=*"));
22332284

@@ -2347,10 +2398,9 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
23472398
max_children = fetch_parallel_config;
23482399

23492400
/* TODO should this also die if we have a previous partial-clone? */
2350-
result = fetch_multiple(&list, max_children);
2401+
result = fetch_multiple(&list, max_children, config.display_format);
23512402
}
23522403

2353-
23542404
/*
23552405
* This is only needed after fetch_one(), which does not fetch
23562406
* submodules by itself.
@@ -2369,7 +2419,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
23692419
if (max_children < 0)
23702420
max_children = fetch_parallel_config;
23712421

2372-
add_options_to_argv(&options);
2422+
add_options_to_argv(&options, config.display_format);
23732423
result = fetch_submodules(the_repository,
23742424
&options,
23752425
submodule_prefix,

0 commit comments

Comments
 (0)