From: Junio C Hamano Date: Sat, 9 Feb 2019 04:44:52 +0000 (-0800) Subject: Merge branch 'js/rebase-i-redo-exec-fix' X-Git-Tag: v2.21.0-rc1~22 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/b966813e716504252ea26a635db21aa628e3dbd8?ds=inline;hp=-c Merge branch 'js/rebase-i-redo-exec-fix' For "rebase -i --reschedule-failed-exec", we do not want the "-y" shortcut after all. * js/rebase-i-redo-exec-fix: Revert "rebase: introduce a shortcut for --reschedule-failed-exec" --- b966813e716504252ea26a635db21aa628e3dbd8 diff --combined Documentation/git-rebase.txt index 7e695b30e4,174be5b1f0..daa16403ec --- a/Documentation/git-rebase.txt +++ b/Documentation/git-rebase.txt @@@ -462,12 -462,6 +462,6 @@@ without an explicit `--interactive` + See also INCOMPATIBLE OPTIONS below. - -y :: - This is the same as passing `--reschedule-failed-exec` before - `-x `, i.e. it appends the specified `exec` command and - turns on the mode where failed `exec` commands are automatically - rescheduled. - --root:: Rebase all commits reachable from , instead of limiting them with an . This allows you to rebase @@@ -515,7 -509,15 +509,7 @@@ See also INCOMPATIBLE OPTIONS below INCOMPATIBLE OPTIONS -------------------- -git-rebase has many flags that are incompatible with each other, -predominantly due to the fact that it has three different underlying -implementations: - - * one based on linkgit:git-am[1] (the default) - * one based on git-merge-recursive (merge backend) - * one based on linkgit:git-cherry-pick[1] (interactive backend) - -Flags only understood by the am backend: +The following options: * --committer-date-is-author-date * --ignore-date @@@ -523,12 -525,15 +517,12 @@@ * --ignore-whitespace * -C -Flags understood by both merge and interactive backends: +are incompatible with the following options: * --merge * --strategy * --strategy-option * --allow-empty-message - -Flags only understood by the interactive backend: - * --[no-]autosquash * --rebase-merges * --preserve-merges @@@ -539,7 -544,7 +533,7 @@@ * --edit-todo * --root when used in combination with --onto -Other incompatible flag pairs: +In addition, the following pairs of options are incompatible: * --preserve-merges and --interactive * --preserve-merges and --signoff @@@ -570,9 -575,8 +564,9 @@@ it to keep commits that started empty Directory rename detection ~~~~~~~~~~~~~~~~~~~~~~~~~~ -The merge and interactive backends work fine with -directory rename detection. The am backend sometimes does not. +Directory rename heuristics are enabled in the merge and interactive +backends. Due to the lack of accurate tree information, directory +rename detection is disabled in the am backend. include::merge-strategies.txt[] @@@ -980,7 -984,7 +974,7 @@@ when the merge operation did not even s At this time, the `merge` command will *always* use the `recursive` merge strategy for regular merges, and `octopus` for octopus merges, -strategy, with no way to choose a different one. To work around +with no way to choose a different one. To work around this, an `exec` command can be used to call `git merge` explicitly, using the fact that the labels are worktree-local refs (the ref `refs/rewritten/onto` would correspond to the label `onto`, for example). diff --combined builtin/rebase.c index b9d61771ab,4839e52555..96efd40901 --- a/builtin/rebase.c +++ b/builtin/rebase.c @@@ -4,7 -4,6 +4,7 @@@ * Copyright (c) 2018 Pratik Karki */ +#define USE_THE_INDEX_COMPATIBILITY_MACROS #include "builtin.h" #include "run-command.h" #include "exec-cmd.h" @@@ -124,7 -123,7 +124,7 @@@ static void imply_interactive(struct re case REBASE_PRESERVE_MERGES: break; case REBASE_MERGE: - /* we silently *upgrade* --merge to --interactive if needed */ + /* we now implement --merge via --interactive */ default: opts->type = REBASE_INTERACTIVE; /* implied */ break; @@@ -187,7 -186,10 +187,7 @@@ static int read_basic_state(struct reba if (get_oid(buf.buf, &opts->orig_head)) return error(_("invalid orig-head: '%s'"), buf.buf); - strbuf_reset(&buf); - if (read_one(state_dir_path("quiet", opts), &buf)) - return -1; - if (buf.len) + if (file_exists(state_dir_path("quiet", opts))) opts->flags &= ~REBASE_NO_QUIET; else opts->flags |= REBASE_NO_QUIET; @@@ -245,37 -247,6 +245,37 @@@ return 0; } +static int write_basic_state(struct rebase_options *opts) +{ + write_file(state_dir_path("head-name", opts), "%s", + opts->head_name ? opts->head_name : "detached HEAD"); + write_file(state_dir_path("onto", opts), "%s", + opts->onto ? oid_to_hex(&opts->onto->object.oid) : ""); + write_file(state_dir_path("orig-head", opts), "%s", + oid_to_hex(&opts->orig_head)); + write_file(state_dir_path("quiet", opts), "%s", + opts->flags & REBASE_NO_QUIET ? "" : "t"); + if (opts->flags & REBASE_VERBOSE) + write_file(state_dir_path("verbose", opts), "%s", ""); + if (opts->strategy) + write_file(state_dir_path("strategy", opts), "%s", + opts->strategy); + if (opts->strategy_opts) + write_file(state_dir_path("strategy_opts", opts), "%s", + opts->strategy_opts); + if (opts->allow_rerere_autoupdate >= 0) + write_file(state_dir_path("allow_rerere_autoupdate", opts), + "-%s-rerere-autoupdate", + opts->allow_rerere_autoupdate ? "" : "-no"); + if (opts->gpg_sign_opt) + write_file(state_dir_path("gpg_sign_opt", opts), "%s", + opts->gpg_sign_opt); + if (opts->signoff) + write_file(state_dir_path("strategy", opts), "--signoff"); + + return 0; +} + static int apply_autostash(struct rebase_options *opts) { const char *path = state_dir_path("autostash", opts); @@@ -363,161 -334,6 +363,161 @@@ static void add_var(struct strbuf *buf } } +#define GIT_REFLOG_ACTION_ENVIRONMENT "GIT_REFLOG_ACTION" + +#define RESET_HEAD_DETACH (1<<0) +#define RESET_HEAD_HARD (1<<1) +#define RESET_HEAD_RUN_POST_CHECKOUT_HOOK (1<<2) +#define RESET_HEAD_REFS_ONLY (1<<3) + +static int reset_head(struct object_id *oid, const char *action, + const char *switch_to_branch, unsigned flags, + const char *reflog_orig_head, const char *reflog_head) +{ + unsigned detach_head = flags & RESET_HEAD_DETACH; + unsigned reset_hard = flags & RESET_HEAD_HARD; + unsigned run_hook = flags & RESET_HEAD_RUN_POST_CHECKOUT_HOOK; + unsigned refs_only = flags & RESET_HEAD_REFS_ONLY; + struct object_id head_oid; + struct tree_desc desc[2] = { { NULL }, { NULL } }; + struct lock_file lock = LOCK_INIT; + struct unpack_trees_options unpack_tree_opts; + struct tree *tree; + const char *reflog_action; + struct strbuf msg = STRBUF_INIT; + size_t prefix_len; + struct object_id *orig = NULL, oid_orig, + *old_orig = NULL, oid_old_orig; + int ret = 0, nr = 0; + + if (switch_to_branch && !starts_with(switch_to_branch, "refs/")) + BUG("Not a fully qualified branch: '%s'", switch_to_branch); + + if (!refs_only && hold_locked_index(&lock, LOCK_REPORT_ON_ERROR) < 0) { + ret = -1; + goto leave_reset_head; + } + + if ((!oid || !reset_hard) && get_oid("HEAD", &head_oid)) { + ret = error(_("could not determine HEAD revision")); + goto leave_reset_head; + } + + if (!oid) + oid = &head_oid; + + if (refs_only) + goto reset_head_refs; + + memset(&unpack_tree_opts, 0, sizeof(unpack_tree_opts)); + setup_unpack_trees_porcelain(&unpack_tree_opts, action); + unpack_tree_opts.head_idx = 1; + unpack_tree_opts.src_index = the_repository->index; + unpack_tree_opts.dst_index = the_repository->index; + unpack_tree_opts.fn = reset_hard ? oneway_merge : twoway_merge; + unpack_tree_opts.update = 1; + unpack_tree_opts.merge = 1; + if (!detach_head) + unpack_tree_opts.reset = 1; + + if (repo_read_index_unmerged(the_repository) < 0) { + ret = error(_("could not read index")); + goto leave_reset_head; + } + + if (!reset_hard && !fill_tree_descriptor(&desc[nr++], &head_oid)) { + ret = error(_("failed to find tree of %s"), + oid_to_hex(&head_oid)); + goto leave_reset_head; + } + + if (!fill_tree_descriptor(&desc[nr++], oid)) { + ret = error(_("failed to find tree of %s"), oid_to_hex(oid)); + goto leave_reset_head; + } + + if (unpack_trees(nr, desc, &unpack_tree_opts)) { + ret = -1; + goto leave_reset_head; + } + + tree = parse_tree_indirect(oid); + 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")); + goto leave_reset_head; + } + +reset_head_refs: + reflog_action = getenv(GIT_REFLOG_ACTION_ENVIRONMENT); + strbuf_addf(&msg, "%s: ", reflog_action ? reflog_action : "rebase"); + prefix_len = msg.len; + + if (!get_oid("ORIG_HEAD", &oid_old_orig)) + old_orig = &oid_old_orig; + if (!get_oid("HEAD", &oid_orig)) { + orig = &oid_orig; + if (!reflog_orig_head) { + strbuf_addstr(&msg, "updating ORIG_HEAD"); + reflog_orig_head = msg.buf; + } + update_ref(reflog_orig_head, "ORIG_HEAD", orig, old_orig, 0, + UPDATE_REFS_MSG_ON_ERR); + } else if (old_orig) + delete_ref(NULL, "ORIG_HEAD", old_orig, 0); + if (!reflog_head) { + strbuf_setlen(&msg, prefix_len); + strbuf_addstr(&msg, "updating HEAD"); + reflog_head = msg.buf; + } + if (!switch_to_branch) + ret = update_ref(reflog_head, "HEAD", oid, orig, + detach_head ? REF_NO_DEREF : 0, + UPDATE_REFS_MSG_ON_ERR); + else { + ret = update_ref(reflog_orig_head, switch_to_branch, oid, + NULL, 0, UPDATE_REFS_MSG_ON_ERR); + if (!ret) + ret = create_symref("HEAD", switch_to_branch, + reflog_head); + } + 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); + rollback_lock_file(&lock); + while (nr) + free((void *)desc[--nr].buffer); + return ret; +} + +static int move_to_original_branch(struct rebase_options *opts) +{ + struct strbuf orig_head_reflog = STRBUF_INIT, head_reflog = STRBUF_INIT; + int ret; + + if (!opts->head_name) + return 0; /* nothing to move back to */ + + if (!opts->onto) + BUG("move_to_original_branch without onto"); + + strbuf_addf(&orig_head_reflog, "rebase finished: %s onto %s", + opts->head_name, oid_to_hex(&opts->onto->object.oid)); + strbuf_addf(&head_reflog, "rebase finished: returning to %s", + opts->head_name); + ret = reset_head(NULL, "", opts->head_name, RESET_HEAD_REFS_ONLY, + orig_head_reflog.buf, head_reflog.buf); + + strbuf_release(&orig_head_reflog); + strbuf_release(&head_reflog); + return ret; +} + static const char *resolvemsg = N_("Resolve all conflicts manually, mark them as resolved with\n" "\"git add/rm \", then run \"git rebase --continue\".\n" @@@ -525,126 -341,6 +525,126 @@@ "To abort and get back to the state before \"git rebase\", run " "\"git rebase --abort\"."); +static int run_am(struct rebase_options *opts) +{ + struct child_process am = CHILD_PROCESS_INIT; + struct child_process format_patch = CHILD_PROCESS_INIT; + struct strbuf revisions = STRBUF_INIT; + int status; + char *rebased_patches; + + am.git_cmd = 1; + argv_array_push(&am.args, "am"); + + if (opts->action && !strcmp("continue", opts->action)) { + argv_array_push(&am.args, "--resolved"); + argv_array_pushf(&am.args, "--resolvemsg=%s", resolvemsg); + if (opts->gpg_sign_opt) + argv_array_push(&am.args, opts->gpg_sign_opt); + status = run_command(&am); + if (status) + return status; + + return move_to_original_branch(opts); + } + if (opts->action && !strcmp("skip", opts->action)) { + argv_array_push(&am.args, "--skip"); + argv_array_pushf(&am.args, "--resolvemsg=%s", resolvemsg); + status = run_command(&am); + if (status) + return status; + + return move_to_original_branch(opts); + } + if (opts->action && !strcmp("show-current-patch", opts->action)) { + argv_array_push(&am.args, "--show-current-patch"); + return run_command(&am); + } + + strbuf_addf(&revisions, "%s...%s", + oid_to_hex(opts->root ? + /* this is now equivalent to !opts->upstream */ + &opts->onto->object.oid : + &opts->upstream->object.oid), + oid_to_hex(&opts->orig_head)); + + rebased_patches = xstrdup(git_path("rebased-patches")); + format_patch.out = open(rebased_patches, + O_WRONLY | O_CREAT | O_TRUNC, 0666); + if (format_patch.out < 0) { + status = error_errno(_("could not open '%s' for writing"), + rebased_patches); + free(rebased_patches); + argv_array_clear(&am.args); + return status; + } + + format_patch.git_cmd = 1; + argv_array_pushl(&format_patch.args, "format-patch", "-k", "--stdout", + "--full-index", "--cherry-pick", "--right-only", + "--src-prefix=a/", "--dst-prefix=b/", "--no-renames", + "--no-cover-letter", "--pretty=mboxrd", "--topo-order", NULL); + if (opts->git_format_patch_opt.len) + argv_array_split(&format_patch.args, + opts->git_format_patch_opt.buf); + argv_array_push(&format_patch.args, revisions.buf); + if (opts->restrict_revision) + argv_array_pushf(&format_patch.args, "^%s", + oid_to_hex(&opts->restrict_revision->object.oid)); + + status = run_command(&format_patch); + if (status) { + unlink(rebased_patches); + free(rebased_patches); + argv_array_clear(&am.args); + + reset_head(&opts->orig_head, "checkout", opts->head_name, 0, + "HEAD", NULL); + error(_("\ngit encountered an error while preparing the " + "patches to replay\n" + "these revisions:\n" + "\n %s\n\n" + "As a result, git cannot rebase them."), + opts->revisions); + + strbuf_release(&revisions); + return status; + } + strbuf_release(&revisions); + + am.in = open(rebased_patches, O_RDONLY); + if (am.in < 0) { + status = error_errno(_("could not open '%s' for reading"), + rebased_patches); + free(rebased_patches); + argv_array_clear(&am.args); + return status; + } + + argv_array_pushv(&am.args, opts->git_am_opts.argv); + argv_array_push(&am.args, "--rebasing"); + argv_array_pushf(&am.args, "--resolvemsg=%s", resolvemsg); + argv_array_push(&am.args, "--patch-format=mboxrd"); + if (opts->allow_rerere_autoupdate > 0) + argv_array_push(&am.args, "--rerere-autoupdate"); + else if (opts->allow_rerere_autoupdate == 0) + argv_array_push(&am.args, "--no-rerere-autoupdate"); + if (opts->gpg_sign_opt) + argv_array_push(&am.args, opts->gpg_sign_opt); + status = run_command(&am); + unlink(rebased_patches); + free(rebased_patches); + + if (!status) { + return move_to_original_branch(opts); + } + + if (is_directory(opts->state_dir)) + write_basic_state(opts); + + return status; +} + static int run_specific_rebase(struct rebase_options *opts) { const char *argv[] = { NULL, NULL }; @@@ -659,8 -355,7 +659,8 @@@ 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; } @@@ -728,11 -423,6 +728,11 @@@ goto finished_rebase; } + if (opts->type == REBASE_AM) { + status = run_am(opts); + goto finished_rebase; + } + add_var(&script_snippet, "GIT_DIR", absolute_path(get_git_dir())); add_var(&script_snippet, "state_dir", opts->state_dir); @@@ -788,7 -478,7 +788,7 @@@ 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; } @@@ -797,6 -487,10 +797,6 @@@ backend = "git-rebase--am"; backend_func = "git_rebase__am"; break; - case REBASE_MERGE: - backend = "git-rebase--merge"; - backend_func = "git_rebase__merge"; - break; case REBASE_PRESERVE_MERGES: backend = "git-rebase--preserve-merges"; backend_func = "git_rebase__preserve_merges"; @@@ -835,6 -529,125 +835,6 @@@ finished_rebase return status ? -1 : 0; } -#define GIT_REFLOG_ACTION_ENVIRONMENT "GIT_REFLOG_ACTION" - -#define RESET_HEAD_DETACH (1<<0) -#define RESET_HEAD_HARD (1<<1) - -static int reset_head(struct object_id *oid, const char *action, - const char *switch_to_branch, unsigned flags, - const char *reflog_orig_head, const char *reflog_head) -{ - unsigned detach_head = flags & RESET_HEAD_DETACH; - unsigned reset_hard = flags & RESET_HEAD_HARD; - struct object_id head_oid; - struct tree_desc desc[2] = { { NULL }, { NULL } }; - struct lock_file lock = LOCK_INIT; - struct unpack_trees_options unpack_tree_opts; - struct tree *tree; - const char *reflog_action; - struct strbuf msg = STRBUF_INIT; - size_t prefix_len; - struct object_id *orig = NULL, oid_orig, - *old_orig = NULL, oid_old_orig; - int ret = 0, nr = 0; - - if (switch_to_branch && !starts_with(switch_to_branch, "refs/")) - BUG("Not a fully qualified branch: '%s'", switch_to_branch); - - if (hold_locked_index(&lock, LOCK_REPORT_ON_ERROR) < 0) { - ret = -1; - goto leave_reset_head; - } - - if ((!oid || !reset_hard) && get_oid("HEAD", &head_oid)) { - ret = error(_("could not determine HEAD revision")); - goto leave_reset_head; - } - - if (!oid) - oid = &head_oid; - - memset(&unpack_tree_opts, 0, sizeof(unpack_tree_opts)); - setup_unpack_trees_porcelain(&unpack_tree_opts, action); - unpack_tree_opts.head_idx = 1; - unpack_tree_opts.src_index = the_repository->index; - unpack_tree_opts.dst_index = the_repository->index; - unpack_tree_opts.fn = reset_hard ? oneway_merge : twoway_merge; - unpack_tree_opts.update = 1; - unpack_tree_opts.merge = 1; - if (!detach_head) - unpack_tree_opts.reset = 1; - - if (read_index_unmerged(the_repository->index) < 0) { - ret = error(_("could not read index")); - goto leave_reset_head; - } - - if (!reset_hard && !fill_tree_descriptor(&desc[nr++], &head_oid)) { - ret = error(_("failed to find tree of %s"), - oid_to_hex(&head_oid)); - goto leave_reset_head; - } - - if (!fill_tree_descriptor(&desc[nr++], oid)) { - ret = error(_("failed to find tree of %s"), oid_to_hex(oid)); - goto leave_reset_head; - } - - if (unpack_trees(nr, desc, &unpack_tree_opts)) { - ret = -1; - goto leave_reset_head; - } - - tree = parse_tree_indirect(oid); - prime_cache_tree(the_repository->index, tree); - - if (write_locked_index(the_repository->index, &lock, COMMIT_LOCK) < 0) { - ret = error(_("could not write index")); - goto leave_reset_head; - } - - reflog_action = getenv(GIT_REFLOG_ACTION_ENVIRONMENT); - strbuf_addf(&msg, "%s: ", reflog_action ? reflog_action : "rebase"); - prefix_len = msg.len; - - if (!get_oid("ORIG_HEAD", &oid_old_orig)) - old_orig = &oid_old_orig; - if (!get_oid("HEAD", &oid_orig)) { - orig = &oid_orig; - if (!reflog_orig_head) { - strbuf_addstr(&msg, "updating ORIG_HEAD"); - reflog_orig_head = msg.buf; - } - update_ref(reflog_orig_head, "ORIG_HEAD", orig, old_orig, 0, - UPDATE_REFS_MSG_ON_ERR); - } else if (old_orig) - delete_ref(NULL, "ORIG_HEAD", old_orig, 0); - if (!reflog_head) { - strbuf_setlen(&msg, prefix_len); - strbuf_addstr(&msg, "updating HEAD"); - reflog_head = msg.buf; - } - if (!switch_to_branch) - ret = update_ref(reflog_head, "HEAD", oid, orig, - detach_head ? REF_NO_DEREF : 0, - UPDATE_REFS_MSG_ON_ERR); - else { - ret = create_symref("HEAD", switch_to_branch, msg.buf); - if (!ret) - ret = update_ref(reflog_head, "HEAD", oid, NULL, 0, - UPDATE_REFS_MSG_ON_ERR); - } - -leave_reset_head: - strbuf_release(&msg); - rollback_lock_file(&lock); - while (nr) - free((void *)desc[--nr].buffer); - return ret; -} - static int rebase_config(const char *var, const char *value, void *data) { struct rebase_options *opts = data; @@@ -941,23 -754,6 +941,6 @@@ 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); @@@ -1005,19 -801,6 +988,19 @@@ static void set_reflog_action(struct re strbuf_release(&buf); } +static int check_exec_cmd(const char *cmd) +{ + if (strchr(cmd, '\n')) + return error(_("exec commands cannot contain newlines")); + + /* Does the command consist purely of whitespace? */ + if (!cmd[strspn(cmd, " \t\r\f\v")]) + return error(_("empty exec command")); + + return 0; +} + + int cmd_rebase(int argc, const char **argv, const char *prefix) { struct rebase_options options = { @@@ -1051,7 -834,6 +1034,6 @@@ 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"), @@@ -1129,9 -911,6 +1111,6 @@@ 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_(""), - N_("same as --reschedule-failed-exec -x "), - PARSE_OPT_NONEG, parse_opt_y }, OPT_BOOL(0, "allow-empty-message", &options.allow_empty_message, N_("allow rebasing commits with empty messages")), @@@ -1247,15 -1026,16 +1226,15 @@@ die(_("Cannot read HEAD")); fd = hold_locked_index(&lock_file, 0); - if (read_index(the_repository->index) < 0) + if (repo_read_index(the_repository) < 0) die(_("could not read index")); refresh_index(the_repository->index, REFRESH_QUIET, NULL, NULL, NULL); if (0 <= fd) - update_index_if_able(the_repository->index, - &lock_file); + repo_update_index_if_able(the_repository, &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); @@@ -1270,13 -1050,13 +1249,13 @@@ 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; @@@ -1286,7 -1066,7 +1265,7 @@@ 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)) @@@ -1296,7 -1076,7 +1275,7 @@@ 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; } @@@ -1361,10 -1141,6 +1340,10 @@@ } } + for (i = 0; i < exec.nr; i++) + if (check_exec_cmd(exec.items[i].string)) + exit(1); + if (!(options.flags & REBASE_NO_QUIET)) argv_array_push(&options.git_am_opts, "-q"); @@@ -1429,9 -1205,6 +1408,9 @@@ } } + if (options.type == REBASE_MERGE) + imply_interactive(&options, "--merge"); + if (options.root && !options.onto_name) imply_interactive(&options, "--root without --onto"); @@@ -1464,8 -1237,14 +1443,8 @@@ break; if (is_interactive(&options) && i >= 0) - die(_("error: cannot combine interactive options " - "(--interactive, --exec, --rebase-merges, " - "--preserve-merges, --keep-empty, --root + " - "--onto) with am options (%s)"), buf.buf); - if (options.type == REBASE_MERGE && i >= 0) - die(_("error: cannot combine merge options (--merge, " - "--strategy, --strategy-option) with am options " - "(%s)"), buf.buf); + die(_("cannot combine am options with either " + "interactive or merge options")); } if (options.signoff) { @@@ -1483,7 -1262,7 +1462,7 @@@ * git-rebase.txt caveats with "unless you know what you are doing" */ if (options.rebase_merges) - die(_("error: cannot combine '--preserve-merges' with " + die(_("cannot combine '--preserve-merges' with " "'--rebase-merges'")); if (options.reschedule_failed_exec) @@@ -1493,10 -1272,10 +1472,10 @@@ if (options.rebase_merges) { if (strategy_options.nr) - die(_("error: cannot combine '--rebase-merges' with " + die(_("cannot combine '--rebase-merges' with " "'--strategy-option'")); if (options.strategy) - die(_("error: cannot combine '--rebase-merges' with " + die(_("cannot combine '--rebase-merges' with " "'--strategy'")); } @@@ -1608,7 -1387,7 +1587,7 @@@ get_fork_point(options.upstream_name, head); } - if (read_index(the_repository->index) < 0) + if (repo_read_index(the_repository) < 0) die(_("could not read index")); if (options.autostash) { @@@ -1618,11 -1397,10 +1597,11 @@@ fd = hold_locked_index(&lock_file, 0); refresh_cache(REFRESH_QUIET); if (0 <= fd) - update_index_if_able(&the_index, &lock_file); + repo_update_index_if_able(the_repository, &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; @@@ -1663,12 -1441,12 +1642,12 @@@ putchar('\n'); if (discard_index(the_repository->index) < 0 || - read_index(the_repository->index) < 0) + repo_read_index(the_repository) < 0) die(_("could not read index")); } } - 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; @@@ -1706,8 -1484,7 +1685,8 @@@ 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"), @@@ -1781,8 -1558,7 +1760,8 @@@ 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 8d6c9aca65,37db5a7ca4..5c2c4e5276 --- a/git-legacy-rebase.sh +++ b/git-legacy-rebase.sh @@@ -26,7 -26,6 +26,6 @@@ 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 @@@ -118,7 -117,7 +117,7 @@@ read_basic_state () else orig_head=$(cat "$state_dir"/head) fi && - GIT_QUIET=$(cat "$state_dir"/quiet) && + test -f "$state_dir"/quiet && GIT_QUIET=t test -f "$state_dir"/verbose && verbose=t test -f "$state_dir"/strategy && strategy="$(cat "$state_dir"/strategy)" test -f "$state_dir"/strategy_opts && @@@ -176,8 -175,8 +175,8 @@@ run_interactive () run_specific_rebase () { if [ "$interactive_rebase" = implied ]; then - GIT_EDITOR=: - export GIT_EDITOR + GIT_SEQUENCE_EDITOR=: + export GIT_SEQUENCE_EDITOR autosquash= fi @@@ -226,7 -225,6 +225,7 @@@ the state_dir="$apply_dir" elif test -d "$merge_dir" then + type=interactive if test -d "$merge_dir"/rewritten then type=preserve-merges @@@ -234,7 -232,10 +233,7 @@@ preserve_merges=t elif test -f "$merge_dir"/interactive then - type=interactive interactive_rebase=explicit - else - type=merge fi state_dir="$merge_dir" fi @@@ -260,11 -261,6 +259,6 @@@ d --exec=*) 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 @@@ -494,7 -490,6 +488,7 @@@ the test -z "$interactive_rebase" && interactive_rebase=implied fi +actually_interactive= if test -n "$interactive_rebase" then if test -z "$preserve_merges" @@@ -503,12 -498,11 +497,12 @@@ else type=preserve-merges fi - + actually_interactive=t state_dir="$merge_dir" elif test -n "$do_merge" then - type=merge + interactive_rebase=implied + type=interactive state_dir="$merge_dir" else type=am @@@ -520,20 -514,28 +514,20 @@@ the git_format_patch_opt="$git_format_patch_opt --progress" fi -if test -n "$git_am_opt"; then - incompatible_opts=$(echo " $git_am_opt " | \ - sed -e 's/ -q / /g' -e 's/^ \(.*\) $/\1/') - if test -n "$interactive_rebase" +incompatible_opts=$(echo " $git_am_opt " | \ + sed -e 's/ -q / /g' -e 's/^ \(.*\) $/\1/') +if test -n "$incompatible_opts" +then + if test -n "$actually_interactive" || test "$do_merge" then - if test -n "$incompatible_opts" - then - die "$(gettext "error: cannot combine interactive options (--interactive, --exec, --rebase-merges, --preserve-merges, --keep-empty, --root + --onto) with am options ($incompatible_opts)")" - fi - fi - if test -n "$do_merge"; then - if test -n "$incompatible_opts" - then - die "$(gettext "error: cannot combine merge options (--merge, --strategy, --strategy-option) with am options ($incompatible_opts)")" - fi + die "$(gettext "fatal: cannot combine am options with either interactive or merge options")" fi fi if test -n "$signoff" then test -n "$preserve_merges" && - die "$(gettext "error: cannot combine '--signoff' with '--preserve-merges'")" + die "$(gettext "fatal: cannot combine '--signoff' with '--preserve-merges'")" git_am_opt="$git_am_opt $signoff" force_rebase=t fi @@@ -544,7 -546,7 +538,7 @@@ the # Note: incompatibility with --interactive is just a strong warning; # 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'")" + die "$(gettext "fatal: cannot combine '--preserve-merges' with '--rebase-merges'")" test -n "$reschedule_failed_exec" && die "$(gettext "error: cannot combine '--preserve-merges' with '--reschedule-failed-exec'")" @@@ -553,9 -555,9 +547,9 @@@ f if test -n "$rebase_merges" then test -n "$strategy_opts" && - die "$(gettext "error: cannot combine '--rebase-merges' with '--strategy-option'")" + die "$(gettext "fatal: cannot combine '--rebase-merges' with '--strategy-option'")" test -n "$strategy" && - die "$(gettext "error: cannot combine '--rebase-merges' with '--strategy'")" + die "$(gettext "fatal: cannot combine '--rebase-merges' with '--strategy'")" fi if test -z "$rebase_root" @@@ -694,7 -696,7 +688,7 @@@ require_clean_work_tree "rebase" "$(get # but this should be done only when upstream and onto are the same # and if this is not an interactive rebase. mb=$(git merge-base "$onto" "$orig_head") -if test -z "$interactive_rebase" && test "$upstream" = "$onto" && +if test -z "$actually_interactive" && test "$upstream" = "$onto" && test "$mb" = "$onto" && test -z "$restrict_revision" && # linear history? ! (git rev-list --parents "$onto".."$orig_head" | sane_grep " .* ") > /dev/null @@@ -744,19 -746,6 +738,19 @@@ the GIT_PAGER='' git diff --stat --summary "$mb_tree" "$onto" fi +if test -z "$actually_interactive" && test "$mb" = "$orig_head" +then + say "$(eval_gettext "Fast-forwarded \$branch_name to \$onto_name.")" + GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION: checkout $onto_name" \ + git checkout -q "$onto^0" || die "could not detach HEAD" + # If the $onto is a proper descendant of the tip of the branch, then + # we just fast-forwarded. + git update-ref ORIG_HEAD $orig_head + move_to_original_branch + finish_rebase + exit 0 +fi + test -n "$interactive_rebase" && run_specific_rebase # Detach HEAD and reset the tree @@@ -766,6 -755,16 +760,6 @@@ GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION: git checkout -q "$onto^0" || die "could not detach HEAD" git update-ref ORIG_HEAD $orig_head -# If the $onto is a proper descendant of the tip of the branch, then -# we just fast-forwarded. -if test "$mb" = "$orig_head" -then - say "$(eval_gettext "Fast-forwarded \$branch_name to \$onto_name.")" - move_to_original_branch - finish_rebase - exit 0 -fi - if test -n "$rebase_root" then revisions="$onto..$orig_head"