diff --git a/Documentation/config/advice.txt b/Documentation/config/advice.txt index d5fd05ce81..0103faa417 100644 --- a/Documentation/config/advice.txt +++ b/Documentation/config/advice.txt @@ -37,6 +37,12 @@ advice.*:: we can still suggest that the user push to either refs/heads/* or refs/tags/* based on the type of the source object. + statusAheadBehind:: + Shown when linkgit:git-status[1] computes the ahead/behind + counts for a local ref compared to its remote tracking ref, + and that calculation takes longer than expected. Will not + appear if `status.aheadBehind` is false or the option + `--no-ahead-behind` is given. statusHints:: Show directions on how to proceed from the current state in the output of linkgit:git-status[1], in diff --git a/Documentation/config/status.txt b/Documentation/config/status.txt index ed72fa7dae..0fc704ab80 100644 --- a/Documentation/config/status.txt +++ b/Documentation/config/status.txt @@ -12,6 +12,11 @@ status.branch:: Set to true to enable --branch by default in linkgit:git-status[1]. The option --no-branch takes precedence over this variable. +status.aheadBehind:: + Set to true to enable `--ahead-behind` and false to enable + `--no-ahead-behind` by default in linkgit:git-status[1] for + non-porcelain status formats. Defaults to true. + status.displayCommentPrefix:: If set to true, linkgit:git-status[1] will insert a comment prefix before each output line (starting with diff --git a/advice.c b/advice.c index b04709bfec..2d330d2b75 100644 --- a/advice.c +++ b/advice.c @@ -12,6 +12,7 @@ int advice_push_needs_force = 1; int advice_push_unqualified_ref_name = 1; int advice_status_hints = 1; int advice_status_u_option = 1; +int advice_status_ahead_behind_warning = 1; int advice_commit_before_merge = 1; int advice_reset_quiet_warning = 1; int advice_resolve_conflict = 1; @@ -68,6 +69,7 @@ static struct { { "pushUnqualifiedRefName", &advice_push_unqualified_ref_name }, { "statusHints", &advice_status_hints }, { "statusUoption", &advice_status_u_option }, + { "statusAheadBehindWarning", &advice_status_ahead_behind_warning }, { "commitBeforeMerge", &advice_commit_before_merge }, { "resetQuiet", &advice_reset_quiet_warning }, { "resolveConflict", &advice_resolve_conflict }, diff --git a/advice.h b/advice.h index e50f02cdfe..c86de9b9b8 100644 --- a/advice.h +++ b/advice.h @@ -12,6 +12,7 @@ extern int advice_push_needs_force; extern int advice_push_unqualified_ref_name; extern int advice_status_hints; extern int advice_status_u_option; +extern int advice_status_ahead_behind_warning; extern int advice_commit_before_merge; extern int advice_reset_quiet_warning; extern int advice_resolve_conflict; diff --git a/builtin/commit.c b/builtin/commit.c index 851b257867..3b561c2a75 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -1078,9 +1078,11 @@ static const char *read_commit_message(const char *name) static struct status_deferred_config { enum wt_status_format status_format; int show_branch; + enum ahead_behind_flags ahead_behind; } status_deferred_config = { STATUS_FORMAT_UNSPECIFIED, - -1 /* unspecified */ + -1, /* unspecified */ + AHEAD_BEHIND_UNSPECIFIED, }; static void finalize_deferred_config(struct wt_status *s) @@ -1107,6 +1109,17 @@ static void finalize_deferred_config(struct wt_status *s) if (s->show_branch < 0) s->show_branch = 0; + /* + * If the user did not give a "--[no]-ahead-behind" command + * line argument *AND* we will print in a human-readable format + * (short, long etc.) then we inherit from the status.aheadbehind + * config setting. In all other cases (and porcelain V[12] formats + * in particular), we inherit _FULL for backwards compatibility. + */ + if (use_deferred_config && + s->ahead_behind_flags == AHEAD_BEHIND_UNSPECIFIED) + s->ahead_behind_flags = status_deferred_config.ahead_behind; + if (s->ahead_behind_flags == AHEAD_BEHIND_UNSPECIFIED) s->ahead_behind_flags = AHEAD_BEHIND_FULL; } @@ -1246,6 +1259,10 @@ static int git_status_config(const char *k, const char *v, void *cb) status_deferred_config.show_branch = git_config_bool(k, v); return 0; } + if (!strcmp(k, "status.aheadbehind")) { + status_deferred_config.ahead_behind = git_config_bool(k, v); + return 0; + } if (!strcmp(k, "status.showstash")) { s->show_stash = git_config_bool(k, v); return 0; diff --git a/t/t6040-tracking-info.sh b/t/t6040-tracking-info.sh index 716283b274..febf63f28a 100755 --- a/t/t6040-tracking-info.sh +++ b/t/t6040-tracking-info.sh @@ -159,6 +159,19 @@ test_expect_success 'status -s -b --no-ahead-behind (diverged from upstream)' ' test_i18ncmp expect actual ' +cat >expect <<\EOF +## b1...origin/master [different] +EOF + +test_expect_success 'status.aheadbehind=false status -s -b (diverged from upstream)' ' + ( + cd test && + git checkout b1 >/dev/null && + git -c status.aheadbehind=false status -s -b | head -1 + ) >actual && + test_i18ncmp expect actual +' + cat >expect <<\EOF On branch b1 Your branch and 'origin/master' have diverged, @@ -174,6 +187,15 @@ test_expect_success 'status --long --branch' ' test_i18ncmp expect actual ' +test_expect_success 'status --long --branch' ' + ( + cd test && + git checkout b1 >/dev/null && + git -c status.aheadbehind=true 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. @@ -188,6 +210,15 @@ test_expect_success 'status --long --branch --no-ahead-behind' ' test_i18ncmp expect actual ' +test_expect_success 'status.aheadbehind=false status --long --branch' ' + ( + cd test && + git checkout b1 >/dev/null && + git -c status.aheadbehind=false status --long -b | head -2 + ) >actual && + test_i18ncmp expect actual +' + cat >expect <<\EOF ## b5...brokenbase [gone] EOF diff --git a/t/t7064-wtstatus-pv2.sh b/t/t7064-wtstatus-pv2.sh index 11eccc231a..537787e598 100755 --- a/t/t7064-wtstatus-pv2.sh +++ b/t/t7064-wtstatus-pv2.sh @@ -445,6 +445,14 @@ test_expect_success 'verify --[no-]ahead-behind with V2 format' ' EOF git status --ahead-behind --porcelain=v2 --branch --untracked-files=all >actual && + test_cmp expect actual && + + # Confirm that "status.aheadbehind" DOES NOT work on V2 format. + git -c status.aheadbehind=false status --porcelain=v2 --branch --untracked-files=all >actual && + test_cmp expect actual && + + # Confirm that "status.aheadbehind" DOES NOT work on V2 format. + git -c status.aheadbehind=true status --porcelain=v2 --branch --untracked-files=all >actual && test_cmp expect actual ) ' diff --git a/wt-status.c b/wt-status.c index dd5270647e..7d776f8a97 100644 --- a/wt-status.c +++ b/wt-status.c @@ -19,6 +19,8 @@ #include "lockfile.h" #include "sequencer.h" +#define AB_DELAY_WARNING_IN_MS (2 * 1000) + static const char cut_line[] = "------------------------ >8 ------------------------\n"; @@ -1097,14 +1099,29 @@ static void wt_longstatus_print_tracking(struct wt_status *s) struct branch *branch; char comment_line_string[3]; int i; + uint64_t t_begin = 0; assert(s->branch && !s->is_initial); if (!skip_prefix(s->branch, "refs/heads/", &branch_name)) return; branch = branch_get(branch_name); + + t_begin = getnanotime(); + if (!format_tracking_info(branch, &sb, s->ahead_behind_flags)) return; + if (advice_status_ahead_behind_warning && + s->ahead_behind_flags == AHEAD_BEHIND_FULL) { + uint64_t t_delta_in_ms = (getnanotime() - t_begin) / 1000000; + if (t_delta_in_ms > AB_DELAY_WARNING_IN_MS) { + strbuf_addf(&sb, _("\n" + "It took %.2f seconds to compute the branch ahead/behind values.\n" + "You can use '--no-ahead-behind' to avoid this.\n"), + t_delta_in_ms / 1000.0); + } + } + i = 0; if (s->display_comment_prefix) { comment_line_string[i++] = comment_line_char;