{"thread":{"id":"47566","subject":"[PATCH v4 0/4] Add --no-ahead-behind to status","startedAt":"2018-01-08T15:48:36Z","lastAt":"2018-01-11T09:39:50Z","messageCount":16,"participants":["Jeff Hostetler","Ben Peart","Jeff King","Johannes Schindelin","Derrick Stolee","Junio C Hamano"],"isPatch":true,"patchVersion":4,"patchTotal":4},"messages":[{"id":"336149","messageId":"20180108154822.54829-1-git@jeffhostetler.com","threadId":"47566","inReplyTo":null,"subject":"[PATCH v4 0/4] Add --no-ahead-behind to status","fromName":"Jeff Hostetler","fromEmail":"git@jeffhostetler.com","sentAt":"2018-01-08T15:48:18Z","receivedAt":"2018-01-08T15:48:36Z","isPatch":true,"sender":{"key":"git@jeffhostetler.com","avatar":null},"body":"From: Jeff Hostetler <jeffhost@microsoft.com>\n\nThis is version 4 of my patch series to avoid expensive ahead/behind\ncalculations in status.  This version removes the last commit containing\nthe experimental config setting.  And removes the undefined return values\nfor the nr_ahead/nr_behind arguments as discussed on the mailing list.\n\nThis version does not address \"git branch -vv\", but that requires\npassing data across the for-each-ref barrier and that seemed beyond\nthe scope of this task.\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/config.txt     |  6 ++++\n Documentation/git-status.txt |  5 +++\n builtin/checkout.c           |  2 +-\n builtin/commit.c             | 18 ++++++++++-\n ref-filter.c                 |  8 ++---\n remote.c                     | 50 ++++++++++++++++++++----------\n remote.h                     | 12 ++++++--\n t/t6040-tracking-info.sh     | 73 ++++++++++++++++++++++++++++++++++++++++++++\n t/t7064-wtstatus-pv2.sh      | 69 +++++++++++++++++++++++++++++++++++++++++\n wt-status.c                  | 41 +++++++++++++++++--------\n wt-status.h                  |  2 ++\n 11 files changed, 250 insertions(+), 36 deletions(-)\n\n-- \n2.9.3\n\n"},{"id":"336150","messageId":"20180108154822.54829-2-git@jeffhostetler.com","threadId":"47566","inReplyTo":"20180108154822.54829-1-git@jeffhostetler.com","subject":"[PATCH v4 1/4] stat_tracking_info: return +1 when branches not equal","fromName":"Jeff Hostetler","fromEmail":"git@jeffhostetler.com","sentAt":"2018-01-08T15:48:19Z","receivedAt":"2018-01-08T15:48:38Z","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":"336151","messageId":"20180108154822.54829-3-git@jeffhostetler.com","threadId":"47566","inReplyTo":"20180108154822.54829-1-git@jeffhostetler.com","subject":"[PATCH v4 2/4] status: add --[no-]ahead-behind to status and commit for V2 format.","fromName":"Jeff Hostetler","fromEmail":"git@jeffhostetler.com","sentAt":"2018-01-08T15:48:20Z","receivedAt":"2018-01-08T15:48:40Z","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                  | 30 +++++++++++++------\n wt-status.h                  |  2 ++\n 8 files changed, 125 insertions(+), 12 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 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..80474e1 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 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+\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+\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":"336152","messageId":"20180108154822.54829-4-git@jeffhostetler.com","threadId":"47566","inReplyTo":"20180108154822.54829-1-git@jeffhostetler.com","subject":"[PATCH v4 3/4] status: update short status to respect --no-ahead-behind","fromName":"Jeff Hostetler","fromEmail":"git@jeffhostetler.com","sentAt":"2018-01-08T15:48:21Z","receivedAt":"2018-01-08T15:48:43Z","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 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":"336153","messageId":"20180108154822.54829-5-git@jeffhostetler.com","threadId":"47566","inReplyTo":"20180108154822.54829-1-git@jeffhostetler.com","subject":"[PATCH v4 4/4] status: support --no-ahead-behind in long format","fromName":"Jeff Hostetler","fromEmail":"git@jeffhostetler.com","sentAt":"2018-01-08T15:48:22Z","receivedAt":"2018-01-08T15:48: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\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 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 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 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"},{"id":"336179","messageId":"7b759564-5544-8845-0594-e8342a0b4ba5@gmail.com","threadId":"47566","inReplyTo":"20180108154822.54829-1-git@jeffhostetler.com","subject":"Re: [PATCH v4 0/4] Add --no-ahead-behind to status","fromName":"Ben Peart","fromEmail":"peartben@gmail.com","sentAt":"2018-01-08T19:49:07Z","receivedAt":"2018-01-08T19:49:15Z","isPatch":true,"sender":{"key":"benpeart@microsoft.com","avatar":"https://avatars.githubusercontent.com/u/15252029?v=4"},"body":"\n\nOn 1/8/2018 10:48 AM, Jeff Hostetler wrote:\n> From: Jeff Hostetler <jeffhost@microsoft.com>\n> \n> This is version 4 of my patch series to avoid expensive ahead/behind\n> calculations in status.  This version removes the last commit containing\n> the experimental config setting.  And removes the undefined return values\n> for the nr_ahead/nr_behind arguments as discussed on the mailing list.\n\nWhile I like the simplicity of just turning this off completely, I do \nwonder if we could come up with a better user experience.  For example, \ncould we somehow limit the time spent computing the before/after and if \nit exceeds that limit, drop back to saying they are \"different\" rather \nthan computing the exact number of commits before/after.\n\nI was thinking about something similar to the logic we use today about \nwhether to start reporting progress on other long commands. That would \nmean you could still get the ahead/behind values if you aren't that far \nbehind but would only get \"different\" if that calculation gets too \nexpensive (which implies the actual value isn't going to be all that \nhelpful anyway).\n\n> \n> This version does not address \"git branch -vv\", but that requires\n> passing data across the for-each-ref barrier and that seemed beyond\n> the scope of this task.\n> \n> Jeff 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/config.txt     |  6 ++++\n>   Documentation/git-status.txt |  5 +++\n>   builtin/checkout.c           |  2 +-\n>   builtin/commit.c             | 18 ++++++++++-\n>   ref-filter.c                 |  8 ++---\n>   remote.c                     | 50 ++++++++++++++++++++----------\n>   remote.h                     | 12 ++++++--\n>   t/t6040-tracking-info.sh     | 73 ++++++++++++++++++++++++++++++++++++++++++++\n>   t/t7064-wtstatus-pv2.sh      | 69 +++++++++++++++++++++++++++++++++++++++++\n>   wt-status.c                  | 41 +++++++++++++++++--------\n>   wt-status.h                  |  2 ++\n>   11 files changed, 250 insertions(+), 36 deletions(-)\n> \n"},{"id":"336182","messageId":"8affe37c-d937-d7e0-fe06-cf7c8db173fa@jeffhostetler.com","threadId":"47566","inReplyTo":"7b759564-5544-8845-0594-e8342a0b4ba5@gmail.com","subject":"Re: [PATCH v4 0/4] Add --no-ahead-behind to status","fromName":"Jeff Hostetler","fromEmail":"git@jeffhostetler.com","sentAt":"2018-01-08T20:04:20Z","receivedAt":"2018-01-08T20:04:29Z","isPatch":true,"sender":{"key":"git@jeffhostetler.com","avatar":null},"body":"\n\nOn 1/8/2018 2:49 PM, Ben Peart wrote:\n> \n> \n> On 1/8/2018 10:48 AM, Jeff Hostetler wrote:\n>> From: Jeff Hostetler <jeffhost@microsoft.com>\n>>\n>> This is version 4 of my patch series to avoid expensive ahead/behind\n>> calculations in status.  This version removes the last commit containing\n>> the experimental config setting.  And removes the undefined return values\n>> for the nr_ahead/nr_behind arguments as discussed on the mailing list.\n> \n> While I like the simplicity of just turning this off completely, I do wonder if we could come up with a better user experience.  For example, could we somehow limit the time spent computing the before/after and if it exceeds that limit, drop back to saying they are \"different\" rather than computing the exact number of commits before/after.\n> \n> I was thinking about something similar to the logic we use today about whether to start reporting progress on other long commands. That would mean you could still get the ahead/behind values if you aren't that far behind but would only get \"different\" if that calculation gets too expensive (which implies the actual value isn't going to be all that helpful anyway).\n\n\nAfter a off-line conversation with the others I'm going to look into\na version that is limited to n commits rather than be completely on or\noff.  I think if you are for example less than 100 a/b then those numbers\nhave value; if you are past n, then they have much less value.\n\nI'd rather do it by a fixed limit than by time to ensure that output\nis predictable on graph shape and not on system load.\n\nJeff\n"},{"id":"336223","messageId":"20180109072044.GD32257@sigill.intra.peff.net","threadId":"47566","inReplyTo":"8affe37c-d937-d7e0-fe06-cf7c8db173fa@jeffhostetler.com","subject":"Re: [PATCH v4 0/4] Add --no-ahead-behind to status","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2018-01-09T07:20:44Z","receivedAt":"2018-01-09T07:20:51Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Mon, Jan 08, 2018 at 03:04:20PM -0500, Jeff Hostetler wrote:\n\n> > I was thinking about something similar to the logic we use today\n> > about whether to start reporting progress on other long commands.\n> > That would mean you could still get the ahead/behind values if you\n> > aren't that far behind but would only get \"different\" if that\n> > calculation gets too expensive (which implies the actual value isn't\n> > going to be all that helpful anyway).\n> \n> After a off-line conversation with the others I'm going to look into\n> a version that is limited to n commits rather than be completely on or\n> off.  I think if you are for example less than 100 a/b then those numbers\n> have value; if you are past n, then they have much less value.\n> \n> I'd rather do it by a fixed limit than by time to ensure that output\n> is predictable on graph shape and not on system load.\n\nI like this direction a lot. I had hoped we could say \"100+ commits\nahead\", but I don't think we can do so accurately without generation\nnumbers. E.g., the case I mentioned at the bottom of this mail:\n\n  https://public-inbox.org/git/20171224143318.GC23648@sigill.intra.peff.net/\n\nI haven't thought too hard on it, but I suspect with generation numbers\nyou could bound it and at least say \"100+ ahead\" or \"100+ behind\". But I\ndon't think you can approximate both ahead and behind together without\nfinding the actual merge base.\n\nBut even still, finding small answers quickly and accurately and punting\nto \"really far, I didn't bother to compute it\" on the big ones would be\nan improvement over always punting.\n\n-Peff\n"},{"id":"336250","messageId":"nycvar.QRO.7.76.6.1801091407480.37@ZVAVAG-6OXH6DA.rhebcr.pbec.zvpebfbsg.pbz","threadId":"47566","inReplyTo":"20180109072044.GD32257@sigill.intra.peff.net","subject":"Re: [PATCH v4 0/4] Add --no-ahead-behind to status","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2018-01-09T13:15:47Z","receivedAt":"2018-01-09T13:16:12Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Peff,\n\nOn Tue, 9 Jan 2018, Jeff King wrote:\n\n> On Mon, Jan 08, 2018 at 03:04:20PM -0500, Jeff Hostetler wrote:\n> \n> > > I was thinking about something similar to the logic we use today\n> > > about whether to start reporting progress on other long commands.\n> > > That would mean you could still get the ahead/behind values if you\n> > > aren't that far behind but would only get \"different\" if that\n> > > calculation gets too expensive (which implies the actual value isn't\n> > > going to be all that helpful anyway).\n> > \n> > After a off-line conversation with the others I'm going to look into\n> > a version that is limited to n commits rather than be completely on or\n> > off.  I think if you are for example less than 100 a/b then those numbers\n> > have value; if you are past n, then they have much less value.\n> > \n> > I'd rather do it by a fixed limit than by time to ensure that output\n> > is predictable on graph shape and not on system load.\n> \n> I like this direction a lot. I had hoped we could say \"100+ commits\n> ahead\",\n\nHow about \"100+ commits apart\" instead?\n\n> but I don't think we can do so accurately without generation numbers.\n\nEven with generation numbers, it is not possible to say whether two given\ncommits reflect diverging branches or have an ancestor-descendant\nrelationship (or in graph speak: whether they are comparable).\n\nIt could potentially make it possible to cut off the commit traversal, but\nI do not even see how that would be possible.\n\nThe only thing you could say for sure is that two different commits with\nthe same generation number are for sure \"uncomparable\", i.e. reflect\ndiverging branches.\n\n> E.g., the case I mentioned at the bottom of this mail:\n> \n>   https://public-inbox.org/git/20171224143318.GC23648@sigill.intra.peff.net/\n> \n> I haven't thought too hard on it, but I suspect with generation numbers\n> you could bound it and at least say \"100+ ahead\" or \"100+ behind\".\n\nIf you have walked 100 commits and still have not found a merge base,\nthere is no telling whether one start point is the ancestor of the other.\nAll you can say is that there are more than 100 commits in the\n\"difference\".\n\nYou would not even be able to say that the *shortest* path between those\ntwo start points is longer than 100 commits, you can construct\npathological DAGs pretty easily.\n\nEven if you had generation numbers, and one commit's generation number\nwas, say, 17, and the other one's was 17,171, you could not necessarily\nassume that the 17 one is the ancestor of the 17,171 one, all you can say\nthat it is not possible the other way round.\n\n> But I don't think you can approximate both ahead and behind together\n> without finding the actual merge base.\n> \n> But even still, finding small answers quickly and accurately and punting\n> to \"really far, I didn't bother to compute it\" on the big ones would be\n> an improvement over always punting.\n\nIndeed. The longer I think about it, the more I like the \"100+ commits\napart\" idea.\n\nCiao,\nDscho\n"},{"id":"336253","messageId":"c19f6469-190a-33e7-20a2-fcb17b8e45d9@gmail.com","threadId":"47566","inReplyTo":"nycvar.QRO.7.76.6.1801091407480.37@ZVAVAG-6OXH6DA.rhebcr.pbec.zvpebfbsg.pbz","subject":"Re: [PATCH v4 0/4] Add --no-ahead-behind to status","fromName":"Derrick Stolee","fromEmail":"stolee@gmail.com","sentAt":"2018-01-09T14:29:31Z","receivedAt":"2018-01-09T14:29:41Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"On 1/9/2018 8:15 AM, Johannes Schindelin wrote:\n> Hi Peff,\n>\n> On Tue, 9 Jan 2018, Jeff King wrote:\n>\n>> On Mon, Jan 08, 2018 at 03:04:20PM -0500, Jeff Hostetler wrote:\n>>\n>>>> I was thinking about something similar to the logic we use today\n>>>> about whether to start reporting progress on other long commands.\n>>>> That would mean you could still get the ahead/behind values if you\n>>>> aren't that far behind but would only get \"different\" if that\n>>>> calculation gets too expensive (which implies the actual value isn't\n>>>> going to be all that helpful anyway).\n>>> After a off-line conversation with the others I'm going to look into\n>>> a version that is limited to n commits rather than be completely on or\n>>> off.  I think if you are for example less than 100 a/b then those numbers\n>>> have value; if you are past n, then they have much less value.\n>>>\n>>> I'd rather do it by a fixed limit than by time to ensure that output\n>>> is predictable on graph shape and not on system load.\n>> I like this direction a lot. I had hoped we could say \"100+ commits\n>> ahead\",\n> How about \"100+ commits apart\" instead?\n\nUnfortunately, we can _never_ guarantee more than 1 commit ahead/behind \nunless we walk to the merge base (or have generation numbers). For \nexample, suppose the 101st commit in each history has a parent that in \nthe recent history of the other commit. (There must be merge commits to \nmake this work without creating cycles, but the ahead/behind counts \ncould be much lower than the number of walked commits.)\n\n>\n>> but I don't think we can do so accurately without generation numbers.\n> Even with generation numbers, it is not possible to say whether two given\n> commits reflect diverging branches or have an ancestor-descendant\n> relationship (or in graph speak: whether they are comparable).\n\nIf you walk commits using a priority queue where the priority is the \ngeneration number, then you can know that you have walked all reachable \ncommits with generation greater than X, so you know among those commits \nwhich are comparable or not.\n\nFor this to work accurately, you must walk from both tips to a \ngeneration lower than each. It does not help the case where one branch \nis 100,000+ commits ahead, where most of those commits have higher \ngeneration number than the behind commit.\n\n> It could potentially make it possible to cut off the commit traversal, but\n> I do not even see how that would be possible.\n>\n> The only thing you could say for sure is that two different commits with\n> the same generation number are for sure \"uncomparable\", i.e. reflect\n> diverging branches.\n>\n>> E.g., the case I mentioned at the bottom of this mail:\n>>\n>>    https://public-inbox.org/git/20171224143318.GC23648@sigill.intra.peff.net/\n>>\n>> I haven't thought too hard on it, but I suspect with generation numbers\n>> you could bound it and at least say \"100+ ahead\" or \"100+ behind\".\n> If you have walked 100 commits and still have not found a merge base,\n> there is no telling whether one start point is the ancestor of the other.\n> All you can say is that there are more than 100 commits in the\n> \"difference\".\n>\n> You would not even be able to say that the *shortest* path between those\n> two start points is longer than 100 commits, you can construct\n> pathological DAGs pretty easily.\n>\n> Even if you had generation numbers, and one commit's generation number\n> was, say, 17, and the other one's was 17,171, you could not necessarily\n> assume that the 17 one is the ancestor of the 17,171 one, all you can say\n> that it is not possible the other way round.\n\nThis is why we cannot _always_ use generation numbers, but they do help \nin some cases.\n\n>> But I don't think you can approximate both ahead and behind together\n>> without finding the actual merge base.\n>>\n>> But even still, finding small answers quickly and accurately and punting\n>> to \"really far, I didn't bother to compute it\" on the big ones would be\n>> an improvement over always punting.\n> Indeed. The longer I think about it, the more I like the \"100+ commits\n> apart\" idea.\n>\n\nAgain, I strongly suggest we drop this approach because it will be more \npain than it is worth.\n\nThanks,\n-Stolee\n"},{"id":"336259","messageId":"98758cd5-de26-0f15-ad8b-c2abd62f67d3@jeffhostetler.com","threadId":"47566","inReplyTo":"c19f6469-190a-33e7-20a2-fcb17b8e45d9@gmail.com","subject":"Re: [PATCH v4 0/4] Add --no-ahead-behind to status","fromName":"Jeff Hostetler","fromEmail":"git@jeffhostetler.com","sentAt":"2018-01-09T14:56:12Z","receivedAt":"2018-01-09T14:56:22Z","isPatch":true,"sender":{"key":"git@jeffhostetler.com","avatar":null},"body":"\n\nOn 1/9/2018 9:29 AM, Derrick Stolee wrote:\n> On 1/9/2018 8:15 AM, Johannes Schindelin wrote:\n>> Hi Peff,\n>>\n>> On Tue, 9 Jan 2018, Jeff King wrote:\n>>\n>>> On Mon, Jan 08, 2018 at 03:04:20PM -0500, Jeff Hostetler wrote:\n>>>\n>>>>> I was thinking about something similar to the logic we use today\n>>>>> about whether to start reporting progress on other long commands.\n>>>>> That would mean you could still get the ahead/behind values if you\n>>>>> aren't that far behind but would only get \"different\" if that\n>>>>> calculation gets too expensive (which implies the actual value isn't\n>>>>> going to be all that helpful anyway).\n>>>> After a off-line conversation with the others I'm going to look into\n>>>> a version that is limited to n commits rather than be completely on or\n>>>> off.  I think if you are for example less than 100 a/b then those numbers\n>>>> have value; if you are past n, then they have much less value.\n>>>>\n>>>> I'd rather do it by a fixed limit than by time to ensure that output\n>>>> is predictable on graph shape and not on system load.\n>>> I like this direction a lot. I had hoped we could say \"100+ commits\n>>> ahead\",\n>> How about \"100+ commits apart\" instead?\n> \n> Unfortunately, we can _never_ guarantee more than 1 commit ahead/behind unless we walk to the merge base (or have generation numbers). For example, suppose the 101st commit in each history has a parent that in the recent history of the other commit. (There must be merge commits to make this work without creating cycles, but the ahead/behind counts could be much lower than the number of walked commits.)\n> \n>>\n>>> but I don't think we can do so accurately without generation numbers.\n>> Even with generation numbers, it is not possible to say whether two given\n>> commits reflect diverging branches or have an ancestor-descendant\n>> relationship (or in graph speak: whether they are comparable).\n> \n> If you walk commits using a priority queue where the priority is the generation number, then you can know that you have walked all reachable commits with generation greater than X, so you know among those commits which are comparable or not.\n> \n> For this to work accurately, you must walk from both tips to a generation lower than each. It does not help the case where one branch is 100,000+ commits ahead, where most of those commits have higher generation number than the behind commit.\n> \n>> It could potentially make it possible to cut off the commit traversal, but\n>> I do not even see how that would be possible.\n>>\n>> The only thing you could say for sure is that two different commits with\n>> the same generation number are for sure \"uncomparable\", i.e. reflect\n>> diverging branches.\n>>\n>>> E.g., the case I mentioned at the bottom of this mail:\n>>>\n>>>    https://public-inbox.org/git/20171224143318.GC23648@sigill.intra.peff.net/\n>>>\n>>> I haven't thought too hard on it, but I suspect with generation numbers\n>>> you could bound it and at least say \"100+ ahead\" or \"100+ behind\".\n>> If you have walked 100 commits and still have not found a merge base,\n>> there is no telling whether one start point is the ancestor of the other.\n>> All you can say is that there are more than 100 commits in the\n>> \"difference\".\n>>\n>> You would not even be able to say that the *shortest* path between those\n>> two start points is longer than 100 commits, you can construct\n>> pathological DAGs pretty easily.\n>>\n>> Even if you had generation numbers, and one commit's generation number\n>> was, say, 17, and the other one's was 17,171, you could not necessarily\n>> assume that the 17 one is the ancestor of the 17,171 one, all you can say\n>> that it is not possible the other way round.\n> \n> This is why we cannot _always_ use generation numbers, but they do help in some cases.\n> \n>>> But I don't think you can approximate both ahead and behind together\n>>> without finding the actual merge base.\n>>>\n>>> But even still, finding small answers quickly and accurately and punting\n>>> to \"really far, I didn't bother to compute it\" on the big ones would be\n>>> an improvement over always punting.\n>> Indeed. The longer I think about it, the more I like the \"100+ commits\n>> apart\" idea.\n>>\n> \n> Again, I strongly suggest we drop this approach because it will be more pain than it is worth.\n\nGood comments all.  Thanks!\nI will leave the patch series as it is with the existing on/off setting\nand call it quits.\n\nThanks,\nJeff\n\n"},{"id":"336267","messageId":"nycvar.QRO.7.76.6.1801091744540.37@ZVAVAG-6OXH6DA.rhebcr.pbec.zvpebfbsg.pbz","threadId":"47566","inReplyTo":"c19f6469-190a-33e7-20a2-fcb17b8e45d9@gmail.com","subject":"Re: [PATCH v4 0/4] Add --no-ahead-behind to status","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2018-01-09T16:48:58Z","receivedAt":"2018-01-09T16:49:24Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Stolee,\n\nOn Tue, 9 Jan 2018, Derrick Stolee wrote:\n\n> On 1/9/2018 8:15 AM, Johannes Schindelin wrote:\n> >\n> > On Tue, 9 Jan 2018, Jeff King wrote:\n> >\n> > > But I don't think you can approximate both ahead and behind together\n> > > without finding the actual merge base.\n> > >\n> > > But even still, finding small answers quickly and accurately and\n> > > punting to \"really far, I didn't bother to compute it\" on the big\n> > > ones would be an improvement over always punting.\n> >\n> > Indeed. The longer I think about it, the more I like the \"100+ commits\n> > apart\" idea.\n> \n> Again, I strongly suggest we drop this approach because it will be more pain\n> than it is worth.\n\nSo what you are saying is if there is a commit graph with *heavy* clock\nskew, you might overestimate how many commits apart the tips are.\n\nI say that this is striking the balance between correctness and usability\non the *wrong* side.\n\nSure, it might be wrong if your commit graph suffers heavily from clock\nskew. In most cases, you still get a pretty darn useful hint where you're\nat.\n\nThe alternative would be *not* to show any useful hint in most cases, i.e.\nwhen you did not find all merge bases within <N> commits. I would really\nhate it if Git spent so much time and did not even give me a hint. Totally\nunsatisfying user experience.\n\nCiao,\nJohannes\n"},{"id":"336341","messageId":"20180110074159.GA16315@sigill.intra.peff.net","threadId":"47566","inReplyTo":"nycvar.QRO.7.76.6.1801091407480.37@ZVAVAG-6OXH6DA.rhebcr.pbec.zvpebfbsg.pbz","subject":"Re: [PATCH v4 0/4] Add --no-ahead-behind to status","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2018-01-10T07:41:59Z","receivedAt":"2018-01-10T07:42:06Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Tue, Jan 09, 2018 at 02:15:47PM +0100, Johannes Schindelin wrote:\n\n> > I like this direction a lot. I had hoped we could say \"100+ commits\n> > ahead\",\n> \n> How about \"100+ commits apart\" instead?\n\nYeah, that is probably more accurate for the general case.\n\n> > but I don't think we can do so accurately without generation numbers.\n> \n> Even with generation numbers, it is not possible to say whether two given\n> commits reflect diverging branches or have an ancestor-descendant\n> relationship (or in graph speak: whether they are comparable).\n> \n> It could potentially make it possible to cut off the commit traversal, but\n> I do not even see how that would be possible.\n> \n> The only thing you could say for sure is that two different commits with\n> the same generation number are for sure \"uncomparable\", i.e. reflect\n> diverging branches.\n\nI think sometimes we can say more. E.g., imagine we have two commits, A\nand B, with generation numbers N and N+1000. We walk back 100 commits\ndeep from B and end up at a commit around N+900. We know that there are\nat least 100 commits in B that are not in A (the 100 we walked, which we\nknow cannot be ancestors of A because of their generation numbers).\nThat's true even if there is no ancestry relationship between the two\ncommits at all.\n\nBut we cannot say in that case how many (if any) commits are in A but\nnot B. So we can say \"100+ commits ahead, unknown behind\" (or if the two\ngeneration numbers are reversed, we can say \"unknown ahead, 100+ commits\nbehind).\n\nIn the more general case, we could actually walk _past_ generation N,\nand end up at N-25 or similar. There we can't say anything intelligent\nabout the commits with generations <= N. But we could say something like\n\"there are 75 commits in B that cannot be in A\" (note that it's probably\n_not_ actually 75; it's however many we walked until we crossed N).\n\nSo that was what I was getting at in the earlier mail. We can sometimes\ngive a partial answer. But I think giving that partial answer is likely\nto be more confusing than useful to a user, because there are a lot of\ncaveats about how much we know for a given situation.\n\nAnd I think from what you wrote below that you probably agree with that\n(that we can make a few claims, but not enough to really be useful).\n\n-Peff\n"},{"id":"336342","messageId":"20180110074701.GB16315@sigill.intra.peff.net","threadId":"47566","inReplyTo":"c19f6469-190a-33e7-20a2-fcb17b8e45d9@gmail.com","subject":"Re: [PATCH v4 0/4] Add --no-ahead-behind to status","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2018-01-10T07:47:01Z","receivedAt":"2018-01-10T07:47:07Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Tue, Jan 09, 2018 at 09:29:31AM -0500, Derrick Stolee wrote:\n\n> > > But even still, finding small answers quickly and accurately and punting\n> > > to \"really far, I didn't bother to compute it\" on the big ones would be\n> > > an improvement over always punting.\n> > Indeed. The longer I think about it, the more I like the \"100+ commits\n> > apart\" idea.\n> > \n> \n> Again, I strongly suggest we drop this approach because it will be more pain\n> than it is worth.\n\nTo be clear, which approach are we talking about? I think there are\nthree options:\n\n  1. The user tells us not to bother computing real ahead/behind values.\n     We always say \"same\" or \"not the same\".\n\n  2. The user tells us not to bother computing ahead/behind values\n     with more effort than N. After traversing N commits without getting\n     an answer, we say \"same\" or \"not the same\". But we may sometimes\n     give a real answer if we found it within N.\n\n  3. The user tells us not to spend more effort than N. After traversing\n     N commits we try to make some partial statement based on\n     generations (or commit timestamps as a proxy for them).\n\nI agree that (3) is probably not going to be useful enough in the\ngeneral case to merit the implementation effort and confusion. But is\nthere anything wrong with (2)?\n\n-Peff\n"},{"id":"336418","messageId":"xmqqfu7d1n2l.fsf@gitster.mtv.corp.google.com","threadId":"47566","inReplyTo":"20180110074701.GB16315@sigill.intra.peff.net","subject":"Re: [PATCH v4 0/4] Add --no-ahead-behind to status","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-01-10T20:22:10Z","receivedAt":"2018-01-10T20:22:17Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff King <peff@peff.net> writes:\n\n> To be clear, which approach are we talking about? I think there are\n> three options:\n>\n>   1. The user tells us not to bother computing real ahead/behind values.\n>      We always say \"same\" or \"not the same\".\n>\n>   2. The user tells us not to bother computing ahead/behind values\n>      with more effort than N. After traversing N commits without getting\n>      an answer, we say \"same\" or \"not the same\". But we may sometimes\n>      give a real answer if we found it within N.\n>\n>   3. The user tells us not to spend more effort than N. After traversing\n>      N commits we try to make some partial statement based on\n>      generations (or commit timestamps as a proxy for them).\n>\n> I agree that (3) is probably not going to be useful enough in the\n> general case to merit the implementation effort and confusion. But is\n> there anything wrong with (2)?\n\nI agree (3) would not be all that interesting.  Offhand I do not see\na problem with (2).  I think with \"real\" in your \"sometimes give a\nreal answer\" you meant to say that we limit our answers to just one\nthree (\"same\", \"not the same\", \"ahead/behind by exactly N/M\") and I\nthink it is a good choice that is easy to explain.\n\nWe might be able to say things other than these three, namely,\n\"ahead by no more than N, behind by no more than M\", but I do not\nknow if that is useful or merely more confusing than it's worth.\n"},{"id":"336448","messageId":"20180111093943.GC9190@sigill.intra.peff.net","threadId":"47566","inReplyTo":"xmqqfu7d1n2l.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v4 0/4] Add --no-ahead-behind to status","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2018-01-11T09:39:43Z","receivedAt":"2018-01-11T09:39:50Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Wed, Jan 10, 2018 at 12:22:10PM -0800, Junio C Hamano wrote:\n\n> Jeff King <peff@peff.net> writes:\n> \n> > To be clear, which approach are we talking about? I think there are\n> > three options:\n> >\n> >   1. The user tells us not to bother computing real ahead/behind values.\n> >      We always say \"same\" or \"not the same\".\n> >\n> >   2. The user tells us not to bother computing ahead/behind values\n> >      with more effort than N. After traversing N commits without getting\n> >      an answer, we say \"same\" or \"not the same\". But we may sometimes\n> >      give a real answer if we found it within N.\n> >\n> >   3. The user tells us not to spend more effort than N. After traversing\n> >      N commits we try to make some partial statement based on\n> >      generations (or commit timestamps as a proxy for them).\n> >\n> > I agree that (3) is probably not going to be useful enough in the\n> > general case to merit the implementation effort and confusion. But is\n> > there anything wrong with (2)?\n> \n> I agree (3) would not be all that interesting.  Offhand I do not see\n> a problem with (2).  I think with \"real\" in your \"sometimes give a\n> real answer\" you meant to say that we limit our answers to just one\n> three (\"same\", \"not the same\", \"ahead/behind by exactly N/M\") and I\n> think it is a good choice that is easy to explain.\n\nYes, exactly. That's a better way of saying it.\n\n-Peff\n"}]}