From d7d1b496aeea5a151c826683ed28c57ef0ac9389 Mon Sep 17 00:00:00 2001 From: Jeff Hostetler Date: Tue, 9 Jan 2018 18:50:15 +0000 Subject: stat_tracking_info: return +1 when branches not equal Extend stat_tracking_info() to return +1 when branches are not equal and to take a new "enum ahead_behind_flags" argument to allow skipping the (possibly expensive) ahead/behind computation. This will be used in the next commit to allow "git status" to avoid full ahead/behind calculations for performance reasons. Signed-off-by: Jeff Hostetler Signed-off-by: Junio C Hamano --- wt-status.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'wt-status.c') diff --git a/wt-status.c b/wt-status.c index ef26f07446..1137f27aa6 100644 --- a/wt-status.c +++ b/wt-status.c @@ -1796,7 +1796,8 @@ static void wt_shortstatus_print_tracking(struct wt_status *s) color_fprintf(s->fp, branch_color_local, "%s", branch_name); - if (stat_tracking_info(branch, &num_ours, &num_theirs, &base) < 0) { + if (stat_tracking_info(branch, &num_ours, &num_theirs, &base, + AHEAD_BEHIND_FULL) < 0) { if (!base) goto conclude; @@ -1933,7 +1934,8 @@ static void wt_porcelain_v2_print_tracking(struct wt_status *s) /* Lookup stats on the upstream tracking branch, if set. */ branch = branch_get(branch_name); base = NULL; - ab_info = (stat_tracking_info(branch, &nr_ahead, &nr_behind, &base) == 0); + ab_info = (stat_tracking_info(branch, &nr_ahead, &nr_behind, + &base, AHEAD_BEHIND_FULL) >= 0); if (base) { base = shorten_unambiguous_ref(base, 0); fprintf(s->fp, "# branch.upstream %s%c", base, eol); -- cgit 1.2.3-korg From fd9b544a2991ad74d73ad1bc0af4d24f91a6802b Mon Sep 17 00:00:00 2001 From: Jeff Hostetler Date: Tue, 9 Jan 2018 18:50:16 +0000 Subject: status: add --[no-]ahead-behind to status and commit for V2 format. Teach "git status" and "git commit" to accept "--no-ahead-behind" and "--ahead-behind" arguments to request quick or full ahead/behind reporting. When "--no-ahead-behind" is given, the existing porcelain V2 line "branch.ab +x -y" is replaced with a new "branch.ab +? -?" line. This indicates that the branch and its upstream are or are not equal without the expense of computing the full ahead/behind values. Signed-off-by: Jeff Hostetler Signed-off-by: Junio C Hamano --- Documentation/git-status.txt | 5 ++++ builtin/commit.c | 7 +++++ remote.c | 2 ++ remote.h | 5 ++-- t/t7064-wtstatus-pv2.sh | 62 ++++++++++++++++++++++++++++++++++++++++++++ wt-status.c | 30 ++++++++++++++------- wt-status.h | 2 ++ 7 files changed, 102 insertions(+), 11 deletions(-) (limited to 'wt-status.c') diff --git a/Documentation/git-status.txt b/Documentation/git-status.txt index 81cab9aefb..b6ec10bf32 100644 --- a/Documentation/git-status.txt +++ b/Documentation/git-status.txt @@ -130,6 +130,11 @@ ignored, then the directory is not shown, but all contents are shown. without options are equivalent to 'always' and 'never' respectively. +--ahead-behind:: +--no-ahead-behind:: + Display or do not display detailed ahead/behind counts for the + branch relative to its upstream branch. Defaults to true. + ...:: See the 'pathspec' entry in linkgit:gitglossary[7]. diff --git a/builtin/commit.c b/builtin/commit.c index 8a87701414..bfcb88f291 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -1151,6 +1151,9 @@ static void finalize_deferred_config(struct wt_status *s) s->show_branch = status_deferred_config.show_branch; if (s->show_branch < 0) s->show_branch = 0; + + if (s->ahead_behind_flags == AHEAD_BEHIND_UNSPECIFIED) + s->ahead_behind_flags = AHEAD_BEHIND_FULL; } static int parse_and_validate_options(int argc, const char *argv[], @@ -1365,6 +1368,8 @@ int cmd_status(int argc, const char **argv, const char *prefix) N_("show branch information")), OPT_BOOL(0, "show-stash", &s.show_stash, N_("show stash information")), + OPT_BOOL(0, "ahead-behind", &s.ahead_behind_flags, + N_("compute full ahead/behind values")), { OPTION_CALLBACK, 0, "porcelain", &status_format, N_("version"), N_("machine-readable output"), PARSE_OPT_OPTARG, opt_parse_porcelain }, @@ -1648,6 +1653,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix) OPT_SET_INT(0, "short", &status_format, N_("show status concisely"), STATUS_FORMAT_SHORT), OPT_BOOL(0, "branch", &s.show_branch, N_("show branch information")), + OPT_BOOL(0, "ahead-behind", &s.ahead_behind_flags, + N_("compute full ahead/behind values")), OPT_SET_INT(0, "porcelain", &status_format, N_("machine-readable output"), STATUS_FORMAT_PORCELAIN), OPT_SET_INT(0, "long", &status_format, diff --git a/remote.c b/remote.c index 29d47a5852..e514536afe 100644 --- a/remote.c +++ b/remote.c @@ -2058,6 +2058,8 @@ int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs, return 0; if (abf == AHEAD_BEHIND_QUICK) return 1; + if (abf != AHEAD_BEHIND_FULL) + BUG("stat_tracking_info: invalid abf '%d'", abf); /* Run "rev-list --left-right ours...theirs" internally... */ argv_array_push(&argv, ""); /* ignored */ diff --git a/remote.h b/remote.h index 80240f0ada..0a6e770c9e 100644 --- a/remote.h +++ b/remote.h @@ -259,8 +259,9 @@ enum match_refs_flags { /* Flags for --ahead-behind option. */ enum ahead_behind_flags { - AHEAD_BEHIND_QUICK = 0, /* just eq/neq reporting */ - AHEAD_BEHIND_FULL = 1, /* traditional a/b reporting */ + AHEAD_BEHIND_UNSPECIFIED = -1, + AHEAD_BEHIND_QUICK = 0, /* just eq/neq reporting */ + AHEAD_BEHIND_FULL = 1, /* traditional a/b reporting */ }; /* Reporting of tracking info */ diff --git a/t/t7064-wtstatus-pv2.sh b/t/t7064-wtstatus-pv2.sh index e319fa2e84..8f795327a0 100755 --- a/t/t7064-wtstatus-pv2.sh +++ b/t/t7064-wtstatus-pv2.sh @@ -390,6 +390,68 @@ test_expect_success 'verify upstream fields in branch header' ' ) ' +test_expect_success 'verify --[no-]ahead-behind with V2 format' ' + git checkout master && + test_when_finished "rm -rf sub_repo" && + git clone . sub_repo && + ( + ## Confirm local master tracks remote master. + cd sub_repo && + HUF=$(git rev-parse HEAD) && + + # Confirm --no-ahead-behind reports traditional branch.ab with 0/0 for equal branches. + cat >expect <<-EOF && + # branch.oid $HUF + # branch.head master + # branch.upstream origin/master + # branch.ab +0 -0 + EOF + + git status --no-ahead-behind --porcelain=v2 --branch --untracked-files=all >actual && + test_cmp expect actual && + + # Confirm --ahead-behind reports traditional branch.ab with 0/0. + cat >expect <<-EOF && + # branch.oid $HUF + # branch.head master + # branch.upstream origin/master + # branch.ab +0 -0 + EOF + + git status --ahead-behind --porcelain=v2 --branch --untracked-files=all >actual && + test_cmp expect actual && + + ## Test non-equal ahead/behind. + echo xyz >file_xyz && + git add file_xyz && + git commit -m xyz && + + HUF=$(git rev-parse HEAD) && + + # Confirm --no-ahead-behind reports branch.ab with ?/? for non-equal branches. + cat >expect <<-EOF && + # branch.oid $HUF + # branch.head master + # branch.upstream origin/master + # branch.ab +? -? + EOF + + git status --no-ahead-behind --porcelain=v2 --branch --untracked-files=all >actual && + test_cmp expect actual && + + # Confirm --ahead-behind reports traditional branch.ab with 1/0. + cat >expect <<-EOF && + # branch.oid $HUF + # branch.head master + # branch.upstream origin/master + # branch.ab +1 -0 + EOF + + git status --ahead-behind --porcelain=v2 --branch --untracked-files=all >actual && + test_cmp expect actual + ) +' + test_expect_success 'create and add submodule, submodule appears clean (A. S...)' ' git checkout master && git clone . sub_repo && diff --git a/wt-status.c b/wt-status.c index 1137f27aa6..7a560c16a7 100644 --- a/wt-status.c +++ b/wt-status.c @@ -136,6 +136,7 @@ void wt_status_prepare(struct wt_status *s) s->ignored.strdup_strings = 1; s->show_branch = -1; /* unspecified */ s->show_stash = 0; + s->ahead_behind_flags = AHEAD_BEHIND_UNSPECIFIED; s->display_comment_prefix = 0; } @@ -1883,18 +1884,19 @@ static void wt_porcelain_print(struct wt_status *s) * * ::= the upstream branch name, when set. * - * ::= integer ahead value, when upstream set - * and the commit is present (not gone). - * - * ::= integer behind value, when upstream set - * and commit is present. + * ::= integer ahead value or '?'. * + * ::= integer behind value or '?'. * * The end-of-line is defined by the -z flag. * * ::= NUL when -z, * LF when NOT -z. * + * When an upstream is set and present, the 'branch.ab' line will + * be printed with the ahead/behind counts for the branch and the + * upstream. When AHEAD_BEHIND_QUICK is requested and the branches + * are different, '?' will be substituted for the actual count. */ static void wt_porcelain_v2_print_tracking(struct wt_status *s) { @@ -1934,15 +1936,25 @@ static void wt_porcelain_v2_print_tracking(struct wt_status *s) /* Lookup stats on the upstream tracking branch, if set. */ branch = branch_get(branch_name); base = NULL; - ab_info = (stat_tracking_info(branch, &nr_ahead, &nr_behind, - &base, AHEAD_BEHIND_FULL) >= 0); + ab_info = stat_tracking_info(branch, &nr_ahead, &nr_behind, + &base, s->ahead_behind_flags); if (base) { base = shorten_unambiguous_ref(base, 0); fprintf(s->fp, "# branch.upstream %s%c", base, eol); free((char *)base); - if (ab_info) - fprintf(s->fp, "# branch.ab +%d -%d%c", nr_ahead, nr_behind, eol); + if (ab_info > 0) { + /* different */ + if (nr_ahead || nr_behind) + fprintf(s->fp, "# branch.ab +%d -%d%c", + nr_ahead, nr_behind, eol); + else + fprintf(s->fp, "# branch.ab +? -?%c", + eol); + } else if (!ab_info) { + /* same */ + fprintf(s->fp, "# branch.ab +0 -0%c", eol); + } } } diff --git a/wt-status.h b/wt-status.h index fe27b465e2..b91267455b 100644 --- a/wt-status.h +++ b/wt-status.h @@ -5,6 +5,7 @@ #include "string-list.h" #include "color.h" #include "pathspec.h" +#include "remote.h" struct worktree; @@ -86,6 +87,7 @@ struct wt_status { int show_branch; int show_stash; int hints; + enum ahead_behind_flags ahead_behind_flags; enum wt_status_format status_format; unsigned char sha1_commit[GIT_MAX_RAWSZ]; /* when not Initial */ -- cgit 1.2.3-korg From 3ca1897cc1a23f3ad140d759280b73ad7affe696 Mon Sep 17 00:00:00 2001 From: Jeff Hostetler Date: Tue, 9 Jan 2018 18:50:17 +0000 Subject: status: update short status to respect --no-ahead-behind Teach "git status --short --branch" to respect "--no-ahead-behind" parameter to skip computing ahead/behind counts for the branch and its upstream and just report '[different]'. Signed-off-by: Jeff Hostetler Signed-off-by: Junio C Hamano --- t/t6040-tracking-info.sh | 13 +++++++++++++ wt-status.c | 11 +++++++---- 2 files changed, 20 insertions(+), 4 deletions(-) (limited to 'wt-status.c') diff --git a/t/t6040-tracking-info.sh b/t/t6040-tracking-info.sh index 8f17fd9da8..019022080b 100755 --- a/t/t6040-tracking-info.sh +++ b/t/t6040-tracking-info.sh @@ -146,6 +146,19 @@ test_expect_success 'status -s -b (diverged from upstream)' ' test_i18ncmp expect actual ' +cat >expect <<\EOF +## b1...origin/master [different] +EOF + +test_expect_success 'status -s -b --no-ahead-behind (diverged from upstream)' ' + ( + cd test && + git checkout b1 >/dev/null && + git status -s -b --no-ahead-behind | head -1 + ) >actual && + test_i18ncmp expect actual +' + cat >expect <<\EOF ## b5...brokenbase [gone] EOF diff --git a/wt-status.c b/wt-status.c index 7a560c16a7..13189614ae 100644 --- a/wt-status.c +++ b/wt-status.c @@ -1771,7 +1771,7 @@ static void wt_shortstatus_print_tracking(struct wt_status *s) const char *base; char *short_base; const char *branch_name; - int num_ours, num_theirs; + int num_ours, num_theirs, sti; int upstream_is_gone = 0; color_fprintf(s->fp, color(WT_STATUS_HEADER, s), "## "); @@ -1797,8 +1797,9 @@ static void wt_shortstatus_print_tracking(struct wt_status *s) color_fprintf(s->fp, branch_color_local, "%s", branch_name); - if (stat_tracking_info(branch, &num_ours, &num_theirs, &base, - AHEAD_BEHIND_FULL) < 0) { + sti = stat_tracking_info(branch, &num_ours, &num_theirs, &base, + s->ahead_behind_flags); + if (sti < 0) { if (!base) goto conclude; @@ -1810,12 +1811,14 @@ static void wt_shortstatus_print_tracking(struct wt_status *s) color_fprintf(s->fp, branch_color_remote, "%s", short_base); free(short_base); - if (!upstream_is_gone && !num_ours && !num_theirs) + if (!upstream_is_gone && !sti) goto conclude; color_fprintf(s->fp, header_color, " ["); if (upstream_is_gone) { color_fprintf(s->fp, header_color, LABEL(N_("gone"))); + } else if (s->ahead_behind_flags == AHEAD_BEHIND_QUICK) { + color_fprintf(s->fp, header_color, LABEL(N_("different"))); } else if (!num_ours) { color_fprintf(s->fp, header_color, LABEL(N_("behind "))); color_fprintf(s->fp, branch_color_remote, "%d", num_theirs); -- cgit 1.2.3-korg From f39a757dd93488103dde76e992a75edf2d772b62 Mon Sep 17 00:00:00 2001 From: Jeff Hostetler Date: Tue, 9 Jan 2018 18:50:18 +0000 Subject: status: support --no-ahead-behind in long format Teach long (normal) status format to respect the --no-ahead-behind parameter and skip the possibly expensive ahead/behind computation between the branch and the upstream. Signed-off-by: Jeff Hostetler Signed-off-by: Junio C Hamano --- builtin/checkout.c | 2 +- remote.c | 18 +++++++++++++----- remote.h | 3 ++- t/t6040-tracking-info.sh | 29 +++++++++++++++++++++++++++++ wt-status.c | 2 +- 5 files changed, 46 insertions(+), 8 deletions(-) (limited to 'wt-status.c') diff --git a/builtin/checkout.c b/builtin/checkout.c index 8bdc927d3f..70d5785a5d 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -610,7 +610,7 @@ static void report_tracking(struct branch_info *new) struct strbuf sb = STRBUF_INIT; struct branch *branch = branch_get(new->name); - if (!format_tracking_info(branch, &sb)) + if (!format_tracking_info(branch, &sb, AHEAD_BEHIND_FULL)) return; fputs(sb.buf, stdout); strbuf_release(&sb); diff --git a/remote.c b/remote.c index e514536afe..d656e33c96 100644 --- a/remote.c +++ b/remote.c @@ -2096,15 +2096,16 @@ int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs, /* * Return true when there is anything to report, otherwise false. */ -int format_tracking_info(struct branch *branch, struct strbuf *sb) +int format_tracking_info(struct branch *branch, struct strbuf *sb, + enum ahead_behind_flags abf) { - int ours, theirs; + int ours, theirs, sti; const char *full_base; char *base; int upstream_is_gone = 0; - if (stat_tracking_info(branch, &ours, &theirs, &full_base, - AHEAD_BEHIND_FULL) < 0) { + sti = stat_tracking_info(branch, &ours, &theirs, &full_base, abf); + if (sti < 0) { if (!full_base) return 0; upstream_is_gone = 1; @@ -2118,10 +2119,17 @@ int format_tracking_info(struct branch *branch, struct strbuf *sb) if (advice_status_hints) strbuf_addstr(sb, _(" (use \"git branch --unset-upstream\" to fixup)\n")); - } else if (!ours && !theirs) { + } else if (!sti) { strbuf_addf(sb, _("Your branch is up to date with '%s'.\n"), base); + } else if (abf == AHEAD_BEHIND_QUICK) { + strbuf_addf(sb, + _("Your branch and '%s' refer to different commits.\n"), + base); + if (advice_status_hints) + strbuf_addf(sb, _(" (use \"%s\" for details)\n"), + "git status --ahead-behind"); } else if (!theirs) { strbuf_addf(sb, Q_("Your branch is ahead of '%s' by %d commit.\n", diff --git a/remote.h b/remote.h index 0a6e770c9e..6360a9db69 100644 --- a/remote.h +++ b/remote.h @@ -267,7 +267,8 @@ enum ahead_behind_flags { /* Reporting of tracking info */ int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs, const char **upstream_name, enum ahead_behind_flags abf); -int format_tracking_info(struct branch *branch, struct strbuf *sb); +int format_tracking_info(struct branch *branch, struct strbuf *sb, + enum ahead_behind_flags abf); struct ref *get_local_heads(void); /* diff --git a/t/t6040-tracking-info.sh b/t/t6040-tracking-info.sh index 019022080b..716283b274 100755 --- a/t/t6040-tracking-info.sh +++ b/t/t6040-tracking-info.sh @@ -159,6 +159,35 @@ test_expect_success 'status -s -b --no-ahead-behind (diverged from upstream)' ' test_i18ncmp expect actual ' +cat >expect <<\EOF +On branch b1 +Your branch and 'origin/master' have diverged, +and have 1 and 1 different commits each, respectively. +EOF + +test_expect_success 'status --long --branch' ' + ( + cd test && + git checkout b1 >/dev/null && + git status --long -b | head -3 + ) >actual && + test_i18ncmp expect actual +' + +cat >expect <<\EOF +On branch b1 +Your branch and 'origin/master' refer to different commits. +EOF + +test_expect_success 'status --long --branch --no-ahead-behind' ' + ( + cd test && + git checkout b1 >/dev/null && + git status --long -b --no-ahead-behind | head -2 + ) >actual && + test_i18ncmp expect actual +' + cat >expect <<\EOF ## b5...brokenbase [gone] EOF diff --git a/wt-status.c b/wt-status.c index 13189614ae..e4555c7fa7 100644 --- a/wt-status.c +++ b/wt-status.c @@ -1011,7 +1011,7 @@ static void wt_longstatus_print_tracking(struct wt_status *s) if (!skip_prefix(s->branch, "refs/heads/", &branch_name)) return; branch = branch_get(branch_name); - if (!format_tracking_info(branch, &sb)) + if (!format_tracking_info(branch, &sb, s->ahead_behind_flags)) return; i = 0; -- cgit 1.2.3-korg