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");
* (typically rebase --interactive) wants to take care
* of the commit itself so remove CHERRY_PICK_HEAD
*/
- unlink(git_path_cherry_pick_head(the_repository));
+ unlink(git_path_cherry_pick_head(r));
return;
}
return 1;
}
-static struct tree *empty_tree(void)
+static struct tree *empty_tree(struct repository *r)
{
- return lookup_tree(the_repository, the_repository->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 index_state *istate, struct replay_opts *opts)
{
- if (read_cache_unmerged())
+ if (read_index_unmerged(istate))
return error_resolve_conflict(_(action_name(opts)));
error(_("your local changes would be overwritten by %s."),
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_index(&the_index);
- if (checkout_fast_forward(the_repository, from, to, 1))
+ read_index(r->index);
+ 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)));
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)
if (hold_locked_index(&index_lock, LOCK_REPORT_ON_ERROR) < 0)
return -1;
- read_cache();
+ read_index(r->index);
init_merge_options(&o);
o.ancestor = base ? base_label : "(empty tree)";
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);
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
_(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(the_repository, &head_oid);
+ head_commit = lookup_commit(r, &head_oid);
/*
* If head_commit is NULL, check_commit, called from
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 oideq(cache_tree_oid, get_commit_tree_oid(head_commit));
* 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;
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)
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;
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,
}
-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;
struct strbuf author_ident = STRBUF_INIT;
struct strbuf committer_ident = STRBUF_INIT;
- commit = lookup_commit(the_repository, oid);
+ commit = lookup_commit(r, oid);
if (!commit)
die(_("couldn't look up newly created commit"));
if (parse_commit(commit))
strbuf_release(&author_ident);
strbuf_release(&committer_ident);
- repo_init_revisions(the_repository, &rev, prefix);
+ repo_init_revisions(r, &rev, prefix);
setup_revisions(0, NULL, &rev, NULL);
rev.diff = 1;
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;
if (get_oid("HEAD", &oid)) {
current_head = NULL;
} else {
- current_head = lookup_commit_reference(the_repository, &oid);
+ current_head = lookup_commit_reference(r, &oid);
if (!current_head)
return error(_("could not parse HEAD"));
if (!oideq(&oid, ¤t_head->object.oid)) {
* 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)
{
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) {
commit_list_insert(current_head, &parents);
}
- if (write_index_as_tree(&tree, &the_index, get_index_file(), 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 (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(),
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;
"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(the_repository));
- unlink(git_path_merge_msg(the_repository));
+ 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;
}
/*
* 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;
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)
return 1;
}
-/*
- * Note that ordering matters in this enum. Not only must it match the mapping
- * below, it is also divided into several sections that matter. When adding
- * new commands, make sure you add it in the right section.
- */
-enum todo_command {
- /* commands that handle commits */
- TODO_PICK = 0,
- TODO_REVERT,
- TODO_EDIT,
- TODO_REWORD,
- TODO_FIXUP,
- TODO_SQUASH,
- /* commands that do something else than handling a single commit */
- TODO_EXEC,
- TODO_BREAK,
- TODO_LABEL,
- TODO_RESET,
- TODO_MERGE,
- /* commands that do nothing but are counted for reporting progress */
- TODO_NOOP,
- TODO_DROP,
- /* comments (not counted for reporting progress) */
- TODO_COMMENT
-};
-
static struct {
char c;
const char *str;
}
}
-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;
if (get_oid("HEAD", &head))
return error(_("need a HEAD to fixup"));
- if (!(head_commit = lookup_commit_reference(the_repository, &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"));
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(the_repository);
+ 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;
* that represents the "current" state for merge-recursive
* to work on.
*/
- if (write_index_as_tree(&head, &the_index, get_index_file(), 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);
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->index, opts);
}
- discard_cache();
+ discard_index(r->index);
if (!commit->parents)
parent = NULL;
(!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;
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)
flags |= CLEANUP_MSG;
msg_file = rebase_path_fixup_msg();
} else {
- const char *dest = git_path_squash_msg(the_repository);
+ 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(the_repository));
+ unlink(git_path_merge_msg(r));
msg_file = dest;
flags |= EDIT_MSG;
}
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(the_repository), 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(the_repository), 0);
+ git_path_merge_msg(r), 0);
commit_list_insert(base, &common);
commit_list_insert(next, &remotes);
- res |= try_merge_command(the_repository, 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);
? _("could not revert %s... %s")
: _("could not apply %s... %s"),
short_commit_name(commit), msg.subject);
- print_advice(res == 1, opts);
- repo_rerere(the_repository, 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;
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"));
}
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(&the_index) < 0) {
+ if (read_index(r->index) < 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)));
TODO_EDIT_MERGE_MSG = 1
};
-struct todo_item {
- enum todo_command command;
- struct commit *commit;
- unsigned int flags;
- const char *arg;
- int arg_len;
- size_t offset_in_buf;
-};
-
-struct todo_list {
- struct strbuf buf;
- struct todo_item *items;
- int nr, alloc, current;
- int done_nr, total_nr;
- struct stat_data stat;
-};
-
-#define TODO_LIST_INIT { STRBUF_INIT }
-
-static void todo_list_release(struct todo_list *todo_list)
+void todo_list_release(struct todo_list *todo_list)
{
strbuf_release(&todo_list->buf);
FREE_AND_NULL(todo_list->items);
return todo_list->items + todo_list->nr++;
}
-static int parse_insn_line(struct todo_item *item, const char *bol, char *eol)
+const char *todo_item_get_arg(struct todo_list *todo_list,
+ struct todo_item *item)
+{
+ return todo_list->buf.buf + item->arg_offset;
+}
+
+static int parse_insn_line(struct repository *r, struct todo_item *item,
+ const char *buf, const char *bol, char *eol)
{
struct object_id commit_oid;
char *end_of_object_name;
if (bol == eol || *bol == '\r' || *bol == comment_line_char) {
item->command = TODO_COMMENT;
item->commit = NULL;
- item->arg = bol;
+ item->arg_offset = bol - buf;
item->arg_len = eol - bol;
return 0;
}
return error(_("%s does not accept arguments: '%s'"),
command_to_string(item->command), bol);
item->commit = NULL;
- item->arg = bol;
+ item->arg_offset = bol - buf;
item->arg_len = eol - bol;
return 0;
}
if (item->command == TODO_EXEC || item->command == TODO_LABEL ||
item->command == TODO_RESET) {
item->commit = NULL;
- item->arg = bol;
+ item->arg_offset = bol - buf;
item->arg_len = (int)(eol - bol);
return 0;
}
} else {
item->flags |= TODO_EDIT_MERGE_MSG;
item->commit = NULL;
- item->arg = bol;
+ item->arg_offset = bol - buf;
item->arg_len = (int)(eol - bol);
return 0;
}
status = get_oid(bol, &commit_oid);
*end_of_object_name = saved;
- item->arg = end_of_object_name + strspn(end_of_object_name, " \t");
- item->arg_len = (int)(eol - item->arg);
+ bol = end_of_object_name + strspn(end_of_object_name, " \t");
+ item->arg_offset = bol - buf;
+ item->arg_len = (int)(eol - bol);
if (status < 0)
return -1;
- item->commit = lookup_commit_reference(the_repository, &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)
+int todo_list_parse_insn_buffer(struct repository *r, char *buf,
+ struct todo_list *todo_list)
{
struct todo_item *item;
char *p = buf, *next_p;
int i, res = 0, fixup_okay = file_exists(rebase_path_done());
+ todo_list->current = todo_list->nr = 0;
+
for (i = 1; *p; i++, p = next_p) {
char *eol = strchrnul(p, '\n');
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, buf, p, eol)) {
res = error(_("invalid line %d: %.*s"),
i, (int)(eol - p), p);
- item->command = TODO_NOOP;
+ item->command = TODO_COMMENT + 1;
+ item->arg_offset = p - buf;
+ item->arg_len = (int)(eol - p);
+ item->commit = NULL;
}
if (fixup_okay)
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);
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 = todo_list_parse_insn_buffer(r, todo_list->buf.buf, todo_list);
if (res) {
if (is_rebase_i(opts))
return error(_("please fix this using "
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))
+ !todo_list_parse_insn_buffer(r, done.buf.buf, &done))
todo_list->done_nr = count_commands(&done);
else
todo_list->done_nr = 0;
item->command = command;
item->commit = commit;
- item->arg = NULL;
+ item->arg_offset = 0;
item->arg_len = 0;
item->offset_in_buf = todo_list->buf.len;
subject_len = find_commit_subject(commit_buffer, &subject);
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(the_repository)) &&
- !file_exists(git_path_revert_head(the_repository)))
+ 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"));
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;
* 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());
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;
strbuf_addf(&buf, "%s/patch", get_dir(opts));
memset(&log_tree_opt, 0, sizeof(log_tree_opt));
- repo_init_revisions(the_repository, &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;
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 (commit) {
- if (make_patch(commit, opts))
+ if (make_patch(r, commit, opts))
return -1;
} else if (copy_file(rebase_path_message(),
- git_path_merge_msg(the_repository), 0666))
+ git_path_merge_msg(r), 0666))
return error(_("unable to copy '%s' to '%s'"),
- git_path_merge_msg(the_repository), rebase_path_message());
+ git_path_merge_msg(r), rebase_path_message());
if (to_amend) {
if (intend_to_amend())
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(the_repository));
- if (copy_file(git_path_merge_msg(the_repository), 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(the_repository));
- return error_with_patch(commit, subject, subject_len, opts, 1, 0);
+ 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 };
child_env.argv);
/* force re-reading of the cache */
- if (discard_cache() < 0 || read_cache() < 0)
+ if (discard_index(r->index) < 0 || read_index(r->index) < 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"
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;
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;
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 (read_index_unmerged(r->index)) {
rollback_lock_file(&lock);
strbuf_release(&ref_name);
return error_resolve_conflict(_(action_name(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);
return commit;
}
-static int do_merge(struct commit *commit, const char *arg, int arg_len,
+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) ?
ret = error(_("octopus merge cannot be executed on "
"top of a [new root]"));
else
- ret = fast_forward_to(&to_merge->item->object.oid,
+ ret = fast_forward_to(r, &to_merge->item->object.oid,
&head_commit->object.oid, 0,
opts);
goto leave_merge;
write_author_script(message);
find_commit_subject(message, &body);
len = strlen(body);
- ret = write_message(body, len, git_path_merge_msg(the_repository), 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(the_repository));
+ git_path_merge_msg(r));
goto leave_merge;
}
} else {
len = buf.len;
}
- ret = write_message(p, len, git_path_merge_msg(the_repository), 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(the_repository));
+ git_path_merge_msg(r));
goto leave_merge;
}
}
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;
}
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(the_repository));
+ argv_array_push(&cmd.args, git_path_merge_msg(r));
if (opts->gpg_sign)
argv_array_push(&cmd.args, opts->gpg_sign);
oid_to_hex(&j->item->object.oid));
strbuf_release(&ref_name);
- unlink(git_path_cherry_pick_head(the_repository));
+ 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_cache() < 0 || read_cache() < 0))
+ if (!ret && (discard_index(r->index) < 0 ||
+ read_index(r->index) < 0))
ret = error(_("could not read index"));
goto leave_merge;
}
}
write_message(oid_to_hex(&merge_commit->object.oid), GIT_SHA1_HEXSZ,
- git_path_merge_head(the_repository), 0);
- write_message("no-ff", 5, git_path_merge_mode(the_repository), 0);
+ 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();
+ read_index(r->index);
init_merge_options(&o);
o.branch1 = "HEAD";
o.branch2 = ref_name.buf;
*/
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)
- repo_rerere(the_repository, 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(the_repository), opts,
- run_commit_flags);
+ ret = !!run_git_commit(r, git_path_merge_msg(r), opts,
+ run_commit_flags);
leave_merge:
strbuf_release(&ref_name);
return update_ref(NULL, "ORIG_HEAD", &oid, NULL, 0, UPDATE_REFS_MSG_ON_ERR);
}
-static int stopped_at_head(void)
+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(the_repository, &head)) ||
+ !(commit = lookup_commit(r, &head)) ||
parse_commit(commit) || get_message(commit, &message))
fprintf(stderr, _("Stopped at HEAD\n"));
else {
" 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;
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) {
struct todo_item *item = todo_list->items + todo_list->current;
+ const char *arg = todo_item_get_arg(todo_list, item);
+
if (save_todo(todo_list, opts))
return -1;
if (is_rebase_i(opts)) {
delete_ref(NULL, "REBASE_HEAD", NULL, REF_NO_DEREF);
if (item->command == TODO_BREAK)
- return stopped_at_head();
+ 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 */
fprintf(stderr,
_("Stopped at %s... %.*s\n"),
short_commit_name(commit),
- item->arg_len, item->arg);
- return error_with_patch(commit,
- item->arg, item->arg_len, opts, res,
- !res);
+ item->arg_len, arg);
+ return error_with_patch(r, commit,
+ arg, item->arg_len, opts, res, !res);
}
if (is_rebase_i(opts) && !res)
record_in_rewritten(&item->commit->object.oid,
if (res && is_fixup(item->command)) {
if (res == 1)
intend_to_amend();
- return error_failed_squash(item->commit, opts,
- item->arg_len, item->arg);
+ return error_failed_squash(r, item->commit, opts,
+ item->arg_len, arg);
} else if (res && is_rebase_i(opts) && item->commit) {
int to_amend = 0;
struct object_id oid;
oideq(&opts->squash_onto, &oid))))
to_amend = 1;
- return res | error_with_patch(item->commit,
- item->arg, item->arg_len, opts,
+ return res | error_with_patch(r, item->commit,
+ arg, item->arg_len, opts,
res, to_amend);
}
} else if (item->command == TODO_EXEC) {
- char *end_of_arg = (char *)(item->arg + item->arg_len);
+ char *end_of_arg = (char *)(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, arg);
*end_of_arg = saved;
/* Reread the todo file if it has changed. */
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, 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, arg, item->arg_len, opts)))
reschedule = 1;
} else if (item->command == TODO_MERGE) {
- if ((res = do_merge(item->commit,
- item->arg, item->arg_len,
+ if ((res = do_merge(r, item->commit,
+ arg, item->arg_len,
item->flags, opts)) < 0)
reschedule = 1;
else if (item->commit)
peek_command(todo_list, 1));
if (res > 0)
/* failed with merge conflicts */
- return error_with_patch(item->commit,
- item->arg,
- item->arg_len, opts,
- res, 0);
+ return error_with_patch(r, item->commit,
+ arg, item->arg_len,
+ opts, res, 0);
} else if (!is_noop(item->command))
return error(_("unknown command %d"), item->command);
if (save_todo(todo_list, opts))
return -1;
if (item->commit)
- return error_with_patch(item->commit,
- item->arg,
- item->arg_len, opts,
- res, 0);
+ return error_with_patch(r,
+ item->commit,
+ arg, item->arg_len,
+ opts, res, 0);
}
todo_list->current++;
struct object_id orig, head;
memset(&log_tree_opt, 0, sizeof(log_tree_opt));
- repo_init_revisions(the_repository, &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;
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(the_repository)) &&
- !file_exists(git_path_revert_head(the_repository)))
+ 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;
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);
}
if (is_clean) {
- const char *cherry_pick_head = git_path_cherry_pick_head(the_repository);
+ 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"));
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());
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(the_repository)) ||
- file_exists(git_path_revert_head(the_repository))) {
- 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->index, opts);
goto release_todo_list;
}
todo_list.current++;
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++) {
continue;
if (!get_oid(name, &oid)) {
- if (!lookup_commit_reference_gently(the_repository, &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"),
return error(_("empty commit set passed"));
if (get_revision(opts->revs))
BUG("unexpected extra commit from walk");
- return single_pick(cmit, opts);
+ return single_pick(r, cmit, 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;
}
}
static int make_script_with_merges(struct pretty_print_context *pp,
- struct rev_info *revs, FILE *out,
+ struct rev_info *revs, struct strbuf *out,
unsigned flags)
{
int keep_empty = flags & TODO_LIST_KEEP_EMPTY;
* gathering commits not yet shown, reversing the list on the fly,
* then outputting that list (labeling revisions as needed).
*/
- fprintf(out, "%s onto\n", cmd_label);
+ strbuf_addf(out, "%s onto\n", cmd_label);
for (iter = tips; iter; iter = iter->next) {
struct commit_list *list = NULL, *iter2;
entry = oidmap_get(&state.commit2label, &commit->object.oid);
if (entry)
- fprintf(out, "\n%c Branch %s\n", comment_line_char, entry->string);
+ strbuf_addf(out, "\n%c Branch %s\n", comment_line_char, entry->string);
else
- fprintf(out, "\n");
+ strbuf_addch(out, '\n');
while (oidset_contains(&interesting, &commit->object.oid) &&
!oidset_contains(&shown, &commit->object.oid)) {
}
if (!commit)
- fprintf(out, "%s %s\n", cmd_reset,
- rebase_cousins ? "onto" : "[new root]");
+ strbuf_addf(out, "%s %s\n", cmd_reset,
+ rebase_cousins ? "onto" : "[new root]");
else {
const char *to = NULL;
&state);
if (!to || !strcmp(to, "onto"))
- fprintf(out, "%s onto\n", cmd_reset);
+ strbuf_addf(out, "%s onto\n", cmd_reset);
else {
strbuf_reset(&oneline);
pretty_print_commit(pp, commit, &oneline);
- fprintf(out, "%s %s # %s\n",
- cmd_reset, to, oneline.buf);
+ strbuf_addf(out, "%s %s # %s\n",
+ cmd_reset, to, oneline.buf);
}
}
entry = oidmap_get(&commit2todo, oid);
/* only show if not already upstream */
if (entry)
- fprintf(out, "%s\n", entry->string);
+ strbuf_addf(out, "%s\n", entry->string);
entry = oidmap_get(&state.commit2label, oid);
if (entry)
- fprintf(out, "%s %s\n",
- cmd_label, entry->string);
+ strbuf_addf(out, "%s %s\n",
+ cmd_label, entry->string);
oidset_insert(&shown, oid);
}
return 0;
}
-int sequencer_make_script(FILE *out, int argc, const char **argv,
- unsigned flags)
+int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,
+ const char **argv, unsigned flags)
{
char *format = NULL;
struct pretty_print_context pp = {0};
- struct strbuf buf = STRBUF_INIT;
struct rev_info revs;
struct commit *commit;
int keep_empty = flags & TODO_LIST_KEEP_EMPTY;
const char *insn = flags & TODO_LIST_ABBREVIATE_CMDS ? "p" : "pick";
int rebase_merges = flags & TODO_LIST_REBASE_MERGES;
- repo_init_revisions(the_repository, &revs, NULL);
+ repo_init_revisions(r, &revs, NULL);
revs.verbose_header = 1;
if (!rebase_merges)
revs.max_parents = 1;
if (!is_empty && (commit->object.flags & PATCHSAME))
continue;
- strbuf_reset(&buf);
if (!keep_empty && is_empty)
- strbuf_addf(&buf, "%c ", comment_line_char);
- strbuf_addf(&buf, "%s %s ", insn,
+ strbuf_addf(out, "%c ", comment_line_char);
+ strbuf_addf(out, "%s %s ", insn,
oid_to_hex(&commit->object.oid));
- pretty_print_commit(&pp, commit, &buf);
- strbuf_addch(&buf, '\n');
- fputs(buf.buf, out);
+ pretty_print_commit(&pp, commit, out);
+ strbuf_addch(out, '\n');
}
- strbuf_release(&buf);
return 0;
}
* Add commands after pick and (series of) squash/fixup commands
* in the todo list.
*/
-int sequencer_add_exec_commands(const char *commands)
+void todo_list_add_exec_commands(struct todo_list *todo_list,
+ struct string_list *commands)
{
- const char *todo_file = rebase_path_todo();
- struct todo_list todo_list = TODO_LIST_INIT;
- struct strbuf *buf = &todo_list.buf;
- size_t offset = 0, commands_len = strlen(commands);
- int i, insert;
+ struct strbuf *buf = &todo_list->buf;
+ size_t base_offset = buf->len;
+ int i, insert, nr = 0, alloc = 0;
+ struct todo_item *items = NULL, *base_items = NULL;
- if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)
- return error(_("could not read '%s'."), todo_file);
+ base_items = xcalloc(commands->nr, sizeof(struct todo_item));
+ for (i = 0; i < commands->nr; i++) {
+ size_t command_len = strlen(commands->items[i].string);
- if (parse_insn_buffer(todo_list.buf.buf, &todo_list)) {
- todo_list_release(&todo_list);
- return error(_("unusable todo list: '%s'"), todo_file);
+ strbuf_addstr(buf, commands->items[i].string);
+ strbuf_addch(buf, '\n');
+
+ base_items[i].command = TODO_EXEC;
+ base_items[i].offset_in_buf = base_offset;
+ base_items[i].arg_offset = base_offset + strlen("exec ");
+ base_items[i].arg_len = command_len - strlen("exec ");
+
+ base_offset += command_len + 1;
}
/*
* 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.
+ *
+ * As we insert the exec commands immediatly after rearranging
+ * any fixups and before the user edits the list, a fixup chain
+ * can never contain comments (any comments are empty picks that
+ * have been commented out because the user did not specify
+ * --keep-empty). So, it is safe to insert an exec command
+ * without looking at the command following a comment.
*/
- 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;
+ insert = 0;
+ for (i = 0; i < todo_list->nr; i++) {
+ enum todo_command command = todo_list->items[i].command;
+ if (insert && !is_fixup(command)) {
+ ALLOC_GROW(items, nr + commands->nr, alloc);
+ COPY_ARRAY(items + nr, base_items, commands->nr);
+ nr += commands->nr;
+
+ insert = 0;
}
+ ALLOC_GROW(items, nr + 1, alloc);
+ items[nr++] = todo_list->items[i];
+
if (command == TODO_PICK || command == TODO_MERGE)
- insert = i + 1;
+ insert = 1;
}
/* 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);
+ if (insert || nr == todo_list->nr) {
+ ALLOC_GROW(items, nr + commands->nr, alloc);
+ COPY_ARRAY(items + nr, base_items, commands->nr);
+ nr += commands->nr;
+ }
- i = write_message(buf->buf, buf->len, todo_file, 0);
- todo_list_release(&todo_list);
- return i;
+ free(base_items);
+ FREE_AND_NULL(todo_list->items);
+ todo_list->items = items;
+ todo_list->nr = nr;
+ todo_list->alloc = alloc;
}
-int transform_todos(unsigned flags)
+static void todo_list_to_strbuf(struct repository *r, struct todo_list *todo_list,
+ struct strbuf *buf, int num, unsigned flags)
{
- const char *todo_file = rebase_path_todo();
- struct todo_list todo_list = TODO_LIST_INIT;
- struct strbuf buf = STRBUF_INIT;
struct todo_item *item;
- int i;
-
- if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)
- return error(_("could not read '%s'."), todo_file);
+ int i, max = todo_list->nr;
- if (parse_insn_buffer(todo_list.buf.buf, &todo_list)) {
- todo_list_release(&todo_list);
- return error(_("unusable todo list: '%s'"), todo_file);
- }
+ if (num > 0 && num < max)
+ max = num;
- for (item = todo_list.items, i = 0; i < todo_list.nr; i++, item++) {
+ for (item = todo_list->items, i = 0; i < max; i++, item++) {
/* if the item is not a command write it and continue */
if (item->command >= TODO_COMMENT) {
- strbuf_addf(&buf, "%.*s\n", item->arg_len, item->arg);
+ strbuf_addf(buf, "%.*s\n", item->arg_len,
+ todo_item_get_arg(todo_list, item));
continue;
}
/* add command to the buffer */
if (flags & TODO_LIST_ABBREVIATE_CMDS)
- strbuf_addch(&buf, command_to_char(item->command));
+ strbuf_addch(buf, command_to_char(item->command));
else
- strbuf_addstr(&buf, command_to_string(item->command));
+ strbuf_addstr(buf, command_to_string(item->command));
/* add commit id */
if (item->commit) {
if (item->command == TODO_MERGE) {
if (item->flags & TODO_EDIT_MERGE_MSG)
- strbuf_addstr(&buf, " -c");
+ strbuf_addstr(buf, " -c");
else
- strbuf_addstr(&buf, " -C");
+ strbuf_addstr(buf, " -C");
}
- strbuf_addf(&buf, " %s", oid);
+ strbuf_addf(buf, " %s", oid);
}
/* add all the rest */
if (!item->arg_len)
- strbuf_addch(&buf, '\n');
+ strbuf_addch(buf, '\n');
else
- strbuf_addf(&buf, " %.*s\n", item->arg_len, item->arg);
+ strbuf_addf(buf, " %.*s\n", item->arg_len,
+ todo_item_get_arg(todo_list, item));
}
-
- i = write_message(buf.buf, buf.len, todo_file, 0);
- todo_list_release(&todo_list);
- return i;
}
-enum missing_commit_check_level get_missing_commit_check_level(void)
+int todo_list_write_to_file(struct repository *r, struct todo_list *todo_list,
+ const char *file, const char *shortrevisions,
+ const char *shortonto, int num, unsigned flags)
{
- const char *value;
+ int edit_todo = !(shortrevisions && shortonto), res;
+ struct strbuf buf = STRBUF_INIT;
- if (git_config_get_value("rebase.missingcommitscheck", &value) ||
- !strcasecmp("ignore", value))
- return MISSING_COMMIT_CHECK_IGNORE;
- if (!strcasecmp("warn", value))
- return MISSING_COMMIT_CHECK_WARN;
- if (!strcasecmp("error", value))
- return MISSING_COMMIT_CHECK_ERROR;
- warning(_("unrecognized setting %s for option "
- "rebase.missingCommitsCheck. Ignoring."), value);
- return MISSING_COMMIT_CHECK_IGNORE;
+ 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);
+ }
+
+ res = write_message(buf.buf, buf.len, file, 0);
+ strbuf_release(&buf);
+
+ return res;
}
-define_commit_slab(commit_seen, unsigned char);
-/*
- * Check if the user dropped some commits by mistake
- * Behaviour determined by rebase.missingCommitsCheck.
- * Check if there is an unrecognized command or a
- * bad SHA-1 in a command.
- */
-int check_todo_list(void)
+int transform_todo_file(struct repository *r, unsigned flags)
{
- enum missing_commit_check_level check_level = get_missing_commit_check_level();
- struct strbuf todo_file = STRBUF_INIT;
+ const char *todo_file = rebase_path_todo();
struct todo_list todo_list = TODO_LIST_INIT;
- struct strbuf missing = STRBUF_INIT;
- int advise_to_edit_todo = 0, res = 0, i;
- struct commit_seen commit_seen;
-
- init_commit_seen(&commit_seen);
-
- strbuf_addstr(&todo_file, rebase_path_todo());
- if (strbuf_read_file_or_whine(&todo_list.buf, todo_file.buf) < 0) {
- res = -1;
- goto leave_check;
- }
- advise_to_edit_todo = res =
- parse_insn_buffer(todo_list.buf.buf, &todo_list);
+ int res;
- if (res || check_level == MISSING_COMMIT_CHECK_IGNORE)
- goto leave_check;
+ if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)
+ return error_errno(_("could not read '%s'."), todo_file);
- /* Mark the commits in git-rebase-todo as seen */
- for (i = 0; i < todo_list.nr; i++) {
- struct commit *commit = todo_list.items[i].commit;
- if (commit)
- *commit_seen_at(&commit_seen, commit) = 1;
+ 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);
- strbuf_addstr(&todo_file, ".backup");
- if (strbuf_read_file_or_whine(&todo_list.buf, todo_file.buf) < 0) {
- res = -1;
- goto leave_check;
- }
- strbuf_release(&todo_file);
- res = !!parse_insn_buffer(todo_list.buf.buf, &todo_list);
-
- /* Find commits in git-rebase-todo.backup yet unseen */
- for (i = todo_list.nr - 1; i >= 0; i--) {
- struct todo_item *item = todo_list.items + i;
- struct commit *commit = item->commit;
- if (commit && !*commit_seen_at(&commit_seen, commit)) {
- strbuf_addf(&missing, " - %s %.*s\n",
- short_commit_name(commit),
- item->arg_len, item->arg);
- *commit_seen_at(&commit_seen, commit) = 1;
- }
- }
- /* Warn about missing commits */
- if (!missing.len)
- goto leave_check;
-
- if (check_level == MISSING_COMMIT_CHECK_ERROR)
- advise_to_edit_todo = res = 1;
+ if (res)
+ return error_errno(_("could not write '%s'."), todo_file);
+ return 0;
+}
- fprintf(stderr,
- _("Warning: some commits may have been dropped accidentally.\n"
- "Dropped commits (newer to older):\n"));
+static const char edit_todo_list_advice[] =
+N_("You can fix this with 'git rebase --edit-todo' "
+"and then run 'git rebase --continue'.\n"
+"Or you can abort the rebase with 'git rebase"
+" --abort'.\n");
- /* Make the list user-friendly and display */
- fputs(missing.buf, stderr);
- strbuf_release(&missing);
+int check_todo_list_from_file(struct repository *r)
+{
+ struct todo_list old_todo = TODO_LIST_INIT, new_todo = TODO_LIST_INIT;
+ int res = 0;
- fprintf(stderr, _("To avoid this message, use \"drop\" to "
- "explicitly remove a commit.\n\n"
- "Use 'git config rebase.missingCommitsCheck' to change "
- "the level of warnings.\n"
- "The possible behaviours are: ignore, warn, error.\n\n"));
+ if (strbuf_read_file_or_whine(&new_todo.buf, rebase_path_todo()) < 0) {
+ res = -1;
+ goto out;
+ }
-leave_check:
- clear_commit_seen(&commit_seen);
- strbuf_release(&todo_file);
- todo_list_release(&todo_list);
+ if (strbuf_read_file_or_whine(&old_todo.buf, rebase_path_todo_backup()) < 0) {
+ res = -1;
+ goto out;
+ }
- if (advise_to_edit_todo)
- fprintf(stderr,
- _("You can fix this with 'git rebase --edit-todo' "
- "and then run 'git rebase --continue'.\n"
- "Or you can abort the rebase with 'git rebase"
- " --abort'.\n"));
+ res = todo_list_parse_insn_buffer(r, old_todo.buf.buf, &old_todo);
+ if (!res)
+ res = todo_list_parse_insn_buffer(r, new_todo.buf.buf, &new_todo);
+ if (!res)
+ res = todo_list_check(&old_todo, &new_todo);
+ if (res)
+ fprintf(stderr, _(edit_todo_list_advice));
+out:
+ todo_list_release(&old_todo);
+ todo_list_release(&new_todo);
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 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 (parse_insn_buffer(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 replay_opts *opts, unsigned flags,
+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 *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 todo_list new_todo = 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 (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())
+ if (autosquash && todo_list_rearrange_squash(todo_list))
return -1;
- if (cmd && *cmd)
- sequencer_add_exec_commands(cmd);
-
- if (strbuf_read_file(buf, todo_file, 0) < 0)
- return error_errno(_("could not read '%s'."), todo_file);
+ if (commands->nr)
+ todo_list_add_exec_commands(todo_list, commands);
- if (parse_insn_buffer(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);
- return -1;
- }
+ if (todo_list_write_to_file(r, todo_list, todo_file,
+ shortrevisions, shortonto, -1,
+ flags | TODO_LIST_SHORTEN_IDS | TODO_LIST_APPEND_TODO_HELP))
+ return error_errno(_("could not write '%s'"), todo_file);
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(flags | TODO_LIST_SHORTEN_IDS))
- return error(_("could not transform the todo list"));
-
- strbuf_reset(buf);
-
- if (launch_sequence_editor(todo_file, buf, NULL)) {
+ if (launch_sequence_editor(todo_file, &new_todo.buf, NULL)) {
apply_autostash(opts);
sequencer_remove_state(opts);
- todo_list_release(&todo_list);
return -1;
}
- strbuf_stripspace(buf, 1);
- if (buf->len == 0) {
+ strbuf_stripspace(&new_todo.buf, 1);
+ if (new_todo.buf.len == 0) {
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 (check_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(opts, onto_name, onto, orig_head);
+ todo_list_release(&new_todo);
+
return -1;
}
- if (transform_todos(flags & ~(TODO_LIST_SHORTEN_IDS)))
- return error(_("could not transform the todo list"));
-
- if (opts->allow_ff && skip_unnecessary_picks(&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);
+ }
+
+ todo_list_release(&new_todo);
if (checkout_onto(opts, onto_name, oid_to_hex(&oid), orig_head))
return -1;
- if (require_clean_work_tree("rebase", "", 1, 1))
+ if (require_clean_work_tree(r, "rebase", "", 1, 1))
return -1;
- return sequencer_continue(opts);
+ return sequencer_continue(r, opts);
}
struct subject2item_entry {
* 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 todo_list_rearrange_squash(struct todo_list *todo_list)
{
- const char *todo_file = rebase_path_todo();
- struct todo_list todo_list = TODO_LIST_INIT;
struct hashmap subject2item;
- int res = 0, rearranged = 0, *next, *tail, i;
+ int rearranged = 0, *next, *tail, i, nr = 0, alloc = 0;
char **subjects;
struct commit_todo_item commit_todo;
-
- 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) {
- todo_list_release(&todo_list);
- return -1;
- }
+ struct todo_item *items = NULL;
init_commit_todo_item(&commit_todo);
/*
* be moved to appear after the i'th.
*/
hashmap_init(&subject2item, (hashmap_cmp_fn) subject2item_cmp,
- NULL, todo_list.nr);
- ALLOC_ARRAY(next, todo_list.nr);
- ALLOC_ARRAY(tail, todo_list.nr);
- ALLOC_ARRAY(subjects, todo_list.nr);
- for (i = 0; i < todo_list.nr; i++) {
+ NULL, todo_list->nr);
+ ALLOC_ARRAY(next, todo_list->nr);
+ ALLOC_ARRAY(tail, todo_list->nr);
+ ALLOC_ARRAY(subjects, todo_list->nr);
+ for (i = 0; i < todo_list->nr; i++) {
struct strbuf buf = STRBUF_INIT;
- struct todo_item *item = todo_list.items + i;
+ struct todo_item *item = todo_list->items + i;
const char *commit_buffer, *subject, *p;
size_t subject_len;
int i2 = -1;
}
if (is_fixup(item->command)) {
- todo_list_release(&todo_list);
clear_commit_todo_item(&commit_todo);
return error(_("the script was already rearranged."));
}
*commit_todo_item_at(&commit_todo, commit2))
/* found by commit name */
i2 = *commit_todo_item_at(&commit_todo, commit2)
- - todo_list.items;
+ - todo_list->items;
else {
/* copy can be a prefix of the commit subject */
for (i2 = 0; i2 < i; i2++)
}
if (i2 >= 0) {
rearranged = 1;
- todo_list.items[i].command =
+ todo_list->items[i].command =
starts_with(subject, "fixup!") ?
TODO_FIXUP : TODO_SQUASH;
if (next[i2] < 0)
}
if (rearranged) {
- struct strbuf buf = STRBUF_INIT;
-
- for (i = 0; i < todo_list.nr; i++) {
- enum todo_command command = todo_list.items[i].command;
+ for (i = 0; i < todo_list->nr; i++) {
+ enum todo_command command = todo_list->items[i].command;
int cur = i;
/*
continue;
while (cur >= 0) {
- const char *bol =
- get_item_line(&todo_list, cur);
- const char *eol =
- get_item_line(&todo_list, cur + 1);
-
- /* replace 'pick', by 'fixup' or 'squash' */
- command = todo_list.items[cur].command;
- if (is_fixup(command)) {
- strbuf_addstr(&buf,
- todo_command_info[command].str);
- bol += strcspn(bol, " \t");
- }
-
- strbuf_add(&buf, bol, eol - bol);
-
+ ALLOC_GROW(items, nr + 1, alloc);
+ items[nr++] = todo_list->items[cur];
cur = next[cur];
}
}
- res = rewrite_file(todo_file, buf.buf, buf.len);
- strbuf_release(&buf);
+ FREE_AND_NULL(todo_list->items);
+ todo_list->items = items;
+ todo_list->nr = nr;
+ todo_list->alloc = alloc;
}
free(next);
free(tail);
- for (i = 0; i < todo_list.nr; i++)
+ for (i = 0; i < todo_list->nr; i++)
free(subjects[i]);
free(subjects);
hashmap_free(&subject2item, 1);
- todo_list_release(&todo_list);
clear_commit_todo_item(&commit_todo);
- return res;
+
+ return 0;
}