Merge branch 'pw/no-editor-in-rebase-i-implicit'
authorJunio C Hamano <gitster@pobox.com>
Tue, 5 Feb 2019 22:26:17 +0000 (14:26 -0800)
committerJunio C Hamano <gitster@pobox.com>
Tue, 5 Feb 2019 22:26:17 +0000 (14:26 -0800)
When GIT_SEQUENCE_EDITOR is set, the command was incorrectly
started when modes of "git rebase" that implicitly uses the
machinery for the interactive rebase are run, which has been
corrected.

* pw/no-editor-in-rebase-i-implicit:
implicit interactive rebase: don't run sequence editor

1  2 
builtin/rebase.c
git-legacy-rebase.sh
diff --combined builtin/rebase.c
index 774264bae8dba0815f95e3000d10ff495c477569,2cb3f4a6a9473c9f92e1f682c21907cbf95ecd0f..fdeb41e899382f3a4d7202e68768d0c1c8f755fb
@@@ -104,7 -104,6 +104,7 @@@ struct rebase_options 
        int rebase_merges, rebase_cousins;
        char *strategy, *strategy_opts;
        struct strbuf git_format_patch_opt;
 +      int reschedule_failed_exec;
  };
  
  static int is_interactive(struct rebase_options *opts)
@@@ -355,7 -354,8 +355,8 @@@ static int run_specific_rebase(struct r
                argv_array_pushf(&child.env_array, "GIT_CHERRY_PICK_HELP=%s",
                                 resolvemsg);
                if (!(opts->flags & REBASE_INTERACTIVE_EXPLICIT)) {
-                       argv_array_push(&child.env_array, "GIT_EDITOR=:");
+                       argv_array_push(&child.env_array,
+                                       "GIT_SEQUENCE_EDITOR=:");
                        opts->autosquash = 0;
                }
  
                        argv_array_push(&child.args, opts->gpg_sign_opt);
                if (opts->signoff)
                        argv_array_push(&child.args, "--signoff");
 +              if (opts->reschedule_failed_exec)
 +                      argv_array_push(&child.args, "--reschedule-failed-exec");
  
                status = run_command(&child);
                goto finished_rebase;
        if (is_interactive(opts) &&
            !(opts->flags & REBASE_INTERACTIVE_EXPLICIT)) {
                strbuf_addstr(&script_snippet,
-                             "GIT_EDITOR=:; export GIT_EDITOR; ");
+                             "GIT_SEQUENCE_EDITOR=:; export GIT_SEQUENCE_EDITOR; ");
                opts->autosquash = 0;
        }
  
@@@ -533,7 -531,6 +534,7 @@@ finished_rebase
  
  #define RESET_HEAD_DETACH (1<<0)
  #define RESET_HEAD_HARD (1<<1)
 +#define RESET_HEAD_RUN_POST_CHECKOUT_HOOK (1<<2)
  
  static int reset_head(struct object_id *oid, const char *action,
                      const char *switch_to_branch, unsigned flags,
  {
        unsigned detach_head = flags & RESET_HEAD_DETACH;
        unsigned reset_hard = flags & RESET_HEAD_HARD;
 +      unsigned run_hook = flags & RESET_HEAD_RUN_POST_CHECKOUT_HOOK;
        struct object_id head_oid;
        struct tree_desc desc[2] = { { NULL }, { NULL } };
        struct lock_file lock = LOCK_INIT;
        }
  
        tree = parse_tree_indirect(oid);
 -      prime_cache_tree(the_repository->index, tree);
 +      prime_cache_tree(the_repository, the_repository->index, tree);
  
        if (write_locked_index(the_repository->index, &lock, COMMIT_LOCK) < 0) {
                ret = error(_("could not write index"));
                        ret = update_ref(reflog_head, "HEAD", oid, NULL, 0,
                                         UPDATE_REFS_MSG_ON_ERR);
        }
 +      if (run_hook)
 +              run_hook_le(NULL, "post-checkout",
 +                          oid_to_hex(orig ? orig : &null_oid),
 +                          oid_to_hex(oid), "1", NULL);
  
  leave_reset_head:
        strbuf_release(&msg);
@@@ -683,11 -675,6 +684,11 @@@ static int rebase_config(const char *va
                return 0;
        }
  
 +      if (!strcmp(var, "rebase.reschedulefailedexec")) {
 +              opts->reschedule_failed_exec = git_config_bool(var, value);
 +              return 0;
 +      }
 +
        return git_default_config(var, value, data);
  }
  
