Merge branch 'pw/rebase-i-after-failure'
Various fixes to the behaviour of "rebase -i" when the command got interrupted by conflicting changes. * pw/rebase-i-after-failure: rebase -i: fix adding failed command to the todo list rebase --continue: refuse to commit after failed command rebase: fix rewritten list for failed pick sequencer: factor out part of pick_commits() sequencer: use rebase_path_message() rebase -i: remove patch file after conflict resolution rebase -i: move unlink() calls
This commit is contained in:
182
sequencer.c
182
sequencer.c
@ -147,6 +147,11 @@ static GIT_PATH_FUNC(rebase_path_amend, "rebase-merge/amend")
|
||||
* the commit object name of the corresponding patch.
|
||||
*/
|
||||
static GIT_PATH_FUNC(rebase_path_stopped_sha, "rebase-merge/stopped-sha")
|
||||
/*
|
||||
* When we stop for the user to resolve conflicts this file contains
|
||||
* the patch of the commit that is being picked.
|
||||
*/
|
||||
static GIT_PATH_FUNC(rebase_path_patch, "rebase-merge/patch")
|
||||
/*
|
||||
* For the post-rewrite hook, we make a list of rewritten commits and
|
||||
* their new sha1s. The rewritten-pending list keeps the sha1s of
|
||||
@ -3412,7 +3417,8 @@ give_advice:
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int save_todo(struct todo_list *todo_list, struct replay_opts *opts)
|
||||
static int save_todo(struct todo_list *todo_list, struct replay_opts *opts,
|
||||
int reschedule)
|
||||
{
|
||||
struct lock_file todo_lock = LOCK_INIT;
|
||||
const char *todo_path = get_todo_path(opts);
|
||||
@ -3422,7 +3428,7 @@ static int save_todo(struct todo_list *todo_list, struct replay_opts *opts)
|
||||
* rebase -i writes "git-rebase-todo" without the currently executing
|
||||
* command, appending it to "done" instead.
|
||||
*/
|
||||
if (is_rebase_i(opts))
|
||||
if (is_rebase_i(opts) && !reschedule)
|
||||
next++;
|
||||
|
||||
fd = hold_lock_file_for_update(&todo_lock, todo_path, 0);
|
||||
@ -3435,7 +3441,7 @@ static int save_todo(struct todo_list *todo_list, struct replay_opts *opts)
|
||||
if (commit_lock_file(&todo_lock) < 0)
|
||||
return error(_("failed to finalize '%s'"), todo_path);
|
||||
|
||||
if (is_rebase_i(opts) && next > 0) {
|
||||
if (is_rebase_i(opts) && !reschedule && next > 0) {
|
||||
const char *done = rebase_path_done();
|
||||
int fd = open(done, O_CREAT | O_WRONLY | O_APPEND, 0666);
|
||||
int ret = 0;
|
||||
@ -3516,18 +3522,19 @@ static int make_patch(struct repository *r,
|
||||
struct commit *commit,
|
||||
struct replay_opts *opts)
|
||||
{
|
||||
struct strbuf buf = STRBUF_INIT;
|
||||
struct rev_info log_tree_opt;
|
||||
const char *subject;
|
||||
char hex[GIT_MAX_HEXSZ + 1];
|
||||
int res = 0;
|
||||
|
||||
if (!is_rebase_i(opts))
|
||||
BUG("make_patch should only be called when rebasing");
|
||||
|
||||
oid_to_hex_r(hex, &commit->object.oid);
|
||||
if (write_message(hex, strlen(hex), rebase_path_stopped_sha(), 1) < 0)
|
||||
return -1;
|
||||
res |= write_rebase_head(&commit->object.oid);
|
||||
|
||||
strbuf_addf(&buf, "%s/patch", get_dir(opts));
|
||||
memset(&log_tree_opt, 0, sizeof(log_tree_opt));
|
||||
repo_init_revisions(r, &log_tree_opt, NULL);
|
||||
log_tree_opt.abbrev = 0;
|
||||
@ -3535,28 +3542,26 @@ static int make_patch(struct repository *r,
|
||||
log_tree_opt.diffopt.output_format = DIFF_FORMAT_PATCH;
|
||||
log_tree_opt.disable_stdin = 1;
|
||||
log_tree_opt.no_commit_id = 1;
|
||||
log_tree_opt.diffopt.file = fopen(buf.buf, "w");
|
||||
log_tree_opt.diffopt.file = fopen(rebase_path_patch(), "w");
|
||||
log_tree_opt.diffopt.use_color = GIT_COLOR_NEVER;
|
||||
if (!log_tree_opt.diffopt.file)
|
||||
res |= error_errno(_("could not open '%s'"), buf.buf);
|
||||
res |= error_errno(_("could not open '%s'"),
|
||||
rebase_path_patch());
|
||||
else {
|
||||
res |= log_tree_commit(&log_tree_opt, commit);
|
||||
fclose(log_tree_opt.diffopt.file);
|
||||
}
|
||||
strbuf_reset(&buf);
|
||||
|
||||
strbuf_addf(&buf, "%s/message", get_dir(opts));
|
||||
if (!file_exists(buf.buf)) {
|
||||
if (!file_exists(rebase_path_message())) {
|
||||
const char *encoding = get_commit_output_encoding();
|
||||
const char *commit_buffer = repo_logmsg_reencode(r,
|
||||
commit, NULL,
|
||||
encoding);
|
||||
find_commit_subject(commit_buffer, &subject);
|
||||
res |= write_message(subject, strlen(subject), buf.buf, 1);
|
||||
res |= write_message(subject, strlen(subject), rebase_path_message(), 1);
|
||||
repo_unuse_commit_buffer(r, commit,
|
||||
commit_buffer);
|
||||
}
|
||||
strbuf_release(&buf);
|
||||
release_revisions(&log_tree_opt);
|
||||
|
||||
return res;
|
||||
@ -4174,6 +4179,7 @@ static int do_merge(struct repository *r,
|
||||
if (ret < 0) {
|
||||
error(_("could not even attempt to merge '%.*s'"),
|
||||
merge_arg_len, arg);
|
||||
unlink(git_path_merge_msg(r));
|
||||
goto leave_merge;
|
||||
}
|
||||
/*
|
||||
@ -4661,6 +4667,68 @@ N_("Could not execute the todo command\n"
|
||||
" git rebase --edit-todo\n"
|
||||
" git rebase --continue\n");
|
||||
|
||||
static int pick_one_commit(struct repository *r,
|
||||
struct todo_list *todo_list,
|
||||
struct replay_opts *opts,
|
||||
int *check_todo, int* reschedule)
|
||||
{
|
||||
int res;
|
||||
struct todo_item *item = todo_list->items + todo_list->current;
|
||||
const char *arg = todo_item_get_arg(todo_list, item);
|
||||
if (is_rebase_i(opts))
|
||||
opts->reflog_message = reflog_message(
|
||||
opts, command_to_string(item->command), NULL);
|
||||
|
||||
res = do_pick_commit(r, item, opts, is_final_fixup(todo_list),
|
||||
check_todo);
|
||||
if (is_rebase_i(opts) && res < 0) {
|
||||
/* Reschedule */
|
||||
*reschedule = 1;
|
||||
return -1;
|
||||
}
|
||||
if (item->command == TODO_EDIT) {
|
||||
struct commit *commit = item->commit;
|
||||
if (!res) {
|
||||
if (!opts->verbose)
|
||||
term_clear_line();
|
||||
fprintf(stderr, _("Stopped at %s... %.*s\n"),
|
||||
short_commit_name(r, commit), item->arg_len, arg);
|
||||
}
|
||||
return error_with_patch(r, commit,
|
||||
arg, item->arg_len, opts, res, !res);
|
||||
}
|
||||
if (is_rebase_i(opts) && !res)
|
||||
record_in_rewritten(&item->commit->object.oid,
|
||||
peek_command(todo_list, 1));
|
||||
if (res && is_fixup(item->command)) {
|
||||
if (res == 1)
|
||||
intend_to_amend();
|
||||
return error_failed_squash(r, item->commit, opts,
|
||||
item->arg_len, arg);
|
||||
} else if (res && is_rebase_i(opts) && item->commit) {
|
||||
int to_amend = 0;
|
||||
struct object_id oid;
|
||||
|
||||
/*
|
||||
* If we are rewording and have either
|
||||
* fast-forwarded already, or are about to
|
||||
* create a new root commit, we want to amend,
|
||||
* otherwise we do not.
|
||||
*/
|
||||
if (item->command == TODO_REWORD &&
|
||||
!repo_get_oid(r, "HEAD", &oid) &&
|
||||
(oideq(&item->commit->object.oid, &oid) ||
|
||||
(opts->have_squash_onto &&
|
||||
oideq(&opts->squash_onto, &oid))))
|
||||
to_amend = 1;
|
||||
|
||||
return res | error_with_patch(r, item->commit,
|
||||
arg, item->arg_len, opts,
|
||||
res, to_amend);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static int pick_commits(struct repository *r,
|
||||
struct todo_list *todo_list,
|
||||
struct replay_opts *opts)
|
||||
@ -4676,12 +4744,17 @@ static int pick_commits(struct repository *r,
|
||||
if (read_and_refresh_cache(r, opts))
|
||||
return -1;
|
||||
|
||||
unlink(rebase_path_message());
|
||||
unlink(rebase_path_stopped_sha());
|
||||
unlink(rebase_path_amend());
|
||||
unlink(rebase_path_patch());
|
||||
|
||||
while (todo_list->current < todo_list->nr) {
|
||||
struct todo_item *item = todo_list->items + todo_list->current;
|
||||
const char *arg = todo_item_get_arg(todo_list, item);
|
||||
int check_todo = 0;
|
||||
|
||||
if (save_todo(todo_list, opts))
|
||||
if (save_todo(todo_list, opts, reschedule))
|
||||
return -1;
|
||||
if (is_rebase_i(opts)) {
|
||||
if (item->command != TODO_COMMENT) {
|
||||
@ -4699,10 +4772,7 @@ static int pick_commits(struct repository *r,
|
||||
todo_list->total_nr,
|
||||
opts->verbose ? "\n" : "\r");
|
||||
}
|
||||
unlink(rebase_path_message());
|
||||
unlink(rebase_path_author_script());
|
||||
unlink(rebase_path_stopped_sha());
|
||||
unlink(rebase_path_amend());
|
||||
unlink(git_path_merge_head(r));
|
||||
unlink(git_path_auto_merge(r));
|
||||
delete_ref(NULL, "REBASE_HEAD", NULL, REF_NO_DEREF);
|
||||
@ -4714,66 +4784,10 @@ static int pick_commits(struct repository *r,
|
||||
}
|
||||
}
|
||||
if (item->command <= TODO_SQUASH) {
|
||||
if (is_rebase_i(opts))
|
||||
opts->reflog_message = reflog_message(opts,
|
||||
command_to_string(item->command), NULL);
|
||||
|
||||
res = do_pick_commit(r, item, opts,
|
||||
is_final_fixup(todo_list),
|
||||
&check_todo);
|
||||
if (is_rebase_i(opts) && res < 0) {
|
||||
/* Reschedule */
|
||||
advise(_(rescheduled_advice),
|
||||
get_item_line_length(todo_list,
|
||||
todo_list->current),
|
||||
get_item_line(todo_list,
|
||||
todo_list->current));
|
||||
todo_list->current--;
|
||||
if (save_todo(todo_list, opts))
|
||||
return -1;
|
||||
}
|
||||
if (item->command == TODO_EDIT) {
|
||||
struct commit *commit = item->commit;
|
||||
if (!res) {
|
||||
if (!opts->verbose)
|
||||
term_clear_line();
|
||||
fprintf(stderr,
|
||||
_("Stopped at %s... %.*s\n"),
|
||||
short_commit_name(r, commit),
|
||||
item->arg_len, arg);
|
||||
}
|
||||
return error_with_patch(r, commit,
|
||||
arg, item->arg_len, opts, res, !res);
|
||||
}
|
||||
if (is_rebase_i(opts) && !res)
|
||||
record_in_rewritten(&item->commit->object.oid,
|
||||
peek_command(todo_list, 1));
|
||||
if (res && is_fixup(item->command)) {
|
||||
if (res == 1)
|
||||
intend_to_amend();
|
||||
return error_failed_squash(r, item->commit, opts,
|
||||
item->arg_len, arg);
|
||||
} else if (res && is_rebase_i(opts) && item->commit) {
|
||||
int to_amend = 0;
|
||||
struct object_id oid;
|
||||
|
||||
/*
|
||||
* If we are rewording and have either
|
||||
* fast-forwarded already, or are about to
|
||||
* create a new root commit, we want to amend,
|
||||
* otherwise we do not.
|
||||
*/
|
||||
if (item->command == TODO_REWORD &&
|
||||
!repo_get_oid(r, "HEAD", &oid) &&
|
||||
(oideq(&item->commit->object.oid, &oid) ||
|
||||
(opts->have_squash_onto &&
|
||||
oideq(&opts->squash_onto, &oid))))
|
||||
to_amend = 1;
|
||||
|
||||
return res | error_with_patch(r, item->commit,
|
||||
arg, item->arg_len, opts,
|
||||
res, to_amend);
|
||||
}
|
||||
res = pick_one_commit(r, todo_list, opts, &check_todo,
|
||||
&reschedule);
|
||||
if (!res && item->command == TODO_EDIT)
|
||||
return 0;
|
||||
} else if (item->command == TODO_EXEC) {
|
||||
char *end_of_arg = (char *)(arg + item->arg_len);
|
||||
int saved = *end_of_arg;
|
||||
@ -4821,22 +4835,19 @@ static int pick_commits(struct repository *r,
|
||||
get_item_line_length(todo_list,
|
||||
todo_list->current),
|
||||
get_item_line(todo_list, todo_list->current));
|
||||
todo_list->current--;
|
||||
if (save_todo(todo_list, opts))
|
||||
if (save_todo(todo_list, opts, reschedule))
|
||||
return -1;
|
||||
if (item->commit)
|
||||
return error_with_patch(r,
|
||||
item->commit,
|
||||
arg, item->arg_len,
|
||||
opts, res, 0);
|
||||
write_rebase_head(&item->commit->object.oid);
|
||||
} else if (is_rebase_i(opts) && check_todo && !res &&
|
||||
reread_todo_if_changed(r, todo_list, opts)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
todo_list->current++;
|
||||
if (res)
|
||||
return res;
|
||||
|
||||
todo_list->current++;
|
||||
}
|
||||
|
||||
if (is_rebase_i(opts)) {
|
||||
@ -4989,6 +5000,11 @@ static int commit_staged_changes(struct repository *r,
|
||||
|
||||
is_clean = !has_uncommitted_changes(r, 0);
|
||||
|
||||
if (!is_clean && !file_exists(rebase_path_message())) {
|
||||
const char *gpg_opt = gpg_sign_opt_quoted(opts);
|
||||
|
||||
return error(_(staged_changes_advice), gpg_opt, gpg_opt);
|
||||
}
|
||||
if (file_exists(rebase_path_amend())) {
|
||||
struct strbuf rev = STRBUF_INIT;
|
||||
struct object_id head, to_amend;
|
||||
|
Reference in New Issue
Block a user