Merge branch 'jh/status-aheadbehind'
authorJunio C Hamano <gitster@pobox.com>
Tue, 9 Jul 2019 22:25:46 +0000 (15:25 -0700)
committerJunio C Hamano <gitster@pobox.com>
Tue, 9 Jul 2019 22:25:46 +0000 (15:25 -0700)
"git status" can be told a non-standard default value for the
"--[no-]ahead-behind" option with a new configuration variable
status.aheadBehind.

* jh/status-aheadbehind:
  status: ignore status.aheadbehind in porcelain formats
  status: warn when a/b calculation takes too long
  status: add status.aheadbehind setting

1  2 
Documentation/config/advice.txt
advice.c
builtin/commit.c
wt-status.c

@@@ -37,13 -37,18 +37,19 @@@ 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
                the template shown when writing commit messages in
                linkgit:git-commit[1], and in the help message shown
 -              by linkgit:git-checkout[1] when switching branch.
 +              by linkgit:git-switch[1] or
 +              linkgit:git-checkout[1] when switching branch.
        statusUoption::
                Advise to consider using the `-u` option to linkgit:git-status[1]
                when the command takes more than 2 seconds to enumerate untracked
                your information is guessed from the system username and
                domain name.
        detachedHead::
 -              Advice shown when you used linkgit:git-checkout[1] to
 -              move to the detach HEAD state, to instruct how to create
 -              a local branch after the fact.
 +              Advice shown when you used
 +              linkgit:git-switch[1] or linkgit:git-checkout[1]
 +              to move to the detach HEAD state, to instruct how to
 +              create a local branch after the fact.
        checkoutAmbiguousRemoteBranchName::
                Advice shown when the argument to
 -              linkgit:git-checkout[1] ambiguously resolves to a
 +              linkgit:git-checkout[1] and linkgit:git-switch[1]
 +              ambiguously resolves to a
                remote tracking branch on more than one remote in
                situations where an unambiguous argument would have
                otherwise caused a remote-tracking branch to be
diff --combined advice.c
+++ b/advice.c
@@@ -12,6 -12,7 +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 +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 },
@@@ -193,22 -195,13 +195,22 @@@ void NORETURN die_conclude_merge(void
  void detach_advice(const char *new_name)
  {
        const char *fmt =
 -      _("Note: checking out '%s'.\n\n"
 +      _("Note: switching to '%s'.\n"
 +      "\n"
        "You are in 'detached HEAD' state. You can look around, make experimental\n"
        "changes and commit them, and you can discard any commits you make in this\n"
 -      "state without impacting any branches by performing another checkout.\n\n"
 +      "state without impacting any branches by switching back to a branch.\n"
 +      "\n"
        "If you want to create a new branch to retain commits you create, you may\n"
 -      "do so (now or later) by using -b with the checkout command again. Example:\n\n"
 -      "  git checkout -b <new-branch-name>\n\n");
 +      "do so (now or later) by using -c with the switch command. Example:\n"
 +      "\n"
 +      "  git switch -c <new-branch-name>\n"
 +      "\n"
 +      "Or undo this operation with:\n"
 +      "\n"
 +      "  git switch -\n"
 +      "\n"
 +      "Turn off this advice by setting config variable advice.detachedHead to false\n\n");
  
        fprintf(stderr, fmt, new_name);
  }
diff --combined builtin/commit.c
@@@ -1078,9 -1078,11 +1078,11 @@@ static const char *read_commit_message(
  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)
        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 +1259,10 @@@ static int git_status_config(const cha
                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;
@@@ -1658,7 -1675,7 +1675,7 @@@ int cmd_commit(int argc, const char **a
                die("%s", err.buf);
        }
  
 -      sequencer_post_commit_cleanup(the_repository);
 +      sequencer_post_commit_cleanup(the_repository, 0);
        unlink(git_path_merge_head(the_repository));
        unlink(git_path_merge_msg(the_repository));
        unlink(git_path_merge_mode(the_repository));
        if (commit_index_files())
                die(_("repository has been updated, but unable to write\n"
                      "new_index file. Check that disk is not full and quota is\n"
 -                    "not exceeded, and then \"git reset HEAD\" to recover."));
 +                    "not exceeded, and then \"git restore --staged :/\" to recover."));
  
 -      if (git_env_bool(GIT_TEST_COMMIT_GRAPH, 0))
 -              write_commit_graph_reachable(get_object_directory(), 0, 0);
 +      if (git_env_bool(GIT_TEST_COMMIT_GRAPH, 0) &&
 +          write_commit_graph_reachable(get_object_directory(), 0))
 +              return 1;
  
        repo_rerere(the_repository, 0);
        run_command_v_opt(argv_gc_auto, RUN_GIT_CMD);