@@@ -760,23 -747,6 +761,23 @@@ static int parse_opt_interactive(const 
        return 0;
  }
  
 +struct opt_y {
 +      struct string_list *list;
 +      struct rebase_options *options;
 +};
 +
 +static int parse_opt_y(const struct option *opt, const char *arg, int unset)
 +{
 +      struct opt_y *o = opt->value;
 +
 +      if (unset || !arg)
 +              return -1;
 +
 +      o->options->reschedule_failed_exec = 1;
 +      string_list_append(o->list, arg);
 +      return 0;
 +}
 +
  static void NORETURN error_on_missing_default_upstream(void)
  {
        struct branch *current_branch = branch_get(NULL);
@@@ -857,7 -827,6 +858,7 @@@ int cmd_rebase(int argc, const char **a
        struct string_list strategy_options = STRING_LIST_INIT_NODUP;
        struct object_id squash_onto;
        char *squash_onto_name = NULL;
 +      struct opt_y opt_y = { .list = &exec, .options = &options };
        struct option builtin_rebase_options[] = {
                OPT_STRING(0, "onto", &options.onto_name,
                           N_("revision"),
                OPT_STRING_LIST('x', "exec", &exec, N_("exec"),
                                N_("add exec lines after each commit of the "
                                   "editable list")),
 +              { OPTION_CALLBACK, 'y', NULL, &opt_y, N_("<cmd>"),
 +                      N_("same as --reschedule-failed-exec -x <cmd>"),
 +                      PARSE_OPT_NONEG, parse_opt_y },
                OPT_BOOL(0, "allow-empty-message",
                         &options.allow_empty_message,
                         N_("allow rebasing commits with empty messages")),
                                   "strategy")),
                OPT_BOOL(0, "root", &options.root,
                         N_("rebase all reachable commits up to the root(s)")),
 +              OPT_BOOL(0, "reschedule-failed-exec",
 +                       &options.reschedule_failed_exec,
 +                       N_("automatically re-schedule any `exec` that fails")),
                OPT_END(),
        };
        int i;
                                             &lock_file);
                rollback_lock_file(&lock_file);
  
 -              if (has_unstaged_changes(1)) {
 +              if (has_unstaged_changes(the_repository, 1)) {
                        puts(_("You must edit all merge conflicts and then\n"
                               "mark them as resolved using git add"));
                        exit(1);
                options.action = "skip";
                set_reflog_action(&options);
  
 -              rerere_clear(&merge_rr);
 +              rerere_clear(the_repository, &merge_rr);
                string_list_clear(&merge_rr, 1);
  
                if (reset_head(NULL, "reset", NULL, RESET_HEAD_HARD,
                               NULL, NULL) < 0)
                        die(_("could not discard worktree changes"));
 -              remove_branch_state();
 +              remove_branch_state(the_repository);
                if (read_basic_state(&options))
                        exit(1);
                goto run_rebase;
                options.action = "abort";
                set_reflog_action(&options);
  
 -              rerere_clear(&merge_rr);
 +              rerere_clear(the_repository, &merge_rr);
                string_list_clear(&merge_rr, 1);
  
                if (read_basic_state(&options))
                               NULL, NULL) < 0)
                        die(_("could not move back to %s"),
                            oid_to_hex(&options.orig_head));
 -              remove_branch_state();
 +              remove_branch_state(the_repository);
                ret = finish_rebase(&options);
                goto cleanup;
        }
                break;
        }
  
 +      if (options.reschedule_failed_exec && !is_interactive(&options))
 +              die(_("--reschedule-failed-exec requires an interactive rebase"));
 +
        if (options.git_am_opts.argc) {
                /* all am options except -q are compatible only with --am */
                for (i = options.git_am_opts.argc - 1; i >= 0; i--)
                options.flags |= REBASE_FORCE;
        }
  
 -      if (options.type == REBASE_PRESERVE_MERGES)
 +      if (options.type == REBASE_PRESERVE_MERGES) {
                /*
                 * Note: incompatibility with --signoff handled in signoff block above
                 * Note: incompatibility with --interactive is just a strong warning;
                        die(_("error: cannot combine '--preserve-merges' with "
                              "'--rebase-merges'"));
  
 +              if (options.reschedule_failed_exec)
 +                      die(_("error: cannot combine '--preserve-merges' with "
 +                            "'--reschedule-failed-exec'"));
 +      }
 +
        if (options.rebase_merges) {
                if (strategy_options.nr)
                        die(_("error: cannot combine '--rebase-merges' with "
                        update_index_if_able(&the_index, &lock_file);
                rollback_lock_file(&lock_file);
  
 -              if (has_unstaged_changes(1) || has_uncommitted_changes(1)) {
 +              if (has_unstaged_changes(the_repository, 1) ||
 +                  has_uncommitted_changes(the_repository, 1)) {
                        const char *autostash =
                                state_dir_path("autostash", &options);
                        struct child_process stash = CHILD_PROCESS_INIT;
                }
        }
  
 -      if (require_clean_work_tree("rebase",
 +      if (require_clean_work_tree(the_repository, "rebase",
                                    _("Please commit or stash them."), 1, 1)) {
                ret = 1;
                goto cleanup;
                                            getenv(GIT_REFLOG_ACTION_ENVIRONMENT),
                                            options.switch_to);
                                if (reset_head(&oid, "checkout",
 -                                             options.head_name, 0,
 +                                             options.head_name,
 +                                             RESET_HEAD_RUN_POST_CHECKOUT_HOOK,
                                               NULL, buf.buf) < 0) {
                                        ret = !!error(_("could not switch to "
                                                        "%s"),
        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))
 +                     RESET_HEAD_DETACH | RESET_HEAD_RUN_POST_CHECKOUT_HOOK,
 +                     NULL, msg.buf))
                die(_("Could not detach HEAD"));
        strbuf_release(&msg);
  
diff --combined git-legacy-rebase.sh
index 3bb0682db5897f514d267946979fa2b9cfa43f20,41f7799bb52c28cfb3b2a7030a150df428d2b5ea..f03ca6c49a69f561478085b5c0e83e618c077921
@@@ -26,7 -26,6 +26,7 @@@ f,force-rebase!    cherry-pick all comm
  m,merge!           use merging strategies to rebase
  i,interactive!     let the user edit the list of commits to rebase
  x,exec=!           add exec lines after each commit of the editable list
 +y=!                same as --reschedule-failed-exec -x
  k,keep-empty     preserve empty commits during rebase
  allow-empty-message allow rebasing commits with empty messages
  stat!              display a diffstat of what changed upstream
@@@ -49,7 -48,6 +49,7 @@@ skip!              skip current patch a
  edit-todo!         edit the todo list during an interactive rebase
  quit!              abort but keep HEAD where it is
  show-current-patch! show the patch file being applied or merged
 +reschedule-failed-exec automatically reschedule failed exec commands
  "
  . git-sh-setup
  set_reflog_action rebase
@@@ -94,14 -92,11 +94,14 @@@ autosquash
  keep_empty=
  allow_empty_message=--allow-empty-message
  signoff=
 +reschedule_failed_exec=
  test "$(git config --bool rebase.autosquash)" = "true" && autosquash=t
  case "$(git config --bool commit.gpgsign)" in
  true) gpg_sign_opt=-S ;;
  *)    gpg_sign_opt= ;;
  esac
 +test "$(git config --bool rebase.reschedulefailedexec)" = "true" &&
 +reschedule_failed_exec=--reschedule-failed-exec
  . git-rebase--common
  
  read_basic_state () {
                signoff="$(cat "$state_dir"/signoff)"
                force_rebase=t
        }
 +      test -f "$state_dir"/reschedule-failed-exec &&
 +              reschedule_failed_exec=t
  }
  
  finish_rebase () {
@@@ -170,14 -163,13 +170,14 @@@ run_interactive () 
                "$allow_empty_message" "$autosquash" "$verbose" \
                "$force_rebase" "$onto_name" "$head_name" "$strategy" \
                "$strategy_opts" "$cmd" "$switch_to" \
 -              "$allow_rerere_autoupdate" "$gpg_sign_opt" "$signoff"
 +              "$allow_rerere_autoupdate" "$gpg_sign_opt" "$signoff" \
 +              "$reschedule_failed_exec"
  }
  
  run_specific_rebase () {
        if [ "$interactive_rebase" = implied ]; then
-               GIT_EDITOR=:
-               export GIT_EDITOR
+               GIT_SEQUENCE_EDITOR=:
+               export GIT_SEQUENCE_EDITOR
                autosquash=
        fi
  
                cmd="${cmd}exec ${1#--exec=}${LF}"
                test -z "$interactive_rebase" && interactive_rebase=implied
                ;;
 +      -y*)
 +              reschedule_failed_exec=--reschedule-failed-exec
 +              cmd="${cmd}exec ${1#-y}${LF}"
 +              test -z "$interactive_rebase" && interactive_rebase=implied
 +              ;;
        --interactive)
                interactive_rebase=explicit
                ;;
        --gpg-sign=*)
                gpg_sign_opt="-S${1#--gpg-sign=}"
                ;;
 +      --reschedule-failed-exec)
 +              reschedule_failed_exec=--reschedule-failed-exec
 +              ;;
 +      --no-reschedule-failed-exec)
 +              reschedule_failed_exec=
 +              ;;
        --)
                shift
                break
@@@ -553,9 -534,6 +553,9 @@@ the
        #       git-rebase.txt caveats with "unless you know what you are doing"
        test -n "$rebase_merges" &&
                die "$(gettext "error: cannot combine '--preserve-merges' with '--rebase-merges'")"
 +
 +      test -n "$reschedule_failed_exec" &&
 +              die "$(gettext "error: cannot combine '--preserve-merges' with '--reschedule-failed-exec'")"
  fi
  
  if test -n "$rebase_merges"