#define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION"
-const char sign_off_header[] = "Signed-off-by: ";
+static const char sign_off_header[] = "Signed-off-by: ";
static const char cherry_picked_prefix[] = "(cherry picked from commit ";
GIT_PATH_FUNC(git_path_commit_editmsg, "COMMIT_EDITMSG")
* file and written to the tail of 'done'.
*/
GIT_PATH_FUNC(rebase_path_todo, "rebase-merge/git-rebase-todo")
-static GIT_PATH_FUNC(rebase_path_todo_backup,
- "rebase-merge/git-rebase-todo.backup")
+GIT_PATH_FUNC(rebase_path_todo_backup, "rebase-merge/git-rebase-todo.backup")
/*
* The rebase command lines that have already been processed. A line
static GIT_PATH_FUNC(rebase_path_gpg_sign_opt, "rebase-merge/gpg_sign_opt")
static GIT_PATH_FUNC(rebase_path_orig_head, "rebase-merge/orig-head")
static GIT_PATH_FUNC(rebase_path_verbose, "rebase-merge/verbose")
+static GIT_PATH_FUNC(rebase_path_quiet, "rebase-merge/quiet")
static GIT_PATH_FUNC(rebase_path_signoff, "rebase-merge/signoff")
static GIT_PATH_FUNC(rebase_path_head_name, "rebase-merge/head-name")
static GIT_PATH_FUNC(rebase_path_onto, "rebase-merge/onto")
static GIT_PATH_FUNC(rebase_path_strategy, "rebase-merge/strategy")
static GIT_PATH_FUNC(rebase_path_strategy_opts, "rebase-merge/strategy_opts")
static GIT_PATH_FUNC(rebase_path_allow_rerere_autoupdate, "rebase-merge/allow_rerere_autoupdate")
-static GIT_PATH_FUNC(rebase_path_quiet, "rebase-merge/quiet")
+static GIT_PATH_FUNC(rebase_path_reschedule_failed_exec, "rebase-merge/reschedule-failed-exec")
static int git_sequencer_config(const char *k, const char *v, void *cb)
{
if (status)
return status;
- if (!strcmp(s, "verbatim"))
+ if (!strcmp(s, "verbatim")) {
opts->default_msg_cleanup = COMMIT_MSG_CLEANUP_NONE;
- else if (!strcmp(s, "whitespace"))
+ opts->explicit_cleanup = 1;
+ } else if (!strcmp(s, "whitespace")) {
opts->default_msg_cleanup = COMMIT_MSG_CLEANUP_SPACE;
- else if (!strcmp(s, "strip"))
+ opts->explicit_cleanup = 1;
+ } else if (!strcmp(s, "strip")) {
opts->default_msg_cleanup = COMMIT_MSG_CLEANUP_ALL;
- else if (!strcmp(s, "scissors"))
- opts->default_msg_cleanup = COMMIT_MSG_CLEANUP_SPACE;
- else
+ opts->explicit_cleanup = 1;
+ } else if (!strcmp(s, "scissors")) {
+ opts->default_msg_cleanup = COMMIT_MSG_CLEANUP_SCISSORS;
+ opts->explicit_cleanup = 1;
+ } else {
warning(_("invalid commit message cleanup mode '%s'"),
s);
+ }
free((char *)s);
return status;
}
}
-int write_message(const void *buf, size_t len, const char *filename,
- int append_eol)
+static int write_message(const void *buf, size_t len, const char *filename,
+ int append_eol)
{
struct lock_file msg_file = LOCK_INIT;
return lookup_tree(r, the_hash_algo->empty_tree);
}
-static int error_dirty_index(struct index_state *istate, struct replay_opts *opts)
+static int error_dirty_index(struct repository *repo, struct replay_opts *opts)
{
- if (read_index_unmerged(istate))
+ if (repo_read_index_unmerged(repo))
return error_resolve_conflict(_(action_name(opts)));
error(_("your local changes would be overwritten by %s."),
struct strbuf sb = STRBUF_INIT;
struct strbuf err = STRBUF_INIT;
- read_index(r->index);
+ repo_read_index(r);
if (checkout_fast_forward(r, from, to, 1))
return -1; /* the callee should have complained already */
return 0;
}
+enum commit_msg_cleanup_mode get_cleanup_mode(const char *cleanup_arg,
+ int use_editor)
+{
+ if (!cleanup_arg || !strcmp(cleanup_arg, "default"))
+ return use_editor ? COMMIT_MSG_CLEANUP_ALL :
+ COMMIT_MSG_CLEANUP_SPACE;
+ else if (!strcmp(cleanup_arg, "verbatim"))
+ return COMMIT_MSG_CLEANUP_NONE;
+ else if (!strcmp(cleanup_arg, "whitespace"))
+ return COMMIT_MSG_CLEANUP_SPACE;
+ else if (!strcmp(cleanup_arg, "strip"))
+ return COMMIT_MSG_CLEANUP_ALL;
+ else if (!strcmp(cleanup_arg, "scissors"))
+ return use_editor ? COMMIT_MSG_CLEANUP_SCISSORS :
+ COMMIT_MSG_CLEANUP_SPACE;
+ else
+ die(_("Invalid cleanup mode %s"), cleanup_arg);
+}
+
+/*
+ * NB using int rather than enum cleanup_mode to stop clang's
+ * -Wtautological-constant-out-of-range-compare complaining that the comparison
+ * is always true.
+ */
+static const char *describe_cleanup_mode(int cleanup_mode)
+{
+ static const char *modes[] = { "whitespace",
+ "verbatim",
+ "scissors",
+ "strip" };
+
+ if (cleanup_mode < ARRAY_SIZE(modes))
+ return modes[cleanup_mode];
+
+ BUG("invalid cleanup_mode provided (%d)", cleanup_mode);
+}
+
void append_conflicts_hint(struct index_state *istate,
- struct strbuf *msgbuf)
+ struct strbuf *msgbuf, enum commit_msg_cleanup_mode cleanup_mode)
{
int i;
+ if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS) {
+ strbuf_addch(msgbuf, '\n');
+ wt_status_append_cut_line(msgbuf);
+ strbuf_addch(msgbuf, comment_line_char);
+ }
+
strbuf_addch(msgbuf, '\n');
strbuf_commented_addf(msgbuf, "Conflicts:\n");
for (i = 0; i < istate->cache_nr;) {
char **xopt;
struct lock_file index_lock = LOCK_INIT;
- if (hold_locked_index(&index_lock, LOCK_REPORT_ON_ERROR) < 0)
+ if (repo_hold_locked_index(r, &index_lock, LOCK_REPORT_ON_ERROR) < 0)
return -1;
- read_index(r->index);
+ repo_read_index(r);
- init_merge_options(&o);
+ init_merge_options(&o, r);
o.ancestor = base ? base_label : "(empty tree)";
o.branch1 = "HEAD";
o.branch2 = next ? next_label : "(empty tree)";
_(action_name(opts)));
if (!clean)
- append_conflicts_hint(r->index, msgbuf);
+ append_conflicts_hint(r->index, msgbuf,
+ opts->default_msg_cleanup);
return !clean;
}
}
strbuf_reset(&out);
- strbuf_addstr(&out, fmt_ident(name, email, date, 0));
+ strbuf_addstr(&out, fmt_ident(name, email, WANT_AUTHOR_IDENT, date, 0));
strbuf_swap(buf, &out);
strbuf_release(&out);
free(name);
unsigned int flags)
{
struct child_process cmd = CHILD_PROCESS_INIT;
- const char *value;
if ((flags & CREATE_ROOT_COMMIT) && !(flags & AMEND_MSG)) {
struct strbuf msg = STRBUF_INIT, script = STRBUF_INIT;
argv_array_push(&cmd.args, "-e");
else if (!(flags & CLEANUP_MSG) &&
!opts->signoff && !opts->record_origin &&
- git_config_get_value("commit.cleanup", &value))
+ !opts->explicit_cleanup)
argv_array_push(&cmd.args, "--cleanup=verbatim");
if ((flags & ALLOW_EMPTY))
return 1;
}
+void cleanup_message(struct strbuf *msgbuf,
+ enum commit_msg_cleanup_mode cleanup_mode, int verbose)
+{
+ if (verbose || /* Truncate the message just before the diff, if any. */
+ cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS)
+ strbuf_setlen(msgbuf, wt_status_locate_end(msgbuf->buf, msgbuf->len));
+ if (cleanup_mode != COMMIT_MSG_CLEANUP_NONE)
+ strbuf_stripspace(msgbuf, cleanup_mode == COMMIT_MSG_CLEANUP_ALL);
+}
+
/*
* Find out if the message in the strbuf contains only whitespace and
* Signed-off-by lines.
proc.argv = argv;
proc.in = -1;
proc.stdout_to_stderr = 1;
+ proc.trace2_hook_name = "post-rewrite";
code = start_command(&proc);
if (code)
return finish_command(&proc);
}
-void commit_post_rewrite(const struct commit *old_head,
+void commit_post_rewrite(struct repository *r,
+ const struct commit *old_head,
const struct object_id *new_head)
{
struct notes_rewrite_cfg *cfg;
if (cfg) {
/* we are amending, so old_head is not NULL */
copy_note_for_rewrite(cfg, &old_head->object.oid, new_head);
- finish_copy_notes_for_rewrite(cfg, "Notes added by 'git commit --amend'");
+ finish_copy_notes_for_rewrite(r, cfg, "Notes added by 'git commit --amend'");
}
run_rewrite_hook(&old_head->object.oid, new_head);
}
msg = &commit_msg;
}
- cleanup = (flags & CLEANUP_MSG) ? COMMIT_MSG_CLEANUP_ALL :
- opts->default_msg_cleanup;
+ if (flags & CLEANUP_MSG)
+ cleanup = COMMIT_MSG_CLEANUP_ALL;
+ else if ((opts->signoff || opts->record_origin) &&
+ !opts->explicit_cleanup)
+ cleanup = COMMIT_MSG_CLEANUP_SPACE;
+ else
+ cleanup = opts->default_msg_cleanup;
if (cleanup != COMMIT_MSG_CLEANUP_NONE)
strbuf_stripspace(msg, cleanup == COMMIT_MSG_CLEANUP_ALL);
}
if (flags & AMEND_MSG)
- commit_post_rewrite(current_head, oid);
+ commit_post_rewrite(r, current_head, oid);
out:
free_commit_extra_headers(extra);
return res;
}
-static void flush_rewritten_pending(void) {
+static void flush_rewritten_pending(void)
+{
struct strbuf buf = STRBUF_INIT;
struct object_id newoid;
FILE *out;
}
static void record_in_rewritten(struct object_id *oid,
- enum todo_command next_command) {
+ enum todo_command next_command)
+{
FILE *out = fopen_or_warn(rebase_path_rewritten_pending(), "a");
if (!out)
oidcpy(&head, the_hash_algo->empty_tree);
if (index_differs_from(r, unborn ? empty_tree_oid_hex() : "HEAD",
NULL, 0))
- return error_dirty_index(r->index, opts);
+ return error_dirty_index(r, opts);
}
discard_index(r->index);
return error(_("commit %s does not have parent %d"),
oid_to_hex(&commit->object.oid), opts->mainline);
parent = p->item;
- } else if (0 < opts->mainline)
- return error(_("mainline was specified but commit %s is not a merge."),
- oid_to_hex(&commit->object.oid));
+ } else if (1 < opts->mainline)
+ /*
+ * Non-first parent explicitly specified as mainline for
+ * non-merge commit
+ */
+ return error(_("commit %s does not have parent %d"),
+ oid_to_hex(&commit->object.oid), opts->mainline);
else
parent = commit->parents->item;
struct replay_opts *opts)
{
struct lock_file index_lock = LOCK_INIT;
- int index_fd = hold_locked_index(&index_lock, 0);
- if (read_index(r->index) < 0) {
+ int index_fd = repo_hold_locked_index(r, &index_lock, 0);
+ if (repo_read_index(r) < 0) {
rollback_lock_file(&index_lock);
return error(_("git %s: failed to read the index"),
_(action_name(opts)));
item->arg_len = (int)(eol - bol);
if (status < 0)
- return -1;
+ return error(_("could not parse '%.*s'"),
+ (int)(end_of_object_name - bol), bol);
item->commit = lookup_commit_reference(r, &commit_oid);
return !item->commit;
}
+int sequencer_get_last_command(struct repository *r, enum replay_action *action)
+{
+ struct todo_item item;
+ char *eol;
+ const char *todo_file;
+ struct strbuf buf = STRBUF_INIT;
+ int ret = -1;
+
+ todo_file = git_path_todo_file();
+ if (strbuf_read_file(&buf, todo_file, 0) < 0) {
+ if (errno == ENOENT)
+ return -1;
+ else
+ return error_errno("unable to open '%s'", todo_file);
+ }
+ eol = strchrnul(buf.buf, '\n');
+ if (buf.buf != eol && eol[-1] == '\r')
+ eol--; /* strip Carriage Return */
+ if (parse_insn_line(r, &item, buf.buf, buf.buf, eol))
+ goto fail;
+ if (item.command == TODO_PICK)
+ *action = REPLAY_PICK;
+ else if (item.command == TODO_REVERT)
+ *action = REPLAY_REVERT;
+ else
+ goto fail;
+
+ ret = 0;
+
+ fail:
+ strbuf_release(&buf);
+
+ return ret;
+}
+
int todo_list_parse_insn_buffer(struct repository *r, char *buf,
struct todo_list *todo_list)
{
return len;
}
+static int have_finished_the_last_pick(void)
+{
+ struct strbuf buf = STRBUF_INIT;
+ const char *eol;
+ const char *todo_path = git_path_todo_file();
+ int ret = 0;
+
+ if (strbuf_read_file(&buf, todo_path, 0) < 0) {
+ if (errno == ENOENT) {
+ return 0;
+ } else {
+ error_errno("unable to open '%s'", todo_path);
+ return 0;
+ }
+ }
+ /* If there is only one line then we are done */
+ eol = strchr(buf.buf, '\n');
+ if (!eol || !eol[1])
+ ret = 1;
+
+ strbuf_release(&buf);
+
+ return ret;
+}
+
+void sequencer_post_commit_cleanup(struct repository *r)
+{
+ struct replay_opts opts = REPLAY_OPTS_INIT;
+ int need_cleanup = 0;
+
+ if (file_exists(git_path_cherry_pick_head(r))) {
+ unlink(git_path_cherry_pick_head(r));
+ opts.action = REPLAY_PICK;
+ need_cleanup = 1;
+ }
+
+ if (file_exists(git_path_revert_head(r))) {
+ unlink(git_path_revert_head(r));
+ opts.action = REPLAY_REVERT;
+ need_cleanup = 1;
+ }
+
+ if (!need_cleanup)
+ return;
+
+ if (!have_finished_the_last_pick())
+ return;
+
+ sequencer_remove_state(&opts);
+}
+
static int read_populate_todo(struct repository *r,
struct todo_list *todo_list,
struct replay_opts *opts)
opts->no_commit = git_config_bool_or_int(key, value, &error_flag);
else if (!strcmp(key, "options.edit"))
opts->edit = git_config_bool_or_int(key, value, &error_flag);
+ else if (!strcmp(key, "options.allow-empty"))
+ opts->allow_empty =
+ git_config_bool_or_int(key, value, &error_flag);
+ else if (!strcmp(key, "options.allow-empty-message"))
+ opts->allow_empty_message =
+ git_config_bool_or_int(key, value, &error_flag);
+ else if (!strcmp(key, "options.keep-redundant-commits"))
+ opts->keep_redundant_commits =
+ git_config_bool_or_int(key, value, &error_flag);
else if (!strcmp(key, "options.signoff"))
opts->signoff = git_config_bool_or_int(key, value, &error_flag);
else if (!strcmp(key, "options.record-origin"))
opts->allow_rerere_auto =
git_config_bool_or_int(key, value, &error_flag) ?
RERERE_AUTOUPDATE : RERERE_NOAUTOUPDATE;
- else
+ else if (!strcmp(key, "options.default-msg-cleanup")) {
+ opts->explicit_cleanup = 1;
+ opts->default_msg_cleanup = get_cleanup_mode(value, 1);
+ } else
return error(_("invalid key: %s"), key);
if (!error_flag)
if (file_exists(rebase_path_verbose()))
opts->verbose = 1;
+ if (file_exists(rebase_path_quiet()))
+ opts->quiet = 1;
+
if (file_exists(rebase_path_signoff())) {
opts->allow_ff = 0;
opts->signoff = 1;
}
+ if (file_exists(rebase_path_reschedule_failed_exec()))
+ opts->reschedule_failed_exec = 1;
+
read_strategy_opts(opts, &buf);
strbuf_release(&buf);
}
int write_basic_state(struct replay_opts *opts, const char *head_name,
- const char *onto, const char *orig_head)
+ struct commit *onto, const char *orig_head)
{
const char *quiet = getenv("GIT_QUIET");
if (head_name)
write_file(rebase_path_head_name(), "%s\n", head_name);
if (onto)
- write_file(rebase_path_onto(), "%s\n", onto);
+ write_file(rebase_path_onto(), "%s\n",
+ oid_to_hex(&onto->object.oid));
if (orig_head)
write_file(rebase_path_orig_head(), "%s\n", orig_head);
if (quiet)
write_file(rebase_path_quiet(), "%s\n", quiet);
- else
- write_file(rebase_path_quiet(), "\n");
-
if (opts->verbose)
write_file(rebase_path_verbose(), "%s", "");
if (opts->strategy)
write_file(rebase_path_gpg_sign_opt(), "-S%s\n", opts->gpg_sign);
if (opts->signoff)
write_file(rebase_path_signoff(), "--signoff\n");
+ if (opts->reschedule_failed_exec)
+ write_file(rebase_path_reschedule_failed_exec(), "%s", "");
return 0;
}
int res = 0;
if (opts->no_commit)
- res |= git_config_set_in_file_gently(opts_file, "options.no-commit", "true");
+ res |= git_config_set_in_file_gently(opts_file,
+ "options.no-commit", "true");
if (opts->edit)
- res |= git_config_set_in_file_gently(opts_file, "options.edit", "true");
+ res |= git_config_set_in_file_gently(opts_file,
+ "options.edit", "true");
+ if (opts->allow_empty)
+ res |= git_config_set_in_file_gently(opts_file,
+ "options.allow-empty", "true");
+ if (opts->allow_empty_message)
+ res |= git_config_set_in_file_gently(opts_file,
+ "options.allow-empty-message", "true");
+ if (opts->keep_redundant_commits)
+ res |= git_config_set_in_file_gently(opts_file,
+ "options.keep-redundant-commits", "true");
if (opts->signoff)
- res |= git_config_set_in_file_gently(opts_file, "options.signoff", "true");
+ res |= git_config_set_in_file_gently(opts_file,
+ "options.signoff", "true");
if (opts->record_origin)
- res |= git_config_set_in_file_gently(opts_file, "options.record-origin", "true");
+ res |= git_config_set_in_file_gently(opts_file,
+ "options.record-origin", "true");
if (opts->allow_ff)
- res |= git_config_set_in_file_gently(opts_file, "options.allow-ff", "true");
+ res |= git_config_set_in_file_gently(opts_file,
+ "options.allow-ff", "true");
if (opts->mainline) {
struct strbuf buf = STRBUF_INIT;
strbuf_addf(&buf, "%d", opts->mainline);
- res |= git_config_set_in_file_gently(opts_file, "options.mainline", buf.buf);
+ res |= git_config_set_in_file_gently(opts_file,
+ "options.mainline", buf.buf);
strbuf_release(&buf);
}
if (opts->strategy)
- res |= git_config_set_in_file_gently(opts_file, "options.strategy", opts->strategy);
+ res |= git_config_set_in_file_gently(opts_file,
+ "options.strategy", opts->strategy);
if (opts->gpg_sign)
- res |= git_config_set_in_file_gently(opts_file, "options.gpg-sign", opts->gpg_sign);
+ res |= git_config_set_in_file_gently(opts_file,
+ "options.gpg-sign", opts->gpg_sign);
if (opts->xopts) {
int i;
for (i = 0; i < opts->xopts_nr; i++)
res |= git_config_set_multivar_in_file_gently(opts_file,
- "options.strategy-option",
- opts->xopts[i], "^$", 0);
+ "options.strategy-option",
+ opts->xopts[i], "^$", 0);
}
if (opts->allow_rerere_auto)
- res |= git_config_set_in_file_gently(opts_file, "options.allow-rerere-auto",
- opts->allow_rerere_auto == RERERE_AUTOUPDATE ?
- "true" : "false");
+ res |= git_config_set_in_file_gently(opts_file,
+ "options.allow-rerere-auto",
+ opts->allow_rerere_auto == RERERE_AUTOUPDATE ?
+ "true" : "false");
+
+ if (opts->explicit_cleanup)
+ res |= git_config_set_in_file_gently(opts_file,
+ "options.default-msg-cleanup",
+ describe_cleanup_mode(opts->default_msg_cleanup));
return res;
}
child_env.argv);
/* force re-reading of the cache */
- if (discard_index(r->index) < 0 || read_index(r->index) < 0)
+ if (discard_index(r->index) < 0 || repo_read_index(r) < 0)
return error(_("could not read index"));
dirty = require_clean_work_tree(r, "rebase", NULL, 1, 1);
struct unpack_trees_options unpack_tree_opts;
int ret = 0;
- if (hold_locked_index(&lock, LOCK_REPORT_ON_ERROR) < 0)
+ if (repo_hold_locked_index(r, &lock, LOCK_REPORT_ON_ERROR) < 0)
return -1;
if (len == 10 && !strncmp("[new root]", name, len)) {
unpack_tree_opts.merge = 1;
unpack_tree_opts.update = 1;
- if (read_index_unmerged(r->index)) {
+ if (repo_read_index_unmerged(r)) {
rollback_lock_file(&lock);
strbuf_release(&ref_name);
return error_resolve_conflict(_(action_name(opts)));
static struct lock_file lock;
const char *p;
- if (hold_locked_index(&lock, LOCK_REPORT_ON_ERROR) < 0) {
+ if (repo_hold_locked_index(r, &lock, LOCK_REPORT_ON_ERROR) < 0) {
ret = -1;
goto leave_merge;
}
/* force re-reading of the cache */
if (!ret && (discard_index(r->index) < 0 ||
- read_index(r->index) < 0))
+ repo_read_index(r) < 0))
ret = error(_("could not read index"));
goto leave_merge;
}
commit_list_insert(j->item, &reversed);
free_commit_list(bases);
- read_index(r->index);
- init_merge_options(&o);
+ repo_read_index(r);
+ init_merge_options(&o, r);
o.branch1 = "HEAD";
o.branch2 = ref_name.buf;
o.buffer_output = 2;
return buf.buf;
}
-static int run_git_checkout(struct replay_opts *opts, const char *commit,
- const char *action)
+static int run_git_checkout(struct repository *r, struct replay_opts *opts,
+ const char *commit, const char *action)
{
struct child_process cmd = CHILD_PROCESS_INIT;
+ int ret;
cmd.git_cmd = 1;
argv_array_pushf(&cmd.env_array, GIT_REFLOG_ACTION "=%s", action);
if (opts->verbose)
- return run_command(&cmd);
+ ret = run_command(&cmd);
else
- return run_command_silent_on_success(&cmd);
+ ret = run_command_silent_on_success(&cmd);
+
+ if (!ret)
+ discard_index(r->index);
+
+ return ret;
}
-int prepare_branch_to_be_rebased(struct replay_opts *opts, const char *commit)
+int prepare_branch_to_be_rebased(struct repository *r, struct replay_opts *opts,
+ const char *commit)
{
const char *action;
if (commit && *commit) {
action = reflog_message(opts, "start", "checkout %s", commit);
- if (run_git_checkout(opts, commit, action))
+ if (run_git_checkout(r, opts, commit, action))
return error(_("could not checkout %s"), commit);
}
return 0;
}
-static int checkout_onto(struct replay_opts *opts,
- const char *onto_name, const char *onto,
+static int checkout_onto(struct repository *r, struct replay_opts *opts,
+ const char *onto_name, const struct object_id *onto,
const char *orig_head)
{
struct object_id oid;
if (get_oid(orig_head, &oid))
return error(_("%s: not a valid OID"), orig_head);
- if (run_git_checkout(opts, onto, action)) {
+ if (run_git_checkout(r, opts, oid_to_hex(onto), action)) {
apply_autostash(opts);
sequencer_remove_state(opts);
return error(_("could not detach HEAD"));
fprintf(f, "%d\n", todo_list->done_nr);
fclose(f);
}
- fprintf(stderr, "Rebasing (%d/%d)%s",
- todo_list->done_nr,
- todo_list->total_nr,
- opts->verbose ? "\n" : "\r");
+ if (!opts->quiet)
+ fprintf(stderr, "Rebasing (%d/%d)%s",
+ todo_list->done_nr,
+ todo_list->total_nr,
+ opts->verbose ? "\n" : "\r");
}
unlink(rebase_path_message());
unlink(rebase_path_author_script());
res = do_exec(r, arg);
*end_of_arg = saved;
- /* Reread the todo file if it has changed. */
- if (res)
- ; /* fall through */
- else if (stat(get_todo_path(opts), &st))
+ if (res) {
+ if (opts->reschedule_failed_exec)
+ reschedule = 1;
+ } else if (stat(get_todo_path(opts), &st))
res = error_errno(_("could not stat '%s'"),
get_todo_path(opts));
else if (match_stat_data(&todo_list->stat, &st)) {
+ /* Reread the todo file if it has changed. */
todo_list_release(todo_list);
if (read_populate_todo(r, todo_list, opts))
res = -1; /* message was printed */
hook.in = open(rebase_path_rewritten_list(),
O_RDONLY);
hook.stdout_to_stderr = 1;
+ hook.trace2_hook_name = "post-rewrite";
argv_array_push(&hook.args, post_rewrite_hook);
argv_array_push(&hook.args, "rebase");
/* we don't care if this hook failed */
}
apply_autostash(opts);
- fprintf(stderr, "Successfully rebased and updated %s.\n",
- head_ref.buf);
+ if (!opts->quiet)
+ fprintf(stderr,
+ "Successfully rebased and updated %s.\n",
+ head_ref.buf);
strbuf_release(&buf);
strbuf_release(&head_ref);
goto release_todo_list;
}
if (index_differs_from(r, "HEAD", NULL, 0)) {
- res = error_dirty_index(r->index, opts);
+ res = error_dirty_index(r, opts);
goto release_todo_list;
}
todo_list.current++;
int has_footer;
strbuf_addstr(&sob, sign_off_header);
- strbuf_addstr(&sob, fmt_name(getenv("GIT_COMMITTER_NAME"),
- getenv("GIT_COMMITTER_EMAIL")));
+ strbuf_addstr(&sob, fmt_name(WANT_COMMITTER_IDENT));
strbuf_addch(&sob, '\n');
if (!ignore_footer)
* Add commands after pick and (series of) squash/fixup commands
* in the todo list.
*/
-static void todo_list_add_exec_commands(struct todo_list *todo_list,
- struct string_list *commands)
+void todo_list_add_exec_commands(struct todo_list *todo_list,
+ struct string_list *commands)
{
struct strbuf *buf = &todo_list->buf;
size_t base_offset = buf->len;
todo_list->alloc = alloc;
}
-int sequencer_add_exec_commands(struct repository *r,
- struct string_list *commands)
-{
- const char *todo_file = rebase_path_todo();
- struct todo_list todo_list = TODO_LIST_INIT;
- int res;
-
- if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)
- return error_errno(_("could not read '%s'."), todo_file);
-
- if (todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list)) {
- todo_list_release(&todo_list);
- return error(_("unusable todo list: '%s'"), todo_file);
- }
-
- todo_list_add_exec_commands(&todo_list, commands);
- res = todo_list_write_to_file(r, &todo_list, todo_file, NULL, NULL, -1, 0);
- todo_list_release(&todo_list);
-
- if (res)
- return error_errno(_("could not write '%s'."), todo_file);
- return 0;
-}
-
static void todo_list_to_strbuf(struct repository *r, struct todo_list *todo_list,
struct strbuf *buf, int num, unsigned flags)
{
const char *file, const char *shortrevisions,
const char *shortonto, int num, unsigned flags)
{
- int edit_todo = !(shortrevisions && shortonto), res;
+ int res;
struct strbuf buf = STRBUF_INIT;
todo_list_to_strbuf(r, todo_list, &buf, num, flags);
-
- if (flags & TODO_LIST_APPEND_TODO_HELP) {
- int command_count = count_commands(todo_list);
- if (!edit_todo) {
- strbuf_addch(&buf, '\n');
- strbuf_commented_addf(&buf, Q_("Rebase %s onto %s (%d command)",
- "Rebase %s onto %s (%d commands)",
- command_count),
- shortrevisions, shortonto, command_count);
- }
- append_todo_help(edit_todo, flags & TODO_LIST_KEEP_EMPTY, &buf);
- }
+ if (flags & TODO_LIST_APPEND_TODO_HELP)
+ append_todo_help(flags & TODO_LIST_KEEP_EMPTY, count_commands(todo_list),
+ shortrevisions, shortonto, &buf);
res = write_message(buf.buf, buf.len, file, 0);
strbuf_release(&buf);
return res;
}
-int transform_todo_file(struct repository *r, unsigned flags)
-{
- const char *todo_file = rebase_path_todo();
- struct todo_list todo_list = TODO_LIST_INIT;
- int res;
-
- if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)
- return error_errno(_("could not read '%s'."), todo_file);
-
- if (todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list)) {
- todo_list_release(&todo_list);
- return error(_("unusable todo list: '%s'"), todo_file);
- }
-
- res = todo_list_write_to_file(r, &todo_list, todo_file,
- NULL, NULL, -1, flags);
- todo_list_release(&todo_list);
-
- if (res)
- return error_errno(_("could not write '%s'."), todo_file);
- return 0;
-}
-
static const char edit_todo_list_advice[] =
N_("You can fix this with 'git rebase --edit-todo' "
"and then run 'git rebase --continue'.\n"
return res;
}
-static int rewrite_file(const char *path, const char *buf, size_t len)
-{
- int rc = 0;
- int fd = open(path, O_WRONLY | O_TRUNC);
- if (fd < 0)
- return error_errno(_("could not open '%s' for writing"), path);
- if (write_in_full(fd, buf, len) < 0)
- rc = error_errno(_("could not write to '%s'"), path);
- if (close(fd) && !rc)
- rc = error_errno(_("could not close '%s'"), path);
- return rc;
-}
-
/* skip picking commits whose parents are unchanged */
-static int skip_unnecessary_picks(struct repository *r, struct object_id *output_oid)
+static int skip_unnecessary_picks(struct repository *r,
+ struct todo_list *todo_list,
+ struct object_id *base_oid)
{
- const char *todo_file = rebase_path_todo();
- struct strbuf buf = STRBUF_INIT;
- struct todo_list todo_list = TODO_LIST_INIT;
struct object_id *parent_oid;
- int fd, i;
-
- if (!read_oneliner(&buf, rebase_path_onto(), 0))
- return error(_("could not read 'onto'"));
- if (get_oid(buf.buf, output_oid)) {
- strbuf_release(&buf);
- return error(_("need a HEAD to fixup"));
- }
- strbuf_release(&buf);
-
- if (strbuf_read_file_or_whine(&todo_list.buf, todo_file) < 0)
- return -1;
- if (todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list) < 0) {
- todo_list_release(&todo_list);
- return -1;
- }
+ int i;
- for (i = 0; i < todo_list.nr; i++) {
- struct todo_item *item = todo_list.items + i;
+ for (i = 0; i < todo_list->nr; i++) {
+ struct todo_item *item = todo_list->items + i;
if (item->command >= TODO_NOOP)
continue;
if (item->command != TODO_PICK)
break;
if (parse_commit(item->commit)) {
- todo_list_release(&todo_list);
return error(_("could not parse commit '%s'"),
oid_to_hex(&item->commit->object.oid));
}
if (item->commit->parents->next)
break; /* merge commit */
parent_oid = &item->commit->parents->item->object.oid;
- if (!oideq(parent_oid, output_oid))
+ if (!oideq(parent_oid, base_oid))
break;
- oidcpy(output_oid, &item->commit->object.oid);
+ oidcpy(base_oid, &item->commit->object.oid);
}
if (i > 0) {
- int offset = get_item_line_offset(&todo_list, i);
const char *done_path = rebase_path_done();
- fd = open(done_path, O_CREAT | O_WRONLY | O_APPEND, 0666);
- if (fd < 0) {
- error_errno(_("could not open '%s' for writing"),
- done_path);
- todo_list_release(&todo_list);
- return -1;
- }
- if (write_in_full(fd, todo_list.buf.buf, offset) < 0) {
+ if (todo_list_write_to_file(r, todo_list, done_path, NULL, NULL, i, 0)) {
error_errno(_("could not write to '%s'"), done_path);
- todo_list_release(&todo_list);
- close(fd);
return -1;
}
- close(fd);
- if (rewrite_file(rebase_path_todo(), todo_list.buf.buf + offset,
- todo_list.buf.len - offset) < 0) {
- todo_list_release(&todo_list);
- return -1;
- }
+ MOVE_ARRAY(todo_list->items, todo_list->items + i, todo_list->nr - i);
+ todo_list->nr -= i;
+ todo_list->current = 0;
- todo_list.current = i;
- if (is_fixup(peek_command(&todo_list, 0)))
- record_in_rewritten(output_oid, peek_command(&todo_list, 0));
+ if (is_fixup(peek_command(todo_list, 0)))
+ record_in_rewritten(base_oid, peek_command(todo_list, 0));
}
- todo_list_release(&todo_list);
-
return 0;
}
int complete_action(struct repository *r, struct replay_opts *opts, unsigned flags,
const char *shortrevisions, const char *onto_name,
- const char *onto, const char *orig_head, struct string_list *commands,
- unsigned autosquash)
+ struct commit *onto, const char *orig_head,
+ struct string_list *commands, unsigned autosquash,
+ struct todo_list *todo_list)
{
const char *shortonto, *todo_file = rebase_path_todo();
- struct todo_list todo_list = TODO_LIST_INIT;
- struct strbuf *buf = &(todo_list.buf);
- struct object_id oid;
- struct stat st;
+ struct todo_list new_todo = TODO_LIST_INIT;
+ struct strbuf *buf = &todo_list->buf;
+ struct object_id oid = onto->object.oid;
+ int res;
- get_oid(onto, &oid);
shortonto = find_unique_abbrev(&oid, DEFAULT_ABBREV);
- if (!lstat(todo_file, &st) && st.st_size == 0 &&
- write_message("noop\n", 5, todo_file, 0))
- return -1;
+ if (buf->len == 0) {
+ struct todo_item *item = append_new_todo(todo_list);
+ item->command = TODO_NOOP;
+ item->commit = NULL;
+ item->arg_len = item->arg_offset = item->flags = item->offset_in_buf = 0;
+ }
- if (autosquash && rearrange_squash_in_todo_file(r))
+ if (autosquash && todo_list_rearrange_squash(todo_list))
return -1;
if (commands->nr)
- sequencer_add_exec_commands(r, commands);
-
- if (strbuf_read_file(buf, todo_file, 0) < 0)
- return error_errno(_("could not read '%s'."), todo_file);
+ todo_list_add_exec_commands(todo_list, commands);
- if (todo_list_parse_insn_buffer(r, buf->buf, &todo_list)) {
- todo_list_release(&todo_list);
- return error(_("unusable todo list: '%s'"), todo_file);
- }
-
- if (count_commands(&todo_list) == 0) {
+ if (count_commands(todo_list) == 0) {
apply_autostash(opts);
sequencer_remove_state(opts);
- todo_list_release(&todo_list);
return error(_("nothing to do"));
}
- strbuf_addch(buf, '\n');
- strbuf_commented_addf(buf, Q_("Rebase %s onto %s (%d command)",
- "Rebase %s onto %s (%d commands)",
- count_commands(&todo_list)),
- shortrevisions, shortonto, count_commands(&todo_list));
- append_todo_help(0, flags & TODO_LIST_KEEP_EMPTY, buf);
-
- if (write_message(buf->buf, buf->len, todo_file, 0)) {
- todo_list_release(&todo_list);
+ res = edit_todo_list(r, todo_list, &new_todo, shortrevisions,
+ shortonto, flags);
+ if (res == -1)
return -1;
- }
-
- if (copy_file(rebase_path_todo_backup(), todo_file, 0666))
- return error(_("could not copy '%s' to '%s'."), todo_file,
- rebase_path_todo_backup());
-
- if (transform_todo_file(r, flags | TODO_LIST_SHORTEN_IDS))
- return error(_("could not transform the todo list"));
-
- strbuf_reset(buf);
-
- if (launch_sequence_editor(todo_file, buf, NULL)) {
+ else if (res == -2) {
apply_autostash(opts);
sequencer_remove_state(opts);
- todo_list_release(&todo_list);
return -1;
- }
-
- strbuf_stripspace(buf, 1);
- if (buf->len == 0) {
+ } else if (res == -3) {
apply_autostash(opts);
sequencer_remove_state(opts);
- todo_list_release(&todo_list);
+ todo_list_release(&new_todo);
return error(_("nothing to do"));
}
- todo_list_release(&todo_list);
+ if (todo_list_parse_insn_buffer(r, new_todo.buf.buf, &new_todo) ||
+ todo_list_check(todo_list, &new_todo)) {
+ fprintf(stderr, _(edit_todo_list_advice));
+ checkout_onto(r, opts, onto_name, &onto->object.oid, orig_head);
+ todo_list_release(&new_todo);
- if (check_todo_list_from_file(r)) {
- checkout_onto(opts, onto_name, onto, orig_head);
return -1;
}
- if (transform_todo_file(r, flags & ~(TODO_LIST_SHORTEN_IDS)))
- return error(_("could not transform the todo list"));
-
- if (opts->allow_ff && skip_unnecessary_picks(r, &oid))
+ if (opts->allow_ff && skip_unnecessary_picks(r, &new_todo, &oid)) {
+ todo_list_release(&new_todo);
return error(_("could not skip unnecessary pick commands"));
+ }
+
+ if (todo_list_write_to_file(r, &new_todo, todo_file, NULL, NULL, -1,
+ flags & ~(TODO_LIST_SHORTEN_IDS))) {
+ todo_list_release(&new_todo);
+ return error_errno(_("could not write '%s'"), todo_file);
+ }
- if (checkout_onto(opts, onto_name, oid_to_hex(&oid), orig_head))
+ todo_list_release(&new_todo);
+
+ if (checkout_onto(r, opts, onto_name, &oid, orig_head))
return -1;
if (require_clean_work_tree(r, "rebase", "", 1, 1))
* message will have to be retrieved from the commit (as the oneline in the
* script cannot be trusted) in order to normalize the autosquash arrangement.
*/
-static int todo_list_rearrange_squash(struct todo_list *todo_list)
+int todo_list_rearrange_squash(struct todo_list *todo_list)
{
struct hashmap subject2item;
int rearranged = 0, *next, *tail, i, nr = 0, alloc = 0;
return 0;
}
-
-int rearrange_squash_in_todo_file(struct repository *r)
-{
- const char *todo_file = rebase_path_todo();
- struct todo_list todo_list = TODO_LIST_INIT;
- int res = 0;
-
- if (strbuf_read_file_or_whine(&todo_list.buf, todo_file) < 0)
- return -1;
- if (todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list) < 0) {
- todo_list_release(&todo_list);
- return -1;
- }
-
- res = todo_list_rearrange_squash(&todo_list);
- if (!res)
- res = todo_list_write_to_file(r, &todo_list, todo_file, NULL, NULL, -1, 0);
-
- todo_list_release(&todo_list);
-
- if (res)
- return error_errno(_("could not write '%s'."), todo_file);
- return 0;
-}