Merge branch 'en/rebase-backend'

"git rebase" has learned to use the merge backend (i.e. the
machinery that drives "rebase -i") by default, while allowing
"--apply" option to use the "apply" backend (e.g. the moral
equivalent of "format-patch piped to am").  The rebase.backend
configuration variable can be set to customize.

* en/rebase-backend:
  rebase: rename the two primary rebase backends
  rebase: change the default backend from "am" to "merge"
  rebase: make the backend configurable via config setting
  rebase tests: repeat some tests using the merge backend instead of am
  rebase tests: mark tests specific to the am-backend with --am
  rebase: drop '-i' from the reflog for interactive-based rebases
  git-prompt: change the prompt for interactive-based rebases
  rebase: add an --am option
  rebase: move incompatibility checks between backend options a bit earlier
  git-rebase.txt: add more details about behavioral differences of backends
  rebase: allow more types of rebases to fast-forward
  t3432: make these tests work with either am or merge backends
  rebase: fix handling of restrict_revision
  rebase: make sure to pass along the quiet flag to the sequencer
  rebase, sequencer: remove the broken GIT_QUIET handling
  t3406: simplify an already simple test
  rebase (interactive-backend): fix handling of commits that become empty
  rebase (interactive-backend): make --keep-empty the default
  t3404: directly test the behavior of interest
  git-rebase.txt: update description of --allow-empty-message
This commit is contained in:
Junio C Hamano
2020-03-02 15:07:18 -08:00
25 changed files with 702 additions and 286 deletions

View File

@ -160,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)
{
@ -290,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;
}
@ -324,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);
}
@ -628,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)));
@ -1485,23 +1487,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 both.
* (3) we allow ones that were initially empty, but
* halt for the ones that become empty;
*
* (4) we allow both.
*/
if (!opts->allow_empty)
return 0; /* let "git commit" barf as necessary */
@ -1515,13 +1524,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 {
@ -1732,7 +1743,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) {
/*
@ -1937,13 +1948,20 @@ 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;
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);
else
@ -2498,6 +2516,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);
@ -2549,8 +2573,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)
@ -2559,8 +2581,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)
@ -2577,6 +2599,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", "");
@ -3176,7 +3202,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) {
@ -4563,7 +4589,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;
@ -4626,8 +4651,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);
@ -4794,7 +4817,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;
@ -4830,12 +4852,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);
@ -4972,7 +4992,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);