Merge branch 'bw/rebase-autostash-keep-current-branch'
authorJunio C Hamano <gitster@pobox.com>
Mon, 30 Sep 2019 04:19:32 +0000 (13:19 +0900)
committerJunio C Hamano <gitster@pobox.com>
Mon, 30 Sep 2019 04:19:32 +0000 (13:19 +0900)
"git rebase --autostash <upstream> <branch>", when <branch> is
different from the current branch, incorrectly moved the tip of the
current branch, which has been corrected.

* bw/rebase-autostash-keep-current-branch:
builtin/rebase.c: Remove pointless message
builtin/rebase.c: make sure the active branch isn't moved when autostashing

1  2 
builtin/rebase.c
diff --combined builtin/rebase.c
index f730b15a787e8fd119d0b4a1a9e87fd0b04d571e,118205e48167d0e02eda34b7449d9502bf0ae9e2..4a20582e724b00a565ee8b0fd05137a11b27568c
@@@ -29,8 -29,8 +29,8 @@@
  #include "rebase-interactive.h"
  
  static char const * const builtin_rebase_usage[] = {
 -      N_("git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] "
 -              "[<upstream>] [<branch>]"),
 +      N_("git rebase [-i] [options] [--exec <cmd>] "
 +              "[--onto <newbase> | --keep-base] [<upstream> [<branch>]]"),
        N_("git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] "
                "--root [<branch>]"),
        N_("git rebase --continue | --abort | --skip | --edit-todo"),
@@@ -62,7 -62,7 +62,7 @@@ struct rebase_options 
        const char *onto_name;
        const char *revisions;
        const char *switch_to;
 -      int root;
 +      int root, root_with_onto;
        struct object_id *squash_onto;
        struct commit *restrict_revision;
        int dont_finish_rebase;
@@@ -374,7 -374,6 +374,7 @@@ static int run_rebase_interactive(struc
        flags |= abbreviate_commands ? TODO_LIST_ABBREVIATE_CMDS : 0;
        flags |= opts->rebase_merges ? TODO_LIST_REBASE_MERGES : 0;
        flags |= opts->rebase_cousins > 0 ? TODO_LIST_REBASE_COUSINS : 0;
 +      flags |= opts->root_with_onto ? TODO_LIST_ROOT_WITH_ONTO : 0;
        flags |= command == ACTION_SHORTEN_OIDS ? TODO_LIST_SHORTEN_IDS : 0;
  
        switch (command) {
@@@ -1261,44 -1260,24 +1261,44 @@@ static int is_linear_history(struct com
        return 1;
  }
  
 -static int can_fast_forward(struct commit *onto, struct object_id *head_oid,
 -                          struct object_id *merge_base)
 +static int can_fast_forward(struct commit *onto, struct commit *upstream,
 +                          struct commit *restrict_revision,
 +                          struct object_id *head_oid, struct object_id *merge_base)
  {
        struct commit *head = lookup_commit(the_repository, head_oid);
 -      struct commit_list *merge_bases;
 -      int res;
 +      struct commit_list *merge_bases = NULL;
 +      int res = 0;
  
        if (!head)
 -              return 0;
 +              goto done;
  
        merge_bases = get_merge_bases(onto, head);
 -      if (merge_bases && !merge_bases->next) {
 -              oidcpy(merge_base, &merge_bases->item->object.oid);
 -              res = oideq(merge_base, &onto->object.oid);
 -      } else {
 +      if (!merge_bases || merge_bases->next) {
                oidcpy(merge_base, &null_oid);
 -              res = 0;
 +              goto done;
        }
 +
 +      oidcpy(merge_base, &merge_bases->item->object.oid);
 +      if (!oideq(merge_base, &onto->object.oid))
 +              goto done;
 +
 +      if (restrict_revision && !oideq(&restrict_revision->object.oid, merge_base))
 +              goto done;
 +
 +      if (!upstream)
 +              goto done;
 +
 +      free_commit_list(merge_bases);
 +      merge_bases = get_merge_bases(upstream, head);
 +      if (!merge_bases || merge_bases->next)
 +              goto done;
 +
 +      if (!oideq(&onto->object.oid, &merge_bases->item->object.oid))
 +              goto done;
 +
 +      res = 1;
 +
 +done:
        free_commit_list(merge_bases);
        return res && is_linear_history(onto, head);
  }
@@@ -1397,7 -1376,6 +1397,7 @@@ int cmd_rebase(int argc, const char **a
        struct rebase_options options = REBASE_OPTIONS_INIT;
        const char *branch_name;
        int ret, flags, total_argc, in_progress = 0;
 +      int keep_base = 0;
        int ok_to_skip_pre_rebase = 0;
        struct strbuf msg = STRBUF_INIT;
        struct strbuf revisions = STRBUF_INIT;
                OPT_STRING(0, "onto", &options.onto_name,
                           N_("revision"),
                           N_("rebase onto given branch instead of upstream")),
 +              OPT_BOOL(0, "keep-base", &keep_base,
 +                       N_("use the merge-base of upstream and branch as the current base")),
                OPT_BOOL(0, "no-verify", &ok_to_skip_pre_rebase,
                         N_("allow pre-rebase hook to run")),
                OPT_NEGBIT('q', "quiet", &options.flags,
                warning(_("git rebase --preserve-merges is deprecated. "
                          "Use --rebase-merges instead."));
  
 +      if (keep_base) {
 +              if (options.onto_name)
 +                      die(_("cannot combine '--keep-base' with '--onto'"));
 +              if (options.root)
 +                      die(_("cannot combine '--keep-base' with '--root'"));
 +      }
 +
        if (action != ACTION_NONE && !in_progress)
                die(_("No rebase in progress?"));
        setenv(GIT_REFLOG_ACTION_ENVIRONMENT, "rebase", 0);
                              "'--reschedule-failed-exec'"));
        }
  
 -      if (options.rebase_merges) {
 -              if (strategy_options.nr)
 -                      die(_("cannot combine '--rebase-merges' with "
 -                            "'--strategy-option'"));
 -              if (options.strategy)
 -                      die(_("cannot combine '--rebase-merges' with "
 -                            "'--strategy'"));
 -      }
 -
        if (!options.root) {
                if (argc < 1) {
                        struct branch *branch;
                        options.squash_onto = &squash_onto;
                        options.onto_name = squash_onto_name =
                                xstrdup(oid_to_hex(&squash_onto));
 -              }
 +              } else
 +                      options.root_with_onto = 1;
 +
                options.upstream_name = NULL;
                options.upstream = NULL;
                if (argc > 1)
        }
  
        /* Make sure the branch to rebase onto is valid. */
 -      if (!options.onto_name)
 +      if (keep_base) {
 +              strbuf_reset(&buf);
 +              strbuf_addstr(&buf, options.upstream_name);
 +              strbuf_addstr(&buf, "...");
 +              options.onto_name = xstrdup(buf.buf);
 +      } else if (!options.onto_name)
                options.onto_name = options.upstream_name;
        if (strstr(options.onto_name, "...")) {
 -              if (get_oid_mb(options.onto_name, &merge_base) < 0)
 -                      die(_("'%s': need exactly one merge base"),
 -                          options.onto_name);
 +              if (get_oid_mb(options.onto_name, &merge_base) < 0) {
 +                      if (keep_base)
 +                              die(_("'%s': need exactly one merge base with branch"),
 +                                  options.upstream_name);
 +                      else
 +                              die(_("'%s': need exactly one merge base"),
 +                                  options.onto_name);
 +              }
                options.onto = lookup_commit_or_die(&merge_base,
                                                    options.onto_name);
        } else {
                                state_dir_path("autostash", &options);
                        struct child_process stash = CHILD_PROCESS_INIT;
                        struct object_id oid;
-                       struct commit *head =
-                               lookup_commit_reference(the_repository,
-                                                       &options.orig_head);
  
                        argv_array_pushl(&stash.args,
                                         "stash", "create", "autostash", NULL);
                                    options.state_dir);
                        write_file(autostash, "%s", oid_to_hex(&oid));
                        printf(_("Created autostash: %s\n"), buf.buf);
-                       if (reset_head(&head->object.oid, "reset --hard",
+                       if (reset_head(NULL, "reset --hard",
                                       NULL, RESET_HEAD_HARD, NULL, NULL) < 0)
                                die(_("could not reset --hard"));
-                       printf(_("HEAD is now at %s"),
-                              find_unique_abbrev(&head->object.oid,
-                                                 DEFAULT_ABBREV));
-                       strbuf_reset(&buf);
-                       pp_commit_easy(CMIT_FMT_ONELINE, head, &buf);
-                       if (buf.len > 0)
-                               printf(" %s", buf.buf);
-                       putchar('\n');
  
                        if (discard_index(the_repository->index) < 0 ||
                                repo_read_index(the_repository) < 0)
  
        /*
         * Check if we are already based on onto with linear history,
 -       * but this should be done only when upstream and onto are the same
 -       * and if this is not an interactive rebase.
 +       * in which case we could fast-forward without replacing the commits
 +       * with new commits recreated by replaying their changes. This
 +       * optimization must not be done if this is an interactive rebase.
         */
 -      if (can_fast_forward(options.onto, &options.orig_head, &merge_base) &&
 -          !is_interactive(&options) && !options.restrict_revision &&
 -          options.upstream &&
 -          !oidcmp(&options.upstream->object.oid, &options.onto->object.oid)) {
 +      if (can_fast_forward(options.onto, options.upstream, options.restrict_revision,
 +                  &options.orig_head, &merge_base) &&
 +          !is_interactive(&options)) {
                int flag;
  
                if (!(options.flags & REBASE_FORCE)) {