Merge branch 'js/rebase-stat-unrelated-fix'
authorJunio C Hamano <gitster@pobox.com>
Sat, 1 Dec 2018 12:41:42 +0000 (21:41 +0900)
committerJunio C Hamano <gitster@pobox.com>
Sat, 1 Dec 2018 12:41:42 +0000 (21:41 +0900)
"git rebase --stat" to transplant a piece of history onto a totally
unrelated history were not working before and silently showed wrong
result. With the recent reimplementation in C, it started to instead
die with an error message, as the original logic was not prepared
to cope with this case. This has now been fixed.

* js/rebase-stat-unrelated-fix:
rebase --stat: fix when rebasing to an unrelated history

1  2 
builtin/rebase.c
t/t3406-rebase-message.sh
diff --combined builtin/rebase.c
index ba0c3c954bf3b760bc4c64569a8bcaf6f9bab60d,1c6f817f4b7197114729a367c97d1950ac3080a5..c87da417fb387570bd3af27e0f5479b628012953
@@@ -776,23 -776,6 +776,23 @@@ static void NORETURN error_on_missing_d
        exit(1);
  }
  
 +static void set_reflog_action(struct rebase_options *options)
 +{
 +      const char *env;
 +      struct strbuf buf = STRBUF_INIT;
 +
 +      if (!is_interactive(options))
 +              return;
 +
 +      env = getenv(GIT_REFLOG_ACTION_ENVIRONMENT);
 +      if (env && strcmp("rebase", env))
 +              return; /* only override it if it is "rebase" */
 +
 +      strbuf_addf(&buf, "rebase -i (%s)", options->action);
 +      setenv(GIT_REFLOG_ACTION_ENVIRONMENT, buf.buf, 1);
 +      strbuf_release(&buf);
 +}
 +
  int cmd_rebase(int argc, const char **argv, const char *prefix)
  {
        struct rebase_options options = {
  
        if (action != NO_ACTION && !in_progress)
                die(_("No rebase in progress?"));
 +      setenv(GIT_REFLOG_ACTION_ENVIRONMENT, "rebase", 0);
  
        if (action == ACTION_EDIT_TODO && !is_interactive(&options))
                die(_("The --edit-todo action can only be used during "
                int fd;
  
                options.action = "continue";
 +              set_reflog_action(&options);
  
                /* Sanity check */
                if (get_oid("HEAD", &head))
                struct string_list merge_rr = STRING_LIST_INIT_DUP;
  
                options.action = "skip";
 +              set_reflog_action(&options);
  
                rerere_clear(&merge_rr);
                string_list_clear(&merge_rr, 1);
        case ACTION_ABORT: {
                struct string_list merge_rr = STRING_LIST_INIT_DUP;
                options.action = "abort";
 +              set_reflog_action(&options);
  
                rerere_clear(&merge_rr);
                string_list_clear(&merge_rr, 1);
                                }
  
                                strbuf_reset(&buf);
 -                              strbuf_addf(&buf, "rebase: checkout %s",
 +                              strbuf_addf(&buf, "%s: checkout %s",
 +                                          getenv(GIT_REFLOG_ACTION_ENVIRONMENT),
                                            options.switch_to);
                                if (reset_head(&oid, "checkout",
                                               options.head_name, 0,
 -                                             NULL, NULL) < 0) {
 +                                             NULL, buf.buf) < 0) {
                                        ret = !!error(_("could not switch to "
                                                        "%s"),
                                                      options.switch_to);
        if (options.flags & REBASE_DIFFSTAT) {
                struct diff_options opts;
  
-               if (options.flags & REBASE_VERBOSE)
-                       printf(_("Changes from %s to %s:\n"),
-                               oid_to_hex(&merge_base),
-                               oid_to_hex(&options.onto->object.oid));
+               if (options.flags & REBASE_VERBOSE) {
+                       if (is_null_oid(&merge_base))
+                               printf(_("Changes to %s:\n"),
+                                      oid_to_hex(&options.onto->object.oid));
+                       else
+                               printf(_("Changes from %s to %s:\n"),
+                                      oid_to_hex(&merge_base),
+                                      oid_to_hex(&options.onto->object.oid));
+               }
  
                /* We want color (if set), but no pager */
                diff_setup(&opts);
                        DIFF_FORMAT_SUMMARY | DIFF_FORMAT_DIFFSTAT;
                opts.detect_rename = DIFF_DETECT_RENAME;
                diff_setup_done(&opts);
-               diff_tree_oid(&merge_base, &options.onto->object.oid,
-                             "", &opts);
+               diff_tree_oid(is_null_oid(&merge_base) ?
+                             the_hash_algo->empty_tree : &merge_base,
+                             &options.onto->object.oid, "", &opts);
                diffcore_std(&opts);
                diff_flush(&opts);
        }
                printf(_("First, rewinding head to replay your work on top of "
                         "it...\n"));
  
 -      strbuf_addf(&msg, "rebase: checkout %s", options.onto_name);
 +      strbuf_addf(&msg, "%s: checkout %s",
 +                  getenv(GIT_REFLOG_ACTION_ENVIRONMENT), options.onto_name);
        if (reset_head(&options.onto->object.oid, "checkout", NULL,
                       RESET_HEAD_DETACH, NULL, msg.buf))
                die(_("Could not detach HEAD"));
index db8505eb86aaceba0412d70eac4a285821535522,c2c9950568f27e17d49d166c647dfc8794dd1a35..f64b130cb805bbca8475f3c262693fa0faa2cafe
@@@ -91,30 -91,14 +91,40 @@@ test_expect_success 'error out early up
        test_i18ngrep "Invalid whitespace option" err
  '
  
 +test_expect_success 'GIT_REFLOG_ACTION' '
 +      git checkout start &&
 +      test_commit reflog-onto &&
 +      git checkout -b reflog-topic start &&
 +      test_commit reflog-to-rebase &&
 +
 +      git rebase reflog-onto &&
 +      git log -g --format=%gs -3 >actual &&
 +      cat >expect <<-\EOF &&
 +      rebase finished: returning to refs/heads/reflog-topic
 +      rebase: reflog-to-rebase
 +      rebase: checkout reflog-onto
 +      EOF
 +      test_cmp expect actual &&
 +
 +      git checkout -b reflog-prefix reflog-to-rebase &&
 +      GIT_REFLOG_ACTION=change-the-reflog git rebase reflog-onto &&
 +      git log -g --format=%gs -3 >actual &&
 +      cat >expect <<-\EOF &&
 +      rebase finished: returning to refs/heads/reflog-prefix
 +      change-the-reflog: reflog-to-rebase
 +      change-the-reflog: checkout reflog-onto
 +      EOF
 +      test_cmp expect actual
 +'
 +
+ test_expect_success 'rebase -i onto unrelated history' '
+       git init unrelated &&
+       test_commit -C unrelated 1 &&
+       git -C unrelated remote add -f origin "$PWD" &&
+       git -C unrelated branch --set-upstream-to=origin/master &&
+       git -C unrelated -c core.editor=true rebase -i -v --stat >actual &&
+       test_i18ngrep "Changes to " actual &&
+       test_i18ngrep "5 files changed" actual
+ '
  test_done