summaryrefslogtreecommitdiff
path: root/sequencer.c
diff options
context:
space:
mode:
Diffstat (limited to 'sequencer.c')
-rw-r--r--sequencer.c228
1 files changed, 127 insertions, 101 deletions
diff --git a/sequencer.c b/sequencer.c
index b9dbf1a..6fd2674 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -40,7 +40,7 @@ static const char cherry_picked_prefix[] = "(cherry picked from commit ";
GIT_PATH_FUNC(git_path_commit_editmsg, "COMMIT_EDITMSG")
-GIT_PATH_FUNC(git_path_seq_dir, "sequencer")
+static GIT_PATH_FUNC(git_path_seq_dir, "sequencer")
static GIT_PATH_FUNC(git_path_todo_file, "sequencer/todo")
static GIT_PATH_FUNC(git_path_opts_file, "sequencer/opts")
@@ -57,6 +57,8 @@ static GIT_PATH_FUNC(rebase_path, "rebase-merge")
GIT_PATH_FUNC(rebase_path_todo, "rebase-merge/git-rebase-todo")
GIT_PATH_FUNC(rebase_path_todo_backup, "rebase-merge/git-rebase-todo.backup")
+GIT_PATH_FUNC(rebase_path_dropped, "rebase-merge/dropped")
+
/*
* The rebase command lines that have already been processed. A line
* is moved here when it is first handled, before any associated user
@@ -158,6 +160,8 @@ static GIT_PATH_FUNC(rebase_path_strategy, "rebase-merge/strategy")
static GIT_PATH_FUNC(rebase_path_strategy_opts, "rebase-merge/strategy_opts")
static GIT_PATH_FUNC(rebase_path_allow_rerere_autoupdate, "rebase-merge/allow_rerere_autoupdate")
static GIT_PATH_FUNC(rebase_path_reschedule_failed_exec, "rebase-merge/reschedule-failed-exec")
+static 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")
static int git_sequencer_config(const char *k, const char *v, void *cb)
{
@@ -288,7 +292,7 @@ int sequencer_remove_state(struct replay_opts *opts)
char *eol = strchr(p, '\n');
if (eol)
*eol = '\0';
- if (delete_ref("(rebase -i) cleanup", p, NULL, 0) < 0) {
+ if (delete_ref("(rebase) cleanup", p, NULL, 0) < 0) {
warning(_("could not delete '%s'"), p);
ret = -1;
}
@@ -322,7 +326,7 @@ static const char *action_name(const struct replay_opts *opts)
case REPLAY_PICK:
return N_("cherry-pick");
case REPLAY_INTERACTIVE_REBASE:
- return N_("rebase -i");
+ return N_("rebase");
}
die(_("unknown action: %d"), opts->action);
}
@@ -588,7 +592,7 @@ static int do_recursive_merge(struct repository *r,
struct merge_options o;
struct tree *next_tree, *base_tree, *head_tree;
int clean;
- char **xopt;
+ int i;
struct lock_file index_lock = LOCK_INIT;
if (repo_hold_locked_index(r, &index_lock, LOCK_REPORT_ON_ERROR) < 0)
@@ -608,8 +612,8 @@ static int do_recursive_merge(struct repository *r,
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);
+ for (i = 0; i < opts->xopts_nr; i++)
+ parse_merge_opt(&o, opts->xopts[i]);
clean = merge_trees(&o,
head_tree,
@@ -626,7 +630,7 @@ static int do_recursive_merge(struct repository *r,
COMMIT_LOCK | SKIP_IF_UNCHANGED))
/*
* TRANSLATORS: %s will be "revert", "cherry-pick" or
- * "rebase -i".
+ * "rebase".
*/
return error(_("%s: Unable to write new index file"),
_(action_name(opts)));
@@ -1319,7 +1323,7 @@ static int try_to_commit(struct repository *r,
return -1;
if (flags & AMEND_MSG) {
- const char *exclude_gpgsig[] = { "gpgsig", NULL };
+ const char *exclude_gpgsig[] = { "gpgsig", "gpgsig-sha256", NULL };
const char *out_enc = get_commit_output_encoding();
const char *message = logmsg_reencode(current_head, NULL,
out_enc);
@@ -1429,9 +1433,19 @@ out:
return res;
}
+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))
+ return error(_("could not update %s"), "REBASE_HEAD");
+
+ return 0;
+}
+
static int do_commit(struct repository *r,
const char *msg_file, const char *author,
- struct replay_opts *opts, unsigned int flags)
+ struct replay_opts *opts, unsigned int flags,
+ struct object_id *oid)
{
int res = 1;
@@ -1456,8 +1470,12 @@ static int do_commit(struct repository *r,
return res;
}
}
- if (res == 1)
+ if (res == 1) {
+ if (is_rebase_i(opts) && oid)
+ if (write_rebase_head(oid))
+ return -1;
return run_git_commit(r, msg_file, opts, flags);
+ }
return res;
}
@@ -1483,23 +1501,30 @@ static int is_original_commit_empty(struct commit *commit)
}
/*
- * Do we run "git commit" with "--allow-empty"?
+ * Should empty commits be allowed? Return status:
+ * <0: Error in is_index_unchanged(r) or is_original_commit_empty(commit)
+ * 0: Halt on empty commit
+ * 1: Allow empty commit
+ * 2: Drop empty commit
*/
static int allow_empty(struct repository *r,
struct replay_opts *opts,
struct commit *commit)
{
- int index_unchanged, empty_commit;
+ int index_unchanged, originally_empty;
/*
- * Three cases:
+ * Four cases:
*
* (1) we do not allow empty at all and error out.
*
- * (2) we allow ones that were initially empty, but
- * forbid the ones that become empty;
+ * (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;
*
- * (3) we allow both.
+ * (4) we allow both.
*/
if (!opts->allow_empty)
return 0; /* let "git commit" barf as necessary */
@@ -1513,13 +1538,15 @@ static int allow_empty(struct repository *r,
if (opts->keep_redundant_commits)
return 1;
- empty_commit = is_original_commit_empty(commit);
- if (empty_commit < 0)
- return empty_commit;
- if (!empty_commit)
- return 0;
- else
+ originally_empty = is_original_commit_empty(commit);
+ if (originally_empty < 0)
+ return originally_empty;
+ if (originally_empty)
return 1;
+ else if (opts->drop_redundant_commits)
+ return 2;
+ else
+ return 0;
}
static struct {
@@ -1730,7 +1757,7 @@ static int do_pick_commit(struct repository *r,
char *author = NULL;
struct commit_message msg = { NULL, NULL, NULL, NULL };
struct strbuf msgbuf = STRBUF_INIT;
- int res, unborn = 0, reword = 0, allow;
+ int res, unborn = 0, reword = 0, allow, drop_commit;
if (opts->no_commit) {
/*
@@ -1916,7 +1943,9 @@ static int do_pick_commit(struct repository *r,
* However, if the merge did not even start, then we don't want to
* write it at all.
*/
- if (command == TODO_PICK && !opts->no_commit && (res == 0 || res == 1) &&
+ 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))
res = -1;
@@ -1935,15 +1964,25 @@ static int do_pick_commit(struct repository *r,
goto leave;
}
+ drop_commit = 0;
allow = allow_empty(r, opts, commit);
if (allow < 0) {
res = allow;
goto leave;
- } else if (allow)
+ } else if (allow == 1) {
flags |= ALLOW_EMPTY;
- if (!opts->no_commit) {
+ } else if (allow == 2) {
+ drop_commit = 1;
+ unlink(git_path_cherry_pick_head(r));
+ unlink(git_path_merge_msg(r));
+ fprintf(stderr,
+ _("dropping %s %s -- patch contents already upstream\n"),
+ oid_to_hex(&commit->object.oid), msg.subject);
+ } /* else allow == 0 and there's nothing special to do */
+ if (!opts->no_commit && !drop_commit) {
if (author || command == TODO_REVERT || (flags & AMEND_MSG))
- res = do_commit(r, msg_file, author, opts, flags);
+ res = do_commit(r, msg_file, author, opts, flags,
+ commit? &commit->object.oid : NULL);
else
res = error(_("unable to parse commit author"));
*check_todo = !!(flags & EDIT_MSG);
@@ -2118,6 +2157,8 @@ static int parse_insn_line(struct repository *r, struct todo_item *item,
saved = *end_of_object_name;
*end_of_object_name = '\0';
status = get_oid(bol, &commit_oid);
+ if (status < 0)
+ error(_("could not parse '%s'"), bol); /* return later */
*end_of_object_name = saved;
bol = end_of_object_name + strspn(end_of_object_name, " \t");
@@ -2125,11 +2166,10 @@ static int parse_insn_line(struct repository *r, struct todo_item *item,
item->arg_len = (int)(eol - bol);
if (status < 0)
- return error(_("could not parse '%.*s'"),
- (int)(end_of_object_name - bol), bol);
+ return status;
item->commit = lookup_commit_reference(r, &commit_oid);
- return !item->commit;
+ return item->commit ? 0 : -1;
}
int sequencer_get_last_command(struct repository *r, enum replay_action *action)
@@ -2495,6 +2535,12 @@ static int read_populate_opts(struct replay_opts *opts)
if (file_exists(rebase_path_reschedule_failed_exec()))
opts->reschedule_failed_exec = 1;
+ if (file_exists(rebase_path_drop_redundant_commits()))
+ opts->drop_redundant_commits = 1;
+
+ if (file_exists(rebase_path_keep_redundant_commits()))
+ opts->keep_redundant_commits = 1;
+
read_strategy_opts(opts, &buf);
strbuf_release(&buf);
@@ -2546,8 +2592,6 @@ static void write_strategy_opts(struct replay_opts *opts)
int write_basic_state(struct replay_opts *opts, const char *head_name,
struct commit *onto, const char *orig_head)
{
- const char *quiet = getenv("GIT_QUIET");
-
if (head_name)
write_file(rebase_path_head_name(), "%s\n", head_name);
if (onto)
@@ -2556,8 +2600,8 @@ int write_basic_state(struct replay_opts *opts, const char *head_name,
if (orig_head)
write_file(rebase_path_orig_head(), "%s\n", orig_head);
- if (quiet)
- write_file(rebase_path_quiet(), "%s\n", quiet);
+ if (opts->quiet)
+ write_file(rebase_path_quiet(), "%s", "");
if (opts->verbose)
write_file(rebase_path_verbose(), "%s", "");
if (opts->strategy)
@@ -2574,6 +2618,10 @@ int write_basic_state(struct replay_opts *opts, const char *head_name,
write_file(rebase_path_gpg_sign_opt(), "-S%s\n", opts->gpg_sign);
if (opts->signoff)
write_file(rebase_path_signoff(), "--signoff\n");
+ if (opts->drop_redundant_commits)
+ write_file(rebase_path_drop_redundant_commits(), "%s", "");
+ if (opts->keep_redundant_commits)
+ write_file(rebase_path_keep_redundant_commits(), "%s", "");
if (opts->reschedule_failed_exec)
write_file(rebase_path_reschedule_failed_exec(), "%s", "");
@@ -2969,9 +3017,7 @@ static int make_patch(struct repository *r,
p = short_commit_name(commit);
if (write_message(p, strlen(p), rebase_path_stopped_sha(), 1) < 0)
return -1;
- if (update_ref("rebase", "REBASE_HEAD", &commit->object.oid,
- NULL, REF_NO_DEREF, UPDATE_REFS_MSG_ON_ERR))
- res |= error(_("could not update %s"), "REBASE_HEAD");
+ res |= write_rebase_head(&commit->object.oid);
strbuf_addf(&buf, "%s/patch", get_dir(opts));
memset(&log_tree_opt, 0, sizeof(log_tree_opt));
@@ -3173,7 +3219,7 @@ static int do_label(struct repository *r, const char *name, int len)
return error(_("illegal label name: '%.*s'"), len, name);
strbuf_addf(&ref_name, "refs/rewritten/%.*s", len, name);
- strbuf_addf(&msg, "rebase -i (label) '%.*s'", len, name);
+ strbuf_addf(&msg, "rebase (label) '%.*s'", len, name);
transaction = ref_store_transaction_begin(refs, &err);
if (!transaction) {
@@ -3259,6 +3305,7 @@ static int do_reset(struct repository *r,
unpack_tree_opts.fn = oneway_merge;
unpack_tree_opts.merge = 1;
unpack_tree_opts.update = 1;
+ init_checkout_metadata(&unpack_tree_opts.meta, name, &oid, NULL);
if (repo_read_index_unmerged(r)) {
rollback_lock_file(&lock);
@@ -3715,20 +3762,6 @@ static int run_git_checkout(struct repository *r, struct replay_opts *opts,
return ret;
}
-int prepare_branch_to_be_rebased(struct repository *r, struct replay_opts *opts,
- const char *commit)
-{
- const char *action;
-
- if (commit && *commit) {
- action = reflog_message(opts, "start", "checkout %s", commit);
- if (run_git_checkout(r, opts, commit, action))
- return error(_("could not checkout %s"), commit);
- }
-
- return 0;
-}
-
static int checkout_onto(struct repository *r, struct replay_opts *opts,
const char *onto_name, const struct object_id *onto,
const char *orig_head)
@@ -4238,6 +4271,14 @@ int sequencer_continue(struct repository *r, struct replay_opts *opts)
if (is_rebase_i(opts)) {
if ((res = read_populate_todo(r, &todo_list, opts)))
goto release_todo_list;
+
+ if (file_exists(rebase_path_dropped())) {
+ if ((res = todo_list_check_against_backup(r, &todo_list)))
+ goto release_todo_list;
+
+ unlink(rebase_path_dropped());
+ }
+
if (commit_staged_changes(r, opts, &todo_list)) {
res = -1;
goto release_todo_list;
@@ -4566,7 +4607,6 @@ static int make_script_with_merges(struct pretty_print_context *pp,
struct rev_info *revs, struct strbuf *out,
unsigned flags)
{
- int keep_empty = flags & TODO_LIST_KEEP_EMPTY;
int rebase_cousins = flags & TODO_LIST_REBASE_COUSINS;
int root_with_onto = flags & TODO_LIST_ROOT_WITH_ONTO;
struct strbuf buf = STRBUF_INIT, oneline = STRBUF_INIT;
@@ -4629,8 +4669,6 @@ static int make_script_with_merges(struct pretty_print_context *pp,
if (!to_merge) {
/* non-merge commit: easy case */
strbuf_reset(&buf);
- if (!keep_empty && is_empty)
- strbuf_addf(&buf, "%c ", comment_line_char);
strbuf_addf(&buf, "%s %s %s", cmd_pick,
oid_to_hex(&commit->object.oid),
oneline.buf);
@@ -4797,7 +4835,6 @@ int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,
struct pretty_print_context pp = {0};
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;
@@ -4833,12 +4870,10 @@ int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,
return make_script_with_merges(&pp, &revs, out, flags);
while ((commit = get_revision(&revs))) {
- int is_empty = is_original_commit_empty(commit);
+ int is_empty = is_original_commit_empty(commit);
if (!is_empty && (commit->object.flags & PATCHSAME))
continue;
- if (!keep_empty && is_empty)
- strbuf_addf(out, "%c ", comment_line_char);
strbuf_addf(out, "%s %s ", insn,
oid_to_hex(&commit->object.oid));
pretty_print_commit(&pp, commit, out);
@@ -4975,7 +5010,7 @@ int todo_list_write_to_file(struct repository *r, struct todo_list *todo_list,
todo_list_to_strbuf(r, todo_list, &buf, num, flags);
if (flags & TODO_LIST_APPEND_TODO_HELP)
- append_todo_help(flags & TODO_LIST_KEEP_EMPTY, count_commands(todo_list),
+ append_todo_help(count_commands(todo_list),
shortrevisions, shortonto, &buf);
res = write_message(buf.buf, buf.len, file, 0);
@@ -4984,41 +5019,6 @@ int todo_list_write_to_file(struct repository *r, struct todo_list *todo_list,
return res;
}
-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");
-
-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;
-
- if (strbuf_read_file_or_whine(&new_todo.buf, rebase_path_todo()) < 0) {
- res = -1;
- goto out;
- }
-
- if (strbuf_read_file_or_whine(&old_todo.buf, rebase_path_todo_backup()) < 0) {
- res = -1;
- goto out;
- }
-
- 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;
-}
-
/* skip picking commits whose parents are unchanged */
static int skip_unnecessary_picks(struct repository *r,
struct todo_list *todo_list,
@@ -5075,7 +5075,7 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla
{
const char *shortonto, *todo_file = rebase_path_todo();
struct todo_list new_todo = TODO_LIST_INIT;
- struct strbuf *buf = &todo_list->buf;
+ struct strbuf *buf = &todo_list->buf, buf2 = STRBUF_INIT;
struct object_id oid = onto->object.oid;
int res;
@@ -5116,17 +5116,22 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla
todo_list_release(&new_todo);
return error(_("nothing to do"));
- }
-
- 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));
+ } else if (res == -4) {
checkout_onto(r, opts, onto_name, &onto->object.oid, orig_head);
todo_list_release(&new_todo);
return -1;
}
+ /* Expand the commit IDs */
+ todo_list_to_strbuf(r, &new_todo, &buf2, -1, 0);
+ strbuf_swap(&new_todo.buf, &buf2);
+ strbuf_release(&buf2);
+ new_todo.total_nr -= new_todo.nr;
+ if (todo_list_parse_insn_buffer(r, new_todo.buf.buf, &new_todo) < 0)
+ BUG("invalid todo list after expanding IDs:\n%s",
+ new_todo.buf.buf);
+
if (opts->allow_ff && skip_unnecessary_picks(r, &new_todo, &oid)) {
todo_list_release(&new_todo);
return error(_("could not skip unnecessary pick commands"));
@@ -5326,3 +5331,24 @@ int todo_list_rearrange_squash(struct todo_list *todo_list)
return 0;
}
+
+int sequencer_determine_whence(struct repository *r, enum commit_whence *whence)
+{
+ if (file_exists(git_path_cherry_pick_head(r))) {
+ struct object_id cherry_pick_head, rebase_head;
+
+ if (file_exists(git_path_seq_dir()))
+ *whence = FROM_CHERRY_PICK_MULTI;
+ if (file_exists(rebase_path()) &&
+ !get_oid("REBASE_HEAD", &rebase_head) &&
+ !get_oid("CHERRY_PICK_HEAD", &cherry_pick_head) &&
+ oideq(&rebase_head, &cherry_pick_head))
+ *whence = FROM_REBASE_PICK;
+ else
+ *whence = FROM_CHERRY_PICK_SINGLE;
+
+ return 1;
+ }
+
+ return 0;
+}