diff options
Diffstat (limited to 'sequencer.c')
-rw-r--r-- | sequencer.c | 1412 |
1 files changed, 978 insertions, 434 deletions
diff --git a/sequencer.c b/sequencer.c index 5354d4d51e..972402e8c0 100644 --- a/sequencer.c +++ b/sequencer.c @@ -2,6 +2,7 @@ #include "config.h" #include "lockfile.h" #include "dir.h" +#include "object-store.h" #include "object.h" #include "commit.h" #include "sequencer.h" @@ -29,6 +30,8 @@ #include "oidset.h" #include "commit-slab.h" #include "alias.h" +#include "commit-reach.h" +#include "rebase-interactive.h" #define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION" @@ -51,7 +54,10 @@ static GIT_PATH_FUNC(rebase_path, "rebase-merge") * the lines are processed, they are removed from the front of this * file and written to the tail of 'done'. */ -static GIT_PATH_FUNC(rebase_path_todo, "rebase-merge/git-rebase-todo") +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") + /* * The rebase command lines that have already been processed. A line * is moved here when it is first handled, before any associated user @@ -62,12 +68,12 @@ static GIT_PATH_FUNC(rebase_path_done, "rebase-merge/done") * The file to keep track of how many commands were already processed (e.g. * for the prompt). */ -static GIT_PATH_FUNC(rebase_path_msgnum, "rebase-merge/msgnum"); +static GIT_PATH_FUNC(rebase_path_msgnum, "rebase-merge/msgnum") /* * The file to keep track of how many commands are to be processed in total * (e.g. for the prompt). */ -static GIT_PATH_FUNC(rebase_path_msgtotal, "rebase-merge/end"); +static GIT_PATH_FUNC(rebase_path_msgtotal, "rebase-merge/end") /* * The commit message that is planned to be used for any changes that * need to be committed following a user interaction. @@ -139,11 +145,12 @@ static GIT_PATH_FUNC(rebase_path_refs_to_delete, "rebase-merge/refs-to-delete") /* * The following files are written by git-rebase just after parsing the - * command-line (and are only consumed, not modified, by the sequencer). + * command-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") @@ -151,6 +158,7 @@ static GIT_PATH_FUNC(rebase_path_autostash, "rebase-merge/autostash") 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_reschedule_failed_exec, "rebase-merge/reschedule-failed-exec") static int git_sequencer_config(const char *k, const char *v, void *cb) { @@ -224,13 +232,16 @@ static const char *get_todo_path(const struct replay_opts *opts) * Returns 3 when sob exists within conforming footer as last entry */ static int has_conforming_footer(struct strbuf *sb, struct strbuf *sob, - int ignore_footer) + size_t ignore_footer) { + struct process_trailer_options opts = PROCESS_TRAILER_OPTIONS_INIT; struct trailer_info info; - int i; + size_t i; int found_sob = 0, found_sob_last = 0; - trailer_info_get(&info, sb->buf); + opts.no_divider = 1; + + trailer_info_get(&info, sb->buf, &opts); if (info.trailer_start == info.trailer_end) return 0; @@ -306,7 +317,7 @@ static const char *action_name(const struct replay_opts *opts) case REPLAY_INTERACTIVE_REBASE: return N_("rebase -i"); } - die(_("Unknown action: %d"), opts->action); + die(_("unknown action: %d"), opts->action); } struct commit_message { @@ -346,7 +357,8 @@ static void free_message(struct commit *commit, struct commit_message *msg) unuse_commit_buffer(commit, msg->message); } -static void print_advice(int show_hint, struct replay_opts *opts) +static void print_advice(struct repository *r, int show_hint, + struct replay_opts *opts) { char *msg = getenv("GIT_CHERRY_PICK_HELP"); @@ -357,7 +369,7 @@ static void print_advice(int show_hint, struct replay_opts *opts) * (typically rebase --interactive) wants to take care * of the commit itself so remove CHERRY_PICK_HEAD */ - unlink(git_path_cherry_pick_head()); + unlink(git_path_cherry_pick_head(r)); return; } @@ -372,8 +384,8 @@ static void print_advice(int show_hint, struct replay_opts *opts) } } -static int write_message(const void *buf, size_t len, const char *filename, - int append_eol) +int write_message(const void *buf, size_t len, const char *filename, + int append_eol) { struct lock_file msg_file = LOCK_INIT; @@ -430,14 +442,14 @@ static int read_oneliner(struct strbuf *buf, return 1; } -static struct tree *empty_tree(void) +static struct tree *empty_tree(struct repository *r) { - return lookup_tree(the_hash_algo->empty_tree); + return lookup_tree(r, the_hash_algo->empty_tree); } -static int error_dirty_index(struct replay_opts *opts) +static int error_dirty_index(struct repository *repo, struct replay_opts *opts) { - if (read_cache_unmerged()) + if (repo_read_index_unmerged(repo)) return error_resolve_conflict(_(action_name(opts))); error(_("your local changes would be overwritten by %s."), @@ -462,15 +474,18 @@ static void update_abort_safety_file(void) write_file(git_path_abort_safety_file(), "%s", ""); } -static int fast_forward_to(const struct object_id *to, const struct object_id *from, - int unborn, struct replay_opts *opts) +static int fast_forward_to(struct repository *r, + const struct object_id *to, + const struct object_id *from, + int unborn, + struct replay_opts *opts) { struct ref_transaction *transaction; struct strbuf sb = STRBUF_INIT; struct strbuf err = STRBUF_INIT; - read_cache(); - if (checkout_fast_forward(from, to, 1)) + repo_read_index(r); + if (checkout_fast_forward(r, from, to, 1)) return -1; /* the callee should have complained already */ strbuf_addf(&sb, _("%s: fast-forward"), _(action_name(opts))); @@ -496,24 +511,26 @@ static int fast_forward_to(const struct object_id *to, const struct object_id *f return 0; } -void append_conflicts_hint(struct strbuf *msgbuf) +void append_conflicts_hint(struct index_state *istate, + struct strbuf *msgbuf) { int i; strbuf_addch(msgbuf, '\n'); strbuf_commented_addf(msgbuf, "Conflicts:\n"); - for (i = 0; i < active_nr;) { - const struct cache_entry *ce = active_cache[i++]; + for (i = 0; i < istate->cache_nr;) { + const struct cache_entry *ce = istate->cache[i++]; if (ce_stage(ce)) { strbuf_commented_addf(msgbuf, "\t%s\n", ce->name); - while (i < active_nr && !strcmp(ce->name, - active_cache[i]->name)) + while (i < istate->cache_nr && + !strcmp(ce->name, istate->cache[i]->name)) i++; } } } -static int do_recursive_merge(struct commit *base, struct commit *next, +static int do_recursive_merge(struct repository *r, + struct commit *base, struct commit *next, const char *base_label, const char *next_label, struct object_id *head, struct strbuf *msgbuf, struct replay_opts *opts) @@ -524,12 +541,12 @@ static int do_recursive_merge(struct commit *base, struct commit *next, 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_cache(); + 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)"; @@ -538,8 +555,8 @@ static int do_recursive_merge(struct commit *base, struct commit *next, o.show_rename_progress = 1; head_tree = parse_tree_indirect(head); - next_tree = next ? get_commit_tree(next) : empty_tree(); - base_tree = base ? get_commit_tree(base) : empty_tree(); + next_tree = next ? get_commit_tree(next) : empty_tree(r); + base_tree = base ? get_commit_tree(base) : empty_tree(r); for (xopt = opts->xopts; xopt != opts->xopts + opts->xopts_nr; xopt++) parse_merge_opt(&o, *xopt); @@ -556,7 +573,7 @@ static int do_recursive_merge(struct commit *base, struct commit *next, return clean; } - if (write_locked_index(&the_index, &index_lock, + if (write_locked_index(r->index, &index_lock, COMMIT_LOCK | SKIP_IF_UNCHANGED)) /* * TRANSLATORS: %s will be "revert", "cherry-pick" or @@ -566,34 +583,35 @@ static int do_recursive_merge(struct commit *base, struct commit *next, _(action_name(opts))); if (!clean) - append_conflicts_hint(msgbuf); + append_conflicts_hint(r->index, msgbuf); return !clean; } -static struct object_id *get_cache_tree_oid(void) +static struct object_id *get_cache_tree_oid(struct index_state *istate) { - if (!active_cache_tree) - active_cache_tree = cache_tree(); + if (!istate->cache_tree) + istate->cache_tree = cache_tree(); - if (!cache_tree_fully_valid(active_cache_tree)) - if (cache_tree_update(&the_index, 0)) { + if (!cache_tree_fully_valid(istate->cache_tree)) + if (cache_tree_update(istate, 0)) { error(_("unable to update cache tree")); return NULL; } - return &active_cache_tree->oid; + return &istate->cache_tree->oid; } -static int is_index_unchanged(void) +static int is_index_unchanged(struct repository *r) { struct object_id head_oid, *cache_tree_oid; struct commit *head_commit; + struct index_state *istate = r->index; if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, &head_oid, NULL)) return error(_("could not resolve HEAD commit")); - head_commit = lookup_commit(&head_oid); + head_commit = lookup_commit(r, &head_oid); /* * If head_commit is NULL, check_commit, called from @@ -606,10 +624,10 @@ static int is_index_unchanged(void) if (parse_commit(head_commit)) return -1; - if (!(cache_tree_oid = get_cache_tree_oid())) + if (!(cache_tree_oid = get_cache_tree_oid(istate))) return -1; - return !oidcmp(cache_tree_oid, get_commit_tree_oid(head_commit)); + return oideq(cache_tree_oid, get_commit_tree_oid(head_commit)); } static int write_author_script(const char *message) @@ -638,7 +656,7 @@ missing_author: else if (*message != '\'') strbuf_addch(&buf, *(message++)); else - strbuf_addf(&buf, "'\\\\%c'", *(message++)); + strbuf_addf(&buf, "'\\%c'", *(message++)); strbuf_addstr(&buf, "'\nGIT_AUTHOR_EMAIL='"); while (*message && *message != '\n' && *message != '\r') if (skip_prefix(message, "> ", &message)) @@ -646,45 +664,144 @@ missing_author: else if (*message != '\'') strbuf_addch(&buf, *(message++)); else - strbuf_addf(&buf, "'\\\\%c'", *(message++)); + strbuf_addf(&buf, "'\\%c'", *(message++)); strbuf_addstr(&buf, "'\nGIT_AUTHOR_DATE='@"); while (*message && *message != '\n' && *message != '\r') if (*message != '\'') strbuf_addch(&buf, *(message++)); else - strbuf_addf(&buf, "'\\\\%c'", *(message++)); + strbuf_addf(&buf, "'\\%c'", *(message++)); + strbuf_addch(&buf, '\''); res = write_message(buf.buf, buf.len, rebase_path_author_script(), 1); strbuf_release(&buf); return res; } +/** + * Take a series of KEY='VALUE' lines where VALUE part is + * sq-quoted, and append <KEY, VALUE> at the end of the string list + */ +static int parse_key_value_squoted(char *buf, struct string_list *list) +{ + while (*buf) { + struct string_list_item *item; + char *np; + char *cp = strchr(buf, '='); + if (!cp) { + np = strchrnul(buf, '\n'); + return error(_("no key present in '%.*s'"), + (int) (np - buf), buf); + } + np = strchrnul(cp, '\n'); + *cp++ = '\0'; + item = string_list_append(list, buf); + + buf = np + (*np == '\n'); + *np = '\0'; + cp = sq_dequote(cp); + if (!cp) + return error(_("unable to dequote value of '%s'"), + item->string); + item->util = xstrdup(cp); + } + return 0; +} + +/** + * Reads and parses the state directory's "author-script" file, and sets name, + * email and date accordingly. + * Returns 0 on success, -1 if the file could not be parsed. + * + * The author script is of the format: + * + * GIT_AUTHOR_NAME='$author_name' + * GIT_AUTHOR_EMAIL='$author_email' + * GIT_AUTHOR_DATE='$author_date' + * + * where $author_name, $author_email and $author_date are quoted. We are strict + * with our parsing, as the file was meant to be eval'd in the old + * git-am.sh/git-rebase--interactive.sh scripts, and thus if the file differs + * from what this function expects, it is better to bail out than to do + * something that the user does not expect. + */ +int read_author_script(const char *path, char **name, char **email, char **date, + int allow_missing) +{ + struct strbuf buf = STRBUF_INIT; + struct string_list kv = STRING_LIST_INIT_DUP; + int retval = -1; /* assume failure */ + int i, name_i = -2, email_i = -2, date_i = -2, err = 0; + + if (strbuf_read_file(&buf, path, 256) <= 0) { + strbuf_release(&buf); + if (errno == ENOENT && allow_missing) + return 0; + else + return error_errno(_("could not open '%s' for reading"), + path); + } + + if (parse_key_value_squoted(buf.buf, &kv)) + goto finish; + + for (i = 0; i < kv.nr; i++) { + if (!strcmp(kv.items[i].string, "GIT_AUTHOR_NAME")) { + if (name_i != -2) + name_i = error(_("'GIT_AUTHOR_NAME' already given")); + else + name_i = i; + } else if (!strcmp(kv.items[i].string, "GIT_AUTHOR_EMAIL")) { + if (email_i != -2) + email_i = error(_("'GIT_AUTHOR_EMAIL' already given")); + else + email_i = i; + } else if (!strcmp(kv.items[i].string, "GIT_AUTHOR_DATE")) { + if (date_i != -2) + date_i = error(_("'GIT_AUTHOR_DATE' already given")); + else + date_i = i; + } else { + err = error(_("unknown variable '%s'"), + kv.items[i].string); + } + } + if (name_i == -2) + error(_("missing 'GIT_AUTHOR_NAME'")); + if (email_i == -2) + error(_("missing 'GIT_AUTHOR_EMAIL'")); + if (date_i == -2) + error(_("missing 'GIT_AUTHOR_DATE'")); + if (date_i < 0 || email_i < 0 || date_i < 0 || err) + goto finish; + *name = kv.items[name_i].util; + *email = kv.items[email_i].util; + *date = kv.items[date_i].util; + retval = 0; +finish: + string_list_clear(&kv, !!retval); + strbuf_release(&buf); + return retval; +} + /* - * Read a list of environment variable assignments (such as the author-script - * file) into an environment block. Returns -1 on error, 0 otherwise. + * Read a GIT_AUTHOR_NAME, GIT_AUTHOR_EMAIL AND GIT_AUTHOR_DATE from a + * file with shell quoting into struct argv_array. Returns -1 on + * error, 0 otherwise. */ static int read_env_script(struct argv_array *env) { - struct strbuf script = STRBUF_INIT; - int i, count = 0; - char *p, *p2; + char *name, *email, *date; - if (strbuf_read_file(&script, rebase_path_author_script(), 256) <= 0) + if (read_author_script(rebase_path_author_script(), + &name, &email, &date, 0)) return -1; - for (p = script.buf; *p; p++) - if (skip_prefix(p, "'\\\\''", (const char **)&p2)) - strbuf_splice(&script, p - script.buf, p2 - p, "'", 1); - else if (*p == '\'') - strbuf_splice(&script, p-- - script.buf, 1, "", 0); - else if (*p == '\n') { - *p = '\0'; - count++; - } - - for (i = 0, p = script.buf; i < count; i++) { - argv_array_push(env, p); - p += strlen(p) + 1; - } + argv_array_pushf(env, "GIT_AUTHOR_NAME=%s", name); + argv_array_pushf(env, "GIT_AUTHOR_EMAIL=%s", email); + argv_array_pushf(env, "GIT_AUTHOR_DATE=%s", date); + free(name); + free(email); + free(date); return 0; } @@ -704,46 +821,28 @@ static char *get_author(const char *message) /* Read author-script and return an ident line (author <email> timestamp) */ static const char *read_author_ident(struct strbuf *buf) { - const char *keys[] = { - "GIT_AUTHOR_NAME=", "GIT_AUTHOR_EMAIL=", "GIT_AUTHOR_DATE=" - }; - char *in, *out, *eol; - int i = 0, len; + struct strbuf out = STRBUF_INIT; + char *name, *email, *date; - if (strbuf_read_file(buf, rebase_path_author_script(), 256) <= 0) + if (read_author_script(rebase_path_author_script(), + &name, &email, &date, 0)) return NULL; - /* dequote values and construct ident line in-place */ - for (in = out = buf->buf; i < 3 && in - buf->buf < buf->len; i++) { - if (!skip_prefix(in, keys[i], (const char **)&in)) { - warning("could not parse '%s' (looking for '%s'", - rebase_path_author_script(), keys[i]); - return NULL; - } - - eol = strchrnul(in, '\n'); - *eol = '\0'; - sq_dequote(in); - len = strlen(in); - - if (i > 0) /* separate values by spaces */ - *(out++) = ' '; - if (i == 1) /* email needs to be surrounded by <...> */ - *(out++) = '<'; - memmove(out, in, len); - out += len; - if (i == 1) /* email needs to be surrounded by <...> */ - *(out++) = '>'; - in = eol + 1; - } - - if (i < 3) { - warning("could not parse '%s' (looking for '%s')", - rebase_path_author_script(), keys[i]); + /* validate date since fmt_ident() will die() on bad value */ + if (parse_date(date, &out)){ + warning(_("invalid date format '%s' in '%s'"), + date, rebase_path_author_script()); + strbuf_release(&out); return NULL; } - buf->len = out - buf->buf; + strbuf_reset(&out); + strbuf_addstr(&out, fmt_ident(name, email, date, 0)); + strbuf_swap(buf, &out); + strbuf_release(&out); + free(name); + free(email); + free(date); return buf->buf; } @@ -768,6 +867,23 @@ N_("you have staged changes in your working tree\n" #define VERIFY_MSG (1<<4) #define CREATE_ROOT_COMMIT (1<<5) +static int run_command_silent_on_success(struct child_process *cmd) +{ + struct strbuf buf = STRBUF_INIT; + int rc; + + cmd->stdout_to_stderr = 1; + rc = pipe_command(cmd, + NULL, 0, + NULL, 0, + &buf, 0); + + if (rc) + fputs(buf.buf, stderr); + strbuf_release(&buf); + return rc; +} + /* * If we are cherry-pick, and if the merge did not result in * hand-editing, we will hit this commit and inherit the original @@ -780,7 +896,9 @@ N_("you have staged changes in your working tree\n" * interactive rebase: in that case, we will want to retain the * author metadata. */ -static int run_git_commit(const char *defmsg, struct replay_opts *opts, +static int run_git_commit(struct repository *r, + const char *defmsg, + struct replay_opts *opts, unsigned int flags) { struct child_process cmd = CHILD_PROCESS_INIT; @@ -788,15 +906,22 @@ static int run_git_commit(const char *defmsg, struct replay_opts *opts, if ((flags & CREATE_ROOT_COMMIT) && !(flags & AMEND_MSG)) { struct strbuf msg = STRBUF_INIT, script = STRBUF_INIT; - const char *author = is_rebase_i(opts) ? - read_author_ident(&script) : NULL; + const char *author = NULL; struct object_id root_commit, *cache_tree_oid; int res = 0; + if (is_rebase_i(opts)) { + author = read_author_ident(&script); + if (!author) { + strbuf_release(&script); + return -1; + } + } + if (!defmsg) BUG("root commit without message"); - if (!(cache_tree_oid = get_cache_tree_oid())) + if (!(cache_tree_oid = get_cache_tree_oid(r->index))) res = -1; if (!res) @@ -822,18 +947,11 @@ static int run_git_commit(const char *defmsg, struct replay_opts *opts, cmd.git_cmd = 1; - if (is_rebase_i(opts)) { - if (!(flags & EDIT_MSG)) { - cmd.stdout_to_stderr = 1; - cmd.err = -1; - } + if (is_rebase_i(opts) && read_env_script(&cmd.env_array)) { + const char *gpg_opt = gpg_sign_opt_quoted(opts); - if (read_env_script(&cmd.env_array)) { - const char *gpg_opt = gpg_sign_opt_quoted(opts); - - return error(_(staged_changes_advice), - gpg_opt, gpg_opt); - } + return error(_(staged_changes_advice), + gpg_opt, gpg_opt); } argv_array_push(&cmd.args, "commit"); @@ -860,24 +978,13 @@ static int run_git_commit(const char *defmsg, struct replay_opts *opts, if ((flags & ALLOW_EMPTY)) argv_array_push(&cmd.args, "--allow-empty"); - if (opts->allow_empty_message) + if (!(flags & EDIT_MSG)) argv_array_push(&cmd.args, "--allow-empty-message"); - if (cmd.err == -1) { - /* hide stderr on success */ - struct strbuf buf = STRBUF_INIT; - int rc = pipe_command(&cmd, - NULL, 0, - /* stdout is already redirected */ - NULL, 0, - &buf, 0); - if (rc) - fputs(buf.buf, stderr); - strbuf_release(&buf); - return rc; - } - - return run_command(&cmd); + if (is_rebase_i(opts) && !(flags & EDIT_MSG)) + return run_command_silent_on_success(&cmd); + else + return run_command(&cmd); } static int rest_is_empty(const struct strbuf *sb, int start) @@ -1009,7 +1116,8 @@ static int run_rewrite_hook(const struct object_id *oldoid, 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; @@ -1018,12 +1126,14 @@ void commit_post_rewrite(const struct commit *old_head, 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); } -static int run_prepare_commit_msg_hook(struct strbuf *msg, const char *commit) +static int run_prepare_commit_msg_hook(struct repository *r, + struct strbuf *msg, + const char *commit) { struct argv_array hook_env = ARGV_ARRAY_INIT; int ret; @@ -1033,7 +1143,7 @@ static int run_prepare_commit_msg_hook(struct strbuf *msg, const char *commit) if (write_message(msg->buf, msg->len, name, 0)) return -1; - argv_array_pushf(&hook_env, "GIT_INDEX_FILE=%s", get_index_file()); + argv_array_pushf(&hook_env, "GIT_INDEX_FILE=%s", r->index_file); argv_array_push(&hook_env, "GIT_EDITOR=:"); if (commit) ret = run_hook_le(hook_env.argv, "prepare-commit-msg", name, @@ -1089,7 +1199,9 @@ static const char *implicit_ident_advice(void) } -void print_commit_summary(const char *prefix, const struct object_id *oid, +void print_commit_summary(struct repository *r, + const char *prefix, + const struct object_id *oid, unsigned int flags) { struct rev_info rev; @@ -1100,7 +1212,7 @@ void print_commit_summary(const char *prefix, const struct object_id *oid, struct strbuf author_ident = STRBUF_INIT; struct strbuf committer_ident = STRBUF_INIT; - commit = lookup_commit(oid); + commit = lookup_commit(r, oid); if (!commit) die(_("couldn't look up newly created commit")); if (parse_commit(commit)) @@ -1133,7 +1245,7 @@ void print_commit_summary(const char *prefix, const struct object_id *oid, strbuf_release(&author_ident); strbuf_release(&committer_ident); - init_revisions(&rev, prefix); + repo_init_revisions(r, &rev, prefix); setup_revisions(0, NULL, &rev, NULL); rev.diff = 1; @@ -1167,7 +1279,7 @@ void print_commit_summary(const char *prefix, const struct object_id *oid, strbuf_release(&format); } -static int parse_head(struct commit **head) +static int parse_head(struct repository *r, struct commit **head) { struct commit *current_head; struct object_id oid; @@ -1175,10 +1287,10 @@ static int parse_head(struct commit **head) if (get_oid("HEAD", &oid)) { current_head = NULL; } else { - current_head = lookup_commit_reference(&oid); + current_head = lookup_commit_reference(r, &oid); if (!current_head) return error(_("could not parse HEAD")); - if (oidcmp(&oid, ¤t_head->object.oid)) { + if (!oideq(&oid, ¤t_head->object.oid)) { warning(_("HEAD %s is not a commit!"), oid_to_hex(&oid)); } @@ -1199,7 +1311,8 @@ static int parse_head(struct commit **head) * 0 - success * 1 - run 'git commit' */ -static int try_to_commit(struct strbuf *msg, const char *author, +static int try_to_commit(struct repository *r, + struct strbuf *msg, const char *author, struct replay_opts *opts, unsigned int flags, struct object_id *oid) { @@ -1214,7 +1327,7 @@ static int try_to_commit(struct strbuf *msg, const char *author, enum commit_msg_cleanup_mode cleanup; int res = 0; - if (parse_head(¤t_head)) + if (parse_head(r, ¤t_head)) return -1; if (flags & AMEND_MSG) { @@ -1243,20 +1356,20 @@ static int try_to_commit(struct strbuf *msg, const char *author, commit_list_insert(current_head, &parents); } - if (write_cache_as_tree(&tree, 0, NULL)) { + if (write_index_as_tree(&tree, r->index, r->index_file, 0, NULL)) { res = error(_("git write-tree failed to write a tree")); goto out; } - if (!(flags & ALLOW_EMPTY) && !oidcmp(current_head ? - get_commit_tree_oid(current_head) : - the_hash_algo->empty_tree, &tree)) { + if (!(flags & ALLOW_EMPTY) && oideq(current_head ? + get_commit_tree_oid(current_head) : + the_hash_algo->empty_tree, &tree)) { res = 1; /* run 'git commit' to display error message */ goto out; } if (find_hook("prepare-commit-msg")) { - res = run_prepare_commit_msg_hook(msg, hook_commit); + res = run_prepare_commit_msg_hook(r, msg, hook_commit); if (res) goto out; if (strbuf_read_file(&commit_msg, git_path_commit_editmsg(), @@ -1274,7 +1387,7 @@ static int try_to_commit(struct strbuf *msg, const char *author, if (cleanup != COMMIT_MSG_CLEANUP_NONE) strbuf_stripspace(msg, cleanup == COMMIT_MSG_CLEANUP_ALL); - if (!opts->allow_empty_message && message_is_empty(msg, cleanup)) { + if ((flags & EDIT_MSG) && message_is_empty(msg, cleanup)) { res = 1; /* run 'git commit' to display error message */ goto out; } @@ -1294,7 +1407,7 @@ static int try_to_commit(struct strbuf *msg, const char *author, } if (flags & AMEND_MSG) - commit_post_rewrite(current_head, oid); + commit_post_rewrite(r, current_head, oid); out: free_commit_extra_headers(extra); @@ -1305,7 +1418,8 @@ out: return res; } -static int do_commit(const char *msg_file, const char *author, +static int do_commit(struct repository *r, + const char *msg_file, const char *author, struct replay_opts *opts, unsigned int flags) { int res = 1; @@ -1320,20 +1434,20 @@ static int do_commit(const char *msg_file, const char *author, "from '%s'"), msg_file); - res = try_to_commit(msg_file ? &sb : NULL, author, opts, flags, - &oid); + res = try_to_commit(r, msg_file ? &sb : NULL, + author, opts, flags, &oid); strbuf_release(&sb); if (!res) { - unlink(git_path_cherry_pick_head()); - unlink(git_path_merge_msg()); + unlink(git_path_cherry_pick_head(r)); + unlink(git_path_merge_msg(r)); if (!is_rebase_i(opts)) - print_commit_summary(NULL, &oid, + print_commit_summary(r, NULL, &oid, SUMMARY_SHOW_AUTHOR_DATE); return res; } } if (res == 1) - return run_git_commit(msg_file, opts, flags); + return run_git_commit(r, msg_file, opts, flags); return res; } @@ -1355,13 +1469,15 @@ static int is_original_commit_empty(struct commit *commit) ptree_oid = the_hash_algo->empty_tree; /* commit is root */ } - return !oidcmp(ptree_oid, get_commit_tree_oid(commit)); + return oideq(ptree_oid, get_commit_tree_oid(commit)); } /* * Do we run "git commit" with "--allow-empty"? */ -static int allow_empty(struct replay_opts *opts, struct commit *commit) +static int allow_empty(struct repository *r, + struct replay_opts *opts, + struct commit *commit) { int index_unchanged, empty_commit; @@ -1378,7 +1494,7 @@ static int allow_empty(struct replay_opts *opts, struct commit *commit) if (!opts->allow_empty) return 0; /* let "git commit" barf as necessary */ - index_unchanged = is_index_unchanged(); + index_unchanged = is_index_unchanged(r); if (index_unchanged < 0) return index_unchanged; if (!index_unchanged) @@ -1411,6 +1527,7 @@ enum todo_command { TODO_SQUASH, /* commands that do something else than handling a single commit */ TODO_EXEC, + TODO_BREAK, TODO_LABEL, TODO_RESET, TODO_MERGE, @@ -1432,6 +1549,7 @@ static struct { { 'f', "fixup" }, { 's', "squash" }, { 'x', "exec" }, + { 'b', "break" }, { 'l', "label" }, { 't', "reset" }, { 'm', "merge" }, @@ -1444,7 +1562,7 @@ static const char *command_to_string(const enum todo_command command) { if (command < TODO_COMMENT) return todo_command_info[command].str; - die("Unknown command: %d", command); + die(_("unknown command: %d"), command); } static char command_to_char(const enum todo_command command) @@ -1480,8 +1598,10 @@ static int is_pick_or_similar(enum todo_command command) } } -static int update_squash_messages(enum todo_command command, - struct commit *commit, struct replay_opts *opts) +static int update_squash_messages(struct repository *r, + enum todo_command command, + struct commit *commit, + struct replay_opts *opts) { struct strbuf buf = STRBUF_INIT; int res; @@ -1510,7 +1630,7 @@ static int update_squash_messages(enum todo_command command, if (get_oid("HEAD", &head)) return error(_("need a HEAD to fixup")); - if (!(head_commit = lookup_commit_reference(&head))) + if (!(head_commit = lookup_commit_reference(r, &head))) return error(_("could not read HEAD")); if (!(head_message = get_commit_buffer(head_commit, NULL))) return error(_("could not read HEAD's commit message")); @@ -1542,13 +1662,13 @@ static int update_squash_messages(enum todo_command command, unlink(rebase_path_fixup_msg()); strbuf_addf(&buf, "\n%c ", comment_line_char); strbuf_addf(&buf, _("This is the commit message #%d:"), - ++opts->current_fixup_count); + ++opts->current_fixup_count + 1); strbuf_addstr(&buf, "\n\n"); strbuf_addstr(&buf, body); } else if (command == TODO_FIXUP) { strbuf_addf(&buf, "\n%c ", comment_line_char); strbuf_addf(&buf, _("The commit message #%d will be skipped:"), - ++opts->current_fixup_count); + ++opts->current_fixup_count + 1); strbuf_addstr(&buf, "\n\n"); strbuf_add_commented_lines(&buf, body, strlen(body)); } else @@ -1571,7 +1691,8 @@ static int update_squash_messages(enum todo_command command, 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; @@ -1596,7 +1717,8 @@ static void flush_rewritten_pending(void) { } 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) @@ -1609,11 +1731,14 @@ static void record_in_rewritten(struct object_id *oid, flush_rewritten_pending(); } -static int do_pick_commit(enum todo_command command, struct commit *commit, - struct replay_opts *opts, int final_fixup) +static int do_pick_commit(struct repository *r, + enum todo_command command, + struct commit *commit, + struct replay_opts *opts, + int final_fixup) { unsigned int flags = opts->edit ? EDIT_MSG : 0; - const char *msg_file = opts->edit ? NULL : git_path_merge_msg(); + const char *msg_file = opts->edit ? NULL : git_path_merge_msg(r); struct object_id head; struct commit *base, *next, *parent; const char *base_label, *next_label; @@ -1629,24 +1754,24 @@ static int do_pick_commit(enum todo_command command, struct commit *commit, * that represents the "current" state for merge-recursive * to work on. */ - if (write_cache_as_tree(&head, 0, NULL)) + if (write_index_as_tree(&head, r->index, r->index_file, 0, NULL)) return error(_("your index file is unmerged.")); } else { unborn = get_oid("HEAD", &head); /* Do we want to generate a root commit? */ if (is_pick_or_similar(command) && opts->have_squash_onto && - !oidcmp(&head, &opts->squash_onto)) { + oideq(&head, &opts->squash_onto)) { if (is_fixup(command)) return error(_("cannot fixup root commit")); flags |= CREATE_ROOT_COMMIT; unborn = 1; } else if (unborn) oidcpy(&head, the_hash_algo->empty_tree); - if (index_differs_from(unborn ? empty_tree_oid_hex() : "HEAD", + if (index_differs_from(r, unborn ? empty_tree_oid_hex() : "HEAD", NULL, 0)) - return error_dirty_index(opts); + return error_dirty_index(r, opts); } - discard_cache(); + discard_index(r->index); if (!commit->parents) parent = NULL; @@ -1667,9 +1792,13 @@ static int do_pick_commit(enum todo_command command, struct commit *commit, 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; @@ -1678,11 +1807,11 @@ static int do_pick_commit(enum todo_command command, struct commit *commit, oid_to_hex(&commit->object.oid)); if (opts->allow_ff && !is_fixup(command) && - ((parent && !oidcmp(&parent->object.oid, &head)) || + ((parent && oideq(&parent->object.oid, &head)) || (!parent && unborn))) { if (is_rebase_i(opts)) write_author_script(msg.message); - res = fast_forward_to(&commit->object.oid, &head, unborn, + res = fast_forward_to(r, &commit->object.oid, &head, unborn, opts); if (res || command != TODO_REWORD) goto leave; @@ -1746,7 +1875,7 @@ static int do_pick_commit(enum todo_command command, struct commit *commit, if (command == TODO_REWORD) flags |= EDIT_MSG | VERIFY_MSG; else if (is_fixup(command)) { - if (update_squash_messages(command, commit, opts)) + if (update_squash_messages(r, command, commit, opts)) return -1; flags |= AMEND_MSG; if (!final_fixup) @@ -1755,12 +1884,12 @@ static int do_pick_commit(enum todo_command command, struct commit *commit, flags |= CLEANUP_MSG; msg_file = rebase_path_fixup_msg(); } else { - const char *dest = git_path_squash_msg(); + const char *dest = git_path_squash_msg(r); unlink(dest); if (copy_file(dest, rebase_path_squash_msg(), 0666)) return error(_("could not rename '%s' to '%s'"), rebase_path_squash_msg(), dest); - unlink(git_path_merge_msg()); + unlink(git_path_merge_msg(r)); msg_file = dest; flags |= EDIT_MSG; } @@ -1772,23 +1901,23 @@ static int do_pick_commit(enum todo_command command, struct commit *commit, if (is_rebase_i(opts) && write_author_script(msg.message) < 0) res = -1; else if (!opts->strategy || !strcmp(opts->strategy, "recursive") || command == TODO_REVERT) { - res = do_recursive_merge(base, next, base_label, next_label, + res = do_recursive_merge(r, base, next, base_label, next_label, &head, &msgbuf, opts); if (res < 0) goto leave; res |= write_message(msgbuf.buf, msgbuf.len, - git_path_merge_msg(), 0); + git_path_merge_msg(r), 0); } else { struct commit_list *common = NULL; struct commit_list *remotes = NULL; res = write_message(msgbuf.buf, msgbuf.len, - git_path_merge_msg(), 0); + git_path_merge_msg(r), 0); commit_list_insert(base, &common); commit_list_insert(next, &remotes); - res |= try_merge_command(opts->strategy, + res |= try_merge_command(r, opts->strategy, opts->xopts_nr, (const char **)opts->xopts, common, oid_to_hex(&head), remotes); free_commit_list(common); @@ -1816,12 +1945,12 @@ static int do_pick_commit(enum todo_command command, struct commit *commit, ? _("could not revert %s... %s") : _("could not apply %s... %s"), short_commit_name(commit), msg.subject); - print_advice(res == 1, opts); - rerere(opts->allow_rerere_auto); + print_advice(r, res == 1, opts); + repo_rerere(r, opts->allow_rerere_auto); goto leave; } - allow = allow_empty(opts, commit); + allow = allow_empty(r, opts, commit); if (allow < 0) { res = allow; goto leave; @@ -1830,7 +1959,7 @@ static int do_pick_commit(enum todo_command command, struct commit *commit, if (!opts->no_commit) { fast_forward_edit: if (author || command == TODO_REVERT || (flags & AMEND_MSG)) - res = do_commit(msg_file, author, opts, flags); + res = do_commit(r, msg_file, author, opts, flags); else res = error(_("unable to parse commit author")); } @@ -1863,23 +1992,22 @@ static int prepare_revs(struct replay_opts *opts) if (prepare_revision_walk(opts->revs)) return error(_("revision walk setup failed")); - if (!opts->revs->commits) - return error(_("empty commit set passed")); return 0; } -static int read_and_refresh_cache(struct replay_opts *opts) +static int read_and_refresh_cache(struct repository *r, + struct replay_opts *opts) { struct lock_file index_lock = LOCK_INIT; - int index_fd = hold_locked_index(&index_lock, 0); - if (read_index_preload(&the_index, NULL) < 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))); } - refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, NULL, NULL, NULL); + refresh_index(r->index, REFRESH_QUIET|REFRESH_UNMERGED, NULL, NULL, NULL); if (index_fd >= 0) { - if (write_locked_index(&the_index, &index_lock, + if (write_locked_index(r->index, &index_lock, COMMIT_LOCK | SKIP_IF_UNCHANGED)) { return error(_("git %s: failed to refresh the index"), _(action_name(opts))); @@ -1924,7 +2052,8 @@ static struct todo_item *append_new_todo(struct todo_list *todo_list) return todo_list->items + todo_list->nr++; } -static int parse_insn_line(struct todo_item *item, const char *bol, char *eol) +static int parse_insn_line(struct repository *r, struct todo_item *item, + const char *bol, char *eol) { struct object_id commit_oid; char *end_of_object_name; @@ -1947,7 +2076,8 @@ static int parse_insn_line(struct todo_item *item, const char *bol, char *eol) if (skip_prefix(bol, todo_command_info[i].str, &bol)) { item->command = i; break; - } else if (bol[1] == ' ' && *bol == todo_command_info[i].c) { + } else if ((bol + 1 == eol || bol[1] == ' ') && + *bol == todo_command_info[i].c) { bol++; item->command = i; break; @@ -1959,7 +2089,7 @@ static int parse_insn_line(struct todo_item *item, const char *bol, char *eol) padding = strspn(bol, " \t"); bol += padding; - if (item->command == TODO_NOOP) { + if (item->command == TODO_NOOP || item->command == TODO_BREAK) { if (bol != eol) return error(_("%s does not accept arguments: '%s'"), command_to_string(item->command), bol); @@ -2008,11 +2138,12 @@ static int parse_insn_line(struct todo_item *item, const char *bol, char *eol) if (status < 0) return -1; - item->commit = lookup_commit_reference(&commit_oid); + item->commit = lookup_commit_reference(r, &commit_oid); return !item->commit; } -static int parse_insn_buffer(char *buf, struct todo_list *todo_list) +static int parse_insn_buffer(struct repository *r, char *buf, + struct todo_list *todo_list) { struct todo_item *item; char *p = buf, *next_p; @@ -2028,7 +2159,7 @@ static int parse_insn_buffer(char *buf, struct todo_list *todo_list) item = append_new_todo(todo_list); item->offset_in_buf = p - todo_list->buf.buf; - if (parse_insn_line(item, p, eol)) { + if (parse_insn_line(r, item, p, eol)) { res = error(_("invalid line %d: %.*s"), i, (int)(eol - p), p); item->command = TODO_NOOP; @@ -2089,8 +2220,9 @@ static ssize_t strbuf_read_file_or_whine(struct strbuf *sb, const char *path) return len; } -static int read_populate_todo(struct todo_list *todo_list, - struct replay_opts *opts) +static int read_populate_todo(struct repository *r, + struct todo_list *todo_list, + struct replay_opts *opts) { struct stat st; const char *todo_file = get_todo_path(opts); @@ -2105,7 +2237,7 @@ static int read_populate_todo(struct todo_list *todo_list, return error(_("could not stat '%s'"), todo_file); fill_stat_data(&todo_list->stat, &st); - res = parse_insn_buffer(todo_list->buf.buf, todo_list); + res = parse_insn_buffer(r, todo_list->buf.buf, todo_list); if (res) { if (is_rebase_i(opts)) return error(_("please fix this using " @@ -2136,7 +2268,7 @@ static int read_populate_todo(struct todo_list *todo_list, FILE *f = fopen_or_warn(rebase_path_msgtotal(), "w"); if (strbuf_read_file(&done.buf, rebase_path_done(), 0) > 0 && - !parse_insn_buffer(done.buf.buf, &done)) + !parse_insn_buffer(r, done.buf.buf, &done)) todo_list->done_nr = count_commands(&done); else todo_list->done_nr = 0; @@ -2203,18 +2335,16 @@ static int populate_opts_cb(const char *key, const char *value, void *data) return 0; } -static void read_strategy_opts(struct replay_opts *opts, struct strbuf *buf) +void parse_strategy_opts(struct replay_opts *opts, char *raw_opts) { int i; + char *strategy_opts_string = raw_opts; - strbuf_reset(buf); - if (!read_oneliner(buf, rebase_path_strategy(), 0)) - return; - opts->strategy = strbuf_detach(buf, NULL); - if (!read_oneliner(buf, rebase_path_strategy_opts(), 0)) - return; + if (*strategy_opts_string == ' ') + strategy_opts_string++; - opts->xopts_nr = split_cmdline(buf->buf, (const char ***)&opts->xopts); + opts->xopts_nr = split_cmdline(strategy_opts_string, + (const char ***)&opts->xopts); for (i = 0; i < opts->xopts_nr; i++) { const char *arg = opts->xopts[i]; @@ -2223,6 +2353,18 @@ static void read_strategy_opts(struct replay_opts *opts, struct strbuf *buf) } } +static void read_strategy_opts(struct replay_opts *opts, struct strbuf *buf) +{ + strbuf_reset(buf); + if (!read_oneliner(buf, rebase_path_strategy(), 0)) + return; + opts->strategy = strbuf_detach(buf, NULL); + if (!read_oneliner(buf, rebase_path_strategy_opts(), 0)) + return; + + parse_strategy_opts(opts, buf->buf); +} + static int read_populate_opts(struct replay_opts *opts) { if (is_rebase_i(opts)) { @@ -2249,11 +2391,17 @@ static int read_populate_opts(struct replay_opts *opts) 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); @@ -2290,6 +2438,54 @@ static int read_populate_opts(struct replay_opts *opts) return 0; } +static void write_strategy_opts(struct replay_opts *opts) +{ + int i; + struct strbuf buf = STRBUF_INIT; + + for (i = 0; i < opts->xopts_nr; ++i) + strbuf_addf(&buf, " --%s", opts->xopts[i]); + + write_file(rebase_path_strategy_opts(), "%s\n", buf.buf); + strbuf_release(&buf); +} + +int write_basic_state(struct replay_opts *opts, const char *head_name, + const char *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); + if (orig_head) + write_file(rebase_path_orig_head(), "%s\n", orig_head); + + if (quiet) + write_file(rebase_path_quiet(), "%s\n", quiet); + if (opts->verbose) + write_file(rebase_path_verbose(), "%s", ""); + if (opts->strategy) + write_file(rebase_path_strategy(), "%s\n", opts->strategy); + if (opts->xopts_nr > 0) + write_strategy_opts(opts); + + if (opts->allow_rerere_auto == RERERE_AUTOUPDATE) + write_file(rebase_path_allow_rerere_autoupdate(), "--rerere-autoupdate\n"); + else if (opts->allow_rerere_auto == RERERE_NOAUTOUPDATE) + write_file(rebase_path_allow_rerere_autoupdate(), "--no-rerere-autoupdate\n"); + + if (opts->gpg_sign) + 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; +} + static int walk_revs_populate_todo(struct todo_list *todo_list, struct replay_opts *opts) { @@ -2317,6 +2513,10 @@ static int walk_revs_populate_todo(struct todo_list *todo_list, short_commit_name(commit), subject_len, subject); unuse_commit_buffer(commit, commit_buffer); } + + if (!todo_list->nr) + return error(_("empty commit set passed")); + return 0; } @@ -2376,7 +2576,7 @@ static int rollback_is_safe(void) if (get_oid("HEAD", &actual_head)) oidclr(&actual_head); - return !oidcmp(&actual_head, &expected_head); + return oideq(&actual_head, &expected_head); } static int reset_for_rollback(const struct object_id *oid) @@ -2390,12 +2590,12 @@ static int reset_for_rollback(const struct object_id *oid) return run_command_v_opt(argv, RUN_GIT_CMD); } -static int rollback_single_pick(void) +static int rollback_single_pick(struct repository *r) { struct object_id head_oid; - if (!file_exists(git_path_cherry_pick_head()) && - !file_exists(git_path_revert_head())) + if (!file_exists(git_path_cherry_pick_head(r)) && + !file_exists(git_path_revert_head(r))) return error(_("no cherry-pick or revert in progress")); if (read_ref_full("HEAD", 0, &head_oid, NULL)) return error(_("cannot resolve HEAD")); @@ -2404,7 +2604,7 @@ static int rollback_single_pick(void) return reset_for_rollback(&head_oid); } -int sequencer_rollback(struct replay_opts *opts) +int sequencer_rollback(struct repository *r, struct replay_opts *opts) { FILE *f; struct object_id oid; @@ -2418,7 +2618,7 @@ int sequencer_rollback(struct replay_opts *opts) * If CHERRY_PICK_HEAD or REVERT_HEAD indicates * a single-cherry-pick in progress, abort that. */ - return rollback_single_pick(); + return rollback_single_pick(r); } if (!f) return error_errno(_("cannot open '%s'"), git_path_head_file()); @@ -2533,7 +2733,9 @@ static int save_opts(struct replay_opts *opts) return res; } -static int make_patch(struct commit *commit, struct replay_opts *opts) +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; @@ -2549,7 +2751,7 @@ static int make_patch(struct commit *commit, struct replay_opts *opts) strbuf_addf(&buf, "%s/patch", get_dir(opts)); memset(&log_tree_opt, 0, sizeof(log_tree_opt)); - init_revisions(&log_tree_opt, NULL); + repo_init_revisions(r, &log_tree_opt, NULL); log_tree_opt.abbrev = 0; log_tree_opt.diff = 1; log_tree_opt.diffopt.output_format = DIFF_FORMAT_PATCH; @@ -2589,45 +2791,67 @@ static int intend_to_amend(void) return write_message(p, strlen(p), rebase_path_amend(), 1); } -static int error_with_patch(struct commit *commit, - const char *subject, int subject_len, - struct replay_opts *opts, int exit_code, int to_amend) +static int error_with_patch(struct repository *r, + struct commit *commit, + const char *subject, int subject_len, + struct replay_opts *opts, + int exit_code, int to_amend) { - if (make_patch(commit, opts)) - return -1; + if (commit) { + if (make_patch(r, commit, opts)) + return -1; + } else if (copy_file(rebase_path_message(), + git_path_merge_msg(r), 0666)) + return error(_("unable to copy '%s' to '%s'"), + git_path_merge_msg(r), rebase_path_message()); if (to_amend) { if (intend_to_amend()) return -1; - fprintf(stderr, "You can amend the commit now, with\n" - "\n" - " git commit --amend %s\n" - "\n" - "Once you are satisfied with your changes, run\n" - "\n" - " git rebase --continue\n", gpg_sign_opt_quoted(opts)); - } else if (exit_code) - fprintf(stderr, "Could not apply %s... %.*s\n", - short_commit_name(commit), subject_len, subject); + fprintf(stderr, + _("You can amend the commit now, with\n" + "\n" + " git commit --amend %s\n" + "\n" + "Once you are satisfied with your changes, run\n" + "\n" + " git rebase --continue\n"), + gpg_sign_opt_quoted(opts)); + } else if (exit_code) { + if (commit) + fprintf_ln(stderr, _("Could not apply %s... %.*s"), + short_commit_name(commit), subject_len, subject); + else + /* + * We don't have the hash of the parent so + * just print the line from the todo file. + */ + fprintf_ln(stderr, _("Could not merge %.*s"), + subject_len, subject); + } return exit_code; } -static int error_failed_squash(struct commit *commit, - struct replay_opts *opts, int subject_len, const char *subject) +static int error_failed_squash(struct repository *r, + struct commit *commit, + struct replay_opts *opts, + int subject_len, + const char *subject) { if (copy_file(rebase_path_message(), rebase_path_squash_msg(), 0666)) return error(_("could not copy '%s' to '%s'"), rebase_path_squash_msg(), rebase_path_message()); - unlink(git_path_merge_msg()); - if (copy_file(git_path_merge_msg(), rebase_path_message(), 0666)) + unlink(git_path_merge_msg(r)); + if (copy_file(git_path_merge_msg(r), rebase_path_message(), 0666)) return error(_("could not copy '%s' to '%s'"), - rebase_path_message(), git_path_merge_msg()); - return error_with_patch(commit, subject, subject_len, opts, 1, 0); + rebase_path_message(), + git_path_merge_msg(r)); + return error_with_patch(r, commit, subject, subject_len, opts, 1, 0); } -static int do_exec(const char *command_line) +static int do_exec(struct repository *r, const char *command_line) { struct argv_array child_env = ARGV_ARRAY_INIT; const char *child_argv[] = { NULL, NULL }; @@ -2636,14 +2860,16 @@ static int do_exec(const char *command_line) fprintf(stderr, "Executing: %s\n", command_line); child_argv[0] = command_line; argv_array_pushf(&child_env, "GIT_DIR=%s", absolute_path(get_git_dir())); + argv_array_pushf(&child_env, "GIT_WORK_TREE=%s", + absolute_path(get_git_work_tree())); status = run_command_v_opt_cd_env(child_argv, RUN_USING_SHELL, NULL, child_env.argv); /* force re-reading of the cache */ - if (discard_cache() < 0 || read_cache() < 0) + if (discard_index(r->index) < 0 || repo_read_index(r) < 0) return error(_("could not read index")); - dirty = require_clean_work_tree("rebase", NULL, 1, 1); + dirty = require_clean_work_tree(r, "rebase", NULL, 1, 1); if (status) { warning(_("execution failed: %s\n%s" @@ -2709,9 +2935,9 @@ static int safe_append(const char *filename, const char *fmt, ...) return 0; } -static int do_label(const char *name, int len) +static int do_label(struct repository *r, const char *name, int len) { - struct ref_store *refs = get_main_ref_store(the_repository); + struct ref_store *refs = get_main_ref_store(r); struct ref_transaction *transaction; struct strbuf ref_name = STRBUF_INIT, err = STRBUF_INIT; struct strbuf msg = STRBUF_INIT; @@ -2719,7 +2945,7 @@ static int do_label(const char *name, int len) struct object_id head_oid; if (len == 1 && *name == '#') - return error("Illegal label name: '%.*s'", len, name); + return error(_("illegal label name: '%.*s'"), len, name); strbuf_addf(&ref_name, "refs/rewritten/%.*s", len, name); strbuf_addf(&msg, "rebase -i (label) '%.*s'", len, name); @@ -2752,7 +2978,9 @@ static int do_label(const char *name, int len) static const char *reflog_message(struct replay_opts *opts, const char *sub_action, const char *fmt, ...); -static int do_reset(const char *name, int len, struct replay_opts *opts) +static int do_reset(struct repository *r, + const char *name, int len, + struct replay_opts *opts) { struct strbuf ref_name = STRBUF_INIT; struct object_id oid; @@ -2760,9 +2988,9 @@ static int do_reset(const char *name, int len, struct replay_opts *opts) struct tree_desc desc; struct tree *tree; struct unpack_trees_options unpack_tree_opts; - int ret = 0, i; + 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)) { @@ -2780,10 +3008,13 @@ static int do_reset(const char *name, int len, struct replay_opts *opts) } oidcpy(&oid, &opts->squash_onto); } else { + int i; + /* Determine the length of the label */ for (i = 0; i < len; i++) if (isspace(name[i])) - len = i; + break; + len = i; strbuf_addf(&ref_name, "refs/rewritten/%.*s", len, name); if (get_oid(ref_name.buf, &oid) && @@ -2798,13 +3029,13 @@ static int do_reset(const char *name, int len, struct replay_opts *opts) memset(&unpack_tree_opts, 0, sizeof(unpack_tree_opts)); setup_unpack_trees_porcelain(&unpack_tree_opts, "reset"); unpack_tree_opts.head_idx = 1; - unpack_tree_opts.src_index = &the_index; - unpack_tree_opts.dst_index = &the_index; + unpack_tree_opts.src_index = r->index; + unpack_tree_opts.dst_index = r->index; unpack_tree_opts.fn = oneway_merge; unpack_tree_opts.merge = 1; unpack_tree_opts.update = 1; - if (read_cache_unmerged()) { + if (repo_read_index_unmerged(r)) { rollback_lock_file(&lock); strbuf_release(&ref_name); return error_resolve_conflict(_(action_name(opts))); @@ -2826,9 +3057,9 @@ static int do_reset(const char *name, int len, struct replay_opts *opts) } tree = parse_tree_indirect(&oid); - prime_cache_tree(&the_index, tree); + prime_cache_tree(r, r->index, tree); - if (write_locked_index(&the_index, &lock, COMMIT_LOCK) < 0) + if (write_locked_index(r->index, &lock, COMMIT_LOCK) < 0) ret = error(_("could not write index")); free((void *)desc.buffer); @@ -2841,7 +3072,29 @@ static int do_reset(const char *name, int len, struct replay_opts *opts) return ret; } -static int do_merge(struct commit *commit, const char *arg, int arg_len, +static struct commit *lookup_label(const char *label, int len, + struct strbuf *buf) +{ + struct commit *commit; + + strbuf_reset(buf); + strbuf_addf(buf, "refs/rewritten/%.*s", len, label); + commit = lookup_commit_reference_by_name(buf->buf); + if (!commit) { + /* fall back to non-rewritten ref or commit */ + strbuf_splice(buf, 0, strlen("refs/rewritten/"), "", 0); + commit = lookup_commit_reference_by_name(buf->buf); + } + + if (!commit) + error(_("could not resolve '%s'"), buf->buf); + + return commit; +} + +static int do_merge(struct repository *r, + struct commit *commit, + const char *arg, int arg_len, int flags, struct replay_opts *opts) { int run_commit_flags = (flags & TODO_EDIT_MERGE_MSG) ? @@ -2849,12 +3102,13 @@ static int do_merge(struct commit *commit, const char *arg, int arg_len, struct strbuf ref_name = STRBUF_INIT; struct commit *head_commit, *merge_commit, *i; struct commit_list *bases, *j, *reversed = NULL; + struct commit_list *to_merge = NULL, **tail = &to_merge; struct merge_options o; - int merge_arg_len, oneline_offset, can_fast_forward, ret; + int merge_arg_len, oneline_offset, can_fast_forward, ret, k; 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; } @@ -2865,38 +3119,51 @@ static int do_merge(struct commit *commit, const char *arg, int arg_len, goto leave_merge; } - oneline_offset = arg_len; - merge_arg_len = strcspn(arg, " \t\n"); - p = arg + merge_arg_len; - p += strspn(p, " \t\n"); - if (*p == '#' && (!p[1] || isspace(p[1]))) { - p += 1 + strspn(p + 1, " \t\n"); - oneline_offset = p - arg; - } else if (p - arg < arg_len) - BUG("octopus merges are not supported yet: '%s'", p); - - strbuf_addf(&ref_name, "refs/rewritten/%.*s", merge_arg_len, arg); - merge_commit = lookup_commit_reference_by_name(ref_name.buf); - if (!merge_commit) { - /* fall back to non-rewritten ref or commit */ - strbuf_splice(&ref_name, 0, strlen("refs/rewritten/"), "", 0); - merge_commit = lookup_commit_reference_by_name(ref_name.buf); + /* + * For octopus merges, the arg starts with the list of revisions to be + * merged. The list is optionally followed by '#' and the oneline. + */ + merge_arg_len = oneline_offset = arg_len; + for (p = arg; p - arg < arg_len; p += strspn(p, " \t\n")) { + if (!*p) + break; + if (*p == '#' && (!p[1] || isspace(p[1]))) { + p += 1 + strspn(p + 1, " \t\n"); + oneline_offset = p - arg; + break; + } + k = strcspn(p, " \t\n"); + if (!k) + continue; + merge_commit = lookup_label(p, k, &ref_name); + if (!merge_commit) { + ret = error(_("unable to parse '%.*s'"), k, p); + goto leave_merge; + } + tail = &commit_list_insert(merge_commit, tail)->next; + p += k; + merge_arg_len = p - arg; } - if (!merge_commit) { - ret = error(_("could not resolve '%s'"), ref_name.buf); + if (!to_merge) { + ret = error(_("nothing to merge: '%.*s'"), arg_len, arg); goto leave_merge; } if (opts->have_squash_onto && - !oidcmp(&head_commit->object.oid, &opts->squash_onto)) { + oideq(&head_commit->object.oid, &opts->squash_onto)) { /* * When the user tells us to "merge" something into a * "[new root]", let's simply fast-forward to the merge head. */ rollback_lock_file(&lock); - ret = fast_forward_to(&merge_commit->object.oid, - &head_commit->object.oid, 0, opts); + if (to_merge->next) + ret = error(_("octopus merge cannot be executed on " + "top of a [new root]")); + else + ret = fast_forward_to(r, &to_merge->item->object.oid, + &head_commit->object.oid, 0, + opts); goto leave_merge; } @@ -2913,11 +3180,11 @@ static int do_merge(struct commit *commit, const char *arg, int arg_len, write_author_script(message); find_commit_subject(message, &body); len = strlen(body); - ret = write_message(body, len, git_path_merge_msg(), 0); + ret = write_message(body, len, git_path_merge_msg(r), 0); unuse_commit_buffer(commit, message); if (ret) { error_errno(_("could not write '%s'"), - git_path_merge_msg()); + git_path_merge_msg(r)); goto leave_merge; } } else { @@ -2932,17 +3199,18 @@ static int do_merge(struct commit *commit, const char *arg, int arg_len, p = arg + oneline_offset; len = arg_len - oneline_offset; } else { - strbuf_addf(&buf, "Merge branch '%.*s'", + strbuf_addf(&buf, "Merge %s '%.*s'", + to_merge->next ? "branches" : "branch", merge_arg_len, arg); p = buf.buf; len = buf.len; } - ret = write_message(p, len, git_path_merge_msg(), 0); + ret = write_message(p, len, git_path_merge_msg(r), 0); strbuf_release(&buf); if (ret) { error_errno(_("could not write '%s'"), - git_path_merge_msg()); + git_path_merge_msg(r)); goto leave_merge; } } @@ -2952,50 +3220,99 @@ static int do_merge(struct commit *commit, const char *arg, int arg_len, * commit, we cannot fast-forward. */ can_fast_forward = opts->allow_ff && commit && commit->parents && - !oidcmp(&commit->parents->item->object.oid, - &head_commit->object.oid); + oideq(&commit->parents->item->object.oid, + &head_commit->object.oid); /* - * If the merge head is different from the original one, we cannot + * If any merge head is different from the original one, we cannot * fast-forward. */ if (can_fast_forward) { - struct commit_list *second_parent = commit->parents->next; + struct commit_list *p = commit->parents->next; - if (second_parent && !second_parent->next && - oidcmp(&merge_commit->object.oid, - &second_parent->item->object.oid)) + for (j = to_merge; j && p; j = j->next, p = p->next) + if (!oideq(&j->item->object.oid, + &p->item->object.oid)) { + can_fast_forward = 0; + break; + } + /* + * If the number of merge heads differs from the original merge + * commit, we cannot fast-forward. + */ + if (j || p) can_fast_forward = 0; } - if (can_fast_forward && commit->parents->next && - !commit->parents->next->next && - !oidcmp(&commit->parents->next->item->object.oid, - &merge_commit->object.oid)) { + if (can_fast_forward) { rollback_lock_file(&lock); - ret = fast_forward_to(&commit->object.oid, + ret = fast_forward_to(r, &commit->object.oid, &head_commit->object.oid, 0, opts); goto leave_merge; } - write_message(oid_to_hex(&merge_commit->object.oid), GIT_SHA1_HEXSZ, - git_path_merge_head(), 0); - write_message("no-ff", 5, git_path_merge_mode(), 0); + if (to_merge->next) { + /* Octopus merge */ + struct child_process cmd = CHILD_PROCESS_INIT; + if (read_env_script(&cmd.env_array)) { + const char *gpg_opt = gpg_sign_opt_quoted(opts); + + ret = error(_(staged_changes_advice), gpg_opt, gpg_opt); + goto leave_merge; + } + + cmd.git_cmd = 1; + argv_array_push(&cmd.args, "merge"); + argv_array_push(&cmd.args, "-s"); + argv_array_push(&cmd.args, "octopus"); + argv_array_push(&cmd.args, "--no-edit"); + argv_array_push(&cmd.args, "--no-ff"); + argv_array_push(&cmd.args, "--no-log"); + argv_array_push(&cmd.args, "--no-stat"); + argv_array_push(&cmd.args, "-F"); + argv_array_push(&cmd.args, git_path_merge_msg(r)); + if (opts->gpg_sign) + argv_array_push(&cmd.args, opts->gpg_sign); + + /* Add the tips to be merged */ + for (j = to_merge; j; j = j->next) + argv_array_push(&cmd.args, + oid_to_hex(&j->item->object.oid)); + + strbuf_release(&ref_name); + unlink(git_path_cherry_pick_head(r)); + rollback_lock_file(&lock); + + rollback_lock_file(&lock); + ret = run_command(&cmd); + + /* force re-reading of the cache */ + if (!ret && (discard_index(r->index) < 0 || + repo_read_index(r) < 0)) + ret = error(_("could not read index")); + goto leave_merge; + } + + merge_commit = to_merge->item; bases = get_merge_bases(head_commit, merge_commit); - if (bases && !oidcmp(&merge_commit->object.oid, - &bases->item->object.oid)) { + if (bases && oideq(&merge_commit->object.oid, + &bases->item->object.oid)) { ret = 0; /* skip merging an ancestor of HEAD */ goto leave_merge; } + write_message(oid_to_hex(&merge_commit->object.oid), GIT_SHA1_HEXSZ, + git_path_merge_head(r), 0); + write_message("no-ff", 5, git_path_merge_mode(r), 0); + for (j = bases; j; j = j->next) commit_list_insert(j->item, &reversed); free_commit_list(bases); - read_cache(); - 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; @@ -3019,27 +3336,28 @@ static int do_merge(struct commit *commit, const char *arg, int arg_len, */ ret = !ret; - if (active_cache_changed && - write_locked_index(&the_index, &lock, COMMIT_LOCK)) { + if (r->index->cache_changed && + write_locked_index(r->index, &lock, COMMIT_LOCK)) { ret = error(_("merge: Unable to write new index file")); goto leave_merge; } rollback_lock_file(&lock); if (ret) - rerere(opts->allow_rerere_auto); + repo_rerere(r, opts->allow_rerere_auto); else /* * In case of problems, we now want to return a positive * value (a negative one would indicate that the `merge` * command needs to be rescheduled). */ - ret = !!run_git_commit(git_path_merge_msg(), opts, - run_commit_flags); + ret = !!run_git_commit(r, git_path_merge_msg(r), opts, + run_commit_flags); leave_merge: strbuf_release(&ref_name); rollback_lock_file(&lock); + free_commit_list(to_merge); return ret; } @@ -3133,6 +3451,73 @@ static const char *reflog_message(struct replay_opts *opts, return buf.buf; } +static int run_git_checkout(struct replay_opts *opts, const char *commit, + const char *action) +{ + struct child_process cmd = CHILD_PROCESS_INIT; + + cmd.git_cmd = 1; + + argv_array_push(&cmd.args, "checkout"); + argv_array_push(&cmd.args, commit); + argv_array_pushf(&cmd.env_array, GIT_REFLOG_ACTION "=%s", action); + + if (opts->verbose) + return run_command(&cmd); + else + return run_command_silent_on_success(&cmd); +} + +int prepare_branch_to_be_rebased(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)) + return error(_("could not checkout %s"), commit); + } + + return 0; +} + +static int checkout_onto(struct replay_opts *opts, + const char *onto_name, const char *onto, + const char *orig_head) +{ + struct object_id oid; + const char *action = reflog_message(opts, "start", "checkout %s", onto_name); + + if (get_oid(orig_head, &oid)) + return error(_("%s: not a valid OID"), orig_head); + + if (run_git_checkout(opts, onto, action)) { + apply_autostash(opts); + sequencer_remove_state(opts); + return error(_("could not detach HEAD")); + } + + return update_ref(NULL, "ORIG_HEAD", &oid, NULL, 0, UPDATE_REFS_MSG_ON_ERR); +} + +static int stopped_at_head(struct repository *r) +{ + struct object_id head; + struct commit *commit; + struct commit_message message; + + if (get_oid("HEAD", &head) || + !(commit = lookup_commit(r, &head)) || + parse_commit(commit) || get_message(commit, &message)) + fprintf(stderr, _("Stopped at HEAD\n")); + else { + fprintf(stderr, _("Stopped at %s\n"), message.label); + free_message(commit, &message); + } + return 0; + +} + static const char rescheduled_advice[] = N_("Could not execute the todo command\n" "\n" @@ -3144,7 +3529,9 @@ N_("Could not execute the todo command\n" " git rebase --edit-todo\n" " git rebase --continue\n"); -static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts) +static int pick_commits(struct repository *r, + struct todo_list *todo_list, + struct replay_opts *opts) { int res = 0, reschedule = 0; @@ -3152,7 +3539,7 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts) if (opts->allow_ff) assert(!(opts->signoff || opts->no_commit || opts->record_origin || opts->edit)); - if (read_and_refresh_cache(opts)) + if (read_and_refresh_cache(r, opts)) return -1; while (todo_list->current < todo_list->nr) { @@ -3169,23 +3556,28 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts) 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()); unlink(rebase_path_stopped_sha()); unlink(rebase_path_amend()); + unlink(git_path_merge_head(the_repository)); delete_ref(NULL, "REBASE_HEAD", NULL, REF_NO_DEREF); + + if (item->command == TODO_BREAK) + return stopped_at_head(r); } if (item->command <= TODO_SQUASH) { if (is_rebase_i(opts)) setenv("GIT_REFLOG_ACTION", reflog_message(opts, command_to_string(item->command), NULL), 1); - res = do_pick_commit(item->command, item->commit, + res = do_pick_commit(r, item->command, item->commit, opts, is_final_fixup(todo_list)); if (is_rebase_i(opts) && res < 0) { /* Reschedule */ @@ -3205,7 +3597,7 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts) _("Stopped at %s... %.*s\n"), short_commit_name(commit), item->arg_len, item->arg); - return error_with_patch(commit, + return error_with_patch(r, commit, item->arg, item->arg_len, opts, res, !res); } @@ -3215,42 +3607,60 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts) if (res && is_fixup(item->command)) { if (res == 1) intend_to_amend(); - return error_failed_squash(item->commit, opts, + return error_failed_squash(r, item->commit, opts, item->arg_len, item->arg); - } else if (res && is_rebase_i(opts) && item->commit) - return res | error_with_patch(item->commit, - item->arg, item->arg_len, opts, res, - item->command == TODO_REWORD); + } 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 && + !get_oid("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, + item->arg, item->arg_len, opts, + res, to_amend); + } } else if (item->command == TODO_EXEC) { char *end_of_arg = (char *)(item->arg + item->arg_len); int saved = *end_of_arg; struct stat st; *end_of_arg = '\0'; - res = do_exec(item->arg); + res = do_exec(r, item->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)) { todo_list_release(todo_list); - if (read_populate_todo(todo_list, opts)) + if (read_populate_todo(r, todo_list, opts)) res = -1; /* message was printed */ /* `current` will be incremented below */ todo_list->current = -1; } } else if (item->command == TODO_LABEL) { - if ((res = do_label(item->arg, item->arg_len))) + if ((res = do_label(r, item->arg, item->arg_len))) reschedule = 1; } else if (item->command == TODO_RESET) { - if ((res = do_reset(item->arg, item->arg_len, opts))) + if ((res = do_reset(r, item->arg, item->arg_len, opts))) reschedule = 1; } else if (item->command == TODO_MERGE) { - if ((res = do_merge(item->commit, + if ((res = do_merge(r, item->commit, item->arg, item->arg_len, item->flags, opts)) < 0) reschedule = 1; @@ -3259,7 +3669,7 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts) peek_command(todo_list, 1)); if (res > 0) /* failed with merge conflicts */ - return error_with_patch(item->commit, + return error_with_patch(r, item->commit, item->arg, item->arg_len, opts, res, 0); @@ -3275,7 +3685,8 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts) if (save_todo(todo_list, opts)) return -1; if (item->commit) - return error_with_patch(item->commit, + return error_with_patch(r, + item->commit, item->arg, item->arg_len, opts, res, 0); @@ -3340,7 +3751,7 @@ cleanup_head_ref: struct object_id orig, head; memset(&log_tree_opt, 0, sizeof(log_tree_opt)); - init_revisions(&log_tree_opt, NULL); + repo_init_revisions(r, &log_tree_opt, NULL); log_tree_opt.diff = 1; log_tree_opt.diffopt.output_format = DIFF_FORMAT_DIFFSTAT; @@ -3383,8 +3794,10 @@ cleanup_head_ref: } 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); @@ -3397,26 +3810,27 @@ cleanup_head_ref: return sequencer_remove_state(opts); } -static int continue_single_pick(void) +static int continue_single_pick(struct repository *r) { const char *argv[] = { "commit", NULL }; - if (!file_exists(git_path_cherry_pick_head()) && - !file_exists(git_path_revert_head())) + if (!file_exists(git_path_cherry_pick_head(r)) && + !file_exists(git_path_revert_head(r))) return error(_("no cherry-pick or revert in progress")); return run_command_v_opt(argv, RUN_GIT_CMD); } -static int commit_staged_changes(struct replay_opts *opts, +static int commit_staged_changes(struct repository *r, + struct replay_opts *opts, struct todo_list *todo_list) { unsigned int flags = ALLOW_EMPTY | EDIT_MSG; unsigned int final_fixup = 0, is_clean; - if (has_unstaged_changes(1)) + if (has_unstaged_changes(r, 1)) return error(_("cannot rebase: You have unstaged changes.")); - is_clean = !has_uncommitted_changes(0); + is_clean = !has_uncommitted_changes(r, 0); if (file_exists(rebase_path_amend())) { struct strbuf rev = STRBUF_INIT; @@ -3429,7 +3843,7 @@ static int commit_staged_changes(struct replay_opts *opts, if (get_oid_hex(rev.buf, &to_amend)) return error(_("invalid contents: '%s'"), rebase_path_amend()); - if (!is_clean && oidcmp(&head, &to_amend)) + if (!is_clean && !oideq(&head, &to_amend)) return error(_("\nYou have uncommitted changes in your " "working tree. Please, commit them\n" "first and then run 'git rebase " @@ -3441,9 +3855,20 @@ static int commit_staged_changes(struct replay_opts *opts, * the commit message and if there was a squash, let the user * edit it. */ - if (is_clean && !oidcmp(&head, &to_amend) && - opts->current_fixup_count > 0 && - file_exists(rebase_path_stopped_sha())) { + if (!is_clean || !opts->current_fixup_count) + ; /* this is not the final fixup */ + else if (!oideq(&head, &to_amend) || + !file_exists(rebase_path_stopped_sha())) { + /* was a final fixup or squash done manually? */ + if (!is_fixup(peek_command(todo_list, 0))) { + unlink(rebase_path_fixup_msg()); + unlink(rebase_path_squash_msg()); + unlink(rebase_path_current_fixups()); + strbuf_reset(&opts->current_fixups); + opts->current_fixup_count = 0; + } + } else { + /* we are in a fixup/squash chain */ const char *p = opts->current_fixups.buf; int len = opts->current_fixups.len; @@ -3489,7 +3914,7 @@ static int commit_staged_changes(struct replay_opts *opts, struct commit *commit; const char *path = rebase_path_squash_msg(); - if (parse_head(&commit) || + if (parse_head(r, &commit) || !(p = get_commit_buffer(commit, NULL)) || write_message(p, strlen(p), path, 0)) { unuse_commit_buffer(commit, p); @@ -3505,7 +3930,7 @@ static int commit_staged_changes(struct replay_opts *opts, } if (is_clean) { - const char *cherry_pick_head = git_path_cherry_pick_head(); + const char *cherry_pick_head = git_path_cherry_pick_head(r); if (file_exists(cherry_pick_head) && unlink(cherry_pick_head)) return error(_("could not remove CHERRY_PICK_HEAD")); @@ -3513,10 +3938,11 @@ static int commit_staged_changes(struct replay_opts *opts, return 0; } - if (run_git_commit(final_fixup ? NULL : rebase_path_message(), + if (run_git_commit(r, final_fixup ? NULL : rebase_path_message(), opts, flags)) return error(_("could not commit staged changes.")); unlink(rebase_path_amend()); + unlink(git_path_merge_head(the_repository)); if (final_fixup) { unlink(rebase_path_fixup_msg()); unlink(rebase_path_squash_msg()); @@ -3533,36 +3959,36 @@ static int commit_staged_changes(struct replay_opts *opts, return 0; } -int sequencer_continue(struct replay_opts *opts) +int sequencer_continue(struct repository *r, struct replay_opts *opts) { struct todo_list todo_list = TODO_LIST_INIT; int res; - if (read_and_refresh_cache(opts)) + if (read_and_refresh_cache(r, opts)) return -1; if (read_populate_opts(opts)) return -1; if (is_rebase_i(opts)) { - if ((res = read_populate_todo(&todo_list, opts))) + if ((res = read_populate_todo(r, &todo_list, opts))) goto release_todo_list; - if (commit_staged_changes(opts, &todo_list)) + if (commit_staged_changes(r, opts, &todo_list)) return -1; } else if (!file_exists(get_todo_path(opts))) - return continue_single_pick(); - else if ((res = read_populate_todo(&todo_list, opts))) + return continue_single_pick(r); + else if ((res = read_populate_todo(r, &todo_list, opts))) goto release_todo_list; if (!is_rebase_i(opts)) { /* Verify that the conflict has been resolved */ - if (file_exists(git_path_cherry_pick_head()) || - file_exists(git_path_revert_head())) { - res = continue_single_pick(); + if (file_exists(git_path_cherry_pick_head(r)) || + file_exists(git_path_revert_head(r))) { + res = continue_single_pick(r); if (res) goto release_todo_list; } - if (index_differs_from("HEAD", NULL, 0)) { - res = error_dirty_index(opts); + if (index_differs_from(r, "HEAD", NULL, 0)) { + res = error_dirty_index(r, opts); goto release_todo_list; } todo_list.current++; @@ -3576,27 +4002,30 @@ int sequencer_continue(struct replay_opts *opts) strbuf_release(&buf); } - res = pick_commits(&todo_list, opts); + res = pick_commits(r, &todo_list, opts); release_todo_list: todo_list_release(&todo_list); return res; } -static int single_pick(struct commit *cmit, struct replay_opts *opts) +static int single_pick(struct repository *r, + struct commit *cmit, + struct replay_opts *opts) { setenv(GIT_REFLOG_ACTION, action_name(opts), 0); - return do_pick_commit(opts->action == REPLAY_PICK ? + return do_pick_commit(r, opts->action == REPLAY_PICK ? TODO_PICK : TODO_REVERT, cmit, opts, 0); } -int sequencer_pick_revisions(struct replay_opts *opts) +int sequencer_pick_revisions(struct repository *r, + struct replay_opts *opts) { struct todo_list todo_list = TODO_LIST_INIT; struct object_id oid; int i, res; assert(opts->revs); - if (read_and_refresh_cache(opts)) + if (read_and_refresh_cache(r, opts)) return -1; for (i = 0; i < opts->revs->pending.nr; i++) { @@ -3608,8 +4037,8 @@ int sequencer_pick_revisions(struct replay_opts *opts) continue; if (!get_oid(name, &oid)) { - if (!lookup_commit_reference_gently(&oid, 1)) { - enum object_type type = oid_object_info(the_repository, + if (!lookup_commit_reference_gently(r, &oid, 1)) { + enum object_type type = oid_object_info(r, &oid, NULL); return error(_("%s: can't cherry-pick a %s"), @@ -3634,9 +4063,11 @@ int sequencer_pick_revisions(struct replay_opts *opts) if (prepare_revision_walk(opts->revs)) return error(_("revision walk setup failed")); cmit = get_revision(opts->revs); - if (!cmit || get_revision(opts->revs)) - return error("BUG: expected exactly one commit from walk"); - return single_pick(cmit, opts); + if (!cmit) + return error(_("empty commit set passed")); + if (get_revision(opts->revs)) + BUG("unexpected extra commit from walk"); + return single_pick(r, cmit, opts); } /* @@ -3655,12 +4086,12 @@ int sequencer_pick_revisions(struct replay_opts *opts) if (save_opts(opts)) return -1; update_abort_safety_file(); - res = pick_commits(&todo_list, opts); + res = pick_commits(r, &todo_list, opts); todo_list_release(&todo_list); return res; } -void append_signoff(struct strbuf *msgbuf, int ignore_footer, unsigned flag) +void append_signoff(struct strbuf *msgbuf, size_t ignore_footer, unsigned flag) { unsigned no_dup_sob = flag & APPEND_SIGNOFF_DEDUP; struct strbuf sob = STRBUF_INIT; @@ -3877,7 +4308,6 @@ static int make_script_with_merges(struct pretty_print_context *pp, */ while ((commit = get_revision(revs))) { struct commit_list *to_merge; - int is_octopus; const char *p1, *p2; struct object_id *oid; int is_empty; @@ -3909,11 +4339,6 @@ static int make_script_with_merges(struct pretty_print_context *pp, continue; } - is_octopus = to_merge && to_merge->next; - - if (is_octopus) - BUG("Octopus merges not yet supported"); - /* Create a label */ strbuf_reset(&label); if (skip_prefix(oneline.buf, "Merge ", &p1) && @@ -3935,13 +4360,17 @@ static int make_script_with_merges(struct pretty_print_context *pp, strbuf_addf(&buf, "%s -C %s", cmd_merge, oid_to_hex(&commit->object.oid)); - /* label the tip of merged branch */ - oid = &to_merge->item->object.oid; - strbuf_addch(&buf, ' '); + /* label the tips of merged branches */ + for (; to_merge; to_merge = to_merge->next) { + oid = &to_merge->item->object.oid; + strbuf_addch(&buf, ' '); + + if (!oidset_contains(&interesting, oid)) { + strbuf_addstr(&buf, label_oid(oid, NULL, + &state)); + continue; + } - if (!oidset_contains(&interesting, oid)) - strbuf_addstr(&buf, label_oid(oid, NULL, &state)); - else { tips_tail = &commit_list_insert(to_merge->item, tips_tail)->next; @@ -3965,9 +4394,7 @@ static int make_script_with_merges(struct pretty_print_context *pp, struct object_id *oid = &parent->item->object.oid; if (!oidset_contains(&interesting, oid)) continue; - if (!oidset_contains(&child_seen, oid)) - oidset_insert(&child_seen, oid); - else + if (oidset_insert(&child_seen, oid)) label_oid(oid, "branch-point", &state); } @@ -3994,7 +4421,7 @@ static int make_script_with_merges(struct pretty_print_context *pp, entry = oidmap_get(&state.commit2label, &commit->object.oid); if (entry) - fprintf(out, "\n# Branch %s\n", entry->string); + fprintf(out, "\n%c Branch %s\n", comment_line_char, entry->string); else fprintf(out, "\n"); @@ -4063,7 +4490,8 @@ static int make_script_with_merges(struct pretty_print_context *pp, return 0; } -int sequencer_make_script(FILE *out, int argc, const char **argv, +int sequencer_make_script(struct repository *r, FILE *out, + int argc, const char **argv, unsigned flags) { char *format = NULL; @@ -4075,7 +4503,7 @@ int sequencer_make_script(FILE *out, int argc, const char **argv, const char *insn = flags & TODO_LIST_ABBREVIATE_CMDS ? "p" : "pick"; int rebase_merges = flags & TODO_LIST_REBASE_MERGES; - init_revisions(&revs, NULL); + repo_init_revisions(r, &revs, NULL); revs.verbose_header = 1; if (!rebase_merges) revs.max_parents = 1; @@ -4128,43 +4556,64 @@ int sequencer_make_script(FILE *out, int argc, const char **argv, * Add commands after pick and (series of) squash/fixup commands * in the todo list. */ -int sequencer_add_exec_commands(const char *commands) +int sequencer_add_exec_commands(struct repository *r, + const char *commands) { const char *todo_file = rebase_path_todo(); struct todo_list todo_list = TODO_LIST_INIT; - struct todo_item *item; struct strbuf *buf = &todo_list.buf; size_t offset = 0, commands_len = strlen(commands); - int i, first; + int i, insert; if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0) return error(_("could not read '%s'."), todo_file); - if (parse_insn_buffer(todo_list.buf.buf, &todo_list)) { + if (parse_insn_buffer(r, todo_list.buf.buf, &todo_list)) { todo_list_release(&todo_list); return error(_("unusable todo list: '%s'"), todo_file); } - first = 1; - /* insert <commands> before every pick except the first one */ - for (item = todo_list.items, i = 0; i < todo_list.nr; i++, item++) { - if (item->command == TODO_PICK && !first) { - strbuf_insert(buf, item->offset_in_buf + offset, - commands, commands_len); + /* + * Insert <commands> after every pick. Here, fixup/squash chains + * are considered part of the pick, so we insert the commands *after* + * those chains if there are any. + */ + insert = -1; + for (i = 0; i < todo_list.nr; i++) { + enum todo_command command = todo_list.items[i].command; + + if (insert >= 0) { + /* skip fixup/squash chains */ + if (command == TODO_COMMENT) + continue; + else if (is_fixup(command)) { + insert = i + 1; + continue; + } + strbuf_insert(buf, + todo_list.items[insert].offset_in_buf + + offset, commands, commands_len); offset += commands_len; + insert = -1; } - first = 0; + + if (command == TODO_PICK || command == TODO_MERGE) + insert = i + 1; } - /* append final <commands> */ - strbuf_add(buf, commands, commands_len); + /* insert or append final <commands> */ + if (insert >= 0 && insert < todo_list.nr) + strbuf_insert(buf, todo_list.items[insert].offset_in_buf + + offset, commands, commands_len); + else if (insert >= 0 || !offset) + strbuf_add(buf, commands, commands_len); i = write_message(buf->buf, buf->len, todo_file, 0); todo_list_release(&todo_list); return i; } -int transform_todos(unsigned flags) +int transform_todos(struct repository *r, unsigned flags) { const char *todo_file = rebase_path_todo(); struct todo_list todo_list = TODO_LIST_INIT; @@ -4175,7 +4624,7 @@ int transform_todos(unsigned flags) if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0) return error(_("could not read '%s'."), todo_file); - if (parse_insn_buffer(todo_list.buf.buf, &todo_list)) { + if (parse_insn_buffer(r, todo_list.buf.buf, &todo_list)) { todo_list_release(&todo_list); return error(_("unusable todo list: '%s'"), todo_file); } @@ -4221,24 +4670,20 @@ int transform_todos(unsigned flags) return i; } -enum check_level { - CHECK_IGNORE = 0, CHECK_WARN, CHECK_ERROR -}; - -static enum check_level get_missing_commit_check_level(void) +enum missing_commit_check_level get_missing_commit_check_level(void) { const char *value; if (git_config_get_value("rebase.missingcommitscheck", &value) || !strcasecmp("ignore", value)) - return CHECK_IGNORE; + return MISSING_COMMIT_CHECK_IGNORE; if (!strcasecmp("warn", value)) - return CHECK_WARN; + return MISSING_COMMIT_CHECK_WARN; if (!strcasecmp("error", value)) - return CHECK_ERROR; + return MISSING_COMMIT_CHECK_ERROR; warning(_("unrecognized setting %s for option " "rebase.missingCommitsCheck. Ignoring."), value); - return CHECK_IGNORE; + return MISSING_COMMIT_CHECK_IGNORE; } define_commit_slab(commit_seen, unsigned char); @@ -4248,9 +4693,9 @@ define_commit_slab(commit_seen, unsigned char); * Check if there is an unrecognized command or a * bad SHA-1 in a command. */ -int check_todo_list(void) +int check_todo_list(struct repository *r) { - enum check_level check_level = get_missing_commit_check_level(); + enum missing_commit_check_level check_level = get_missing_commit_check_level(); struct strbuf todo_file = STRBUF_INIT; struct todo_list todo_list = TODO_LIST_INIT; struct strbuf missing = STRBUF_INIT; @@ -4265,9 +4710,9 @@ int check_todo_list(void) goto leave_check; } advise_to_edit_todo = res = - parse_insn_buffer(todo_list.buf.buf, &todo_list); + parse_insn_buffer(r, todo_list.buf.buf, &todo_list); - if (res || check_level == CHECK_IGNORE) + if (res || check_level == MISSING_COMMIT_CHECK_IGNORE) goto leave_check; /* Mark the commits in git-rebase-todo as seen */ @@ -4284,7 +4729,7 @@ int check_todo_list(void) goto leave_check; } strbuf_release(&todo_file); - res = !!parse_insn_buffer(todo_list.buf.buf, &todo_list); + res = !!parse_insn_buffer(r, todo_list.buf.buf, &todo_list); /* Find commits in git-rebase-todo.backup yet unseen */ for (i = todo_list.nr - 1; i >= 0; i--) { @@ -4302,7 +4747,7 @@ int check_todo_list(void) if (!missing.len) goto leave_check; - if (check_level == CHECK_ERROR) + if (check_level == MISSING_COMMIT_CHECK_ERROR) advise_to_edit_todo = res = 1; fprintf(stderr, @@ -4348,17 +4793,17 @@ static int rewrite_file(const char *path, const char *buf, size_t len) } /* skip picking commits whose parents are unchanged */ -int skip_unnecessary_picks(void) +static int skip_unnecessary_picks(struct repository *r, struct object_id *output_oid) { const char *todo_file = rebase_path_todo(); struct strbuf buf = STRBUF_INIT; struct todo_list todo_list = TODO_LIST_INIT; - struct object_id onto_oid, *oid = &onto_oid, *parent_oid; + 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, &onto_oid)) { + if (get_oid(buf.buf, output_oid)) { strbuf_release(&buf); return error(_("need a HEAD to fixup")); } @@ -4366,7 +4811,7 @@ int skip_unnecessary_picks(void) if (strbuf_read_file_or_whine(&todo_list.buf, todo_file) < 0) return -1; - if (parse_insn_buffer(todo_list.buf.buf, &todo_list) < 0) { + if (parse_insn_buffer(r, todo_list.buf.buf, &todo_list) < 0) { todo_list_release(&todo_list); return -1; } @@ -4388,9 +4833,9 @@ int skip_unnecessary_picks(void) if (item->commit->parents->next) break; /* merge commit */ parent_oid = &item->commit->parents->item->object.oid; - if (hashcmp(parent_oid->hash, oid->hash)) + if (!oideq(parent_oid, output_oid)) break; - oid = &item->commit->object.oid; + oidcpy(output_oid, &item->commit->object.oid); } if (i > 0) { int offset = get_item_line_offset(&todo_list, i); @@ -4419,15 +4864,114 @@ int skip_unnecessary_picks(void) todo_list.current = i; if (is_fixup(peek_command(&todo_list, 0))) - record_in_rewritten(oid, peek_command(&todo_list, 0)); + record_in_rewritten(output_oid, peek_command(&todo_list, 0)); } todo_list_release(&todo_list); - printf("%s\n", oid_to_hex(oid)); 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, const char *cmd, + unsigned autosquash) +{ + 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; + + 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 (autosquash && rearrange_squash(r)) + return -1; + + if (cmd && *cmd) + sequencer_add_exec_commands(r, cmd); + + if (strbuf_read_file(buf, todo_file, 0) < 0) + return error_errno(_("could not read '%s'."), todo_file); + + if (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) { + 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); + 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_todos(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)) { + apply_autostash(opts); + sequencer_remove_state(opts); + todo_list_release(&todo_list); + + return -1; + } + + strbuf_stripspace(buf, 1); + if (buf->len == 0) { + apply_autostash(opts); + sequencer_remove_state(opts); + todo_list_release(&todo_list); + + return error(_("nothing to do")); + } + + todo_list_release(&todo_list); + + if (check_todo_list(r)) { + checkout_onto(opts, onto_name, onto, orig_head); + return -1; + } + + if (transform_todos(r, flags & ~(TODO_LIST_SHORTEN_IDS))) + return error(_("could not transform the todo list")); + + if (opts->allow_ff && skip_unnecessary_picks(r, &oid)) + return error(_("could not skip unnecessary pick commands")); + + if (checkout_onto(opts, onto_name, oid_to_hex(&oid), orig_head)) + return -1; + + if (require_clean_work_tree(r, "rebase", "", 1, 1)) + return -1; + + return sequencer_continue(r, opts); +} + struct subject2item_entry { struct hashmap_entry entry; int i; @@ -4452,7 +4996,7 @@ define_commit_slab(commit_todo_item, struct todo_item *); * 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. */ -int rearrange_squash(void) +int rearrange_squash(struct repository *r) { const char *todo_file = rebase_path_todo(); struct todo_list todo_list = TODO_LIST_INIT; @@ -4463,7 +5007,7 @@ int rearrange_squash(void) if (strbuf_read_file_or_whine(&todo_list.buf, todo_file) < 0) return -1; - if (parse_insn_buffer(todo_list.buf.buf, &todo_list) < 0) { + if (parse_insn_buffer(r, todo_list.buf.buf, &todo_list) < 0) { todo_list_release(&todo_list); return -1; } |