diff --combined wt-status.c
@@@ -19,6 -19,8 +19,8 @@@
  #include "lockfile.h"
  #include "sequencer.h"
  
+ #define AB_DELAY_WARNING_IN_MS (2 * 1000)
  static const char cut_line[] =
  "------------------------ >8 ------------------------\n";
  
@@@ -179,15 -181,9 +181,15 @@@ static void wt_longstatus_print_unmerge
                return;
        if (s->whence != FROM_COMMIT)
                ;
 -      else if (!s->is_initial)
 -              status_printf_ln(s, c, _("  (use \"git reset %s <file>...\" to unstage)"), s->reference);
 -      else
 +      else if (!s->is_initial) {
 +              if (!strcmp(s->reference, "HEAD"))
 +                      status_printf_ln(s, c,
 +                                       _("  (use \"git restore --staged <file>...\" to unstage)"));
 +              else
 +                      status_printf_ln(s, c,
 +                                       _("  (use \"git restore --source=%s --staged <file>...\" to unstage)"),
 +                                       s->reference);
 +      } else
                status_printf_ln(s, c, _("  (use \"git rm --cached <file>...\" to unstage)"));
  
        if (!both_deleted) {
@@@ -212,15 -208,9 +214,15 @@@ static void wt_longstatus_print_cached_
                return;
        if (s->whence != FROM_COMMIT)
                ; /* NEEDSWORK: use "git reset --unresolve"??? */
 -      else if (!s->is_initial)
 -              status_printf_ln(s, c, _("  (use \"git reset %s <file>...\" to unstage)"), s->reference);
 -      else
 +      else if (!s->is_initial) {
 +              if (!strcmp(s->reference, "HEAD"))
 +                      status_printf_ln(s, c
 +                                       , _("  (use \"git restore --staged <file>...\" to unstage)"));
 +              else
 +                      status_printf_ln(s, c,
 +                                       _("  (use \"git restore --source=%s --staged <file>...\" to unstage)"),
 +                                       s->reference);
 +      } else
                status_printf_ln(s, c, _("  (use \"git rm --cached <file>...\" to unstage)"));
        status_printf_ln(s, c, "%s", "");
  }
@@@ -238,7 -228,7 +240,7 @@@ static void wt_longstatus_print_dirty_h
                status_printf_ln(s, c, _("  (use \"git add <file>...\" to update what will be committed)"));
        else
                status_printf_ln(s, c, _("  (use \"git add/rm <file>...\" to update what will be committed)"));
 -      status_printf_ln(s, c, _("  (use \"git checkout -- <file>...\" to discard changes in working directory)"));
 +      status_printf_ln(s, c, _("  (use \"git restore <file>...\" to discard changes in working directory)"));
        if (has_dirty_submodules)
                status_printf_ln(s, c, _("  (commit or discard the untracked or modified content in submodules)"));
        status_printf_ln(s, c, "%s", "");
@@@ -1097,14 -1087,29 +1099,29 @@@ static void wt_longstatus_print_trackin
        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;
@@@ -1688,9 -1693,9 +1705,9 @@@ static void wt_longstatus_print(struct 
                        } else if (s->state.detached_from) {
                                branch_name = s->state.detached_from;
                                if (s->state.detached_at)
 -                                      on_what = _("HEAD detached at ");
 +                                      on_what = HEAD_DETACHED_AT;
                                else
 -                                      on_what = _("HEAD detached from ");
 +                                      on_what = HEAD_DETACHED_FROM;
                        } else {
                                branch_name = "";
                                on_what = _("Not currently on any branch.");