{"thread":{"id":"47579","subject":"[PATCH v5 0/4] Add --no-ahead-behind to status","startedAt":"2018-01-09T18:50:38Z","lastAt":"2018-01-09T18:50:46Z","messageCount":5,"participants":["Jeff Hostetler"],"isPatch":true,"patchVersion":5,"patchTotal":4},"messages":[{"id":"336286","messageId":"20180109185018.69164-1-git@jeffhostetler.com","threadId":"47579","inReplyTo":null,"subject":"[PATCH v5 0/4] Add --no-ahead-behind to status","fromName":"Jeff Hostetler","fromEmail":"git@jeffhostetler.com","sentAt":"2018-01-09T18:50:14Z","receivedAt":"2018-01-09T18:50:38Z","isPatch":true,"sender":{"key":"git@jeffhostetler.com","avatar":null},"body":"From: Jeff Hostetler <jeffhost@microsoft.com>\n\nThis is version 5 of my patch series to avoid expensive ahead/behind\ncalculations in status.  This version completely removes the config\nsetting and is just the --[no-]ahead-behind command line argument.\n\nInternally (inside MSFT) we have had further discussions in this\narea and identified 2 somewhat independent needs:\n\n[1] The first is to just be able to turn off the a/b calculation when\n    the invoker does not need the result at all.\n\n[2] The second is to greatly speed up or limit the a/b calculation on\n    extremely large repositories.\n\n    In earlier versions of this patch series, there were several\n    discussions of a limited mode that would look for no more than n\n    commits or milliseconds.  Also discussed were some problems that\n    such limiting will have and it was decided to be not worth the effort.\n\n    We have been discussing some ways to speed up the calculation on the\n    client and have tentatively scheduled this shortly.  Hopefully, this\n    will eliminate the performance problems and reduce the likelyhood\n    that anyone would need to set a config setting to change the default\n    behavior (in either porcelain or non-porcelain) formats.\n\nSo with that in mind, this version elimates the config setting so that\nwe have don't to carry forward a soon-to-be-obsolete setting.\n\nAll that remains in this version is the command line argument to turn\non/off the a/b calculation.\n\nJeff Hostetler (4):\n  stat_tracking_info: return +1 when branches not equal\n  status: add --[no-]ahead-behind to status and commit for V2 format.\n  status: update short status to respect --no-ahead-behind\n  status: support --no-ahead-behind in long format\n\n Documentation/git-status.txt |  5 ++++\n builtin/checkout.c           |  2 +-\n builtin/commit.c             |  7 +++++\n ref-filter.c                 |  8 +++---\n remote.c                     | 50 +++++++++++++++++++++++------------\n remote.h                     | 12 +++++++--\n t/t6040-tracking-info.sh     | 42 ++++++++++++++++++++++++++++++\n t/t7064-wtstatus-pv2.sh      | 62 ++++++++++++++++++++++++++++++++++++++++++++\n wt-status.c                  | 41 ++++++++++++++++++++---------\n wt-status.h                  |  2 ++\n 10 files changed, 196 insertions(+), 35 deletions(-)\n\n-- \n2.9.3\n\n"},{"id":"336287","messageId":"20180109185018.69164-4-git@jeffhostetler.com","threadId":"47579","inReplyTo":"20180109185018.69164-1-git@jeffhostetler.com","subject":"[PATCH v5 3/4] status: update short status to respect --no-ahead-behind","fromName":"Jeff Hostetler","fromEmail":"git@jeffhostetler.com","sentAt":"2018-01-09T18:50:17Z","receivedAt":"2018-01-09T18:50:40Z","isPatch":true,"sender":{"key":"git@jeffhostetler.com","avatar":null},"body":"From: Jeff Hostetler <jeffhost@microsoft.com>\n\nTeach \"git status --short --branch\" to respect \"--no-ahead-behind\"\nparameter to skip computing ahead/behind counts for the branch and\nits upstream and just report '[different]'.\n\nSigned-off-by: Jeff Hostetler <jeffhost@microsoft.com>\n---\n t/t6040-tracking-info.sh | 13 +++++++++++++\n wt-status.c              | 11 +++++++----\n 2 files changed, 20 insertions(+), 4 deletions(-)\n\ndiff --git a/t/t6040-tracking-info.sh b/t/t6040-tracking-info.sh\nindex 8f17fd9..0190220 100755\n--- a/t/t6040-tracking-info.sh\n+++ b/t/t6040-tracking-info.sh\n@@ -147,6 +147,19 @@ test_expect_success 'status -s -b (diverged from upstream)' '\n '\n \n cat >expect <<\\EOF\n+## b1...origin/master [different]\n+EOF\n+\n+test_expect_success 'status -s -b --no-ahead-behind (diverged from upstream)' '\n+\t(\n+\t\tcd test &&\n+\t\tgit checkout b1 >/dev/null &&\n+\t\tgit status -s -b --no-ahead-behind | head -1\n+\t) >actual &&\n+\ttest_i18ncmp expect actual\n+'\n+\n+cat >expect <<\\EOF\n ## b5...brokenbase [gone]\n EOF\n \ndiff --git a/wt-status.c b/wt-status.c\nindex 3fcab57..a4d3470 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -1766,7 +1766,7 @@ static void wt_shortstatus_print_tracking(struct wt_status *s)\n \tconst char *base;\n \tchar *short_base;\n \tconst char *branch_name;\n-\tint num_ours, num_theirs;\n+\tint num_ours, num_theirs, sti;\n \tint upstream_is_gone = 0;\n \n \tcolor_fprintf(s->fp, color(WT_STATUS_HEADER, s), \"## \");\n@@ -1792,8 +1792,9 @@ static void wt_shortstatus_print_tracking(struct wt_status *s)\n \n \tcolor_fprintf(s->fp, branch_color_local, \"%s\", branch_name);\n \n-\tif (stat_tracking_info(branch, &num_ours, &num_theirs, &base,\n-\t\t\t       AHEAD_BEHIND_FULL) < 0) {\n+\tsti = stat_tracking_info(branch, &num_ours, &num_theirs, &base,\n+\t\t\t\t s->ahead_behind_flags);\n+\tif (sti < 0) {\n \t\tif (!base)\n \t\t\tgoto conclude;\n \n@@ -1805,12 +1806,14 @@ static void wt_shortstatus_print_tracking(struct wt_status *s)\n \tcolor_fprintf(s->fp, branch_color_remote, \"%s\", short_base);\n \tfree(short_base);\n \n-\tif (!upstream_is_gone && !num_ours && !num_theirs)\n+\tif (!upstream_is_gone && !sti)\n \t\tgoto conclude;\n \n \tcolor_fprintf(s->fp, header_color, \" [\");\n \tif (upstream_is_gone) {\n \t\tcolor_fprintf(s->fp, header_color, LABEL(N_(\"gone\")));\n+\t} else if (s->ahead_behind_flags == AHEAD_BEHIND_QUICK) {\n+\t\tcolor_fprintf(s->fp, header_color, LABEL(N_(\"different\")));\n \t} else if (!num_ours) {\n \t\tcolor_fprintf(s->fp, header_color, LABEL(N_(\"behind \")));\n \t\tcolor_fprintf(s->fp, branch_color_remote, \"%d\", num_theirs);\n-- \n2.9.3\n\n"},{"id":"336288","messageId":"20180109185018.69164-2-git@jeffhostetler.com","threadId":"47579","inReplyTo":"20180109185018.69164-1-git@jeffhostetler.com","subject":"[PATCH v5 1/4] stat_tracking_info: return +1 when branches not equal","fromName":"Jeff Hostetler","fromEmail":"git@jeffhostetler.com","sentAt":"2018-01-09T18:50:15Z","receivedAt":"2018-01-09T18:50:41Z","isPatch":true,"sender":{"key":"git@jeffhostetler.com","avatar":null},"body":"From: Jeff Hostetler <jeffhost@microsoft.com>\n\nExtend stat_tracking_info() to return +1 when branches are not equal and to\ntake a new \"enum ahead_behind_flags\" argument to allow skipping the (possibly\nexpensive) ahead/behind computation.\n\nThis will be used in the next commit to allow \"git status\" to avoid full\nahead/behind calculations for performance reasons.\n\nSigned-off-by: Jeff Hostetler <jeffhost@microsoft.com>\n---\n ref-filter.c |  8 ++++----\n remote.c     | 34 +++++++++++++++++++++-------------\n remote.h     |  8 +++++++-\n wt-status.c  |  6 ++++--\n 4 files changed, 36 insertions(+), 20 deletions(-)\n\ndiff --git a/ref-filter.c b/ref-filter.c\nindex e728b15..23bcdc4 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -1238,8 +1238,8 @@ static void fill_remote_ref_details(struct used_atom *atom, const char *refname,\n \tif (atom->u.remote_ref.option == RR_REF)\n \t\t*s = show_ref(&atom->u.remote_ref.refname, refname);\n \telse if (atom->u.remote_ref.option == RR_TRACK) {\n-\t\tif (stat_tracking_info(branch, &num_ours,\n-\t\t\t\t       &num_theirs, NULL)) {\n+\t\tif (stat_tracking_info(branch, &num_ours, &num_theirs,\n+\t\t\t\t       NULL, AHEAD_BEHIND_FULL) < 0) {\n \t\t\t*s = xstrdup(msgs.gone);\n \t\t} else if (!num_ours && !num_theirs)\n \t\t\t*s = \"\";\n@@ -1256,8 +1256,8 @@ static void fill_remote_ref_details(struct used_atom *atom, const char *refname,\n \t\t\tfree((void *)to_free);\n \t\t}\n \t} else if (atom->u.remote_ref.option == RR_TRACKSHORT) {\n-\t\tif (stat_tracking_info(branch, &num_ours,\n-\t\t\t\t       &num_theirs, NULL))\n+\t\tif (stat_tracking_info(branch, &num_ours, &num_theirs,\n+\t\t\t\t       NULL, AHEAD_BEHIND_FULL) < 0)\n \t\t\treturn;\n \n \t\tif (!num_ours && !num_theirs)\ndiff --git a/remote.c b/remote.c\nindex b220f0d..23177f5 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -1977,16 +1977,23 @@ int ref_newer(const struct object_id *new_oid, const struct object_id *old_oid)\n }\n \n /*\n- * Compare a branch with its upstream, and save their differences (number\n- * of commits) in *num_ours and *num_theirs. The name of the upstream branch\n- * (or NULL if no upstream is defined) is returned via *upstream_name, if it\n- * is not itself NULL.\n+ * Lookup the upstream branch for the given branch and if present, optionally\n+ * compute the commit ahead/behind values for the pair.\n+ *\n+ * If abf is AHEAD_BEHIND_FULL, compute the full ahead/behind and return the\n+ * counts in *num_ours and *num_theirs.  If abf is AHEAD_BEHIND_QUICK, skip\n+ * the (potentially expensive) a/b computation (*num_ours and *num_theirs are\n+ * set to zero).\n+ *\n+ * The name of the upstream branch (or NULL if no upstream is defined) is\n+ * returned via *upstream_name, if it is not itself NULL.\n  *\n  * Returns -1 if num_ours and num_theirs could not be filled in (e.g., no\n- * upstream defined, or ref does not exist), 0 otherwise.\n+ * upstream defined, or ref does not exist).  Returns 0 if the commits are\n+ * identical.  Returns 1 if commits are different.\n  */\n int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs,\n-\t\t       const char **upstream_name)\n+\t\t       const char **upstream_name, enum ahead_behind_flags abf)\n {\n \tstruct object_id oid;\n \tstruct commit *ours, *theirs;\n@@ -2014,11 +2021,13 @@ int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs,\n \tif (!ours)\n \t\treturn -1;\n \n+\t*num_theirs = *num_ours = 0;\n+\n \t/* are we the same? */\n-\tif (theirs == ours) {\n-\t\t*num_theirs = *num_ours = 0;\n+\tif (theirs == ours)\n \t\treturn 0;\n-\t}\n+\tif (abf == AHEAD_BEHIND_QUICK)\n+\t\treturn 1;\n \n \t/* Run \"rev-list --left-right ours...theirs\" internally... */\n \targv_array_push(&argv, \"\"); /* ignored */\n@@ -2034,8 +2043,6 @@ int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs,\n \t\tdie(\"revision walk setup failed\");\n \n \t/* ... and count the commits on each side. */\n-\t*num_ours = 0;\n-\t*num_theirs = 0;\n \twhile (1) {\n \t\tstruct commit *c = get_revision(&revs);\n \t\tif (!c)\n@@ -2051,7 +2058,7 @@ int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs,\n \tclear_commit_marks(theirs, ALL_REV_FLAGS);\n \n \targv_array_clear(&argv);\n-\treturn 0;\n+\treturn 1;\n }\n \n /*\n@@ -2064,7 +2071,8 @@ int format_tracking_info(struct branch *branch, struct strbuf *sb)\n \tchar *base;\n \tint upstream_is_gone = 0;\n \n-\tif (stat_tracking_info(branch, &ours, &theirs, &full_base) < 0) {\n+\tif (stat_tracking_info(branch, &ours, &theirs, &full_base,\n+\t\t\t       AHEAD_BEHIND_FULL) < 0) {\n \t\tif (!full_base)\n \t\t\treturn 0;\n \t\tupstream_is_gone = 1;\ndiff --git a/remote.h b/remote.h\nindex 2ecf4c8..00932f5 100644\n--- a/remote.h\n+++ b/remote.h\n@@ -255,9 +255,15 @@ enum match_refs_flags {\n \tMATCH_REFS_FOLLOW_TAGS\t= (1 << 3)\n };\n \n+/* Flags for --ahead-behind option. */\n+enum ahead_behind_flags {\n+\tAHEAD_BEHIND_QUICK = 0,  /* just eq/neq reporting */\n+\tAHEAD_BEHIND_FULL  = 1,  /* traditional a/b reporting */\n+};\n+\n /* Reporting of tracking info */\n int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs,\n-\t\t       const char **upstream_name);\n+\t\t       const char **upstream_name, enum ahead_behind_flags abf);\n int format_tracking_info(struct branch *branch, struct strbuf *sb);\n \n struct ref *get_local_heads(void);\ndiff --git a/wt-status.c b/wt-status.c\nindex 94e5eba..8f7fdc6 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -1791,7 +1791,8 @@ static void wt_shortstatus_print_tracking(struct wt_status *s)\n \n \tcolor_fprintf(s->fp, branch_color_local, \"%s\", branch_name);\n \n-\tif (stat_tracking_info(branch, &num_ours, &num_theirs, &base) < 0) {\n+\tif (stat_tracking_info(branch, &num_ours, &num_theirs, &base,\n+\t\t\t       AHEAD_BEHIND_FULL) < 0) {\n \t\tif (!base)\n \t\t\tgoto conclude;\n \n@@ -1928,7 +1929,8 @@ static void wt_porcelain_v2_print_tracking(struct wt_status *s)\n \t\t/* Lookup stats on the upstream tracking branch, if set. */\n \t\tbranch = branch_get(branch_name);\n \t\tbase = NULL;\n-\t\tab_info = (stat_tracking_info(branch, &nr_ahead, &nr_behind, &base) == 0);\n+\t\tab_info = (stat_tracking_info(branch, &nr_ahead, &nr_behind,\n+\t\t\t\t\t      &base, AHEAD_BEHIND_FULL) >= 0);\n \t\tif (base) {\n \t\t\tbase = shorten_unambiguous_ref(base, 0);\n \t\t\tfprintf(s->fp, \"# branch.upstream %s%c\", base, eol);\n-- \n2.9.3\n\n"},{"id":"336289","messageId":"20180109185018.69164-3-git@jeffhostetler.com","threadId":"47579","inReplyTo":"20180109185018.69164-1-git@jeffhostetler.com","subject":"[PATCH v5 2/4] status: add --[no-]ahead-behind to status and commit for V2 format.","fromName":"Jeff Hostetler","fromEmail":"git@jeffhostetler.com","sentAt":"2018-01-09T18:50:16Z","receivedAt":"2018-01-09T18:50:44Z","isPatch":true,"sender":{"key":"git@jeffhostetler.com","avatar":null},"body":"From: Jeff Hostetler <jeffhost@microsoft.com>\n\nTeach \"git status\" and \"git commit\" to accept \"--no-ahead-behind\"\nand \"--ahead-behind\" arguments to request quick or full ahead/behind\nreporting.\n\nWhen \"--no-ahead-behind\" is given, the existing porcelain V2 line\n\"branch.ab +x -y\" is replaced with a new \"branch.ab +? -?\" line.\nThis indicates that the branch and its upstream are or are not equal\nwithout the expense of computing the full ahead/behind values.\n\nSigned-off-by: Jeff Hostetler <jeffhost@microsoft.com>\n---\n Documentation/git-status.txt |  5 ++++\n builtin/commit.c             |  7 +++++\n remote.c                     |  2 ++\n remote.h                     |  5 ++--\n t/t7064-wtstatus-pv2.sh      | 62 ++++++++++++++++++++++++++++++++++++++++++++\n wt-status.c                  | 30 ++++++++++++++-------\n wt-status.h                  |  2 ++\n 7 files changed, 102 insertions(+), 11 deletions(-)\n\ndiff --git a/Documentation/git-status.txt b/Documentation/git-status.txt\nindex 9f3a78a..603bf40 100644\n--- a/Documentation/git-status.txt\n+++ b/Documentation/git-status.txt\n@@ -111,6 +111,11 @@ configuration variable documented in linkgit:git-config[1].\n \twithout options are equivalent to 'always' and 'never'\n \trespectively.\n \n+--ahead-behind::\n+--no-ahead-behind::\n+\tDisplay or do not display detailed ahead/behind counts for the\n+\tbranch relative to its upstream branch.  Defaults to true.\n+\n <pathspec>...::\n \tSee the 'pathspec' entry in linkgit:gitglossary[7].\n \ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex be370f6..cfe7c62 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -1137,6 +1137,9 @@ static void finalize_deferred_config(struct wt_status *s)\n \t\ts->show_branch = status_deferred_config.show_branch;\n \tif (s->show_branch < 0)\n \t\ts->show_branch = 0;\n+\n+\tif (s->ahead_behind_flags == AHEAD_BEHIND_UNSPECIFIED)\n+\t\ts->ahead_behind_flags = AHEAD_BEHIND_FULL;\n }\n \n static int parse_and_validate_options(int argc, const char *argv[],\n@@ -1351,6 +1354,8 @@ int cmd_status(int argc, const char **argv, const char *prefix)\n \t\t\t N_(\"show branch information\")),\n \t\tOPT_BOOL(0, \"show-stash\", &s.show_stash,\n \t\t\t N_(\"show stash information\")),\n+\t\tOPT_BOOL(0, \"ahead-behind\", &s.ahead_behind_flags,\n+\t\t\t N_(\"compute full ahead/behind values\")),\n \t\t{ OPTION_CALLBACK, 0, \"porcelain\", &status_format,\n \t\t  N_(\"version\"), N_(\"machine-readable output\"),\n \t\t  PARSE_OPT_OPTARG, opt_parse_porcelain },\n@@ -1628,6 +1633,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix)\n \t\tOPT_SET_INT(0, \"short\", &status_format, N_(\"show status concisely\"),\n \t\t\t    STATUS_FORMAT_SHORT),\n \t\tOPT_BOOL(0, \"branch\", &s.show_branch, N_(\"show branch information\")),\n+\t\tOPT_BOOL(0, \"ahead-behind\", &s.ahead_behind_flags,\n+\t\t\t N_(\"compute full ahead/behind values\")),\n \t\tOPT_SET_INT(0, \"porcelain\", &status_format,\n \t\t\t    N_(\"machine-readable output\"), STATUS_FORMAT_PORCELAIN),\n \t\tOPT_SET_INT(0, \"long\", &status_format,\ndiff --git a/remote.c b/remote.c\nindex 23177f5..2486afb 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -2028,6 +2028,8 @@ int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs,\n \t\treturn 0;\n \tif (abf == AHEAD_BEHIND_QUICK)\n \t\treturn 1;\n+\tif (abf != AHEAD_BEHIND_FULL)\n+\t\tBUG(\"stat_tracking_info: invalid abf '%d'\", abf);\n \n \t/* Run \"rev-list --left-right ours...theirs\" internally... */\n \targv_array_push(&argv, \"\"); /* ignored */\ndiff --git a/remote.h b/remote.h\nindex 00932f5..27feb63 100644\n--- a/remote.h\n+++ b/remote.h\n@@ -257,8 +257,9 @@ enum match_refs_flags {\n \n /* Flags for --ahead-behind option. */\n enum ahead_behind_flags {\n-\tAHEAD_BEHIND_QUICK = 0,  /* just eq/neq reporting */\n-\tAHEAD_BEHIND_FULL  = 1,  /* traditional a/b reporting */\n+\tAHEAD_BEHIND_UNSPECIFIED = -1,\n+\tAHEAD_BEHIND_QUICK       =  0,  /* just eq/neq reporting */\n+\tAHEAD_BEHIND_FULL        =  1,  /* traditional a/b reporting */\n };\n \n /* Reporting of tracking info */\ndiff --git a/t/t7064-wtstatus-pv2.sh b/t/t7064-wtstatus-pv2.sh\nindex e319fa2..8f79532 100755\n--- a/t/t7064-wtstatus-pv2.sh\n+++ b/t/t7064-wtstatus-pv2.sh\n@@ -390,6 +390,68 @@ test_expect_success 'verify upstream fields in branch header' '\n \t)\n '\n \n+test_expect_success 'verify --[no-]ahead-behind with V2 format' '\n+\tgit checkout master &&\n+\ttest_when_finished \"rm -rf sub_repo\" &&\n+\tgit clone . sub_repo &&\n+\t(\n+\t\t## Confirm local master tracks remote master.\n+\t\tcd sub_repo &&\n+\t\tHUF=$(git rev-parse HEAD) &&\n+\n+\t\t# Confirm --no-ahead-behind reports traditional branch.ab with 0/0 for equal branches.\n+\t\tcat >expect <<-EOF &&\n+\t\t# branch.oid $HUF\n+\t\t# branch.head master\n+\t\t# branch.upstream origin/master\n+\t\t# branch.ab +0 -0\n+\t\tEOF\n+\n+\t\tgit status --no-ahead-behind --porcelain=v2 --branch --untracked-files=all >actual &&\n+\t\ttest_cmp expect actual &&\n+\n+\t\t# Confirm --ahead-behind reports traditional branch.ab with 0/0.\n+\t\tcat >expect <<-EOF &&\n+\t\t# branch.oid $HUF\n+\t\t# branch.head master\n+\t\t# branch.upstream origin/master\n+\t\t# branch.ab +0 -0\n+\t\tEOF\n+\n+\t\tgit status --ahead-behind --porcelain=v2 --branch --untracked-files=all >actual &&\n+\t\ttest_cmp expect actual &&\n+\n+\t\t## Test non-equal ahead/behind.\n+\t\techo xyz >file_xyz &&\n+\t\tgit add file_xyz &&\n+\t\tgit commit -m xyz &&\n+\n+\t\tHUF=$(git rev-parse HEAD) &&\n+\n+\t\t# Confirm --no-ahead-behind reports branch.ab with ?/? for non-equal branches.\n+\t\tcat >expect <<-EOF &&\n+\t\t# branch.oid $HUF\n+\t\t# branch.head master\n+\t\t# branch.upstream origin/master\n+\t\t# branch.ab +? -?\n+\t\tEOF\n+\n+\t\tgit status --no-ahead-behind --porcelain=v2 --branch --untracked-files=all >actual &&\n+\t\ttest_cmp expect actual &&\n+\n+\t\t# Confirm --ahead-behind reports traditional branch.ab with 1/0.\n+\t\tcat >expect <<-EOF &&\n+\t\t# branch.oid $HUF\n+\t\t# branch.head master\n+\t\t# branch.upstream origin/master\n+\t\t# branch.ab +1 -0\n+\t\tEOF\n+\n+\t\tgit status --ahead-behind --porcelain=v2 --branch --untracked-files=all >actual &&\n+\t\ttest_cmp expect actual\n+\t)\n+'\n+\n test_expect_success 'create and add submodule, submodule appears clean (A. S...)' '\n \tgit checkout master &&\n \tgit clone . sub_repo &&\ndiff --git a/wt-status.c b/wt-status.c\nindex 8f7fdc6..3fcab57 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -136,6 +136,7 @@ void wt_status_prepare(struct wt_status *s)\n \ts->ignored.strdup_strings = 1;\n \ts->show_branch = -1;  /* unspecified */\n \ts->show_stash = 0;\n+\ts->ahead_behind_flags = AHEAD_BEHIND_UNSPECIFIED;\n \ts->display_comment_prefix = 0;\n }\n \n@@ -1878,18 +1879,19 @@ static void wt_porcelain_print(struct wt_status *s)\n  *\n  *    <upstream> ::= the upstream branch name, when set.\n  *\n- *       <ahead> ::= integer ahead value, when upstream set\n- *                   and the commit is present (not gone).\n- *\n- *      <behind> ::= integer behind value, when upstream set\n- *                   and commit is present.\n+ *       <ahead> ::= integer ahead value or '?'.\n  *\n+ *      <behind> ::= integer behind value or '?'.\n  *\n  * The end-of-line is defined by the -z flag.\n  *\n  *                 <eol> ::= NUL when -z,\n  *                           LF when NOT -z.\n  *\n+ * When an upstream is set and present, the 'branch.ab' line will\n+ * be printed with the ahead/behind counts for the branch and the\n+ * upstream.  When AHEAD_BEHIND_QUICK is requested and the branches\n+ * are different, '?' will be substituted for the actual count.\n  */\n static void wt_porcelain_v2_print_tracking(struct wt_status *s)\n {\n@@ -1929,15 +1931,25 @@ static void wt_porcelain_v2_print_tracking(struct wt_status *s)\n \t\t/* Lookup stats on the upstream tracking branch, if set. */\n \t\tbranch = branch_get(branch_name);\n \t\tbase = NULL;\n-\t\tab_info = (stat_tracking_info(branch, &nr_ahead, &nr_behind,\n-\t\t\t\t\t      &base, AHEAD_BEHIND_FULL) >= 0);\n+\t\tab_info = stat_tracking_info(branch, &nr_ahead, &nr_behind,\n+\t\t\t\t\t     &base, s->ahead_behind_flags);\n \t\tif (base) {\n \t\t\tbase = shorten_unambiguous_ref(base, 0);\n \t\t\tfprintf(s->fp, \"# branch.upstream %s%c\", base, eol);\n \t\t\tfree((char *)base);\n \n-\t\t\tif (ab_info)\n-\t\t\t\tfprintf(s->fp, \"# branch.ab +%d -%d%c\", nr_ahead, nr_behind, eol);\n+\t\t\tif (ab_info > 0) {\n+\t\t\t\t/* different */\n+\t\t\t\tif (nr_ahead || nr_behind)\n+\t\t\t\t\tfprintf(s->fp, \"# branch.ab +%d -%d%c\",\n+\t\t\t\t\t\tnr_ahead, nr_behind, eol);\n+\t\t\t\telse\n+\t\t\t\t\tfprintf(s->fp, \"# branch.ab +? -?%c\",\n+\t\t\t\t\t\teol);\n+\t\t\t} else if (!ab_info) {\n+\t\t\t\t/* same */\n+\t\t\t\tfprintf(s->fp, \"# branch.ab +0 -0%c\", eol);\n+\t\t\t}\n \t\t}\n \t}\n \ndiff --git a/wt-status.h b/wt-status.h\nindex 64f4d33..b450b53 100644\n--- a/wt-status.h\n+++ b/wt-status.h\n@@ -5,6 +5,7 @@\n #include \"string-list.h\"\n #include \"color.h\"\n #include \"pathspec.h\"\n+#include \"remote.h\"\n \n struct worktree;\n \n@@ -80,6 +81,7 @@ struct wt_status {\n \tint show_branch;\n \tint show_stash;\n \tint hints;\n+\tenum ahead_behind_flags ahead_behind_flags;\n \n \tenum wt_status_format status_format;\n \tunsigned char sha1_commit[GIT_MAX_RAWSZ]; /* when not Initial */\n-- \n2.9.3\n\n"},{"id":"336290","messageId":"20180109185018.69164-5-git@jeffhostetler.com","threadId":"47579","inReplyTo":"20180109185018.69164-1-git@jeffhostetler.com","subject":"[PATCH v5 4/4] status: support --no-ahead-behind in long format","fromName":"Jeff Hostetler","fromEmail":"git@jeffhostetler.com","sentAt":"2018-01-09T18:50:18Z","receivedAt":"2018-01-09T18:50:46Z","isPatch":true,"sender":{"key":"git@jeffhostetler.com","avatar":null},"body":"From: Jeff Hostetler <jeffhost@microsoft.com>\n\nTeach long (normal) status format to respect the --no-ahead-behind\nparameter and skip the possibly expensive ahead/behind computation\nbetween the branch and the upstream.\n\nSigned-off-by: Jeff Hostetler <jeffhost@microsoft.com>\n---\n builtin/checkout.c       |  2 +-\n remote.c                 | 18 +++++++++++++-----\n remote.h                 |  3 ++-\n t/t6040-tracking-info.sh | 29 +++++++++++++++++++++++++++++\n wt-status.c              |  2 +-\n 5 files changed, 46 insertions(+), 8 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex fc4f8fd..655dac2 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -605,7 +605,7 @@ static void report_tracking(struct branch_info *new)\n \tstruct strbuf sb = STRBUF_INIT;\n \tstruct branch *branch = branch_get(new->name);\n \n-\tif (!format_tracking_info(branch, &sb))\n+\tif (!format_tracking_info(branch, &sb, AHEAD_BEHIND_FULL))\n \t\treturn;\n \tfputs(sb.buf, stdout);\n \tstrbuf_release(&sb);\ndiff --git a/remote.c b/remote.c\nindex 2486afb..e668091 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -2066,15 +2066,16 @@ int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs,\n /*\n  * Return true when there is anything to report, otherwise false.\n  */\n-int format_tracking_info(struct branch *branch, struct strbuf *sb)\n+int format_tracking_info(struct branch *branch, struct strbuf *sb,\n+\t\t\t enum ahead_behind_flags abf)\n {\n-\tint ours, theirs;\n+\tint ours, theirs, sti;\n \tconst char *full_base;\n \tchar *base;\n \tint upstream_is_gone = 0;\n \n-\tif (stat_tracking_info(branch, &ours, &theirs, &full_base,\n-\t\t\t       AHEAD_BEHIND_FULL) < 0) {\n+\tsti = stat_tracking_info(branch, &ours, &theirs, &full_base, abf);\n+\tif (sti < 0) {\n \t\tif (!full_base)\n \t\t\treturn 0;\n \t\tupstream_is_gone = 1;\n@@ -2088,10 +2089,17 @@ int format_tracking_info(struct branch *branch, struct strbuf *sb)\n \t\tif (advice_status_hints)\n \t\t\tstrbuf_addstr(sb,\n \t\t\t\t_(\"  (use \\\"git branch --unset-upstream\\\" to fixup)\\n\"));\n-\t} else if (!ours && !theirs) {\n+\t} else if (!sti) {\n \t\tstrbuf_addf(sb,\n \t\t\t_(\"Your branch is up to date with '%s'.\\n\"),\n \t\t\tbase);\n+\t} else if (abf == AHEAD_BEHIND_QUICK) {\n+\t\tstrbuf_addf(sb,\n+\t\t\t    _(\"Your branch and '%s' refer to different commits.\\n\"),\n+\t\t\t    base);\n+\t\tif (advice_status_hints)\n+\t\t\tstrbuf_addf(sb, _(\"  (use \\\"%s\\\" for details)\\n\"),\n+\t\t\t\t    \"git status --ahead-behind\");\n \t} else if (!theirs) {\n \t\tstrbuf_addf(sb,\n \t\t\tQ_(\"Your branch is ahead of '%s' by %d commit.\\n\",\ndiff --git a/remote.h b/remote.h\nindex 27feb63..b2fa5cc 100644\n--- a/remote.h\n+++ b/remote.h\n@@ -265,7 +265,8 @@ enum ahead_behind_flags {\n /* Reporting of tracking info */\n int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs,\n \t\t       const char **upstream_name, enum ahead_behind_flags abf);\n-int format_tracking_info(struct branch *branch, struct strbuf *sb);\n+int format_tracking_info(struct branch *branch, struct strbuf *sb,\n+\t\t\t enum ahead_behind_flags abf);\n \n struct ref *get_local_heads(void);\n /*\ndiff --git a/t/t6040-tracking-info.sh b/t/t6040-tracking-info.sh\nindex 0190220..716283b 100755\n--- a/t/t6040-tracking-info.sh\n+++ b/t/t6040-tracking-info.sh\n@@ -160,6 +160,35 @@ test_expect_success 'status -s -b --no-ahead-behind (diverged from upstream)' '\n '\n \n cat >expect <<\\EOF\n+On branch b1\n+Your branch and 'origin/master' have diverged,\n+and have 1 and 1 different commits each, respectively.\n+EOF\n+\n+test_expect_success 'status --long --branch' '\n+\t(\n+\t\tcd test &&\n+\t\tgit checkout b1 >/dev/null &&\n+\t\tgit status --long -b | head -3\n+\t) >actual &&\n+\ttest_i18ncmp expect actual\n+'\n+\n+cat >expect <<\\EOF\n+On branch b1\n+Your branch and 'origin/master' refer to different commits.\n+EOF\n+\n+test_expect_success 'status --long --branch --no-ahead-behind' '\n+\t(\n+\t\tcd test &&\n+\t\tgit checkout b1 >/dev/null &&\n+\t\tgit status --long -b --no-ahead-behind | head -2\n+\t) >actual &&\n+\ttest_i18ncmp expect actual\n+'\n+\n+cat >expect <<\\EOF\n ## b5...brokenbase [gone]\n EOF\n \ndiff --git a/wt-status.c b/wt-status.c\nindex a4d3470..98d0501 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -1006,7 +1006,7 @@ static void wt_longstatus_print_tracking(struct wt_status *s)\n \tif (!skip_prefix(s->branch, \"refs/heads/\", &branch_name))\n \t\treturn;\n \tbranch = branch_get(branch_name);\n-\tif (!format_tracking_info(branch, &sb))\n+\tif (!format_tracking_info(branch, &sb, s->ahead_behind_flags))\n \t\treturn;\n \n \ti = 0;\n-- \n2.9.3\n\n"}]}