summaryrefslogtreecommitdiff
path: root/sequencer.c
diff options
context:
space:
mode:
Diffstat (limited to 'sequencer.c')
-rw-r--r--sequencer.c1038
1 files changed, 671 insertions, 367 deletions
diff --git a/sequencer.c b/sequencer.c
index 42f495c4b4..287f4e5e87 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "abspath.h"
#include "advice.h"
@@ -15,10 +17,8 @@
#include "pager.h"
#include "commit.h"
#include "sequencer.h"
-#include "tag.h"
#include "run-command.h"
#include "hook.h"
-#include "exec-cmd.h"
#include "utf8.h"
#include "cache-tree.h"
#include "diff.h"
@@ -39,7 +39,6 @@
#include "notes-utils.h"
#include "sigchain.h"
#include "unpack-trees.h"
-#include "worktree.h"
#include "oidmap.h"
#include "oidset.h"
#include "commit-slab.h"
@@ -210,6 +209,46 @@ static GIT_PATH_FUNC(rebase_path_no_reschedule_failed_exec, "rebase-merge/no-res
static GIT_PATH_FUNC(rebase_path_drop_redundant_commits, "rebase-merge/drop_redundant_commits")
static GIT_PATH_FUNC(rebase_path_keep_redundant_commits, "rebase-merge/keep_redundant_commits")
+/*
+ * A 'struct replay_ctx' represents the private state of the sequencer.
+ */
+struct replay_ctx {
+ /*
+ * The commit message that will be used except at the end of a
+ * chain of fixup and squash commands.
+ */
+ struct strbuf message;
+ /*
+ * The list of completed fixup and squash commands in the
+ * current chain.
+ */
+ struct strbuf current_fixups;
+ /*
+ * Stores the reflog message that will be used when creating a
+ * commit. Points to a static buffer and should not be free()'d.
+ */
+ const char *reflog_message;
+ /*
+ * The number of completed fixup and squash commands in the
+ * current chain.
+ */
+ int current_fixup_count;
+ /*
+ * Whether message contains a commit message.
+ */
+ unsigned have_message :1;
+};
+
+struct replay_ctx* replay_ctx_new(void)
+{
+ struct replay_ctx *ctx = xcalloc(1, sizeof(*ctx));
+
+ strbuf_init(&ctx->current_fixups, 0);
+ strbuf_init(&ctx->message, 0);
+
+ return ctx;
+}
+
/**
* A 'struct update_refs_record' represents a value in the update-refs
* list. We use a string_list to map refs to these (before, after) pairs.
@@ -229,7 +268,7 @@ static struct update_ref_record *init_update_ref_record(const char *ref)
oidcpy(&rec->after, null_oid());
/* This may fail, but that's fine, we will keep the null OID. */
- read_ref(ref, &rec->before);
+ refs_read_ref(get_main_ref_store(the_repository), ref, &rec->before);
return rec;
}
@@ -238,43 +277,39 @@ static int git_sequencer_config(const char *k, const char *v,
const struct config_context *ctx, void *cb)
{
struct replay_opts *opts = cb;
- int status;
if (!strcmp(k, "commit.cleanup")) {
- const char *s;
-
- status = git_config_string(&s, k, v);
- if (status)
- return status;
+ if (!v)
+ return config_error_nonbool(k);
- if (!strcmp(s, "verbatim")) {
+ if (!strcmp(v, "verbatim")) {
opts->default_msg_cleanup = COMMIT_MSG_CLEANUP_NONE;
opts->explicit_cleanup = 1;
- } else if (!strcmp(s, "whitespace")) {
+ } else if (!strcmp(v, "whitespace")) {
opts->default_msg_cleanup = COMMIT_MSG_CLEANUP_SPACE;
opts->explicit_cleanup = 1;
- } else if (!strcmp(s, "strip")) {
+ } else if (!strcmp(v, "strip")) {
opts->default_msg_cleanup = COMMIT_MSG_CLEANUP_ALL;
opts->explicit_cleanup = 1;
- } else if (!strcmp(s, "scissors")) {
+ } else if (!strcmp(v, "scissors")) {
opts->default_msg_cleanup = COMMIT_MSG_CLEANUP_SCISSORS;
opts->explicit_cleanup = 1;
} else {
warning(_("invalid commit message cleanup mode '%s'"),
- s);
+ v);
}
- free((char *)s);
- return status;
+ return 0;
}
if (!strcmp(k, "commit.gpgsign")) {
+ free(opts->gpg_sign);
opts->gpg_sign = git_config_bool(k, v) ? xstrdup("") : NULL;
return 0;
}
if (!opts->default_strategy && !strcmp(k, "pull.twohead")) {
- int ret = git_config_string((const char**)&opts->default_strategy, k, v);
+ int ret = git_config_string(&opts->default_strategy, k, v);
if (ret == 0) {
/*
* pull.twohead is allowed to be multi-valued; we only
@@ -327,35 +362,32 @@ static const char *get_todo_path(const struct replay_opts *opts)
static int has_conforming_footer(struct strbuf *sb, struct strbuf *sob,
size_t ignore_footer)
{
- struct process_trailer_options opts = PROCESS_TRAILER_OPTIONS_INIT;
- struct trailer_info info;
- size_t i;
+ struct trailer_iterator iter;
+ size_t i = 0;
int found_sob = 0, found_sob_last = 0;
char saved_char;
- opts.no_divider = 1;
-
if (ignore_footer) {
saved_char = sb->buf[sb->len - ignore_footer];
sb->buf[sb->len - ignore_footer] = '\0';
}
- trailer_info_get(&info, sb->buf, &opts);
+ trailer_iterator_init(&iter, sb->buf);
if (ignore_footer)
sb->buf[sb->len - ignore_footer] = saved_char;
- if (info.trailer_start == info.trailer_end)
- return 0;
+ while (trailer_iterator_advance(&iter)) {
+ i++;
+ if (sob && !strncmp(iter.raw, sob->buf, sob->len))
+ found_sob = i;
+ }
+ trailer_iterator_release(&iter);
- for (i = 0; i < info.trailer_nr; i++)
- if (sob && !strncmp(info.trailers[i], sob->buf, sob->len)) {
- found_sob = 1;
- if (i == info.trailer_nr - 1)
- found_sob_last = 1;
- }
+ if (!i)
+ return 0;
- trailer_info_release(&info);
+ found_sob_last = (int)i == found_sob;
if (found_sob_last)
return 3;
@@ -374,17 +406,26 @@ static const char *gpg_sign_opt_quoted(struct replay_opts *opts)
return buf.buf;
}
+static void replay_ctx_release(struct replay_ctx *ctx)
+{
+ strbuf_release(&ctx->current_fixups);
+ strbuf_release(&ctx->message);
+}
+
void replay_opts_release(struct replay_opts *opts)
{
+ struct replay_ctx *ctx = opts->ctx;
+
free(opts->gpg_sign);
free(opts->reflog_action);
free(opts->default_strategy);
free(opts->strategy);
strvec_clear (&opts->xopts);
- strbuf_release(&opts->current_fixups);
if (opts->revs)
release_revisions(opts->revs);
free(opts->revs);
+ replay_ctx_release(ctx);
+ free(opts->ctx);
}
int sequencer_remove_state(struct replay_opts *opts)
@@ -399,7 +440,7 @@ int sequencer_remove_state(struct replay_opts *opts)
char *eol = strchr(p, '\n');
if (eol)
*eol = '\0';
- if (delete_ref("(rebase) cleanup", p, NULL, 0) < 0) {
+ if (refs_delete_ref(get_main_ref_store(the_repository), "(rebase) cleanup", p, NULL, 0) < 0) {
warning(_("could not delete '%s'"), p);
ret = -1;
}
@@ -438,10 +479,9 @@ struct commit_message {
const char *message;
};
-static const char *short_commit_name(struct commit *commit)
+static const char *short_commit_name(struct repository *r, struct commit *commit)
{
- return repo_find_unique_abbrev(the_repository, &commit->object.oid,
- DEFAULT_ABBREV);
+ return repo_find_unique_abbrev(r, &commit->object.oid, DEFAULT_ABBREV);
}
static int get_message(struct commit *commit, struct commit_message *out)
@@ -451,7 +491,7 @@ static int get_message(struct commit *commit, struct commit_message *out)
out->message = repo_logmsg_reencode(the_repository, commit, NULL,
get_commit_output_encoding());
- abbrev = short_commit_name(commit);
+ abbrev = short_commit_name(the_repository, commit);
subject_len = find_commit_subject(out->message, &subject);
@@ -470,41 +510,56 @@ static void free_message(struct commit *commit, struct commit_message *msg)
repo_unuse_commit_buffer(the_repository, commit, msg->message);
}
+const char *rebase_resolvemsg =
+N_("Resolve all conflicts manually, mark them as resolved with\n"
+"\"git add/rm <conflicted_files>\", then run \"git rebase --continue\".\n"
+"You can instead skip this commit: run \"git rebase --skip\".\n"
+"To abort and get back to the state before \"git rebase\", run "
+"\"git rebase --abort\".");
+
static void print_advice(struct repository *r, int show_hint,
struct replay_opts *opts)
{
- char *msg = getenv("GIT_CHERRY_PICK_HELP");
+ const char *msg;
+
+ if (is_rebase_i(opts))
+ msg = rebase_resolvemsg;
+ else
+ msg = getenv("GIT_CHERRY_PICK_HELP");
if (msg) {
- advise("%s\n", msg);
+ advise_if_enabled(ADVICE_MERGE_CONFLICT, "%s", msg);
/*
* A conflict has occurred but the porcelain
* (typically rebase --interactive) wants to take care
* of the commit itself so remove CHERRY_PICK_HEAD
*/
refs_delete_ref(get_main_ref_store(r), "", "CHERRY_PICK_HEAD",
- NULL, 0);
+ NULL, REF_NO_DEREF);
return;
}
if (show_hint) {
if (opts->no_commit)
- advise(_("after resolving the conflicts, mark the corrected paths\n"
- "with 'git add <paths>' or 'git rm <paths>'"));
+ advise_if_enabled(ADVICE_MERGE_CONFLICT,
+ _("after resolving the conflicts, mark the corrected paths\n"
+ "with 'git add <paths>' or 'git rm <paths>'"));
else if (opts->action == REPLAY_PICK)
- advise(_("After resolving the conflicts, mark them with\n"
- "\"git add/rm <pathspec>\", then run\n"
- "\"git cherry-pick --continue\".\n"
- "You can instead skip this commit with \"git cherry-pick --skip\".\n"
- "To abort and get back to the state before \"git cherry-pick\",\n"
- "run \"git cherry-pick --abort\"."));
+ advise_if_enabled(ADVICE_MERGE_CONFLICT,
+ _("After resolving the conflicts, mark them with\n"
+ "\"git add/rm <pathspec>\", then run\n"
+ "\"git cherry-pick --continue\".\n"
+ "You can instead skip this commit with \"git cherry-pick --skip\".\n"
+ "To abort and get back to the state before \"git cherry-pick\",\n"
+ "run \"git cherry-pick --abort\"."));
else if (opts->action == REPLAY_REVERT)
- advise(_("After resolving the conflicts, mark them with\n"
- "\"git add/rm <pathspec>\", then run\n"
- "\"git revert --continue\".\n"
- "You can instead skip this commit with \"git revert --skip\".\n"
- "To abort and get back to the state before \"git revert\",\n"
- "run \"git revert --abort\"."));
+ advise_if_enabled(ADVICE_MERGE_CONFLICT,
+ _("After resolving the conflicts, mark them with\n"
+ "\"git add/rm <pathspec>\", then run\n"
+ "\"git revert --continue\".\n"
+ "You can instead skip this commit with \"git revert --skip\".\n"
+ "To abort and get back to the state before \"git revert\",\n"
+ "run \"git revert --abort\"."));
else
BUG("unexpected pick action in print_advice()");
}
@@ -606,11 +661,12 @@ static int fast_forward_to(struct repository *r,
strbuf_addf(&sb, "%s: fast-forward", action_name(opts));
- transaction = ref_transaction_begin(&err);
+ transaction = ref_store_transaction_begin(get_main_ref_store(the_repository),
+ 0, &err);
if (!transaction ||
ref_transaction_update(transaction, "HEAD",
to, unborn && !is_rebase_i(opts) ?
- null_oid() : from,
+ null_oid() : from, NULL, NULL,
0, sb.buf, &err) ||
ref_transaction_commit(transaction, &err)) {
ref_transaction_free(transaction);
@@ -672,15 +728,15 @@ void append_conflicts_hint(struct index_state *istate,
if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS) {
strbuf_addch(msgbuf, '\n');
wt_status_append_cut_line(msgbuf);
- strbuf_addch(msgbuf, comment_line_char);
+ strbuf_addstr(msgbuf, comment_line_str);
}
strbuf_addch(msgbuf, '\n');
- strbuf_commented_addf(msgbuf, comment_line_char, "Conflicts:\n");
+ strbuf_commented_addf(msgbuf, comment_line_str, "Conflicts:\n");
for (i = 0; i < istate->cache_nr;) {
const struct cache_entry *ce = istate->cache[i++];
if (ce_stage(ce)) {
- strbuf_commented_addf(msgbuf, comment_line_char,
+ strbuf_commented_addf(msgbuf, comment_line_str,
"\t%s\n", ce->name);
while (i < istate->cache_nr &&
!strcmp(ce->name, istate->cache[i]->name))
@@ -707,7 +763,7 @@ static int do_recursive_merge(struct repository *r,
repo_read_index(r);
- init_merge_options(&o, r);
+ init_ui_merge_options(&o, r);
o.ancestor = base ? base_label : "(empty tree)";
o.branch1 = "HEAD";
o.branch2 = next ? next_label : "(empty tree)";
@@ -716,6 +772,8 @@ static int do_recursive_merge(struct repository *r,
o.show_rename_progress = 1;
head_tree = parse_tree_indirect(head);
+ if (!head_tree)
+ return error(_("unable to read tree (%s)"), oid_to_hex(head));
next_tree = next ? repo_get_commit_tree(r, next) : empty_tree(r);
base_tree = base ? repo_get_commit_tree(r, base) : empty_tree(r);
@@ -779,29 +837,43 @@ static struct object_id *get_cache_tree_oid(struct index_state *istate)
static int is_index_unchanged(struct repository *r)
{
struct object_id head_oid, *cache_tree_oid;
+ const struct object_id *head_tree_oid;
struct commit *head_commit;
struct index_state *istate = r->index;
+ const char *head_name;
+
+ if (!refs_resolve_ref_unsafe(get_main_ref_store(the_repository), "HEAD", RESOLVE_REF_READING, &head_oid, NULL)) {
+ /* Check to see if this is an unborn branch */
+ head_name = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+ "HEAD",
+ RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,
+ &head_oid, NULL);
+ if (!head_name ||
+ !starts_with(head_name, "refs/heads/") ||
+ !is_null_oid(&head_oid))
+ return error(_("could not resolve HEAD commit"));
+ head_tree_oid = the_hash_algo->empty_tree;
+ } else {
+ head_commit = lookup_commit(r, &head_oid);
- if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, &head_oid, NULL))
- return error(_("could not resolve HEAD commit"));
-
- head_commit = lookup_commit(r, &head_oid);
+ /*
+ * If head_commit is NULL, check_commit, called from
+ * lookup_commit, would have indicated that head_commit is not
+ * a commit object already. repo_parse_commit() will return failure
+ * without further complaints in such a case. Otherwise, if
+ * the commit is invalid, repo_parse_commit() will complain. So
+ * there is nothing for us to say here. Just return failure.
+ */
+ if (repo_parse_commit(r, head_commit))
+ return -1;
- /*
- * If head_commit is NULL, check_commit, called from
- * lookup_commit, would have indicated that head_commit is not
- * a commit object already. repo_parse_commit() will return failure
- * without further complaints in such a case. Otherwise, if
- * the commit is invalid, repo_parse_commit() will complain. So
- * there is nothing for us to say here. Just return failure.
- */
- if (repo_parse_commit(r, head_commit))
- return -1;
+ head_tree_oid = get_commit_tree_oid(head_commit);
+ }
if (!(cache_tree_oid = get_cache_tree_oid(istate)))
return -1;
- return oideq(cache_tree_oid, get_commit_tree_oid(head_commit));
+ return oideq(cache_tree_oid, head_tree_oid);
}
static int write_author_script(const char *message)
@@ -1063,6 +1135,7 @@ static int run_git_commit(const char *defmsg,
struct replay_opts *opts,
unsigned int flags)
{
+ struct replay_ctx *ctx = opts->ctx;
struct child_process cmd = CHILD_PROCESS_INIT;
if ((flags & CLEANUP_MSG) && (flags & VERBATIM_MSG))
@@ -1080,7 +1153,7 @@ static int run_git_commit(const char *defmsg,
gpg_opt, gpg_opt);
}
- strvec_pushf(&cmd.env, GIT_REFLOG_ACTION "=%s", opts->reflog_message);
+ strvec_pushf(&cmd.env, GIT_REFLOG_ACTION "=%s", ctx->reflog_message);
if (opts->committer_date_is_author_date)
strvec_pushf(&cmd.env, "GIT_COMMITTER_DATE=%s",
@@ -1161,7 +1234,7 @@ void cleanup_message(struct strbuf *msgbuf,
strbuf_setlen(msgbuf, wt_status_locate_end(msgbuf->buf, msgbuf->len));
if (cleanup_mode != COMMIT_MSG_CLEANUP_NONE)
strbuf_stripspace(msgbuf,
- cleanup_mode == COMMIT_MSG_CLEANUP_ALL ? comment_line_char : '\0');
+ cleanup_mode == COMMIT_MSG_CLEANUP_ALL ? comment_line_str : NULL);
}
/*
@@ -1193,7 +1266,7 @@ int template_untouched(const struct strbuf *sb, const char *template_file,
return 0;
strbuf_stripspace(&tmpl,
- cleanup_mode == COMMIT_MSG_CLEANUP_ALL ? comment_line_char : '\0');
+ cleanup_mode == COMMIT_MSG_CLEANUP_ALL ? comment_line_str : NULL);
if (!skip_prefix(sb->buf, tmpl.buf, &start))
start = sb->buf;
strbuf_release(&tmpl);
@@ -1223,11 +1296,12 @@ int update_head_with_reflog(const struct commit *old_head,
strbuf_addch(&sb, '\n');
}
- transaction = ref_transaction_begin(err);
+ transaction = ref_store_transaction_begin(get_main_ref_store(the_repository),
+ 0, err);
if (!transaction ||
ref_transaction_update(transaction, "HEAD", new_head,
old_head ? &old_head->object.oid : null_oid(),
- 0, sb.buf, err) ||
+ NULL, NULL, 0, sb.buf, err) ||
ref_transaction_commit(transaction, err)) {
ret = -1;
}
@@ -1243,7 +1317,7 @@ static int run_rewrite_hook(const struct object_id *oldoid,
struct child_process proc = CHILD_PROCESS_INIT;
int code;
struct strbuf sb = STRBUF_INIT;
- const char *hook_path = find_hook("post-rewrite");
+ const char *hook_path = find_hook(the_repository, "post-rewrite");
if (!hook_path)
return 0;
@@ -1466,6 +1540,7 @@ static int try_to_commit(struct repository *r,
struct replay_opts *opts, unsigned int flags,
struct object_id *oid)
{
+ struct replay_ctx *ctx = opts->ctx;
struct object_id tree;
struct commit *current_head = NULL;
struct commit_list *parents = NULL;
@@ -1540,7 +1615,7 @@ static int try_to_commit(struct repository *r,
}
}
- if (hook_exists("prepare-commit-msg")) {
+ if (hook_exists(r, "prepare-commit-msg")) {
res = run_prepare_commit_msg_hook(r, msg, hook_commit);
if (res)
goto out;
@@ -1566,7 +1641,7 @@ static int try_to_commit(struct repository *r,
if (cleanup != COMMIT_MSG_CLEANUP_NONE)
strbuf_stripspace(msg,
- cleanup == COMMIT_MSG_CLEANUP_ALL ? comment_line_char : '\0');
+ cleanup == COMMIT_MSG_CLEANUP_ALL ? comment_line_str : NULL);
if ((flags & EDIT_MSG) && message_is_empty(msg, cleanup)) {
res = 1; /* run 'git commit' to display error message */
goto out;
@@ -1627,7 +1702,7 @@ static int try_to_commit(struct repository *r,
goto out;
}
- if (update_head_with_reflog(current_head, oid, opts->reflog_message,
+ if (update_head_with_reflog(current_head, oid, ctx->reflog_message,
msg, &err)) {
res = error("%s", err.buf);
goto out;
@@ -1639,6 +1714,7 @@ static int try_to_commit(struct repository *r,
out:
free_commit_extra_headers(extra);
+ free_commit_list(parents);
strbuf_release(&err);
strbuf_release(&commit_msg);
free(amend_author);
@@ -1648,8 +1724,8 @@ out:
static int write_rebase_head(struct object_id *oid)
{
- if (update_ref("rebase", "REBASE_HEAD", oid,
- NULL, REF_NO_DEREF, UPDATE_REFS_MSG_ON_ERR))
+ if (refs_update_ref(get_main_ref_store(the_repository), "rebase", "REBASE_HEAD", oid,
+ NULL, REF_NO_DEREF, UPDATE_REFS_MSG_ON_ERR))
return error(_("could not update %s"), "REBASE_HEAD");
return 0;
@@ -1676,7 +1752,7 @@ static int do_commit(struct repository *r,
strbuf_release(&sb);
if (!res) {
refs_delete_ref(get_main_ref_store(r), "",
- "CHERRY_PICK_HEAD", NULL, 0);
+ "CHERRY_PICK_HEAD", NULL, REF_NO_DEREF);
unlink(git_path_merge_msg(r));
if (!is_rebase_i(opts))
print_commit_summary(r, NULL, &oid,
@@ -1728,34 +1804,25 @@ static int allow_empty(struct repository *r,
int index_unchanged, originally_empty;
/*
- * Four cases:
- *
- * (1) we do not allow empty at all and error out.
+ * For a commit that is initially empty, allow_empty determines if it
+ * should be kept or not
*
- * (2) we allow ones that were initially empty, and
- * just drop the ones that become empty
- *
- * (3) we allow ones that were initially empty, but
- * halt for the ones that become empty;
- *
- * (4) we allow both.
+ * For a commit that becomes empty, keep_redundant_commits and
+ * drop_redundant_commits determine whether the commit should be kept or
+ * dropped. If neither is specified, halt.
*/
- if (!opts->allow_empty)
- return 0; /* let "git commit" barf as necessary */
-
index_unchanged = is_index_unchanged(r);
if (index_unchanged < 0)
return index_unchanged;
if (!index_unchanged)
return 0; /* we do not have to say --allow-empty */
- if (opts->keep_redundant_commits)
- return 1;
-
originally_empty = is_original_commit_empty(commit);
if (originally_empty < 0)
return originally_empty;
if (originally_empty)
+ return opts->allow_empty;
+ else if (opts->keep_redundant_commits)
return 1;
else if (opts->drop_redundant_commits)
return 2;
@@ -1788,6 +1855,8 @@ static const char *command_to_string(const enum todo_command command)
{
if (command < TODO_COMMENT)
return todo_command_info[command].str;
+ if (command == TODO_COMMENT)
+ return comment_line_str;
die(_("unknown command: %d"), command);
}
@@ -1795,7 +1864,7 @@ static char command_to_char(const enum todo_command command)
{
if (command < TODO_COMMENT)
return todo_command_info[command].c;
- return comment_line_char;
+ return 0;
}
static int is_noop(const enum todo_command command)
@@ -1849,7 +1918,7 @@ static int is_fixup_flag(enum todo_command command, unsigned flag)
static void add_commented_lines(struct strbuf *buf, const void *str, size_t len)
{
const char *s = str;
- while (len > 0 && s[0] == comment_line_char) {
+ while (starts_with_mem(s, len, comment_line_str)) {
size_t count;
const char *n = memchr(s, '\n', len);
if (!n)
@@ -1860,14 +1929,14 @@ static void add_commented_lines(struct strbuf *buf, const void *str, size_t len)
s += count;
len -= count;
}
- strbuf_add_commented_lines(buf, s, len, comment_line_char);
+ strbuf_add_commented_lines(buf, s, len, comment_line_str);
}
/* Does the current fixup chain contain a squash command? */
-static int seen_squash(struct replay_opts *opts)
+static int seen_squash(struct replay_ctx *ctx)
{
- return starts_with(opts->current_fixups.buf, "squash") ||
- strstr(opts->current_fixups.buf, "\nsquash");
+ return starts_with(ctx->current_fixups.buf, "squash") ||
+ strstr(ctx->current_fixups.buf, "\nsquash");
}
static void update_comment_bufs(struct strbuf *buf1, struct strbuf *buf2, int n)
@@ -1943,6 +2012,7 @@ static int append_squash_message(struct strbuf *buf, const char *body,
enum todo_command command, struct replay_opts *opts,
unsigned flag)
{
+ struct replay_ctx *ctx = opts->ctx;
const char *fixup_msg;
size_t commented_len = 0, fixup_off;
/*
@@ -1951,21 +2021,21 @@ static int append_squash_message(struct strbuf *buf, const char *body,
* squashing commit messages.
*/
if (starts_with(body, "amend!") ||
- ((command == TODO_SQUASH || seen_squash(opts)) &&
+ ((command == TODO_SQUASH || seen_squash(ctx)) &&
(starts_with(body, "squash!") || starts_with(body, "fixup!"))))
commented_len = commit_subject_length(body);
- strbuf_addf(buf, "\n%c ", comment_line_char);
+ strbuf_addf(buf, "\n%s ", comment_line_str);
strbuf_addf(buf, _(nth_commit_msg_fmt),
- ++opts->current_fixup_count + 1);
+ ++ctx->current_fixup_count + 1);
strbuf_addstr(buf, "\n\n");
- strbuf_add_commented_lines(buf, body, commented_len, comment_line_char);
+ strbuf_add_commented_lines(buf, body, commented_len, comment_line_str);
/* buf->buf may be reallocated so store an offset into the buffer */
fixup_off = buf->len;
strbuf_addstr(buf, body + commented_len);
/* fixup -C after squash behaves like squash */
- if (is_fixup_flag(command, flag) && !seen_squash(opts)) {
+ if (is_fixup_flag(command, flag) && !seen_squash(ctx)) {
/*
* We're replacing the commit message so we need to
* append the Signed-off-by: trailer if the user
@@ -1999,12 +2069,13 @@ static int update_squash_messages(struct repository *r,
struct replay_opts *opts,
unsigned flag)
{
+ struct replay_ctx *ctx = opts->ctx;
struct strbuf buf = STRBUF_INIT;
int res = 0;
const char *message, *body;
const char *encoding = get_commit_output_encoding();
- if (opts->current_fixup_count > 0) {
+ if (ctx->current_fixup_count > 0) {
struct strbuf header = STRBUF_INIT;
char *eol;
@@ -2012,15 +2083,15 @@ static int update_squash_messages(struct repository *r,
return error(_("could not read '%s'"),
rebase_path_squash_msg());
- eol = buf.buf[0] != comment_line_char ?
+ eol = !starts_with(buf.buf, comment_line_str) ?
buf.buf : strchrnul(buf.buf, '\n');
- strbuf_addf(&header, "%c ", comment_line_char);
+ strbuf_addf(&header, "%s ", comment_line_str);
strbuf_addf(&header, _(combined_commit_msg_fmt),
- opts->current_fixup_count + 2);
+ ctx->current_fixup_count + 2);
strbuf_splice(&buf, 0, eol - buf.buf, header.buf, header.len);
strbuf_release(&header);
- if (is_fixup_flag(command, flag) && !seen_squash(opts))
+ if (is_fixup_flag(command, flag) && !seen_squash(ctx))
update_squash_message_for_fixup(&buf);
} else {
struct object_id head;
@@ -2041,16 +2112,16 @@ static int update_squash_messages(struct repository *r,
repo_unuse_commit_buffer(r, head_commit, head_message);
return error(_("cannot write '%s'"), rebase_path_fixup_msg());
}
- strbuf_addf(&buf, "%c ", comment_line_char);
+ strbuf_addf(&buf, "%s ", comment_line_str);
strbuf_addf(&buf, _(combined_commit_msg_fmt), 2);
- strbuf_addf(&buf, "\n%c ", comment_line_char);
+ strbuf_addf(&buf, "\n%s ", comment_line_str);
strbuf_addstr(&buf, is_fixup_flag(command, flag) ?
_(skip_first_commit_msg_str) :
_(first_commit_msg_str));
strbuf_addstr(&buf, "\n\n");
if (is_fixup_flag(command, flag))
strbuf_add_commented_lines(&buf, body, strlen(body),
- comment_line_char);
+ comment_line_str);
else
strbuf_addstr(&buf, body);
@@ -2065,12 +2136,12 @@ static int update_squash_messages(struct repository *r,
if (command == TODO_SQUASH || is_fixup_flag(command, flag)) {
res = append_squash_message(&buf, body, command, opts, flag);
} else if (command == TODO_FIXUP) {
- strbuf_addf(&buf, "\n%c ", comment_line_char);
+ strbuf_addf(&buf, "\n%s ", comment_line_str);
strbuf_addf(&buf, _(skip_nth_commit_msg_fmt),
- ++opts->current_fixup_count + 1);
+ ++ctx->current_fixup_count + 1);
strbuf_addstr(&buf, "\n\n");
strbuf_add_commented_lines(&buf, body, strlen(body),
- comment_line_char);
+ comment_line_str);
} else
return error(_("unknown command: %d"), command);
repo_unuse_commit_buffer(r, commit, message);
@@ -2081,12 +2152,12 @@ static int update_squash_messages(struct repository *r,
strbuf_release(&buf);
if (!res) {
- strbuf_addf(&opts->current_fixups, "%s%s %s",
- opts->current_fixups.len ? "\n" : "",
+ strbuf_addf(&ctx->current_fixups, "%s%s %s",
+ ctx->current_fixups.len ? "\n" : "",
command_to_string(command),
oid_to_hex(&commit->object.oid));
- res = write_message(opts->current_fixups.buf,
- opts->current_fixups.len,
+ res = write_message(ctx->current_fixups.buf,
+ ctx->current_fixups.len,
rebase_path_current_fixups(), 0);
}
@@ -2164,6 +2235,7 @@ static int do_pick_commit(struct repository *r,
struct replay_opts *opts,
int final_fixup, int *check_todo)
{
+ struct replay_ctx *ctx = opts->ctx;
unsigned int flags = should_edit(opts) ? EDIT_MSG : 0;
const char *msg_file = should_edit(opts) ? NULL : git_path_merge_msg(r);
struct object_id head;
@@ -2171,7 +2243,6 @@ static int do_pick_commit(struct repository *r,
const char *base_label, *next_label;
char *author = NULL;
struct commit_message msg = { NULL, NULL, NULL, NULL };
- struct strbuf msgbuf = STRBUF_INIT;
int res, unborn = 0, reword = 0, allow, drop_commit;
enum todo_command command = item->command;
struct commit *commit = item->commit;
@@ -2196,7 +2267,7 @@ static int do_pick_commit(struct repository *r,
unborn = 1;
} else if (unborn)
oidcpy(&head, the_hash_algo->empty_tree);
- if (index_differs_from(r, unborn ? empty_tree_oid_hex() : "HEAD",
+ if (index_differs_from(r, unborn ? empty_tree_oid_hex(the_repository->hash_algo) : "HEAD",
NULL, 0))
return error_dirty_index(r, opts);
}
@@ -2263,26 +2334,37 @@ static int do_pick_commit(struct repository *r,
*/
if (command == TODO_REVERT) {
+ const char *orig_subject;
+
base = commit;
base_label = msg.label;
next = parent;
next_label = msg.parent_label;
if (opts->commit_use_reference) {
- strbuf_addstr(&msgbuf,
+ strbuf_addstr(&ctx->message,
"# *** SAY WHY WE ARE REVERTING ON THE TITLE LINE ***");
+ } else if (skip_prefix(msg.subject, "Revert \"", &orig_subject) &&
+ /*
+ * We don't touch pre-existing repeated reverts, because
+ * theoretically these can be nested arbitrarily deeply,
+ * thus requiring excessive complexity to deal with.
+ */
+ !starts_with(orig_subject, "Revert \"")) {
+ strbuf_addstr(&ctx->message, "Reapply \"");
+ strbuf_addstr(&ctx->message, orig_subject);
} else {
- strbuf_addstr(&msgbuf, "Revert \"");
- strbuf_addstr(&msgbuf, msg.subject);
- strbuf_addstr(&msgbuf, "\"");
+ strbuf_addstr(&ctx->message, "Revert \"");
+ strbuf_addstr(&ctx->message, msg.subject);
+ strbuf_addstr(&ctx->message, "\"");
}
- strbuf_addstr(&msgbuf, "\n\nThis reverts commit ");
- refer_to_commit(opts, &msgbuf, commit);
+ strbuf_addstr(&ctx->message, "\n\nThis reverts commit ");
+ refer_to_commit(opts, &ctx->message, commit);
if (commit->parents && commit->parents->next) {
- strbuf_addstr(&msgbuf, ", reversing\nchanges made to ");
- refer_to_commit(opts, &msgbuf, parent);
+ strbuf_addstr(&ctx->message, ", reversing\nchanges made to ");
+ refer_to_commit(opts, &ctx->message, parent);
}
- strbuf_addstr(&msgbuf, ".\n");
+ strbuf_addstr(&ctx->message, ".\n");
} else {
const char *p;
@@ -2291,21 +2373,22 @@ static int do_pick_commit(struct repository *r,
next = commit;
next_label = msg.label;
- /* Append the commit log message to msgbuf. */
+ /* Append the commit log message to ctx->message. */
if (find_commit_subject(msg.message, &p))
- strbuf_addstr(&msgbuf, p);
+ strbuf_addstr(&ctx->message, p);
if (opts->record_origin) {
- strbuf_complete_line(&msgbuf);
- if (!has_conforming_footer(&msgbuf, NULL, 0))
- strbuf_addch(&msgbuf, '\n');
- strbuf_addstr(&msgbuf, cherry_picked_prefix);
- strbuf_addstr(&msgbuf, oid_to_hex(&commit->object.oid));
- strbuf_addstr(&msgbuf, ")\n");
+ strbuf_complete_line(&ctx->message);
+ if (!has_conforming_footer(&ctx->message, NULL, 0))
+ strbuf_addch(&ctx->message, '\n');
+ strbuf_addstr(&ctx->message, cherry_picked_prefix);
+ strbuf_addstr(&ctx->message, oid_to_hex(&commit->object.oid));
+ strbuf_addstr(&ctx->message, ")\n");
}
if (!is_fixup(command))
author = get_author(msg.message);
}
+ ctx->have_message = 1;
if (command == TODO_REWORD)
reword = 1;
@@ -2336,7 +2419,7 @@ static int do_pick_commit(struct repository *r,
}
if (opts->signoff && !is_fixup(command))
- append_signoff(&msgbuf, 0, 0);
+ append_signoff(&ctx->message, 0, 0);
if (is_rebase_i(opts) && write_author_script(msg.message) < 0)
res = -1;
@@ -2345,17 +2428,17 @@ static int do_pick_commit(struct repository *r,
!strcmp(opts->strategy, "ort") ||
command == TODO_REVERT) {
res = do_recursive_merge(r, base, next, base_label, next_label,
- &head, &msgbuf, opts);
+ &head, &ctx->message, opts);
if (res < 0)
goto leave;
- res |= write_message(msgbuf.buf, msgbuf.len,
+ res |= write_message(ctx->message.buf, ctx->message.len,
git_path_merge_msg(r), 0);
} else {
struct commit_list *common = NULL;
struct commit_list *remotes = NULL;
- res = write_message(msgbuf.buf, msgbuf.len,
+ res = write_message(ctx->message.buf, ctx->message.len,
git_path_merge_msg(r), 0);
commit_list_insert(base, &common);
@@ -2376,19 +2459,19 @@ static int do_pick_commit(struct repository *r,
if ((command == TODO_PICK || command == TODO_REWORD ||
command == TODO_EDIT) && !opts->no_commit &&
(res == 0 || res == 1) &&
- update_ref(NULL, "CHERRY_PICK_HEAD", &commit->object.oid, NULL,
- REF_NO_DEREF, UPDATE_REFS_MSG_ON_ERR))
+ refs_update_ref(get_main_ref_store(the_repository), NULL, "CHERRY_PICK_HEAD", &commit->object.oid, NULL,
+ REF_NO_DEREF, UPDATE_REFS_MSG_ON_ERR))
res = -1;
if (command == TODO_REVERT && ((opts->no_commit && res == 0) || res == 1) &&
- update_ref(NULL, "REVERT_HEAD", &commit->object.oid, NULL,
- REF_NO_DEREF, UPDATE_REFS_MSG_ON_ERR))
+ refs_update_ref(get_main_ref_store(the_repository), NULL, "REVERT_HEAD", &commit->object.oid, NULL,
+ REF_NO_DEREF, UPDATE_REFS_MSG_ON_ERR))
res = -1;
if (res) {
error(command == TODO_REVERT
? _("could not revert %s... %s")
: _("could not apply %s... %s"),
- short_commit_name(commit), msg.subject);
+ short_commit_name(r, commit), msg.subject);
print_advice(r, res == 1, opts);
repo_rerere(r, opts->allow_rerere_auto);
goto leave;
@@ -2404,9 +2487,10 @@ static int do_pick_commit(struct repository *r,
} else if (allow == 2) {
drop_commit = 1;
refs_delete_ref(get_main_ref_store(r), "", "CHERRY_PICK_HEAD",
- NULL, 0);
+ NULL, REF_NO_DEREF);
unlink(git_path_merge_msg(r));
- unlink(git_path_auto_merge(r));
+ refs_delete_ref(get_main_ref_store(r), "", "AUTO_MERGE",
+ NULL, REF_NO_DEREF);
fprintf(stderr,
_("dropping %s %s -- patch contents already upstream\n"),
oid_to_hex(&commit->object.oid), msg.subject);
@@ -2432,14 +2516,13 @@ fast_forward_edit:
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;
+ strbuf_reset(&ctx->current_fixups);
+ ctx->current_fixup_count = 0;
}
leave:
free_message(commit, &msg);
free(author);
- strbuf_release(&msgbuf);
update_abort_safety_file();
return res;
@@ -2547,8 +2630,63 @@ static int check_label_or_ref_arg(enum todo_command command, const char *arg)
return 0;
}
-static int parse_insn_line(struct repository *r, struct todo_item *item,
- const char *buf, const char *bol, char *eol)
+static int check_merge_commit_insn(enum todo_command command)
+{
+ switch(command) {
+ case TODO_PICK:
+ error(_("'%s' does not accept merge commits"),
+ todo_command_info[command].str);
+ advise_if_enabled(ADVICE_REBASE_TODO_ERROR, _(
+ /*
+ * TRANSLATORS: 'pick' and 'merge -C' should not be
+ * translated.
+ */
+ "'pick' does not take a merge commit. If you wanted to\n"
+ "replay the merge, use 'merge -C' on the commit."));
+ return -1;
+
+ case TODO_REWORD:
+ error(_("'%s' does not accept merge commits"),
+ todo_command_info[command].str);
+ advise_if_enabled(ADVICE_REBASE_TODO_ERROR, _(
+ /*
+ * TRANSLATORS: 'reword' and 'merge -c' should not be
+ * translated.
+ */
+ "'reword' does not take a merge commit. If you wanted to\n"
+ "replay the merge and reword the commit message, use\n"
+ "'merge -c' on the commit"));
+ return -1;
+
+ case TODO_EDIT:
+ error(_("'%s' does not accept merge commits"),
+ todo_command_info[command].str);
+ advise_if_enabled(ADVICE_REBASE_TODO_ERROR, _(
+ /*
+ * TRANSLATORS: 'edit', 'merge -C' and 'break' should
+ * not be translated.
+ */
+ "'edit' does not take a merge commit. If you wanted to\n"
+ "replay the merge, use 'merge -C' on the commit, and then\n"
+ "'break' to give the control back to you so that you can\n"
+ "do 'git commit --amend && git rebase --continue'."));
+ return -1;
+
+ case TODO_FIXUP:
+ case TODO_SQUASH:
+ return error(_("cannot squash merge commit into another commit"));
+
+ case TODO_MERGE:
+ return 0;
+
+ default:
+ BUG("unexpected todo_command");
+ }
+}
+
+static int parse_insn_line(struct repository *r, struct replay_opts *opts,
+ struct todo_item *item, const char *buf,
+ const char *bol, char *eol)
{
struct object_id commit_oid;
char *end_of_object_name;
@@ -2559,7 +2697,7 @@ static int parse_insn_line(struct repository *r, struct todo_item *item,
/* left-trim */
bol += strspn(bol, " \t");
- if (bol == eol || *bol == '\r' || *bol == comment_line_char) {
+ if (bol == eol || *bol == '\r' || starts_with_mem(bol, eol - bol, comment_line_str)) {
item->command = TODO_COMMENT;
item->commit = NULL;
item->arg_offset = bol - buf;
@@ -2652,10 +2790,15 @@ static int parse_insn_line(struct repository *r, struct todo_item *item,
return status;
item->commit = lookup_commit_reference(r, &commit_oid);
- return item->commit ? 0 : -1;
+ if (!item->commit)
+ return -1;
+ if (is_rebase_i(opts) &&
+ item->commit->parents && item->commit->parents->next)
+ return check_merge_commit_insn(item->command);
+ return 0;
}
-int sequencer_get_last_command(struct repository *r, enum replay_action *action)
+int sequencer_get_last_command(struct repository *r UNUSED, enum replay_action *action)
{
const char *todo_file, *bol;
struct strbuf buf = STRBUF_INIT;
@@ -2682,8 +2825,8 @@ int sequencer_get_last_command(struct repository *r, enum replay_action *action)
return ret;
}
-int todo_list_parse_insn_buffer(struct repository *r, char *buf,
- struct todo_list *todo_list)
+int todo_list_parse_insn_buffer(struct repository *r, struct replay_opts *opts,
+ char *buf, struct todo_list *todo_list)
{
struct todo_item *item;
char *p = buf, *next_p;
@@ -2701,7 +2844,7 @@ int todo_list_parse_insn_buffer(struct repository *r, char *buf,
item = append_new_todo(todo_list);
item->offset_in_buf = p - todo_list->buf.buf;
- if (parse_insn_line(r, item, buf, p, eol)) {
+ if (parse_insn_line(r, opts, item, buf, p, eol)) {
res = error(_("invalid line %d: %.*s"),
i, (int)(eol - p), p);
item->command = TODO_COMMENT + 1;
@@ -2800,7 +2943,7 @@ void sequencer_post_commit_cleanup(struct repository *r, int verbose)
if (refs_ref_exists(get_main_ref_store(r), "CHERRY_PICK_HEAD")) {
if (!refs_delete_ref(get_main_ref_store(r), "",
- "CHERRY_PICK_HEAD", NULL, 0) &&
+ "CHERRY_PICK_HEAD", NULL, REF_NO_DEREF) &&
verbose)
warning(_("cancelling a cherry picking in progress"));
opts.action = REPLAY_PICK;
@@ -2809,22 +2952,25 @@ void sequencer_post_commit_cleanup(struct repository *r, int verbose)
if (refs_ref_exists(get_main_ref_store(r), "REVERT_HEAD")) {
if (!refs_delete_ref(get_main_ref_store(r), "", "REVERT_HEAD",
- NULL, 0) &&
+ NULL, REF_NO_DEREF) &&
verbose)
warning(_("cancelling a revert in progress"));
opts.action = REPLAY_REVERT;
need_cleanup = 1;
}
- unlink(git_path_auto_merge(r));
+ refs_delete_ref(get_main_ref_store(r), "", "AUTO_MERGE",
+ NULL, REF_NO_DEREF);
if (!need_cleanup)
- return;
+ goto out;
if (!have_finished_the_last_pick())
- return;
+ goto out;
sequencer_remove_state(&opts);
+out:
+ replay_opts_release(&opts);
}
static void todo_list_write_total_nr(struct todo_list *todo_list)
@@ -2848,7 +2994,7 @@ static int read_populate_todo(struct repository *r,
if (strbuf_read_file_or_whine(&todo_list->buf, todo_file) < 0)
return -1;
- res = todo_list_parse_insn_buffer(r, todo_list->buf.buf, todo_list);
+ res = todo_list_parse_insn_buffer(r, opts, todo_list->buf.buf, todo_list);
if (res) {
if (is_rebase_i(opts))
return error(_("please fix this using "
@@ -2878,7 +3024,7 @@ static int read_populate_todo(struct repository *r,
struct todo_list done = TODO_LIST_INIT;
if (strbuf_read_file(&done.buf, rebase_path_done(), 0) > 0 &&
- !todo_list_parse_insn_buffer(r, done.buf.buf, &done))
+ !todo_list_parse_insn_buffer(r, opts, done.buf.buf, &done))
todo_list->done_nr = count_commands(&done);
else
todo_list->done_nr = 0;
@@ -2922,6 +3068,9 @@ static int populate_opts_cb(const char *key, const char *value,
else if (!strcmp(key, "options.allow-empty-message"))
opts->allow_empty_message =
git_config_bool_or_int(key, value, ctx->kvi, &error_flag);
+ else if (!strcmp(key, "options.drop-redundant-commits"))
+ opts->drop_redundant_commits =
+ git_config_bool_or_int(key, value, ctx->kvi, &error_flag);
else if (!strcmp(key, "options.keep-redundant-commits"))
opts->keep_redundant_commits =
git_config_bool_or_int(key, value, ctx->kvi, &error_flag);
@@ -2992,6 +3141,8 @@ static void read_strategy_opts(struct replay_opts *opts, struct strbuf *buf)
static int read_populate_opts(struct replay_opts *opts)
{
+ struct replay_ctx *ctx = opts->ctx;
+
if (is_rebase_i(opts)) {
struct strbuf buf = STRBUF_INIT;
int ret = 0;
@@ -3051,13 +3202,13 @@ static int read_populate_opts(struct replay_opts *opts)
read_strategy_opts(opts, &buf);
strbuf_reset(&buf);
- if (read_oneliner(&opts->current_fixups,
+ if (read_oneliner(&ctx->current_fixups,
rebase_path_current_fixups(),
READ_ONELINER_SKIP_IF_EMPTY)) {
- const char *p = opts->current_fixups.buf;
- opts->current_fixup_count = 1;
+ const char *p = ctx->current_fixups.buf;
+ ctx->current_fixup_count = 1;
while ((p = strchr(p, '\n'))) {
- opts->current_fixup_count++;
+ ctx->current_fixup_count++;
p++;
}
}
@@ -3177,7 +3328,8 @@ static int walk_revs_populate_todo(struct todo_list *todo_list,
item->offset_in_buf = todo_list->buf.len;
subject_len = find_commit_subject(commit_buffer, &subject);
strbuf_addf(&todo_list->buf, "%s %s %.*s\n", command_string,
- short_commit_name(commit), subject_len, subject);
+ short_commit_name(the_repository, commit),
+ subject_len, subject);
repo_unuse_commit_buffer(the_repository, commit,
commit_buffer);
}
@@ -3246,12 +3398,12 @@ static int rollback_is_safe(void)
strbuf_release(&sb);
}
else if (errno == ENOENT)
- oidclr(&expected_head);
+ oidclr(&expected_head, the_repository->hash_algo);
else
die_errno(_("could not read '%s'"), git_path_abort_safety_file());
if (repo_get_oid(the_repository, "HEAD", &actual_head))
- oidclr(&actual_head);
+ oidclr(&actual_head, the_repository->hash_algo);
return oideq(&actual_head, &expected_head);
}
@@ -3276,7 +3428,7 @@ static int rollback_single_pick(struct repository *r)
if (!refs_ref_exists(get_main_ref_store(r), "CHERRY_PICK_HEAD") &&
!refs_ref_exists(get_main_ref_store(r), "REVERT_HEAD"))
return error(_("no cherry-pick or revert in progress"));
- if (read_ref_full("HEAD", 0, &head_oid, NULL))
+ if (refs_read_ref_full(get_main_ref_store(the_repository), "HEAD", 0, &head_oid, NULL))
return error(_("cannot resolve HEAD"));
if (is_null_oid(&head_oid))
return error(_("cannot abort from a branch yet to be born"));
@@ -3287,7 +3439,7 @@ static int skip_single_pick(void)
{
struct object_id head;
- if (read_ref_full("HEAD", 0, &head, NULL))
+ if (refs_read_ref_full(get_main_ref_store(the_repository), "HEAD", 0, &head, NULL))
return error(_("cannot resolve HEAD"));
return reset_merge(&head);
}
@@ -3455,54 +3607,57 @@ static int save_opts(struct replay_opts *opts)
if (opts->no_commit)
res |= git_config_set_in_file_gently(opts_file,
- "options.no-commit", "true");
+ "options.no-commit", NULL, "true");
if (opts->edit >= 0)
- res |= git_config_set_in_file_gently(opts_file, "options.edit",
+ res |= git_config_set_in_file_gently(opts_file, "options.edit", NULL,
opts->edit ? "true" : "false");
if (opts->allow_empty)
res |= git_config_set_in_file_gently(opts_file,
- "options.allow-empty", "true");
+ "options.allow-empty", NULL, "true");
if (opts->allow_empty_message)
res |= git_config_set_in_file_gently(opts_file,
- "options.allow-empty-message", "true");
+ "options.allow-empty-message", NULL, "true");
+ if (opts->drop_redundant_commits)
+ res |= git_config_set_in_file_gently(opts_file,
+ "options.drop-redundant-commits", NULL, "true");
if (opts->keep_redundant_commits)
res |= git_config_set_in_file_gently(opts_file,
- "options.keep-redundant-commits", "true");
+ "options.keep-redundant-commits", NULL, "true");
if (opts->signoff)
res |= git_config_set_in_file_gently(opts_file,
- "options.signoff", "true");
+ "options.signoff", NULL, "true");
if (opts->record_origin)
res |= git_config_set_in_file_gently(opts_file,
- "options.record-origin", "true");
+ "options.record-origin", NULL, "true");
if (opts->allow_ff)
res |= git_config_set_in_file_gently(opts_file,
- "options.allow-ff", "true");
+ "options.allow-ff", NULL, "true");
if (opts->mainline) {
struct strbuf buf = STRBUF_INIT;
strbuf_addf(&buf, "%d", opts->mainline);
res |= git_config_set_in_file_gently(opts_file,
- "options.mainline", buf.buf);
+ "options.mainline", NULL, buf.buf);
strbuf_release(&buf);
}
if (opts->strategy)
res |= git_config_set_in_file_gently(opts_file,
- "options.strategy", opts->strategy);
+ "options.strategy", NULL, opts->strategy);
if (opts->gpg_sign)
res |= git_config_set_in_file_gently(opts_file,
- "options.gpg-sign", opts->gpg_sign);
+ "options.gpg-sign", NULL, opts->gpg_sign);
for (size_t i = 0; i < opts->xopts.nr; i++)
res |= git_config_set_multivar_in_file_gently(opts_file,
"options.strategy-option",
- opts->xopts.v[i], "^$", 0);
+ opts->xopts.v[i], "^$", NULL, 0);
if (opts->allow_rerere_auto)
res |= git_config_set_in_file_gently(opts_file,
- "options.allow-rerere-auto",
+ "options.allow-rerere-auto", NULL,
opts->allow_rerere_auto == RERERE_AUTOUPDATE ?
"true" : "false");
if (opts->explicit_cleanup)
res |= git_config_set_in_file_gently(opts_file,
- "options.default-msg-cleanup",
+ "options.default-msg-cleanup", NULL,
describe_cleanup_mode(opts->default_msg_cleanup));
return res;
}
@@ -3574,13 +3729,24 @@ static int error_with_patch(struct repository *r,
struct replay_opts *opts,
int exit_code, int to_amend)
{
- if (commit) {
- if (make_patch(r, commit, opts))
+ struct replay_ctx *ctx = opts->ctx;
+
+ /*
+ * Write the commit message to be used by "git rebase
+ * --continue". If a "fixup" or "squash" command has conflicts
+ * then we will have already written rebase_path_message() in
+ * error_failed_squash(). If an "edit" command was
+ * fast-forwarded then we don't have a message in ctx->message
+ * and rely on make_patch() to write rebase_path_message()
+ * instead.
+ */
+ if (ctx->have_message && !file_exists(rebase_path_message()) &&
+ write_message(ctx->message.buf, ctx->message.len,
+ rebase_path_message(), 0))
+ return error(_("could not write commit message file"));
+
+ if (commit && 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())
@@ -3598,7 +3764,7 @@ static int error_with_patch(struct repository *r,
} else if (exit_code) {
if (commit)
fprintf_ln(stderr, _("Could not apply %s... %.*s"),
- short_commit_name(commit), subject_len, subject);
+ short_commit_name(r, commit), subject_len, subject);
else
/*
* We don't have the hash of the parent so
@@ -3628,14 +3794,16 @@ static int error_failed_squash(struct repository *r,
return error_with_patch(r, commit, subject, subject_len, opts, 1, 0);
}
-static int do_exec(struct repository *r, const char *command_line)
+static int do_exec(struct repository *r, const char *command_line, int quiet)
{
struct child_process cmd = CHILD_PROCESS_INIT;
int dirty, status;
- fprintf(stderr, _("Executing: %s\n"), command_line);
+ if (!quiet)
+ fprintf(stderr, _("Executing: %s\n"), command_line);
cmd.use_shell = 1;
strvec_push(&cmd.args, command_line);
+ strvec_push(&cmd.env, "GIT_CHERRY_PICK_HELP");
status = run_command(&cmd);
/* force re-reading of the cache */
@@ -3722,15 +3890,16 @@ static int do_label(struct repository *r, const char *name, int len)
strbuf_addf(&ref_name, "refs/rewritten/%.*s", len, name);
strbuf_addf(&msg, "rebase (label) '%.*s'", len, name);
- transaction = ref_store_transaction_begin(refs, &err);
+ transaction = ref_store_transaction_begin(refs, 0, &err);
if (!transaction) {
error("%s", err.buf);
ret = -1;
} else if (repo_get_oid(r, "HEAD", &head_oid)) {
error(_("could not read HEAD"));
ret = -1;
- } else if (ref_transaction_update(transaction, ref_name.buf, &head_oid,
- NULL, 0, msg.buf, &err) < 0 ||
+ } else if (ref_transaction_update(transaction, ref_name.buf,
+ &head_oid, NULL, NULL, NULL,
+ 0, msg.buf, &err) < 0 ||
ref_transaction_commit(transaction, &err)) {
error("%s", err.buf);
ret = -1;
@@ -3788,7 +3957,7 @@ static struct commit *lookup_label(struct repository *r, const char *label,
strbuf_reset(buf);
strbuf_addf(buf, "refs/rewritten/%.*s", len, label);
- if (!read_ref(buf->buf, &oid)) {
+ if (!refs_read_ref(get_main_ref_store(the_repository), buf->buf, &oid)) {
commit = lookup_commit_object(r, &oid);
} else {
/* fall back to non-rewritten ref or commit */
@@ -3876,15 +4045,18 @@ static int do_reset(struct repository *r,
}
tree = parse_tree_indirect(&oid);
+ if (!tree)
+ return error(_("unable to read tree (%s)"), oid_to_hex(&oid));
prime_cache_tree(r, r->index, tree);
if (write_locked_index(r->index, &lock, COMMIT_LOCK) < 0)
ret = error(_("could not write index"));
if (!ret)
- ret = update_ref(reflog_message(opts, "reset", "'%.*s'",
- len, name), "HEAD", &oid,
- NULL, 0, UPDATE_REFS_MSG_ON_ERR);
+ ret = refs_update_ref(get_main_ref_store(the_repository), reflog_message(opts, "reset", "'%.*s'",
+ len, name),
+ "HEAD", &oid,
+ NULL, 0, UPDATE_REFS_MSG_ON_ERR);
cleanup:
free((void *)desc.buffer);
if (ret < 0)
@@ -3899,10 +4071,11 @@ static int do_merge(struct repository *r,
const char *arg, int arg_len,
int flags, int *check_todo, struct replay_opts *opts)
{
+ struct replay_ctx *ctx = opts->ctx;
int run_commit_flags = 0;
struct strbuf ref_name = STRBUF_INIT;
struct commit *head_commit, *merge_commit, *i;
- struct commit_list *bases, *j;
+ struct commit_list *bases = NULL, *j;
struct commit_list *to_merge = NULL, **tail = &to_merge;
const char *strategy = !opts->xopts.nr &&
(!opts->strategy ||
@@ -4027,40 +4200,31 @@ static int do_merge(struct repository *r,
write_author_script(message);
find_commit_subject(message, &body);
len = strlen(body);
- ret = write_message(body, len, git_path_merge_msg(r), 0);
+ strbuf_add(&ctx->message, body, len);
repo_unuse_commit_buffer(r, commit, message);
- if (ret) {
- error_errno(_("could not write '%s'"),
- git_path_merge_msg(r));
- goto leave_merge;
- }
} else {
struct strbuf buf = STRBUF_INIT;
- int len;
strbuf_addf(&buf, "author %s", git_author_info(0));
write_author_script(buf.buf);
- strbuf_reset(&buf);
+ strbuf_release(&buf);
if (oneline_offset < arg_len) {
- p = arg + oneline_offset;
- len = arg_len - oneline_offset;
+ strbuf_add(&ctx->message, arg + oneline_offset,
+ arg_len - oneline_offset);
} else {
- strbuf_addf(&buf, "Merge %s '%.*s'",
+ strbuf_addf(&ctx->message, "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(r), 0);
- strbuf_release(&buf);
- if (ret) {
- error_errno(_("could not write '%s'"),
- git_path_merge_msg(r));
- goto leave_merge;
}
}
+ ctx->have_message = 1;
+ if (write_message(ctx->message.buf, ctx->message.len,
+ git_path_merge_msg(r), 0)) {
+ ret = error_errno(_("could not write '%s'"),
+ git_path_merge_msg(r));
+ goto leave_merge;
+ }
if (strategy || to_merge->next) {
/* Octopus merge */
@@ -4113,7 +4277,7 @@ static int do_merge(struct repository *r,
strbuf_release(&ref_name);
refs_delete_ref(get_main_ref_store(r), "", "CHERRY_PICK_HEAD",
- NULL, 0);
+ NULL, REF_NO_DEREF);
rollback_lock_file(&lock);
ret = run_command(&cmd);
@@ -4128,7 +4292,11 @@ static int do_merge(struct repository *r,
}
merge_commit = to_merge->item;
- bases = repo_get_merge_bases(r, head_commit, merge_commit);
+ if (repo_get_merge_bases(r, head_commit, merge_commit, &bases) < 0) {
+ ret = -1;
+ goto leave_merge;
+ }
+
if (bases && oideq(&merge_commit->object.oid,
&bases->item->object.oid)) {
ret = 0;
@@ -4143,7 +4311,7 @@ static int do_merge(struct repository *r,
bases = reverse_commit_list(bases);
repo_read_index(r);
- init_merge_options(&o, r);
+ init_ui_merge_options(&o, r);
o.branch1 = "HEAD";
o.branch2 = ref_name.buf;
o.buffer_output = 2;
@@ -4211,6 +4379,7 @@ leave_merge:
strbuf_release(&ref_name);
rollback_lock_file(&lock);
free_commit_list(to_merge);
+ free_commit_list(bases);
return ret;
}
@@ -4370,7 +4539,7 @@ static int do_update_ref(struct repository *r, const char *refname)
for_each_string_list_item(item, &list) {
if (!strcmp(item->string, refname)) {
struct update_ref_record *rec = item->util;
- if (read_ref("HEAD", &rec->after))
+ if (refs_read_ref(get_main_ref_store(the_repository), "HEAD", &rec->after))
return -1;
break;
}
@@ -4458,12 +4627,17 @@ static enum todo_command peek_command(struct todo_list *todo_list, int offset)
return -1;
}
-void create_autostash(struct repository *r, const char *path)
+static void create_autostash_internal(struct repository *r,
+ const char *path,
+ const char *refname)
{
struct strbuf buf = STRBUF_INIT;
struct lock_file lock_file = LOCK_INIT;
int fd;
+ if (path && refname)
+ BUG("can only pass path or refname");
+
fd = repo_hold_locked_index(r, &lock_file, 0);
refresh_index(r->index, REFRESH_QUIET, NULL, NULL, NULL);
if (0 <= fd)
@@ -4490,10 +4664,16 @@ void create_autostash(struct repository *r, const char *path)
strbuf_reset(&buf);
strbuf_add_unique_abbrev(&buf, &oid, DEFAULT_ABBREV);
- if (safe_create_leading_directories_const(path))
- die(_("Could not create directory for '%s'"),
- path);
- write_file(path, "%s", oid_to_hex(&oid));
+ if (path) {
+ if (safe_create_leading_directories_const(path))
+ die(_("Could not create directory for '%s'"),
+ path);
+ write_file(path, "%s", oid_to_hex(&oid));
+ } else {
+ refs_update_ref(get_main_ref_store(r), "", refname,
+ &oid, null_oid(), 0, UPDATE_REFS_DIE_ON_ERR);
+ }
+
printf(_("Created autostash: %s\n"), buf.buf);
if (reset_head(r, &ropts) < 0)
die(_("could not reset --hard"));
@@ -4504,6 +4684,16 @@ void create_autostash(struct repository *r, const char *path)
strbuf_release(&buf);
}
+void create_autostash(struct repository *r, const char *path)
+{
+ create_autostash_internal(r, path, NULL);
+}
+
+void create_autostash_ref(struct repository *r, const char *refname)
+{
+ create_autostash_internal(r, NULL, refname);
+}
+
static int apply_save_autostash_oid(const char *stash_oid, int attempt_apply)
{
struct child_process child = CHILD_PROCESS_INIT;
@@ -4581,6 +4771,41 @@ int apply_autostash_oid(const char *stash_oid)
return apply_save_autostash_oid(stash_oid, 1);
}
+static int apply_save_autostash_ref(struct repository *r, const char *refname,
+ int attempt_apply)
+{
+ struct object_id stash_oid;
+ char stash_oid_hex[GIT_MAX_HEXSZ + 1];
+ int flag, ret;
+
+ if (!refs_ref_exists(get_main_ref_store(r), refname))
+ return 0;
+
+ if (!refs_resolve_ref_unsafe(get_main_ref_store(r), refname,
+ RESOLVE_REF_READING, &stash_oid, &flag))
+ return -1;
+ if (flag & REF_ISSYMREF)
+ return error(_("autostash reference is a symref"));
+
+ oid_to_hex_r(stash_oid_hex, &stash_oid);
+ ret = apply_save_autostash_oid(stash_oid_hex, attempt_apply);
+
+ refs_delete_ref(get_main_ref_store(r), "", refname,
+ &stash_oid, REF_NO_DEREF);
+
+ return ret;
+}
+
+int save_autostash_ref(struct repository *r, const char *refname)
+{
+ return apply_save_autostash_ref(r, refname, 0);
+}
+
+int apply_autostash_ref(struct repository *r, const char *refname)
+{
+ return apply_save_autostash_ref(r, refname, 1);
+}
+
static int checkout_onto(struct repository *r, struct replay_opts *opts,
const char *onto_name, const struct object_id *onto,
const struct object_id *orig_head)
@@ -4661,11 +4886,12 @@ static int pick_one_commit(struct repository *r,
struct replay_opts *opts,
int *check_todo, int* reschedule)
{
+ struct replay_ctx *ctx = opts->ctx;
int res;
struct todo_item *item = todo_list->items + todo_list->current;
const char *arg = todo_item_get_arg(todo_list, item);
if (is_rebase_i(opts))
- opts->reflog_message = reflog_message(
+ ctx->reflog_message = reflog_message(
opts, command_to_string(item->command), NULL);
res = do_pick_commit(r, item, opts, is_final_fixup(todo_list),
@@ -4681,7 +4907,7 @@ static int pick_one_commit(struct repository *r,
if (!opts->verbose)
term_clear_line();
fprintf(stderr, _("Stopped at %s... %.*s\n"),
- short_commit_name(commit), item->arg_len, arg);
+ short_commit_name(r, commit), item->arg_len, arg);
}
return error_with_patch(r, commit,
arg, item->arg_len, opts, res, !res);
@@ -4722,9 +4948,10 @@ static int pick_commits(struct repository *r,
struct todo_list *todo_list,
struct replay_opts *opts)
{
+ struct replay_ctx *ctx = opts->ctx;
int res = 0, reschedule = 0;
- opts->reflog_message = sequencer_reflog_action(opts);
+ ctx->reflog_message = sequencer_reflog_action(opts);
if (opts->allow_ff)
assert(!(opts->signoff || opts->no_commit ||
opts->record_origin || should_edit(opts) ||
@@ -4763,8 +4990,10 @@ static int pick_commits(struct repository *r,
}
unlink(rebase_path_author_script());
unlink(git_path_merge_head(r));
- unlink(git_path_auto_merge(r));
- delete_ref(NULL, "REBASE_HEAD", NULL, REF_NO_DEREF);
+ refs_delete_ref(get_main_ref_store(r), "", "AUTO_MERGE",
+ NULL, REF_NO_DEREF);
+ refs_delete_ref(get_main_ref_store(r), "", "REBASE_HEAD",
+ NULL, REF_NO_DEREF);
if (item->command == TODO_BREAK) {
if (!opts->verbose)
@@ -4772,6 +5001,8 @@ static int pick_commits(struct repository *r,
return stopped_at_head(r);
}
}
+ strbuf_reset(&ctx->message);
+ ctx->have_message = 0;
if (item->command <= TODO_SQUASH) {
res = pick_one_commit(r, todo_list, opts, &check_todo,
&reschedule);
@@ -4784,7 +5015,7 @@ static int pick_commits(struct repository *r,
if (!opts->verbose)
term_clear_line();
*end_of_arg = '\0';
- res = do_exec(r, arg);
+ res = do_exec(r, arg, opts->quiet);
*end_of_arg = saved;
if (res) {
@@ -4868,15 +5099,15 @@ cleanup_head_ref:
}
msg = reflog_message(opts, "finish", "%s onto %s",
head_ref.buf, buf.buf);
- if (update_ref(msg, head_ref.buf, &head, &orig,
- REF_NO_DEREF, UPDATE_REFS_MSG_ON_ERR)) {
+ if (refs_update_ref(get_main_ref_store(the_repository), msg, head_ref.buf, &head, &orig,
+ REF_NO_DEREF, UPDATE_REFS_MSG_ON_ERR)) {
res = error(_("could not update %s"),
head_ref.buf);
goto cleanup_head_ref;
}
msg = reflog_message(opts, "finish", "returning to %s",
head_ref.buf);
- if (create_symref("HEAD", head_ref.buf, msg)) {
+ if (refs_update_symref(get_main_ref_store(the_repository), "HEAD", head_ref.buf, msg)) {
res = error(_("could not update HEAD to %s"),
head_ref.buf);
goto cleanup_head_ref;
@@ -4920,7 +5151,7 @@ cleanup_head_ref:
hook_opt.path_to_stdin = rebase_path_rewritten_list();
strvec_push(&hook_opt.args, "rebase");
- run_hooks_opt("post-rewrite", &hook_opt);
+ run_hooks_opt(r, "post-rewrite", &hook_opt);
}
apply_autostash(rebase_path_autostash());
@@ -4977,35 +5208,50 @@ static int commit_staged_changes(struct repository *r,
struct replay_opts *opts,
struct todo_list *todo_list)
{
+ struct replay_ctx *ctx = opts->ctx;
unsigned int flags = ALLOW_EMPTY | EDIT_MSG;
unsigned int final_fixup = 0, is_clean;
+ struct strbuf rev = STRBUF_INIT;
+ int ret;
- if (has_unstaged_changes(r, 1))
- return error(_("cannot rebase: You have unstaged changes."));
+ if (has_unstaged_changes(r, 1)) {
+ ret = error(_("cannot rebase: You have unstaged changes."));
+ goto out;
+ }
is_clean = !has_uncommitted_changes(r, 0);
if (!is_clean && !file_exists(rebase_path_message())) {
const char *gpg_opt = gpg_sign_opt_quoted(opts);
-
- return error(_(staged_changes_advice), gpg_opt, gpg_opt);
+ ret = error(_(staged_changes_advice), gpg_opt, gpg_opt);
+ goto out;
}
+
if (file_exists(rebase_path_amend())) {
- struct strbuf rev = STRBUF_INIT;
struct object_id head, to_amend;
- if (repo_get_oid(r, "HEAD", &head))
- return error(_("cannot amend non-existing commit"));
- if (!read_oneliner(&rev, rebase_path_amend(), 0))
- return error(_("invalid file: '%s'"), rebase_path_amend());
- if (get_oid_hex(rev.buf, &to_amend))
- return error(_("invalid contents: '%s'"),
- rebase_path_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 "
- "--continue' again."));
+ if (repo_get_oid(r, "HEAD", &head)) {
+ ret = error(_("cannot amend non-existing commit"));
+ goto out;
+ }
+
+ if (!read_oneliner(&rev, rebase_path_amend(), 0)) {
+ ret = error(_("invalid file: '%s'"), rebase_path_amend());
+ goto out;
+ }
+
+ if (get_oid_hex(rev.buf, &to_amend)) {
+ ret = error(_("invalid contents: '%s'"),
+ rebase_path_amend());
+ goto out;
+ }
+ if (!is_clean && !oideq(&head, &to_amend)) {
+ ret = error(_("\nYou have uncommitted changes in your "
+ "working tree. Please, commit them\n"
+ "first and then run 'git rebase "
+ "--continue' again."));
+ goto out;
+ }
/*
* When skipping a failed fixup/squash, we need to edit the
* commit message, the current fixup list and count, and if it
@@ -5013,7 +5259,7 @@ static int commit_staged_changes(struct repository *r,
* the commit message and if there was a squash, let the user
* edit it.
*/
- if (!is_clean || !opts->current_fixup_count)
+ if (!is_clean || !ctx->current_fixup_count)
; /* this is not the final fixup */
else if (!oideq(&head, &to_amend) ||
!file_exists(rebase_path_stopped_sha())) {
@@ -5022,24 +5268,26 @@ static int commit_staged_changes(struct repository *r,
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;
+ strbuf_reset(&ctx->current_fixups);
+ ctx->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;
+ const char *p = ctx->current_fixups.buf;
+ int len = ctx->current_fixups.len;
- opts->current_fixup_count--;
+ ctx->current_fixup_count--;
if (!len)
BUG("Incorrect current_fixups:\n%s", p);
while (len && p[len - 1] != '\n')
len--;
- strbuf_setlen(&opts->current_fixups, len);
+ strbuf_setlen(&ctx->current_fixups, len);
if (write_message(p, len, rebase_path_current_fixups(),
- 0) < 0)
- return error(_("could not write file: '%s'"),
- rebase_path_current_fixups());
+ 0) < 0) {
+ ret = error(_("could not write file: '%s'"),
+ rebase_path_current_fixups());
+ goto out;
+ }
/*
* If a fixup/squash in a fixup/squash chain failed, the
@@ -5052,7 +5300,7 @@ static int commit_staged_changes(struct repository *r,
* actually need to re-commit with a cleaned up commit
* message.
*/
- if (opts->current_fixup_count > 0 &&
+ if (ctx->current_fixup_count > 0 &&
!is_fixup(peek_command(todo_list, 0))) {
final_fixup = 1;
/*
@@ -5069,35 +5317,38 @@ static int commit_staged_changes(struct repository *r,
* We need to update the squash message to skip
* the latest commit message.
*/
- int res = 0;
struct commit *commit;
const char *msg;
const char *path = rebase_path_squash_msg();
const char *encoding = get_commit_output_encoding();
- if (parse_head(r, &commit))
- return error(_("could not parse HEAD"));
+ if (parse_head(r, &commit)) {
+ ret = error(_("could not parse HEAD"));
+ goto out;
+ }
p = repo_logmsg_reencode(r, commit, NULL, encoding);
if (!p) {
- res = error(_("could not parse commit %s"),
+ ret = error(_("could not parse commit %s"),
oid_to_hex(&commit->object.oid));
goto unuse_commit_buffer;
}
find_commit_subject(p, &msg);
if (write_message(msg, strlen(msg), path, 0)) {
- res = error(_("could not write file: "
+ ret = error(_("could not write file: "
"'%s'"), path);
goto unuse_commit_buffer;
}
+
+ ret = 0;
+
unuse_commit_buffer:
repo_unuse_commit_buffer(r, commit, p);
- if (res)
- return res;
+ if (ret)
+ goto out;
}
}
- strbuf_release(&rev);
flags |= AMEND_MSG;
}
@@ -5105,39 +5356,57 @@ static int commit_staged_changes(struct repository *r,
if (refs_ref_exists(get_main_ref_store(r),
"CHERRY_PICK_HEAD") &&
refs_delete_ref(get_main_ref_store(r), "",
- "CHERRY_PICK_HEAD", NULL, 0))
- return error(_("could not remove CHERRY_PICK_HEAD"));
- if (unlink(git_path_merge_msg(r)) && errno != ENOENT)
- return error_errno(_("could not remove '%s'"),
- git_path_merge_msg(r));
- if (!final_fixup)
- return 0;
+ "CHERRY_PICK_HEAD", NULL, REF_NO_DEREF)) {
+ ret = error(_("could not remove CHERRY_PICK_HEAD"));
+ goto out;
+ }
+
+ if (unlink(git_path_merge_msg(r)) && errno != ENOENT) {
+ ret = error_errno(_("could not remove '%s'"),
+ git_path_merge_msg(r));
+ goto out;
+ }
+
+ if (!final_fixup) {
+ ret = 0;
+ goto out;
+ }
}
if (run_git_commit(final_fixup ? NULL : rebase_path_message(),
- opts, flags))
- return error(_("could not commit staged changes."));
+ opts, flags)) {
+ ret = error(_("could not commit staged changes."));
+ goto out;
+ }
+
unlink(rebase_path_amend());
unlink(git_path_merge_head(r));
- unlink(git_path_auto_merge(r));
+ refs_delete_ref(get_main_ref_store(r), "", "AUTO_MERGE",
+ NULL, REF_NO_DEREF);
if (final_fixup) {
unlink(rebase_path_fixup_msg());
unlink(rebase_path_squash_msg());
}
- if (opts->current_fixup_count > 0) {
+ if (ctx->current_fixup_count > 0) {
/*
* Whether final fixup or not, we just cleaned up the commit
* message...
*/
unlink(rebase_path_current_fixups());
- strbuf_reset(&opts->current_fixups);
- opts->current_fixup_count = 0;
+ strbuf_reset(&ctx->current_fixups);
+ ctx->current_fixup_count = 0;
}
- return 0;
+
+ ret = 0;
+
+out:
+ strbuf_release(&rev);
+ return ret;
}
int sequencer_continue(struct repository *r, struct replay_opts *opts)
{
+ struct replay_ctx *ctx = opts->ctx;
struct todo_list todo_list = TODO_LIST_INIT;
int res;
@@ -5151,13 +5420,14 @@ int sequencer_continue(struct repository *r, struct replay_opts *opts)
goto release_todo_list;
if (file_exists(rebase_path_dropped())) {
- if ((res = todo_list_check_against_backup(r, &todo_list)))
+ if ((res = todo_list_check_against_backup(r, opts,
+ &todo_list)))
goto release_todo_list;
unlink(rebase_path_dropped());
}
- opts->reflog_message = reflog_message(opts, "continue", NULL);
+ ctx->reflog_message = reflog_message(opts, "continue", NULL);
if (commit_staged_changes(r, opts, &todo_list)) {
res = -1;
goto release_todo_list;
@@ -5209,7 +5479,7 @@ static int single_pick(struct repository *r,
TODO_PICK : TODO_REVERT;
item.commit = cmit;
- opts->reflog_message = sequencer_reflog_action(opts);
+ opts->ctx->reflog_message = sequencer_reflog_action(opts);
return do_pick_commit(r, &item, opts, 0, &check_todo);
}
@@ -5221,8 +5491,10 @@ int sequencer_pick_revisions(struct repository *r,
int i, res;
assert(opts->revs);
- if (read_and_refresh_cache(r, opts))
- return -1;
+ if (read_and_refresh_cache(r, opts)) {
+ res = -1;
+ goto out;
+ }
for (i = 0; i < opts->revs->pending.nr; i++) {
struct object_id oid;
@@ -5237,11 +5509,14 @@ int sequencer_pick_revisions(struct repository *r,
enum object_type type = oid_object_info(r,
&oid,
NULL);
- return error(_("%s: can't cherry-pick a %s"),
- name, type_name(type));
+ res = error(_("%s: can't cherry-pick a %s"),
+ name, type_name(type));
+ goto out;
}
- } else
- return error(_("%s: bad revision"), name);
+ } else {
+ res = error(_("%s: bad revision"), name);
+ goto out;
+ }
}
/*
@@ -5256,14 +5531,23 @@ int sequencer_pick_revisions(struct repository *r,
opts->revs->no_walk &&
!opts->revs->cmdline.rev->flags) {
struct commit *cmit;
- if (prepare_revision_walk(opts->revs))
- return error(_("revision walk setup failed"));
+
+ if (prepare_revision_walk(opts->revs)) {
+ res = error(_("revision walk setup failed"));
+ goto out;
+ }
+
cmit = get_revision(opts->revs);
- if (!cmit)
- return error(_("empty commit set passed"));
+ if (!cmit) {
+ res = error(_("empty commit set passed"));
+ goto out;
+ }
+
if (get_revision(opts->revs))
BUG("unexpected extra commit from walk");
- return single_pick(r, cmit, opts);
+
+ res = single_pick(r, cmit, opts);
+ goto out;
}
/*
@@ -5273,16 +5557,30 @@ int sequencer_pick_revisions(struct repository *r,
*/
if (walk_revs_populate_todo(&todo_list, opts) ||
- create_seq_dir(r) < 0)
- return -1;
- if (repo_get_oid(r, "HEAD", &oid) && (opts->action == REPLAY_REVERT))
- return error(_("can't revert as initial commit"));
- if (save_head(oid_to_hex(&oid)))
- return -1;
- if (save_opts(opts))
- return -1;
+ create_seq_dir(r) < 0) {
+ res = -1;
+ goto out;
+ }
+
+ if (repo_get_oid(r, "HEAD", &oid) && (opts->action == REPLAY_REVERT)) {
+ res = error(_("can't revert as initial commit"));
+ goto out;
+ }
+
+ if (save_head(oid_to_hex(&oid))) {
+ res = -1;
+ goto out;
+ }
+
+ if (save_opts(opts)) {
+ res = -1;
+ goto out;
+ }
+
update_abort_safety_file();
res = pick_commits(r, &todo_list, opts);
+
+out:
todo_list_release(&todo_list);
return res;
}
@@ -5521,7 +5819,7 @@ static int make_script_with_merges(struct pretty_print_context *pp,
int root_with_onto = flags & TODO_LIST_ROOT_WITH_ONTO;
int skipped_commit = 0;
struct strbuf buf = STRBUF_INIT, oneline = STRBUF_INIT;
- struct strbuf label = STRBUF_INIT;
+ struct strbuf label_from_message = STRBUF_INIT;
struct commit_list *commits = NULL, **tail = &commits, *iter;
struct commit_list *tips = NULL, **tips_tail = &tips;
struct commit *commit;
@@ -5544,6 +5842,7 @@ static int make_script_with_merges(struct pretty_print_context *pp,
oidmap_init(&state.commit2label, 0);
hashmap_init(&state.labels, labels_cmp, NULL, 0);
strbuf_init(&state.buf, 32);
+ load_branch_decorations();
if (revs->cmdline.nr && (revs->cmdline.rev[0].flags & BOTTOM)) {
struct labels_entry *onto_label_entry;
@@ -5576,7 +5875,7 @@ static int make_script_with_merges(struct pretty_print_context *pp,
if (!is_empty && (commit->object.flags & PATCHSAME)) {
if (flags & TODO_LIST_WARN_SKIPPED_CHERRY_PICKS)
warning(_("skipped previously applied commit %s"),
- short_commit_name(commit));
+ short_commit_name(the_repository, commit));
skipped_commit = 1;
continue;
}
@@ -5594,8 +5893,8 @@ static int make_script_with_merges(struct pretty_print_context *pp,
oid_to_hex(&commit->object.oid),
oneline.buf);
if (is_empty)
- strbuf_addf(&buf, " %c empty",
- comment_line_char);
+ strbuf_addf(&buf, " %s empty",
+ comment_line_str);
FLEX_ALLOC_STR(entry, string, buf.buf);
oidcpy(&entry->entry.oid, &commit->object.oid);
@@ -5604,18 +5903,18 @@ static int make_script_with_merges(struct pretty_print_context *pp,
continue;
}
- /* Create a label */
- strbuf_reset(&label);
+ /* Create a label from the commit message */
+ strbuf_reset(&label_from_message);
if (skip_prefix(oneline.buf, "Merge ", &p1) &&
(p1 = strchr(p1, '\'')) &&
(p2 = strchr(++p1, '\'')))
- strbuf_add(&label, p1, p2 - p1);
+ strbuf_add(&label_from_message, p1, p2 - p1);
else if (skip_prefix(oneline.buf, "Merge pull request ",
&p1) &&
(p1 = strstr(p1, " from ")))
- strbuf_addstr(&label, p1 + strlen(" from "));
+ strbuf_addstr(&label_from_message, p1 + strlen(" from "));
else
- strbuf_addbuf(&label, &oneline);
+ strbuf_addbuf(&label_from_message, &oneline);
strbuf_reset(&buf);
strbuf_addf(&buf, "%s -C %s",
@@ -5623,6 +5922,14 @@ static int make_script_with_merges(struct pretty_print_context *pp,
/* label the tips of merged branches */
for (; to_merge; to_merge = to_merge->next) {
+ const char *label = label_from_message.buf;
+ const struct name_decoration *decoration =
+ get_name_decoration(&to_merge->item->object);
+
+ if (decoration)
+ skip_prefix(decoration->name, "refs/heads/",
+ &label);
+
oid = &to_merge->item->object.oid;
strbuf_addch(&buf, ' ');
@@ -5635,7 +5942,7 @@ static int make_script_with_merges(struct pretty_print_context *pp,
tips_tail = &commit_list_insert(to_merge->item,
tips_tail)->next;
- strbuf_addstr(&buf, label_oid(oid, label.buf, &state));
+ strbuf_addstr(&buf, label_oid(oid, label, &state));
}
strbuf_addf(&buf, " # %s", oneline.buf);
@@ -5685,7 +5992,7 @@ static int make_script_with_merges(struct pretty_print_context *pp,
entry = oidmap_get(&state.commit2label, &commit->object.oid);
if (entry)
- strbuf_addf(out, "\n%c Branch %s\n", comment_line_char, entry->string);
+ strbuf_addf(out, "\n%s Branch %s\n", comment_line_str, entry->string);
else
strbuf_addch(out, '\n');
@@ -5743,10 +6050,13 @@ static int make_script_with_merges(struct pretty_print_context *pp,
free_commit_list(commits);
free_commit_list(tips);
- strbuf_release(&label);
+ strbuf_release(&label_from_message);
strbuf_release(&oneline);
strbuf_release(&buf);
+ oidset_clear(&interesting);
+ oidset_clear(&child_seen);
+ oidset_clear(&shown);
oidmap_free(&commit2todo, 1);
oidmap_free(&state.commit2label, 1);
hashmap_clear_and_free(&state.labels, struct labels_entry, entry);
@@ -5812,7 +6122,7 @@ int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,
if (!is_empty && (commit->object.flags & PATCHSAME)) {
if (flags & TODO_LIST_WARN_SKIPPED_CHERRY_PICKS)
warning(_("skipped previously applied commit %s"),
- short_commit_name(commit));
+ short_commit_name(r, commit));
skipped_commit = 1;
continue;
}
@@ -5822,7 +6132,7 @@ int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,
oid_to_hex(&commit->object.oid));
pretty_print_commit(&pp, commit, out);
if (is_empty)
- strbuf_addf(out, " %c empty", comment_line_char);
+ strbuf_addf(out, " %s empty", comment_line_str);
strbuf_addch(out, '\n');
}
if (skipped_commit)
@@ -5904,7 +6214,8 @@ static void todo_list_add_exec_commands(struct todo_list *todo_list,
todo_list->alloc = alloc;
}
-static void todo_list_to_strbuf(struct repository *r, struct todo_list *todo_list,
+static void todo_list_to_strbuf(struct repository *r,
+ struct todo_list *todo_list,
struct strbuf *buf, int num, unsigned flags)
{
struct todo_item *item;
@@ -5933,7 +6244,7 @@ static void todo_list_to_strbuf(struct repository *r, struct todo_list *todo_lis
/* add commit id */
if (item->commit) {
const char *oid = flags & TODO_LIST_SHORTEN_IDS ?
- short_commit_name(item->commit) :
+ short_commit_name(r, item->commit) :
oid_to_hex(&item->commit->object.oid);
if (item->command == TODO_FIXUP) {
@@ -6042,10 +6353,11 @@ static int add_decorations_to_list(const struct commit *commit,
struct todo_add_branch_context *ctx)
{
const struct name_decoration *decoration = get_name_decoration(&commit->object);
- const char *head_ref = resolve_ref_unsafe("HEAD",
- RESOLVE_REF_READING,
- NULL,
- NULL);
+ const char *head_ref = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+ "HEAD",
+ RESOLVE_REF_READING,
+ NULL,
+ NULL);
while (decoration) {
struct todo_item *item;
@@ -6100,14 +6412,6 @@ static int add_decorations_to_list(const struct commit *commit,
static int todo_list_add_update_ref_commands(struct todo_list *todo_list)
{
int i, res;
- static struct string_list decorate_refs_exclude = STRING_LIST_INIT_NODUP;
- static struct string_list decorate_refs_exclude_config = STRING_LIST_INIT_NODUP;
- static struct string_list decorate_refs_include = STRING_LIST_INIT_NODUP;
- struct decoration_filter decoration_filter = {
- .include_ref_pattern = &decorate_refs_include,
- .exclude_ref_pattern = &decorate_refs_exclude,
- .exclude_ref_config_pattern = &decorate_refs_exclude_config,
- };
struct todo_add_branch_context ctx = {
.buf = &todo_list->buf,
.refs_to_oids = STRING_LIST_INIT_DUP,
@@ -6116,8 +6420,7 @@ static int todo_list_add_update_ref_commands(struct todo_list *todo_list)
ctx.items_alloc = 2 * todo_list->nr + 1;
ALLOC_ARRAY(ctx.items, ctx.items_alloc);
- string_list_append(&decorate_refs_include, "refs/heads/");
- load_ref_decorations(&decoration_filter, 0);
+ load_branch_decorations();
for (i = 0; i < todo_list->nr; ) {
struct todo_item *item = &todo_list->items[i];
@@ -6193,7 +6496,7 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla
return error(_("nothing to do"));
}
- res = edit_todo_list(r, todo_list, &new_todo, shortrevisions,
+ res = edit_todo_list(r, opts, todo_list, &new_todo, shortrevisions,
shortonto, flags);
if (res == -1)
return -1;
@@ -6221,7 +6524,7 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla
strbuf_release(&buf2);
/* Nothing is done yet, and we're reparsing, so let's reset the count */
new_todo.total_nr = 0;
- if (todo_list_parse_insn_buffer(r, new_todo.buf.buf, &new_todo) < 0)
+ if (todo_list_parse_insn_buffer(r, opts, new_todo.buf.buf, &new_todo) < 0)
BUG("invalid todo list after expanding IDs:\n%s",
new_todo.buf.buf);
@@ -6292,7 +6595,7 @@ static int skip_fixupish(const char *subject, const char **p) {
int todo_list_rearrange_squash(struct todo_list *todo_list)
{
struct hashmap subject2item;
- int rearranged = 0, *next, *tail, i, nr = 0, alloc = 0;
+ int rearranged = 0, *next, *tail, i, nr = 0;
char **subjects;
struct commit_todo_item commit_todo;
struct todo_item *items = NULL;
@@ -6404,6 +6707,8 @@ int todo_list_rearrange_squash(struct todo_list *todo_list)
}
if (rearranged) {
+ ALLOC_ARRAY(items, todo_list->nr);
+
for (i = 0; i < todo_list->nr; i++) {
enum todo_command command = todo_list->items[i].command;
int cur = i;
@@ -6416,16 +6721,15 @@ int todo_list_rearrange_squash(struct todo_list *todo_list)
continue;
while (cur >= 0) {
- ALLOC_GROW(items, nr + 1, alloc);
items[nr++] = todo_list->items[cur];
cur = next[cur];
}
}
+ assert(nr == todo_list->nr);
+ todo_list->alloc = nr;
FREE_AND_NULL(todo_list->items);
todo_list->items = items;
- todo_list->nr = nr;
- todo_list->alloc = alloc;
}
free(next);