status: add --[no-]ahead-behind to status and commit for V2 format.

Teach "git status" and "git commit" to accept "--no-ahead-behind" and "--ahead-behind" arguments to request quick or full ahead/behind reporting. When "--no-ahead-behind" is given, the existing porcelain V2 line "branch.ab +x -y" is replaced with a new "branch.ab +? -?" line. This indicates that the branch and its upstream are or are not equal without the expense of computing the full ahead/behind values. Signed-off-by: Jeff Hostetler <jeffhost@microsoft.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Jeff Hostetler committed Jan 9, 2018 at 18:50 UTC fd9b544a2991ad74d73ad1bc0af4d24f91a6802b
7 files changed +102 -11
Documentation/git-status.txt
+5
@@ -130,6 +130,11 @@ ignored, then the directory is not shown, but all contents are shown.
130 without options are equivalent to 'always' and 'never'
131 respectively.
132
133 +--ahead-behind::
134 +--no-ahead-behind::
135 + Display or do not display detailed ahead/behind counts for the
136 + branch relative to its upstream branch. Defaults to true.
137 +
138 <pathspec>...::
139 See the 'pathspec' entry in linkgit:gitglossary[7].
140
builtin/commit.c
+7
@@ -1151,6 +1151,9 @@ static void finalize_deferred_config(struct wt_status *s)
1151 s->show_branch = status_deferred_config.show_branch;
1152 if (s->show_branch < 0)
1153 s->show_branch = 0;
1154 +
1155 + if (s->ahead_behind_flags == AHEAD_BEHIND_UNSPECIFIED)
1156 + s->ahead_behind_flags = AHEAD_BEHIND_FULL;
1157 }
1158
1159 static int parse_and_validate_options(int argc, const char *argv[],
@@ -1365,6 +1368,8 @@ int cmd_status(int argc, const char **argv, const char *prefix)
1368 N_("show branch information")),
1369 OPT_BOOL(0, "show-stash", &s.show_stash,
1370 N_("show stash information")),
1371 + OPT_BOOL(0, "ahead-behind", &s.ahead_behind_flags,
1372 + N_("compute full ahead/behind values")),
1373 { OPTION_CALLBACK, 0, "porcelain", &status_format,
1374 N_("version"), N_("machine-readable output"),
1375 PARSE_OPT_OPTARG, opt_parse_porcelain },
@@ -1648,6 +1653,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
1653 OPT_SET_INT(0, "short", &status_format, N_("show status concisely"),
1654 STATUS_FORMAT_SHORT),
1655 OPT_BOOL(0, "branch", &s.show_branch, N_("show branch information")),
1656 + OPT_BOOL(0, "ahead-behind", &s.ahead_behind_flags,
1657 + N_("compute full ahead/behind values")),
1658 OPT_SET_INT(0, "porcelain", &status_format,
1659 N_("machine-readable output"), STATUS_FORMAT_PORCELAIN),
1660 OPT_SET_INT(0, "long", &status_format,
remote.c
+2
@@ -2058,6 +2058,8 @@ int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs,
2058 return 0;
2059 if (abf == AHEAD_BEHIND_QUICK)
2060 return 1;
2061 + if (abf != AHEAD_BEHIND_FULL)
2062 + BUG("stat_tracking_info: invalid abf '%d'", abf);
2063
2064 /* Run "rev-list --left-right ours...theirs" internally... */
2065 argv_array_push(&argv, ""); /* ignored */
remote.h
+3 -2
@@ -259,8 +259,9 @@ enum match_refs_flags {
259
260 /* Flags for --ahead-behind option. */
261 enum ahead_behind_flags {
262 - AHEAD_BEHIND_QUICK = 0, /* just eq/neq reporting */
263 - AHEAD_BEHIND_FULL = 1, /* traditional a/b reporting */
262 + AHEAD_BEHIND_UNSPECIFIED = -1,
263 + AHEAD_BEHIND_QUICK = 0, /* just eq/neq reporting */
264 + AHEAD_BEHIND_FULL = 1, /* traditional a/b reporting */
265 };
266
267 /* Reporting of tracking info */
t/t7064-wtstatus-pv2.sh
+62
@@ -390,6 +390,68 @@ test_expect_success 'verify upstream fields in branch header' '
390 )
391 '
392
393 +test_expect_success 'verify --[no-]ahead-behind with V2 format' '
394 + git checkout master &&
395 + test_when_finished "rm -rf sub_repo" &&
396 + git clone . sub_repo &&
397 + (
398 + ## Confirm local master tracks remote master.
399 + cd sub_repo &&
400 + HUF=$(git rev-parse HEAD) &&
401 +
402 + # Confirm --no-ahead-behind reports traditional branch.ab with 0/0 for equal branches.
403 + cat >expect <<-EOF &&
404 + # branch.oid $HUF
405 + # branch.head master
406 + # branch.upstream origin/master
407 + # branch.ab +0 -0
408 + EOF
409 +
410 + git status --no-ahead-behind --porcelain=v2 --branch --untracked-files=all >actual &&
411 + test_cmp expect actual &&
412 +
413 + # Confirm --ahead-behind reports traditional branch.ab with 0/0.
414 + cat >expect <<-EOF &&
415 + # branch.oid $HUF
416 + # branch.head master
417 + # branch.upstream origin/master
418 + # branch.ab +0 -0
419 + EOF
420 +
421 + git status --ahead-behind --porcelain=v2 --branch --untracked-files=all >actual &&
422 + test_cmp expect actual &&
423 +
424 + ## Test non-equal ahead/behind.
425 + echo xyz >file_xyz &&
426 + git add file_xyz &&
427 + git commit -m xyz &&
428 +
429 + HUF=$(git rev-parse HEAD) &&
430 +
431 + # Confirm --no-ahead-behind reports branch.ab with ?/? for non-equal branches.
432 + cat >expect <<-EOF &&
433 + # branch.oid $HUF
434 + # branch.head master
435 + # branch.upstream origin/master
436 + # branch.ab +? -?
437 + EOF
438 +
439 + git status --no-ahead-behind --porcelain=v2 --branch --untracked-files=all >actual &&
440 + test_cmp expect actual &&
441 +
442 + # Confirm --ahead-behind reports traditional branch.ab with 1/0.
443 + cat >expect <<-EOF &&
444 + # branch.oid $HUF
445 + # branch.head master
446 + # branch.upstream origin/master
447 + # branch.ab +1 -0
448 + EOF
449 +
450 + git status --ahead-behind --porcelain=v2 --branch --untracked-files=all >actual &&
451 + test_cmp expect actual
452 + )
453 +'
454 +
455 test_expect_success 'create and add submodule, submodule appears clean (A. S...)' '
456 git checkout master &&
457 git clone . sub_repo &&
wt-status.c
+21 -9
@@ -136,6 +136,7 @@ void wt_status_prepare(struct wt_status *s)
136 s->ignored.strdup_strings = 1;
137 s->show_branch = -1; /* unspecified */
138 s->show_stash = 0;
139 + s->ahead_behind_flags = AHEAD_BEHIND_UNSPECIFIED;
140 s->display_comment_prefix = 0;
141 }
142
@@ -1883,18 +1884,19 @@ static void wt_porcelain_print(struct wt_status *s)
1884 *
1885 * <upstream> ::= the upstream branch name, when set.
1886 *
1886 - * <ahead> ::= integer ahead value, when upstream set
1887 - * and the commit is present (not gone).
1888 - *
1889 - * <behind> ::= integer behind value, when upstream set
1890 - * and commit is present.
1887 + * <ahead> ::= integer ahead value or '?'.
1888 *
1889 + * <behind> ::= integer behind value or '?'.
1890 *
1891 * The end-of-line is defined by the -z flag.
1892 *
1893 * <eol> ::= NUL when -z,
1894 * LF when NOT -z.
1895 *
1896 + * When an upstream is set and present, the 'branch.ab' line will
1897 + * be printed with the ahead/behind counts for the branch and the
1898 + * upstream. When AHEAD_BEHIND_QUICK is requested and the branches
1899 + * are different, '?' will be substituted for the actual count.
1900 */
1901 static void wt_porcelain_v2_print_tracking(struct wt_status *s)
1902 {
@@ -1934,15 +1936,25 @@ static void wt_porcelain_v2_print_tracking(struct wt_status *s)
1936 /* Lookup stats on the upstream tracking branch, if set. */
1937 branch = branch_get(branch_name);
1938 base = NULL;
1937 - ab_info = (stat_tracking_info(branch, &nr_ahead, &nr_behind,
1938 - &base, AHEAD_BEHIND_FULL) >= 0);
1939 + ab_info = stat_tracking_info(branch, &nr_ahead, &nr_behind,
1940 + &base, s->ahead_behind_flags);
1941 if (base) {
1942 base = shorten_unambiguous_ref(base, 0);
1943 fprintf(s->fp, "# branch.upstream %s%c", base, eol);
1944 free((char *)base);
1945
1944 - if (ab_info)
1945 - fprintf(s->fp, "# branch.ab +%d -%d%c", nr_ahead, nr_behind, eol);
1946 + if (ab_info > 0) {
1947 + /* different */
1948 + if (nr_ahead || nr_behind)
1949 + fprintf(s->fp, "# branch.ab +%d -%d%c",
1950 + nr_ahead, nr_behind, eol);
1951 + else
1952 + fprintf(s->fp, "# branch.ab +? -?%c",
1953 + eol);
1954 + } else if (!ab_info) {
1955 + /* same */
1956 + fprintf(s->fp, "# branch.ab +0 -0%c", eol);
1957 + }
1958 }
1959 }
1960
wt-status.h
+2
@@ -5,6 +5,7 @@
5 #include "string-list.h"
6 #include "color.h"
7 #include "pathspec.h"
8 +#include "remote.h"
9
10 struct worktree;
11
@@ -86,6 +87,7 @@ struct wt_status {
87 int show_branch;
88 int show_stash;
89 int hints;
90 + enum ahead_behind_flags ahead_behind_flags;
91
92 enum wt_status_format status_format;
93 unsigned char sha1_commit[GIT_MAX_RAWSZ]; /* when not Initial */