{"thread":{"id":"47534","subject":"[PATCH v3 0/5] Add --no-ahead-behind to status","startedAt":"2018-01-03T21:47:50Z","lastAt":"2018-01-08T14:22:39Z","messageCount":14,"participants":["Jeff Hostetler","Junio C Hamano","Jeff King"],"isPatch":true,"patchVersion":3,"patchTotal":5},"messages":[{"id":"335788","messageId":"20180103214733.797-1-git@jeffhostetler.com","threadId":"47534","inReplyTo":null,"subject":"[PATCH v3 0/5] Add --no-ahead-behind to status","fromName":"Jeff Hostetler","fromEmail":"git@jeffhostetler.com","sentAt":"2018-01-03T21:47:28Z","receivedAt":"2018-01-03T21:47:50Z","isPatch":true,"sender":{"key":"git@jeffhostetler.com","avatar":null},"body":"From: Jeff Hostetler <jeffhost@microsoft.com>\n\nThis is version 3 of my patch series to avoid expensive\nahead/behind calculations in status.  This version tries\nto address most of the comments in V2.\n\nI've switched back to a \"status.aheadBehind\" config setting\nrather than in \"core.*\".  This has been better integrated\nwith the existing status_deferred_config mechanism in\nbuiltin/commit.c and lets both status and commit inherit it.\n\nConfig values of true and false control non-porcelain formats\nfor compatibility reasons as previously discussed.  In the\nlast commit I added a new value of 2 for the config setting\nto allow porcelain formats to inherit the new setting.  I've\nmarked this experimental for now or so that we can discuss\nit.\n\nJeff Hostetler (5):\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  status: add status.aheadBehind value for porcelain output\n\n Documentation/config.txt     | 11 ++++++\n Documentation/git-status.txt |  5 +++\n builtin/checkout.c           |  2 +-\n builtin/commit.c             | 37 +++++++++++++++++++-\n ref-filter.c                 |  8 ++---\n remote.c                     | 42 +++++++++++++++++------\n remote.h                     | 20 +++++++++--\n t/t6040-tracking-info.sh     | 82 ++++++++++++++++++++++++++++++++++++++++++++\n t/t7064-wtstatus-pv2.sh      | 73 +++++++++++++++++++++++++++++++++++++++\n wt-status.c                  | 38 +++++++++++++++-----\n wt-status.h                  |  2 ++\n 11 files changed, 292 insertions(+), 28 deletions(-)\n\n-- \n2.9.3\n\n"},{"id":"335789","messageId":"20180103214733.797-4-git@jeffhostetler.com","threadId":"47534","inReplyTo":"20180103214733.797-1-git@jeffhostetler.com","subject":"[PATCH v3 3/5] status: update short status to respect --no-ahead-behind","fromName":"Jeff Hostetler","fromEmail":"git@jeffhostetler.com","sentAt":"2018-01-03T21:47:31Z","receivedAt":"2018-01-03T21:47:52Z","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\nShort status also respect the \"status.aheadBehind\" config setting.\n\nSigned-off-by: Jeff Hostetler <jeffhost@microsoft.com>\n---\n t/t6040-tracking-info.sh | 26 ++++++++++++++++++++++++++\n wt-status.c              | 11 +++++++----\n 2 files changed, 33 insertions(+), 4 deletions(-)\n\ndiff --git a/t/t6040-tracking-info.sh b/t/t6040-tracking-info.sh\nindex 8f17fd9..053dff3 100755\n--- a/t/t6040-tracking-info.sh\n+++ b/t/t6040-tracking-info.sh\n@@ -147,6 +147,32 @@ 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+## b1...origin/master [different]\n+EOF\n+\n+test_expect_success 'status.aheadbehind=false status -s -b (diverged from upstream)' '\n+\t(\n+\t\tcd test &&\n+\t\tgit checkout b1 >/dev/null &&\n+\t\tgit -c status.aheadbehind=false status -s -b | 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 3959d31..df6cc33 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":"335790","messageId":"20180103214733.797-2-git@jeffhostetler.com","threadId":"47534","inReplyTo":"20180103214733.797-1-git@jeffhostetler.com","subject":"[PATCH v3 1/5] stat_tracking_info: return +1 when branches not equal","fromName":"Jeff Hostetler","fromEmail":"git@jeffhostetler.com","sentAt":"2018-01-03T21:47:29Z","receivedAt":"2018-01-03T21:47:54Z","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     | 26 ++++++++++++++++++--------\n remote.h     |  8 +++++++-\n wt-status.c  |  6 ++++--\n 4 files changed, 33 insertions(+), 15 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..ca5a416 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+ * left undefined).\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@@ -2019,6 +2026,8 @@ int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs,\n \t\t*num_theirs = *num_ours = 0;\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@@ -2051,7 +2060,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 +2073,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":"335791","messageId":"20180103214733.797-6-git@jeffhostetler.com","threadId":"47534","inReplyTo":"20180103214733.797-1-git@jeffhostetler.com","subject":"[PATCH v3 5/5] status: add status.aheadBehind value for porcelain output","fromName":"Jeff Hostetler","fromEmail":"git@jeffhostetler.com","sentAt":"2018-01-03T21:47:33Z","receivedAt":"2018-01-03T21:47:56Z","isPatch":true,"sender":{"key":"git@jeffhostetler.com","avatar":null},"body":"From: Jeff Hostetler <jeffhost@microsoft.com>\n\nAdd status.aheadBehind=2 value to enable --no-ahead-behind\nfor all formats (both porcelain and non-porcelain).  The\ncurrent boolean values only affect non-porcelain formats.\n\nSigned-off-by: Jeff Hostetler <jeffhost@microsoft.com>\n---\n Documentation/config.txt |  5 +++++\n builtin/commit.c         | 31 +++++++++++++++++++++++++------\n remote.h                 |  8 ++++++++\n t/t6040-tracking-info.sh |  9 +++++++++\n t/t7064-wtstatus-pv2.sh  |  6 +++++-\n 5 files changed, 52 insertions(+), 7 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex affb0d6..eaa1058 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -3040,6 +3040,11 @@ status.aheadBehind::\n \t--no-ahead-behind by default in linkgit:git-status[1] for\n \tnon-porcelain formats.  This setting is ignored by porcelain\n \tformats for backwards compatibility.\n++\n+(EXPERIMENTAL) Set to 2 to allow both porcelain and non-porcelain\n+formats to inherit --no-ahead-behind.  This may break backward\n+compatibility for scripts using porcelain status formats and expecting\n+ahead/behind information in the output.\n \n status.displayCommentPrefix::\n \tIf set to true, linkgit:git-status[1] will insert a comment\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex 416fe2c..194a6eb 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -1109,13 +1109,32 @@ static const char *read_commit_message(const char *name)\n static struct status_deferred_config {\n \tenum wt_status_format status_format;\n \tint show_branch;\n-\tenum ahead_behind_flags ahead_behind;\n+\tenum ahead_behind_config_flags ahead_behind_config;\n } status_deferred_config = {\n \tSTATUS_FORMAT_UNSPECIFIED,\n \t-1, /* unspecified */\n \tAHEAD_BEHIND_UNSPECIFIED,\n };\n \n+static inline enum ahead_behind_flags inherit_deferred_ab_flags(\n+\tint is_porcelain)\n+{\n+\tswitch (status_deferred_config.ahead_behind_config) {\n+\tcase AHEAD_BEHIND_CONFIG_UNSPECIFIED:\n+\tcase AHEAD_BEHIND_CONFIG_FULL:\n+\t\treturn AHEAD_BEHIND_FULL;\n+\n+\tcase AHEAD_BEHIND_CONFIG_QUICK2:\n+\t\treturn AHEAD_BEHIND_QUICK;\n+\n+\tcase AHEAD_BEHIND_CONFIG_QUICK:\n+\t\treturn is_porcelain ? AHEAD_BEHIND_FULL : AHEAD_BEHIND_QUICK;\n+\n+\tdefault: /* don't complain about bogus config settings */\n+\t\treturn AHEAD_BEHIND_FULL;\n+\t}\n+}\n+\n static void finalize_deferred_config(struct wt_status *s)\n {\n \tint use_deferred_config = (status_format != STATUS_FORMAT_PORCELAIN &&\n@@ -1140,11 +1159,9 @@ static void finalize_deferred_config(struct wt_status *s)\n \tif (s->show_branch < 0)\n \t\ts->show_branch = 0;\n \n-\tif (use_deferred_config &&\n-\t    s->ahead_behind_flags == AHEAD_BEHIND_UNSPECIFIED)\n-\t\ts->ahead_behind_flags = status_deferred_config.ahead_behind;\n \tif (s->ahead_behind_flags == AHEAD_BEHIND_UNSPECIFIED)\n-\t\ts->ahead_behind_flags = AHEAD_BEHIND_FULL;\n+\t\ts->ahead_behind_flags =\n+\t\t\tinherit_deferred_ab_flags(!use_deferred_config);\n }\n \n static int parse_and_validate_options(int argc, const char *argv[],\n@@ -1306,7 +1323,9 @@ static int git_status_config(const char *k, const char *v, void *cb)\n \t\treturn 0;\n \t}\n \tif (!strcmp(k, \"status.aheadbehind\")) {\n-\t\tstatus_deferred_config.ahead_behind = git_config_bool(k, v);\n+\t\tint is_bool;\n+\t\tstatus_deferred_config.ahead_behind_config =\n+\t\t\tgit_config_bool_or_int(k, v, &is_bool);\n \t\treturn 0;\n \t}\n \tif (!strcmp(k, \"status.showstash\")) {\ndiff --git a/remote.h b/remote.h\nindex b2fa5cc..bcf846a 100644\n--- a/remote.h\n+++ b/remote.h\n@@ -262,6 +262,14 @@ enum ahead_behind_flags {\n \tAHEAD_BEHIND_FULL        =  1,  /* traditional a/b reporting */\n };\n \n+/* Flags for status.aheadBehind values. */\n+enum ahead_behind_config_flags {\n+\tAHEAD_BEHIND_CONFIG_UNSPECIFIED = -1,\n+\tAHEAD_BEHIND_CONFIG_QUICK       =  0, /* eq/neq for non-porcelain only */\n+\tAHEAD_BEHIND_CONFIG_FULL        =  1, /* a/b reporting for all formats */\n+\tAHEAD_BEHIND_CONFIG_QUICK2      =  2, /* eq/neq for all formats */\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, enum ahead_behind_flags abf);\ndiff --git a/t/t6040-tracking-info.sh b/t/t6040-tracking-info.sh\nindex febf63f..5003366 100755\n--- a/t/t6040-tracking-info.sh\n+++ b/t/t6040-tracking-info.sh\n@@ -219,6 +219,15 @@ test_expect_success 'status.aheadbehind=false status --long --branch' '\n \ttest_i18ncmp expect actual\n '\n \n+test_expect_success 'status.aheadbehind=2 status --long --branch' '\n+\t(\n+\t\tcd test &&\n+\t\tgit checkout b1 >/dev/null &&\n+\t\tgit -c status.aheadbehind=false status --long -b | head -2\n+\t) >actual &&\n+\ttest_i18ncmp expect actual\n+'\n+\n cat >expect <<\\EOF\n ## b5...brokenbase [gone]\n EOF\ndiff --git a/t/t7064-wtstatus-pv2.sh b/t/t7064-wtstatus-pv2.sh\nindex 67b90cd..7171a43 100755\n--- a/t/t7064-wtstatus-pv2.sh\n+++ b/t/t7064-wtstatus-pv2.sh\n@@ -410,6 +410,10 @@ test_expect_success 'verify --[no-]ahead-behind with V2 format' '\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 that porcelain=v2 format inherits status.aheadBehind value when _QUICK2.\n+\t\tgit -c status.aheadbehind=2 status --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@@ -421,7 +425,7 @@ test_expect_success 'verify --[no-]ahead-behind with V2 format' '\n \t\tgit status --ahead-behind --porcelain=v2 --branch --untracked-files=all >actual &&\n \t\ttest_cmp expect actual &&\n \n-\t\t# Confirm that porcelain=v2 format does not inherit status.aheadBehind value.\n+\t\t# Confirm that porcelain=v2 format does not inherit status.aheadBehind value when _FULL/_QUICK.\n \t\tgit -c status.aheadbehind=false status --porcelain=v2 --branch --untracked-files=all >actual &&\n \t\ttest_cmp expect actual &&\n \n-- \n2.9.3\n\n"},{"id":"335792","messageId":"20180103214733.797-3-git@jeffhostetler.com","threadId":"47534","inReplyTo":"20180103214733.797-1-git@jeffhostetler.com","subject":"[PATCH v3 2/5] status: add --[no-]ahead-behind to status and commit for V2 format.","fromName":"Jeff Hostetler","fromEmail":"git@jeffhostetler.com","sentAt":"2018-01-03T21:47:30Z","receivedAt":"2018-01-03T21:48:00Z","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 equal eq|neq\" 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\nAdded \"status.aheadBehind\" config setting.  This is only used by\nnon-porcelain format for backward-compatibility.\n\nSigned-off-by: Jeff Hostetler <jeffhost@microsoft.com>\n---\n Documentation/config.txt     |  6 ++++\n Documentation/git-status.txt |  5 ++++\n builtin/commit.c             | 18 +++++++++++-\n remote.c                     |  2 ++\n remote.h                     |  5 ++--\n t/t7064-wtstatus-pv2.sh      | 69 ++++++++++++++++++++++++++++++++++++++++++++\n wt-status.c                  | 27 +++++++++++++----\n wt-status.h                  |  2 ++\n 8 files changed, 125 insertions(+), 9 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 9593bfa..affb0d6 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -3035,6 +3035,12 @@ status.branch::\n \tSet to true to enable --branch by default in linkgit:git-status[1].\n \tThe option --no-branch takes precedence over this variable.\n \n+status.aheadBehind::\n+\tSet to true to enable --ahead-behind and to false to enable\n+\t--no-ahead-behind by default in linkgit:git-status[1] for\n+\tnon-porcelain formats.  This setting is ignored by porcelain\n+\tformats for backwards compatibility.\n+\n status.displayCommentPrefix::\n \tIf set to true, linkgit:git-status[1] will insert a comment\n \tprefix before each output line (starting with\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..416fe2c 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -1109,9 +1109,11 @@ static const char *read_commit_message(const char *name)\n static struct status_deferred_config {\n \tenum wt_status_format status_format;\n \tint show_branch;\n+\tenum ahead_behind_flags ahead_behind;\n } status_deferred_config = {\n \tSTATUS_FORMAT_UNSPECIFIED,\n-\t-1 /* unspecified */\n+\t-1, /* unspecified */\n+\tAHEAD_BEHIND_UNSPECIFIED,\n };\n \n static void finalize_deferred_config(struct wt_status *s)\n@@ -1137,6 +1139,12 @@ 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 (use_deferred_config &&\n+\t    s->ahead_behind_flags == AHEAD_BEHIND_UNSPECIFIED)\n+\t\ts->ahead_behind_flags = status_deferred_config.ahead_behind;\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@@ -1297,6 +1305,10 @@ static int git_status_config(const char *k, const char *v, void *cb)\n \t\tstatus_deferred_config.show_branch = git_config_bool(k, v);\n \t\treturn 0;\n \t}\n+\tif (!strcmp(k, \"status.aheadbehind\")) {\n+\t\tstatus_deferred_config.ahead_behind = git_config_bool(k, v);\n+\t\treturn 0;\n+\t}\n \tif (!strcmp(k, \"status.showstash\")) {\n \t\ts->show_stash = git_config_bool(k, v);\n \t\treturn 0;\n@@ -1351,6 +1363,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 +1642,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 ca5a416..32706bc 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}\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..67b90cd 100755\n--- a/t/t7064-wtstatus-pv2.sh\n+++ b/t/t7064-wtstatus-pv2.sh\n@@ -390,6 +390,75 @@ 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 branch.equal with \"eq\".\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.equal eq\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# Confirm that porcelain=v2 format does not inherit status.aheadBehind value.\n+\t\tgit -c status.aheadbehind=false status --porcelain=v2 --branch --untracked-files=all >actual &&\n+\t\ttest_cmp expect actual &&\n+\n+\t\tgit -c status.aheadbehind=true status --porcelain=v2 --branch --untracked-files=all >actual &&\n+\t\ttest_cmp expect actual &&\n+\n+\t\t## Test 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.equal with \"neq\".\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.equal neq\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..3959d31 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@@ -1866,7 +1867,8 @@ static void wt_porcelain_print(struct wt_status *s)\n  *    # branch.oid <commit><eol>\n  *    # branch.head <head><eol>\n  *   [# branch.upstream <upstream><eol>\n- *   [# branch.ab +<ahead> -<behind><eol>]]\n+ *   [# branch.ab +<ahead> -<behind><eol>]\n+ *   [# branch.equal <equal><eol>]]\n  *\n  *      <commit> ::= the current commit hash or the the literal\n  *                   \"(initial)\" to indicate an initialized repo\n@@ -1884,12 +1886,16 @@ static void wt_porcelain_print(struct wt_status *s)\n  *      <behind> ::= integer behind value, when upstream set\n  *                   and commit is present.\n  *\n+ *       <equal> ::= literal string \"eq\" or \"neq\".\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 when the ahead_behind_flags are set to _FULL and\n+ * the 'branch.equal' line will be printed when set to _QUICK.\n  */\n static void wt_porcelain_v2_print_tracking(struct wt_status *s)\n {\n@@ -1897,7 +1903,7 @@ static void wt_porcelain_v2_print_tracking(struct wt_status *s)\n \tconst char *base;\n \tconst char *branch_name;\n \tstruct wt_status_state state;\n-\tint ab_info, nr_ahead, nr_behind;\n+\tint sti, nr_ahead, nr_behind;\n \tchar eol = s->null_termination ? '\\0' : '\\n';\n \n \tmemset(&state, 0, sizeof(state));\n@@ -1929,15 +1935,24 @@ 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\tsti = 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 (sti >= 0) {\n+\t\t\t\tif (s->ahead_behind_flags == AHEAD_BEHIND_FULL)\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 if (s->ahead_behind_flags == AHEAD_BEHIND_QUICK)\n+\t\t\t\t\tfprintf(s->fp, \"# branch.equal %s%c\",\n+\t\t\t\t\t\tsti ? \"neq\" : \"eq\", eol);\n+\t\t\t\telse\n+\t\t\t\t\tBUG(\"invalid ahead_behind_flag '%d'\",\n+\t\t\t\t\t    s->ahead_behind_flags);\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":"335793","messageId":"20180103214733.797-5-git@jeffhostetler.com","threadId":"47534","inReplyTo":"20180103214733.797-1-git@jeffhostetler.com","subject":"[PATCH v3 4/5] status: support --no-ahead-behind in long format","fromName":"Jeff Hostetler","fromEmail":"git@jeffhostetler.com","sentAt":"2018-01-03T21:47:32Z","receivedAt":"2018-01-03T21:48:04Z","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\nLong status also respects \"status.aheadBehind\" config setting.\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 | 47 +++++++++++++++++++++++++++++++++++++++++++++++\n wt-status.c              |  2 +-\n 5 files changed, 64 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 32706bc..bbbe3e5 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -2068,15 +2068,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@@ -2090,10 +2091,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 053dff3..febf63f 100755\n--- a/t/t6040-tracking-info.sh\n+++ b/t/t6040-tracking-info.sh\n@@ -173,6 +173,53 @@ test_expect_success 'status.aheadbehind=false status -s -b (diverged from upstre\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+test_expect_success 'status --long --branch' '\n+\t(\n+\t\tcd test &&\n+\t\tgit checkout b1 >/dev/null &&\n+\t\tgit -c status.aheadbehind=true 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+test_expect_success 'status.aheadbehind=false status --long --branch' '\n+\t(\n+\t\tcd test &&\n+\t\tgit checkout b1 >/dev/null &&\n+\t\tgit -c status.aheadbehind=false status --long -b | 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 df6cc33..0a40b85 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"},{"id":"335870","messageId":"xmqqr2r5e1yh.fsf@gitster.mtv.corp.google.com","threadId":"47534","inReplyTo":"20180103214733.797-2-git@jeffhostetler.com","subject":"Re: [PATCH v3 1/5] stat_tracking_info: return +1 when branches not equal","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-01-04T21:41:42Z","receivedAt":"2018-01-04T21:41:49Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff Hostetler <git@jeffhostetler.com> writes:\n\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> ...\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\nMental note: any code that reacted to stat_tracking_info() returning\nnon-zero was reacting to \"no useful info in num_{ours,theirs}\".\nThey now have to compare the returned value with \"< 0\" for the same\npurpose.\n\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> @@ -2019,6 +2026,8 @@ int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs,\n>  \t\t*num_theirs = *num_ours = 0;\n>  \t\treturn 0;\n>  \t}\n> +\tif (abf == AHEAD_BEHIND_QUICK)\n> +\t\treturn 1;\n> ...\n>  \targv_array_clear(&argv);\n> -\treturn 0;\n> +\treturn 1;\n>  }\n\nWhen a caller gets +1 from the function, it is not safe to peek into\n*num_{ours,theirs} if it passed _QUICK.\n\n> @@ -2064,7 +2073,8 @@ int format_tracking_info(struct branch *branch, struct strbuf *sb)\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\nSane conversion to the new return value convention.\n\n> diff --git a/wt-status.c b/wt-status.c\n> index 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\nDitto.\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\nIf a later step plans to (conditionally) allow _QUICK to be passed\nhere, this conversion is questionable, because ab_info being true\nno longer is a sign that nr_{ahead,behind} are valid.\n\nI suspect that moving the \"s/ab_info/sti/\" bits around here from\nstep [2/5] to this commit may make the result after this patch more\nconsistent, but it is not a big deal either way.\n\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\n"},{"id":"335872","messageId":"xmqqlghde0uy.fsf@gitster.mtv.corp.google.com","threadId":"47534","inReplyTo":"20180103214733.797-3-git@jeffhostetler.com","subject":"Re: [PATCH v3 2/5] status: add --[no-]ahead-behind to status and commit for V2 format.","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-01-04T22:05:25Z","receivedAt":"2018-01-04T22:05:33Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff Hostetler <git@jeffhostetler.com> writes:\n\n> +\t\tsti = 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 (sti >= 0) {\n> +\t\t\t\tif (s->ahead_behind_flags == AHEAD_BEHIND_FULL)\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 if (s->ahead_behind_flags == AHEAD_BEHIND_QUICK)\n> +\t\t\t\t\tfprintf(s->fp, \"# branch.equal %s%c\",\n> +\t\t\t\t\t\tsti ? \"neq\" : \"eq\", eol);\n\nThis is related to what I said in the review of [1/5], but this\narrangement to require the caller to know that _QUICK request\nresults in incomplete information smells like a bit of maintenance\nhassle.\n\nWe'd rather allow the caller to tell if it was given incomplete\ninformation from the values returned by stat_tracking_info()\nfunction (notice the plural \"values\" here; what is returned in\nnr_{ahead,behind} also counts).  For example, we can keep the (-1 =>\n\"no relation\", 0 => \"identical\", 1 => \"different\") as return values\nof the function, but have it clear nr_{ahead,behind} when it only\nknows the two are different but not by how many commits.  That way,\nthis step can instead do:\n\n\tab_info = stat_tracking_info(...);\n\tif (base) {\n\t\t... do the base thing ...\n\t\tif (ab_info > 0) {\n\t\t\t/* different */\n\t\t\tif (nr_ahead || nr_behind)\n\t\t\t\tfprintf(... +%d -%d ... nr_ahead, nr_behind, ...);\n\t\t\telse\n\t\t\t\tfprintf(... \"neq\" ...);\n\t\t} else if (!ab_info) {\n\t\t\t/* same */\n\t\t\tfprintf(... +%d -%d ... nr_ahead, nr_behind, ...);\n\t\t}\n\t\t...\n\nThat would allow us to later add different kinds of _QUICK that do\nnot give full information without having to update this consumer\nwith something like\n\n-\telse if (s->ahead_behind_flags == AHEAD_BEHIND_QUICK)\n+\telse if (s->ahead_behind_flags == AHEAD_BEHIND_QUICK ||\n+\t\t s->ahead_behind_flags == AHEAD_BEHIND_QUICK_DIFFERENTLY)\n\nwhen it happens.\n\nTwo related tangents.  \n\n - I do not see why \"eq\" need to exist at all.  Even in _QUICK mode,\n   when you determine that two are identical, you know your branch\n   is ahead and behind by 0 commit each.\n\n - A short-format that is easy to parse by machines does not have to\n   be cryptic to humans.  s/neq/not-equal/ or something may be an\n   improvement.\n\nThanks.\n"},{"id":"335892","messageId":"20180104230630.GA2599@sigill.intra.peff.net","threadId":"47534","inReplyTo":"20180103214733.797-1-git@jeffhostetler.com","subject":"Re: [PATCH v3 0/5] Add --no-ahead-behind to status","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2018-01-04T23:06:31Z","receivedAt":"2018-01-04T23:06:53Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Wed, Jan 03, 2018 at 09:47:28PM +0000, Jeff Hostetler wrote:\n\n> Config values of true and false control non-porcelain formats\n> for compatibility reasons as previously discussed.  In the\n> last commit I added a new value of 2 for the config setting\n> to allow porcelain formats to inherit the new setting.  I've\n> marked this experimental for now or so that we can discuss\n> it.\n\nI'm mildly negative on this \"level 2\" config. If influencing the\nporcelain via config creates compatibility headaches, then why would we\nallow it here? And if it doesn't, then why do we need to protect against\nit? This seems to exist in a funny middle ground that cannot decide\nwhether it is bad or not.\n\nIt's like we're inserting a foot-gun, but putting it just far enough out\nof reach that we can blame the user when they shoot themselves with it.\n\nIs there a compelling use case for this? From the previous discussion,\nthis is the strawman I came up with:\n\n  Scripted callers like Visual Studio don't want to unconditionally pass\n  --no-ahead-behind, because it makes sense only for large repositories\n  (and small ones would prefer the more exact answer, if we can get it\n  cheaply). So we'd like the user to trigger \"this is large\" on a\n  per-repo basis, and accept the consequences of possibly broken\n  porcelain callers.\n\nI think we could have the best of both worlds, though, if the existing\nconfig option were coupled with a command-line option to say \"yes, I\nunderstand no-ahead-behind, so use it for the porcelain if applicable\".\nIOW, the user does:\n\n  git config status.aheadbehind false\n\nand VS does:\n\n  git status --ahead-behind=maybe\n\nand together both sides have assented to the \"quick\" thing.\n\n-Peff\n"},{"id":"335931","messageId":"6fe41395-6cb5-cbfe-88cd-5b0b21fad7b8@jeffhostetler.com","threadId":"47534","inReplyTo":"xmqqlghde0uy.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v3 2/5] status: add --[no-]ahead-behind to status and commit for V2 format.","fromName":"Jeff Hostetler","fromEmail":"git@jeffhostetler.com","sentAt":"2018-01-05T16:31:08Z","receivedAt":"2018-01-05T16:31:15Z","isPatch":true,"sender":{"key":"git@jeffhostetler.com","avatar":null},"body":"\n\nOn 1/4/2018 5:05 PM, Junio C Hamano wrote:\n> Jeff Hostetler <git@jeffhostetler.com> writes:\n> \n>> +\t\tsti = 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 (sti >= 0) {\n>> +\t\t\t\tif (s->ahead_behind_flags == AHEAD_BEHIND_FULL)\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 if (s->ahead_behind_flags == AHEAD_BEHIND_QUICK)\n>> +\t\t\t\t\tfprintf(s->fp, \"# branch.equal %s%c\",\n>> +\t\t\t\t\t\tsti ? \"neq\" : \"eq\", eol);\n> \n> This is related to what I said in the review of [1/5], but this\n> arrangement to require the caller to know that _QUICK request\n> results in incomplete information smells like a bit of maintenance\n> hassle.\n> \n> We'd rather allow the caller to tell if it was given incomplete\n> information from the values returned by stat_tracking_info()\n> function (notice the plural \"values\" here; what is returned in\n> nr_{ahead,behind} also counts).  For example, we can keep the (-1 =>\n> \"no relation\", 0 => \"identical\", 1 => \"different\") as return values\n> of the function, but have it clear nr_{ahead,behind} when it only\n> knows the two are different but not by how many commits.  That way,\n> this step can instead do:\n> \n> \tab_info = stat_tracking_info(...);\n> \tif (base) {\n> \t\t... do the base thing ...\n> \t\tif (ab_info > 0) {\n> \t\t\t/* different */\n> \t\t\tif (nr_ahead || nr_behind)\n> \t\t\t\tfprintf(... +%d -%d ... nr_ahead, nr_behind, ...);\n> \t\t\telse\n> \t\t\t\tfprintf(... \"neq\" ...);\n> \t\t} else if (!ab_info) {\n> \t\t\t/* same */\n> \t\t\tfprintf(... +%d -%d ... nr_ahead, nr_behind, ...);\n> \t\t}\n> \t\t...\n> \n> That would allow us to later add different kinds of _QUICK that do\n> not give full information without having to update this consumer\n> with something like\n> \n> -\telse if (s->ahead_behind_flags == AHEAD_BEHIND_QUICK)\n> +\telse if (s->ahead_behind_flags == AHEAD_BEHIND_QUICK ||\n> +\t\t s->ahead_behind_flags == AHEAD_BEHIND_QUICK_DIFFERENTLY)\n> \n> when it happens.\n> \n> Two related tangents.\n> \n>   - I do not see why \"eq\" need to exist at all.  Even in _QUICK mode,\n>     when you determine that two are identical, you know your branch\n>     is ahead and behind by 0 commit each.\n> \n>   - A short-format that is easy to parse by machines does not have to\n>     be cryptic to humans.  s/neq/not-equal/ or something may be an\n>     improvement.\n> \n> Thanks.\n> \n\nThanks for the review.  Let me update it along the lines suggested here.\nIt felt odd to define the nr_{ahead,behind} as undefined, but short of\nmaking them negative or something I wasn't sure what was best.\n\nThanks,\nJeff\n\n"},{"id":"335933","messageId":"2887ad5a-5de9-3c5b-92c3-40b19120e604@jeffhostetler.com","threadId":"47534","inReplyTo":"20180104230630.GA2599@sigill.intra.peff.net","subject":"Re: [PATCH v3 0/5] Add --no-ahead-behind to status","fromName":"Jeff Hostetler","fromEmail":"git@jeffhostetler.com","sentAt":"2018-01-05T16:46:24Z","receivedAt":"2018-01-05T16:46:29Z","isPatch":true,"sender":{"key":"git@jeffhostetler.com","avatar":null},"body":"\n\nOn 1/4/2018 6:06 PM, Jeff King wrote:\n> On Wed, Jan 03, 2018 at 09:47:28PM +0000, Jeff Hostetler wrote:\n> \n>> Config values of true and false control non-porcelain formats\n>> for compatibility reasons as previously discussed.  In the\n>> last commit I added a new value of 2 for the config setting\n>> to allow porcelain formats to inherit the new setting.  I've\n>> marked this experimental for now or so that we can discuss\n>> it.\n> \n> I'm mildly negative on this \"level 2\" config. If influencing the\n> porcelain via config creates compatibility headaches, then why would we\n> allow it here? And if it doesn't, then why do we need to protect against\n> it? This seems to exist in a funny middle ground that cannot decide\n> whether it is bad or not.\n> \n> It's like we're inserting a foot-gun, but putting it just far enough out\n> of reach that we can blame the user when they shoot themselves with it.\n> \n> Is there a compelling use case for this? From the previous discussion,\n> this is the strawman I came up with:\n> \n>    Scripted callers like Visual Studio don't want to unconditionally pass\n>    --no-ahead-behind, because it makes sense only for large repositories\n>    (and small ones would prefer the more exact answer, if we can get it\n>    cheaply). So we'd like the user to trigger \"this is large\" on a\n>    per-repo basis, and accept the consequences of possibly broken\n>    porcelain callers.\n> \n> I think we could have the best of both worlds, though, if the existing\n> config option were coupled with a command-line option to say \"yes, I\n> understand no-ahead-behind, so use it for the porcelain if applicable\".\n> IOW, the user does:\n> \n>    git config status.aheadbehind false\n> \n> and VS does:\n> \n>    git status --ahead-behind=maybe\n> \n> and together both sides have assented to the \"quick\" thing.\n> \n> -Peff\n\nI kinda trying to serve 2 masters here.  As we discussed earlier, we\ndon't want config options to change porcelain formats, hence the\ntrue/false thing only affecting non-porcelain formats.  On the other\nhand, VS and git.exe are on different release schedules.  Normally,\nI'd just have VS emit a \"git status --no-ahead-behind --porcelain=v2\"\nand be done, but that requires that git.exe gets updated before VS.\nWe do control some of that, but if VS gets updated first, that causes\nan error, whereas \"git -c status.aheadbehind=<x> status --porcelain=v2\"\ndoes not.  It is respected if/when git is updated and ignored until\nthen.  Likewise, if they update git first, we can tell them to set a\nconfig setting on the repo and inherit it for porcelain v2 output\nwithout VS knowing about it.  Sorry, if that's too much detail.\n\nIt is OK with me if we omit the last commit in the patch series (that\ndoes the experimental =2 extension) and I'll deal with this separately\n(maybe differently) in the gvfs fork.\n\nThanks,\nJeff\n\n  \n"},{"id":"335949","messageId":"xmqqbmi8cc59.fsf@gitster.mtv.corp.google.com","threadId":"47534","inReplyTo":"2887ad5a-5de9-3c5b-92c3-40b19120e604@jeffhostetler.com","subject":"Re: [PATCH v3 0/5] Add --no-ahead-behind to status","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-01-05T19:56:50Z","receivedAt":"2018-01-05T19:56:57Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff Hostetler <git@jeffhostetler.com> writes:\n\n> I kinda trying to serve 2 masters here.  As we discussed earlier, we\n> don't want config options to change porcelain formats, hence the\n> true/false thing only affecting non-porcelain formats.  On the other\n> hand, VS and git.exe are on different release schedules.  Normally,\n> I'd just have VS emit a \"git status --no-ahead-behind --porcelain=v2\"\n> and be done, but that requires that git.exe gets updated before VS.\n> We do control some of that, but if VS gets updated first, that causes\n> an error, whereas \"git -c status.aheadbehind=<x> status --porcelain=v2\"\n> does not.  It is respected if/when git is updated and ignored until\n> then.  Likewise, if they update git first, we can tell them to set a\n> config setting on the repo and inherit it for porcelain v2 output\n> without VS knowing about it.  Sorry, if that's too much detail.\n\nThat is not really too much detail.  \n\nBut if the above is your plan, then boolean=2 cannot merely be \"an\nexperimental\" as described in [5/5]; it needs to be carried for some\nextended period of time, depending on the release schedule of the\nother half of the coin.\n\n> It is OK with me if we omit the last commit in the patch series (that\n> does the experimental =2 extension) and I'll deal with this separately\n> (maybe differently) in the gvfs fork.\n\nI think that sounds more sensible.  Thanks.\n"},{"id":"336122","messageId":"20180108063712.GB10933@sigill.intra.peff.net","threadId":"47534","inReplyTo":"2887ad5a-5de9-3c5b-92c3-40b19120e604@jeffhostetler.com","subject":"Re: [PATCH v3 0/5] Add --no-ahead-behind to status","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2018-01-08T06:37:12Z","receivedAt":"2018-01-08T06:37:25Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Fri, Jan 05, 2018 at 11:46:24AM -0500, Jeff Hostetler wrote:\n\n> > I'm mildly negative on this \"level 2\" config. If influencing the\n> > porcelain via config creates compatibility headaches, then why would we\n> > allow it here? And if it doesn't, then why do we need to protect against\n> > it? This seems to exist in a funny middle ground that cannot decide\n> > whether it is bad or not.\n> > \n> > It's like we're inserting a foot-gun, but putting it just far enough out\n> > of reach that we can blame the user when they shoot themselves with it.\n> > \n> > Is there a compelling use case for this? From the previous discussion,\n> > this is the strawman I came up with:\n> [...]\n> \n> I kinda trying to serve 2 masters here.  As we discussed earlier, we\n> don't want config options to change porcelain formats, hence the\n> true/false thing only affecting non-porcelain formats.  On the other\n> hand, VS and git.exe are on different release schedules.  Normally,\n> I'd just have VS emit a \"git status --no-ahead-behind --porcelain=v2\"\n> and be done, but that requires that git.exe gets updated before VS.\n> We do control some of that, but if VS gets updated first, that causes\n> an error, whereas \"git -c status.aheadbehind=<x> status --porcelain=v2\"\n> does not.  It is respected if/when git is updated and ignored until\n> then.  Likewise, if they update git first, we can tell them to set a\n> config setting on the repo and inherit it for porcelain v2 output\n> without VS knowing about it.  Sorry, if that's too much detail.\n\nOK, so my strawman was totally off-base. :)\n\nThat explanation is interesting. I do think you're facing a real problem\nwith moving the versions in lockstep. But shoe-horning the feature into\nconfig like this seems like a pretty ugly solution:\n\n  1. Then we're stuck with this weird foot-gun config option forever.\n\n  2. It only solves the problem for this one option. Is there a more\n     general solution?\n\nThe more general solutions I can come up with are:\n\n  1. Is there a way for a caller to query Git to say \"do you understand\n     --no-ahead-behind?\".\n\n     You can ask \"git version\", but parsing version numbers is\n     problematic. We don't have any kind of \"feature flags\" output, and\n     I'm not sure we'd want to get down to the level of specific\n     command-line options there.\n\n     One thing you can do is speculatively run \"status --no-ahead-behind\",\n     and if it returns 129, try again without as a fallback. That incurs\n     a failed invocation for the fallback case, but it's quick (we fail\n     before looking at any data) and you can cache it for the duration\n     of a VS session.\n\n  2. There could be some way to tell the option parser that the next\n     flag is optional. E.g.:\n\n       git status --optional=--no-ahead-behind\n\n     That would be pretty easy to implement globally in parse-options.c.\n     It knows when an option is unrecognized, so it could just treat\n     that as a silent noop rather than barfing.\n\n     Of course, that doesn't solve your problem today. It wouldn't be\n     safe to start using \"--optional\" until it had been in several\n     released versions.\n\n     And I have a feeling it may not be sufficient without further\n     feedback to the caller. For this flag, the caller is happy to say\n     \"do this if you know how, but otherwise I will cope\". But there are\n     probably flag where it would need to know whether it had any effect\n     or not. So this whole direction is probably crazy.\n\nOf the two, I think (1) is not so bad.\n\n> It is OK with me if we omit the last commit in the patch series (that\n> does the experimental =2 extension) and I'll deal with this separately\n> (maybe differently) in the gvfs fork.\n\nThat would be my preference. Thanks.\n\n-Peff\n"},{"id":"336143","messageId":"3ed06c39-2eac-e9d7-2976-6a7b55536cb0@jeffhostetler.com","threadId":"47534","inReplyTo":"20180108063712.GB10933@sigill.intra.peff.net","subject":"Re: [PATCH v3 0/5] Add --no-ahead-behind to status","fromName":"Jeff Hostetler","fromEmail":"git@jeffhostetler.com","sentAt":"2018-01-08T14:22:30Z","receivedAt":"2018-01-08T14:22:39Z","isPatch":true,"sender":{"key":"git@jeffhostetler.com","avatar":null},"body":"\n\nOn 1/8/2018 1:37 AM, Jeff King wrote:\n> On Fri, Jan 05, 2018 at 11:46:24AM -0500, Jeff Hostetler wrote:\n> \n>>> I'm mildly negative on this \"level 2\" config. If influencing the\n>>> porcelain via config creates compatibility headaches, then why would we\n>>> allow it here? And if it doesn't, then why do we need to protect against\n>>> it? This seems to exist in a funny middle ground that cannot decide\n>>> whether it is bad or not.\n>>>\n>>> It's like we're inserting a foot-gun, but putting it just far enough out\n>>> of reach that we can blame the user when they shoot themselves with it.\n>>>\n>>> Is there a compelling use case for this? From the previous discussion,\n>>> this is the strawman I came up with:\n>> [...]\n>>\n>> I kinda trying to serve 2 masters here.  As we discussed earlier, we\n>> don't want config options to change porcelain formats, hence the\n>> true/false thing only affecting non-porcelain formats.  On the other\n>> hand, VS and git.exe are on different release schedules.  Normally,\n>> I'd just have VS emit a \"git status --no-ahead-behind --porcelain=v2\"\n>> and be done, but that requires that git.exe gets updated before VS.\n>> We do control some of that, but if VS gets updated first, that causes\n>> an error, whereas \"git -c status.aheadbehind=<x> status --porcelain=v2\"\n>> does not.  It is respected if/when git is updated and ignored until\n>> then.  Likewise, if they update git first, we can tell them to set a\n>> config setting on the repo and inherit it for porcelain v2 output\n>> without VS knowing about it.  Sorry, if that's too much detail.\n> \n> OK, so my strawman was totally off-base. :)\n> \n> That explanation is interesting. I do think you're facing a real problem\n> with moving the versions in lockstep. But shoe-horning the feature into\n> config like this seems like a pretty ugly solution:\n> \n>    1. Then we're stuck with this weird foot-gun config option forever.\n> \n>    2. It only solves the problem for this one option. Is there a more\n>       general solution?\n> \n> The more general solutions I can come up with are:\n> \n>    1. Is there a way for a caller to query Git to say \"do you understand\n>       --no-ahead-behind?\".\n> \n>       You can ask \"git version\", but parsing version numbers is\n>       problematic. We don't have any kind of \"feature flags\" output, and\n>       I'm not sure we'd want to get down to the level of specific\n>       command-line options there.\n> \n>       One thing you can do is speculatively run \"status --no-ahead-behind\",\n>       and if it returns 129, try again without as a fallback. That incurs\n>       a failed invocation for the fallback case, but it's quick (we fail\n>       before looking at any data) and you can cache it for the duration\n>       of a VS session.\n> \n>    2. There could be some way to tell the option parser that the next\n>       flag is optional. E.g.:\n> \n>         git status --optional=--no-ahead-behind\n> \n>       That would be pretty easy to implement globally in parse-options.c.\n>       It knows when an option is unrecognized, so it could just treat\n>       that as a silent noop rather than barfing.\n> \n>       Of course, that doesn't solve your problem today. It wouldn't be\n>       safe to start using \"--optional\" until it had been in several\n>       released versions.\n> \n>       And I have a feeling it may not be sufficient without further\n>       feedback to the caller. For this flag, the caller is happy to say\n>       \"do this if you know how, but otherwise I will cope\". But there are\n>       probably flag where it would need to know whether it had any effect\n>       or not. So this whole direction is probably crazy.\n> \n> Of the two, I think (1) is not so bad.\n> \n>> It is OK with me if we omit the last commit in the patch series (that\n>> does the experimental =2 extension) and I'll deal with this separately\n>> (maybe differently) in the gvfs fork.\n> \n> That would be my preference. Thanks.\n> \n> -Peff\n> \n\nInteresting ideas, but probably overkill for now.  I'll pull it out\nof my next version and deal with it differently our gvfs fork.\n\nThanks,\nJeff\n"}]}