* [PATCH 00/15] rebase: make the default backend configurable
@ 2019-12-20 17:09 Elijah Newren via GitGitGadget
2019-12-20 17:09 ` [PATCH 01/15] rebase: extend the options for handling of empty commits Elijah Newren via GitGitGadget
` (16 more replies)
0 siblings, 17 replies; 161+ messages in thread
From: Elijah Newren via GitGitGadget @ 2019-12-20 17:09 UTC (permalink / raw)
To: git
Cc: Johannes.Schindelin, phillip.wood, liu.denton, gitster, plroskin,
Junio C Hamano
This series does a lot of work around making the default rebase backend
configurable:
* provide configurability of handling empty commits and iron out
differences between backends
* increase/fix capabilities of the merge/interactive backend to make it
workable as the default
* document the remaining differences in backends more thoroughly
* add an --am option for explicitly requesting the am-backend
* extend merge/interactive backend testing to put it on par with the am
backend
* add a 'rebase.backend' config option and:
* switch the rebase.backend default value from 'am' to 'merge'.
Areas I'd like reviewers to focus, in priority order:
* Patch 15 (Are we ready to switch the default backend? Or should we leave
this patch out for now? Junio suggested we may be ready or at least are
close[1])
* Patch 1 (Does my empty handling make sense? Do others agree I fixed
--keep-empty, or do they view it as breaking it?)
* Patch 8 (Do the updates to the documentation of behavioral differences
make sense? Is it too long?) * Patch 11 (okay to change the
git-completion shell prompt slightly, especially in light of patches
15 & 16? We did a prompt change previously when we merged the merge
backend with the interactive one, so I assume so, but just want to
make sure people have a chance to chime in.)
If it's too soon to switch from the 'am' to 'merge' backend, we can just
drop the last patch and I'll resubmit it later. I generated this series
mostly through switching the default first and then watching what broke, but
moved the patch to the end to make it easy to drop.
Briefly, reasons for switching the default backend boil down to the fact
that the am-backend drops information and thus limits what it can do. This
manifests in different ways:
* lack of tree information that would allow us to warn users that new files
in old directories might want to move along with the other files that
were renamed with those directories[1]
* incorrect application of patches in the presence of non-unique context
lines[2], which could be avoided with access to the original files
involved.
* less information available to annotate conflict markers (since am creates
fake ancestors and commits on top of them, and doesn't have access to the
original commits)
[1] https://lore.kernel.org/git/xmqqa78d2qmk.fsf@gitster-ct.c.googlers.com/
[2] https://lore.kernel.org/git/xmqqh8jeh1id.fsf@gitster-ct.c.googlers.com/
[3]
https://lore.kernel.org/git/CABPp-BGiu2nVMQY_t-rnFR5GQUz_ipyEE8oDocKeO+h+t4Mn4A@mail.gmail.com/
Elijah Newren (15):
rebase: extend the options for handling of empty commits
t3406: simplify an already simple test
rebase, sequencer: remove the broken GIT_QUIET handling
rebase: make sure to pass along the quiet flag to the sequencer
rebase: fix handling of restrict_revision
t3432: make these tests work with either am or merge backends
rebase: allow more types of rebases to fast-forward
git-rebase.txt: add more details about behavioral differences of
backends
rebase: move incompatibility checks between backend options a bit
earlier
rebase: add an --am option
contrib: change the prompt for am-based rebases
rebase tests: mark tests specific to the am-backend with --am
rebase tests: repeat some tests using the merge backend instead of am
rebase: make the backend configurable via config setting
rebase: change the default backend from "am" to "merge"
Documentation/config/rebase.txt | 8 ++
Documentation/git-rebase.txt | 150 ++++++++++++++++----
builtin/rebase.c | 181 +++++++++++++++++++-----
contrib/completion/git-prompt.sh | 2 +-
rebase-interactive.c | 4 +-
rebase-interactive.h | 2 +-
sequencer.c | 80 ++++++++---
sequencer.h | 6 +-
t/t3400-rebase.sh | 36 ++++-
t/t3401-rebase-and-am-rename.sh | 4 +-
t/t3404-rebase-interactive.sh | 2 +-
t/t3406-rebase-message.sh | 19 ++-
t/t3407-rebase-abort.sh | 6 +-
t/t3420-rebase-autostash.sh | 2 +-
t/t3421-rebase-topology-linear.sh | 4 +-
t/t3424-rebase-empty.sh | 89 ++++++++++++
t/t3425-rebase-topology-merges.sh | 8 +-
t/t3427-rebase-subtree.sh | 16 ++-
t/t3432-rebase-fast-forward.sh | 59 ++++----
t/t3433-rebase-options-compatibility.sh | 13 +-
t/t5407-post-rewrite-hook.sh | 12 +-
t/t5520-pull.sh | 27 +++-
t/t6047-diff3-conflict-markers.sh | 13 +-
t/t7512-status-help.sh | 12 +-
t/t9106-git-svn-commit-diff-clobber.sh | 3 +-
t/t9903-bash-prompt.sh | 6 +-
26 files changed, 582 insertions(+), 182 deletions(-)
create mode 100755 t/t3424-rebase-empty.sh
base-commit: 12029dc57db23baef008e77db1909367599210ee
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-679%2Fnewren%2Frebase-fixes-v1
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-679/newren/rebase-fixes-v1
Pull-Request: https://github.com/git/git/pull/679
--
gitgitgadget
^ permalink raw reply [flat|nested] 161+ messages in thread
* [PATCH 01/15] rebase: extend the options for handling of empty commits
2019-12-20 17:09 [PATCH 00/15] rebase: make the default backend configurable Elijah Newren via GitGitGadget
@ 2019-12-20 17:09 ` Elijah Newren via GitGitGadget
2019-12-20 21:29 ` Junio C Hamano
2019-12-20 17:09 ` [PATCH 02/15] t3406: simplify an already simple test Elijah Newren via GitGitGadget
` (15 subsequent siblings)
16 siblings, 1 reply; 161+ messages in thread
From: Elijah Newren via GitGitGadget @ 2019-12-20 17:09 UTC (permalink / raw)
To: git
Cc: Johannes.Schindelin, phillip.wood, liu.denton, gitster, plroskin,
Junio C Hamano, Elijah Newren
From: Elijah Newren <newren@gmail.com>
Extend the interactive machinery with the ability to handle the full
spread of options for how to handle commits that either start or become
empty (by "become empty" I mean the changes in a commit are a subset of
changes that exist upstream, so the net effect of applying the commit is
no changes). Introduce a new command line flag for selecting the
desired behavior:
--empty={drop,keep,ask}
with the definitions:
drop: drop empty commits
keep: keep empty commits
ask: provide the user a chance to interact and pick what to do with
empty commits on a case-by-case basis
Note that traditionally, am-based rebases have always dropped commits
that either started or became empty, while interactive-based rebases
have defaulted to ask (and provided an option to keep commits that
started empty). This difference made sense since users of an am-based
rebase just wanted to quickly batch apply a sequence of commits, while
users editing a todo list will likely want the chance to interact and
handle unusual cases on a case-by-case basis. However, not all rebases
using the interactive machinery are explicitly interactive anymore. In
particular --merge was always meant to behave more like --am: just
rebase a batch of commits without popping up a todo list.
If the --empty flag is not specified, pick defaults as follows:
explicitly interactive: ask
--exec: keep (exec is about checking existing commits, and often
used without actually changing the base. Thus the
expectation is that the user doesn't necessarily want
anything to change; they just want to test).
otherwise: drop
Also, this commit makes --keep-empty just imply --empty=keep, and hides
it from help so that we aren't confusing users with different ways to do
the same thing. (I could have added a --drop-empty flag, but then that
invites users to specify both --keep-empty and --drop-empty and we have
to add sanity checking around that; it seems cleaner to have a single
multi-valued option.) This actually fixes --keep-empty too; previously,
it only meant to sometimes keep empty commits, in particular commits
which started empty would be kept. But it would still error out and ask
the user what to do with commits that became empty. Now it keeps empty
commits, as instructed.
Signed-off-by: Elijah Newren <newren@gmail.com>
---
Documentation/git-rebase.txt | 35 ++++++------
builtin/rebase.c | 87 +++++++++++++++++++++++++++---
rebase-interactive.c | 4 +-
rebase-interactive.h | 2 +-
sequencer.c | 74 +++++++++++++++++++------
sequencer.h | 6 ++-
t/t3421-rebase-topology-linear.sh | 4 +-
t/t3424-rebase-empty.sh | 89 +++++++++++++++++++++++++++++++
t/t3427-rebase-subtree.sh | 16 +++---
9 files changed, 267 insertions(+), 50 deletions(-)
create mode 100755 t/t3424-rebase-empty.sh
diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index 1d0e2d27cc..ff32ca1080 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -258,9 +258,25 @@ See also INCOMPATIBLE OPTIONS below.
original branch. The index and working tree are also left
unchanged as a result.
+--empty={drop,keep,ask}::
+ How to handle commits that become empty (because they contain a
+ subset of already upstream changes) or start empty. With drop
+ (the default), commits that start or become empty are dropped.
+ With keep (implied by --exec), such commits are kept. With ask
+ (implied by --interactive), the rebase will halt when an empty
+ commit is applied allowing you to choose whether to drop it or
+ commit it. Also with ask, if the rebase is interactive then
+ commits which start empty will be commented out in the todo
+ action list (giving you a chance to uncomment).
++
+Note that this has no effect on commits which are already upstream (as
+can be checked via `git log --cherry-mark ...`), which are always
+dropped by rebase.
++
+See also INCOMPATIBLE OPTIONS below.
+
--keep-empty::
- Keep the commits that do not change anything from its
- parents in the result.
+ Deprecated alias for what is now known as --empty=keep.
+
See also INCOMPATIBLE OPTIONS below.
@@ -569,6 +585,7 @@ are incompatible with the following options:
* --interactive
* --exec
* --keep-empty
+ * --empty=
* --edit-todo
* --root when used in combination with --onto
@@ -580,6 +597,7 @@ In addition, the following pairs of options are incompatible:
* --preserve-merges and --ignore-whitespace
* --preserve-merges and --committer-date-is-author-date
* --preserve-merges and --ignore-date
+ * --preserve-merges and --empty=
* --keep-base and --onto
* --keep-base and --root
@@ -588,19 +606,6 @@ BEHAVIORAL DIFFERENCES
There are some subtle differences how the backends behave.
-Empty commits
-~~~~~~~~~~~~~
-
-The am backend drops any "empty" commits, regardless of whether the
-commit started empty (had no changes relative to its parent to
-start with) or ended empty (all changes were already applied
-upstream in other commits).
-
-The interactive backend drops commits by default that
-started empty and halts if it hits a commit that ended up empty.
-The `--keep-empty` option exists for the interactive backend to allow
-it to keep commits that started empty.
-
Directory rename detection
~~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/builtin/rebase.c b/builtin/rebase.c
index ddf33bc9d4..96db10eaed 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -50,8 +50,16 @@ enum rebase_type {
REBASE_PRESERVE_MERGES
};
+enum empty_type {
+ EMPTY_UNSPECIFIED = -1,
+ EMPTY_DROP,
+ EMPTY_KEEP,
+ EMPTY_ASK
+};
+
struct rebase_options {
enum rebase_type type;
+ enum empty_type empty;
const char *state_dir;
struct commit *upstream;
const char *upstream_name;
@@ -77,7 +85,6 @@ struct rebase_options {
const char *action;
int signoff;
int allow_rerere_autoupdate;
- int keep_empty;
int autosquash;
int ignore_whitespace;
char *gpg_sign_opt;
@@ -95,6 +102,7 @@ struct rebase_options {
#define REBASE_OPTIONS_INIT { \
.type = REBASE_UNSPECIFIED, \
+ .empty = EMPTY_UNSPECIFIED, \
.flags = REBASE_NO_QUIET, \
.git_am_opts = ARGV_ARRAY_INIT, \
.git_format_patch_opt = STRBUF_INIT \
@@ -114,6 +122,10 @@ static struct replay_opts get_replay_opts(const struct rebase_options *opts)
replay.allow_rerere_auto = opts->allow_rerere_autoupdate;
replay.allow_empty = 1;
replay.allow_empty_message = opts->allow_empty_message;
+ replay.drop_redundant_commits = (opts->empty == EMPTY_DROP);
+ replay.keep_redundant_commits = (opts->empty == EMPTY_KEEP);
+ replay.ask_on_initially_empty = (opts->empty == EMPTY_ASK &&
+ !(opts->flags & REBASE_INTERACTIVE_EXPLICIT));
replay.verbose = opts->flags & REBASE_VERBOSE;
replay.reschedule_failed_exec = opts->reschedule_failed_exec;
replay.committer_date_is_author_date =
@@ -389,7 +401,10 @@ static int run_rebase_interactive(struct rebase_options *opts,
git_config_get_bool("rebase.abbreviatecommands", &abbreviate_commands);
- flags |= opts->keep_empty ? TODO_LIST_KEEP_EMPTY : 0;
+ flags |= (opts->empty == EMPTY_DROP) ? TODO_LIST_DROP_EMPTY : 0;
+ flags |= (opts->empty == EMPTY_ASK &&
+ opts->flags & REBASE_INTERACTIVE_EXPLICIT) ?
+ TODO_LIST_ASK_EMPTY : 0;
flags |= abbreviate_commands ? TODO_LIST_ABBREVIATE_CMDS : 0;
flags |= opts->rebase_merges ? TODO_LIST_REBASE_MERGES : 0;
flags |= opts->rebase_cousins > 0 ? TODO_LIST_REBASE_COUSINS : 0;
@@ -453,6 +468,19 @@ static int run_rebase_interactive(struct rebase_options *opts,
return ret;
}
+static int parse_opt_keep_empty(const struct option *opt, const char *arg,
+ int unset)
+{
+ struct rebase_options *opts = opt->value;
+
+ BUG_ON_OPT_NEG(unset);
+ BUG_ON_OPT_ARG(arg);
+
+ opts->empty = EMPTY_KEEP;
+ opts->type = REBASE_INTERACTIVE;
+ return 0;
+}
+
static const char * const builtin_rebase_interactive_usage[] = {
N_("git rebase--interactive [<options>]"),
NULL
@@ -466,7 +494,10 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)
struct option options[] = {
OPT_NEGBIT(0, "ff", &opts.flags, N_("allow fast-forward"),
REBASE_FORCE),
- OPT_BOOL(0, "keep-empty", &opts.keep_empty, N_("keep empty commits")),
+ { OPTION_CALLBACK, 'k', "keep-empty", &options, NULL,
+ N_("(DEPRECATED) keep empty commits"),
+ PARSE_OPT_NOARG | PARSE_OPT_NONEG | PARSE_OPT_HIDDEN,
+ parse_opt_keep_empty },
OPT_BOOL(0, "allow-empty-message", &opts.allow_empty_message,
N_("allow commits with empty messages")),
OPT_BOOL(0, "rebase-merges", &opts.rebase_merges, N_("rebase merge commits")),
@@ -1166,7 +1197,7 @@ static int run_specific_rebase(struct rebase_options *opts, enum action action)
opts->allow_rerere_autoupdate ?
opts->allow_rerere_autoupdate == RERERE_AUTOUPDATE ?
"--rerere-autoupdate" : "--no-rerere-autoupdate" : "");
- add_var(&script_snippet, "keep_empty", opts->keep_empty ? "yes" : "");
+ add_var(&script_snippet, "empty", opts->empty == EMPTY_KEEP ? "yes" : "");
add_var(&script_snippet, "autosquash", opts->autosquash ? "t" : "");
add_var(&script_snippet, "gpg_sign_opt", opts->gpg_sign_opt);
add_var(&script_snippet, "cmd", opts->cmd);
@@ -1360,6 +1391,33 @@ static int parse_opt_interactive(const struct option *opt, const char *arg,
return 0;
}
+static long parse_empty_value(const char *value)
+{
+ if (!value)
+ return EMPTY_UNSPECIFIED;
+ else if (!strcasecmp(value, "drop"))
+ return EMPTY_DROP;
+ else if (!strcasecmp(value, "keep"))
+ return EMPTY_KEEP;
+ else if (!strcasecmp(value, "ask"))
+ return EMPTY_ASK;
+ return EMPTY_UNSPECIFIED;
+}
+
+static int parse_opt_empty(const struct option *opt, const char *arg, int unset)
+{
+ struct rebase_options *options = opt->value;
+ long value = parse_empty_value(arg);
+
+ BUG_ON_OPT_NEG(unset);
+ if (value < 0)
+ return error(_("option empty accepts \"drop\", "
+ "\"keep\", and \"ask\""));
+
+ options->empty = value;
+ return 0;
+}
+
static void NORETURN error_on_missing_default_upstream(void)
{
struct branch *current_branch = branch_get(NULL);
@@ -1505,8 +1563,13 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
"ignoring them"),
REBASE_PRESERVE_MERGES, PARSE_OPT_HIDDEN),
OPT_RERERE_AUTOUPDATE(&options.allow_rerere_autoupdate),
- OPT_BOOL('k', "keep-empty", &options.keep_empty,
- N_("preserve empty commits during rebase")),
+ OPT_CALLBACK_F(0, "empty", &options, N_("{drop,keep,ask}"),
+ N_("how to handle empty commits"),
+ PARSE_OPT_NONEG, parse_opt_empty),
+ { OPTION_CALLBACK, 'k', "keep-empty", &options, NULL,
+ N_("(DEPRECATED) keep empty commits"),
+ PARSE_OPT_NOARG | PARSE_OPT_NONEG | PARSE_OPT_HIDDEN,
+ parse_opt_keep_empty },
OPT_BOOL(0, "autosquash", &options.autosquash,
N_("move commits that begin with "
"squash!/fixup! under -i")),
@@ -1770,8 +1833,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
if (!(options.flags & REBASE_NO_QUIET))
argv_array_push(&options.git_am_opts, "-q");
- if (options.keep_empty)
- imply_interactive(&options, "--keep-empty");
+ if (options.empty != EMPTY_UNSPECIFIED)
+ imply_interactive(&options, "--empty");
if (gpg_sign) {
free(options.gpg_sign_opt);
@@ -1856,6 +1919,14 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
break;
}
+ if (options.empty == EMPTY_UNSPECIFIED) {
+ if (options.flags & REBASE_INTERACTIVE_EXPLICIT)
+ options.empty = EMPTY_ASK;
+ else if (exec.nr > 0)
+ options.empty = EMPTY_KEEP;
+ else
+ options.empty = EMPTY_DROP;
+ }
if (reschedule_failed_exec > 0 && !is_interactive(&options))
die(_("--reschedule-failed-exec requires "
"--exec or --interactive"));
diff --git a/rebase-interactive.c b/rebase-interactive.c
index aa18ae82b7..ad82bf77df 100644
--- a/rebase-interactive.c
+++ b/rebase-interactive.c
@@ -28,7 +28,7 @@ static enum missing_commit_check_level get_missing_commit_check_level(void)
return MISSING_COMMIT_CHECK_IGNORE;
}
-void append_todo_help(unsigned keep_empty, int command_count,
+void append_todo_help(unsigned no_ask_empty, int command_count,
const char *shortrevisions, const char *shortonto,
struct strbuf *buf)
{
@@ -81,7 +81,7 @@ void append_todo_help(unsigned keep_empty, int command_count,
strbuf_add_commented_lines(buf, msg, strlen(msg));
- if (!keep_empty) {
+ if (!no_ask_empty) {
msg = _("Note that empty commits are commented out");
strbuf_add_commented_lines(buf, msg, strlen(msg));
}
diff --git a/rebase-interactive.h b/rebase-interactive.h
index 44dbb06311..f531e00ba7 100644
--- a/rebase-interactive.h
+++ b/rebase-interactive.h
@@ -5,7 +5,7 @@ struct strbuf;
struct repository;
struct todo_list;
-void append_todo_help(unsigned keep_empty, int command_count,
+void append_todo_help(unsigned no_ask_empty, int command_count,
const char *shortrevisions, const char *shortonto,
struct strbuf *buf);
int edit_todo_list(struct repository *r, struct todo_list *todo_list,
diff --git a/sequencer.c b/sequencer.c
index 763ccbbc45..d2c11f34b7 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -160,6 +160,9 @@ 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 GIT_PATH_FUNC(rebase_path_ask_on_initially_empty, "rebase-merge/ask_on_initially_empty")
static int git_sequencer_config(const char *k, const char *v, void *cb)
{
@@ -1623,7 +1626,7 @@ static int allow_empty(struct repository *r,
empty_commit = is_original_commit_empty(commit);
if (empty_commit < 0)
return empty_commit;
- if (!empty_commit)
+ if (!empty_commit || opts->ask_on_initially_empty)
return 0;
else
return 1;
@@ -1837,7 +1840,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) {
/*
@@ -2042,13 +2045,20 @@ static int do_pick_commit(struct repository *r,
goto leave;
}
- allow = allow_empty(r, opts, commit);
- if (allow < 0) {
- res = allow;
- goto leave;
- } else if (allow)
- flags |= ALLOW_EMPTY;
- if (!opts->no_commit) {
+ drop_commit = 0;
+ if (opts->drop_redundant_commits && is_index_unchanged(r)) {
+ drop_commit = 1;
+ fprintf(stderr, _("No changes -- Patch already applied."));
+ } else {
+ allow = allow_empty(r, opts, commit);
+ if (allow < 0) {
+ res = allow;
+ goto leave;
+ } else if (allow) {
+ flags |= ALLOW_EMPTY;
+ }
+ }
+ if (!opts->no_commit && !drop_commit) {
if (author || command == TODO_REVERT || (flags & AMEND_MSG))
res = do_commit(r, msg_file, author, opts, flags);
else
@@ -2501,9 +2511,15 @@ static int populate_opts_cb(const char *key, const char *value, void *data)
else if (!strcmp(key, "options.allow-empty-message"))
opts->allow_empty_message =
git_config_bool_or_int(key, value, &error_flag);
+ else if (!strcmp(key, "options.drop-redundant-commits"))
+ opts->drop_redundant_commits =
+ git_config_bool_or_int(key, value, &error_flag);
else if (!strcmp(key, "options.keep-redundant-commits"))
opts->keep_redundant_commits =
git_config_bool_or_int(key, value, &error_flag);
+ else if (!strcmp(key, "options.ask_on_initially_empty"))
+ opts->ask_on_initially_empty =
+ git_config_bool_or_int(key, value, &error_flag);
else if (!strcmp(key, "options.signoff"))
opts->signoff = git_config_bool_or_int(key, value, &error_flag);
else if (!strcmp(key, "options.record-origin"))
@@ -2612,6 +2628,15 @@ 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;
+
+ if (file_exists(rebase_path_ask_on_initially_empty()))
+ opts->ask_on_initially_empty = 1;
+
read_strategy_opts(opts, &buf);
strbuf_release(&buf);
@@ -2695,6 +2720,12 @@ int write_basic_state(struct replay_opts *opts, const char *head_name,
write_file(rebase_path_cdate_is_adate(), "%s", "");
if (opts->ignore_date)
write_file(rebase_path_ignore_date(), "%s", "");
+ 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->ask_on_initially_empty)
+ write_file(rebase_path_ask_on_initially_empty(), "%s", "");
if (opts->reschedule_failed_exec)
write_file(rebase_path_reschedule_failed_exec(), "%s", "");
@@ -3033,9 +3064,15 @@ static int save_opts(struct replay_opts *opts)
if (opts->allow_empty_message)
res |= git_config_set_in_file_gently(opts_file,
"options.allow-empty-message", "true");
+ if (opts->drop_redundant_commits)
+ res |= git_config_set_in_file_gently(opts_file,
+ "options.drop-redundant-commits", "true");
if (opts->keep_redundant_commits)
res |= git_config_set_in_file_gently(opts_file,
"options.keep-redundant-commits", "true");
+ if (opts->ask_on_initially_empty)
+ res |= git_config_set_in_file_gently(opts_file,
+ "options.ask_on_initially_empty", "true");
if (opts->signoff)
res |= git_config_set_in_file_gently(opts_file,
"options.signoff", "true");
@@ -4691,7 +4728,8 @@ 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 drop_empty = flags & TODO_LIST_DROP_EMPTY;
+ int ask_empty = flags & TODO_LIST_ASK_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;
@@ -4746,6 +4784,8 @@ static int make_script_with_merges(struct pretty_print_context *pp,
is_empty = is_original_commit_empty(commit);
if (!is_empty && (commit->object.flags & PATCHSAME))
continue;
+ if (is_empty && drop_empty)
+ continue;
strbuf_reset(&oneline);
pretty_print_commit(pp, commit, &oneline);
@@ -4754,7 +4794,7 @@ 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)
+ if (is_empty && ask_empty)
strbuf_addf(&buf, "%c ", comment_line_char);
strbuf_addf(&buf, "%s %s %s", cmd_pick,
oid_to_hex(&commit->object.oid),
@@ -4922,7 +4962,8 @@ 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;
+ int drop_empty = flags & TODO_LIST_DROP_EMPTY;
+ int ask_empty = flags & TODO_LIST_ASK_EMPTY;
const char *insn = flags & TODO_LIST_ABBREVIATE_CMDS ? "p" : "pick";
int rebase_merges = flags & TODO_LIST_REBASE_MERGES;
@@ -4958,11 +4999,13 @@ 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)
+ if (is_empty && drop_empty)
+ continue;
+ if (is_empty && ask_empty)
strbuf_addf(out, "%c ", comment_line_char);
strbuf_addf(out, "%s %s ", insn,
oid_to_hex(&commit->object.oid));
@@ -5100,7 +5143,8 @@ 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(!(flags & TODO_LIST_ASK_EMPTY),
+ count_commands(todo_list),
shortrevisions, shortonto, &buf);
res = write_message(buf.buf, buf.len, file, 0);
diff --git a/sequencer.h b/sequencer.h
index e9a0e03ea2..1c3abb661c 100644
--- a/sequencer.h
+++ b/sequencer.h
@@ -39,7 +39,9 @@ struct replay_opts {
int allow_rerere_auto;
int allow_empty;
int allow_empty_message;
+ int drop_redundant_commits;
int keep_redundant_commits;
+ int ask_on_initially_empty;
int verbose;
int quiet;
int reschedule_failed_exec;
@@ -134,7 +136,7 @@ int sequencer_rollback(struct repository *repo, struct replay_opts *opts);
int sequencer_skip(struct repository *repo, struct replay_opts *opts);
int sequencer_remove_state(struct replay_opts *opts);
-#define TODO_LIST_KEEP_EMPTY (1U << 0)
+/* #define TODO_LIST_KEEP_EMPTY (1U << 0) */ /* No longer used */
#define TODO_LIST_SHORTEN_IDS (1U << 1)
#define TODO_LIST_ABBREVIATE_CMDS (1U << 2)
#define TODO_LIST_REBASE_MERGES (1U << 3)
@@ -150,6 +152,8 @@ int sequencer_remove_state(struct replay_opts *opts);
* `--onto`, we do not want to re-generate the root commits.
*/
#define TODO_LIST_ROOT_WITH_ONTO (1U << 6)
+#define TODO_LIST_DROP_EMPTY (1U << 7)
+#define TODO_LIST_ASK_EMPTY (1U << 8)
int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,
diff --git a/t/t3421-rebase-topology-linear.sh b/t/t3421-rebase-topology-linear.sh
index 325072b0a3..d23e0bf778 100755
--- a/t/t3421-rebase-topology-linear.sh
+++ b/t/t3421-rebase-topology-linear.sh
@@ -230,7 +230,7 @@ test_run_rebase () {
test_run_rebase success ''
test_run_rebase success -m
test_run_rebase success -i
-test_have_prereq !REBASE_P || test_run_rebase failure -p
+test_have_prereq !REBASE_P || test_run_rebase success -p
test_run_rebase () {
result=$1
@@ -245,7 +245,7 @@ test_run_rebase () {
test_run_rebase success ''
test_run_rebase success -m
test_run_rebase success -i
-test_have_prereq !REBASE_P || test_run_rebase failure -p
+test_have_prereq !REBASE_P || test_run_rebase success -p
test_run_rebase success --rebase-merges
# m
diff --git a/t/t3424-rebase-empty.sh b/t/t3424-rebase-empty.sh
new file mode 100755
index 0000000000..9d52e1417f
--- /dev/null
+++ b/t/t3424-rebase-empty.sh
@@ -0,0 +1,89 @@
+#!/bin/sh
+
+test_description='git rebase of commits that start or become empty'
+
+. ./test-lib.sh
+
+test_expect_success 'setup test repository' '
+ test_write_lines 1 2 3 4 5 6 7 8 9 10 >numbers &&
+ test_write_lines A B C D E F G H I J >letters &&
+ git add numbers letters &&
+ git commit -m A &&
+
+ git branch upstream &&
+ git branch localmods &&
+
+ git checkout upstream &&
+ test_write_lines A B C D E >letters &&
+ git add letters &&
+ git commit -m B &&
+
+ test_write_lines 1 2 3 4 five 6 7 8 9 ten >numbers &&
+ git add numbers &&
+ git commit -m C &&
+
+ git checkout localmods &&
+ test_write_lines 1 2 3 4 five 6 7 8 9 10 >numbers &&
+ git add numbers &&
+ git commit -m C2 &&
+
+ git commit --allow-empty -m D &&
+
+ test_write_lines A B C D E >letters &&
+ git add letters &&
+ git commit -m "Five letters ought to be enough for anybody"
+'
+
+test_expect_success 'rebase --merge --empty=drop' '
+ git checkout -B testing localmods &&
+ git rebase --merge --empty=drop upstream &&
+
+ test_write_lines C B A >expect &&
+ git log --format=%s >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'rebase --merge --empty=keep' '
+ git checkout -B testing localmods &&
+ git rebase --merge --empty=keep upstream &&
+
+ test_write_lines D C2 C B A >expect &&
+ git log --format=%s >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'rebase --merge --empty=ask' '
+ git checkout -B testing localmods &&
+ test_must_fail git rebase --merge --empty=ask upstream &&
+
+ test_must_fail git rebase --skip &&
+ git commit --allow-empty &&
+ git rebase --continue &&
+
+ test_write_lines D C B A >expect &&
+ git log --format=%s >actual &&
+ test_cmp expect actual
+'
+
+GIT_SEQUENCE_EDITOR=: && export GIT_SEQUENCE_EDITOR
+
+test_expect_success 'rebase --interactive --empty=drop' '
+ git checkout -B testing localmods &&
+ git rebase --interactive --empty=drop upstream &&
+
+ test_write_lines C B A >expect &&
+ git log --format=%s >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'rebase --interactive --empty=keep' '
+ git checkout -B testing localmods &&
+ git rebase --interactive --empty=keep upstream &&
+
+ test_write_lines D C2 C B A >expect &&
+ git log --format=%s >actual &&
+ test_cmp expect actual
+'
+
+
+test_done
diff --git a/t/t3427-rebase-subtree.sh b/t/t3427-rebase-subtree.sh
index bec48e6a1f..468ebc1bef 100755
--- a/t/t3427-rebase-subtree.sh
+++ b/t/t3427-rebase-subtree.sh
@@ -85,23 +85,27 @@ test_expect_failure REBASE_P 'Rebase -Xsubtree --keep-empty --preserve-merges --
verbose test "$(commit_message HEAD)" = "Empty commit"
'
-test_expect_success 'Rebase -Xsubtree --keep-empty --onto commit' '
+test_expect_success 'Rebase -Xsubtree --empty=ask --onto commit' '
reset_rebase &&
git checkout -b rebase-onto to-rebase &&
- test_must_fail git rebase -Xsubtree=files_subtree --keep-empty --onto files-master master &&
+ test_must_fail git rebase -Xsubtree=files_subtree --empty=ask --onto files-master master &&
: first pick results in no changes &&
- git rebase --continue &&
+ test_must_fail git rebase --skip &&
+ : last pick was an empty commit that has no changes, but we want to keep it &&
+ git commit --allow-empty &&
verbose test "$(commit_message HEAD~2)" = "master4" &&
verbose test "$(commit_message HEAD~)" = "files_subtree/master5" &&
verbose test "$(commit_message HEAD)" = "Empty commit"
'
-test_expect_success 'Rebase -Xsubtree --keep-empty --rebase-merges --onto commit' '
+test_expect_success 'Rebase -Xsubtree --empty=ask --rebase-merges --onto commit' '
reset_rebase &&
git checkout -b rebase-merges-onto to-rebase &&
- test_must_fail git rebase -Xsubtree=files_subtree --keep-empty --rebase-merges --onto files-master --root &&
+ test_must_fail git rebase -Xsubtree=files_subtree --empty=ask --rebase-merges --onto files-master --root &&
: first pick results in no changes &&
- git rebase --continue &&
+ test_must_fail git rebase --skip &&
+ : last pick was an empty commit that has no changes, but we want to keep it &&
+ git commit --allow-empty &&
verbose test "$(commit_message HEAD~2)" = "master4" &&
verbose test "$(commit_message HEAD~)" = "files_subtree/master5" &&
verbose test "$(commit_message HEAD)" = "Empty commit"
--
gitgitgadget
^ permalink raw reply related [flat|nested] 161+ messages in thread
* [PATCH 02/15] t3406: simplify an already simple test
2019-12-20 17:09 [PATCH 00/15] rebase: make the default backend configurable Elijah Newren via GitGitGadget
2019-12-20 17:09 ` [PATCH 01/15] rebase: extend the options for handling of empty commits Elijah Newren via GitGitGadget
@ 2019-12-20 17:09 ` Elijah Newren via GitGitGadget
2019-12-20 17:09 ` [PATCH 03/15] rebase, sequencer: remove the broken GIT_QUIET handling Elijah Newren via GitGitGadget
` (14 subsequent siblings)
16 siblings, 0 replies; 161+ messages in thread
From: Elijah Newren via GitGitGadget @ 2019-12-20 17:09 UTC (permalink / raw)
To: git
Cc: Johannes.Schindelin, phillip.wood, liu.denton, gitster, plroskin,
Junio C Hamano, Elijah Newren
From: Elijah Newren <newren@gmail.com>
When the merge backend was re-implemented on top of the interactive
backend, the output of rebase --merge changed a little. This change
allowed this test to be simplified, though it wasn't noticed until now.
Simplify the testcase a little.
Signed-off-by: Elijah Newren <newren@gmail.com>
---
t/t3406-rebase-message.sh | 7 ++-----
1 file changed, 2 insertions(+), 5 deletions(-)
diff --git a/t/t3406-rebase-message.sh b/t/t3406-rebase-message.sh
index b393e1e9fe..0c2c569f95 100755
--- a/t/t3406-rebase-message.sh
+++ b/t/t3406-rebase-message.sh
@@ -18,11 +18,8 @@ test_expect_success 'setup' '
'
test_expect_success 'rebase -m' '
- git rebase -m master >report &&
- >expect &&
- sed -n -e "/^Already applied: /p" \
- -e "/^Committed: /p" report >actual &&
- test_cmp expect actual
+ git rebase -m master >actual &&
+ test_must_be_empty actual
'
test_expect_success 'rebase against master twice' '
--
gitgitgadget
^ permalink raw reply related [flat|nested] 161+ messages in thread
* [PATCH 03/15] rebase, sequencer: remove the broken GIT_QUIET handling
2019-12-20 17:09 [PATCH 00/15] rebase: make the default backend configurable Elijah Newren via GitGitGadget
2019-12-20 17:09 ` [PATCH 01/15] rebase: extend the options for handling of empty commits Elijah Newren via GitGitGadget
2019-12-20 17:09 ` [PATCH 02/15] t3406: simplify an already simple test Elijah Newren via GitGitGadget
@ 2019-12-20 17:09 ` Elijah Newren via GitGitGadget
2019-12-20 21:34 ` Junio C Hamano
2019-12-20 17:09 ` [PATCH 04/15] rebase: make sure to pass along the quiet flag to the sequencer Elijah Newren via GitGitGadget
` (13 subsequent siblings)
16 siblings, 1 reply; 161+ messages in thread
From: Elijah Newren via GitGitGadget @ 2019-12-20 17:09 UTC (permalink / raw)
To: git
Cc: Johannes.Schindelin, phillip.wood, liu.denton, gitster, plroskin,
Junio C Hamano, Elijah Newren
From: Elijah Newren <newren@gmail.com>
The GIT_QUIET environment variable was used to signal the non-am
backends that the rebase should perform quietly. The preserve-merges
backend does not make use of the quiet flag anywhere (other than to
write out its state whenever it writes state), and this mechanism was
broken in the conversion from shell to C. Since this environment
variable was specifically designed for scripts and the only backend that
would still use it is no longer a script, just gut this code.
A subsequent commit will fix --quiet for the interactive/merge backend
in a different way.
Signed-off-by: Elijah Newren <newren@gmail.com>
---
builtin/rebase.c | 6 ++----
sequencer.c | 6 ++----
2 files changed, 4 insertions(+), 8 deletions(-)
diff --git a/builtin/rebase.c b/builtin/rebase.c
index 96db10eaed..c71d169688 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -718,8 +718,8 @@ static int rebase_write_basic_state(struct rebase_options *opts)
opts->onto ? oid_to_hex(&opts->onto->object.oid) : "");
write_file(state_dir_path("orig-head", opts), "%s",
oid_to_hex(&opts->orig_head));
- write_file(state_dir_path("quiet", opts), "%s",
- opts->flags & REBASE_NO_QUIET ? "" : "t");
+ if (!(opts->flags & REBASE_NO_QUIET))
+ write_file(state_dir_path("quiet", opts), "%s", "");
if (opts->flags & REBASE_VERBOSE)
write_file(state_dir_path("verbose", opts), "%s", "");
if (opts->strategy)
@@ -1178,8 +1178,6 @@ static int run_specific_rebase(struct rebase_options *opts, enum action action)
add_var(&script_snippet, "revisions", opts->revisions);
add_var(&script_snippet, "restrict_revision", opts->restrict_revision ?
oid_to_hex(&opts->restrict_revision->object.oid) : NULL);
- add_var(&script_snippet, "GIT_QUIET",
- opts->flags & REBASE_NO_QUIET ? "" : "t");
sq_quote_argv_pretty(&buf, opts->git_am_opts.argv);
add_var(&script_snippet, "git_am_opt", buf.buf);
strbuf_release(&buf);
diff --git a/sequencer.c b/sequencer.c
index d2c11f34b7..71062212a5 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -2688,8 +2688,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)
@@ -2698,8 +2696,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)
--
gitgitgadget
^ permalink raw reply related [flat|nested] 161+ messages in thread
* [PATCH 04/15] rebase: make sure to pass along the quiet flag to the sequencer
2019-12-20 17:09 [PATCH 00/15] rebase: make the default backend configurable Elijah Newren via GitGitGadget
` (2 preceding siblings ...)
2019-12-20 17:09 ` [PATCH 03/15] rebase, sequencer: remove the broken GIT_QUIET handling Elijah Newren via GitGitGadget
@ 2019-12-20 17:09 ` Elijah Newren via GitGitGadget
2019-12-20 17:09 ` [PATCH 05/15] rebase: fix handling of restrict_revision Elijah Newren via GitGitGadget
` (12 subsequent siblings)
16 siblings, 0 replies; 161+ messages in thread
From: Elijah Newren via GitGitGadget @ 2019-12-20 17:09 UTC (permalink / raw)
To: git
Cc: Johannes.Schindelin, phillip.wood, liu.denton, gitster, plroskin,
Junio C Hamano, Elijah Newren
From: Elijah Newren <newren@gmail.com>
Signed-off-by: Elijah Newren <newren@gmail.com>
---
builtin/rebase.c | 3 ++-
t/t3400-rebase.sh | 8 +++++++-
2 files changed, 9 insertions(+), 2 deletions(-)
diff --git a/builtin/rebase.c b/builtin/rebase.c
index c71d169688..cc8f3f008f 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -126,6 +126,7 @@ static struct replay_opts get_replay_opts(const struct rebase_options *opts)
replay.keep_redundant_commits = (opts->empty == EMPTY_KEEP);
replay.ask_on_initially_empty = (opts->empty == EMPTY_ASK &&
!(opts->flags & REBASE_INTERACTIVE_EXPLICIT));
+ replay.quiet = !(opts->flags & REBASE_NO_QUIET);
replay.verbose = opts->flags & REBASE_VERBOSE;
replay.reschedule_failed_exec = opts->reschedule_failed_exec;
replay.committer_date_is_author_date =
@@ -1506,7 +1507,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
N_("allow pre-rebase hook to run")),
OPT_NEGBIT('q', "quiet", &options.flags,
N_("be quiet. implies --no-stat"),
- REBASE_NO_QUIET| REBASE_VERBOSE | REBASE_DIFFSTAT),
+ REBASE_NO_QUIET | REBASE_VERBOSE | REBASE_DIFFSTAT),
OPT_BIT('v', "verbose", &options.flags,
N_("display a diffstat of what changed upstream"),
REBASE_NO_QUIET | REBASE_VERBOSE | REBASE_DIFFSTAT),
diff --git a/t/t3400-rebase.sh b/t/t3400-rebase.sh
index 221b35f2df..79762b989a 100755
--- a/t/t3400-rebase.sh
+++ b/t/t3400-rebase.sh
@@ -206,12 +206,18 @@ test_expect_success 'cherry-picked commits and fork-point work together' '
test_cmp expect D
'
-test_expect_success 'rebase -q is quiet' '
+test_expect_success 'rebase --am -q is quiet' '
git checkout -b quiet topic &&
git rebase -q master >output.out 2>&1 &&
test_must_be_empty output.out
'
+test_expect_success 'rebase --merge -q is quiet' '
+ git checkout -B quiet topic &&
+ git rebase --merge -q master >output.out 2>&1 &&
+ test_must_be_empty output.out
+'
+
test_expect_success 'Rebase a commit that sprinkles CRs in' '
(
echo "One" &&
--
gitgitgadget
^ permalink raw reply related [flat|nested] 161+ messages in thread
* [PATCH 05/15] rebase: fix handling of restrict_revision
2019-12-20 17:09 [PATCH 00/15] rebase: make the default backend configurable Elijah Newren via GitGitGadget
` (3 preceding siblings ...)
2019-12-20 17:09 ` [PATCH 04/15] rebase: make sure to pass along the quiet flag to the sequencer Elijah Newren via GitGitGadget
@ 2019-12-20 17:09 ` Elijah Newren via GitGitGadget
2019-12-20 21:37 ` Junio C Hamano
2019-12-20 17:09 ` [PATCH 06/15] t3432: make these tests work with either am or merge backends Elijah Newren via GitGitGadget
` (11 subsequent siblings)
16 siblings, 1 reply; 161+ messages in thread
From: Elijah Newren via GitGitGadget @ 2019-12-20 17:09 UTC (permalink / raw)
To: git
Cc: Johannes.Schindelin, phillip.wood, liu.denton, gitster, plroskin,
Junio C Hamano, Elijah Newren
From: Elijah Newren <newren@gmail.com>
restrict_revision in the original shell script was an excluded revision
range. It is also treated that way by the am-backend. In the
conversion from shell to C (see commit 6ab54d17be3f ("rebase -i:
implement the logic to initialize $revisions in C", 2018-08-28)), the
interactive-backend accidentally treated it as a positive revision
rather than a negated one.
This was missed as there were no tests in the testsuite that tested an
interactive rebase with fork-point behavior.
Signed-off-by: Elijah Newren <newren@gmail.com>
---
builtin/rebase.c | 4 ++--
t/t3400-rebase.sh | 20 +++++++++++++++++++-
2 files changed, 21 insertions(+), 3 deletions(-)
diff --git a/builtin/rebase.c b/builtin/rebase.c
index cc8f3f008f..b320bb3a30 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -364,8 +364,8 @@ static int do_interactive_rebase(struct rebase_options *opts, unsigned flags)
argv_array_pushl(&make_script_args, "", revisions, NULL);
if (opts->restrict_revision)
- argv_array_push(&make_script_args,
- oid_to_hex(&opts->restrict_revision->object.oid));
+ argv_array_pushf(&make_script_args, "^%s",
+ oid_to_hex(&opts->restrict_revision->object.oid));
ret = sequencer_make_script(the_repository, &todo_list.buf,
make_script_args.argc, make_script_args.argv,
diff --git a/t/t3400-rebase.sh b/t/t3400-rebase.sh
index 79762b989a..71fd6396cd 100755
--- a/t/t3400-rebase.sh
+++ b/t/t3400-rebase.sh
@@ -165,11 +165,29 @@ test_expect_success 'rebase works with format.useAutoBase' '
git rebase master
'
-test_expect_success 'default to common base in @{upstream}s reflog if no upstream arg' '
+test_expect_success 'default to common base in @{upstream}s reflog if no upstream arg (--merge)' '
git checkout -b default-base master &&
git checkout -b default topic &&
git config branch.default.remote . &&
git config branch.default.merge refs/heads/default-base &&
+ git rebase --merge &&
+ git rev-parse --verify default-base >expect &&
+ git rev-parse default~1 >actual &&
+ test_cmp expect actual &&
+ git checkout default-base &&
+ git reset --hard HEAD^ &&
+ git checkout default &&
+ git rebase --merge &&
+ git rev-parse --verify default-base >expect &&
+ git rev-parse default~1 >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'default to common base in @{upstream}s reflog if no upstream arg' '
+ git checkout -B default-base master &&
+ git checkout -B default topic &&
+ git config branch.default.remote . &&
+ git config branch.default.merge refs/heads/default-base &&
git rebase &&
git rev-parse --verify default-base >expect &&
git rev-parse default~1 >actual &&
--
gitgitgadget
^ permalink raw reply related [flat|nested] 161+ messages in thread
* [PATCH 06/15] t3432: make these tests work with either am or merge backends
2019-12-20 17:09 [PATCH 00/15] rebase: make the default backend configurable Elijah Newren via GitGitGadget
` (4 preceding siblings ...)
2019-12-20 17:09 ` [PATCH 05/15] rebase: fix handling of restrict_revision Elijah Newren via GitGitGadget
@ 2019-12-20 17:09 ` Elijah Newren via GitGitGadget
2019-12-22 5:11 ` Denton Liu
2019-12-20 17:09 ` [PATCH 07/15] rebase: allow more types of rebases to fast-forward Elijah Newren via GitGitGadget
` (10 subsequent siblings)
16 siblings, 1 reply; 161+ messages in thread
From: Elijah Newren via GitGitGadget @ 2019-12-20 17:09 UTC (permalink / raw)
To: git
Cc: Johannes.Schindelin, phillip.wood, liu.denton, gitster, plroskin,
Junio C Hamano, Elijah Newren
From: Elijah Newren <newren@gmail.com>
t3432 had several stress tests for can_fast_forward(), whose intent was
to ensure we were using the optimization of just fast forwarding when
possible. However, these tests verified that fast forwards had happened
based on the output that rebase printed to the terminal. We can instead
test more directly that we actually fast-forwarded by checking the
reflog, which also has the side effect of making the tests applicable
for the merge/interactive backend.
This change does lose the distinction between "noop" and "noop-force",
but as stated in commit c9efc216830f ("t3432: test for --no-ff's
interaction with fast-forward", 2019-08-27) which introduced that
distinction: "These tests aren't supposed to endorse the status quo,
just test for what we're currently doing.".
This change does not actually run these tests with the merge/interactive
backend; instead this is just a preparatory commit. A subsequent commit
which fixes can_fast_forward() to work with that backend will then also
change t3432 to add tests of that backend as well.
Signed-off-by: Elijah Newren <newren@gmail.com>
---
t/t3432-rebase-fast-forward.sh | 53 ++++++++++++++++------------------
1 file changed, 25 insertions(+), 28 deletions(-)
diff --git a/t/t3432-rebase-fast-forward.sh b/t/t3432-rebase-fast-forward.sh
index 92f95b57da..3879a43fa0 100755
--- a/t/t3432-rebase-fast-forward.sh
+++ b/t/t3432-rebase-fast-forward.sh
@@ -44,19 +44,16 @@ test_rebase_same_head_ () {
test_expect_$status "git rebase$flag $* with $changes is $what with $cmp HEAD" "
oldhead=\$(git rev-parse HEAD) &&
test_when_finished 'git reset --hard \$oldhead' &&
+ cp .git/logs/HEAD expect &&
git rebase$flag $* >stdout &&
if test $what = work
then
- # Must check this case first, for 'is up to
- # date, rebase forced[...]rewinding head' cases
- test_i18ngrep 'rewinding head' stdout
+ wc -l .git/logs/HEAD >old &&
+ wc -l .git/logs/HEAD >new &&
+ test_line_count '-gt' $(($old + 2)) .git/logs/HEAD
elif test $what = noop
then
- test_i18ngrep 'is up to date' stdout &&
- test_i18ngrep ! 'rebase forced' stdout
- elif test $what = noop-force
- then
- test_i18ngrep 'is up to date, rebase forced' stdout
+ test_cmp expect .git/logs/HEAD
fi &&
newhead=\$(git rev-parse HEAD) &&
if test $cmp = same
@@ -71,14 +68,14 @@ test_rebase_same_head_ () {
changes='no changes'
test_rebase_same_head success noop same success work same
-test_rebase_same_head success noop same success noop-force same master
-test_rebase_same_head success noop same success noop-force diff --onto B B
-test_rebase_same_head success noop same success noop-force diff --onto B... B
-test_rebase_same_head success noop same success noop-force same --onto master... master
-test_rebase_same_head success noop same success noop-force same --keep-base master
-test_rebase_same_head success noop same success noop-force same --keep-base
-test_rebase_same_head success noop same success noop-force same --no-fork-point
-test_rebase_same_head success noop same success noop-force same --keep-base --no-fork-point
+test_rebase_same_head success noop same success work same master
+test_rebase_same_head success noop same success work diff --onto B B
+test_rebase_same_head success noop same success work diff --onto B... B
+test_rebase_same_head success noop same success work same --onto master... master
+test_rebase_same_head success noop same success work same --keep-base master
+test_rebase_same_head success noop same success work same --keep-base
+test_rebase_same_head success noop same success work same --no-fork-point
+test_rebase_same_head success noop same success work same --keep-base --no-fork-point
test_rebase_same_head success noop same success work same --fork-point master
test_rebase_same_head success noop same success work diff --fork-point --onto B B
test_rebase_same_head success noop same success work diff --fork-point --onto B... B
@@ -91,14 +88,14 @@ test_expect_success 'add work same to side' '
changes='our changes'
test_rebase_same_head success noop same success work same
-test_rebase_same_head success noop same success noop-force same master
-test_rebase_same_head success noop same success noop-force diff --onto B B
-test_rebase_same_head success noop same success noop-force diff --onto B... B
-test_rebase_same_head success noop same success noop-force same --onto master... master
-test_rebase_same_head success noop same success noop-force same --keep-base master
-test_rebase_same_head success noop same success noop-force same --keep-base
-test_rebase_same_head success noop same success noop-force same --no-fork-point
-test_rebase_same_head success noop same success noop-force same --keep-base --no-fork-point
+test_rebase_same_head success noop same success work same master
+test_rebase_same_head success noop same success work diff --onto B B
+test_rebase_same_head success noop same success work diff --onto B... B
+test_rebase_same_head success noop same success work same --onto master... master
+test_rebase_same_head success noop same success work same --keep-base master
+test_rebase_same_head success noop same success work same --keep-base
+test_rebase_same_head success noop same success work same --no-fork-point
+test_rebase_same_head success noop same success work same --keep-base --no-fork-point
test_rebase_same_head success noop same success work same --fork-point master
test_rebase_same_head success noop same success work diff --fork-point --onto B B
test_rebase_same_head success noop same success work diff --fork-point --onto B... B
@@ -112,13 +109,13 @@ test_expect_success 'add work same to upstream' '
'
changes='our and their changes'
-test_rebase_same_head success noop same success noop-force diff --onto B B
-test_rebase_same_head success noop same success noop-force diff --onto B... B
+test_rebase_same_head success noop same success work diff --onto B B
+test_rebase_same_head success noop same success work diff --onto B... B
test_rebase_same_head success noop same success work diff --onto master... master
test_rebase_same_head success noop same success work diff --keep-base master
test_rebase_same_head success noop same success work diff --keep-base
-test_rebase_same_head failure work same success work diff --fork-point --onto B B
-test_rebase_same_head failure work same success work diff --fork-point --onto B... B
+test_rebase_same_head success work same success work diff --fork-point --onto B B
+test_rebase_same_head success work same success work diff --fork-point --onto B... B
test_rebase_same_head success noop same success work diff --fork-point --onto master... master
test_rebase_same_head success noop same success work diff --fork-point --keep-base master
--
gitgitgadget
^ permalink raw reply related [flat|nested] 161+ messages in thread
* [PATCH 07/15] rebase: allow more types of rebases to fast-forward
2019-12-20 17:09 [PATCH 00/15] rebase: make the default backend configurable Elijah Newren via GitGitGadget
` (5 preceding siblings ...)
2019-12-20 17:09 ` [PATCH 06/15] t3432: make these tests work with either am or merge backends Elijah Newren via GitGitGadget
@ 2019-12-20 17:09 ` Elijah Newren via GitGitGadget
2019-12-20 17:09 ` [PATCH 08/15] git-rebase.txt: add more details about behavioral differences of backends Elijah Newren via GitGitGadget
` (9 subsequent siblings)
16 siblings, 0 replies; 161+ messages in thread
From: Elijah Newren via GitGitGadget @ 2019-12-20 17:09 UTC (permalink / raw)
To: git
Cc: Johannes.Schindelin, phillip.wood, liu.denton, gitster, plroskin,
Junio C Hamano, Elijah Newren
From: Elijah Newren <newren@gmail.com>
In the past, we dis-allowed rebases using the interactive backend from
performing a fast-forward to short-circuit the rebase operation. This
made sense for explicitly interactive rebases and some implicitly
interactive rebases, but certainly became overly stringent when the
merge backend was re-implemented via the interactive backend.
Just as the am-based rebase has always had to disable the fast-forward
based on a variety of conditions or flags (e.g. --signoff, --whitespace,
etc.), we need to do the same but now with a few more options. However,
continuing to use REBASE_FORCE for tracking this is problematic because
the interactive backend used it for a different purpose. (When
REBASE_FORCE wasn't set, the interactive backend would not fast-forward
the whole series but would fast-forward individual "pick" commits at the
beginning of the todo list, and then a squash or something would cause
it to start generating new commits.) So, introduce a new
allow_preemptive_ff flag contained within cmd_rebase() and use it to
track whether we are going to allow a pre-emptive fast-forward that
short-circuits the whole rebase.
Signed-off-by: Elijah Newren <newren@gmail.com>
---
builtin/rebase.c | 18 ++++++++++++++----
t/t3432-rebase-fast-forward.sh | 2 ++
2 files changed, 16 insertions(+), 4 deletions(-)
diff --git a/builtin/rebase.c b/builtin/rebase.c
index b320bb3a30..67bccd876f 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -1497,6 +1497,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
struct object_id squash_onto;
char *squash_onto_name = NULL;
int reschedule_failed_exec = -1;
+ int allow_preemptive_ff = 1;
struct option builtin_rebase_options[] = {
OPT_STRING(0, "onto", &options.onto_name,
N_("revision"),
@@ -1808,11 +1809,18 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
options.ignore_date)
options.flags |= REBASE_FORCE;
+ if ((options.flags & REBASE_INTERACTIVE_EXPLICIT) ||
+ (action != ACTION_NONE) ||
+ (exec.nr > 0) ||
+ options.autosquash) {
+ allow_preemptive_ff = 0;
+ }
+
for (i = 0; i < options.git_am_opts.argc; i++) {
const char *option = options.git_am_opts.argv[i], *p;
if (!strcmp(option, "--whitespace=fix") ||
!strcmp(option, "--whitespace=strip"))
- options.flags |= REBASE_FORCE;
+ allow_preemptive_ff = 0;
else if (skip_prefix(option, "-C", &p)) {
while (*p)
if (!isdigit(*(p++)))
@@ -2148,12 +2156,14 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
/*
* Check if we are already based on onto with linear history,
* in which case we could fast-forward without replacing the commits
- * with new commits recreated by replaying their changes. This
- * optimization must not be done if this is an interactive rebase.
+ * with new commits recreated by replaying their changes.
+ *
+ * Note that can_fast_forward() initializes merge_base, so we have to
+ * call it before checking allow_preemptive_ff.
*/
if (can_fast_forward(options.onto, options.upstream, options.restrict_revision,
&options.orig_head, &merge_base) &&
- !is_interactive(&options)) {
+ allow_preemptive_ff) {
int flag;
if (!(options.flags & REBASE_FORCE)) {
diff --git a/t/t3432-rebase-fast-forward.sh b/t/t3432-rebase-fast-forward.sh
index 3879a43fa0..58c91c6899 100755
--- a/t/t3432-rebase-fast-forward.sh
+++ b/t/t3432-rebase-fast-forward.sh
@@ -30,6 +30,8 @@ test_rebase_same_head () {
shift &&
test_rebase_same_head_ $status_n $what_n $cmp_n "" "$*" &&
test_rebase_same_head_ $status_f $what_f $cmp_f " --no-ff" "$*"
+ test_rebase_same_head_ $status_n $what_n $cmp_n " --merge" "$*" &&
+ test_rebase_same_head_ $status_f $what_f $cmp_f " --merge --no-ff" "$*"
}
test_rebase_same_head_ () {
--
gitgitgadget
^ permalink raw reply related [flat|nested] 161+ messages in thread
* [PATCH 08/15] git-rebase.txt: add more details about behavioral differences of backends
2019-12-20 17:09 [PATCH 00/15] rebase: make the default backend configurable Elijah Newren via GitGitGadget
` (6 preceding siblings ...)
2019-12-20 17:09 ` [PATCH 07/15] rebase: allow more types of rebases to fast-forward Elijah Newren via GitGitGadget
@ 2019-12-20 17:09 ` Elijah Newren via GitGitGadget
2019-12-20 17:09 ` [PATCH 09/15] rebase: move incompatibility checks between backend options a bit earlier Elijah Newren via GitGitGadget
` (8 subsequent siblings)
16 siblings, 0 replies; 161+ messages in thread
From: Elijah Newren via GitGitGadget @ 2019-12-20 17:09 UTC (permalink / raw)
To: git
Cc: Johannes.Schindelin, phillip.wood, liu.denton, gitster, plroskin,
Junio C Hamano, Elijah Newren
From: Elijah Newren <newren@gmail.com>
Signed-off-by: Elijah Newren <newren@gmail.com>
---
Documentation/git-rebase.txt | 102 +++++++++++++++++++++---
t/t3433-rebase-options-compatibility.sh | 5 +-
2 files changed, 94 insertions(+), 13 deletions(-)
diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index ff32ca1080..f1ace07c38 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -409,13 +409,10 @@ your branch contains commits which were dropped, this option can be used
with `--keep-base` in order to drop those commits from your branch.
--ignore-whitespace::
- Behaves differently depending on which backend is selected.
-+
-'am' backend: When applying a patch, ignore changes in whitespace in
-context lines if necessary.
-+
-'interactive' backend: Treat lines with only whitespace changes as
-unchanged for the sake of a three-way merge.
+ Ignore whitespace-only changes in the commits being rebased,
+ which may avoid "unnecessary" conflicts. (Both backends
+ currently have differing edgecase bugs with this option; see
+ BEHAVIORAL DIFFERENCES.)
--whitespace=<option>::
This flag is passed to the 'git apply' program
@@ -609,9 +606,94 @@ There are some subtle differences how the backends behave.
Directory rename detection
~~~~~~~~~~~~~~~~~~~~~~~~~~
-Directory rename heuristics are enabled in the merge and interactive
-backends. Due to the lack of accurate tree information, directory
-rename detection is disabled in the am backend.
+Due to the lack of accurate tree information (arising from
+constructing fake ancestors with the limited information available in
+patches), directory rename detection is disabled in the am backend.
+Disabled directory rename detection means that if one side of history
+renames a directory and the other adds new files to the old directory,
+then the new files will be left behind in the old directory without
+any warning at the time of rebasing that you may want to move these
+files into the new directory.
+
+Directory rename detection works with the merge and interactive
+backends to provide you warnings in such cases.
+
+Context
+~~~~~~~
+
+The am backend works by creating a sequence of patches (by calling
+`format-patch` internally), and then applying the patches in sequence
+(calling `am` internally). Patches are composed of multiple hunks,
+each with line numbers, a context region, and the actual changes. The
+line numbers have to be taken with some fuzz, since the other side
+will likely have inserted or deleted lines earlier in the file. The
+context region is meant to help find how to adjust the line numbers in
+order to apply the changes to the right lines. However, if multiple
+areas of the code have the same surrounding lines of context, the
+wrong one can be picked. There are real-world cases where this has
+caused commits to be reapplied incorrectly with no conflicts reported.
+Setting diff.context to a larger value may prevent such types of
+problems, but increases the chance of spurious conflicts (since it
+will require more lines of matching context to apply).
+
+The interactive backend works with a full copy of each relevant file,
+insulating it from these types of problems.
+
+Labelling of conflicts markers
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+When there are content conflicts, the merge machinery tries to
+annotate each side's conflict markers with the commits where the
+content came from. Since the am backend drops the original
+information about the rebased commits and their parents (and instead
+generates new fake commits based off limited information in the
+generated patches), those commits cannot be identified; instead it has
+to fall back to a commit summary. Also, when merge.conflictStyle is
+set to diff3, the am backend will use "constructed merge base" to
+label the content from the merge base, and thus provide no information
+about the merge base commit whatsoever.
+
+The interactive backend works with the full commits on both sides of
+history and thus has no such limitations.
+
+--ignore-whitespace
+~~~~~~~~~~~~~~~~~~~
+
+The --ignore-whitespace option is supposed to ignore whitespace-only
+changes if it allows the code to merge cleanly. Unfortunately, the
+different backends implement this differently, and both have different
+edge case bugs.
++
+'am' backend: When applying a patch, ignore changes in whitespace in
+context lines if necessary. (Which implies that if the whitespace
+change was not in the context lines but on a line with a real change,
+then the rebase will still fail with "unnecessary" content conflicts.)
++
+'interactive' backend: Treat lines with only whitespace changes as
+unchanged for the sake of a three-way merge. This means that if one
+side made no changes and the commits being rebased had whitespace-only
+changes, those whitespaces fixups will be discarded despite the fact
+that they present no content conflict.
+
+Miscellaneous differences
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+There are a few more behavioral differences that most folks would
+probably consider inconsequential but which are mentioned for
+completeness:
+
+* Reflog: The two backends will use different wording when describing
+ the changes made in the reflog, though both will make use of the
+ word "rebase".
+
+* Progress, informational, and error messages: The two backends
+ provide slightly different progress and informational messages.
+ Also, the am backend writes error messages (such as "Your files
+ would be overwritten...") to stdout, while the interactive backend
+ writes them to stderr.
+
+* State directories: The two backends keep their state in different
+ directories under .git/
include::merge-strategies.txt[]
diff --git a/t/t3433-rebase-options-compatibility.sh b/t/t3433-rebase-options-compatibility.sh
index 5166f158dd..bd4d2d2f63 100755
--- a/t/t3433-rebase-options-compatibility.sh
+++ b/t/t3433-rebase-options-compatibility.sh
@@ -10,9 +10,8 @@ test_description='tests to ensure compatibility between am and interactive backe
GIT_AUTHOR_DATE="1999-04-02T08:03:20+05:30"
export GIT_AUTHOR_DATE
-# This is a special case in which both am and interactive backends
-# provide the same output. It was done intentionally because
-# both the backends fall short of optimal behaviour.
+# This is a common case in which both am and interactive backends
+# provide the same output with --ignore-whitespace.
test_expect_success 'setup' '
git checkout -b topic &&
q_to_tab >file <<-\EOF &&
--
gitgitgadget
^ permalink raw reply related [flat|nested] 161+ messages in thread
* [PATCH 09/15] rebase: move incompatibility checks between backend options a bit earlier
2019-12-20 17:09 [PATCH 00/15] rebase: make the default backend configurable Elijah Newren via GitGitGadget
` (7 preceding siblings ...)
2019-12-20 17:09 ` [PATCH 08/15] git-rebase.txt: add more details about behavioral differences of backends Elijah Newren via GitGitGadget
@ 2019-12-20 17:09 ` Elijah Newren via GitGitGadget
2019-12-20 17:09 ` [PATCH 10/15] rebase: add an --am option Elijah Newren via GitGitGadget
` (7 subsequent siblings)
16 siblings, 0 replies; 161+ messages in thread
From: Elijah Newren via GitGitGadget @ 2019-12-20 17:09 UTC (permalink / raw)
To: git
Cc: Johannes.Schindelin, phillip.wood, liu.denton, gitster, plroskin,
Junio C Hamano, Elijah Newren
From: Elijah Newren <newren@gmail.com>
Signed-off-by: Elijah Newren <newren@gmail.com>
---
builtin/rebase.c | 22 +++++++++++-----------
1 file changed, 11 insertions(+), 11 deletions(-)
diff --git a/builtin/rebase.c b/builtin/rebase.c
index 67bccd876f..9e7e88b147 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -1910,6 +1910,17 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
if (isatty(2) && options.flags & REBASE_NO_QUIET)
strbuf_addstr(&options.git_format_patch_opt, " --progress");
+ if (options.git_am_opts.argc) {
+ /* all am options except -q are compatible only with --am */
+ for (i = options.git_am_opts.argc - 1; i >= 0; i--)
+ if (strcmp(options.git_am_opts.argv[i], "-q"))
+ break;
+
+ if (is_interactive(&options) && i >= 0)
+ die(_("cannot combine am options with either "
+ "interactive or merge options"));
+ }
+
switch (options.type) {
case REBASE_MERGE:
case REBASE_INTERACTIVE:
@@ -1940,17 +1951,6 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
if (reschedule_failed_exec >= 0)
options.reschedule_failed_exec = reschedule_failed_exec;
- if (options.git_am_opts.argc) {
- /* all am options except -q are compatible only with --am */
- for (i = options.git_am_opts.argc - 1; i >= 0; i--)
- if (strcmp(options.git_am_opts.argv[i], "-q"))
- break;
-
- if (is_interactive(&options) && i >= 0)
- die(_("cannot combine am options with either "
- "interactive or merge options"));
- }
-
if (options.signoff) {
if (options.type == REBASE_PRESERVE_MERGES)
die("cannot combine '--signoff' with "
--
gitgitgadget
^ permalink raw reply related [flat|nested] 161+ messages in thread
* [PATCH 10/15] rebase: add an --am option
2019-12-20 17:09 [PATCH 00/15] rebase: make the default backend configurable Elijah Newren via GitGitGadget
` (8 preceding siblings ...)
2019-12-20 17:09 ` [PATCH 09/15] rebase: move incompatibility checks between backend options a bit earlier Elijah Newren via GitGitGadget
@ 2019-12-20 17:09 ` Elijah Newren via GitGitGadget
2019-12-20 17:09 ` [PATCH 11/15] contrib: change the prompt for am-based rebases Elijah Newren via GitGitGadget
` (6 subsequent siblings)
16 siblings, 0 replies; 161+ messages in thread
From: Elijah Newren via GitGitGadget @ 2019-12-20 17:09 UTC (permalink / raw)
To: git
Cc: Johannes.Schindelin, phillip.wood, liu.denton, gitster, plroskin,
Junio C Hamano, Elijah Newren
From: Elijah Newren <newren@gmail.com>
Currently, this option doesn't do anything except error out if any
options requiring the interactive-backend are also passed. However,
when we make the default backend configurable later in this series, this
flag will provide a way to override the config setting.
Signed-off-by: Elijah Newren <newren@gmail.com>
---
Documentation/git-rebase.txt | 11 ++++++++++-
builtin/rebase.c | 18 +++++++++++++++++-
2 files changed, 27 insertions(+), 2 deletions(-)
diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index f1ace07c38..cf1ac2e359 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -258,6 +258,13 @@ See also INCOMPATIBLE OPTIONS below.
original branch. The index and working tree are also left
unchanged as a result.
+--am:
+ Use git-am internally to rebase. This option may become a
+ no-op in the future once the interactive backend handles
+ everything the am one does.
++
+See also INCOMPATIBLE OPTIONS below.
+
--empty={drop,keep,ask}::
How to handle commits that become empty (because they contain a
subset of already upstream changes) or start empty. With drop
@@ -372,7 +379,7 @@ See also INCOMPATIBLE OPTIONS below.
Ensure at least <n> lines of surrounding context match before
and after each change. When fewer lines of surrounding
context exist they all must match. By default no context is
- ever ignored.
+ ever ignored. Implies --am.
+
See also INCOMPATIBLE OPTIONS below.
@@ -417,6 +424,7 @@ with `--keep-base` in order to drop those commits from your branch.
--whitespace=<option>::
This flag is passed to the 'git apply' program
(see linkgit:git-apply[1]) that applies the patch.
+ Implies --am.
+
See also INCOMPATIBLE OPTIONS below.
@@ -567,6 +575,7 @@ INCOMPATIBLE OPTIONS
The following options:
+ * --am
* --whitespace
* -C
diff --git a/builtin/rebase.c b/builtin/rebase.c
index 9e7e88b147..ab9e16b206 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -1361,6 +1361,18 @@ static int can_fast_forward(struct commit *onto, struct commit *upstream,
return res && is_linear_history(onto, head);
}
+static int parse_opt_am(const struct option *opt, const char *arg, int unset)
+{
+ struct rebase_options *opts = opt->value;
+
+ BUG_ON_OPT_NEG(unset);
+ BUG_ON_OPT_ARG(arg);
+
+ opts->type = REBASE_AM;
+
+ return 0;
+}
+
/* -i followed by -m is still -i */
static int parse_opt_merge(const struct option *opt, const char *arg, int unset)
{
@@ -1550,6 +1562,10 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
OPT_CMDMODE(0, "show-current-patch", &action,
N_("show the patch file being applied or merged"),
ACTION_SHOW_CURRENT_PATCH),
+ { OPTION_CALLBACK, 0, "am", &options, NULL,
+ N_("use apply-mail strategies to rebase"),
+ PARSE_OPT_NOARG | PARSE_OPT_NONEG,
+ parse_opt_am },
{ OPTION_CALLBACK, 'm', "merge", &options, NULL,
N_("use merging strategies to rebase"),
PARSE_OPT_NOARG | PARSE_OPT_NONEG,
@@ -1910,7 +1926,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
if (isatty(2) && options.flags & REBASE_NO_QUIET)
strbuf_addstr(&options.git_format_patch_opt, " --progress");
- if (options.git_am_opts.argc) {
+ if (options.git_am_opts.argc || options.type == REBASE_AM) {
/* all am options except -q are compatible only with --am */
for (i = options.git_am_opts.argc - 1; i >= 0; i--)
if (strcmp(options.git_am_opts.argv[i], "-q"))
--
gitgitgadget
^ permalink raw reply related [flat|nested] 161+ messages in thread
* [PATCH 11/15] contrib: change the prompt for am-based rebases
2019-12-20 17:09 [PATCH 00/15] rebase: make the default backend configurable Elijah Newren via GitGitGadget
` (9 preceding siblings ...)
2019-12-20 17:09 ` [PATCH 10/15] rebase: add an --am option Elijah Newren via GitGitGadget
@ 2019-12-20 17:09 ` Elijah Newren via GitGitGadget
2019-12-20 23:07 ` SZEDER Gábor
2019-12-20 17:09 ` [PATCH 12/15] rebase tests: mark tests specific to the am-backend with --am Elijah Newren via GitGitGadget
` (5 subsequent siblings)
16 siblings, 1 reply; 161+ messages in thread
From: Elijah Newren via GitGitGadget @ 2019-12-20 17:09 UTC (permalink / raw)
To: git
Cc: Johannes.Schindelin, phillip.wood, liu.denton, gitster, plroskin,
Junio C Hamano, Elijah Newren
From: Elijah Newren <newren@gmail.com>
The prompt for am-based rebases was REBASE, while for interactive-based
rebases was REBASE-i. A while ago, we switched merge-based rebases from
using REBASE-m to REBASE-i via re-implementing the merge backend based
on the interactive backend. We will soon be changing the default rebase
backend to the interactive one, meaning the default prompt will be
REBASE-i rather than REBASE. We have also noted in the documentation
that currently am-specific options will be implemented in the
interactive backend, and even the --am flag may eventually imply an
interactive-based rebase. As such, change the prompt for an am-based
rebase from REBASE to REBASE-a.
Signed-off-by: Elijah Newren <newren@gmail.com>
---
contrib/completion/git-prompt.sh | 2 +-
t/t9903-bash-prompt.sh | 6 +++---
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/contrib/completion/git-prompt.sh b/contrib/completion/git-prompt.sh
index 1d510cd47b..3c81099d60 100644
--- a/contrib/completion/git-prompt.sh
+++ b/contrib/completion/git-prompt.sh
@@ -440,7 +440,7 @@ __git_ps1 ()
__git_eread "$g/rebase-apply/last" total
if [ -f "$g/rebase-apply/rebasing" ]; then
__git_eread "$g/rebase-apply/head-name" b
- r="|REBASE"
+ r="|REBASE-a"
elif [ -f "$g/rebase-apply/applying" ]; then
r="|AM"
else
diff --git a/t/t9903-bash-prompt.sh b/t/t9903-bash-prompt.sh
index 88bc733ad6..8da5b1aee2 100755
--- a/t/t9903-bash-prompt.sh
+++ b/t/t9903-bash-prompt.sh
@@ -189,11 +189,11 @@ test_expect_success 'prompt - rebase merge' '
test_cmp expected "$actual"
'
-test_expect_success 'prompt - rebase' '
- printf " (b2|REBASE 1/3)" >expected &&
+test_expect_success 'prompt - rebase am' '
+ printf " (b2|REBASE-a 1/3)" >expected &&
git checkout b2 &&
test_when_finished "git checkout master" &&
- test_must_fail git rebase b1 b2 &&
+ test_must_fail git rebase --am b1 b2 &&
test_when_finished "git rebase --abort" &&
__git_ps1 >"$actual" &&
test_cmp expected "$actual"
--
gitgitgadget
^ permalink raw reply related [flat|nested] 161+ messages in thread
* [PATCH 12/15] rebase tests: mark tests specific to the am-backend with --am
2019-12-20 17:09 [PATCH 00/15] rebase: make the default backend configurable Elijah Newren via GitGitGadget
` (10 preceding siblings ...)
2019-12-20 17:09 ` [PATCH 11/15] contrib: change the prompt for am-based rebases Elijah Newren via GitGitGadget
@ 2019-12-20 17:09 ` Elijah Newren via GitGitGadget
2019-12-20 17:09 ` [PATCH 13/15] rebase tests: repeat some tests using the merge backend instead of am Elijah Newren via GitGitGadget
` (4 subsequent siblings)
16 siblings, 0 replies; 161+ messages in thread
From: Elijah Newren via GitGitGadget @ 2019-12-20 17:09 UTC (permalink / raw)
To: git
Cc: Johannes.Schindelin, phillip.wood, liu.denton, gitster, plroskin,
Junio C Hamano, Elijah Newren
From: Elijah Newren <newren@gmail.com>
We have many rebase tests in the testsuite, and often the same test is
repeated multiple times just testing different backends. For those
tests that were specifically trying to test the am backend, add the --am
flag.
Signed-off-by: Elijah Newren <newren@gmail.com>
---
t/t3400-rebase.sh | 10 +++++-----
t/t3401-rebase-and-am-rename.sh | 4 ++--
t/t3404-rebase-interactive.sh | 2 +-
t/t3406-rebase-message.sh | 12 ++++++------
t/t3407-rebase-abort.sh | 6 +++---
t/t3420-rebase-autostash.sh | 2 +-
t/t3425-rebase-topology-merges.sh | 8 ++++----
t/t3432-rebase-fast-forward.sh | 4 ++--
t/t3433-rebase-options-compatibility.sh | 8 ++++----
t/t5407-post-rewrite-hook.sh | 12 ++++++------
t/t7512-status-help.sh | 12 ++++++------
11 files changed, 40 insertions(+), 40 deletions(-)
diff --git a/t/t3400-rebase.sh b/t/t3400-rebase.sh
index 71fd6396cd..0a491f2363 100755
--- a/t/t3400-rebase.sh
+++ b/t/t3400-rebase.sh
@@ -183,19 +183,19 @@ test_expect_success 'default to common base in @{upstream}s reflog if no upstrea
test_cmp expect actual
'
-test_expect_success 'default to common base in @{upstream}s reflog if no upstream arg' '
+test_expect_success 'default to common base in @{upstream}s reflog if no upstream arg (--am)' '
git checkout -B default-base master &&
git checkout -B default topic &&
git config branch.default.remote . &&
git config branch.default.merge refs/heads/default-base &&
- git rebase &&
+ git rebase --am &&
git rev-parse --verify default-base >expect &&
git rev-parse default~1 >actual &&
test_cmp expect actual &&
git checkout default-base &&
git reset --hard HEAD^ &&
git checkout default &&
- git rebase &&
+ git rebase --am &&
git rev-parse --verify default-base >expect &&
git rev-parse default~1 >actual &&
test_cmp expect actual
@@ -226,7 +226,7 @@ test_expect_success 'cherry-picked commits and fork-point work together' '
test_expect_success 'rebase --am -q is quiet' '
git checkout -b quiet topic &&
- git rebase -q master >output.out 2>&1 &&
+ git rebase --am -q master >output.out 2>&1 &&
test_must_be_empty output.out
'
@@ -325,7 +325,7 @@ test_expect_success 'rebase --am and --show-current-patch' '
echo two >>init.t &&
git commit -a -m two &&
git tag two &&
- test_must_fail git rebase -f --onto init HEAD^ &&
+ test_must_fail git rebase --am -f --onto init HEAD^ &&
GIT_TRACE=1 git rebase --show-current-patch >/dev/null 2>stderr &&
grep "show.*$(git rev-parse two)" stderr
)
diff --git a/t/t3401-rebase-and-am-rename.sh b/t/t3401-rebase-and-am-rename.sh
index a0b9438b22..50803958fd 100755
--- a/t/t3401-rebase-and-am-rename.sh
+++ b/t/t3401-rebase-and-am-rename.sh
@@ -52,13 +52,13 @@ test_expect_success 'rebase --interactive: directory rename detected' '
)
'
-test_expect_failure 'rebase (am): directory rename detected' '
+test_expect_failure 'rebase --am: directory rename detected' '
(
cd dir-rename &&
git checkout B^0 &&
- git -c merge.directoryRenames=true rebase A &&
+ git -c merge.directoryRenames=true rebase --am A &&
git ls-files -s >out &&
test_line_count = 5 out &&
diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
index ae6e55ce79..743b7e511a 100755
--- a/t/t3404-rebase-interactive.sh
+++ b/t/t3404-rebase-interactive.sh
@@ -1137,7 +1137,7 @@ test_expect_success C_LOCALE_OUTPUT 'rebase --edit-todo does not work on non-int
git checkout conflict-branch &&
(
set_fake_editor &&
- test_must_fail git rebase -f --onto HEAD~2 HEAD~ &&
+ test_must_fail git rebase -f --am --onto HEAD~2 HEAD~ &&
test_must_fail git rebase --edit-todo
) &&
git rebase --abort
diff --git a/t/t3406-rebase-message.sh b/t/t3406-rebase-message.sh
index 0c2c569f95..7ce617fc1f 100755
--- a/t/t3406-rebase-message.sh
+++ b/t/t3406-rebase-message.sh
@@ -23,24 +23,24 @@ test_expect_success 'rebase -m' '
'
test_expect_success 'rebase against master twice' '
- git rebase master >out &&
+ git rebase --am master >out &&
test_i18ngrep "Current branch topic is up to date" out
'
test_expect_success 'rebase against master twice with --force' '
- git rebase --force-rebase master >out &&
+ git rebase --force-rebase --am master >out &&
test_i18ngrep "Current branch topic is up to date, rebase forced" out
'
test_expect_success 'rebase against master twice from another branch' '
git checkout topic^ &&
- git rebase master topic >out &&
+ git rebase --am master topic >out &&
test_i18ngrep "Current branch topic is up to date" out
'
test_expect_success 'rebase fast-forward to master' '
git checkout topic^ &&
- git rebase topic >out &&
+ git rebase --am topic >out &&
test_i18ngrep "Fast-forwarded HEAD to topic" out
'
@@ -89,7 +89,7 @@ test_expect_success 'GIT_REFLOG_ACTION' '
git checkout -b reflog-topic start &&
test_commit reflog-to-rebase &&
- git rebase reflog-onto &&
+ git rebase --am reflog-onto &&
git log -g --format=%gs -3 >actual &&
cat >expect <<-\EOF &&
rebase finished: returning to refs/heads/reflog-topic
@@ -99,7 +99,7 @@ test_expect_success 'GIT_REFLOG_ACTION' '
test_cmp expect actual &&
git checkout -b reflog-prefix reflog-to-rebase &&
- GIT_REFLOG_ACTION=change-the-reflog git rebase reflog-onto &&
+ GIT_REFLOG_ACTION=change-the-reflog git rebase --am reflog-onto &&
git log -g --format=%gs -3 >actual &&
cat >expect <<-\EOF &&
rebase finished: returning to refs/heads/reflog-prefix
diff --git a/t/t3407-rebase-abort.sh b/t/t3407-rebase-abort.sh
index 910f218284..3e31826170 100755
--- a/t/t3407-rebase-abort.sh
+++ b/t/t3407-rebase-abort.sh
@@ -96,14 +96,14 @@ testrebase() {
'
}
-testrebase "" .git/rebase-apply
+testrebase " --am" .git/rebase-apply
testrebase " --merge" .git/rebase-merge
-test_expect_success 'rebase --quit' '
+test_expect_success 'rebase --am --quit' '
cd "$work_dir" &&
# Clean up the state from the previous one
git reset --hard pre-rebase &&
- test_must_fail git rebase master &&
+ test_must_fail git rebase --am master &&
test_path_is_dir .git/rebase-apply &&
head_before=$(git rev-parse HEAD) &&
git rebase --quit &&
diff --git a/t/t3420-rebase-autostash.sh b/t/t3420-rebase-autostash.sh
index 5f7e73cf83..3816159e20 100755
--- a/t/t3420-rebase-autostash.sh
+++ b/t/t3420-rebase-autostash.sh
@@ -234,7 +234,7 @@ test_expect_success "rebase: noop rebase" '
git checkout feature-branch
'
-testrebase "" .git/rebase-apply
+testrebase " --am" .git/rebase-apply
testrebase " --merge" .git/rebase-merge
testrebase " --interactive" .git/rebase-merge
diff --git a/t/t3425-rebase-topology-merges.sh b/t/t3425-rebase-topology-merges.sh
index fd8efe84fe..19700b025b 100755
--- a/t/t3425-rebase-topology-merges.sh
+++ b/t/t3425-rebase-topology-merges.sh
@@ -54,7 +54,7 @@ test_run_rebase () {
test_linear_range 'n o' e..
"
}
-test_run_rebase success ''
+test_run_rebase success --am
test_run_rebase success -m
test_run_rebase success -i
@@ -70,7 +70,7 @@ test_run_rebase () {
test_linear_range "\'"$expected"\'" d..
"
}
-test_run_rebase success 'n o e' ''
+test_run_rebase success 'n o e' --am
test_run_rebase success 'n o e' -m
test_run_rebase success 'n o e' -i
@@ -86,7 +86,7 @@ test_run_rebase () {
test_linear_range "\'"$expected"\'" c..
"
}
-test_run_rebase success 'd n o e' ''
+test_run_rebase success 'd n o e' --am
test_run_rebase success 'd n o e' -m
test_run_rebase success 'd n o e' -i
@@ -102,7 +102,7 @@ test_run_rebase () {
test_linear_range "\'"$expected"\'" c..
"
}
-test_run_rebase success 'd n o e' ''
+test_run_rebase success 'd n o e' --am
test_run_rebase success 'd n o e' -m
test_run_rebase success 'd n o e' -i
diff --git a/t/t3432-rebase-fast-forward.sh b/t/t3432-rebase-fast-forward.sh
index 58c91c6899..b22aa8c7ee 100755
--- a/t/t3432-rebase-fast-forward.sh
+++ b/t/t3432-rebase-fast-forward.sh
@@ -28,8 +28,8 @@ test_rebase_same_head () {
shift &&
cmp_f="$1" &&
shift &&
- test_rebase_same_head_ $status_n $what_n $cmp_n "" "$*" &&
- test_rebase_same_head_ $status_f $what_f $cmp_f " --no-ff" "$*"
+ test_rebase_same_head_ $status_n $what_n $cmp_n " --am" "$*" &&
+ test_rebase_same_head_ $status_f $what_f $cmp_f " --am --no-ff" "$*"
test_rebase_same_head_ $status_n $what_n $cmp_n " --merge" "$*" &&
test_rebase_same_head_ $status_f $what_f $cmp_f " --merge --no-ff" "$*"
}
diff --git a/t/t3433-rebase-options-compatibility.sh b/t/t3433-rebase-options-compatibility.sh
index bd4d2d2f63..a07e1f276b 100755
--- a/t/t3433-rebase-options-compatibility.sh
+++ b/t/t3433-rebase-options-compatibility.sh
@@ -51,9 +51,9 @@ test_expect_success '--ignore-whitespace works with am backend' '
new line 2
line 3
EOF
- test_must_fail git rebase main side &&
+ test_must_fail git rebase --am main side &&
git rebase --abort &&
- git rebase --ignore-whitespace main side &&
+ git rebase --am --ignore-whitespace main side &&
test_cmp expect file
'
@@ -71,7 +71,7 @@ test_expect_success '--ignore-whitespace works with interactive backend' '
test_expect_success '--committer-date-is-author-date works with am backend' '
git commit --amend &&
- git rebase --committer-date-is-author-date HEAD^ &&
+ git rebase --am --committer-date-is-author-date HEAD^ &&
git show HEAD --pretty="format:%ai" >authortime &&
git show HEAD --pretty="format:%ci" >committertime &&
test_cmp authortime committertime
@@ -103,7 +103,7 @@ test_expect_success '--committer-date-is-author-date works with rebase -r' '
# sets to +0530.
test_expect_success '--ignore-date works with am backend' '
git commit --amend --date="$GIT_AUTHOR_DATE" &&
- git rebase --ignore-date HEAD^ &&
+ git rebase --am --ignore-date HEAD^ &&
git show HEAD --pretty="format:%ai" >authortime &&
grep "+0000" authortime
'
diff --git a/t/t5407-post-rewrite-hook.sh b/t/t5407-post-rewrite-hook.sh
index 7344253bfb..a8a73616e4 100755
--- a/t/t5407-post-rewrite-hook.sh
+++ b/t/t5407-post-rewrite-hook.sh
@@ -53,10 +53,10 @@ test_expect_success 'git commit --amend --no-post-rewrite' '
test ! -f post-rewrite.data
'
-test_expect_success 'git rebase' '
+test_expect_success 'git rebase --am' '
git reset --hard D &&
clear_hook_input &&
- test_must_fail git rebase --onto A B &&
+ test_must_fail git rebase --am --onto A B &&
echo C > foo &&
git add foo &&
git rebase --continue &&
@@ -68,10 +68,10 @@ test_expect_success 'git rebase' '
verify_hook_input
'
-test_expect_success 'git rebase --skip' '
+test_expect_success 'git rebase --am --skip' '
git reset --hard D &&
clear_hook_input &&
- test_must_fail git rebase --onto A B &&
+ test_must_fail git rebase --am --onto A B &&
test_must_fail git rebase --skip &&
echo D > foo &&
git add foo &&
@@ -84,10 +84,10 @@ test_expect_success 'git rebase --skip' '
verify_hook_input
'
-test_expect_success 'git rebase --skip the last one' '
+test_expect_success 'git rebase --am --skip the last one' '
git reset --hard F &&
clear_hook_input &&
- test_must_fail git rebase --onto D A &&
+ test_must_fail git rebase --am --onto D A &&
git rebase --skip &&
echo rebase >expected.args &&
cat >expected.data <<-EOF &&
diff --git a/t/t7512-status-help.sh b/t/t7512-status-help.sh
index 66d7a62797..d22b0acf2a 100755
--- a/t/t7512-status-help.sh
+++ b/t/t7512-status-help.sh
@@ -71,10 +71,10 @@ test_expect_success 'prepare for rebase conflicts' '
'
-test_expect_success 'status when rebase in progress before resolving conflicts' '
+test_expect_success 'status when rebase --am in progress before resolving conflicts' '
test_when_finished "git rebase --abort" &&
ONTO=$(git rev-parse --short HEAD^^) &&
- test_must_fail git rebase HEAD^ --onto HEAD^^ &&
+ test_must_fail git rebase --am HEAD^ --onto HEAD^^ &&
cat >expected <<EOF &&
rebase in progress; onto $ONTO
You are currently rebasing branch '\''rebase_conflicts'\'' on '\''$ONTO'\''.
@@ -94,11 +94,11 @@ EOF
'
-test_expect_success 'status when rebase in progress before rebase --continue' '
+test_expect_success 'status when rebase --am in progress before rebase --continue' '
git reset --hard rebase_conflicts &&
test_when_finished "git rebase --abort" &&
ONTO=$(git rev-parse --short HEAD^^) &&
- test_must_fail git rebase HEAD^ --onto HEAD^^ &&
+ test_must_fail git rebase --am HEAD^ --onto HEAD^^ &&
echo three >main.txt &&
git add main.txt &&
cat >expected <<EOF &&
@@ -688,7 +688,7 @@ EOF
'
-test_expect_success 'status when rebase conflicts with statushints disabled' '
+test_expect_success 'status when rebase --am conflicts with statushints disabled' '
git reset --hard master &&
git checkout -b statushints_disabled &&
test_when_finished "git config --local advice.statushints true" &&
@@ -698,7 +698,7 @@ test_expect_success 'status when rebase conflicts with statushints disabled' '
test_commit three_statushints main.txt three &&
test_when_finished "git rebase --abort" &&
ONTO=$(git rev-parse --short HEAD^^) &&
- test_must_fail git rebase HEAD^ --onto HEAD^^ &&
+ test_must_fail git rebase --am HEAD^ --onto HEAD^^ &&
cat >expected <<EOF &&
rebase in progress; onto $ONTO
You are currently rebasing branch '\''statushints_disabled'\'' on '\''$ONTO'\''.
--
gitgitgadget
^ permalink raw reply related [flat|nested] 161+ messages in thread
* [PATCH 13/15] rebase tests: repeat some tests using the merge backend instead of am
2019-12-20 17:09 [PATCH 00/15] rebase: make the default backend configurable Elijah Newren via GitGitGadget
` (11 preceding siblings ...)
2019-12-20 17:09 ` [PATCH 12/15] rebase tests: mark tests specific to the am-backend with --am Elijah Newren via GitGitGadget
@ 2019-12-20 17:09 ` Elijah Newren via GitGitGadget
2019-12-20 17:09 ` [PATCH 14/15] rebase: make the backend configurable via config setting Elijah Newren via GitGitGadget
` (3 subsequent siblings)
16 siblings, 0 replies; 161+ messages in thread
From: Elijah Newren via GitGitGadget @ 2019-12-20 17:09 UTC (permalink / raw)
To: git
Cc: Johannes.Schindelin, phillip.wood, liu.denton, gitster, plroskin,
Junio C Hamano, Elijah Newren
From: Elijah Newren <newren@gmail.com>
In order to ensure the merge/interactive backend gets similar coverage
to the am one, add some tests for cases where previously only the am
backend was tested.
Signed-off-by: Elijah Newren <newren@gmail.com>
---
t/t5520-pull.sh | 17 +++++++++++++++--
t/t6047-diff3-conflict-markers.sh | 13 +++++++++++--
2 files changed, 26 insertions(+), 4 deletions(-)
diff --git a/t/t5520-pull.sh b/t/t5520-pull.sh
index 602d996a33..3fff6a06fa 100755
--- a/t/t5520-pull.sh
+++ b/t/t5520-pull.sh
@@ -277,14 +277,27 @@ test_expect_success '--rebase' '
test_cmp expect actual
'
-test_expect_success '--rebase fast forward' '
+test_expect_success '--rebase (merge) fast forward' '
git reset --hard before-rebase &&
git checkout -b ff &&
echo another modification >file &&
git commit -m third file &&
git checkout to-rebase &&
- git pull --rebase . ff &&
+ git -c rebase.backend=merge pull --rebase . ff &&
+ test_cmp_rev HEAD ff &&
+
+ # The above only validates the result. Did we actually bypass rebase?
+ git reflog -1 >reflog.actual &&
+ sed "s/^[0-9a-f][0-9a-f]*/OBJID/" reflog.actual >reflog.fuzzy &&
+ echo "OBJID HEAD@{0}: pull --rebase . ff: Fast-forward" >reflog.expected &&
+ test_cmp reflog.expected reflog.fuzzy
+'
+
+test_expect_success '--rebase (am) fast forward' '
+ git reset --hard before-rebase &&
+
+ git -c rebase.backend=am pull --rebase . ff &&
test_cmp_rev HEAD ff &&
# The above only validates the result. Did we actually bypass rebase?
diff --git a/t/t6047-diff3-conflict-markers.sh b/t/t6047-diff3-conflict-markers.sh
index 860542aad0..d383ce8130 100755
--- a/t/t6047-diff3-conflict-markers.sh
+++ b/t/t6047-diff3-conflict-markers.sh
@@ -186,7 +186,7 @@ test_expect_success 'check multiple merge bases' '
)
'
-test_expect_success 'rebase describes fake ancestor base' '
+test_expect_success 'rebase --merge describes parent of commit being picked' '
test_create_repo rebase &&
(
cd rebase &&
@@ -194,7 +194,16 @@ test_expect_success 'rebase describes fake ancestor base' '
test_commit master file &&
git checkout -b side HEAD^ &&
test_commit side file &&
- test_must_fail git -c merge.conflictstyle=diff3 rebase master &&
+ test_must_fail git -c merge.conflictstyle=diff3 rebase --merge master &&
+ grep "||||||| parent of" file
+ )
+'
+
+test_expect_success 'rebase --am describes fake ancestor base' '
+ (
+ cd rebase &&
+ git rebase --abort &&
+ test_must_fail git -c merge.conflictstyle=diff3 rebase --am master &&
grep "||||||| constructed merge base" file
)
'
--
gitgitgadget
^ permalink raw reply related [flat|nested] 161+ messages in thread
* [PATCH 14/15] rebase: make the backend configurable via config setting
2019-12-20 17:09 [PATCH 00/15] rebase: make the default backend configurable Elijah Newren via GitGitGadget
` (12 preceding siblings ...)
2019-12-20 17:09 ` [PATCH 13/15] rebase tests: repeat some tests using the merge backend instead of am Elijah Newren via GitGitGadget
@ 2019-12-20 17:09 ` Elijah Newren via GitGitGadget
2019-12-20 17:09 ` [PATCH 15/15] rebase: change the default backend from "am" to "merge" Elijah Newren via GitGitGadget
` (2 subsequent siblings)
16 siblings, 0 replies; 161+ messages in thread
From: Elijah Newren via GitGitGadget @ 2019-12-20 17:09 UTC (permalink / raw)
To: git
Cc: Johannes.Schindelin, phillip.wood, liu.denton, gitster, plroskin,
Junio C Hamano, Elijah Newren
From: Elijah Newren <newren@gmail.com>
Signed-off-by: Elijah Newren <newren@gmail.com>
---
Documentation/config/rebase.txt | 8 ++++++++
builtin/rebase.c | 31 ++++++++++++++++++++++++-------
2 files changed, 32 insertions(+), 7 deletions(-)
diff --git a/Documentation/config/rebase.txt b/Documentation/config/rebase.txt
index d98e32d812..e6ae30c999 100644
--- a/Documentation/config/rebase.txt
+++ b/Documentation/config/rebase.txt
@@ -5,6 +5,14 @@ rebase.useBuiltin::
is always used. Setting this will emit a warning, to alert any
remaining users that setting this now does nothing.
+rebase.backend::
+ Default backend to use for rebasing. Possible choices are
+ 'am' or 'merge' (note that the merge backend is sometimes also
+ refered to as the interactive backend or the interactive
+ machinery elsewhere in the docs). Also, in the future, if the
+ merge backend gains all remaining capabilities of the am
+ backend, this setting may become unused.
+
rebase.stat::
Whether to show a diffstat of what changed upstream since the last
rebase. False by default.
diff --git a/builtin/rebase.c b/builtin/rebase.c
index ab9e16b206..eaa5eac59e 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -60,6 +60,7 @@ enum empty_type {
struct rebase_options {
enum rebase_type type;
enum empty_type empty;
+ const char *default_backend;
const char *state_dir;
struct commit *upstream;
const char *upstream_name;
@@ -103,6 +104,7 @@ struct rebase_options {
#define REBASE_OPTIONS_INIT { \
.type = REBASE_UNSPECIFIED, \
.empty = EMPTY_UNSPECIFIED, \
+ .default_backend = "am", \
.flags = REBASE_NO_QUIET, \
.git_am_opts = ARGV_ARRAY_INIT, \
.git_format_patch_opt = STRBUF_INIT \
@@ -1298,6 +1300,10 @@ static int rebase_config(const char *var, const char *value, void *data)
return 0;
}
+ if (!strcmp(var, "rebase.backend")) {
+ return git_config_string(&opts->default_backend, var, value);
+ }
+
return git_default_config(var, value, data);
}
@@ -1932,9 +1938,23 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
if (strcmp(options.git_am_opts.argv[i], "-q"))
break;
- if (is_interactive(&options) && i >= 0)
- die(_("cannot combine am options with either "
- "interactive or merge options"));
+ if (i >= 0) {
+ if (is_interactive(&options))
+ die(_("cannot combine am options with either "
+ "interactive or merge options"));
+ else
+ options.type = REBASE_AM;
+ }
+ }
+
+ if (options.type == REBASE_UNSPECIFIED) {
+ if (!strcmp(options.default_backend, "merge"))
+ options.type = REBASE_MERGE;
+ else if (!strcmp(options.default_backend, "am"))
+ options.type = REBASE_AM;
+ else
+ die(_("Unknown rebase backend: %s"),
+ options.default_backend);
}
switch (options.type) {
@@ -1947,10 +1967,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
options.state_dir = apply_dir();
break;
default:
- /* the default rebase backend is `--am` */
- options.type = REBASE_AM;
- options.state_dir = apply_dir();
- break;
+ BUG("options.type was just set above; should be unreachable.");
}
if (options.empty == EMPTY_UNSPECIFIED) {
--
gitgitgadget
^ permalink raw reply related [flat|nested] 161+ messages in thread
* [PATCH 15/15] rebase: change the default backend from "am" to "merge"
2019-12-20 17:09 [PATCH 00/15] rebase: make the default backend configurable Elijah Newren via GitGitGadget
` (13 preceding siblings ...)
2019-12-20 17:09 ` [PATCH 14/15] rebase: make the backend configurable via config setting Elijah Newren via GitGitGadget
@ 2019-12-20 17:09 ` Elijah Newren via GitGitGadget
2019-12-20 18:51 ` [PATCH 00/15] rebase: make the default backend configurable Alban Gruin
2019-12-23 18:49 ` [PATCH v2 " Elijah Newren via GitGitGadget
16 siblings, 0 replies; 161+ messages in thread
From: Elijah Newren via GitGitGadget @ 2019-12-20 17:09 UTC (permalink / raw)
To: git
Cc: Johannes.Schindelin, phillip.wood, liu.denton, gitster, plroskin,
Junio C Hamano, Elijah Newren
From: Elijah Newren <newren@gmail.com>
The am-backend drops information and thus limits what we can do:
* lack of full tree information from the original commits means we
cannot do directory rename detection and warn users that they might
want to move some of their new files that they placed in old
directories to prevent their becoming orphaned.[1]
* reduction in context from only having a few lines beyond those
changed means that when context lines are non-unique we can apply
patches incorrectly.[2]
* lack of access to original commits means that conflict marker
annotation has less information available.
Also, the merge/interactive backend have far more abilities, appear to
currently have a slight performance advantage[3] and have room for more
optimizations than the am backend[4] (and work is underway to take
advantage of some of those possibilities).
[1] https://lore.kernel.org/git/xmqqh8jeh1id.fsf@gitster-ct.c.googlers.com/
[2] https://lore.kernel.org/git/CABPp-BGiu2nVMQY_t-rnFR5GQUz_ipyEE8oDocKeO+h+t4Mn4A@mail.gmail.com/
[3] https://public-inbox.org/git/CABPp-BF=ev03WgODk6TMQmuNoatg2kiEe5DR__gJ0OTVqHSnfQ@mail.gmail.com/
[4] https://lore.kernel.org/git/CABPp-BGh7yW69QwxQb13K0HM38NKmQif3A6C6UULEKYnkEJ5vA@mail.gmail.com/
Signed-off-by: Elijah Newren <newren@gmail.com>
---
Documentation/git-rebase.txt | 2 +-
builtin/rebase.c | 4 ++--
t/t5520-pull.sh | 10 ++++++----
t/t9106-git-svn-commit-diff-clobber.sh | 3 ++-
4 files changed, 11 insertions(+), 8 deletions(-)
diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index cf1ac2e359..e819889a31 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -309,7 +309,7 @@ See also INCOMPATIBLE OPTIONS below.
--merge::
Use merging strategies to rebase. When the recursive (default) merge
strategy is used, this allows rebase to be aware of renames on the
- upstream side.
+ upstream side. This is the default.
+
Note that a rebase merge works by replaying each commit from the working
branch on top of the <upstream> branch. Because of this, when a merge
diff --git a/builtin/rebase.c b/builtin/rebase.c
index eaa5eac59e..1c3d0b97d5 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -104,7 +104,7 @@ struct rebase_options {
#define REBASE_OPTIONS_INIT { \
.type = REBASE_UNSPECIFIED, \
.empty = EMPTY_UNSPECIFIED, \
- .default_backend = "am", \
+ .default_backend = "merge", \
.flags = REBASE_NO_QUIET, \
.git_am_opts = ARGV_ARRAY_INIT, \
.git_format_patch_opt = STRBUF_INIT \
@@ -1949,7 +1949,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
if (options.type == REBASE_UNSPECIFIED) {
if (!strcmp(options.default_backend, "merge"))
- options.type = REBASE_MERGE;
+ imply_interactive(&options, "--merge");
else if (!strcmp(options.default_backend, "am"))
options.type = REBASE_AM;
else
diff --git a/t/t5520-pull.sh b/t/t5520-pull.sh
index 3fff6a06fa..4f9e7f7ff6 100755
--- a/t/t5520-pull.sh
+++ b/t/t5520-pull.sh
@@ -340,7 +340,7 @@ test_expect_success '--rebase with conflicts shows advice' '
test_tick &&
git commit -m "Create conflict" seq.txt &&
test_must_fail git pull --rebase . seq 2>err >out &&
- test_i18ngrep "Resolve all conflicts manually" out
+ test_i18ngrep "Resolve all conflicts manually" err
'
test_expect_success 'failed --rebase shows advice' '
@@ -354,7 +354,7 @@ test_expect_success 'failed --rebase shows advice' '
git checkout -f -b fails-to-rebase HEAD^ &&
test_commit v2-without-cr file "2" file2-lf &&
test_must_fail git pull --rebase . diverging 2>err >out &&
- test_i18ngrep "Resolve all conflicts manually" out
+ test_i18ngrep "Resolve all conflicts manually" err
'
test_expect_success '--rebase fails with multiple branches' '
@@ -774,8 +774,10 @@ test_expect_success 'git pull --rebase does not reapply old patches' '
(
cd dst &&
test_must_fail git pull --rebase &&
- find .git/rebase-apply -name "000*" >patches &&
- test_line_count = 1 patches
+ cat .git/rebase-merge/done .git/rebase-merge/git-rebase-todo >work &&
+ grep -v -e \# -e ^$ work >patches &&
+ test_line_count = 1 patches &&
+ rm -f work
)
'
diff --git a/t/t9106-git-svn-commit-diff-clobber.sh b/t/t9106-git-svn-commit-diff-clobber.sh
index dbe8deac0d..aec45bca3b 100755
--- a/t/t9106-git-svn-commit-diff-clobber.sh
+++ b/t/t9106-git-svn-commit-diff-clobber.sh
@@ -92,7 +92,8 @@ test_expect_success 'multiple dcommit from git svn will not clobber svn' "
test_expect_success 'check that rebase really failed' '
- test -d .git/rebase-apply
+ git status >output &&
+ grep currently.rebasing output
'
test_expect_success 'resolve, continue the rebase and dcommit' "
--
gitgitgadget
^ permalink raw reply related [flat|nested] 161+ messages in thread
* Re: [PATCH 00/15] rebase: make the default backend configurable
2019-12-20 17:09 [PATCH 00/15] rebase: make the default backend configurable Elijah Newren via GitGitGadget
` (14 preceding siblings ...)
2019-12-20 17:09 ` [PATCH 15/15] rebase: change the default backend from "am" to "merge" Elijah Newren via GitGitGadget
@ 2019-12-20 18:51 ` Alban Gruin
2019-12-20 18:55 ` Elijah Newren
2019-12-23 18:49 ` [PATCH v2 " Elijah Newren via GitGitGadget
16 siblings, 1 reply; 161+ messages in thread
From: Alban Gruin @ 2019-12-20 18:51 UTC (permalink / raw)
To: Elijah Newren via GitGitGadget, git
Cc: Johannes.Schindelin, phillip.wood, liu.denton, gitster, plroskin
Hi Elijah,
Le 20/12/2019 à 18:09, Elijah Newren via GitGitGadget a écrit :
> This series does a lot of work around making the default rebase backend
> configurable:
>
> * provide configurability of handling empty commits and iron out
> differences between backends
> * increase/fix capabilities of the merge/interactive backend to make it
> workable as the default
> * document the remaining differences in backends more thoroughly
> * add an --am option for explicitly requesting the am-backend
> * extend merge/interactive backend testing to put it on par with the am
> backend
> * add a 'rebase.backend' config option and:
> * switch the rebase.backend default value from 'am' to 'merge'.
>
> Areas I'd like reviewers to focus, in priority order:
>
> * Patch 15 (Are we ready to switch the default backend? Or should we leave
> this patch out for now? Junio suggested we may be ready or at least are
> close[1])
> * Patch 1 (Does my empty handling make sense? Do others agree I fixed
> --keep-empty, or do they view it as breaking it?)
> * Patch 8 (Do the updates to the documentation of behavioral differences
> make sense? Is it too long?) * Patch 11 (okay to change the
> git-completion shell prompt slightly, especially in light of patches
> 15 & 16? We did a prompt change previously when we merged the merge
> backend with the interactive one, so I assume so, but just want to
> make sure people have a chance to chime in.)
>
>
>
> If it's too soon to switch from the 'am' to 'merge' backend, we can just
> drop the last patch and I'll resubmit it later. I generated this series
> mostly through switching the default first and then watching what broke, but
> moved the patch to the end to make it easy to drop.
>
> Briefly, reasons for switching the default backend boil down to the fact
> that the am-backend drops information and thus limits what it can do. This
> manifests in different ways:
>
> * lack of tree information that would allow us to warn users that new files
> in old directories might want to move along with the other files that
> were renamed with those directories[1]
> * incorrect application of patches in the presence of non-unique context
> lines[2], which could be avoided with access to the original files
> involved.
> * less information available to annotate conflict markers (since am creates
> fake ancestors and commits on top of them, and doesn't have access to the
> original commits)
>
> [1] https://lore.kernel.org/git/xmqqa78d2qmk.fsf@gitster-ct.c.googlers.com/
> [2] https://lore.kernel.org/git/xmqqh8jeh1id.fsf@gitster-ct.c.googlers.com/
> [3]
> https://lore.kernel.org/git/CABPp-BGiu2nVMQY_t-rnFR5GQUz_ipyEE8oDocKeO+h+t4Mn4A@mail.gmail.com/
>
> Elijah Newren (15):
> rebase: extend the options for handling of empty commits
> t3406: simplify an already simple test
> rebase, sequencer: remove the broken GIT_QUIET handling
> rebase: make sure to pass along the quiet flag to the sequencer
> rebase: fix handling of restrict_revision
> t3432: make these tests work with either am or merge backends
> rebase: allow more types of rebases to fast-forward
> git-rebase.txt: add more details about behavioral differences of
> backends
> rebase: move incompatibility checks between backend options a bit
> earlier
> rebase: add an --am option
> contrib: change the prompt for am-based rebases
> rebase tests: mark tests specific to the am-backend with --am
> rebase tests: repeat some tests using the merge backend instead of am
> rebase: make the backend configurable via config setting
> rebase: change the default backend from "am" to "merge"
>
> Documentation/config/rebase.txt | 8 ++
> Documentation/git-rebase.txt | 150 ++++++++++++++++----
> builtin/rebase.c | 181 +++++++++++++++++++-----
> contrib/completion/git-prompt.sh | 2 +-
> rebase-interactive.c | 4 +-
> rebase-interactive.h | 2 +-
> sequencer.c | 80 ++++++++---
> sequencer.h | 6 +-
> t/t3400-rebase.sh | 36 ++++-
> t/t3401-rebase-and-am-rename.sh | 4 +-
> t/t3404-rebase-interactive.sh | 2 +-
> t/t3406-rebase-message.sh | 19 ++-
> t/t3407-rebase-abort.sh | 6 +-
> t/t3420-rebase-autostash.sh | 2 +-
> t/t3421-rebase-topology-linear.sh | 4 +-
> t/t3424-rebase-empty.sh | 89 ++++++++++++
> t/t3425-rebase-topology-merges.sh | 8 +-
> t/t3427-rebase-subtree.sh | 16 ++-
> t/t3432-rebase-fast-forward.sh | 59 ++++----
> t/t3433-rebase-options-compatibility.sh | 13 +-
> t/t5407-post-rewrite-hook.sh | 12 +-
> t/t5520-pull.sh | 27 +++-
> t/t6047-diff3-conflict-markers.sh | 13 +-
> t/t7512-status-help.sh | 12 +-
> t/t9106-git-svn-commit-diff-clobber.sh | 3 +-
> t/t9903-bash-prompt.sh | 6 +-
> 26 files changed, 582 insertions(+), 182 deletions(-)
> create mode 100755 t/t3424-rebase-empty.sh
>
>
> base-commit: 12029dc57db23baef008e77db1909367599210ee
> Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-679%2Fnewren%2Frebase-fixes-v1
> Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-679/newren/rebase-fixes-v1
> Pull-Request: https://github.com/git/git/pull/679
>
BTW, I have investigated on the performance regression when split-index
is enabled, I’ll give my conclusions next week.
Cheers,
Alban
^ permalink raw reply [flat|nested] 161+ messages in thread
* Re: [PATCH 00/15] rebase: make the default backend configurable
2019-12-20 18:51 ` [PATCH 00/15] rebase: make the default backend configurable Alban Gruin
@ 2019-12-20 18:55 ` Elijah Newren
0 siblings, 0 replies; 161+ messages in thread
From: Elijah Newren @ 2019-12-20 18:55 UTC (permalink / raw)
To: Alban Gruin
Cc: Elijah Newren via GitGitGadget, Git Mailing List,
Johannes Schindelin, Phillip Wood, Denton Liu, Junio C Hamano,
Pavel Roskin
Hi Alban,
On Fri, Dec 20, 2019 at 10:53 AM Alban Gruin <alban.gruin@gmail.com> wrote:
>
> Hi Elijah,
>
> Le 20/12/2019 à 18:09, Elijah Newren via GitGitGadget a écrit :
> > This series does a lot of work around making the default rebase backend
> > configurable:
> >
>
> BTW, I have investigated on the performance regression when split-index
> is enabled, I’ll give my conclusions next week.
>
> Cheers,
> Alban
Nice!
^ permalink raw reply [flat|nested] 161+ messages in thread
* Re: [PATCH 01/15] rebase: extend the options for handling of empty commits
2019-12-20 17:09 ` [PATCH 01/15] rebase: extend the options for handling of empty commits Elijah Newren via GitGitGadget
@ 2019-12-20 21:29 ` Junio C Hamano
2019-12-21 0:32 ` Elijah Newren
0 siblings, 1 reply; 161+ messages in thread
From: Junio C Hamano @ 2019-12-20 21:29 UTC (permalink / raw)
To: Elijah Newren via GitGitGadget
Cc: git, Johannes.Schindelin, phillip.wood, liu.denton, plroskin,
Elijah Newren
"Elijah Newren via GitGitGadget" <gitgitgadget@gmail.com> writes:
> From: Elijah Newren <newren@gmail.com>
>
> Extend the interactive machinery with the ability to handle the full
> spread of options for how to handle commits that either start or become
> empty (by "become empty" I mean the changes in a commit are a subset of
> changes that exist upstream, so the net effect of applying the commit is
> no changes). Introduce a new command line flag for selecting the
> desired behavior:
> --empty={drop,keep,ask}
> with the definitions:
> drop: drop empty commits
> keep: keep empty commits
> ask: provide the user a chance to interact and pick what to do with
> empty commits on a case-by-case basis
This looks like a logical and natural extension of the --keep-empty
option.
After seeing the stress on "empty from the beginning and ending up
to be empty" in the description, I somehow expected that we may be
able to specify what happens to the empty commit separately, but
that does not seem to be what the patch is about, which was somewhat
disappointing.
> +static long parse_empty_value(const char *value)
> +{
> + if (!value)
> + return EMPTY_UNSPECIFIED;
> + else if (!strcasecmp(value, "drop"))
> + return EMPTY_DROP;
> + else if (!strcasecmp(value, "keep"))
> + return EMPTY_KEEP;
> + else if (!strcasecmp(value, "ask"))
> + return EMPTY_ASK;
Not an error but just silently ignored?
> + return EMPTY_UNSPECIFIED;
> +}
> +
> +static int parse_opt_empty(const struct option *opt, const char *arg, int unset)
> +{
> + struct rebase_options *options = opt->value;
> + long value = parse_empty_value(arg);
Ahh, OK.
Wouldn't it be better to make the variable and the parsing helper
function of type "enum empty_type", not "long", just like the field
in the rebase_options struct?
> + BUG_ON_OPT_NEG(unset);
> + if (value < 0)
> + return error(_("option empty accepts \"drop\", "
> + "\"keep\", and \"ask\""));
> +
> + options->empty = value;
> + return 0;
> +}
^ permalink raw reply [flat|nested] 161+ messages in thread
* Re: [PATCH 03/15] rebase, sequencer: remove the broken GIT_QUIET handling
2019-12-20 17:09 ` [PATCH 03/15] rebase, sequencer: remove the broken GIT_QUIET handling Elijah Newren via GitGitGadget
@ 2019-12-20 21:34 ` Junio C Hamano
0 siblings, 0 replies; 161+ messages in thread
From: Junio C Hamano @ 2019-12-20 21:34 UTC (permalink / raw)
To: Elijah Newren via GitGitGadget
Cc: git, Johannes.Schindelin, phillip.wood, liu.denton, plroskin,
Elijah Newren
"Elijah Newren via GitGitGadget" <gitgitgadget@gmail.com> writes:
> From: Elijah Newren <newren@gmail.com>
>
> The GIT_QUIET environment variable was used to signal the non-am
> backends that the rebase should perform quietly. The preserve-merges
> backend does not make use of the quiet flag anywhere (other than to
> write out its state whenever it writes state), and this mechanism was
> broken in the conversion from shell to C. Since this environment
> variable was specifically designed for scripts and the only backend that
> would still use it is no longer a script, just gut this code.
Nice to see a useless code go ;-)
^ permalink raw reply [flat|nested] 161+ messages in thread
* Re: [PATCH 05/15] rebase: fix handling of restrict_revision
2019-12-20 17:09 ` [PATCH 05/15] rebase: fix handling of restrict_revision Elijah Newren via GitGitGadget
@ 2019-12-20 21:37 ` Junio C Hamano
0 siblings, 0 replies; 161+ messages in thread
From: Junio C Hamano @ 2019-12-20 21:37 UTC (permalink / raw)
To: Elijah Newren via GitGitGadget
Cc: git, Johannes.Schindelin, phillip.wood, liu.denton, plroskin,
Elijah Newren
"Elijah Newren via GitGitGadget" <gitgitgadget@gmail.com> writes:
> From: Elijah Newren <newren@gmail.com>
>
> restrict_revision in the original shell script was an excluded revision
> range. It is also treated that way by the am-backend. In the
> conversion from shell to C (see commit 6ab54d17be3f ("rebase -i:
> implement the logic to initialize $revisions in C", 2018-08-28)), the
> interactive-backend accidentally treated it as a positive revision
> rather than a negated one.
>
> This was missed as there were no tests in the testsuite that tested an
> interactive rebase with fork-point behavior.
Thanks.
> Signed-off-by: Elijah Newren <newren@gmail.com>
> ---
> builtin/rebase.c | 4 ++--
> t/t3400-rebase.sh | 20 +++++++++++++++++++-
> 2 files changed, 21 insertions(+), 3 deletions(-)
>
> diff --git a/builtin/rebase.c b/builtin/rebase.c
> index cc8f3f008f..b320bb3a30 100644
> --- a/builtin/rebase.c
> +++ b/builtin/rebase.c
> @@ -364,8 +364,8 @@ static int do_interactive_rebase(struct rebase_options *opts, unsigned flags)
>
> argv_array_pushl(&make_script_args, "", revisions, NULL);
> if (opts->restrict_revision)
> - argv_array_push(&make_script_args,
> - oid_to_hex(&opts->restrict_revision->object.oid));
> + argv_array_pushf(&make_script_args, "^%s",
> + oid_to_hex(&opts->restrict_revision->object.oid));
>
> ret = sequencer_make_script(the_repository, &todo_list.buf,
> make_script_args.argc, make_script_args.argv,
> diff --git a/t/t3400-rebase.sh b/t/t3400-rebase.sh
> index 79762b989a..71fd6396cd 100755
> --- a/t/t3400-rebase.sh
> +++ b/t/t3400-rebase.sh
> @@ -165,11 +165,29 @@ test_expect_success 'rebase works with format.useAutoBase' '
> git rebase master
> '
>
> -test_expect_success 'default to common base in @{upstream}s reflog if no upstream arg' '
> +test_expect_success 'default to common base in @{upstream}s reflog if no upstream arg (--merge)' '
> git checkout -b default-base master &&
> git checkout -b default topic &&
> git config branch.default.remote . &&
> git config branch.default.merge refs/heads/default-base &&
> + git rebase --merge &&
> + git rev-parse --verify default-base >expect &&
> + git rev-parse default~1 >actual &&
> + test_cmp expect actual &&
> + git checkout default-base &&
> + git reset --hard HEAD^ &&
> + git checkout default &&
> + git rebase --merge &&
> + git rev-parse --verify default-base >expect &&
> + git rev-parse default~1 >actual &&
> + test_cmp expect actual
> +'
> +
> +test_expect_success 'default to common base in @{upstream}s reflog if no upstream arg' '
> + git checkout -B default-base master &&
> + git checkout -B default topic &&
> + git config branch.default.remote . &&
> + git config branch.default.merge refs/heads/default-base &&
> git rebase &&
> git rev-parse --verify default-base >expect &&
> git rev-parse default~1 >actual &&
^ permalink raw reply [flat|nested] 161+ messages in thread
* Re: [PATCH 11/15] contrib: change the prompt for am-based rebases
2019-12-20 17:09 ` [PATCH 11/15] contrib: change the prompt for am-based rebases Elijah Newren via GitGitGadget
@ 2019-12-20 23:07 ` SZEDER Gábor
2019-12-21 0:17 ` Elijah Newren
0 siblings, 1 reply; 161+ messages in thread
From: SZEDER Gábor @ 2019-12-20 23:07 UTC (permalink / raw)
To: Elijah Newren via GitGitGadget
Cc: git, Johannes.Schindelin, phillip.wood, liu.denton, gitster,
plroskin, Elijah Newren
The subject prefix for this file is most of the time 'bash prompt' or
'git-prompt'.
On Fri, Dec 20, 2019 at 05:09:44PM +0000, Elijah Newren via GitGitGadget wrote:
> From: Elijah Newren <newren@gmail.com>
>
> The prompt for am-based rebases was REBASE, while for interactive-based
> rebases was REBASE-i. A while ago, we switched merge-based rebases from
> using REBASE-m to REBASE-i via re-implementing the merge backend based
> on the interactive backend. We will soon be changing the default rebase
> backend to the interactive one, meaning the default prompt will be
> REBASE-i rather than REBASE. We have also noted in the documentation
> that currently am-specific options will be implemented in the
> interactive backend, and even the --am flag may eventually imply an
> interactive-based rebase. As such, change the prompt for an am-based
> rebase from REBASE to REBASE-a.
I had a bit of a hard time following which rebase variant is
implemented with which backend and when it was changed, and... etc.
Makes me wonder: do we really need those "-i", "-m" or "-a" suffixes?
What benefit do they bring? Why don't we just say "REBASE" in the
prompt?
> Signed-off-by: Elijah Newren <newren@gmail.com>
> ---
> contrib/completion/git-prompt.sh | 2 +-
> t/t9903-bash-prompt.sh | 6 +++---
> 2 files changed, 4 insertions(+), 4 deletions(-)
>
> diff --git a/contrib/completion/git-prompt.sh b/contrib/completion/git-prompt.sh
> index 1d510cd47b..3c81099d60 100644
> --- a/contrib/completion/git-prompt.sh
> +++ b/contrib/completion/git-prompt.sh
> @@ -440,7 +440,7 @@ __git_ps1 ()
> __git_eread "$g/rebase-apply/last" total
> if [ -f "$g/rebase-apply/rebasing" ]; then
> __git_eread "$g/rebase-apply/head-name" b
> - r="|REBASE"
> + r="|REBASE-a"
> elif [ -f "$g/rebase-apply/applying" ]; then
> r="|AM"
> else
> diff --git a/t/t9903-bash-prompt.sh b/t/t9903-bash-prompt.sh
> index 88bc733ad6..8da5b1aee2 100755
> --- a/t/t9903-bash-prompt.sh
> +++ b/t/t9903-bash-prompt.sh
> @@ -189,11 +189,11 @@ test_expect_success 'prompt - rebase merge' '
> test_cmp expected "$actual"
> '
>
> -test_expect_success 'prompt - rebase' '
> - printf " (b2|REBASE 1/3)" >expected &&
> +test_expect_success 'prompt - rebase am' '
> + printf " (b2|REBASE-a 1/3)" >expected &&
> git checkout b2 &&
> test_when_finished "git checkout master" &&
> - test_must_fail git rebase b1 b2 &&
> + test_must_fail git rebase --am b1 b2 &&
> test_when_finished "git rebase --abort" &&
> __git_ps1 >"$actual" &&
> test_cmp expected "$actual"
> --
> gitgitgadget
>
^ permalink raw reply [flat|nested] 161+ messages in thread
* Re: [PATCH 11/15] contrib: change the prompt for am-based rebases
2019-12-20 23:07 ` SZEDER Gábor
@ 2019-12-21 0:17 ` Elijah Newren
0 siblings, 0 replies; 161+ messages in thread
From: Elijah Newren @ 2019-12-21 0:17 UTC (permalink / raw)
To: SZEDER Gábor
Cc: Elijah Newren via GitGitGadget, Git Mailing List,
Johannes Schindelin, Phillip Wood, Denton Liu, Junio C Hamano,
Pavel Roskin
On Fri, Dec 20, 2019 at 3:07 PM SZEDER Gábor <szeder.dev@gmail.com> wrote:
>
> The subject prefix for this file is most of the time 'bash prompt' or
> 'git-prompt'.
>
> On Fri, Dec 20, 2019 at 05:09:44PM +0000, Elijah Newren via GitGitGadget wrote:
> > From: Elijah Newren <newren@gmail.com>
> >
> > The prompt for am-based rebases was REBASE, while for interactive-based
> > rebases was REBASE-i. A while ago, we switched merge-based rebases from
> > using REBASE-m to REBASE-i via re-implementing the merge backend based
> > on the interactive backend. We will soon be changing the default rebase
> > backend to the interactive one, meaning the default prompt will be
> > REBASE-i rather than REBASE. We have also noted in the documentation
> > that currently am-specific options will be implemented in the
> > interactive backend, and even the --am flag may eventually imply an
> > interactive-based rebase. As such, change the prompt for an am-based
> > rebase from REBASE to REBASE-a.
>
> I had a bit of a hard time following which rebase variant is
> implemented with which backend and when it was changed, and... etc.
Sorry about that. I could fix it up, but...
> Makes me wonder: do we really need those "-i", "-m" or "-a" suffixes?
> What benefit do they bring? Why don't we just say "REBASE" in the
> prompt?
Ooh, I like this idea. A lot easier to explain in the commit message
and probably less confusing for users as well.
^ permalink raw reply [flat|nested] 161+ messages in thread
* Re: [PATCH 01/15] rebase: extend the options for handling of empty commits
2019-12-20 21:29 ` Junio C Hamano
@ 2019-12-21 0:32 ` Elijah Newren
2019-12-21 18:52 ` Elijah Newren
2019-12-21 23:49 ` Junio C Hamano
0 siblings, 2 replies; 161+ messages in thread
From: Elijah Newren @ 2019-12-21 0:32 UTC (permalink / raw)
To: Junio C Hamano
Cc: Elijah Newren via GitGitGadget, Git Mailing List,
Johannes Schindelin, Phillip Wood, Denton Liu, Pavel Roskin
On Fri, Dec 20, 2019 at 1:29 PM Junio C Hamano <gitster@pobox.com> wrote:
>
> "Elijah Newren via GitGitGadget" <gitgitgadget@gmail.com> writes:
>
> > From: Elijah Newren <newren@gmail.com>
> >
> > Extend the interactive machinery with the ability to handle the full
> > spread of options for how to handle commits that either start or become
> > empty (by "become empty" I mean the changes in a commit are a subset of
> > changes that exist upstream, so the net effect of applying the commit is
> > no changes). Introduce a new command line flag for selecting the
> > desired behavior:
> > --empty={drop,keep,ask}
> > with the definitions:
> > drop: drop empty commits
> > keep: keep empty commits
> > ask: provide the user a chance to interact and pick what to do with
> > empty commits on a case-by-case basis
>
> This looks like a logical and natural extension of the --keep-empty
> option.
>
> After seeing the stress on "empty from the beginning and ending up
> to be empty" in the description, I somehow expected that we may be
> able to specify what happens to the empty commit separately, but
> that does not seem to be what the patch is about, which was somewhat
> disappointing.
My main reason for stressing both was that I've commits and code that
talked about "empty" commits, but which often only dealt with one of
the two types (sometimes when I would expect both to have been
addressed), so I was just trying to be careful in my explanation of
what it was about.
If wanted, we could implement finer control, but I don't know what the
UI would look like or what the range of options we might want. Do we
want to mix all possible permutations and have two flags, e.g.
--become-empty=drop --start-empty=keep or --start-empty=ask
--become-empty=keep? Would --empty=<whatever> imply both
--become-empty=<whatever> and --start-empty=<whatever> or would users
need to specify both? Do we think all the permutations make sense, or
would we want to limit users to some subset?
The current choice of three covered the things that I had wanted in
the past, and which I think made for good defaults for various
different flags, so I just implemented it. We could always extend it
later.
>
> > +static long parse_empty_value(const char *value)
> > +{
> > + if (!value)
> > + return EMPTY_UNSPECIFIED;
> > + else if (!strcasecmp(value, "drop"))
> > + return EMPTY_DROP;
> > + else if (!strcasecmp(value, "keep"))
> > + return EMPTY_KEEP;
> > + else if (!strcasecmp(value, "ask"))
> > + return EMPTY_ASK;
>
> Not an error but just silently ignored?
Oops, I'll switch it to an error.
> > + return EMPTY_UNSPECIFIED;
> > +}
> > +
>
> > +static int parse_opt_empty(const struct option *opt, const char *arg, int unset)
> > +{
> > + struct rebase_options *options = opt->value;
> > + long value = parse_empty_value(arg);
>
> Ahh, OK.
>
> Wouldn't it be better to make the variable and the parsing helper
> function of type "enum empty_type", not "long", just like the field
> in the rebase_options struct?
Indeed, I'll fix this up.
^ permalink raw reply [flat|nested] 161+ messages in thread
* Re: [PATCH 01/15] rebase: extend the options for handling of empty commits
2019-12-21 0:32 ` Elijah Newren
@ 2019-12-21 18:52 ` Elijah Newren
2019-12-21 23:49 ` Junio C Hamano
1 sibling, 0 replies; 161+ messages in thread
From: Elijah Newren @ 2019-12-21 18:52 UTC (permalink / raw)
To: Junio C Hamano
Cc: Elijah Newren via GitGitGadget, Git Mailing List,
Johannes Schindelin, Phillip Wood, Denton Liu, Pavel Roskin
On Fri, Dec 20, 2019 at 4:32 PM Elijah Newren <newren@gmail.com> wrote:
>
> On Fri, Dec 20, 2019 at 1:29 PM Junio C Hamano <gitster@pobox.com> wrote:
> >
> > "Elijah Newren via GitGitGadget" <gitgitgadget@gmail.com> writes:
> >
> > > +static long parse_empty_value(const char *value)
> > > +{
> > > + if (!value)
> > > + return EMPTY_UNSPECIFIED;
> > > + else if (!strcasecmp(value, "drop"))
> > > + return EMPTY_DROP;
> > > + else if (!strcasecmp(value, "keep"))
> > > + return EMPTY_KEEP;
> > > + else if (!strcasecmp(value, "ask"))
> > > + return EMPTY_ASK;
> >
> > Not an error but just silently ignored?
>
> Oops, I'll switch it to an error.
By the way, diff.c's parse_algorithm_value() and
diff_opt_diff_algorithm() behave the same way, where
parse_algorithm_value() will return a negative value if it's not one
of the valid known cases, and diff_opt_diff_algorithm() checks for
negative and throws an error. Should I also modify those to put the
error in parse_algorithm_value()? (Not part of this series, but as a
consistency cleanup since it clearly surprised you and even I forgot
that I had the calling function returning the error when I copied that
style?)
^ permalink raw reply [flat|nested] 161+ messages in thread
* Re: [PATCH 01/15] rebase: extend the options for handling of empty commits
2019-12-21 0:32 ` Elijah Newren
2019-12-21 18:52 ` Elijah Newren
@ 2019-12-21 23:49 ` Junio C Hamano
1 sibling, 0 replies; 161+ messages in thread
From: Junio C Hamano @ 2019-12-21 23:49 UTC (permalink / raw)
To: Elijah Newren
Cc: Elijah Newren via GitGitGadget, Git Mailing List,
Johannes Schindelin, Phillip Wood, Denton Liu, Pavel Roskin
Elijah Newren <newren@gmail.com> writes:
>> > +static long parse_empty_value(const char *value)
>> > +{
>> > + if (!value)
>> > + return EMPTY_UNSPECIFIED;
>> > + else if (!strcasecmp(value, "drop"))
>> > + return EMPTY_DROP;
>> > + else if (!strcasecmp(value, "keep"))
>> > + return EMPTY_KEEP;
>> > + else if (!strcasecmp(value, "ask"))
>> > + return EMPTY_ASK;
>>
>> Not an error but just silently ignored?
>
> Oops, I'll switch it to an error.
Not necessarily (see "Ahh, OK" below).
>> > + return EMPTY_UNSPECIFIED;
>> > +}
>> > +
>>
>> > +static int parse_opt_empty(const struct option *opt, const char *arg, int unset)
>> > +{
>> > + struct rebase_options *options = opt->value;
>> > + long value = parse_empty_value(arg);
>>
>> Ahh, OK.
>>
>> Wouldn't it be better to make the variable and the parsing helper
>> function of type "enum empty_type", not "long", just like the field
>> in the rebase_options struct?
>
> Indeed, I'll fix this up.
Thanks. I was wondering if there is something subtle I didn't see
going on around here.
^ permalink raw reply [flat|nested] 161+ messages in thread
* Re: [PATCH 06/15] t3432: make these tests work with either am or merge backends
2019-12-20 17:09 ` [PATCH 06/15] t3432: make these tests work with either am or merge backends Elijah Newren via GitGitGadget
@ 2019-12-22 5:11 ` Denton Liu
2019-12-23 17:17 ` Elijah Newren
0 siblings, 1 reply; 161+ messages in thread
From: Denton Liu @ 2019-12-22 5:11 UTC (permalink / raw)
To: Elijah Newren via GitGitGadget
Cc: git, Johannes.Schindelin, phillip.wood, gitster, plroskin, Elijah Newren
Hi Elijah,
On Fri, Dec 20, 2019 at 05:09:39PM +0000, Elijah Newren via GitGitGadget wrote:
> diff --git a/t/t3432-rebase-fast-forward.sh b/t/t3432-rebase-fast-forward.sh
> index 92f95b57da..3879a43fa0 100755
> --- a/t/t3432-rebase-fast-forward.sh
> +++ b/t/t3432-rebase-fast-forward.sh
> @@ -44,19 +44,16 @@ test_rebase_same_head_ () {
> test_expect_$status "git rebase$flag $* with $changes is $what with $cmp HEAD" "
> oldhead=\$(git rev-parse HEAD) &&
> test_when_finished 'git reset --hard \$oldhead' &&
> + cp .git/logs/HEAD expect &&
> git rebase$flag $* >stdout &&
> if test $what = work
> then
> - # Must check this case first, for 'is up to
> - # date, rebase forced[...]rewinding head' cases
> - test_i18ngrep 'rewinding head' stdout
> + wc -l .git/logs/HEAD >old &&
> + wc -l .git/logs/HEAD >new &&
Are the above two lines some yet-to-be cleaned up leftovers? I can't see
where `old` and `new` are being used elsewhere.
> + test_line_count '-gt' $(($old + 2)) .git/logs/HEAD
> elif test $what = noop
> then
> - test_i18ngrep 'is up to date' stdout &&
> - test_i18ngrep ! 'rebase forced' stdout
> - elif test $what = noop-force
> - then
> - test_i18ngrep 'is up to date, rebase forced' stdout
> + test_cmp expect .git/logs/HEAD
> fi &&
> newhead=\$(git rev-parse HEAD) &&
> if test $cmp = same
^ permalink raw reply [flat|nested] 161+ messages in thread
* Re: [PATCH 06/15] t3432: make these tests work with either am or merge backends
2019-12-22 5:11 ` Denton Liu
@ 2019-12-23 17:17 ` Elijah Newren
0 siblings, 0 replies; 161+ messages in thread
From: Elijah Newren @ 2019-12-23 17:17 UTC (permalink / raw)
To: Denton Liu
Cc: Elijah Newren via GitGitGadget, Git Mailing List,
Johannes Schindelin, Phillip Wood, Junio C Hamano, Pavel Roskin
On Sat, Dec 21, 2019 at 9:11 PM Denton Liu <liu.denton@gmail.com> wrote:
>
> Hi Elijah,
>
> On Fri, Dec 20, 2019 at 05:09:39PM +0000, Elijah Newren via GitGitGadget wrote:
> > diff --git a/t/t3432-rebase-fast-forward.sh b/t/t3432-rebase-fast-forward.sh
> > index 92f95b57da..3879a43fa0 100755
> > --- a/t/t3432-rebase-fast-forward.sh
> > +++ b/t/t3432-rebase-fast-forward.sh
> > @@ -44,19 +44,16 @@ test_rebase_same_head_ () {
> > test_expect_$status "git rebase$flag $* with $changes is $what with $cmp HEAD" "
> > oldhead=\$(git rev-parse HEAD) &&
> > test_when_finished 'git reset --hard \$oldhead' &&
> > + cp .git/logs/HEAD expect &&
> > git rebase$flag $* >stdout &&
> > if test $what = work
> > then
> > - # Must check this case first, for 'is up to
> > - # date, rebase forced[...]rewinding head' cases
> > - test_i18ngrep 'rewinding head' stdout
> > + wc -l .git/logs/HEAD >old &&
> > + wc -l .git/logs/HEAD >new &&
>
> Are the above two lines some yet-to-be cleaned up leftovers? I can't see
> where `old` and `new` are being used elsewhere.
Um, this whole section is broken and causes it to pass the test for
stupid reasons. Not sure how I bungled that so badly. I'll send a
re-roll with a corrected 6/15.
>
> > + test_line_count '-gt' $(($old + 2)) .git/logs/HEAD
> > elif test $what = noop
> > then
> > - test_i18ngrep 'is up to date' stdout &&
> > - test_i18ngrep ! 'rebase forced' stdout
> > - elif test $what = noop-force
> > - then
> > - test_i18ngrep 'is up to date, rebase forced' stdout
> > + test_cmp expect .git/logs/HEAD
> > fi &&
> > newhead=\$(git rev-parse HEAD) &&
> > if test $cmp = same
^ permalink raw reply [flat|nested] 161+ messages in thread
* [PATCH v2 00/15] rebase: make the default backend configurable
2019-12-20 17:09 [PATCH 00/15] rebase: make the default backend configurable Elijah Newren via GitGitGadget
` (15 preceding siblings ...)
2019-12-20 18:51 ` [PATCH 00/15] rebase: make the default backend configurable Alban Gruin
@ 2019-12-23 18:49 ` Elijah Newren via GitGitGadget
2019-12-23 18:49 ` [PATCH v2 01/15] rebase: extend the options for handling of empty commits Elijah Newren via GitGitGadget
` (15 more replies)
16 siblings, 16 replies; 161+ messages in thread
From: Elijah Newren via GitGitGadget @ 2019-12-23 18:49 UTC (permalink / raw)
To: git
Cc: Johannes.Schindelin, phillip.wood, liu.denton, gitster, plroskin,
alban.gruin, szeder.dev, Junio C Hamano
This series does a lot of work around making the default rebase backend
configurable, and switching the default from the am backend to the
merge/interactive one.
See the cover letter for v1[*] for a more detailed motivation for the
series, the type of changes being made, and areas I'd like to reviewers to
focus on.
Changes since v1:
* Made some cleanups to the empty type parsing, as suggested by Junio
(Patch 1)
* Changed the shell prompt for all rebases to just be "REBASE", as
suggested by SZEDER (Patch 11)
* Correctly check that work was done (by checking that reflog grew, patch
6); flagged by Denton
[*]
https://lore.kernel.org/git/pull.679.git.git.1576861788.gitgitgadget@gmail.com/
Elijah Newren (15):
rebase: extend the options for handling of empty commits
t3406: simplify an already simple test
rebase, sequencer: remove the broken GIT_QUIET handling
rebase: make sure to pass along the quiet flag to the sequencer
rebase: fix handling of restrict_revision
t3432: make these tests work with either am or merge backends
rebase: allow more types of rebases to fast-forward
git-rebase.txt: add more details about behavioral differences of
backends
rebase: move incompatibility checks between backend options a bit
earlier
rebase: add an --am option
contrib: change the prompt for interactive-based rebases
rebase tests: mark tests specific to the am-backend with --am
rebase tests: repeat some tests using the merge backend instead of am
rebase: make the backend configurable via config setting
rebase: change the default backend from "am" to "merge"
Documentation/config/rebase.txt | 8 ++
Documentation/git-rebase.txt | 150 ++++++++++++++++----
builtin/rebase.c | 177 +++++++++++++++++++-----
contrib/completion/git-prompt.sh | 4 +-
rebase-interactive.c | 4 +-
rebase-interactive.h | 2 +-
sequencer.c | 80 ++++++++---
sequencer.h | 6 +-
t/t3400-rebase.sh | 36 ++++-
t/t3401-rebase-and-am-rename.sh | 4 +-
t/t3404-rebase-interactive.sh | 2 +-
t/t3406-rebase-message.sh | 19 ++-
t/t3407-rebase-abort.sh | 6 +-
t/t3420-rebase-autostash.sh | 2 +-
t/t3421-rebase-topology-linear.sh | 4 +-
t/t3424-rebase-empty.sh | 89 ++++++++++++
t/t3425-rebase-topology-merges.sh | 8 +-
t/t3427-rebase-subtree.sh | 16 ++-
t/t3432-rebase-fast-forward.sh | 54 ++++----
t/t3433-rebase-options-compatibility.sh | 13 +-
t/t5407-post-rewrite-hook.sh | 12 +-
t/t5520-pull.sh | 27 +++-
t/t6047-diff3-conflict-markers.sh | 13 +-
t/t7512-status-help.sh | 12 +-
t/t9106-git-svn-commit-diff-clobber.sh | 3 +-
t/t9903-bash-prompt.sh | 8 +-
26 files changed, 577 insertions(+), 182 deletions(-)
create mode 100755 t/t3424-rebase-empty.sh
base-commit: 12029dc57db23baef008e77db1909367599210ee
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-679%2Fnewren%2Frebase-fixes-v2
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-679/newren/rebase-fixes-v2
Pull-Request: https://github.com/git/git/pull/679
Range-diff vs v1:
1: 13e2056e78 ! 1: 1c2b77e94d rebase: extend the options for handling of empty commits
@@ -219,28 +219,24 @@
return 0;
}
-+static long parse_empty_value(const char *value)
++static enum empty_type parse_empty_value(const char *value)
+{
-+ if (!value)
-+ return EMPTY_UNSPECIFIED;
-+ else if (!strcasecmp(value, "drop"))
++ if (!strcasecmp(value, "drop"))
+ return EMPTY_DROP;
+ else if (!strcasecmp(value, "keep"))
+ return EMPTY_KEEP;
+ else if (!strcasecmp(value, "ask"))
+ return EMPTY_ASK;
-+ return EMPTY_UNSPECIFIED;
++
++ die(_("unrecognized empty type '%s'; valid values are \"drop\", \"keep\", and \"ask\"."), value);
+}
+
+static int parse_opt_empty(const struct option *opt, const char *arg, int unset)
+{
+ struct rebase_options *options = opt->value;
-+ long value = parse_empty_value(arg);
++ enum empty_type value = parse_empty_value(arg);
+
+ BUG_ON_OPT_NEG(unset);
-+ if (value < 0)
-+ return error(_("option empty accepts \"drop\", "
-+ "\"keep\", and \"ask\""));
+
+ options->empty = value;
+ return 0;
2: 47ea99fb30 = 2: bd3c5ec155 t3406: simplify an already simple test
3: b99feebae1 = 3: 49388b79fd rebase, sequencer: remove the broken GIT_QUIET handling
4: 66bee10d1c = 4: 478479358f rebase: make sure to pass along the quiet flag to the sequencer
5: b84faa8684 = 5: ee26f5a161 rebase: fix handling of restrict_revision
6: 9b1ad46c58 ! 6: 34a69def33 t3432: make these tests work with either am or merge backends
@@ -37,9 +37,8 @@
- # Must check this case first, for 'is up to
- # date, rebase forced[...]rewinding head' cases
- test_i18ngrep 'rewinding head' stdout
-+ wc -l .git/logs/HEAD >old &&
-+ wc -l .git/logs/HEAD >new &&
-+ test_line_count '-gt' $(($old + 2)) .git/logs/HEAD
++ old=\$(wc -l <expect) &&
++ test_line_count '-gt' \$old .git/logs/HEAD
elif test $what = noop
then
- test_i18ngrep 'is up to date' stdout &&
@@ -108,10 +107,3 @@
test_rebase_same_head success noop same success work diff --onto master... master
test_rebase_same_head success noop same success work diff --keep-base master
test_rebase_same_head success noop same success work diff --keep-base
--test_rebase_same_head failure work same success work diff --fork-point --onto B B
--test_rebase_same_head failure work same success work diff --fork-point --onto B... B
-+test_rebase_same_head success work same success work diff --fork-point --onto B B
-+test_rebase_same_head success work same success work diff --fork-point --onto B... B
- test_rebase_same_head success noop same success work diff --fork-point --onto master... master
- test_rebase_same_head success noop same success work diff --fork-point --keep-base master
-
7: a4073df910 = 7: f2c92853b4 rebase: allow more types of rebases to fast-forward
8: 90e8927ea0 = 8: b307340f7c git-rebase.txt: add more details about behavioral differences of backends
9: 5c31d664f3 = 9: 7c3f2e07f3 rebase: move incompatibility checks between backend options a bit earlier
10: 340bf71c5c = 10: 1df11f0b51 rebase: add an --am option
11: 1c3f8ba328 ! 11: 94b5a3051d contrib: change the prompt for am-based rebases
@@ -1,17 +1,38 @@
Author: Elijah Newren <newren@gmail.com>
- contrib: change the prompt for am-based rebases
-
- The prompt for am-based rebases was REBASE, while for interactive-based
- rebases was REBASE-i. A while ago, we switched merge-based rebases from
- using REBASE-m to REBASE-i via re-implementing the merge backend based
- on the interactive backend. We will soon be changing the default rebase
- backend to the interactive one, meaning the default prompt will be
- REBASE-i rather than REBASE. We have also noted in the documentation
- that currently am-specific options will be implemented in the
- interactive backend, and even the --am flag may eventually imply an
- interactive-based rebase. As such, change the prompt for an am-based
- rebase from REBASE to REBASE-a.
+ contrib: change the prompt for interactive-based rebases
+
+ In the past, we had different prompts for different types of rebases:
+ REBASE: for am-based rebases
+ REBASE-m: for merge-based rebases
+ REBASE-i: for interactive-based rebases
+
+ It's not clear why this distinction was necessary or helpful; when the
+ prompt was added in commit e75201963f67 ("Improve bash prompt to detect
+ various states like an unfinished merge", 2007-09-30), it simply added
+ these three different types. Perhaps there was a useful purpose back
+ then, but there have been some changes:
+
+ * The merge backend was deleted after being implemented on top of the
+ interactive backend, causing the prompt for merge-based rebases to
+ change from REBASE-m to REBASE-i.
+ * The interactive backend is used for multiple different types of
+ non-interactive rebases, so the "-i" part of the prompt doesn't
+ really mean what it used to.
+ * Rebase backends have gained more abilities and have a great deal of
+ overlap, sometimes making it hard to distinguish them.
+ * Behavioral differences between the backends have also been ironed
+ out.
+ * We want to change the default backend from am to interactive, which
+ means people would get "REBASE-i" by default if we didn't change
+ the prompt, and only if they specified --am or --whitespace or -C
+ would they get the "REBASE" prompt.
+ * In the future, we plan to have "--whitespace", "-C", and even "--am"
+ run the interactive backend once it can handle everything the
+ am-backend can.
+
+ For all these reasons, make the prompt for any type of rebase just be
+ "REBASE".
Signed-off-by: Elijah Newren <newren@gmail.com>
@@ -19,26 +40,46 @@
--- a/contrib/completion/git-prompt.sh
+++ b/contrib/completion/git-prompt.sh
@@
- __git_eread "$g/rebase-apply/last" total
- if [ -f "$g/rebase-apply/rebasing" ]; then
- __git_eread "$g/rebase-apply/head-name" b
-- r="|REBASE"
-+ r="|REBASE-a"
- elif [ -f "$g/rebase-apply/applying" ]; then
- r="|AM"
- else
+ __git_eread "$g/rebase-merge/msgnum" step
+ __git_eread "$g/rebase-merge/end" total
+ if [ -f "$g/rebase-merge/interactive" ]; then
+- r="|REBASE-i"
++ r="|REBASE"
+ else
+- r="|REBASE-m"
++ r="|REBASE"
+ fi
+ else
+ if [ -d "$g/rebase-apply" ]; then
diff --git a/t/t9903-bash-prompt.sh b/t/t9903-bash-prompt.sh
--- a/t/t9903-bash-prompt.sh
+++ b/t/t9903-bash-prompt.sh
+@@
+ '
+
+ test_expect_success 'prompt - interactive rebase' '
+- printf " (b1|REBASE-i 2/3)" >expected &&
++ printf " (b1|REBASE 2/3)" >expected &&
+ write_script fake_editor.sh <<-\EOF &&
+ echo "exec echo" >"$1"
+ echo "edit $(git log -1 --format="%h")" >>"$1"
+@@
+ '
+
+ test_expect_success 'prompt - rebase merge' '
+- printf " (b2|REBASE-i 1/3)" >expected &&
++ printf " (b2|REBASE 1/3)" >expected &&
+ git checkout b2 &&
+ test_when_finished "git checkout master" &&
+ test_must_fail git rebase --merge b1 b2 &&
@@
test_cmp expected "$actual"
'
-test_expect_success 'prompt - rebase' '
-- printf " (b2|REBASE 1/3)" >expected &&
+test_expect_success 'prompt - rebase am' '
-+ printf " (b2|REBASE-a 1/3)" >expected &&
+ printf " (b2|REBASE 1/3)" >expected &&
git checkout b2 &&
test_when_finished "git checkout master" &&
- test_must_fail git rebase b1 b2 &&
12: bcac3b77e8 = 12: c905d288bf rebase tests: mark tests specific to the am-backend with --am
13: 81d0f18b3e = 13: 0287881361 rebase tests: repeat some tests using the merge backend instead of am
14: 04db4472d3 = 14: ec782e711c rebase: make the backend configurable via config setting
15: 999587933b = 15: 7adcbc0bc5 rebase: change the default backend from "am" to "merge"
--
gitgitgadget
^ permalink raw reply [flat|nested] 161+ messages in thread
* [PATCH v2 01/15] rebase: extend the options for handling of empty commits
2019-12-23 18:49 ` [PATCH v2 " Elijah Newren via GitGitGadget
@ 2019-12-23 18:49 ` Elijah Newren via GitGitGadget
2019-12-23 18:49 ` [PATCH v2 02/15] t3406: simplify an already simple test Elijah Newren via GitGitGadget
` (14 subsequent siblings)
15 siblings, 0 replies; 161+ messages in thread
From: Elijah Newren via GitGitGadget @ 2019-12-23 18:49 UTC (permalink / raw)
To: git
Cc: Johannes.Schindelin, phillip.wood, liu.denton, gitster, plroskin,
alban.gruin, szeder.dev, Junio C Hamano, Elijah Newren
From: Elijah Newren <newren@gmail.com>
Extend the interactive machinery with the ability to handle the full
spread of options for how to handle commits that either start or become
empty (by "become empty" I mean the changes in a commit are a subset of
changes that exist upstream, so the net effect of applying the commit is
no changes). Introduce a new command line flag for selecting the
desired behavior:
--empty={drop,keep,ask}
with the definitions:
drop: drop empty commits
keep: keep empty commits
ask: provide the user a chance to interact and pick what to do with
empty commits on a case-by-case basis
Note that traditionally, am-based rebases have always dropped commits
that either started or became empty, while interactive-based rebases
have defaulted to ask (and provided an option to keep commits that
started empty). This difference made sense since users of an am-based
rebase just wanted to quickly batch apply a sequence of commits, while
users editing a todo list will likely want the chance to interact and
handle unusual cases on a case-by-case basis. However, not all rebases
using the interactive machinery are explicitly interactive anymore. In
particular --merge was always meant to behave more like --am: just
rebase a batch of commits without popping up a todo list.
If the --empty flag is not specified, pick defaults as follows:
explicitly interactive: ask
--exec: keep (exec is about checking existing commits, and often
used without actually changing the base. Thus the
expectation is that the user doesn't necessarily want
anything to change; they just want to test).
otherwise: drop
Also, this commit makes --keep-empty just imply --empty=keep, and hides
it from help so that we aren't confusing users with different ways to do
the same thing. (I could have added a --drop-empty flag, but then that
invites users to specify both --keep-empty and --drop-empty and we have
to add sanity checking around that; it seems cleaner to have a single
multi-valued option.) This actually fixes --keep-empty too; previously,
it only meant to sometimes keep empty commits, in particular commits
which started empty would be kept. But it would still error out and ask
the user what to do with commits that became empty. Now it keeps empty
commits, as instructed.
Signed-off-by: Elijah Newren <newren@gmail.com>
---
Documentation/git-rebase.txt | 35 ++++++------
builtin/rebase.c | 83 +++++++++++++++++++++++++---
rebase-interactive.c | 4 +-
rebase-interactive.h | 2 +-
sequencer.c | 74 +++++++++++++++++++------
sequencer.h | 6 ++-
t/t3421-rebase-topology-linear.sh | 4 +-
t/t3424-rebase-empty.sh | 89 +++++++++++++++++++++++++++++++
t/t3427-rebase-subtree.sh | 16 +++---
9 files changed, 263 insertions(+), 50 deletions(-)
create mode 100755 t/t3424-rebase-empty.sh
diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index 1d0e2d27cc..ff32ca1080 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -258,9 +258,25 @@ See also INCOMPATIBLE OPTIONS below.
original branch. The index and working tree are also left
unchanged as a result.
+--empty={drop,keep,ask}::
+ How to handle commits that become empty (because they contain a
+ subset of already upstream changes) or start empty. With drop
+ (the default), commits that start or become empty are dropped.
+ With keep (implied by --exec), such commits are kept. With ask
+ (implied by --interactive), the rebase will halt when an empty
+ commit is applied allowing you to choose whether to drop it or
+ commit it. Also with ask, if the rebase is interactive then
+ commits which start empty will be commented out in the todo
+ action list (giving you a chance to uncomment).
++
+Note that this has no effect on commits which are already upstream (as
+can be checked via `git log --cherry-mark ...`), which are always
+dropped by rebase.
++
+See also INCOMPATIBLE OPTIONS below.
+
--keep-empty::
- Keep the commits that do not change anything from its
- parents in the result.
+ Deprecated alias for what is now known as --empty=keep.
+
See also INCOMPATIBLE OPTIONS below.
@@ -569,6 +585,7 @@ are incompatible with the following options:
* --interactive
* --exec
* --keep-empty
+ * --empty=
* --edit-todo
* --root when used in combination with --onto
@@ -580,6 +597,7 @@ In addition, the following pairs of options are incompatible:
* --preserve-merges and --ignore-whitespace
* --preserve-merges and --committer-date-is-author-date
* --preserve-merges and --ignore-date
+ * --preserve-merges and --empty=
* --keep-base and --onto
* --keep-base and --root
@@ -588,19 +606,6 @@ BEHAVIORAL DIFFERENCES
There are some subtle differences how the backends behave.
-Empty commits
-~~~~~~~~~~~~~
-
-The am backend drops any "empty" commits, regardless of whether the
-commit started empty (had no changes relative to its parent to
-start with) or ended empty (all changes were already applied
-upstream in other commits).
-
-The interactive backend drops commits by default that
-started empty and halts if it hits a commit that ended up empty.
-The `--keep-empty` option exists for the interactive backend to allow
-it to keep commits that started empty.
-
Directory rename detection
~~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/builtin/rebase.c b/builtin/rebase.c
index ddf33bc9d4..6903249307 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -50,8 +50,16 @@ enum rebase_type {
REBASE_PRESERVE_MERGES
};
+enum empty_type {
+ EMPTY_UNSPECIFIED = -1,
+ EMPTY_DROP,
+ EMPTY_KEEP,
+ EMPTY_ASK
+};
+
struct rebase_options {
enum rebase_type type;
+ enum empty_type empty;
const char *state_dir;
struct commit *upstream;
const char *upstream_name;
@@ -77,7 +85,6 @@ struct rebase_options {
const char *action;
int signoff;
int allow_rerere_autoupdate;
- int keep_empty;
int autosquash;
int ignore_whitespace;
char *gpg_sign_opt;
@@ -95,6 +102,7 @@ struct rebase_options {
#define REBASE_OPTIONS_INIT { \
.type = REBASE_UNSPECIFIED, \
+ .empty = EMPTY_UNSPECIFIED, \
.flags = REBASE_NO_QUIET, \
.git_am_opts = ARGV_ARRAY_INIT, \
.git_format_patch_opt = STRBUF_INIT \
@@ -114,6 +122,10 @@ static struct replay_opts get_replay_opts(const struct rebase_options *opts)
replay.allow_rerere_auto = opts->allow_rerere_autoupdate;
replay.allow_empty = 1;
replay.allow_empty_message = opts->allow_empty_message;
+ replay.drop_redundant_commits = (opts->empty == EMPTY_DROP);
+ replay.keep_redundant_commits = (opts->empty == EMPTY_KEEP);
+ replay.ask_on_initially_empty = (opts->empty == EMPTY_ASK &&
+ !(opts->flags & REBASE_INTERACTIVE_EXPLICIT));
replay.verbose = opts->flags & REBASE_VERBOSE;
replay.reschedule_failed_exec = opts->reschedule_failed_exec;
replay.committer_date_is_author_date =
@@ -389,7 +401,10 @@ static int run_rebase_interactive(struct rebase_options *opts,
git_config_get_bool("rebase.abbreviatecommands", &abbreviate_commands);
- flags |= opts->keep_empty ? TODO_LIST_KEEP_EMPTY : 0;
+ flags |= (opts->empty == EMPTY_DROP) ? TODO_LIST_DROP_EMPTY : 0;
+ flags |= (opts->empty == EMPTY_ASK &&
+ opts->flags & REBASE_INTERACTIVE_EXPLICIT) ?
+ TODO_LIST_ASK_EMPTY : 0;
flags |= abbreviate_commands ? TODO_LIST_ABBREVIATE_CMDS : 0;
flags |= opts->rebase_merges ? TODO_LIST_REBASE_MERGES : 0;
flags |= opts->rebase_cousins > 0 ? TODO_LIST_REBASE_COUSINS : 0;
@@ -453,6 +468,19 @@ static int run_rebase_interactive(struct rebase_options *opts,
return ret;
}
+static int parse_opt_keep_empty(const struct option *opt, const char *arg,
+ int unset)
+{
+ struct rebase_options *opts = opt->value;
+
+ BUG_ON_OPT_NEG(unset);
+ BUG_ON_OPT_ARG(arg);
+
+ opts->empty = EMPTY_KEEP;
+ opts->type = REBASE_INTERACTIVE;
+ return 0;
+}
+
static const char * const builtin_rebase_interactive_usage[] = {
N_("git rebase--interactive [<options>]"),
NULL
@@ -466,7 +494,10 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)
struct option options[] = {
OPT_NEGBIT(0, "ff", &opts.flags, N_("allow fast-forward"),
REBASE_FORCE),
- OPT_BOOL(0, "keep-empty", &opts.keep_empty, N_("keep empty commits")),
+ { OPTION_CALLBACK, 'k', "keep-empty", &options, NULL,
+ N_("(DEPRECATED) keep empty commits"),
+ PARSE_OPT_NOARG | PARSE_OPT_NONEG | PARSE_OPT_HIDDEN,
+ parse_opt_keep_empty },
OPT_BOOL(0, "allow-empty-message", &opts.allow_empty_message,
N_("allow commits with empty messages")),
OPT_BOOL(0, "rebase-merges", &opts.rebase_merges, N_("rebase merge commits")),
@@ -1166,7 +1197,7 @@ static int run_specific_rebase(struct rebase_options *opts, enum action action)
opts->allow_rerere_autoupdate ?
opts->allow_rerere_autoupdate == RERERE_AUTOUPDATE ?
"--rerere-autoupdate" : "--no-rerere-autoupdate" : "");
- add_var(&script_snippet, "keep_empty", opts->keep_empty ? "yes" : "");
+ add_var(&script_snippet, "empty", opts->empty == EMPTY_KEEP ? "yes" : "");
add_var(&script_snippet, "autosquash", opts->autosquash ? "t" : "");
add_var(&script_snippet, "gpg_sign_opt", opts->gpg_sign_opt);
add_var(&script_snippet, "cmd", opts->cmd);
@@ -1360,6 +1391,29 @@ static int parse_opt_interactive(const struct option *opt, const char *arg,
return 0;
}
+static enum empty_type parse_empty_value(const char *value)
+{
+ if (!strcasecmp(value, "drop"))
+ return EMPTY_DROP;
+ else if (!strcasecmp(value, "keep"))
+ return EMPTY_KEEP;
+ else if (!strcasecmp(value, "ask"))
+ return EMPTY_ASK;
+
+ die(_("unrecognized empty type '%s'; valid values are \"drop\", \"keep\", and \"ask\"."), value);
+}
+
+static int parse_opt_empty(const struct option *opt, const char *arg, int unset)
+{
+ struct rebase_options *options = opt->value;
+ enum empty_type value = parse_empty_value(arg);
+
+ BUG_ON_OPT_NEG(unset);
+
+ options->empty = value;
+ return 0;
+}
+
static void NORETURN error_on_missing_default_upstream(void)
{
struct branch *current_branch = branch_get(NULL);
@@ -1505,8 +1559,13 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
"ignoring them"),
REBASE_PRESERVE_MERGES, PARSE_OPT_HIDDEN),
OPT_RERERE_AUTOUPDATE(&options.allow_rerere_autoupdate),
- OPT_BOOL('k', "keep-empty", &options.keep_empty,
- N_("preserve empty commits during rebase")),
+ OPT_CALLBACK_F(0, "empty", &options, N_("{drop,keep,ask}"),
+ N_("how to handle empty commits"),
+ PARSE_OPT_NONEG, parse_opt_empty),
+ { OPTION_CALLBACK, 'k', "keep-empty", &options, NULL,
+ N_("(DEPRECATED) keep empty commits"),
+ PARSE_OPT_NOARG | PARSE_OPT_NONEG | PARSE_OPT_HIDDEN,
+ parse_opt_keep_empty },
OPT_BOOL(0, "autosquash", &options.autosquash,
N_("move commits that begin with "
"squash!/fixup! under -i")),
@@ -1770,8 +1829,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
if (!(options.flags & REBASE_NO_QUIET))
argv_array_push(&options.git_am_opts, "-q");
- if (options.keep_empty)
- imply_interactive(&options, "--keep-empty");
+ if (options.empty != EMPTY_UNSPECIFIED)
+ imply_interactive(&options, "--empty");
if (gpg_sign) {
free(options.gpg_sign_opt);
@@ -1856,6 +1915,14 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
break;
}
+ if (options.empty == EMPTY_UNSPECIFIED) {
+ if (options.flags & REBASE_INTERACTIVE_EXPLICIT)
+ options.empty = EMPTY_ASK;
+ else if (exec.nr > 0)
+ options.empty = EMPTY_KEEP;
+ else
+ options.empty = EMPTY_DROP;
+ }
if (reschedule_failed_exec > 0 && !is_interactive(&options))
die(_("--reschedule-failed-exec requires "
"--exec or --interactive"));
diff --git a/rebase-interactive.c b/rebase-interactive.c
index aa18ae82b7..ad82bf77df 100644
--- a/rebase-interactive.c
+++ b/rebase-interactive.c
@@ -28,7 +28,7 @@ static enum missing_commit_check_level get_missing_commit_check_level(void)
return MISSING_COMMIT_CHECK_IGNORE;
}
-void append_todo_help(unsigned keep_empty, int command_count,
+void append_todo_help(unsigned no_ask_empty, int command_count,
const char *shortrevisions, const char *shortonto,
struct strbuf *buf)
{
@@ -81,7 +81,7 @@ void append_todo_help(unsigned keep_empty, int command_count,
strbuf_add_commented_lines(buf, msg, strlen(msg));
- if (!keep_empty) {
+ if (!no_ask_empty) {
msg = _("Note that empty commits are commented out");
strbuf_add_commented_lines(buf, msg, strlen(msg));
}
diff --git a/rebase-interactive.h b/rebase-interactive.h
index 44dbb06311..f531e00ba7 100644
--- a/rebase-interactive.h
+++ b/rebase-interactive.h
@@ -5,7 +5,7 @@ struct strbuf;
struct repository;
struct todo_list;
-void append_todo_help(unsigned keep_empty, int command_count,
+void append_todo_help(unsigned no_ask_empty, int command_count,
const char *shortrevisions, const char *shortonto,
struct strbuf *buf);
int edit_todo_list(struct repository *r, struct todo_list *todo_list,
diff --git a/sequencer.c b/sequencer.c
index 763ccbbc45..d2c11f34b7 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -160,6 +160,9 @@ 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 GIT_PATH_FUNC(rebase_path_ask_on_initially_empty, "rebase-merge/ask_on_initially_empty")
static int git_sequencer_config(const char *k, const char *v, void *cb)
{
@@ -1623,7 +1626,7 @@ static int allow_empty(struct repository *r,
empty_commit = is_original_commit_empty(commit);
if (empty_commit < 0)
return empty_commit;
- if (!empty_commit)
+ if (!empty_commit || opts->ask_on_initially_empty)
return 0;
else
return 1;
@@ -1837,7 +1840,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) {
/*
@@ -2042,13 +2045,20 @@ static int do_pick_commit(struct repository *r,
goto leave;
}
- allow = allow_empty(r, opts, commit);
- if (allow < 0) {
- res = allow;
- goto leave;
- } else if (allow)
- flags |= ALLOW_EMPTY;
- if (!opts->no_commit) {
+ drop_commit = 0;
+ if (opts->drop_redundant_commits && is_index_unchanged(r)) {
+ drop_commit = 1;
+ fprintf(stderr, _("No changes -- Patch already applied."));
+ } else {
+ allow = allow_empty(r, opts, commit);
+ if (allow < 0) {
+ res = allow;
+ goto leave;
+ } else if (allow) {
+ flags |= ALLOW_EMPTY;
+ }
+ }
+ if (!opts->no_commit && !drop_commit) {
if (author || command == TODO_REVERT || (flags & AMEND_MSG))
res = do_commit(r, msg_file, author, opts, flags);
else
@@ -2501,9 +2511,15 @@ static int populate_opts_cb(const char *key, const char *value, void *data)
else if (!strcmp(key, "options.allow-empty-message"))
opts->allow_empty_message =
git_config_bool_or_int(key, value, &error_flag);
+ else if (!strcmp(key, "options.drop-redundant-commits"))
+ opts->drop_redundant_commits =
+ git_config_bool_or_int(key, value, &error_flag);
else if (!strcmp(key, "options.keep-redundant-commits"))
opts->keep_redundant_commits =
git_config_bool_or_int(key, value, &error_flag);
+ else if (!strcmp(key, "options.ask_on_initially_empty"))
+ opts->ask_on_initially_empty =
+ git_config_bool_or_int(key, value, &error_flag);
else if (!strcmp(key, "options.signoff"))
opts->signoff = git_config_bool_or_int(key, value, &error_flag);
else if (!strcmp(key, "options.record-origin"))
@@ -2612,6 +2628,15 @@ 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;
+
+ if (file_exists(rebase_path_ask_on_initially_empty()))
+ opts->ask_on_initially_empty = 1;
+
read_strategy_opts(opts, &buf);
strbuf_release(&buf);
@@ -2695,6 +2720,12 @@ int write_basic_state(struct replay_opts *opts, const char *head_name,
write_file(rebase_path_cdate_is_adate(), "%s", "");
if (opts->ignore_date)
write_file(rebase_path_ignore_date(), "%s", "");
+ 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->ask_on_initially_empty)
+ write_file(rebase_path_ask_on_initially_empty(), "%s", "");
if (opts->reschedule_failed_exec)
write_file(rebase_path_reschedule_failed_exec(), "%s", "");
@@ -3033,9 +3064,15 @@ static int save_opts(struct replay_opts *opts)
if (opts->allow_empty_message)
res |= git_config_set_in_file_gently(opts_file,
"options.allow-empty-message", "true");
+ if (opts->drop_redundant_commits)
+ res |= git_config_set_in_file_gently(opts_file,
+ "options.drop-redundant-commits", "true");
if (opts->keep_redundant_commits)
res |= git_config_set_in_file_gently(opts_file,
"options.keep-redundant-commits", "true");
+ if (opts->ask_on_initially_empty)
+ res |= git_config_set_in_file_gently(opts_file,
+ "options.ask_on_initially_empty", "true");
if (opts->signoff)
res |= git_config_set_in_file_gently(opts_file,
"options.signoff", "true");
@@ -4691,7 +4728,8 @@ 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 drop_empty = flags & TODO_LIST_DROP_EMPTY;
+ int ask_empty = flags & TODO_LIST_ASK_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;
@@ -4746,6 +4784,8 @@ static int make_script_with_merges(struct pretty_print_context *pp,
is_empty = is_original_commit_empty(commit);
if (!is_empty && (commit->object.flags & PATCHSAME))
continue;
+ if (is_empty && drop_empty)
+ continue;
strbuf_reset(&oneline);
pretty_print_commit(pp, commit, &oneline);
@@ -4754,7 +4794,7 @@ 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)
+ if (is_empty && ask_empty)
strbuf_addf(&buf, "%c ", comment_line_char);
strbuf_addf(&buf, "%s %s %s", cmd_pick,
oid_to_hex(&commit->object.oid),
@@ -4922,7 +4962,8 @@ 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;
+ int drop_empty = flags & TODO_LIST_DROP_EMPTY;
+ int ask_empty = flags & TODO_LIST_ASK_EMPTY;
const char *insn = flags & TODO_LIST_ABBREVIATE_CMDS ? "p" : "pick";
int rebase_merges = flags & TODO_LIST_REBASE_MERGES;
@@ -4958,11 +4999,13 @@ 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)
+ if (is_empty && drop_empty)
+ continue;
+ if (is_empty && ask_empty)
strbuf_addf(out, "%c ", comment_line_char);
strbuf_addf(out, "%s %s ", insn,
oid_to_hex(&commit->object.oid));
@@ -5100,7 +5143,8 @@ 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(!(flags & TODO_LIST_ASK_EMPTY),
+ count_commands(todo_list),
shortrevisions, shortonto, &buf);
res = write_message(buf.buf, buf.len, file, 0);
diff --git a/sequencer.h b/sequencer.h
index e9a0e03ea2..1c3abb661c 100644
--- a/sequencer.h
+++ b/sequencer.h
@@ -39,7 +39,9 @@ struct replay_opts {
int allow_rerere_auto;
int allow_empty;
int allow_empty_message;
+ int drop_redundant_commits;
int keep_redundant_commits;
+ int ask_on_initially_empty;
int verbose;
int quiet;
int reschedule_failed_exec;
@@ -134,7 +136,7 @@ int sequencer_rollback(struct repository *repo, struct replay_opts *opts);
int sequencer_skip(struct repository *repo, struct replay_opts *opts);
int sequencer_remove_state(struct replay_opts *opts);
-#define TODO_LIST_KEEP_EMPTY (1U << 0)
+/* #define TODO_LIST_KEEP_EMPTY (1U << 0) */ /* No longer used */
#define TODO_LIST_SHORTEN_IDS (1U << 1)
#define TODO_LIST_ABBREVIATE_CMDS (1U << 2)
#define TODO_LIST_REBASE_MERGES (1U << 3)
@@ -150,6 +152,8 @@ int sequencer_remove_state(struct replay_opts *opts);
* `--onto`, we do not want to re-generate the root commits.
*/
#define TODO_LIST_ROOT_WITH_ONTO (1U << 6)
+#define TODO_LIST_DROP_EMPTY (1U << 7)
+#define TODO_LIST_ASK_EMPTY (1U << 8)
int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,
diff --git a/t/t3421-rebase-topology-linear.sh b/t/t3421-rebase-topology-linear.sh
index 325072b0a3..d23e0bf778 100755
--- a/t/t3421-rebase-topology-linear.sh
+++ b/t/t3421-rebase-topology-linear.sh
@@ -230,7 +230,7 @@ test_run_rebase () {
test_run_rebase success ''
test_run_rebase success -m
test_run_rebase success -i
-test_have_prereq !REBASE_P || test_run_rebase failure -p
+test_have_prereq !REBASE_P || test_run_rebase success -p
test_run_rebase () {
result=$1
@@ -245,7 +245,7 @@ test_run_rebase () {
test_run_rebase success ''
test_run_rebase success -m
test_run_rebase success -i
-test_have_prereq !REBASE_P || test_run_rebase failure -p
+test_have_prereq !REBASE_P || test_run_rebase success -p
test_run_rebase success --rebase-merges
# m
diff --git a/t/t3424-rebase-empty.sh b/t/t3424-rebase-empty.sh
new file mode 100755
index 0000000000..9d52e1417f
--- /dev/null
+++ b/t/t3424-rebase-empty.sh
@@ -0,0 +1,89 @@
+#!/bin/sh
+
+test_description='git rebase of commits that start or become empty'
+
+. ./test-lib.sh
+
+test_expect_success 'setup test repository' '
+ test_write_lines 1 2 3 4 5 6 7 8 9 10 >numbers &&
+ test_write_lines A B C D E F G H I J >letters &&
+ git add numbers letters &&
+ git commit -m A &&
+
+ git branch upstream &&
+ git branch localmods &&
+
+ git checkout upstream &&
+ test_write_lines A B C D E >letters &&
+ git add letters &&
+ git commit -m B &&
+
+ test_write_lines 1 2 3 4 five 6 7 8 9 ten >numbers &&
+ git add numbers &&
+ git commit -m C &&
+
+ git checkout localmods &&
+ test_write_lines 1 2 3 4 five 6 7 8 9 10 >numbers &&
+ git add numbers &&
+ git commit -m C2 &&
+
+ git commit --allow-empty -m D &&
+
+ test_write_lines A B C D E >letters &&
+ git add letters &&
+ git commit -m "Five letters ought to be enough for anybody"
+'
+
+test_expect_success 'rebase --merge --empty=drop' '
+ git checkout -B testing localmods &&
+ git rebase --merge --empty=drop upstream &&
+
+ test_write_lines C B A >expect &&
+ git log --format=%s >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'rebase --merge --empty=keep' '
+ git checkout -B testing localmods &&
+ git rebase --merge --empty=keep upstream &&
+
+ test_write_lines D C2 C B A >expect &&
+ git log --format=%s >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'rebase --merge --empty=ask' '
+ git checkout -B testing localmods &&
+ test_must_fail git rebase --merge --empty=ask upstream &&
+
+ test_must_fail git rebase --skip &&
+ git commit --allow-empty &&
+ git rebase --continue &&
+
+ test_write_lines D C B A >expect &&
+ git log --format=%s >actual &&
+ test_cmp expect actual
+'
+
+GIT_SEQUENCE_EDITOR=: && export GIT_SEQUENCE_EDITOR
+
+test_expect_success 'rebase --interactive --empty=drop' '
+ git checkout -B testing localmods &&
+ git rebase --interactive --empty=drop upstream &&
+
+ test_write_lines C B A >expect &&
+ git log --format=%s >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'rebase --interactive --empty=keep' '
+ git checkout -B testing localmods &&
+ git rebase --interactive --empty=keep upstream &&
+
+ test_write_lines D C2 C B A >expect &&
+ git log --format=%s >actual &&
+ test_cmp expect actual
+'
+
+
+test_done
diff --git a/t/t3427-rebase-subtree.sh b/t/t3427-rebase-subtree.sh
index bec48e6a1f..468ebc1bef 100755
--- a/t/t3427-rebase-subtree.sh
+++ b/t/t3427-rebase-subtree.sh
@@ -85,23 +85,27 @@ test_expect_failure REBASE_P 'Rebase -Xsubtree --keep-empty --preserve-merges --
verbose test "$(commit_message HEAD)" = "Empty commit"
'
-test_expect_success 'Rebase -Xsubtree --keep-empty --onto commit' '
+test_expect_success 'Rebase -Xsubtree --empty=ask --onto commit' '
reset_rebase &&
git checkout -b rebase-onto to-rebase &&
- test_must_fail git rebase -Xsubtree=files_subtree --keep-empty --onto files-master master &&
+ test_must_fail git rebase -Xsubtree=files_subtree --empty=ask --onto files-master master &&
: first pick results in no changes &&
- git rebase --continue &&
+ test_must_fail git rebase --skip &&
+ : last pick was an empty commit that has no changes, but we want to keep it &&
+ git commit --allow-empty &&
verbose test "$(commit_message HEAD~2)" = "master4" &&
verbose test "$(commit_message HEAD~)" = "files_subtree/master5" &&
verbose test "$(commit_message HEAD)" = "Empty commit"
'
-test_expect_success 'Rebase -Xsubtree --keep-empty --rebase-merges --onto commit' '
+test_expect_success 'Rebase -Xsubtree --empty=ask --rebase-merges --onto commit' '
reset_rebase &&
git checkout -b rebase-merges-onto to-rebase &&
- test_must_fail git rebase -Xsubtree=files_subtree --keep-empty --rebase-merges --onto files-master --root &&
+ test_must_fail git rebase -Xsubtree=files_subtree --empty=ask --rebase-merges --onto files-master --root &&
: first pick results in no changes &&
- git rebase --continue &&
+ test_must_fail git rebase --skip &&
+ : last pick was an empty commit that has no changes, but we want to keep it &&
+ git commit --allow-empty &&
verbose test "$(commit_message HEAD~2)" = "master4" &&
verbose test "$(commit_message HEAD~)" = "files_subtree/master5" &&
verbose test "$(commit_message HEAD)" = "Empty commit"
--
gitgitgadget
^ permalink raw reply related [flat|nested] 161+ messages in thread
* [PATCH v2 02/15] t3406: simplify an already simple test
2019-12-23 18:49 ` [PATCH v2 " Elijah Newren via GitGitGadget
2019-12-23 18:49 ` [PATCH v2 01/15] rebase: extend the options for handling of empty commits Elijah Newren via GitGitGadget
@ 2019-12-23 18:49 ` Elijah Newren via GitGitGadget
2019-12-23 18:49 ` [PATCH v2 03/15] rebase, sequencer: remove the broken GIT_QUIET handling Elijah Newren via GitGitGadget
` (13 subsequent siblings)
15 siblings, 0 replies; 161+ messages in thread
From: Elijah Newren via GitGitGadget @ 2019-12-23 18:49 UTC (permalink / raw)
To: git
Cc: Johannes.Schindelin, phillip.wood, liu.denton, gitster, plroskin,
alban.gruin, szeder.dev, Junio C Hamano, Elijah Newren
From: Elijah Newren <newren@gmail.com>
When the merge backend was re-implemented on top of the interactive
backend, the output of rebase --merge changed a little. This change
allowed this test to be simplified, though it wasn't noticed until now.
Simplify the testcase a little.
Signed-off-by: Elijah Newren <newren@gmail.com>
---
t/t3406-rebase-message.sh | 7 ++-----
1 file changed, 2 insertions(+), 5 deletions(-)
diff --git a/t/t3406-rebase-message.sh b/t/t3406-rebase-message.sh
index b393e1e9fe..0c2c569f95 100755
--- a/t/t3406-rebase-message.sh
+++ b/t/t3406-rebase-message.sh
@@ -18,11 +18,8 @@ test_expect_success 'setup' '
'
test_expect_success 'rebase -m' '
- git rebase -m master >report &&
- >expect &&
- sed -n -e "/^Already applied: /p" \
- -e "/^Committed: /p" report >actual &&
- test_cmp expect actual
+ git rebase -m master >actual &&
+ test_must_be_empty actual
'
test_expect_success 'rebase against master twice' '
--
gitgitgadget
^ permalink raw reply related [flat|nested] 161+ messages in thread
* [PATCH v2 03/15] rebase, sequencer: remove the broken GIT_QUIET handling
2019-12-23 18:49 ` [PATCH v2 " Elijah Newren via GitGitGadget
2019-12-23 18:49 ` [PATCH v2 01/15] rebase: extend the options for handling of empty commits Elijah Newren via GitGitGadget
2019-12-23 18:49 ` [PATCH v2 02/15] t3406: simplify an already simple test Elijah Newren via GitGitGadget
@ 2019-12-23 18:49 ` Elijah Newren via GitGitGadget
2019-12-23 18:49 ` [PATCH v2 04/15] rebase: make sure to pass along the quiet flag to the sequencer Elijah Newren via GitGitGadget
` (12 subsequent siblings)
15 siblings, 0 replies; 161+ messages in thread
From: Elijah Newren via GitGitGadget @ 2019-12-23 18:49 UTC (permalink / raw)
To: git
Cc: Johannes.Schindelin, phillip.wood, liu.denton, gitster, plroskin,
alban.gruin, szeder.dev, Junio C Hamano, Elijah Newren
From: Elijah Newren <newren@gmail.com>
The GIT_QUIET environment variable was used to signal the non-am
backends that the rebase should perform quietly. The preserve-merges
backend does not make use of the quiet flag anywhere (other than to
write out its state whenever it writes state), and this mechanism was
broken in the conversion from shell to C. Since this environment
variable was specifically designed for scripts and the only backend that
would still use it is no longer a script, just gut this code.
A subsequent commit will fix --quiet for the interactive/merge backend
in a different way.
Signed-off-by: Elijah Newren <newren@gmail.com>
---
builtin/rebase.c | 6 ++----
sequencer.c | 6 ++----
2 files changed, 4 insertions(+), 8 deletions(-)
diff --git a/builtin/rebase.c b/builtin/rebase.c
index 6903249307..32026a62e8 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -718,8 +718,8 @@ static int rebase_write_basic_state(struct rebase_options *opts)
opts->onto ? oid_to_hex(&opts->onto->object.oid) : "");
write_file(state_dir_path("orig-head", opts), "%s",
oid_to_hex(&opts->orig_head));
- write_file(state_dir_path("quiet", opts), "%s",
- opts->flags & REBASE_NO_QUIET ? "" : "t");
+ if (!(opts->flags & REBASE_NO_QUIET))
+ write_file(state_dir_path("quiet", opts), "%s", "");
if (opts->flags & REBASE_VERBOSE)
write_file(state_dir_path("verbose", opts), "%s", "");
if (opts->strategy)
@@ -1178,8 +1178,6 @@ static int run_specific_rebase(struct rebase_options *opts, enum action action)
add_var(&script_snippet, "revisions", opts->revisions);
add_var(&script_snippet, "restrict_revision", opts->restrict_revision ?
oid_to_hex(&opts->restrict_revision->object.oid) : NULL);
- add_var(&script_snippet, "GIT_QUIET",
- opts->flags & REBASE_NO_QUIET ? "" : "t");
sq_quote_argv_pretty(&buf, opts->git_am_opts.argv);
add_var(&script_snippet, "git_am_opt", buf.buf);
strbuf_release(&buf);
diff --git a/sequencer.c b/sequencer.c
index d2c11f34b7..71062212a5 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -2688,8 +2688,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)
@@ -2698,8 +2696,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)
--
gitgitgadget
^ permalink raw reply related [flat|nested] 161+ messages in thread
* [PATCH v2 04/15] rebase: make sure to pass along the quiet flag to the sequencer
2019-12-23 18:49 ` [PATCH v2 " Elijah Newren via GitGitGadget
` (2 preceding siblings ...)
2019-12-23 18:49 ` [PATCH v2 03/15] rebase, sequencer: remove the broken GIT_QUIET handling Elijah Newren via GitGitGadget
@ 2019-12-23 18:49 ` Elijah Newren via GitGitGadget
2019-12-23 18:49 ` [PATCH v2 05/15] rebase: fix handling of restrict_revision Elijah Newren via GitGitGadget
` (11 subsequent siblings)
15 siblings, 0 replies; 161+ messages in thread
From: Elijah Newren via GitGitGadget @ 2019-12-23 18:49 UTC (permalink / raw)
To: git
Cc: Johannes.Schindelin, phillip.wood, liu.denton, gitster, plroskin,
alban.gruin, szeder.dev, Junio C Hamano, Elijah Newren
From: Elijah Newren <newren@gmail.com>
Signed-off-by: Elijah Newren <newren@gmail.com>
---
builtin/rebase.c | 3 ++-
t/t3400-rebase.sh | 8 +++++++-
2 files changed, 9 insertions(+), 2 deletions(-)
diff --git a/builtin/rebase.c b/builtin/rebase.c
index 32026a62e8..5014c9a437 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -126,6 +126,7 @@ static struct replay_opts get_replay_opts(const struct rebase_options *opts)
replay.keep_redundant_commits = (opts->empty == EMPTY_KEEP);
replay.ask_on_initially_empty = (opts->empty == EMPTY_ASK &&
!(opts->flags & REBASE_INTERACTIVE_EXPLICIT));
+ replay.quiet = !(opts->flags & REBASE_NO_QUIET);
replay.verbose = opts->flags & REBASE_VERBOSE;
replay.reschedule_failed_exec = opts->reschedule_failed_exec;
replay.committer_date_is_author_date =
@@ -1502,7 +1503,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
N_("allow pre-rebase hook to run")),
OPT_NEGBIT('q', "quiet", &options.flags,
N_("be quiet. implies --no-stat"),
- REBASE_NO_QUIET| REBASE_VERBOSE | REBASE_DIFFSTAT),
+ REBASE_NO_QUIET | REBASE_VERBOSE | REBASE_DIFFSTAT),
OPT_BIT('v', "verbose", &options.flags,
N_("display a diffstat of what changed upstream"),
REBASE_NO_QUIET | REBASE_VERBOSE | REBASE_DIFFSTAT),
diff --git a/t/t3400-rebase.sh b/t/t3400-rebase.sh
index 221b35f2df..79762b989a 100755
--- a/t/t3400-rebase.sh
+++ b/t/t3400-rebase.sh
@@ -206,12 +206,18 @@ test_expect_success 'cherry-picked commits and fork-point work together' '
test_cmp expect D
'
-test_expect_success 'rebase -q is quiet' '
+test_expect_success 'rebase --am -q is quiet' '
git checkout -b quiet topic &&
git rebase -q master >output.out 2>&1 &&
test_must_be_empty output.out
'
+test_expect_success 'rebase --merge -q is quiet' '
+ git checkout -B quiet topic &&
+ git rebase --merge -q master >output.out 2>&1 &&
+ test_must_be_empty output.out
+'
+
test_expect_success 'Rebase a commit that sprinkles CRs in' '
(
echo "One" &&
--
gitgitgadget
^ permalink raw reply related [flat|nested] 161+ messages in thread
* [PATCH v2 05/15] rebase: fix handling of restrict_revision
2019-12-23 18:49 ` [PATCH v2 " Elijah Newren via GitGitGadget
` (3 preceding siblings ...)
2019-12-23 18:49 ` [PATCH v2 04/15] rebase: make sure to pass along the quiet flag to the sequencer Elijah Newren via GitGitGadget
@ 2019-12-23 18:49 ` Elijah Newren via GitGitGadget
2019-12-23 18:49 ` [PATCH v2 06/15] t3432: make these tests work with either am or merge backends Elijah Newren via GitGitGadget
` (10 subsequent siblings)
15 siblings, 0 replies; 161+ messages in thread
From: Elijah Newren via GitGitGadget @ 2019-12-23 18:49 UTC (permalink / raw)
To: git
Cc: Johannes.Schindelin, phillip.wood, liu.denton, gitster, plroskin,
alban.gruin, szeder.dev, Junio C Hamano, Elijah Newren
From: Elijah Newren <newren@gmail.com>
restrict_revision in the original shell script was an excluded revision
range. It is also treated that way by the am-backend. In the
conversion from shell to C (see commit 6ab54d17be3f ("rebase -i:
implement the logic to initialize $revisions in C", 2018-08-28)), the
interactive-backend accidentally treated it as a positive revision
rather than a negated one.
This was missed as there were no tests in the testsuite that tested an
interactive rebase with fork-point behavior.
Signed-off-by: Elijah Newren <newren@gmail.com>
---
builtin/rebase.c | 4 ++--
t/t3400-rebase.sh | 20 +++++++++++++++++++-
2 files changed, 21 insertions(+), 3 deletions(-)
diff --git a/builtin/rebase.c b/builtin/rebase.c
index 5014c9a437..f1de5c8186 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -364,8 +364,8 @@ static int do_interactive_rebase(struct rebase_options *opts, unsigned flags)
argv_array_pushl(&make_script_args, "", revisions, NULL);
if (opts->restrict_revision)
- argv_array_push(&make_script_args,
- oid_to_hex(&opts->restrict_revision->object.oid));
+ argv_array_pushf(&make_script_args, "^%s",
+ oid_to_hex(&opts->restrict_revision->object.oid));
ret = sequencer_make_script(the_repository, &todo_list.buf,
make_script_args.argc, make_script_args.argv,
diff --git a/t/t3400-rebase.sh b/t/t3400-rebase.sh
index 79762b989a..71fd6396cd 100755
--- a/t/t3400-rebase.sh
+++ b/t/t3400-rebase.sh
@@ -165,11 +165,29 @@ test_expect_success 'rebase works with format.useAutoBase' '
git rebase master
'
-test_expect_success 'default to common base in @{upstream}s reflog if no upstream arg' '
+test_expect_success 'default to common base in @{upstream}s reflog if no upstream arg (--merge)' '
git checkout -b default-base master &&
git checkout -b default topic &&
git config branch.default.remote . &&
git config branch.default.merge refs/heads/default-base &&
+ git rebase --merge &&
+ git rev-parse --verify default-base >expect &&
+ git rev-parse default~1 >actual &&
+ test_cmp expect actual &&
+ git checkout default-base &&
+ git reset --hard HEAD^ &&
+ git checkout default &&
+ git rebase --merge &&
+ git rev-parse --verify default-base >expect &&
+ git rev-parse default~1 >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'default to common base in @{upstream}s reflog if no upstream arg' '
+ git checkout -B default-base master &&
+ git checkout -B default topic &&
+ git config branch.default.remote . &&
+ git config branch.default.merge refs/heads/default-base &&
git rebase &&
git rev-parse --verify default-base >expect &&
git rev-parse default~1 >actual &&
--
gitgitgadget
^ permalink raw reply related [flat|nested] 161+ messages in thread
* [PATCH v2 06/15] t3432: make these tests work with either am or merge backends
2019-12-23 18:49 ` [PATCH v2 " Elijah Newren via GitGitGadget
` (4 preceding siblings ...)
2019-12-23 18:49 ` [PATCH v2 05/15] rebase: fix handling of restrict_revision Elijah Newren via GitGitGadget
@ 2019-12-23 18:49 ` Elijah Newren via GitGitGadget
2019-12-23 18:49 ` [PATCH v2 07/15] rebase: allow more types of rebases to fast-forward Elijah Newren via GitGitGadget
` (9 subsequent siblings)
15 siblings, 0 replies; 161+ messages in thread
From: Elijah Newren via GitGitGadget @ 2019-12-23 18:49 UTC (permalink / raw)
To: git
Cc: Johannes.Schindelin, phillip.wood, liu.denton, gitster, plroskin,
alban.gruin, szeder.dev, Junio C Hamano, Elijah Newren
From: Elijah Newren <newren@gmail.com>
t3432 had several stress tests for can_fast_forward(), whose intent was
to ensure we were using the optimization of just fast forwarding when
possible. However, these tests verified that fast forwards had happened
based on the output that rebase printed to the terminal. We can instead
test more directly that we actually fast-forwarded by checking the
reflog, which also has the side effect of making the tests applicable
for the merge/interactive backend.
This change does lose the distinction between "noop" and "noop-force",
but as stated in commit c9efc216830f ("t3432: test for --no-ff's
interaction with fast-forward", 2019-08-27) which introduced that
distinction: "These tests aren't supposed to endorse the status quo,
just test for what we're currently doing.".
This change does not actually run these tests with the merge/interactive
backend; instead this is just a preparatory commit. A subsequent commit
which fixes can_fast_forward() to work with that backend will then also
change t3432 to add tests of that backend as well.
Signed-off-by: Elijah Newren <newren@gmail.com>
---
t/t3432-rebase-fast-forward.sh | 48 ++++++++++++++++------------------
1 file changed, 22 insertions(+), 26 deletions(-)
diff --git a/t/t3432-rebase-fast-forward.sh b/t/t3432-rebase-fast-forward.sh
index 92f95b57da..7432c0e241 100755
--- a/t/t3432-rebase-fast-forward.sh
+++ b/t/t3432-rebase-fast-forward.sh
@@ -44,19 +44,15 @@ test_rebase_same_head_ () {
test_expect_$status "git rebase$flag $* with $changes is $what with $cmp HEAD" "
oldhead=\$(git rev-parse HEAD) &&
test_when_finished 'git reset --hard \$oldhead' &&
+ cp .git/logs/HEAD expect &&
git rebase$flag $* >stdout &&
if test $what = work
then
- # Must check this case first, for 'is up to
- # date, rebase forced[...]rewinding head' cases
- test_i18ngrep 'rewinding head' stdout
+ old=\$(wc -l <expect) &&
+ test_line_count '-gt' \$old .git/logs/HEAD
elif test $what = noop
then
- test_i18ngrep 'is up to date' stdout &&
- test_i18ngrep ! 'rebase forced' stdout
- elif test $what = noop-force
- then
- test_i18ngrep 'is up to date, rebase forced' stdout
+ test_cmp expect .git/logs/HEAD
fi &&
newhead=\$(git rev-parse HEAD) &&
if test $cmp = same
@@ -71,14 +67,14 @@ test_rebase_same_head_ () {
changes='no changes'
test_rebase_same_head success noop same success work same
-test_rebase_same_head success noop same success noop-force same master
-test_rebase_same_head success noop same success noop-force diff --onto B B
-test_rebase_same_head success noop same success noop-force diff --onto B... B
-test_rebase_same_head success noop same success noop-force same --onto master... master
-test_rebase_same_head success noop same success noop-force same --keep-base master
-test_rebase_same_head success noop same success noop-force same --keep-base
-test_rebase_same_head success noop same success noop-force same --no-fork-point
-test_rebase_same_head success noop same success noop-force same --keep-base --no-fork-point
+test_rebase_same_head success noop same success work same master
+test_rebase_same_head success noop same success work diff --onto B B
+test_rebase_same_head success noop same success work diff --onto B... B
+test_rebase_same_head success noop same success work same --onto master... master
+test_rebase_same_head success noop same success work same --keep-base master
+test_rebase_same_head success noop same success work same --keep-base
+test_rebase_same_head success noop same success work same --no-fork-point
+test_rebase_same_head success noop same success work same --keep-base --no-fork-point
test_rebase_same_head success noop same success work same --fork-point master
test_rebase_same_head success noop same success work diff --fork-point --onto B B
test_rebase_same_head success noop same success work diff --fork-point --onto B... B
@@ -91,14 +87,14 @@ test_expect_success 'add work same to side' '
changes='our changes'
test_rebase_same_head success noop same success work same
-test_rebase_same_head success noop same success noop-force same master
-test_rebase_same_head success noop same success noop-force diff --onto B B
-test_rebase_same_head success noop same success noop-force diff --onto B... B
-test_rebase_same_head success noop same success noop-force same --onto master... master
-test_rebase_same_head success noop same success noop-force same --keep-base master
-test_rebase_same_head success noop same success noop-force same --keep-base
-test_rebase_same_head success noop same success noop-force same --no-fork-point
-test_rebase_same_head success noop same success noop-force same --keep-base --no-fork-point
+test_rebase_same_head success noop same success work same master
+test_rebase_same_head success noop same success work diff --onto B B
+test_rebase_same_head success noop same success work diff --onto B... B
+test_rebase_same_head success noop same success work same --onto master... master
+test_rebase_same_head success noop same success work same --keep-base master
+test_rebase_same_head success noop same success work same --keep-base
+test_rebase_same_head success noop same success work same --no-fork-point
+test_rebase_same_head success noop same success work same --keep-base --no-fork-point
test_rebase_same_head success noop same success work same --fork-point master
test_rebase_same_head success noop same success work diff --fork-point --onto B B
test_rebase_same_head success noop same success work diff --fork-point --onto B... B
@@ -112,8 +108,8 @@ test_expect_success 'add work same to upstream' '
'
changes='our and their changes'
-test_rebase_same_head success noop same success noop-force diff --onto B B
-test_rebase_same_head success noop same success noop-force diff --onto B... B
+test_rebase_same_head success noop same success work diff --onto B B
+test_rebase_same_head success noop same success work diff --onto B... B
test_rebase_same_head success noop same success work diff --onto master... master
test_rebase_same_head success noop same success work diff --keep-base master
test_rebase_same_head success noop same success work diff --keep-base
--
gitgitgadget
^ permalink raw reply related [flat|nested] 161+ messages in thread
* [PATCH v2 07/15] rebase: allow more types of rebases to fast-forward
2019-12-23 18:49 ` [PATCH v2 " Elijah Newren via GitGitGadget
` (5 preceding siblings ...)
2019-12-23 18:49 ` [PATCH v2 06/15] t3432: make these tests work with either am or merge backends Elijah Newren via GitGitGadget
@ 2019-12-23 18:49 ` Elijah Newren via GitGitGadget
2019-12-23 18:49 ` [PATCH v2 08/15] git-rebase.txt: add more details about behavioral differences of backends Elijah Newren via GitGitGadget
` (8 subsequent siblings)
15 siblings, 0 replies; 161+ messages in thread
From: Elijah Newren via GitGitGadget @ 2019-12-23 18:49 UTC (permalink / raw)
To: git
Cc: Johannes.Schindelin, phillip.wood, liu.denton, gitster, plroskin,
alban.gruin, szeder.dev, Junio C Hamano, Elijah Newren
From: Elijah Newren <newren@gmail.com>
In the past, we dis-allowed rebases using the interactive backend from
performing a fast-forward to short-circuit the rebase operation. This
made sense for explicitly interactive rebases and some implicitly
interactive rebases, but certainly became overly stringent when the
merge backend was re-implemented via the interactive backend.
Just as the am-based rebase has always had to disable the fast-forward
based on a variety of conditions or flags (e.g. --signoff, --whitespace,
etc.), we need to do the same but now with a few more options. However,
continuing to use REBASE_FORCE for tracking this is problematic because
the interactive backend used it for a different purpose. (When
REBASE_FORCE wasn't set, the interactive backend would not fast-forward
the whole series but would fast-forward individual "pick" commits at the
beginning of the todo list, and then a squash or something would cause
it to start generating new commits.) So, introduce a new
allow_preemptive_ff flag contained within cmd_rebase() and use it to
track whether we are going to allow a pre-emptive fast-forward that
short-circuits the whole rebase.
Signed-off-by: Elijah Newren <newren@gmail.com>
---
builtin/rebase.c | 18 ++++++++++++++----
t/t3432-rebase-fast-forward.sh | 2 ++
2 files changed, 16 insertions(+), 4 deletions(-)
diff --git a/builtin/rebase.c b/builtin/rebase.c
index f1de5c8186..7027e34567 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -1493,6 +1493,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
struct object_id squash_onto;
char *squash_onto_name = NULL;
int reschedule_failed_exec = -1;
+ int allow_preemptive_ff = 1;
struct option builtin_rebase_options[] = {
OPT_STRING(0, "onto", &options.onto_name,
N_("revision"),
@@ -1804,11 +1805,18 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
options.ignore_date)
options.flags |= REBASE_FORCE;
+ if ((options.flags & REBASE_INTERACTIVE_EXPLICIT) ||
+ (action != ACTION_NONE) ||
+ (exec.nr > 0) ||
+ options.autosquash) {
+ allow_preemptive_ff = 0;
+ }
+
for (i = 0; i < options.git_am_opts.argc; i++) {
const char *option = options.git_am_opts.argv[i], *p;
if (!strcmp(option, "--whitespace=fix") ||
!strcmp(option, "--whitespace=strip"))
- options.flags |= REBASE_FORCE;
+ allow_preemptive_ff = 0;
else if (skip_prefix(option, "-C", &p)) {
while (*p)
if (!isdigit(*(p++)))
@@ -2144,12 +2152,14 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
/*
* Check if we are already based on onto with linear history,
* in which case we could fast-forward without replacing the commits
- * with new commits recreated by replaying their changes. This
- * optimization must not be done if this is an interactive rebase.
+ * with new commits recreated by replaying their changes.
+ *
+ * Note that can_fast_forward() initializes merge_base, so we have to
+ * call it before checking allow_preemptive_ff.
*/
if (can_fast_forward(options.onto, options.upstream, options.restrict_revision,
&options.orig_head, &merge_base) &&
- !is_interactive(&options)) {
+ allow_preemptive_ff) {
int flag;
if (!(options.flags & REBASE_FORCE)) {
diff --git a/t/t3432-rebase-fast-forward.sh b/t/t3432-rebase-fast-forward.sh
index 7432c0e241..40388ccf9f 100755
--- a/t/t3432-rebase-fast-forward.sh
+++ b/t/t3432-rebase-fast-forward.sh
@@ -30,6 +30,8 @@ test_rebase_same_head () {
shift &&
test_rebase_same_head_ $status_n $what_n $cmp_n "" "$*" &&
test_rebase_same_head_ $status_f $what_f $cmp_f " --no-ff" "$*"
+ test_rebase_same_head_ $status_n $what_n $cmp_n " --merge" "$*" &&
+ test_rebase_same_head_ $status_f $what_f $cmp_f " --merge --no-ff" "$*"
}
test_rebase_same_head_ () {
--
gitgitgadget
^ permalink raw reply related [flat|nested] 161+ messages in thread
* [PATCH v2 08/15] git-rebase.txt: add more details about behavioral differences of backends
2019-12-23 18:49 ` [PATCH v2 " Elijah Newren via GitGitGadget
` (6 preceding siblings ...)
2019-12-23 18:49 ` [PATCH v2 07/15] rebase: allow more types of rebases to fast-forward Elijah Newren via GitGitGadget
@ 2019-12-23 18:49 ` Elijah Newren via GitGitGadget
2019-12-23 18:49 ` [PATCH v2 09/15] rebase: move incompatibility checks between backend options a bit earlier Elijah Newren via GitGitGadget
` (7 subsequent siblings)
15 siblings, 0 replies; 161+ messages in thread
From: Elijah Newren via GitGitGadget @ 2019-12-23 18:49 UTC (permalink / raw)
To: git
Cc: Johannes.Schindelin, phillip.wood, liu.denton, gitster, plroskin,
alban.gruin, szeder.dev, Junio C Hamano, Elijah Newren
From: Elijah Newren <newren@gmail.com>
Signed-off-by: Elijah Newren <newren@gmail.com>
---
Documentation/git-rebase.txt | 102 +++++++++++++++++++++---
t/t3433-rebase-options-compatibility.sh | 5 +-
2 files changed, 94 insertions(+), 13 deletions(-)
diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index ff32ca1080..f1ace07c38 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -409,13 +409,10 @@ your branch contains commits which were dropped, this option can be used
with `--keep-base` in order to drop those commits from your branch.
--ignore-whitespace::
- Behaves differently depending on which backend is selected.
-+
-'am' backend: When applying a patch, ignore changes in whitespace in
-context lines if necessary.
-+
-'interactive' backend: Treat lines with only whitespace changes as
-unchanged for the sake of a three-way merge.
+ Ignore whitespace-only changes in the commits being rebased,
+ which may avoid "unnecessary" conflicts. (Both backends
+ currently have differing edgecase bugs with this option; see
+ BEHAVIORAL DIFFERENCES.)
--whitespace=<option>::
This flag is passed to the 'git apply' program
@@ -609,9 +606,94 @@ There are some subtle differences how the backends behave.
Directory rename detection
~~~~~~~~~~~~~~~~~~~~~~~~~~
-Directory rename heuristics are enabled in the merge and interactive
-backends. Due to the lack of accurate tree information, directory
-rename detection is disabled in the am backend.
+Due to the lack of accurate tree information (arising from
+constructing fake ancestors with the limited information available in
+patches), directory rename detection is disabled in the am backend.
+Disabled directory rename detection means that if one side of history
+renames a directory and the other adds new files to the old directory,
+then the new files will be left behind in the old directory without
+any warning at the time of rebasing that you may want to move these
+files into the new directory.
+
+Directory rename detection works with the merge and interactive
+backends to provide you warnings in such cases.
+
+Context
+~~~~~~~
+
+The am backend works by creating a sequence of patches (by calling
+`format-patch` internally), and then applying the patches in sequence
+(calling `am` internally). Patches are composed of multiple hunks,
+each with line numbers, a context region, and the actual changes. The
+line numbers have to be taken with some fuzz, since the other side
+will likely have inserted or deleted lines earlier in the file. The
+context region is meant to help find how to adjust the line numbers in
+order to apply the changes to the right lines. However, if multiple
+areas of the code have the same surrounding lines of context, the
+wrong one can be picked. There are real-world cases where this has
+caused commits to be reapplied incorrectly with no conflicts reported.
+Setting diff.context to a larger value may prevent such types of
+problems, but increases the chance of spurious conflicts (since it
+will require more lines of matching context to apply).
+
+The interactive backend works with a full copy of each relevant file,
+insulating it from these types of problems.
+
+Labelling of conflicts markers
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+When there are content conflicts, the merge machinery tries to
+annotate each side's conflict markers with the commits where the
+content came from. Since the am backend drops the original
+information about the rebased commits and their parents (and instead
+generates new fake commits based off limited information in the
+generated patches), those commits cannot be identified; instead it has
+to fall back to a commit summary. Also, when merge.conflictStyle is
+set to diff3, the am backend will use "constructed merge base" to
+label the content from the merge base, and thus provide no information
+about the merge base commit whatsoever.
+
+The interactive backend works with the full commits on both sides of
+history and thus has no such limitations.
+
+--ignore-whitespace
+~~~~~~~~~~~~~~~~~~~
+
+The --ignore-whitespace option is supposed to ignore whitespace-only
+changes if it allows the code to merge cleanly. Unfortunately, the
+different backends implement this differently, and both have different
+edge case bugs.
++
+'am' backend: When applying a patch, ignore changes in whitespace in
+context lines if necessary. (Which implies that if the whitespace
+change was not in the context lines but on a line with a real change,
+then the rebase will still fail with "unnecessary" content conflicts.)
++
+'interactive' backend: Treat lines with only whitespace changes as
+unchanged for the sake of a three-way merge. This means that if one
+side made no changes and the commits being rebased had whitespace-only
+changes, those whitespaces fixups will be discarded despite the fact
+that they present no content conflict.
+
+Miscellaneous differences
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+There are a few more behavioral differences that most folks would
+probably consider inconsequential but which are mentioned for
+completeness:
+
+* Reflog: The two backends will use different wording when describing
+ the changes made in the reflog, though both will make use of the
+ word "rebase".
+
+* Progress, informational, and error messages: The two backends
+ provide slightly different progress and informational messages.
+ Also, the am backend writes error messages (such as "Your files
+ would be overwritten...") to stdout, while the interactive backend
+ writes them to stderr.
+
+* State directories: The two backends keep their state in different
+ directories under .git/
include::merge-strategies.txt[]
diff --git a/t/t3433-rebase-options-compatibility.sh b/t/t3433-rebase-options-compatibility.sh
index 5166f158dd..bd4d2d2f63 100755
--- a/t/t3433-rebase-options-compatibility.sh
+++ b/t/t3433-rebase-options-compatibility.sh
@@ -10,9 +10,8 @@ test_description='tests to ensure compatibility between am and interactive backe
GIT_AUTHOR_DATE="1999-04-02T08:03:20+05:30"
export GIT_AUTHOR_DATE
-# This is a special case in which both am and interactive backends
-# provide the same output. It was done intentionally because
-# both the backends fall short of optimal behaviour.
+# This is a common case in which both am and interactive backends
+# provide the same output with --ignore-whitespace.
test_expect_success 'setup' '
git checkout -b topic &&
q_to_tab >file <<-\EOF &&
--
gitgitgadget
^ permalink raw reply related [flat|nested] 161+ messages in thread
* [PATCH v2 09/15] rebase: move incompatibility checks between backend options a bit earlier
2019-12-23 18:49 ` [PATCH v2 " Elijah Newren via GitGitGadget
` (7 preceding siblings ...)
2019-12-23 18:49 ` [PATCH v2 08/15] git-rebase.txt: add more details about behavioral differences of backends Elijah Newren via GitGitGadget
@ 2019-12-23 18:49 ` Elijah Newren via GitGitGadget
2019-12-23 18:49 ` [PATCH v2 10/15] rebase: add an --am option Elijah Newren via GitGitGadget
` (6 subsequent siblings)
15 siblings, 0 replies; 161+ messages in thread
From: Elijah Newren via GitGitGadget @ 2019-12-23 18:49 UTC (permalink / raw)
To: git
Cc: Johannes.Schindelin, phillip.wood, liu.denton, gitster, plroskin,
alban.gruin, szeder.dev, Junio C Hamano, Elijah Newren
From: Elijah Newren <newren@gmail.com>
Signed-off-by: Elijah Newren <newren@gmail.com>
---
builtin/rebase.c | 22 +++++++++++-----------
1 file changed, 11 insertions(+), 11 deletions(-)
diff --git a/builtin/rebase.c b/builtin/rebase.c
index 7027e34567..d2b99e9908 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -1906,6 +1906,17 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
if (isatty(2) && options.flags & REBASE_NO_QUIET)
strbuf_addstr(&options.git_format_patch_opt, " --progress");
+ if (options.git_am_opts.argc) {
+ /* all am options except -q are compatible only with --am */
+ for (i = options.git_am_opts.argc - 1; i >= 0; i--)
+ if (strcmp(options.git_am_opts.argv[i], "-q"))
+ break;
+
+ if (is_interactive(&options) && i >= 0)
+ die(_("cannot combine am options with either "
+ "interactive or merge options"));
+ }
+
switch (options.type) {
case REBASE_MERGE:
case REBASE_INTERACTIVE:
@@ -1936,17 +1947,6 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
if (reschedule_failed_exec >= 0)
options.reschedule_failed_exec = reschedule_failed_exec;
- if (options.git_am_opts.argc) {
- /* all am options except -q are compatible only with --am */
- for (i = options.git_am_opts.argc - 1; i >= 0; i--)
- if (strcmp(options.git_am_opts.argv[i], "-q"))
- break;
-
- if (is_interactive(&options) && i >= 0)
- die(_("cannot combine am options with either "
- "interactive or merge options"));
- }
-
if (options.signoff) {
if (options.type == REBASE_PRESERVE_MERGES)
die("cannot combine '--signoff' with "
--
gitgitgadget
^ permalink raw reply related [flat|nested] 161+ messages in thread
* [PATCH v2 10/15] rebase: add an --am option
2019-12-23 18:49 ` [PATCH v2 " Elijah Newren via GitGitGadget
` (8 preceding siblings ...)
2019-12-23 18:49 ` [PATCH v2 09/15] rebase: move incompatibility checks between backend options a bit earlier Elijah Newren via GitGitGadget
@ 2019-12-23 18:49 ` Elijah Newren via GitGitGadget
2019-12-23 18:49 ` [PATCH v2 11/15] contrib: change the prompt for interactive-based rebases Elijah Newren via GitGitGadget
` (5 subsequent siblings)
15 siblings, 0 replies; 161+ messages in thread
From: Elijah Newren via GitGitGadget @ 2019-12-23 18:49 UTC (permalink / raw)
To: git
Cc: Johannes.Schindelin, phillip.wood, liu.denton, gitster, plroskin,
alban.gruin, szeder.dev, Junio C Hamano, Elijah Newren
From: Elijah Newren <newren@gmail.com>
Currently, this option doesn't do anything except error out if any
options requiring the interactive-backend are also passed. However,
when we make the default backend configurable later in this series, this
flag will provide a way to override the config setting.
Signed-off-by: Elijah Newren <newren@gmail.com>
---
Documentation/git-rebase.txt | 11 ++++++++++-
builtin/rebase.c | 18 +++++++++++++++++-
2 files changed, 27 insertions(+), 2 deletions(-)
diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index f1ace07c38..cf1ac2e359 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -258,6 +258,13 @@ See also INCOMPATIBLE OPTIONS below.
original branch. The index and working tree are also left
unchanged as a result.
+--am:
+ Use git-am internally to rebase. This option may become a
+ no-op in the future once the interactive backend handles
+ everything the am one does.
++
+See also INCOMPATIBLE OPTIONS below.
+
--empty={drop,keep,ask}::
How to handle commits that become empty (because they contain a
subset of already upstream changes) or start empty. With drop
@@ -372,7 +379,7 @@ See also INCOMPATIBLE OPTIONS below.
Ensure at least <n> lines of surrounding context match before
and after each change. When fewer lines of surrounding
context exist they all must match. By default no context is
- ever ignored.
+ ever ignored. Implies --am.
+
See also INCOMPATIBLE OPTIONS below.
@@ -417,6 +424,7 @@ with `--keep-base` in order to drop those commits from your branch.
--whitespace=<option>::
This flag is passed to the 'git apply' program
(see linkgit:git-apply[1]) that applies the patch.
+ Implies --am.
+
See also INCOMPATIBLE OPTIONS below.
@@ -567,6 +575,7 @@ INCOMPATIBLE OPTIONS
The following options:
+ * --am
* --whitespace
* -C
diff --git a/builtin/rebase.c b/builtin/rebase.c
index d2b99e9908..b7915fc0cb 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -1361,6 +1361,18 @@ static int can_fast_forward(struct commit *onto, struct commit *upstream,
return res && is_linear_history(onto, head);
}
+static int parse_opt_am(const struct option *opt, const char *arg, int unset)
+{
+ struct rebase_options *opts = opt->value;
+
+ BUG_ON_OPT_NEG(unset);
+ BUG_ON_OPT_ARG(arg);
+
+ opts->type = REBASE_AM;
+
+ return 0;
+}
+
/* -i followed by -m is still -i */
static int parse_opt_merge(const struct option *opt, const char *arg, int unset)
{
@@ -1546,6 +1558,10 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
OPT_CMDMODE(0, "show-current-patch", &action,
N_("show the patch file being applied or merged"),
ACTION_SHOW_CURRENT_PATCH),
+ { OPTION_CALLBACK, 0, "am", &options, NULL,
+ N_("use apply-mail strategies to rebase"),
+ PARSE_OPT_NOARG | PARSE_OPT_NONEG,
+ parse_opt_am },
{ OPTION_CALLBACK, 'm', "merge", &options, NULL,
N_("use merging strategies to rebase"),
PARSE_OPT_NOARG | PARSE_OPT_NONEG,
@@ -1906,7 +1922,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
if (isatty(2) && options.flags & REBASE_NO_QUIET)
strbuf_addstr(&options.git_format_patch_opt, " --progress");
- if (options.git_am_opts.argc) {
+ if (options.git_am_opts.argc || options.type == REBASE_AM) {
/* all am options except -q are compatible only with --am */
for (i = options.git_am_opts.argc - 1; i >= 0; i--)
if (strcmp(options.git_am_opts.argv[i], "-q"))
--
gitgitgadget
^ permalink raw reply related [flat|nested] 161+ messages in thread
* [PATCH v2 11/15] contrib: change the prompt for interactive-based rebases
2019-12-23 18:49 ` [PATCH v2 " Elijah Newren via GitGitGadget
` (9 preceding siblings ...)
2019-12-23 18:49 ` [PATCH v2 10/15] rebase: add an --am option Elijah Newren via GitGitGadget
@ 2019-12-23 18:49 ` Elijah Newren via GitGitGadget
2019-12-23 22:00 ` Denton Liu
2019-12-23 18:49 ` [PATCH v2 12/15] rebase tests: mark tests specific to the am-backend with --am Elijah Newren via GitGitGadget
` (4 subsequent siblings)
15 siblings, 1 reply; 161+ messages in thread
From: Elijah Newren via GitGitGadget @ 2019-12-23 18:49 UTC (permalink / raw)
To: git
Cc: Johannes.Schindelin, phillip.wood, liu.denton, gitster, plroskin,
alban.gruin, szeder.dev, Junio C Hamano, Elijah Newren
From: Elijah Newren <newren@gmail.com>
In the past, we had different prompts for different types of rebases:
REBASE: for am-based rebases
REBASE-m: for merge-based rebases
REBASE-i: for interactive-based rebases
It's not clear why this distinction was necessary or helpful; when the
prompt was added in commit e75201963f67 ("Improve bash prompt to detect
various states like an unfinished merge", 2007-09-30), it simply added
these three different types. Perhaps there was a useful purpose back
then, but there have been some changes:
* The merge backend was deleted after being implemented on top of the
interactive backend, causing the prompt for merge-based rebases to
change from REBASE-m to REBASE-i.
* The interactive backend is used for multiple different types of
non-interactive rebases, so the "-i" part of the prompt doesn't
really mean what it used to.
* Rebase backends have gained more abilities and have a great deal of
overlap, sometimes making it hard to distinguish them.
* Behavioral differences between the backends have also been ironed
out.
* We want to change the default backend from am to interactive, which
means people would get "REBASE-i" by default if we didn't change
the prompt, and only if they specified --am or --whitespace or -C
would they get the "REBASE" prompt.
* In the future, we plan to have "--whitespace", "-C", and even "--am"
run the interactive backend once it can handle everything the
am-backend can.
For all these reasons, make the prompt for any type of rebase just be
"REBASE".
Signed-off-by: Elijah Newren <newren@gmail.com>
---
contrib/completion/git-prompt.sh | 4 ++--
t/t9903-bash-prompt.sh | 8 ++++----
2 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/contrib/completion/git-prompt.sh b/contrib/completion/git-prompt.sh
index 1d510cd47b..8f8a22ba60 100644
--- a/contrib/completion/git-prompt.sh
+++ b/contrib/completion/git-prompt.sh
@@ -430,9 +430,9 @@ __git_ps1 ()
__git_eread "$g/rebase-merge/msgnum" step
__git_eread "$g/rebase-merge/end" total
if [ -f "$g/rebase-merge/interactive" ]; then
- r="|REBASE-i"
+ r="|REBASE"
else
- r="|REBASE-m"
+ r="|REBASE"
fi
else
if [ -d "$g/rebase-apply" ]; then
diff --git a/t/t9903-bash-prompt.sh b/t/t9903-bash-prompt.sh
index 88bc733ad6..7ca35d358d 100755
--- a/t/t9903-bash-prompt.sh
+++ b/t/t9903-bash-prompt.sh
@@ -163,7 +163,7 @@ test_expect_success 'prompt - inside bare repository' '
'
test_expect_success 'prompt - interactive rebase' '
- printf " (b1|REBASE-i 2/3)" >expected &&
+ printf " (b1|REBASE 2/3)" >expected &&
write_script fake_editor.sh <<-\EOF &&
echo "exec echo" >"$1"
echo "edit $(git log -1 --format="%h")" >>"$1"
@@ -180,7 +180,7 @@ test_expect_success 'prompt - interactive rebase' '
'
test_expect_success 'prompt - rebase merge' '
- printf " (b2|REBASE-i 1/3)" >expected &&
+ printf " (b2|REBASE 1/3)" >expected &&
git checkout b2 &&
test_when_finished "git checkout master" &&
test_must_fail git rebase --merge b1 b2 &&
@@ -189,11 +189,11 @@ test_expect_success 'prompt - rebase merge' '
test_cmp expected "$actual"
'
-test_expect_success 'prompt - rebase' '
+test_expect_success 'prompt - rebase am' '
printf " (b2|REBASE 1/3)" >expected &&
git checkout b2 &&
test_when_finished "git checkout master" &&
- test_must_fail git rebase b1 b2 &&
+ test_must_fail git rebase --am b1 b2 &&
test_when_finished "git rebase --abort" &&
__git_ps1 >"$actual" &&
test_cmp expected "$actual"
--
gitgitgadget
^ permalink raw reply related [flat|nested] 161+ messages in thread
* [PATCH v2 12/15] rebase tests: mark tests specific to the am-backend with --am
2019-12-23 18:49 ` [PATCH v2 " Elijah Newren via GitGitGadget
` (10 preceding siblings ...)
2019-12-23 18:49 ` [PATCH v2 11/15] contrib: change the prompt for interactive-based rebases Elijah Newren via GitGitGadget
@ 2019-12-23 18:49 ` Elijah Newren via GitGitGadget
2019-12-23 18:49 ` [PATCH v2 13/15] rebase tests: repeat some tests using the merge backend instead of am Elijah Newren via GitGitGadget
` (3 subsequent siblings)
15 siblings, 0 replies; 161+ messages in thread
From: Elijah Newren via GitGitGadget @ 2019-12-23 18:49 UTC (permalink / raw)
To: git
Cc: Johannes.Schindelin, phillip.wood, liu.denton, gitster, plroskin,
alban.gruin, szeder.dev, Junio C Hamano, Elijah Newren
From: Elijah Newren <newren@gmail.com>
We have many rebase tests in the testsuite, and often the same test is
repeated multiple times just testing different backends. For those
tests that were specifically trying to test the am backend, add the --am
flag.
Signed-off-by: Elijah Newren <newren@gmail.com>
---
t/t3400-rebase.sh | 10 +++++-----
t/t3401-rebase-and-am-rename.sh | 4 ++--
t/t3404-rebase-interactive.sh | 2 +-
t/t3406-rebase-message.sh | 12 ++++++------
t/t3407-rebase-abort.sh | 6 +++---
t/t3420-rebase-autostash.sh | 2 +-
t/t3425-rebase-topology-merges.sh | 8 ++++----
t/t3432-rebase-fast-forward.sh | 4 ++--
t/t3433-rebase-options-compatibility.sh | 8 ++++----
t/t5407-post-rewrite-hook.sh | 12 ++++++------
t/t7512-status-help.sh | 12 ++++++------
11 files changed, 40 insertions(+), 40 deletions(-)
diff --git a/t/t3400-rebase.sh b/t/t3400-rebase.sh
index 71fd6396cd..0a491f2363 100755
--- a/t/t3400-rebase.sh
+++ b/t/t3400-rebase.sh
@@ -183,19 +183,19 @@ test_expect_success 'default to common base in @{upstream}s reflog if no upstrea
test_cmp expect actual
'
-test_expect_success 'default to common base in @{upstream}s reflog if no upstream arg' '
+test_expect_success 'default to common base in @{upstream}s reflog if no upstream arg (--am)' '
git checkout -B default-base master &&
git checkout -B default topic &&
git config branch.default.remote . &&
git config branch.default.merge refs/heads/default-base &&
- git rebase &&
+ git rebase --am &&
git rev-parse --verify default-base >expect &&
git rev-parse default~1 >actual &&
test_cmp expect actual &&
git checkout default-base &&
git reset --hard HEAD^ &&
git checkout default &&
- git rebase &&
+ git rebase --am &&
git rev-parse --verify default-base >expect &&
git rev-parse default~1 >actual &&
test_cmp expect actual
@@ -226,7 +226,7 @@ test_expect_success 'cherry-picked commits and fork-point work together' '
test_expect_success 'rebase --am -q is quiet' '
git checkout -b quiet topic &&
- git rebase -q master >output.out 2>&1 &&
+ git rebase --am -q master >output.out 2>&1 &&
test_must_be_empty output.out
'
@@ -325,7 +325,7 @@ test_expect_success 'rebase --am and --show-current-patch' '
echo two >>init.t &&
git commit -a -m two &&
git tag two &&
- test_must_fail git rebase -f --onto init HEAD^ &&
+ test_must_fail git rebase --am -f --onto init HEAD^ &&
GIT_TRACE=1 git rebase --show-current-patch >/dev/null 2>stderr &&
grep "show.*$(git rev-parse two)" stderr
)
diff --git a/t/t3401-rebase-and-am-rename.sh b/t/t3401-rebase-and-am-rename.sh
index a0b9438b22..50803958fd 100755
--- a/t/t3401-rebase-and-am-rename.sh
+++ b/t/t3401-rebase-and-am-rename.sh
@@ -52,13 +52,13 @@ test_expect_success 'rebase --interactive: directory rename detected' '
)
'
-test_expect_failure 'rebase (am): directory rename detected' '
+test_expect_failure 'rebase --am: directory rename detected' '
(
cd dir-rename &&
git checkout B^0 &&
- git -c merge.directoryRenames=true rebase A &&
+ git -c merge.directoryRenames=true rebase --am A &&
git ls-files -s >out &&
test_line_count = 5 out &&
diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
index ae6e55ce79..743b7e511a 100755
--- a/t/t3404-rebase-interactive.sh
+++ b/t/t3404-rebase-interactive.sh
@@ -1137,7 +1137,7 @@ test_expect_success C_LOCALE_OUTPUT 'rebase --edit-todo does not work on non-int
git checkout conflict-branch &&
(
set_fake_editor &&
- test_must_fail git rebase -f --onto HEAD~2 HEAD~ &&
+ test_must_fail git rebase -f --am --onto HEAD~2 HEAD~ &&
test_must_fail git rebase --edit-todo
) &&
git rebase --abort
diff --git a/t/t3406-rebase-message.sh b/t/t3406-rebase-message.sh
index 0c2c569f95..7ce617fc1f 100755
--- a/t/t3406-rebase-message.sh
+++ b/t/t3406-rebase-message.sh
@@ -23,24 +23,24 @@ test_expect_success 'rebase -m' '
'
test_expect_success 'rebase against master twice' '
- git rebase master >out &&
+ git rebase --am master >out &&
test_i18ngrep "Current branch topic is up to date" out
'
test_expect_success 'rebase against master twice with --force' '
- git rebase --force-rebase master >out &&
+ git rebase --force-rebase --am master >out &&
test_i18ngrep "Current branch topic is up to date, rebase forced" out
'
test_expect_success 'rebase against master twice from another branch' '
git checkout topic^ &&
- git rebase master topic >out &&
+ git rebase --am master topic >out &&
test_i18ngrep "Current branch topic is up to date" out
'
test_expect_success 'rebase fast-forward to master' '
git checkout topic^ &&
- git rebase topic >out &&
+ git rebase --am topic >out &&
test_i18ngrep "Fast-forwarded HEAD to topic" out
'
@@ -89,7 +89,7 @@ test_expect_success 'GIT_REFLOG_ACTION' '
git checkout -b reflog-topic start &&
test_commit reflog-to-rebase &&
- git rebase reflog-onto &&
+ git rebase --am reflog-onto &&
git log -g --format=%gs -3 >actual &&
cat >expect <<-\EOF &&
rebase finished: returning to refs/heads/reflog-topic
@@ -99,7 +99,7 @@ test_expect_success 'GIT_REFLOG_ACTION' '
test_cmp expect actual &&
git checkout -b reflog-prefix reflog-to-rebase &&
- GIT_REFLOG_ACTION=change-the-reflog git rebase reflog-onto &&
+ GIT_REFLOG_ACTION=change-the-reflog git rebase --am reflog-onto &&
git log -g --format=%gs -3 >actual &&
cat >expect <<-\EOF &&
rebase finished: returning to refs/heads/reflog-prefix
diff --git a/t/t3407-rebase-abort.sh b/t/t3407-rebase-abort.sh
index 910f218284..3e31826170 100755
--- a/t/t3407-rebase-abort.sh
+++ b/t/t3407-rebase-abort.sh
@@ -96,14 +96,14 @@ testrebase() {
'
}
-testrebase "" .git/rebase-apply
+testrebase " --am" .git/rebase-apply
testrebase " --merge" .git/rebase-merge
-test_expect_success 'rebase --quit' '
+test_expect_success 'rebase --am --quit' '
cd "$work_dir" &&
# Clean up the state from the previous one
git reset --hard pre-rebase &&
- test_must_fail git rebase master &&
+ test_must_fail git rebase --am master &&
test_path_is_dir .git/rebase-apply &&
head_before=$(git rev-parse HEAD) &&
git rebase --quit &&
diff --git a/t/t3420-rebase-autostash.sh b/t/t3420-rebase-autostash.sh
index 5f7e73cf83..3816159e20 100755
--- a/t/t3420-rebase-autostash.sh
+++ b/t/t3420-rebase-autostash.sh
@@ -234,7 +234,7 @@ test_expect_success "rebase: noop rebase" '
git checkout feature-branch
'
-testrebase "" .git/rebase-apply
+testrebase " --am" .git/rebase-apply
testrebase " --merge" .git/rebase-merge
testrebase " --interactive" .git/rebase-merge
diff --git a/t/t3425-rebase-topology-merges.sh b/t/t3425-rebase-topology-merges.sh
index fd8efe84fe..19700b025b 100755
--- a/t/t3425-rebase-topology-merges.sh
+++ b/t/t3425-rebase-topology-merges.sh
@@ -54,7 +54,7 @@ test_run_rebase () {
test_linear_range 'n o' e..
"
}
-test_run_rebase success ''
+test_run_rebase success --am
test_run_rebase success -m
test_run_rebase success -i
@@ -70,7 +70,7 @@ test_run_rebase () {
test_linear_range "\'"$expected"\'" d..
"
}
-test_run_rebase success 'n o e' ''
+test_run_rebase success 'n o e' --am
test_run_rebase success 'n o e' -m
test_run_rebase success 'n o e' -i
@@ -86,7 +86,7 @@ test_run_rebase () {
test_linear_range "\'"$expected"\'" c..
"
}
-test_run_rebase success 'd n o e' ''
+test_run_rebase success 'd n o e' --am
test_run_rebase success 'd n o e' -m
test_run_rebase success 'd n o e' -i
@@ -102,7 +102,7 @@ test_run_rebase () {
test_linear_range "\'"$expected"\'" c..
"
}
-test_run_rebase success 'd n o e' ''
+test_run_rebase success 'd n o e' --am
test_run_rebase success 'd n o e' -m
test_run_rebase success 'd n o e' -i
diff --git a/t/t3432-rebase-fast-forward.sh b/t/t3432-rebase-fast-forward.sh
index 40388ccf9f..4b3cecce56 100755
--- a/t/t3432-rebase-fast-forward.sh
+++ b/t/t3432-rebase-fast-forward.sh
@@ -28,8 +28,8 @@ test_rebase_same_head () {
shift &&
cmp_f="$1" &&
shift &&
- test_rebase_same_head_ $status_n $what_n $cmp_n "" "$*" &&
- test_rebase_same_head_ $status_f $what_f $cmp_f " --no-ff" "$*"
+ test_rebase_same_head_ $status_n $what_n $cmp_n " --am" "$*" &&
+ test_rebase_same_head_ $status_f $what_f $cmp_f " --am --no-ff" "$*"
test_rebase_same_head_ $status_n $what_n $cmp_n " --merge" "$*" &&
test_rebase_same_head_ $status_f $what_f $cmp_f " --merge --no-ff" "$*"
}
diff --git a/t/t3433-rebase-options-compatibility.sh b/t/t3433-rebase-options-compatibility.sh
index bd4d2d2f63..a07e1f276b 100755
--- a/t/t3433-rebase-options-compatibility.sh
+++ b/t/t3433-rebase-options-compatibility.sh
@@ -51,9 +51,9 @@ test_expect_success '--ignore-whitespace works with am backend' '
new line 2
line 3
EOF
- test_must_fail git rebase main side &&
+ test_must_fail git rebase --am main side &&
git rebase --abort &&
- git rebase --ignore-whitespace main side &&
+ git rebase --am --ignore-whitespace main side &&
test_cmp expect file
'
@@ -71,7 +71,7 @@ test_expect_success '--ignore-whitespace works with interactive backend' '
test_expect_success '--committer-date-is-author-date works with am backend' '
git commit --amend &&
- git rebase --committer-date-is-author-date HEAD^ &&
+ git rebase --am --committer-date-is-author-date HEAD^ &&
git show HEAD --pretty="format:%ai" >authortime &&
git show HEAD --pretty="format:%ci" >committertime &&
test_cmp authortime committertime
@@ -103,7 +103,7 @@ test_expect_success '--committer-date-is-author-date works with rebase -r' '
# sets to +0530.
test_expect_success '--ignore-date works with am backend' '
git commit --amend --date="$GIT_AUTHOR_DATE" &&
- git rebase --ignore-date HEAD^ &&
+ git rebase --am --ignore-date HEAD^ &&
git show HEAD --pretty="format:%ai" >authortime &&
grep "+0000" authortime
'
diff --git a/t/t5407-post-rewrite-hook.sh b/t/t5407-post-rewrite-hook.sh
index 7344253bfb..a8a73616e4 100755
--- a/t/t5407-post-rewrite-hook.sh
+++ b/t/t5407-post-rewrite-hook.sh
@@ -53,10 +53,10 @@ test_expect_success 'git commit --amend --no-post-rewrite' '
test ! -f post-rewrite.data
'
-test_expect_success 'git rebase' '
+test_expect_success 'git rebase --am' '
git reset --hard D &&
clear_hook_input &&
- test_must_fail git rebase --onto A B &&
+ test_must_fail git rebase --am --onto A B &&
echo C > foo &&
git add foo &&
git rebase --continue &&
@@ -68,10 +68,10 @@ test_expect_success 'git rebase' '
verify_hook_input
'
-test_expect_success 'git rebase --skip' '
+test_expect_success 'git rebase --am --skip' '
git reset --hard D &&
clear_hook_input &&
- test_must_fail git rebase --onto A B &&
+ test_must_fail git rebase --am --onto A B &&
test_must_fail git rebase --skip &&
echo D > foo &&
git add foo &&
@@ -84,10 +84,10 @@ test_expect_success 'git rebase --skip' '
verify_hook_input
'
-test_expect_success 'git rebase --skip the last one' '
+test_expect_success 'git rebase --am --skip the last one' '
git reset --hard F &&
clear_hook_input &&
- test_must_fail git rebase --onto D A &&
+ test_must_fail git rebase --am --onto D A &&
git rebase --skip &&
echo rebase >expected.args &&
cat >expected.data <<-EOF &&
diff --git a/t/t7512-status-help.sh b/t/t7512-status-help.sh
index 66d7a62797..d22b0acf2a 100755
--- a/t/t7512-status-help.sh
+++ b/t/t7512-status-help.sh
@@ -71,10 +71,10 @@ test_expect_success 'prepare for rebase conflicts' '
'
-test_expect_success 'status when rebase in progress before resolving conflicts' '
+test_expect_success 'status when rebase --am in progress before resolving conflicts' '
test_when_finished "git rebase --abort" &&
ONTO=$(git rev-parse --short HEAD^^) &&
- test_must_fail git rebase HEAD^ --onto HEAD^^ &&
+ test_must_fail git rebase --am HEAD^ --onto HEAD^^ &&
cat >expected <<EOF &&
rebase in progress; onto $ONTO
You are currently rebasing branch '\''rebase_conflicts'\'' on '\''$ONTO'\''.
@@ -94,11 +94,11 @@ EOF
'
-test_expect_success 'status when rebase in progress before rebase --continue' '
+test_expect_success 'status when rebase --am in progress before rebase --continue' '
git reset --hard rebase_conflicts &&
test_when_finished "git rebase --abort" &&
ONTO=$(git rev-parse --short HEAD^^) &&
- test_must_fail git rebase HEAD^ --onto HEAD^^ &&
+ test_must_fail git rebase --am HEAD^ --onto HEAD^^ &&
echo three >main.txt &&
git add main.txt &&
cat >expected <<EOF &&
@@ -688,7 +688,7 @@ EOF
'
-test_expect_success 'status when rebase conflicts with statushints disabled' '
+test_expect_success 'status when rebase --am conflicts with statushints disabled' '
git reset --hard master &&
git checkout -b statushints_disabled &&
test_when_finished "git config --local advice.statushints true" &&
@@ -698,7 +698,7 @@ test_expect_success 'status when rebase conflicts with statushints disabled' '
test_commit three_statushints main.txt three &&
test_when_finished "git rebase --abort" &&
ONTO=$(git rev-parse --short HEAD^^) &&
- test_must_fail git rebase HEAD^ --onto HEAD^^ &&
+ test_must_fail git rebase --am HEAD^ --onto HEAD^^ &&
cat >expected <<EOF &&
rebase in progress; onto $ONTO
You are currently rebasing branch '\''statushints_disabled'\'' on '\''$ONTO'\''.
--
gitgitgadget
^ permalink raw reply related [flat|nested] 161+ messages in thread
* [PATCH v2 13/15] rebase tests: repeat some tests using the merge backend instead of am
2019-12-23 18:49 ` [PATCH v2 " Elijah Newren via GitGitGadget
` (11 preceding siblings ...)
2019-12-23 18:49 ` [PATCH v2 12/15] rebase tests: mark tests specific to the am-backend with --am Elijah Newren via GitGitGadget
@ 2019-12-23 18:49 ` Elijah Newren via GitGitGadget
2019-12-23 18:49 ` [PATCH v2 14/15] rebase: make the backend configurable via config setting Elijah Newren via GitGitGadget
` (2 subsequent siblings)
15 siblings, 0 replies; 161+ messages in thread
From: Elijah Newren via GitGitGadget @ 2019-12-23 18:49 UTC (permalink / raw)
To: git
Cc: Johannes.Schindelin, phillip.wood, liu.denton, gitster, plroskin,
alban.gruin, szeder.dev, Junio C Hamano, Elijah Newren
From: Elijah Newren <newren@gmail.com>
In order to ensure the merge/interactive backend gets similar coverage
to the am one, add some tests for cases where previously only the am
backend was tested.
Signed-off-by: Elijah Newren <newren@gmail.com>
---
t/t5520-pull.sh | 17 +++++++++++++++--
t/t6047-diff3-conflict-markers.sh | 13 +++++++++++--
2 files changed, 26 insertions(+), 4 deletions(-)
diff --git a/t/t5520-pull.sh b/t/t5520-pull.sh
index 602d996a33..3fff6a06fa 100755
--- a/t/t5520-pull.sh
+++ b/t/t5520-pull.sh
@@ -277,14 +277,27 @@ test_expect_success '--rebase' '
test_cmp expect actual
'
-test_expect_success '--rebase fast forward' '
+test_expect_success '--rebase (merge) fast forward' '
git reset --hard before-rebase &&
git checkout -b ff &&
echo another modification >file &&
git commit -m third file &&
git checkout to-rebase &&
- git pull --rebase . ff &&
+ git -c rebase.backend=merge pull --rebase . ff &&
+ test_cmp_rev HEAD ff &&
+
+ # The above only validates the result. Did we actually bypass rebase?
+ git reflog -1 >reflog.actual &&
+ sed "s/^[0-9a-f][0-9a-f]*/OBJID/" reflog.actual >reflog.fuzzy &&
+ echo "OBJID HEAD@{0}: pull --rebase . ff: Fast-forward" >reflog.expected &&
+ test_cmp reflog.expected reflog.fuzzy
+'
+
+test_expect_success '--rebase (am) fast forward' '
+ git reset --hard before-rebase &&
+
+ git -c rebase.backend=am pull --rebase . ff &&
test_cmp_rev HEAD ff &&
# The above only validates the result. Did we actually bypass rebase?
diff --git a/t/t6047-diff3-conflict-markers.sh b/t/t6047-diff3-conflict-markers.sh
index 860542aad0..d383ce8130 100755
--- a/t/t6047-diff3-conflict-markers.sh
+++ b/t/t6047-diff3-conflict-markers.sh
@@ -186,7 +186,7 @@ test_expect_success 'check multiple merge bases' '
)
'
-test_expect_success 'rebase describes fake ancestor base' '
+test_expect_success 'rebase --merge describes parent of commit being picked' '
test_create_repo rebase &&
(
cd rebase &&
@@ -194,7 +194,16 @@ test_expect_success 'rebase describes fake ancestor base' '
test_commit master file &&
git checkout -b side HEAD^ &&
test_commit side file &&
- test_must_fail git -c merge.conflictstyle=diff3 rebase master &&
+ test_must_fail git -c merge.conflictstyle=diff3 rebase --merge master &&
+ grep "||||||| parent of" file
+ )
+'
+
+test_expect_success 'rebase --am describes fake ancestor base' '
+ (
+ cd rebase &&
+ git rebase --abort &&
+ test_must_fail git -c merge.conflictstyle=diff3 rebase --am master &&
grep "||||||| constructed merge base" file
)
'
--
gitgitgadget
^ permalink raw reply related [flat|nested] 161+ messages in thread
* [PATCH v2 14/15] rebase: make the backend configurable via config setting
2019-12-23 18:49 ` [PATCH v2 " Elijah Newren via GitGitGadget
` (12 preceding siblings ...)
2019-12-23 18:49 ` [PATCH v2 13/15] rebase tests: repeat some tests using the merge backend instead of am Elijah Newren via GitGitGadget
@ 2019-12-23 18:49 ` Elijah Newren via GitGitGadget
2019-12-23 18:49 ` [PATCH v2 15/15] rebase: change the default backend from "am" to "merge" Elijah Newren via GitGitGadget
2019-12-24 19:54 ` [PATCH v3 00/15] rebase: make the default backend configurable Elijah Newren via GitGitGadget
15 siblings, 0 replies; 161+ messages in thread
From: Elijah Newren via GitGitGadget @ 2019-12-23 18:49 UTC (permalink / raw)
To: git
Cc: Johannes.Schindelin, phillip.wood, liu.denton, gitster, plroskin,
alban.gruin, szeder.dev, Junio C Hamano, Elijah Newren
From: Elijah Newren <newren@gmail.com>
Signed-off-by: Elijah Newren <newren@gmail.com>
---
Documentation/config/rebase.txt | 8 ++++++++
builtin/rebase.c | 31 ++++++++++++++++++++++++-------
2 files changed, 32 insertions(+), 7 deletions(-)
diff --git a/Documentation/config/rebase.txt b/Documentation/config/rebase.txt
index d98e32d812..e6ae30c999 100644
--- a/Documentation/config/rebase.txt
+++ b/Documentation/config/rebase.txt
@@ -5,6 +5,14 @@ rebase.useBuiltin::
is always used. Setting this will emit a warning, to alert any
remaining users that setting this now does nothing.
+rebase.backend::
+ Default backend to use for rebasing. Possible choices are
+ 'am' or 'merge' (note that the merge backend is sometimes also
+ refered to as the interactive backend or the interactive
+ machinery elsewhere in the docs). Also, in the future, if the
+ merge backend gains all remaining capabilities of the am
+ backend, this setting may become unused.
+
rebase.stat::
Whether to show a diffstat of what changed upstream since the last
rebase. False by default.
diff --git a/builtin/rebase.c b/builtin/rebase.c
index b7915fc0cb..d602b2da4c 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -60,6 +60,7 @@ enum empty_type {
struct rebase_options {
enum rebase_type type;
enum empty_type empty;
+ const char *default_backend;
const char *state_dir;
struct commit *upstream;
const char *upstream_name;
@@ -103,6 +104,7 @@ struct rebase_options {
#define REBASE_OPTIONS_INIT { \
.type = REBASE_UNSPECIFIED, \
.empty = EMPTY_UNSPECIFIED, \
+ .default_backend = "am", \
.flags = REBASE_NO_QUIET, \
.git_am_opts = ARGV_ARRAY_INIT, \
.git_format_patch_opt = STRBUF_INIT \
@@ -1298,6 +1300,10 @@ static int rebase_config(const char *var, const char *value, void *data)
return 0;
}
+ if (!strcmp(var, "rebase.backend")) {
+ return git_config_string(&opts->default_backend, var, value);
+ }
+
return git_default_config(var, value, data);
}
@@ -1928,9 +1934,23 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
if (strcmp(options.git_am_opts.argv[i], "-q"))
break;
- if (is_interactive(&options) && i >= 0)
- die(_("cannot combine am options with either "
- "interactive or merge options"));
+ if (i >= 0) {
+ if (is_interactive(&options))
+ die(_("cannot combine am options with either "
+ "interactive or merge options"));
+ else
+ options.type = REBASE_AM;
+ }
+ }
+
+ if (options.type == REBASE_UNSPECIFIED) {
+ if (!strcmp(options.default_backend, "merge"))
+ options.type = REBASE_MERGE;
+ else if (!strcmp(options.default_backend, "am"))
+ options.type = REBASE_AM;
+ else
+ die(_("Unknown rebase backend: %s"),
+ options.default_backend);
}
switch (options.type) {
@@ -1943,10 +1963,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
options.state_dir = apply_dir();
break;
default:
- /* the default rebase backend is `--am` */
- options.type = REBASE_AM;
- options.state_dir = apply_dir();
- break;
+ BUG("options.type was just set above; should be unreachable.");
}
if (options.empty == EMPTY_UNSPECIFIED) {
--
gitgitgadget
^ permalink raw reply related [flat|nested] 161+ messages in thread
* [PATCH v2 15/15] rebase: change the default backend from "am" to "merge"
2019-12-23 18:49 ` [PATCH v2 " Elijah Newren via GitGitGadget
` (13 preceding siblings ...)
2019-12-23 18:49 ` [PATCH v2 14/15] rebase: make the backend configurable via config setting Elijah Newren via GitGitGadget
@ 2019-12-23 18:49 ` Elijah Newren via GitGitGadget
2019-12-24 19:54 ` [PATCH v3 00/15] rebase: make the default backend configurable Elijah Newren via GitGitGadget
15 siblings, 0 replies; 161+ messages in thread
From: Elijah Newren via GitGitGadget @ 2019-12-23 18:49 UTC (permalink / raw)
To: git
Cc: Johannes.Schindelin, phillip.wood, liu.denton, gitster, plroskin,
alban.gruin, szeder.dev, Junio C Hamano, Elijah Newren
From: Elijah Newren <newren@gmail.com>
The am-backend drops information and thus limits what we can do:
* lack of full tree information from the original commits means we
cannot do directory rename detection and warn users that they might
want to move some of their new files that they placed in old
directories to prevent their becoming orphaned.[1]
* reduction in context from only having a few lines beyond those
changed means that when context lines are non-unique we can apply
patches incorrectly.[2]
* lack of access to original commits means that conflict marker
annotation has less information available.
Also, the merge/interactive backend have far more abilities, appear to
currently have a slight performance advantage[3] and have room for more
optimizations than the am backend[4] (and work is underway to take
advantage of some of those possibilities).
[1] https://lore.kernel.org/git/xmqqh8jeh1id.fsf@gitster-ct.c.googlers.com/
[2] https://lore.kernel.org/git/CABPp-BGiu2nVMQY_t-rnFR5GQUz_ipyEE8oDocKeO+h+t4Mn4A@mail.gmail.com/
[3] https://public-inbox.org/git/CABPp-BF=ev03WgODk6TMQmuNoatg2kiEe5DR__gJ0OTVqHSnfQ@mail.gmail.com/
[4] https://lore.kernel.org/git/CABPp-BGh7yW69QwxQb13K0HM38NKmQif3A6C6UULEKYnkEJ5vA@mail.gmail.com/
Signed-off-by: Elijah Newren <newren@gmail.com>
---
Documentation/git-rebase.txt | 2 +-
builtin/rebase.c | 4 ++--
t/t5520-pull.sh | 10 ++++++----
t/t9106-git-svn-commit-diff-clobber.sh | 3 ++-
4 files changed, 11 insertions(+), 8 deletions(-)
diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index cf1ac2e359..e819889a31 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -309,7 +309,7 @@ See also INCOMPATIBLE OPTIONS below.
--merge::
Use merging strategies to rebase. When the recursive (default) merge
strategy is used, this allows rebase to be aware of renames on the
- upstream side.
+ upstream side. This is the default.
+
Note that a rebase merge works by replaying each commit from the working
branch on top of the <upstream> branch. Because of this, when a merge
diff --git a/builtin/rebase.c b/builtin/rebase.c
index d602b2da4c..938a8840b1 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -104,7 +104,7 @@ struct rebase_options {
#define REBASE_OPTIONS_INIT { \
.type = REBASE_UNSPECIFIED, \
.empty = EMPTY_UNSPECIFIED, \
- .default_backend = "am", \
+ .default_backend = "merge", \
.flags = REBASE_NO_QUIET, \
.git_am_opts = ARGV_ARRAY_INIT, \
.git_format_patch_opt = STRBUF_INIT \
@@ -1945,7 +1945,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
if (options.type == REBASE_UNSPECIFIED) {
if (!strcmp(options.default_backend, "merge"))
- options.type = REBASE_MERGE;
+ imply_interactive(&options, "--merge");
else if (!strcmp(options.default_backend, "am"))
options.type = REBASE_AM;
else
diff --git a/t/t5520-pull.sh b/t/t5520-pull.sh
index 3fff6a06fa..4f9e7f7ff6 100755
--- a/t/t5520-pull.sh
+++ b/t/t5520-pull.sh
@@ -340,7 +340,7 @@ test_expect_success '--rebase with conflicts shows advice' '
test_tick &&
git commit -m "Create conflict" seq.txt &&
test_must_fail git pull --rebase . seq 2>err >out &&
- test_i18ngrep "Resolve all conflicts manually" out
+ test_i18ngrep "Resolve all conflicts manually" err
'
test_expect_success 'failed --rebase shows advice' '
@@ -354,7 +354,7 @@ test_expect_success 'failed --rebase shows advice' '
git checkout -f -b fails-to-rebase HEAD^ &&
test_commit v2-without-cr file "2" file2-lf &&
test_must_fail git pull --rebase . diverging 2>err >out &&
- test_i18ngrep "Resolve all conflicts manually" out
+ test_i18ngrep "Resolve all conflicts manually" err
'
test_expect_success '--rebase fails with multiple branches' '
@@ -774,8 +774,10 @@ test_expect_success 'git pull --rebase does not reapply old patches' '
(
cd dst &&
test_must_fail git pull --rebase &&
- find .git/rebase-apply -name "000*" >patches &&
- test_line_count = 1 patches
+ cat .git/rebase-merge/done .git/rebase-merge/git-rebase-todo >work &&
+ grep -v -e \# -e ^$ work >patches &&
+ test_line_count = 1 patches &&
+ rm -f work
)
'
diff --git a/t/t9106-git-svn-commit-diff-clobber.sh b/t/t9106-git-svn-commit-diff-clobber.sh
index dbe8deac0d..aec45bca3b 100755
--- a/t/t9106-git-svn-commit-diff-clobber.sh
+++ b/t/t9106-git-svn-commit-diff-clobber.sh
@@ -92,7 +92,8 @@ test_expect_success 'multiple dcommit from git svn will not clobber svn' "
test_expect_success 'check that rebase really failed' '
- test -d .git/rebase-apply
+ git status >output &&
+ grep currently.rebasing output
'
test_expect_success 'resolve, continue the rebase and dcommit' "
--
gitgitgadget
^ permalink raw reply related [flat|nested] 161+ messages in thread
* Re: [PATCH v2 11/15] contrib: change the prompt for interactive-based rebases
2019-12-23 18:49 ` [PATCH v2 11/15] contrib: change the prompt for interactive-based rebases Elijah Newren via GitGitGadget
@ 2019-12-23 22:00 ` Denton Liu
0 siblings, 0 replies; 161+ messages in thread
From: Denton Liu @ 2019-12-23 22:00 UTC (permalink / raw)
To: Elijah Newren via GitGitGadget
Cc: git, Johannes.Schindelin, phillip.wood, gitster, plroskin,
alban.gruin, szeder.dev, Elijah Newren
Hi Elijah,
> Subject: contrib: change the prompt for interactive-based rebases
I'll also echo Gábor's comments and suggest that you use "git-prompt"
for the change area.
On Mon, Dec 23, 2019 at 06:49:55PM +0000, Elijah Newren via GitGitGadget wrote:
> From: Elijah Newren <newren@gmail.com>
>
> In the past, we had different prompts for different types of rebases:
> REBASE: for am-based rebases
> REBASE-m: for merge-based rebases
> REBASE-i: for interactive-based rebases
>
> It's not clear why this distinction was necessary or helpful; when the
> prompt was added in commit e75201963f67 ("Improve bash prompt to detect
> various states like an unfinished merge", 2007-09-30), it simply added
> these three different types. Perhaps there was a useful purpose back
> then, but there have been some changes:
>
> * The merge backend was deleted after being implemented on top of the
> interactive backend, causing the prompt for merge-based rebases to
> change from REBASE-m to REBASE-i.
> * The interactive backend is used for multiple different types of
> non-interactive rebases, so the "-i" part of the prompt doesn't
> really mean what it used to.
> * Rebase backends have gained more abilities and have a great deal of
> overlap, sometimes making it hard to distinguish them.
> * Behavioral differences between the backends have also been ironed
> out.
> * We want to change the default backend from am to interactive, which
> means people would get "REBASE-i" by default if we didn't change
> the prompt, and only if they specified --am or --whitespace or -C
> would they get the "REBASE" prompt.
> * In the future, we plan to have "--whitespace", "-C", and even "--am"
> run the interactive backend once it can handle everything the
> am-backend can.
>
> For all these reasons, make the prompt for any type of rebase just be
> "REBASE".
>
> Signed-off-by: Elijah Newren <newren@gmail.com>
> ---
> contrib/completion/git-prompt.sh | 4 ++--
> t/t9903-bash-prompt.sh | 8 ++++----
> 2 files changed, 6 insertions(+), 6 deletions(-)
>
> diff --git a/contrib/completion/git-prompt.sh b/contrib/completion/git-prompt.sh
> index 1d510cd47b..8f8a22ba60 100644
> --- a/contrib/completion/git-prompt.sh
> +++ b/contrib/completion/git-prompt.sh
> @@ -430,9 +430,9 @@ __git_ps1 ()
> __git_eread "$g/rebase-merge/msgnum" step
> __git_eread "$g/rebase-merge/end" total
> if [ -f "$g/rebase-merge/interactive" ]; then
> - r="|REBASE-i"
> + r="|REBASE"
> else
> - r="|REBASE-m"
> + r="|REBASE"
We should just drop the if here since both arms are the same..
Thanks,
Denton
> fi
> else
> if [ -d "$g/rebase-apply" ]; then
> diff --git a/t/t9903-bash-prompt.sh b/t/t9903-bash-prompt.sh
> index 88bc733ad6..7ca35d358d 100755
> --- a/t/t9903-bash-prompt.sh
> +++ b/t/t9903-bash-prompt.sh
> @@ -163,7 +163,7 @@ test_expect_success 'prompt - inside bare repository' '
> '
>
> test_expect_success 'prompt - interactive rebase' '
> - printf " (b1|REBASE-i 2/3)" >expected &&
> + printf " (b1|REBASE 2/3)" >expected &&
> write_script fake_editor.sh <<-\EOF &&
> echo "exec echo" >"$1"
> echo "edit $(git log -1 --format="%h")" >>"$1"
> @@ -180,7 +180,7 @@ test_expect_success 'prompt - interactive rebase' '
> '
>
> test_expect_success 'prompt - rebase merge' '
> - printf " (b2|REBASE-i 1/3)" >expected &&
> + printf " (b2|REBASE 1/3)" >expected &&
> git checkout b2 &&
> test_when_finished "git checkout master" &&
> test_must_fail git rebase --merge b1 b2 &&
> @@ -189,11 +189,11 @@ test_expect_success 'prompt - rebase merge' '
> test_cmp expected "$actual"
> '
>
> -test_expect_success 'prompt - rebase' '
> +test_expect_success 'prompt - rebase am' '
> printf " (b2|REBASE 1/3)" >expected &&
> git checkout b2 &&
> test_when_finished "git checkout master" &&
> - test_must_fail git rebase b1 b2 &&
> + test_must_fail git rebase --am b1 b2 &&
> test_when_finished "git rebase --abort" &&
> __git_ps1 >"$actual" &&
> test_cmp expected "$actual"
> --
> gitgitgadget
>
^ permalink raw reply [flat|nested] 161+ messages in thread
* [PATCH v3 00/15] rebase: make the default backend configurable
2019-12-23 18:49 ` [PATCH v2 " Elijah Newren via GitGitGadget
` (14 preceding siblings ...)
2019-12-23 18:49 ` [PATCH v2 15/15] rebase: change the default backend from "am" to "merge" Elijah Newren via GitGitGadget
@ 2019-12-24 19:54 ` Elijah Newren via GitGitGadget
2019-12-24 19:54 ` [PATCH v3 01/15] rebase: extend the options for handling of empty commits Elijah Newren via GitGitGadget
` (15 more replies)
15 siblings, 16 replies; 161+ messages in thread
From: Elijah Newren via GitGitGadget @ 2019-12-24 19:54 UTC (permalink / raw)
To: git
Cc: Johannes.Schindelin, phillip.wood, liu.denton, gitster, plroskin,
alban.gruin, szeder.dev, Junio C Hamano
This series does a lot of work around making the default rebase backend
configurable, and switching the default from the am backend to the
merge/interactive one.
See the cover letter for v1[*] for a more detailed motivation for the
series, the type of changes being made, and areas I'd like to reviewers to
focus on.
Changes since v2:
* Tweaked patch 11 (commit message, and condensing if-else block that was
same on both sides), as suggested by Denton
[*]
https://lore.kernel.org/git/pull.679.git.git.1576861788.gitgitgadget@gmail.com/
Elijah Newren (15):
rebase: extend the options for handling of empty commits
t3406: simplify an already simple test
rebase, sequencer: remove the broken GIT_QUIET handling
rebase: make sure to pass along the quiet flag to the sequencer
rebase: fix handling of restrict_revision
t3432: make these tests work with either am or merge backends
rebase: allow more types of rebases to fast-forward
git-rebase.txt: add more details about behavioral differences of
backends
rebase: move incompatibility checks between backend options a bit
earlier
rebase: add an --am option
git-prompt: change the prompt for interactive-based rebases
rebase tests: mark tests specific to the am-backend with --am
rebase tests: repeat some tests using the merge backend instead of am
rebase: make the backend configurable via config setting
rebase: change the default backend from "am" to "merge"
Documentation/config/rebase.txt | 8 ++
Documentation/git-rebase.txt | 150 ++++++++++++++++----
builtin/rebase.c | 177 +++++++++++++++++++-----
contrib/completion/git-prompt.sh | 6 +-
rebase-interactive.c | 4 +-
rebase-interactive.h | 2 +-
sequencer.c | 80 ++++++++---
sequencer.h | 6 +-
t/t3400-rebase.sh | 36 ++++-
t/t3401-rebase-and-am-rename.sh | 4 +-
t/t3404-rebase-interactive.sh | 2 +-
t/t3406-rebase-message.sh | 19 ++-
t/t3407-rebase-abort.sh | 6 +-
t/t3420-rebase-autostash.sh | 2 +-
t/t3421-rebase-topology-linear.sh | 4 +-
t/t3424-rebase-empty.sh | 89 ++++++++++++
t/t3425-rebase-topology-merges.sh | 8 +-
t/t3427-rebase-subtree.sh | 16 ++-
t/t3432-rebase-fast-forward.sh | 54 ++++----
t/t3433-rebase-options-compatibility.sh | 13 +-
t/t5407-post-rewrite-hook.sh | 12 +-
t/t5520-pull.sh | 27 +++-
t/t6047-diff3-conflict-markers.sh | 13 +-
t/t7512-status-help.sh | 12 +-
t/t9106-git-svn-commit-diff-clobber.sh | 3 +-
t/t9903-bash-prompt.sh | 8 +-
26 files changed, 576 insertions(+), 185 deletions(-)
create mode 100755 t/t3424-rebase-empty.sh
base-commit: 12029dc57db23baef008e77db1909367599210ee
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-679%2Fnewren%2Frebase-fixes-v3
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-679/newren/rebase-fixes-v3
Pull-Request: https://github.com/git/git/pull/679
Range-diff vs v2:
1: 1c2b77e94d = 1: 1c2b77e94d rebase: extend the options for handling of empty commits
2: bd3c5ec155 = 2: bd3c5ec155 t3406: simplify an already simple test
3: 49388b79fd = 3: 49388b79fd rebase, sequencer: remove the broken GIT_QUIET handling
4: 478479358f = 4: 478479358f rebase: make sure to pass along the quiet flag to the sequencer
5: ee26f5a161 = 5: ee26f5a161 rebase: fix handling of restrict_revision
6: 34a69def33 = 6: 34a69def33 t3432: make these tests work with either am or merge backends
7: f2c92853b4 = 7: f2c92853b4 rebase: allow more types of rebases to fast-forward
8: b307340f7c = 8: b307340f7c git-rebase.txt: add more details about behavioral differences of backends
9: 7c3f2e07f3 = 9: 7c3f2e07f3 rebase: move incompatibility checks between backend options a bit earlier
10: 1df11f0b51 = 10: 1df11f0b51 rebase: add an --am option
11: 94b5a3051d ! 11: ff43593211 contrib: change the prompt for interactive-based rebases
@@ -1,6 +1,6 @@
Author: Elijah Newren <newren@gmail.com>
- contrib: change the prompt for interactive-based rebases
+ git-prompt: change the prompt for interactive-based rebases
In the past, we had different prompts for different types of rebases:
REBASE: for am-based rebases
@@ -40,17 +40,18 @@
--- a/contrib/completion/git-prompt.sh
+++ b/contrib/completion/git-prompt.sh
@@
+ __git_eread "$g/rebase-merge/head-name" b
__git_eread "$g/rebase-merge/msgnum" step
__git_eread "$g/rebase-merge/end" total
- if [ -f "$g/rebase-merge/interactive" ]; then
+- if [ -f "$g/rebase-merge/interactive" ]; then
- r="|REBASE-i"
-+ r="|REBASE"
- else
+- else
- r="|REBASE-m"
-+ r="|REBASE"
- fi
+- fi
++ r="|REBASE"
else
if [ -d "$g/rebase-apply" ]; then
+ __git_eread "$g/rebase-apply/next" step
diff --git a/t/t9903-bash-prompt.sh b/t/t9903-bash-prompt.sh
--- a/t/t9903-bash-prompt.sh
12: c905d288bf = 12: 99388f24e5 rebase tests: mark tests specific to the am-backend with --am
13: 0287881361 = 13: c2ba6317bf rebase tests: repeat some tests using the merge backend instead of am
14: ec782e711c = 14: 8bec6df51a rebase: make the backend configurable via config setting
15: 7adcbc0bc5 = 15: 044853fd61 rebase: change the default backend from "am" to "merge"
--
gitgitgadget
^ permalink raw reply [flat|nested] 161+ messages in thread
* [PATCH v3 01/15] rebase: extend the options for handling of empty commits
2019-12-24 19:54 ` [PATCH v3 00/15] rebase: make the default backend configurable Elijah Newren via GitGitGadget
@ 2019-12-24 19:54 ` Elijah Newren via GitGitGadget
2020-01-07 14:37 ` Phillip Wood
2019-12-24 19:54 ` [PATCH v3 02/15] t3406: simplify an already simple test Elijah Newren via GitGitGadget
` (14 subsequent siblings)
15 siblings, 1 reply; 161+ messages in thread
From: Elijah Newren via GitGitGadget @ 2019-12-24 19:54 UTC (permalink / raw)
To: git
Cc: Johannes.Schindelin, phillip.wood, liu.denton, gitster, plroskin,
alban.gruin, szeder.dev, Junio C Hamano, Elijah Newren
From: Elijah Newren <newren@gmail.com>
Extend the interactive machinery with the ability to handle the full
spread of options for how to handle commits that either start or become
empty (by "become empty" I mean the changes in a commit are a subset of
changes that exist upstream, so the net effect of applying the commit is
no changes). Introduce a new command line flag for selecting the
desired behavior:
--empty={drop,keep,ask}
with the definitions:
drop: drop empty commits
keep: keep empty commits
ask: provide the user a chance to interact and pick what to do with
empty commits on a case-by-case basis
Note that traditionally, am-based rebases have always dropped commits
that either started or became empty, while interactive-based rebases
have defaulted to ask (and provided an option to keep commits that
started empty). This difference made sense since users of an am-based
rebase just wanted to quickly batch apply a sequence of commits, while
users editing a todo list will likely want the chance to interact and
handle unusual cases on a case-by-case basis. However, not all rebases
using the interactive machinery are explicitly interactive anymore. In
particular --merge was always meant to behave more like --am: just
rebase a batch of commits without popping up a todo list.
If the --empty flag is not specified, pick defaults as follows:
explicitly interactive: ask
--exec: keep (exec is about checking existing commits, and often
used without actually changing the base. Thus the
expectation is that the user doesn't necessarily want
anything to change; they just want to test).
otherwise: drop
Also, this commit makes --keep-empty just imply --empty=keep, and hides
it from help so that we aren't confusing users with different ways to do
the same thing. (I could have added a --drop-empty flag, but then that
invites users to specify both --keep-empty and --drop-empty and we have
to add sanity checking around that; it seems cleaner to have a single
multi-valued option.) This actually fixes --keep-empty too; previously,
it only meant to sometimes keep empty commits, in particular commits
which started empty would be kept. But it would still error out and ask
the user what to do with commits that became empty. Now it keeps empty
commits, as instructed.
Signed-off-by: Elijah Newren <newren@gmail.com>
---
Documentation/git-rebase.txt | 35 ++++++------
builtin/rebase.c | 83 +++++++++++++++++++++++++---
rebase-interactive.c | 4 +-
rebase-interactive.h | 2 +-
sequencer.c | 74 +++++++++++++++++++------
sequencer.h | 6 ++-
t/t3421-rebase-topology-linear.sh | 4 +-
t/t3424-rebase-empty.sh | 89 +++++++++++++++++++++++++++++++
t/t3427-rebase-subtree.sh | 16 +++---
9 files changed, 263 insertions(+), 50 deletions(-)
create mode 100755 t/t3424-rebase-empty.sh
diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index 1d0e2d27cc..ff32ca1080 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -258,9 +258,25 @@ See also INCOMPATIBLE OPTIONS below.
original branch. The index and working tree are also left
unchanged as a result.
+--empty={drop,keep,ask}::
+ How to handle commits that become empty (because they contain a
+ subset of already upstream changes) or start empty. With drop
+ (the default), commits that start or become empty are dropped.
+ With keep (implied by --exec), such commits are kept. With ask
+ (implied by --interactive), the rebase will halt when an empty
+ commit is applied allowing you to choose whether to drop it or
+ commit it. Also with ask, if the rebase is interactive then
+ commits which start empty will be commented out in the todo
+ action list (giving you a chance to uncomment).
++
+Note that this has no effect on commits which are already upstream (as
+can be checked via `git log --cherry-mark ...`), which are always
+dropped by rebase.
++
+See also INCOMPATIBLE OPTIONS below.
+
--keep-empty::
- Keep the commits that do not change anything from its
- parents in the result.
+ Deprecated alias for what is now known as --empty=keep.
+
See also INCOMPATIBLE OPTIONS below.
@@ -569,6 +585,7 @@ are incompatible with the following options:
* --interactive
* --exec
* --keep-empty
+ * --empty=
* --edit-todo
* --root when used in combination with --onto
@@ -580,6 +597,7 @@ In addition, the following pairs of options are incompatible:
* --preserve-merges and --ignore-whitespace
* --preserve-merges and --committer-date-is-author-date
* --preserve-merges and --ignore-date
+ * --preserve-merges and --empty=
* --keep-base and --onto
* --keep-base and --root
@@ -588,19 +606,6 @@ BEHAVIORAL DIFFERENCES
There are some subtle differences how the backends behave.
-Empty commits
-~~~~~~~~~~~~~
-
-The am backend drops any "empty" commits, regardless of whether the
-commit started empty (had no changes relative to its parent to
-start with) or ended empty (all changes were already applied
-upstream in other commits).
-
-The interactive backend drops commits by default that
-started empty and halts if it hits a commit that ended up empty.
-The `--keep-empty` option exists for the interactive backend to allow
-it to keep commits that started empty.
-
Directory rename detection
~~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/builtin/rebase.c b/builtin/rebase.c
index ddf33bc9d4..6903249307 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -50,8 +50,16 @@ enum rebase_type {
REBASE_PRESERVE_MERGES
};
+enum empty_type {
+ EMPTY_UNSPECIFIED = -1,
+ EMPTY_DROP,
+ EMPTY_KEEP,
+ EMPTY_ASK
+};
+
struct rebase_options {
enum rebase_type type;
+ enum empty_type empty;
const char *state_dir;
struct commit *upstream;
const char *upstream_name;
@@ -77,7 +85,6 @@ struct rebase_options {
const char *action;
int signoff;
int allow_rerere_autoupdate;
- int keep_empty;
int autosquash;
int ignore_whitespace;
char *gpg_sign_opt;
@@ -95,6 +102,7 @@ struct rebase_options {
#define REBASE_OPTIONS_INIT { \
.type = REBASE_UNSPECIFIED, \
+ .empty = EMPTY_UNSPECIFIED, \
.flags = REBASE_NO_QUIET, \
.git_am_opts = ARGV_ARRAY_INIT, \
.git_format_patch_opt = STRBUF_INIT \
@@ -114,6 +122,10 @@ static struct replay_opts get_replay_opts(const struct rebase_options *opts)
replay.allow_rerere_auto = opts->allow_rerere_autoupdate;
replay.allow_empty = 1;
replay.allow_empty_message = opts->allow_empty_message;
+ replay.drop_redundant_commits = (opts->empty == EMPTY_DROP);
+ replay.keep_redundant_commits = (opts->empty == EMPTY_KEEP);
+ replay.ask_on_initially_empty = (opts->empty == EMPTY_ASK &&
+ !(opts->flags & REBASE_INTERACTIVE_EXPLICIT));
replay.verbose = opts->flags & REBASE_VERBOSE;
replay.reschedule_failed_exec = opts->reschedule_failed_exec;
replay.committer_date_is_author_date =
@@ -389,7 +401,10 @@ static int run_rebase_interactive(struct rebase_options *opts,
git_config_get_bool("rebase.abbreviatecommands", &abbreviate_commands);
- flags |= opts->keep_empty ? TODO_LIST_KEEP_EMPTY : 0;
+ flags |= (opts->empty == EMPTY_DROP) ? TODO_LIST_DROP_EMPTY : 0;
+ flags |= (opts->empty == EMPTY_ASK &&
+ opts->flags & REBASE_INTERACTIVE_EXPLICIT) ?
+ TODO_LIST_ASK_EMPTY : 0;
flags |= abbreviate_commands ? TODO_LIST_ABBREVIATE_CMDS : 0;
flags |= opts->rebase_merges ? TODO_LIST_REBASE_MERGES : 0;
flags |= opts->rebase_cousins > 0 ? TODO_LIST_REBASE_COUSINS : 0;
@@ -453,6 +468,19 @@ static int run_rebase_interactive(struct rebase_options *opts,
return ret;
}
+static int parse_opt_keep_empty(const struct option *opt, const char *arg,
+ int unset)
+{
+ struct rebase_options *opts = opt->value;
+
+ BUG_ON_OPT_NEG(unset);
+ BUG_ON_OPT_ARG(arg);
+
+ opts->empty = EMPTY_KEEP;
+ opts->type = REBASE_INTERACTIVE;
+ return 0;
+}
+
static const char * const builtin_rebase_interactive_usage[] = {
N_("git rebase--interactive [<options>]"),
NULL
@@ -466,7 +494,10 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)
struct option options[] = {
OPT_NEGBIT(0, "ff", &opts.flags, N_("allow fast-forward"),
REBASE_FORCE),
- OPT_BOOL(0, "keep-empty", &opts.keep_empty, N_("keep empty commits")),
+ { OPTION_CALLBACK, 'k', "keep-empty", &options, NULL,
+ N_("(DEPRECATED) keep empty commits"),
+ PARSE_OPT_NOARG | PARSE_OPT_NONEG | PARSE_OPT_HIDDEN,
+ parse_opt_keep_empty },
OPT_BOOL(0, "allow-empty-message", &opts.allow_empty_message,
N_("allow commits with empty messages")),
OPT_BOOL(0, "rebase-merges", &opts.rebase_merges, N_("rebase merge commits")),
@@ -1166,7 +1197,7 @@ static int run_specific_rebase(struct rebase_options *opts, enum action action)
opts->allow_rerere_autoupdate ?
opts->allow_rerere_autoupdate == RERERE_AUTOUPDATE ?
"--rerere-autoupdate" : "--no-rerere-autoupdate" : "");
- add_var(&script_snippet, "keep_empty", opts->keep_empty ? "yes" : "");
+ add_var(&script_snippet, "empty", opts->empty == EMPTY_KEEP ? "yes" : "");
add_var(&script_snippet, "autosquash", opts->autosquash ? "t" : "");
add_var(&script_snippet, "gpg_sign_opt", opts->gpg_sign_opt);
add_var(&script_snippet, "cmd", opts->cmd);
@@ -1360,6 +1391,29 @@ static int parse_opt_interactive(const struct option *opt, const char *arg,
return 0;
}
+static enum empty_type parse_empty_value(const char *value)
+{
+ if (!strcasecmp(value, "drop"))
+ return EMPTY_DROP;
+ else if (!strcasecmp(value, "keep"))
+ return EMPTY_KEEP;
+ else if (!strcasecmp(value, "ask"))
+ return EMPTY_ASK;
+
+ die(_("unrecognized empty type '%s'; valid values are \"drop\", \"keep\", and \"ask\"."), value);
+}
+
+static int parse_opt_empty(const struct option *opt, const char *arg, int unset)
+{
+ struct rebase_options *options = opt->value;
+ enum empty_type value = parse_empty_value(arg);
+
+ BUG_ON_OPT_NEG(unset);
+
+ options->empty = value;
+ return 0;
+}
+
static void NORETURN error_on_missing_default_upstream(void)
{
struct branch *current_branch = branch_get(NULL);
@@ -1505,8 +1559,13 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
"ignoring them"),
REBASE_PRESERVE_MERGES, PARSE_OPT_HIDDEN),
OPT_RERERE_AUTOUPDATE(&options.allow_rerere_autoupdate),
- OPT_BOOL('k', "keep-empty", &options.keep_empty,
- N_("preserve empty commits during rebase")),
+ OPT_CALLBACK_F(0, "empty", &options, N_("{drop,keep,ask}"),
+ N_("how to handle empty commits"),
+ PARSE_OPT_NONEG, parse_opt_empty),
+ { OPTION_CALLBACK, 'k', "keep-empty", &options, NULL,
+ N_("(DEPRECATED) keep empty commits"),
+ PARSE_OPT_NOARG | PARSE_OPT_NONEG | PARSE_OPT_HIDDEN,
+ parse_opt_keep_empty },
OPT_BOOL(0, "autosquash", &options.autosquash,
N_("move commits that begin with "
"squash!/fixup! under -i")),
@@ -1770,8 +1829,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
if (!(options.flags & REBASE_NO_QUIET))
argv_array_push(&options.git_am_opts, "-q");
- if (options.keep_empty)
- imply_interactive(&options, "--keep-empty");
+ if (options.empty != EMPTY_UNSPECIFIED)
+ imply_interactive(&options, "--empty");
if (gpg_sign) {
free(options.gpg_sign_opt);
@@ -1856,6 +1915,14 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
break;
}
+ if (options.empty == EMPTY_UNSPECIFIED) {
+ if (options.flags & REBASE_INTERACTIVE_EXPLICIT)
+ options.empty = EMPTY_ASK;
+ else if (exec.nr > 0)
+ options.empty = EMPTY_KEEP;
+ else
+ options.empty = EMPTY_DROP;
+ }
if (reschedule_failed_exec > 0 && !is_interactive(&options))
die(_("--reschedule-failed-exec requires "
"--exec or --interactive"));
diff --git a/rebase-interactive.c b/rebase-interactive.c
index aa18ae82b7..ad82bf77df 100644
--- a/rebase-interactive.c
+++ b/rebase-interactive.c
@@ -28,7 +28,7 @@ static enum missing_commit_check_level get_missing_commit_check_level(void)
return MISSING_COMMIT_CHECK_IGNORE;
}
-void append_todo_help(unsigned keep_empty, int command_count,
+void append_todo_help(unsigned no_ask_empty, int command_count,
const char *shortrevisions, const char *shortonto,
struct strbuf *buf)
{
@@ -81,7 +81,7 @@ void append_todo_help(unsigned keep_empty, int command_count,
strbuf_add_commented_lines(buf, msg, strlen(msg));
- if (!keep_empty) {
+ if (!no_ask_empty) {
msg = _("Note that empty commits are commented out");
strbuf_add_commented_lines(buf, msg, strlen(msg));
}
diff --git a/rebase-interactive.h b/rebase-interactive.h
index 44dbb06311..f531e00ba7 100644
--- a/rebase-interactive.h
+++ b/rebase-interactive.h
@@ -5,7 +5,7 @@ struct strbuf;
struct repository;
struct todo_list;
-void append_todo_help(unsigned keep_empty, int command_count,
+void append_todo_help(unsigned no_ask_empty, int command_count,
const char *shortrevisions, const char *shortonto,
struct strbuf *buf);
int edit_todo_list(struct repository *r, struct todo_list *todo_list,
diff --git a/sequencer.c b/sequencer.c
index 763ccbbc45..d2c11f34b7 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -160,6 +160,9 @@ 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 GIT_PATH_FUNC(rebase_path_ask_on_initially_empty, "rebase-merge/ask_on_initially_empty")
static int git_sequencer_config(const char *k, const char *v, void *cb)
{
@@ -1623,7 +1626,7 @@ static int allow_empty(struct repository *r,
empty_commit = is_original_commit_empty(commit);
if (empty_commit < 0)
return empty_commit;
- if (!empty_commit)
+ if (!empty_commit || opts->ask_on_initially_empty)
return 0;
else
return 1;
@@ -1837,7 +1840,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) {
/*
@@ -2042,13 +2045,20 @@ static int do_pick_commit(struct repository *r,
goto leave;
}
- allow = allow_empty(r, opts, commit);
- if (allow < 0) {
- res = allow;
- goto leave;
- } else if (allow)
- flags |= ALLOW_EMPTY;
- if (!opts->no_commit) {
+ drop_commit = 0;
+ if (opts->drop_redundant_commits && is_index_unchanged(r)) {
+ drop_commit = 1;
+ fprintf(stderr, _("No changes -- Patch already applied."));
+ } else {
+ allow = allow_empty(r, opts, commit);
+ if (allow < 0) {
+ res = allow;
+ goto leave;
+ } else if (allow) {
+ flags |= ALLOW_EMPTY;
+ }
+ }
+ if (!opts->no_commit && !drop_commit) {
if (author || command == TODO_REVERT || (flags & AMEND_MSG))
res = do_commit(r, msg_file, author, opts, flags);
else
@@ -2501,9 +2511,15 @@ static int populate_opts_cb(const char *key, const char *value, void *data)
else if (!strcmp(key, "options.allow-empty-message"))
opts->allow_empty_message =
git_config_bool_or_int(key, value, &error_flag);
+ else if (!strcmp(key, "options.drop-redundant-commits"))
+ opts->drop_redundant_commits =
+ git_config_bool_or_int(key, value, &error_flag);
else if (!strcmp(key, "options.keep-redundant-commits"))
opts->keep_redundant_commits =
git_config_bool_or_int(key, value, &error_flag);
+ else if (!strcmp(key, "options.ask_on_initially_empty"))
+ opts->ask_on_initially_empty =
+ git_config_bool_or_int(key, value, &error_flag);
else if (!strcmp(key, "options.signoff"))
opts->signoff = git_config_bool_or_int(key, value, &error_flag);
else if (!strcmp(key, "options.record-origin"))
@@ -2612,6 +2628,15 @@ 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;
+
+ if (file_exists(rebase_path_ask_on_initially_empty()))
+ opts->ask_on_initially_empty = 1;
+
read_strategy_opts(opts, &buf);
strbuf_release(&buf);
@@ -2695,6 +2720,12 @@ int write_basic_state(struct replay_opts *opts, const char *head_name,
write_file(rebase_path_cdate_is_adate(), "%s", "");
if (opts->ignore_date)
write_file(rebase_path_ignore_date(), "%s", "");
+ 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->ask_on_initially_empty)
+ write_file(rebase_path_ask_on_initially_empty(), "%s", "");
if (opts->reschedule_failed_exec)
write_file(rebase_path_reschedule_failed_exec(), "%s", "");
@@ -3033,9 +3064,15 @@ static int save_opts(struct replay_opts *opts)
if (opts->allow_empty_message)
res |= git_config_set_in_file_gently(opts_file,
"options.allow-empty-message", "true");
+ if (opts->drop_redundant_commits)
+ res |= git_config_set_in_file_gently(opts_file,
+ "options.drop-redundant-commits", "true");
if (opts->keep_redundant_commits)
res |= git_config_set_in_file_gently(opts_file,
"options.keep-redundant-commits", "true");
+ if (opts->ask_on_initially_empty)
+ res |= git_config_set_in_file_gently(opts_file,
+ "options.ask_on_initially_empty", "true");
if (opts->signoff)
res |= git_config_set_in_file_gently(opts_file,
"options.signoff", "true");
@@ -4691,7 +4728,8 @@ 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 drop_empty = flags & TODO_LIST_DROP_EMPTY;
+ int ask_empty = flags & TODO_LIST_ASK_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;
@@ -4746,6 +4784,8 @@ static int make_script_with_merges(struct pretty_print_context *pp,
is_empty = is_original_commit_empty(commit);
if (!is_empty && (commit->object.flags & PATCHSAME))
continue;
+ if (is_empty && drop_empty)
+ continue;
strbuf_reset(&oneline);
pretty_print_commit(pp, commit, &oneline);
@@ -4754,7 +4794,7 @@ 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)
+ if (is_empty && ask_empty)
strbuf_addf(&buf, "%c ", comment_line_char);
strbuf_addf(&buf, "%s %s %s", cmd_pick,
oid_to_hex(&commit->object.oid),
@@ -4922,7 +4962,8 @@ 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;
+ int drop_empty = flags & TODO_LIST_DROP_EMPTY;
+ int ask_empty = flags & TODO_LIST_ASK_EMPTY;
const char *insn = flags & TODO_LIST_ABBREVIATE_CMDS ? "p" : "pick";
int rebase_merges = flags & TODO_LIST_REBASE_MERGES;
@@ -4958,11 +4999,13 @@ 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)
+ if (is_empty && drop_empty)
+ continue;
+ if (is_empty && ask_empty)
strbuf_addf(out, "%c ", comment_line_char);
strbuf_addf(out, "%s %s ", insn,
oid_to_hex(&commit->object.oid));
@@ -5100,7 +5143,8 @@ 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(!(flags & TODO_LIST_ASK_EMPTY),
+ count_commands(todo_list),
shortrevisions, shortonto, &buf);
res = write_message(buf.buf, buf.len, file, 0);
diff --git a/sequencer.h b/sequencer.h
index e9a0e03ea2..1c3abb661c 100644
--- a/sequencer.h
+++ b/sequencer.h
@@ -39,7 +39,9 @@ struct replay_opts {
int allow_rerere_auto;
int allow_empty;
int allow_empty_message;
+ int drop_redundant_commits;
int keep_redundant_commits;
+ int ask_on_initially_empty;
int verbose;
int quiet;
int reschedule_failed_exec;
@@ -134,7 +136,7 @@ int sequencer_rollback(struct repository *repo, struct replay_opts *opts);
int sequencer_skip(struct repository *repo, struct replay_opts *opts);
int sequencer_remove_state(struct replay_opts *opts);
-#define TODO_LIST_KEEP_EMPTY (1U << 0)
+/* #define TODO_LIST_KEEP_EMPTY (1U << 0) */ /* No longer used */
#define TODO_LIST_SHORTEN_IDS (1U << 1)
#define TODO_LIST_ABBREVIATE_CMDS (1U << 2)
#define TODO_LIST_REBASE_MERGES (1U << 3)
@@ -150,6 +152,8 @@ int sequencer_remove_state(struct replay_opts *opts);
* `--onto`, we do not want to re-generate the root commits.
*/
#define TODO_LIST_ROOT_WITH_ONTO (1U << 6)
+#define TODO_LIST_DROP_EMPTY (1U << 7)
+#define TODO_LIST_ASK_EMPTY (1U << 8)
int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,
diff --git a/t/t3421-rebase-topology-linear.sh b/t/t3421-rebase-topology-linear.sh
index 325072b0a3..d23e0bf778 100755
--- a/t/t3421-rebase-topology-linear.sh
+++ b/t/t3421-rebase-topology-linear.sh
@@ -230,7 +230,7 @@ test_run_rebase () {
test_run_rebase success ''
test_run_rebase success -m
test_run_rebase success -i
-test_have_prereq !REBASE_P || test_run_rebase failure -p
+test_have_prereq !REBASE_P || test_run_rebase success -p
test_run_rebase () {
result=$1
@@ -245,7 +245,7 @@ test_run_rebase () {
test_run_rebase success ''
test_run_rebase success -m
test_run_rebase success -i
-test_have_prereq !REBASE_P || test_run_rebase failure -p
+test_have_prereq !REBASE_P || test_run_rebase success -p
test_run_rebase success --rebase-merges
# m
diff --git a/t/t3424-rebase-empty.sh b/t/t3424-rebase-empty.sh
new file mode 100755
index 0000000000..9d52e1417f
--- /dev/null
+++ b/t/t3424-rebase-empty.sh
@@ -0,0 +1,89 @@
+#!/bin/sh
+
+test_description='git rebase of commits that start or become empty'
+
+. ./test-lib.sh
+
+test_expect_success 'setup test repository' '
+ test_write_lines 1 2 3 4 5 6 7 8 9 10 >numbers &&
+ test_write_lines A B C D E F G H I J >letters &&
+ git add numbers letters &&
+ git commit -m A &&
+
+ git branch upstream &&
+ git branch localmods &&
+
+ git checkout upstream &&
+ test_write_lines A B C D E >letters &&
+ git add letters &&
+ git commit -m B &&
+
+ test_write_lines 1 2 3 4 five 6 7 8 9 ten >numbers &&
+ git add numbers &&
+ git commit -m C &&
+
+ git checkout localmods &&
+ test_write_lines 1 2 3 4 five 6 7 8 9 10 >numbers &&
+ git add numbers &&
+ git commit -m C2 &&
+
+ git commit --allow-empty -m D &&
+
+ test_write_lines A B C D E >letters &&
+ git add letters &&
+ git commit -m "Five letters ought to be enough for anybody"
+'
+
+test_expect_success 'rebase --merge --empty=drop' '
+ git checkout -B testing localmods &&
+ git rebase --merge --empty=drop upstream &&
+
+ test_write_lines C B A >expect &&
+ git log --format=%s >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'rebase --merge --empty=keep' '
+ git checkout -B testing localmods &&
+ git rebase --merge --empty=keep upstream &&
+
+ test_write_lines D C2 C B A >expect &&
+ git log --format=%s >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'rebase --merge --empty=ask' '
+ git checkout -B testing localmods &&
+ test_must_fail git rebase --merge --empty=ask upstream &&
+
+ test_must_fail git rebase --skip &&
+ git commit --allow-empty &&
+ git rebase --continue &&
+
+ test_write_lines D C B A >expect &&
+ git log --format=%s >actual &&
+ test_cmp expect actual
+'
+
+GIT_SEQUENCE_EDITOR=: && export GIT_SEQUENCE_EDITOR
+
+test_expect_success 'rebase --interactive --empty=drop' '
+ git checkout -B testing localmods &&
+ git rebase --interactive --empty=drop upstream &&
+
+ test_write_lines C B A >expect &&
+ git log --format=%s >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'rebase --interactive --empty=keep' '
+ git checkout -B testing localmods &&
+ git rebase --interactive --empty=keep upstream &&
+
+ test_write_lines D C2 C B A >expect &&
+ git log --format=%s >actual &&
+ test_cmp expect actual
+'
+
+
+test_done
diff --git a/t/t3427-rebase-subtree.sh b/t/t3427-rebase-subtree.sh
index bec48e6a1f..468ebc1bef 100755
--- a/t/t3427-rebase-subtree.sh
+++ b/t/t3427-rebase-subtree.sh
@@ -85,23 +85,27 @@ test_expect_failure REBASE_P 'Rebase -Xsubtree --keep-empty --preserve-merges --
verbose test "$(commit_message HEAD)" = "Empty commit"
'
-test_expect_success 'Rebase -Xsubtree --keep-empty --onto commit' '
+test_expect_success 'Rebase -Xsubtree --empty=ask --onto commit' '
reset_rebase &&
git checkout -b rebase-onto to-rebase &&
- test_must_fail git rebase -Xsubtree=files_subtree --keep-empty --onto files-master master &&
+ test_must_fail git rebase -Xsubtree=files_subtree --empty=ask --onto files-master master &&
: first pick results in no changes &&
- git rebase --continue &&
+ test_must_fail git rebase --skip &&
+ : last pick was an empty commit that has no changes, but we want to keep it &&
+ git commit --allow-empty &&
verbose test "$(commit_message HEAD~2)" = "master4" &&
verbose test "$(commit_message HEAD~)" = "files_subtree/master5" &&
verbose test "$(commit_message HEAD)" = "Empty commit"
'
-test_expect_success 'Rebase -Xsubtree --keep-empty --rebase-merges --onto commit' '
+test_expect_success 'Rebase -Xsubtree --empty=ask --rebase-merges --onto commit' '
reset_rebase &&
git checkout -b rebase-merges-onto to-rebase &&
- test_must_fail git rebase -Xsubtree=files_subtree --keep-empty --rebase-merges --onto files-master --root &&
+ test_must_fail git rebase -Xsubtree=files_subtree --empty=ask --rebase-merges --onto files-master --root &&
: first pick results in no changes &&
- git rebase --continue &&
+ test_must_fail git rebase --skip &&
+ : last pick was an empty commit that has no changes, but we want to keep it &&
+ git commit --allow-empty &&
verbose test "$(commit_message HEAD~2)" = "master4" &&
verbose test "$(commit_message HEAD~)" = "files_subtree/master5" &&
verbose test "$(commit_message HEAD)" = "Empty commit"
--
gitgitgadget
^ permalink raw reply related [flat|nested] 161+ messages in thread
* [PATCH v3 02/15] t3406: simplify an already simple test
2019-12-24 19:54 ` [PATCH v3 00/15] rebase: make the default backend configurable Elijah Newren via GitGitGadget
2019-12-24 19:54 ` [PATCH v3 01/15] rebase: extend the options for handling of empty commits Elijah Newren via GitGitGadget
@ 2019-12-24 19:54 ` Elijah Newren via GitGitGadget
2019-12-24 19:54 ` [PATCH v3 03/15] rebase, sequencer: remove the broken GIT_QUIET handling Elijah Newren via GitGitGadget
` (13 subsequent siblings)
15 siblings, 0 replies; 161+ messages in thread
From: Elijah Newren via GitGitGadget @ 2019-12-24 19:54 UTC (permalink / raw)
To: git
Cc: Johannes.Schindelin, phillip.wood, liu.denton, gitster, plroskin,
alban.gruin, szeder.dev, Junio C Hamano, Elijah Newren
From: Elijah Newren <newren@gmail.com>
When the merge backend was re-implemented on top of the interactive
backend, the output of rebase --merge changed a little. This change
allowed this test to be simplified, though it wasn't noticed until now.
Simplify the testcase a little.
Signed-off-by: Elijah Newren <newren@gmail.com>
---
t/t3406-rebase-message.sh | 7 ++-----
1 file changed, 2 insertions(+), 5 deletions(-)
diff --git a/t/t3406-rebase-message.sh b/t/t3406-rebase-message.sh
index b393e1e9fe..0c2c569f95 100755
--- a/t/t3406-rebase-message.sh
+++ b/t/t3406-rebase-message.sh
@@ -18,11 +18,8 @@ test_expect_success 'setup' '
'
test_expect_success 'rebase -m' '
- git rebase -m master >report &&
- >expect &&
- sed -n -e "/^Already applied: /p" \
- -e "/^Committed: /p" report >actual &&
- test_cmp expect actual
+ git rebase -m master >actual &&
+ test_must_be_empty actual
'
test_expect_success 'rebase against master twice' '
--
gitgitgadget
^ permalink raw reply related [flat|nested] 161+ messages in thread
* [PATCH v3 03/15] rebase, sequencer: remove the broken GIT_QUIET handling
2019-12-24 19:54 ` [PATCH v3 00/15] rebase: make the default backend configurable Elijah Newren via GitGitGadget
2019-12-24 19:54 ` [PATCH v3 01/15] rebase: extend the options for handling of empty commits Elijah Newren via GitGitGadget
2019-12-24 19:54 ` [PATCH v3 02/15] t3406: simplify an already simple test Elijah Newren via GitGitGadget
@ 2019-12-24 19:54 ` Elijah Newren via GitGitGadget
2019-12-24 19:54 ` [PATCH v3 04/15] rebase: make sure to pass along the quiet flag to the sequencer Elijah Newren via GitGitGadget
` (12 subsequent siblings)
15 siblings, 0 replies; 161+ messages in thread
From: Elijah Newren via GitGitGadget @ 2019-12-24 19:54 UTC (permalink / raw)
To: git
Cc: Johannes.Schindelin, phillip.wood, liu.denton, gitster, plroskin,
alban.gruin, szeder.dev, Junio C Hamano, Elijah Newren
From: Elijah Newren <newren@gmail.com>
The GIT_QUIET environment variable was used to signal the non-am
backends that the rebase should perform quietly. The preserve-merges
backend does not make use of the quiet flag anywhere (other than to
write out its state whenever it writes state), and this mechanism was
broken in the conversion from shell to C. Since this environment
variable was specifically designed for scripts and the only backend that
would still use it is no longer a script, just gut this code.
A subsequent commit will fix --quiet for the interactive/merge backend
in a different way.
Signed-off-by: Elijah Newren <newren@gmail.com>
---
builtin/rebase.c | 6 ++----
sequencer.c | 6 ++----
2 files changed, 4 insertions(+), 8 deletions(-)
diff --git a/builtin/rebase.c b/builtin/rebase.c
index 6903249307..32026a62e8 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -718,8 +718,8 @@ static int rebase_write_basic_state(struct rebase_options *opts)
opts->onto ? oid_to_hex(&opts->onto->object.oid) : "");
write_file(state_dir_path("orig-head", opts), "%s",
oid_to_hex(&opts->orig_head));
- write_file(state_dir_path("quiet", opts), "%s",
- opts->flags & REBASE_NO_QUIET ? "" : "t");
+ if (!(opts->flags & REBASE_NO_QUIET))
+ write_file(state_dir_path("quiet", opts), "%s", "");
if (opts->flags & REBASE_VERBOSE)
write_file(state_dir_path("verbose", opts), "%s", "");
if (opts->strategy)
@@ -1178,8 +1178,6 @@ static int run_specific_rebase(struct rebase_options *opts, enum action action)
add_var(&script_snippet, "revisions", opts->revisions);
add_var(&script_snippet, "restrict_revision", opts->restrict_revision ?
oid_to_hex(&opts->restrict_revision->object.oid) : NULL);
- add_var(&script_snippet, "GIT_QUIET",
- opts->flags & REBASE_NO_QUIET ? "" : "t");
sq_quote_argv_pretty(&buf, opts->git_am_opts.argv);
add_var(&script_snippet, "git_am_opt", buf.buf);
strbuf_release(&buf);
diff --git a/sequencer.c b/sequencer.c
index d2c11f34b7..71062212a5 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -2688,8 +2688,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)
@@ -2698,8 +2696,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)
--
gitgitgadget
^ permalink raw reply related [flat|nested] 161+ messages in thread
* [PATCH v3 04/15] rebase: make sure to pass along the quiet flag to the sequencer
2019-12-24 19:54 ` [PATCH v3 00/15] rebase: make the default backend configurable Elijah Newren via GitGitGadget
` (2 preceding siblings ...)
2019-12-24 19:54 ` [PATCH v3 03/15] rebase, sequencer: remove the broken GIT_QUIET handling Elijah Newren via GitGitGadget
@ 2019-12-24 19:54 ` Elijah Newren via GitGitGadget
2019-12-24 19:54 ` [PATCH v3 05/15] rebase: fix handling of restrict_revision Elijah Newren via GitGitGadget
` (11 subsequent siblings)
15 siblings, 0 replies; 161+ messages in thread
From: Elijah Newren via GitGitGadget @ 2019-12-24 19:54 UTC (permalink / raw)
To: git
Cc: Johannes.Schindelin, phillip.wood, liu.denton, gitster, plroskin,
alban.gruin, szeder.dev, Junio C Hamano, Elijah Newren
From: Elijah Newren <newren@gmail.com>
Signed-off-by: Elijah Newren <newren@gmail.com>
---
builtin/rebase.c | 3 ++-
t/t3400-rebase.sh | 8 +++++++-
2 files changed, 9 insertions(+), 2 deletions(-)
diff --git a/builtin/rebase.c b/builtin/rebase.c
index 32026a62e8..5014c9a437 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -126,6 +126,7 @@ static struct replay_opts get_replay_opts(const struct rebase_options *opts)
replay.keep_redundant_commits = (opts->empty == EMPTY_KEEP);
replay.ask_on_initially_empty = (opts->empty == EMPTY_ASK &&
!(opts->flags & REBASE_INTERACTIVE_EXPLICIT));
+ replay.quiet = !(opts->flags & REBASE_NO_QUIET);
replay.verbose = opts->flags & REBASE_VERBOSE;
replay.reschedule_failed_exec = opts->reschedule_failed_exec;
replay.committer_date_is_author_date =
@@ -1502,7 +1503,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
N_("allow pre-rebase hook to run")),
OPT_NEGBIT('q', "quiet", &options.flags,
N_("be quiet. implies --no-stat"),
- REBASE_NO_QUIET| REBASE_VERBOSE | REBASE_DIFFSTAT),
+ REBASE_NO_QUIET | REBASE_VERBOSE | REBASE_DIFFSTAT),
OPT_BIT('v', "verbose", &options.flags,
N_("display a diffstat of what changed upstream"),
REBASE_NO_QUIET | REBASE_VERBOSE | REBASE_DIFFSTAT),
diff --git a/t/t3400-rebase.sh b/t/t3400-rebase.sh
index 221b35f2df..79762b989a 100755
--- a/t/t3400-rebase.sh
+++ b/t/t3400-rebase.sh
@@ -206,12 +206,18 @@ test_expect_success 'cherry-picked commits and fork-point work together' '
test_cmp expect D
'
-test_expect_success 'rebase -q is quiet' '
+test_expect_success 'rebase --am -q is quiet' '
git checkout -b quiet topic &&
git rebase -q master >output.out 2>&1 &&
test_must_be_empty output.out
'
+test_expect_success 'rebase --merge -q is quiet' '
+ git checkout -B quiet topic &&
+ git rebase --merge -q master >output.out 2>&1 &&
+ test_must_be_empty output.out
+'
+
test_expect_success 'Rebase a commit that sprinkles CRs in' '
(
echo "One" &&
--
gitgitgadget
^ permalink raw reply related [flat|nested] 161+ messages in thread
* [PATCH v3 05/15] rebase: fix handling of restrict_revision
2019-12-24 19:54 ` [PATCH v3 00/15] rebase: make the default backend configurable Elijah Newren via GitGitGadget
` (3 preceding siblings ...)
2019-12-24 19:54 ` [PATCH v3 04/15] rebase: make sure to pass along the quiet flag to the sequencer Elijah Newren via GitGitGadget
@ 2019-12-24 19:54 ` Elijah Newren via GitGitGadget
2019-12-24 19:54 ` [PATCH v3 06/15] t3432: make these tests work with either am or merge backends Elijah Newren via GitGitGadget
` (10 subsequent siblings)
15 siblings, 0 replies; 161+ messages in thread
From: Elijah Newren via GitGitGadget @ 2019-12-24 19:54 UTC (permalink / raw)
To: git
Cc: Johannes.Schindelin, phillip.wood, liu.denton, gitster, plroskin,
alban.gruin, szeder.dev, Junio C Hamano, Elijah Newren
From: Elijah Newren <newren@gmail.com>
restrict_revision in the original shell script was an excluded revision
range. It is also treated that way by the am-backend. In the
conversion from shell to C (see commit 6ab54d17be3f ("rebase -i:
implement the logic to initialize $revisions in C", 2018-08-28)), the
interactive-backend accidentally treated it as a positive revision
rather than a negated one.
This was missed as there were no tests in the testsuite that tested an
interactive rebase with fork-point behavior.
Signed-off-by: Elijah Newren <newren@gmail.com>
---
builtin/rebase.c | 4 ++--
t/t3400-rebase.sh | 20 +++++++++++++++++++-
2 files changed, 21 insertions(+), 3 deletions(-)
diff --git a/builtin/rebase.c b/builtin/rebase.c
index 5014c9a437..f1de5c8186 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -364,8 +364,8 @@ static int do_interactive_rebase(struct rebase_options *opts, unsigned flags)
argv_array_pushl(&make_script_args, "", revisions, NULL);
if (opts->restrict_revision)
- argv_array_push(&make_script_args,
- oid_to_hex(&opts->restrict_revision->object.oid));
+ argv_array_pushf(&make_script_args, "^%s",
+ oid_to_hex(&opts->restrict_revision->object.oid));
ret = sequencer_make_script(the_repository, &todo_list.buf,
make_script_args.argc, make_script_args.argv,
diff --git a/t/t3400-rebase.sh b/t/t3400-rebase.sh
index 79762b989a..71fd6396cd 100755
--- a/t/t3400-rebase.sh
+++ b/t/t3400-rebase.sh
@@ -165,11 +165,29 @@ test_expect_success 'rebase works with format.useAutoBase' '
git rebase master
'
-test_expect_success 'default to common base in @{upstream}s reflog if no upstream arg' '
+test_expect_success 'default to common base in @{upstream}s reflog if no upstream arg (--merge)' '
git checkout -b default-base master &&
git checkout -b default topic &&
git config branch.default.remote . &&
git config branch.default.merge refs/heads/default-base &&
+ git rebase --merge &&
+ git rev-parse --verify default-base >expect &&
+ git rev-parse default~1 >actual &&
+ test_cmp expect actual &&
+ git checkout default-base &&
+ git reset --hard HEAD^ &&
+ git checkout default &&
+ git rebase --merge &&
+ git rev-parse --verify default-base >expect &&
+ git rev-parse default~1 >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'default to common base in @{upstream}s reflog if no upstream arg' '
+ git checkout -B default-base master &&
+ git checkout -B default topic &&
+ git config branch.default.remote . &&
+ git config branch.default.merge refs/heads/default-base &&
git rebase &&
git rev-parse --verify default-base >expect &&
git rev-parse default~1 >actual &&
--
gitgitgadget
^ permalink raw reply related [flat|nested] 161+ messages in thread
* [PATCH v3 06/15] t3432: make these tests work with either am or merge backends
2019-12-24 19:54 ` [PATCH v3 00/15] rebase: make the default backend configurable Elijah Newren via GitGitGadget
` (4 preceding siblings ...)
2019-12-24 19:54 ` [PATCH v3 05/15] rebase: fix handling of restrict_revision Elijah Newren via GitGitGadget
@ 2019-12-24 19:54 ` Elijah Newren via GitGitGadget
2019-12-24 19:54 ` [PATCH v3 07/15] rebase: allow more types of rebases to fast-forward Elijah Newren via GitGitGadget
` (9 subsequent siblings)
15 siblings, 0 replies; 161+ messages in thread
From: Elijah Newren via GitGitGadget @ 2019-12-24 19:54 UTC (permalink / raw)
To: git
Cc: Johannes.Schindelin, phillip.wood, liu.denton, gitster, plroskin,
alban.gruin, szeder.dev, Junio C Hamano, Elijah Newren
From: Elijah Newren <newren@gmail.com>
t3432 had several stress tests for can_fast_forward(), whose intent was
to ensure we were using the optimization of just fast forwarding when
possible. However, these tests verified that fast forwards had happened
based on the output that rebase printed to the terminal. We can instead
test more directly that we actually fast-forwarded by checking the
reflog, which also has the side effect of making the tests applicable
for the merge/interactive backend.
This change does lose the distinction between "noop" and "noop-force",
but as stated in commit c9efc216830f ("t3432: test for --no-ff's
interaction with fast-forward", 2019-08-27) which introduced that
distinction: "These tests aren't supposed to endorse the status quo,
just test for what we're currently doing.".
This change does not actually run these tests with the merge/interactive
backend; instead this is just a preparatory commit. A subsequent commit
which fixes can_fast_forward() to work with that backend will then also
change t3432 to add tests of that backend as well.
Signed-off-by: Elijah Newren <newren@gmail.com>
---
t/t3432-rebase-fast-forward.sh | 48 ++++++++++++++++------------------
1 file changed, 22 insertions(+), 26 deletions(-)
diff --git a/t/t3432-rebase-fast-forward.sh b/t/t3432-rebase-fast-forward.sh
index 92f95b57da..7432c0e241 100755
--- a/t/t3432-rebase-fast-forward.sh
+++ b/t/t3432-rebase-fast-forward.sh
@@ -44,19 +44,15 @@ test_rebase_same_head_ () {
test_expect_$status "git rebase$flag $* with $changes is $what with $cmp HEAD" "
oldhead=\$(git rev-parse HEAD) &&
test_when_finished 'git reset --hard \$oldhead' &&
+ cp .git/logs/HEAD expect &&
git rebase$flag $* >stdout &&
if test $what = work
then
- # Must check this case first, for 'is up to
- # date, rebase forced[...]rewinding head' cases
- test_i18ngrep 'rewinding head' stdout
+ old=\$(wc -l <expect) &&
+ test_line_count '-gt' \$old .git/logs/HEAD
elif test $what = noop
then
- test_i18ngrep 'is up to date' stdout &&
- test_i18ngrep ! 'rebase forced' stdout
- elif test $what = noop-force
- then
- test_i18ngrep 'is up to date, rebase forced' stdout
+ test_cmp expect .git/logs/HEAD
fi &&
newhead=\$(git rev-parse HEAD) &&
if test $cmp = same
@@ -71,14 +67,14 @@ test_rebase_same_head_ () {
changes='no changes'
test_rebase_same_head success noop same success work same
-test_rebase_same_head success noop same success noop-force same master
-test_rebase_same_head success noop same success noop-force diff --onto B B
-test_rebase_same_head success noop same success noop-force diff --onto B... B
-test_rebase_same_head success noop same success noop-force same --onto master... master
-test_rebase_same_head success noop same success noop-force same --keep-base master
-test_rebase_same_head success noop same success noop-force same --keep-base
-test_rebase_same_head success noop same success noop-force same --no-fork-point
-test_rebase_same_head success noop same success noop-force same --keep-base --no-fork-point
+test_rebase_same_head success noop same success work same master
+test_rebase_same_head success noop same success work diff --onto B B
+test_rebase_same_head success noop same success work diff --onto B... B
+test_rebase_same_head success noop same success work same --onto master... master
+test_rebase_same_head success noop same success work same --keep-base master
+test_rebase_same_head success noop same success work same --keep-base
+test_rebase_same_head success noop same success work same --no-fork-point
+test_rebase_same_head success noop same success work same --keep-base --no-fork-point
test_rebase_same_head success noop same success work same --fork-point master
test_rebase_same_head success noop same success work diff --fork-point --onto B B
test_rebase_same_head success noop same success work diff --fork-point --onto B... B
@@ -91,14 +87,14 @@ test_expect_success 'add work same to side' '
changes='our changes'
test_rebase_same_head success noop same success work same
-test_rebase_same_head success noop same success noop-force same master
-test_rebase_same_head success noop same success noop-force diff --onto B B
-test_rebase_same_head success noop same success noop-force diff --onto B... B
-test_rebase_same_head success noop same success noop-force same --onto master... master
-test_rebase_same_head success noop same success noop-force same --keep-base master
-test_rebase_same_head success noop same success noop-force same --keep-base
-test_rebase_same_head success noop same success noop-force same --no-fork-point
-test_rebase_same_head success noop same success noop-force same --keep-base --no-fork-point
+test_rebase_same_head success noop same success work same master
+test_rebase_same_head success noop same success work diff --onto B B
+test_rebase_same_head success noop same success work diff --onto B... B
+test_rebase_same_head success noop same success work same --onto master... master
+test_rebase_same_head success noop same success work same --keep-base master
+test_rebase_same_head success noop same success work same --keep-base
+test_rebase_same_head success noop same success work same --no-fork-point
+test_rebase_same_head success noop same success work same --keep-base --no-fork-point
test_rebase_same_head success noop same success work same --fork-point master
test_rebase_same_head success noop same success work diff --fork-point --onto B B
test_rebase_same_head success noop same success work diff --fork-point --onto B... B
@@ -112,8 +108,8 @@ test_expect_success 'add work same to upstream' '
'
changes='our and their changes'
-test_rebase_same_head success noop same success noop-force diff --onto B B
-test_rebase_same_head success noop same success noop-force diff --onto B... B
+test_rebase_same_head success noop same success work diff --onto B B
+test_rebase_same_head success noop same success work diff --onto B... B
test_rebase_same_head success noop same success work diff --onto master... master
test_rebase_same_head success noop same success work diff --keep-base master
test_rebase_same_head success noop same success work diff --keep-base
--
gitgitgadget
^ permalink raw reply related [flat|nested] 161+ messages in thread
* [PATCH v3 07/15] rebase: allow more types of rebases to fast-forward
2019-12-24 19:54 ` [PATCH v3 00/15] rebase: make the default backend configurable Elijah Newren via GitGitGadget
` (5 preceding siblings ...)
2019-12-24 19:54 ` [PATCH v3 06/15] t3432: make these tests work with either am or merge backends Elijah Newren via GitGitGadget
@ 2019-12-24 19:54 ` Elijah Newren via GitGitGadget
2019-12-24 19:54 ` [PATCH v3 08/15] git-rebase.txt: add more details about behavioral differences of backends Elijah Newren via GitGitGadget
` (8 subsequent siblings)
15 siblings, 0 replies; 161+ messages in thread
From: Elijah Newren via GitGitGadget @ 2019-12-24 19:54 UTC (permalink / raw)
To: git
Cc: Johannes.Schindelin, phillip.wood, liu.denton, gitster, plroskin,
alban.gruin, szeder.dev, Junio C Hamano, Elijah Newren
From: Elijah Newren <newren@gmail.com>
In the past, we dis-allowed rebases using the interactive backend from
performing a fast-forward to short-circuit the rebase operation. This
made sense for explicitly interactive rebases and some implicitly
interactive rebases, but certainly became overly stringent when the
merge backend was re-implemented via the interactive backend.
Just as the am-based rebase has always had to disable the fast-forward
based on a variety of conditions or flags (e.g. --signoff, --whitespace,
etc.), we need to do the same but now with a few more options. However,
continuing to use REBASE_FORCE for tracking this is problematic because
the interactive backend used it for a different purpose. (When
REBASE_FORCE wasn't set, the interactive backend would not fast-forward
the whole series but would fast-forward individual "pick" commits at the
beginning of the todo list, and then a squash or something would cause
it to start generating new commits.) So, introduce a new
allow_preemptive_ff flag contained within cmd_rebase() and use it to
track whether we are going to allow a pre-emptive fast-forward that
short-circuits the whole rebase.
Signed-off-by: Elijah Newren <newren@gmail.com>
---
builtin/rebase.c | 18 ++++++++++++++----
t/t3432-rebase-fast-forward.sh | 2 ++
2 files changed, 16 insertions(+), 4 deletions(-)
diff --git a/builtin/rebase.c b/builtin/rebase.c
index f1de5c8186..7027e34567 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -1493,6 +1493,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
struct object_id squash_onto;
char *squash_onto_name = NULL;
int reschedule_failed_exec = -1;
+ int allow_preemptive_ff = 1;
struct option builtin_rebase_options[] = {
OPT_STRING(0, "onto", &options.onto_name,
N_("revision"),
@@ -1804,11 +1805,18 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
options.ignore_date)
options.flags |= REBASE_FORCE;
+ if ((options.flags & REBASE_INTERACTIVE_EXPLICIT) ||
+ (action != ACTION_NONE) ||
+ (exec.nr > 0) ||
+ options.autosquash) {
+ allow_preemptive_ff = 0;
+ }
+
for (i = 0; i < options.git_am_opts.argc; i++) {
const char *option = options.git_am_opts.argv[i], *p;
if (!strcmp(option, "--whitespace=fix") ||
!strcmp(option, "--whitespace=strip"))
- options.flags |= REBASE_FORCE;
+ allow_preemptive_ff = 0;
else if (skip_prefix(option, "-C", &p)) {
while (*p)
if (!isdigit(*(p++)))
@@ -2144,12 +2152,14 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
/*
* Check if we are already based on onto with linear history,
* in which case we could fast-forward without replacing the commits
- * with new commits recreated by replaying their changes. This
- * optimization must not be done if this is an interactive rebase.
+ * with new commits recreated by replaying their changes.
+ *
+ * Note that can_fast_forward() initializes merge_base, so we have to
+ * call it before checking allow_preemptive_ff.
*/
if (can_fast_forward(options.onto, options.upstream, options.restrict_revision,
&options.orig_head, &merge_base) &&
- !is_interactive(&options)) {
+ allow_preemptive_ff) {
int flag;
if (!(options.flags & REBASE_FORCE)) {
diff --git a/t/t3432-rebase-fast-forward.sh b/t/t3432-rebase-fast-forward.sh
index 7432c0e241..40388ccf9f 100755
--- a/t/t3432-rebase-fast-forward.sh
+++ b/t/t3432-rebase-fast-forward.sh
@@ -30,6 +30,8 @@ test_rebase_same_head () {
shift &&
test_rebase_same_head_ $status_n $what_n $cmp_n "" "$*" &&
test_rebase_same_head_ $status_f $what_f $cmp_f " --no-ff" "$*"
+ test_rebase_same_head_ $status_n $what_n $cmp_n " --merge" "$*" &&
+ test_rebase_same_head_ $status_f $what_f $cmp_f " --merge --no-ff" "$*"
}
test_rebase_same_head_ () {
--
gitgitgadget
^ permalink raw reply related [flat|nested] 161+ messages in thread
* [PATCH v3 08/15] git-rebase.txt: add more details about behavioral differences of backends
2019-12-24 19:54 ` [PATCH v3 00/15] rebase: make the default backend configurable Elijah Newren via GitGitGadget
` (6 preceding siblings ...)
2019-12-24 19:54 ` [PATCH v3 07/15] rebase: allow more types of rebases to fast-forward Elijah Newren via GitGitGadget
@ 2019-12-24 19:54 ` Elijah Newren via GitGitGadget
2019-12-24 19:54 ` [PATCH v3 09/15] rebase: move incompatibility checks between backend options a bit earlier Elijah Newren via GitGitGadget
` (7 subsequent siblings)
15 siblings, 0 replies; 161+ messages in thread
From: Elijah Newren via GitGitGadget @ 2019-12-24 19:54 UTC (permalink / raw)
To: git
Cc: Johannes.Schindelin, phillip.wood, liu.denton, gitster, plroskin,
alban.gruin, szeder.dev, Junio C Hamano, Elijah Newren
From: Elijah Newren <newren@gmail.com>
Signed-off-by: Elijah Newren <newren@gmail.com>
---
Documentation/git-rebase.txt | 102 +++++++++++++++++++++---
t/t3433-rebase-options-compatibility.sh | 5 +-
2 files changed, 94 insertions(+), 13 deletions(-)
diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index ff32ca1080..f1ace07c38 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -409,13 +409,10 @@ your branch contains commits which were dropped, this option can be used
with `--keep-base` in order to drop those commits from your branch.
--ignore-whitespace::
- Behaves differently depending on which backend is selected.
-+
-'am' backend: When applying a patch, ignore changes in whitespace in
-context lines if necessary.
-+
-'interactive' backend: Treat lines with only whitespace changes as
-unchanged for the sake of a three-way merge.
+ Ignore whitespace-only changes in the commits being rebased,
+ which may avoid "unnecessary" conflicts. (Both backends
+ currently have differing edgecase bugs with this option; see
+ BEHAVIORAL DIFFERENCES.)
--whitespace=<option>::
This flag is passed to the 'git apply' program
@@ -609,9 +606,94 @@ There are some subtle differences how the backends behave.
Directory rename detection
~~~~~~~~~~~~~~~~~~~~~~~~~~
-Directory rename heuristics are enabled in the merge and interactive
-backends. Due to the lack of accurate tree information, directory
-rename detection is disabled in the am backend.
+Due to the lack of accurate tree information (arising from
+constructing fake ancestors with the limited information available in
+patches), directory rename detection is disabled in the am backend.
+Disabled directory rename detection means that if one side of history
+renames a directory and the other adds new files to the old directory,
+then the new files will be left behind in the old directory without
+any warning at the time of rebasing that you may want to move these
+files into the new directory.
+
+Directory rename detection works with the merge and interactive
+backends to provide you warnings in such cases.
+
+Context
+~~~~~~~
+
+The am backend works by creating a sequence of patches (by calling
+`format-patch` internally), and then applying the patches in sequence
+(calling `am` internally). Patches are composed of multiple hunks,
+each with line numbers, a context region, and the actual changes. The
+line numbers have to be taken with some fuzz, since the other side
+will likely have inserted or deleted lines earlier in the file. The
+context region is meant to help find how to adjust the line numbers in
+order to apply the changes to the right lines. However, if multiple
+areas of the code have the same surrounding lines of context, the
+wrong one can be picked. There are real-world cases where this has
+caused commits to be reapplied incorrectly with no conflicts reported.
+Setting diff.context to a larger value may prevent such types of
+problems, but increases the chance of spurious conflicts (since it
+will require more lines of matching context to apply).
+
+The interactive backend works with a full copy of each relevant file,
+insulating it from these types of problems.
+
+Labelling of conflicts markers
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+When there are content conflicts, the merge machinery tries to
+annotate each side's conflict markers with the commits where the
+content came from. Since the am backend drops the original
+information about the rebased commits and their parents (and instead
+generates new fake commits based off limited information in the
+generated patches), those commits cannot be identified; instead it has
+to fall back to a commit summary. Also, when merge.conflictStyle is
+set to diff3, the am backend will use "constructed merge base" to
+label the content from the merge base, and thus provide no information
+about the merge base commit whatsoever.
+
+The interactive backend works with the full commits on both sides of
+history and thus has no such limitations.
+
+--ignore-whitespace
+~~~~~~~~~~~~~~~~~~~
+
+The --ignore-whitespace option is supposed to ignore whitespace-only
+changes if it allows the code to merge cleanly. Unfortunately, the
+different backends implement this differently, and both have different
+edge case bugs.
++
+'am' backend: When applying a patch, ignore changes in whitespace in
+context lines if necessary. (Which implies that if the whitespace
+change was not in the context lines but on a line with a real change,
+then the rebase will still fail with "unnecessary" content conflicts.)
++
+'interactive' backend: Treat lines with only whitespace changes as
+unchanged for the sake of a three-way merge. This means that if one
+side made no changes and the commits being rebased had whitespace-only
+changes, those whitespaces fixups will be discarded despite the fact
+that they present no content conflict.
+
+Miscellaneous differences
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+There are a few more behavioral differences that most folks would
+probably consider inconsequential but which are mentioned for
+completeness:
+
+* Reflog: The two backends will use different wording when describing
+ the changes made in the reflog, though both will make use of the
+ word "rebase".
+
+* Progress, informational, and error messages: The two backends
+ provide slightly different progress and informational messages.
+ Also, the am backend writes error messages (such as "Your files
+ would be overwritten...") to stdout, while the interactive backend
+ writes them to stderr.
+
+* State directories: The two backends keep their state in different
+ directories under .git/
include::merge-strategies.txt[]
diff --git a/t/t3433-rebase-options-compatibility.sh b/t/t3433-rebase-options-compatibility.sh
index 5166f158dd..bd4d2d2f63 100755
--- a/t/t3433-rebase-options-compatibility.sh
+++ b/t/t3433-rebase-options-compatibility.sh
@@ -10,9 +10,8 @@ test_description='tests to ensure compatibility between am and interactive backe
GIT_AUTHOR_DATE="1999-04-02T08:03:20+05:30"
export GIT_AUTHOR_DATE
-# This is a special case in which both am and interactive backends
-# provide the same output. It was done intentionally because
-# both the backends fall short of optimal behaviour.
+# This is a common case in which both am and interactive backends
+# provide the same output with --ignore-whitespace.
test_expect_success 'setup' '
git checkout -b topic &&
q_to_tab >file <<-\EOF &&
--
gitgitgadget
^ permalink raw reply related [flat|nested] 161+ messages in thread
* [PATCH v3 09/15] rebase: move incompatibility checks between backend options a bit earlier
2019-12-24 19:54 ` [PATCH v3 00/15] rebase: make the default backend configurable Elijah Newren via GitGitGadget
` (7 preceding siblings ...)
2019-12-24 19:54 ` [PATCH v3 08/15] git-rebase.txt: add more details about behavioral differences of backends Elijah Newren via GitGitGadget
@ 2019-12-24 19:54 ` Elijah Newren via GitGitGadget
2019-12-24 19:54 ` [PATCH v3 10/15] rebase: add an --am option Elijah Newren via GitGitGadget
` (6 subsequent siblings)
15 siblings, 0 replies; 161+ messages in thread
From: Elijah Newren via GitGitGadget @ 2019-12-24 19:54 UTC (permalink / raw)
To: git
Cc: Johannes.Schindelin, phillip.wood, liu.denton, gitster, plroskin,
alban.gruin, szeder.dev, Junio C Hamano, Elijah Newren
From: Elijah Newren <newren@gmail.com>
Signed-off-by: Elijah Newren <newren@gmail.com>
---
builtin/rebase.c | 22 +++++++++++-----------
1 file changed, 11 insertions(+), 11 deletions(-)
diff --git a/builtin/rebase.c b/builtin/rebase.c
index 7027e34567..d2b99e9908 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -1906,6 +1906,17 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
if (isatty(2) && options.flags & REBASE_NO_QUIET)
strbuf_addstr(&options.git_format_patch_opt, " --progress");
+ if (options.git_am_opts.argc) {
+ /* all am options except -q are compatible only with --am */
+ for (i = options.git_am_opts.argc - 1; i >= 0; i--)
+ if (strcmp(options.git_am_opts.argv[i], "-q"))
+ break;
+
+ if (is_interactive(&options) && i >= 0)
+ die(_("cannot combine am options with either "
+ "interactive or merge options"));
+ }
+
switch (options.type) {
case REBASE_MERGE:
case REBASE_INTERACTIVE:
@@ -1936,17 +1947,6 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
if (reschedule_failed_exec >= 0)
options.reschedule_failed_exec = reschedule_failed_exec;
- if (options.git_am_opts.argc) {
- /* all am options except -q are compatible only with --am */
- for (i = options.git_am_opts.argc - 1; i >= 0; i--)
- if (strcmp(options.git_am_opts.argv[i], "-q"))
- break;
-
- if (is_interactive(&options) && i >= 0)
- die(_("cannot combine am options with either "
- "interactive or merge options"));
- }
-
if (options.signoff) {
if (options.type == REBASE_PRESERVE_MERGES)
die("cannot combine '--signoff' with "
--
gitgitgadget
^ permalink raw reply related [flat|nested] 161+ messages in thread
* [PATCH v3 10/15] rebase: add an --am option
2019-12-24 19:54 ` [PATCH v3 00/15] rebase: make the default backend configurable Elijah Newren via GitGitGadget
` (8 preceding siblings ...)
2019-12-24 19:54 ` [PATCH v3 09/15] rebase: move incompatibility checks between backend options a bit earlier Elijah Newren via GitGitGadget
@ 2019-12-24 19:54 ` Elijah Newren via GitGitGadget
2020-01-07 14:43 ` Phillip Wood
2019-12-24 19:54 ` [PATCH v3 11/15] git-prompt: change the prompt for interactive-based rebases Elijah Newren via GitGitGadget
` (5 subsequent siblings)
15 siblings, 1 reply; 161+ messages in thread
From: Elijah Newren via GitGitGadget @ 2019-12-24 19:54 UTC (permalink / raw)
To: git
Cc: Johannes.Schindelin, phillip.wood, liu.denton, gitster, plroskin,
alban.gruin, szeder.dev, Junio C Hamano, Elijah Newren
From: Elijah Newren <newren@gmail.com>
Currently, this option doesn't do anything except error out if any
options requiring the interactive-backend are also passed. However,
when we make the default backend configurable later in this series, this
flag will provide a way to override the config setting.
Signed-off-by: Elijah Newren <newren@gmail.com>
---
Documentation/git-rebase.txt | 11 ++++++++++-
builtin/rebase.c | 18 +++++++++++++++++-
2 files changed, 27 insertions(+), 2 deletions(-)
diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index f1ace07c38..cf1ac2e359 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -258,6 +258,13 @@ See also INCOMPATIBLE OPTIONS below.
original branch. The index and working tree are also left
unchanged as a result.
+--am:
+ Use git-am internally to rebase. This option may become a
+ no-op in the future once the interactive backend handles
+ everything the am one does.
++
+See also INCOMPATIBLE OPTIONS below.
+
--empty={drop,keep,ask}::
How to handle commits that become empty (because they contain a
subset of already upstream changes) or start empty. With drop
@@ -372,7 +379,7 @@ See also INCOMPATIBLE OPTIONS below.
Ensure at least <n> lines of surrounding context match before
and after each change. When fewer lines of surrounding
context exist they all must match. By default no context is
- ever ignored.
+ ever ignored. Implies --am.
+
See also INCOMPATIBLE OPTIONS below.
@@ -417,6 +424,7 @@ with `--keep-base` in order to drop those commits from your branch.
--whitespace=<option>::
This flag is passed to the 'git apply' program
(see linkgit:git-apply[1]) that applies the patch.
+ Implies --am.
+
See also INCOMPATIBLE OPTIONS below.
@@ -567,6 +575,7 @@ INCOMPATIBLE OPTIONS
The following options:
+ * --am
* --whitespace
* -C
diff --git a/builtin/rebase.c b/builtin/rebase.c
index d2b99e9908..b7915fc0cb 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -1361,6 +1361,18 @@ static int can_fast_forward(struct commit *onto, struct commit *upstream,
return res && is_linear_history(onto, head);
}
+static int parse_opt_am(const struct option *opt, const char *arg, int unset)
+{
+ struct rebase_options *opts = opt->value;
+
+ BUG_ON_OPT_NEG(unset);
+ BUG_ON_OPT_ARG(arg);
+
+ opts->type = REBASE_AM;
+
+ return 0;
+}
+
/* -i followed by -m is still -i */
static int parse_opt_merge(const struct option *opt, const char *arg, int unset)
{
@@ -1546,6 +1558,10 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
OPT_CMDMODE(0, "show-current-patch", &action,
N_("show the patch file being applied or merged"),
ACTION_SHOW_CURRENT_PATCH),
+ { OPTION_CALLBACK, 0, "am", &options, NULL,
+ N_("use apply-mail strategies to rebase"),
+ PARSE_OPT_NOARG | PARSE_OPT_NONEG,
+ parse_opt_am },
{ OPTION_CALLBACK, 'm', "merge", &options, NULL,
N_("use merging strategies to rebase"),
PARSE_OPT_NOARG | PARSE_OPT_NONEG,
@@ -1906,7 +1922,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
if (isatty(2) && options.flags & REBASE_NO_QUIET)
strbuf_addstr(&options.git_format_patch_opt, " --progress");
- if (options.git_am_opts.argc) {
+ if (options.git_am_opts.argc || options.type == REBASE_AM) {
/* all am options except -q are compatible only with --am */
for (i = options.git_am_opts.argc - 1; i >= 0; i--)
if (strcmp(options.git_am_opts.argv[i], "-q"))
--
gitgitgadget
^ permalink raw reply related [flat|nested] 161+ messages in thread
* [PATCH v3 11/15] git-prompt: change the prompt for interactive-based rebases
2019-12-24 19:54 ` [PATCH v3 00/15] rebase: make the default backend configurable Elijah Newren via GitGitGadget
` (9 preceding siblings ...)
2019-12-24 19:54 ` [PATCH v3 10/15] rebase: add an --am option Elijah Newren via GitGitGadget
@ 2019-12-24 19:54 ` Elijah Newren via GitGitGadget
2019-12-24 19:54 ` [PATCH v3 12/15] rebase tests: mark tests specific to the am-backend with --am Elijah Newren via GitGitGadget
` (4 subsequent siblings)
15 siblings, 0 replies; 161+ messages in thread
From: Elijah Newren via GitGitGadget @ 2019-12-24 19:54 UTC (permalink / raw)
To: git
Cc: Johannes.Schindelin, phillip.wood, liu.denton, gitster, plroskin,
alban.gruin, szeder.dev, Junio C Hamano, Elijah Newren
From: Elijah Newren <newren@gmail.com>
In the past, we had different prompts for different types of rebases:
REBASE: for am-based rebases
REBASE-m: for merge-based rebases
REBASE-i: for interactive-based rebases
It's not clear why this distinction was necessary or helpful; when the
prompt was added in commit e75201963f67 ("Improve bash prompt to detect
various states like an unfinished merge", 2007-09-30), it simply added
these three different types. Perhaps there was a useful purpose back
then, but there have been some changes:
* The merge backend was deleted after being implemented on top of the
interactive backend, causing the prompt for merge-based rebases to
change from REBASE-m to REBASE-i.
* The interactive backend is used for multiple different types of
non-interactive rebases, so the "-i" part of the prompt doesn't
really mean what it used to.
* Rebase backends have gained more abilities and have a great deal of
overlap, sometimes making it hard to distinguish them.
* Behavioral differences between the backends have also been ironed
out.
* We want to change the default backend from am to interactive, which
means people would get "REBASE-i" by default if we didn't change
the prompt, and only if they specified --am or --whitespace or -C
would they get the "REBASE" prompt.
* In the future, we plan to have "--whitespace", "-C", and even "--am"
run the interactive backend once it can handle everything the
am-backend can.
For all these reasons, make the prompt for any type of rebase just be
"REBASE".
Signed-off-by: Elijah Newren <newren@gmail.com>
---
contrib/completion/git-prompt.sh | 6 +-----
t/t9903-bash-prompt.sh | 8 ++++----
2 files changed, 5 insertions(+), 9 deletions(-)
diff --git a/contrib/completion/git-prompt.sh b/contrib/completion/git-prompt.sh
index 1d510cd47b..014cd7c3cf 100644
--- a/contrib/completion/git-prompt.sh
+++ b/contrib/completion/git-prompt.sh
@@ -429,11 +429,7 @@ __git_ps1 ()
__git_eread "$g/rebase-merge/head-name" b
__git_eread "$g/rebase-merge/msgnum" step
__git_eread "$g/rebase-merge/end" total
- if [ -f "$g/rebase-merge/interactive" ]; then
- r="|REBASE-i"
- else
- r="|REBASE-m"
- fi
+ r="|REBASE"
else
if [ -d "$g/rebase-apply" ]; then
__git_eread "$g/rebase-apply/next" step
diff --git a/t/t9903-bash-prompt.sh b/t/t9903-bash-prompt.sh
index 88bc733ad6..7ca35d358d 100755
--- a/t/t9903-bash-prompt.sh
+++ b/t/t9903-bash-prompt.sh
@@ -163,7 +163,7 @@ test_expect_success 'prompt - inside bare repository' '
'
test_expect_success 'prompt - interactive rebase' '
- printf " (b1|REBASE-i 2/3)" >expected &&
+ printf " (b1|REBASE 2/3)" >expected &&
write_script fake_editor.sh <<-\EOF &&
echo "exec echo" >"$1"
echo "edit $(git log -1 --format="%h")" >>"$1"
@@ -180,7 +180,7 @@ test_expect_success 'prompt - interactive rebase' '
'
test_expect_success 'prompt - rebase merge' '
- printf " (b2|REBASE-i 1/3)" >expected &&
+ printf " (b2|REBASE 1/3)" >expected &&
git checkout b2 &&
test_when_finished "git checkout master" &&
test_must_fail git rebase --merge b1 b2 &&
@@ -189,11 +189,11 @@ test_expect_success 'prompt - rebase merge' '
test_cmp expected "$actual"
'
-test_expect_success 'prompt - rebase' '
+test_expect_success 'prompt - rebase am' '
printf " (b2|REBASE 1/3)" >expected &&
git checkout b2 &&
test_when_finished "git checkout master" &&
- test_must_fail git rebase b1 b2 &&
+ test_must_fail git rebase --am b1 b2 &&
test_when_finished "git rebase --abort" &&
__git_ps1 >"$actual" &&
test_cmp expected "$actual"
--
gitgitgadget
^ permalink raw reply related [flat|nested] 161+ messages in thread
* [PATCH v3 12/15] rebase tests: mark tests specific to the am-backend with --am
2019-12-24 19:54 ` [PATCH v3 00/15] rebase: make the default backend configurable Elijah Newren via GitGitGadget
` (10 preceding siblings ...)
2019-12-24 19:54 ` [PATCH v3 11/15] git-prompt: change the prompt for interactive-based rebases Elijah Newren via GitGitGadget
@ 2019-12-24 19:54 ` Elijah Newren via GitGitGadget
2019-12-24 19:54 ` [PATCH v3 13/15] rebase tests: repeat some tests using the merge backend instead of am Elijah Newren via GitGitGadget
` (3 subsequent siblings)
15 siblings, 0 replies; 161+ messages in thread
From: Elijah Newren via GitGitGadget @ 2019-12-24 19:54 UTC (permalink / raw)
To: git
Cc: Johannes.Schindelin, phillip.wood, liu.denton, gitster, plroskin,
alban.gruin, szeder.dev, Junio C Hamano, Elijah Newren
From: Elijah Newren <newren@gmail.com>
We have many rebase tests in the testsuite, and often the same test is
repeated multiple times just testing different backends. For those
tests that were specifically trying to test the am backend, add the --am
flag.
Signed-off-by: Elijah Newren <newren@gmail.com>
---
t/t3400-rebase.sh | 10 +++++-----
t/t3401-rebase-and-am-rename.sh | 4 ++--
t/t3404-rebase-interactive.sh | 2 +-
t/t3406-rebase-message.sh | 12 ++++++------
t/t3407-rebase-abort.sh | 6 +++---
t/t3420-rebase-autostash.sh | 2 +-
t/t3425-rebase-topology-merges.sh | 8 ++++----
t/t3432-rebase-fast-forward.sh | 4 ++--
t/t3433-rebase-options-compatibility.sh | 8 ++++----
t/t5407-post-rewrite-hook.sh | 12 ++++++------
t/t7512-status-help.sh | 12 ++++++------
11 files changed, 40 insertions(+), 40 deletions(-)
diff --git a/t/t3400-rebase.sh b/t/t3400-rebase.sh
index 71fd6396cd..0a491f2363 100755
--- a/t/t3400-rebase.sh
+++ b/t/t3400-rebase.sh
@@ -183,19 +183,19 @@ test_expect_success 'default to common base in @{upstream}s reflog if no upstrea
test_cmp expect actual
'
-test_expect_success 'default to common base in @{upstream}s reflog if no upstream arg' '
+test_expect_success 'default to common base in @{upstream}s reflog if no upstream arg (--am)' '
git checkout -B default-base master &&
git checkout -B default topic &&
git config branch.default.remote . &&
git config branch.default.merge refs/heads/default-base &&
- git rebase &&
+ git rebase --am &&
git rev-parse --verify default-base >expect &&
git rev-parse default~1 >actual &&
test_cmp expect actual &&
git checkout default-base &&
git reset --hard HEAD^ &&
git checkout default &&
- git rebase &&
+ git rebase --am &&
git rev-parse --verify default-base >expect &&
git rev-parse default~1 >actual &&
test_cmp expect actual
@@ -226,7 +226,7 @@ test_expect_success 'cherry-picked commits and fork-point work together' '
test_expect_success 'rebase --am -q is quiet' '
git checkout -b quiet topic &&
- git rebase -q master >output.out 2>&1 &&
+ git rebase --am -q master >output.out 2>&1 &&
test_must_be_empty output.out
'
@@ -325,7 +325,7 @@ test_expect_success 'rebase --am and --show-current-patch' '
echo two >>init.t &&
git commit -a -m two &&
git tag two &&
- test_must_fail git rebase -f --onto init HEAD^ &&
+ test_must_fail git rebase --am -f --onto init HEAD^ &&
GIT_TRACE=1 git rebase --show-current-patch >/dev/null 2>stderr &&
grep "show.*$(git rev-parse two)" stderr
)
diff --git a/t/t3401-rebase-and-am-rename.sh b/t/t3401-rebase-and-am-rename.sh
index a0b9438b22..50803958fd 100755
--- a/t/t3401-rebase-and-am-rename.sh
+++ b/t/t3401-rebase-and-am-rename.sh
@@ -52,13 +52,13 @@ test_expect_success 'rebase --interactive: directory rename detected' '
)
'
-test_expect_failure 'rebase (am): directory rename detected' '
+test_expect_failure 'rebase --am: directory rename detected' '
(
cd dir-rename &&
git checkout B^0 &&
- git -c merge.directoryRenames=true rebase A &&
+ git -c merge.directoryRenames=true rebase --am A &&
git ls-files -s >out &&
test_line_count = 5 out &&
diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
index ae6e55ce79..743b7e511a 100755
--- a/t/t3404-rebase-interactive.sh
+++ b/t/t3404-rebase-interactive.sh
@@ -1137,7 +1137,7 @@ test_expect_success C_LOCALE_OUTPUT 'rebase --edit-todo does not work on non-int
git checkout conflict-branch &&
(
set_fake_editor &&
- test_must_fail git rebase -f --onto HEAD~2 HEAD~ &&
+ test_must_fail git rebase -f --am --onto HEAD~2 HEAD~ &&
test_must_fail git rebase --edit-todo
) &&
git rebase --abort
diff --git a/t/t3406-rebase-message.sh b/t/t3406-rebase-message.sh
index 0c2c569f95..7ce617fc1f 100755
--- a/t/t3406-rebase-message.sh
+++ b/t/t3406-rebase-message.sh
@@ -23,24 +23,24 @@ test_expect_success 'rebase -m' '
'
test_expect_success 'rebase against master twice' '
- git rebase master >out &&
+ git rebase --am master >out &&
test_i18ngrep "Current branch topic is up to date" out
'
test_expect_success 'rebase against master twice with --force' '
- git rebase --force-rebase master >out &&
+ git rebase --force-rebase --am master >out &&
test_i18ngrep "Current branch topic is up to date, rebase forced" out
'
test_expect_success 'rebase against master twice from another branch' '
git checkout topic^ &&
- git rebase master topic >out &&
+ git rebase --am master topic >out &&
test_i18ngrep "Current branch topic is up to date" out
'
test_expect_success 'rebase fast-forward to master' '
git checkout topic^ &&
- git rebase topic >out &&
+ git rebase --am topic >out &&
test_i18ngrep "Fast-forwarded HEAD to topic" out
'
@@ -89,7 +89,7 @@ test_expect_success 'GIT_REFLOG_ACTION' '
git checkout -b reflog-topic start &&
test_commit reflog-to-rebase &&
- git rebase reflog-onto &&
+ git rebase --am reflog-onto &&
git log -g --format=%gs -3 >actual &&
cat >expect <<-\EOF &&
rebase finished: returning to refs/heads/reflog-topic
@@ -99,7 +99,7 @@ test_expect_success 'GIT_REFLOG_ACTION' '
test_cmp expect actual &&
git checkout -b reflog-prefix reflog-to-rebase &&
- GIT_REFLOG_ACTION=change-the-reflog git rebase reflog-onto &&
+ GIT_REFLOG_ACTION=change-the-reflog git rebase --am reflog-onto &&
git log -g --format=%gs -3 >actual &&
cat >expect <<-\EOF &&
rebase finished: returning to refs/heads/reflog-prefix
diff --git a/t/t3407-rebase-abort.sh b/t/t3407-rebase-abort.sh
index 910f218284..3e31826170 100755
--- a/t/t3407-rebase-abort.sh
+++ b/t/t3407-rebase-abort.sh
@@ -96,14 +96,14 @@ testrebase() {
'
}
-testrebase "" .git/rebase-apply
+testrebase " --am" .git/rebase-apply
testrebase " --merge" .git/rebase-merge
-test_expect_success 'rebase --quit' '
+test_expect_success 'rebase --am --quit' '
cd "$work_dir" &&
# Clean up the state from the previous one
git reset --hard pre-rebase &&
- test_must_fail git rebase master &&
+ test_must_fail git rebase --am master &&
test_path_is_dir .git/rebase-apply &&
head_before=$(git rev-parse HEAD) &&
git rebase --quit &&
diff --git a/t/t3420-rebase-autostash.sh b/t/t3420-rebase-autostash.sh
index 5f7e73cf83..3816159e20 100755
--- a/t/t3420-rebase-autostash.sh
+++ b/t/t3420-rebase-autostash.sh
@@ -234,7 +234,7 @@ test_expect_success "rebase: noop rebase" '
git checkout feature-branch
'
-testrebase "" .git/rebase-apply
+testrebase " --am" .git/rebase-apply
testrebase " --merge" .git/rebase-merge
testrebase " --interactive" .git/rebase-merge
diff --git a/t/t3425-rebase-topology-merges.sh b/t/t3425-rebase-topology-merges.sh
index fd8efe84fe..19700b025b 100755
--- a/t/t3425-rebase-topology-merges.sh
+++ b/t/t3425-rebase-topology-merges.sh
@@ -54,7 +54,7 @@ test_run_rebase () {
test_linear_range 'n o' e..
"
}
-test_run_rebase success ''
+test_run_rebase success --am
test_run_rebase success -m
test_run_rebase success -i
@@ -70,7 +70,7 @@ test_run_rebase () {
test_linear_range "\'"$expected"\'" d..
"
}
-test_run_rebase success 'n o e' ''
+test_run_rebase success 'n o e' --am
test_run_rebase success 'n o e' -m
test_run_rebase success 'n o e' -i
@@ -86,7 +86,7 @@ test_run_rebase () {
test_linear_range "\'"$expected"\'" c..
"
}
-test_run_rebase success 'd n o e' ''
+test_run_rebase success 'd n o e' --am
test_run_rebase success 'd n o e' -m
test_run_rebase success 'd n o e' -i
@@ -102,7 +102,7 @@ test_run_rebase () {
test_linear_range "\'"$expected"\'" c..
"
}
-test_run_rebase success 'd n o e' ''
+test_run_rebase success 'd n o e' --am
test_run_rebase success 'd n o e' -m
test_run_rebase success 'd n o e' -i
diff --git a/t/t3432-rebase-fast-forward.sh b/t/t3432-rebase-fast-forward.sh
index 40388ccf9f..4b3cecce56 100755
--- a/t/t3432-rebase-fast-forward.sh
+++ b/t/t3432-rebase-fast-forward.sh
@@ -28,8 +28,8 @@ test_rebase_same_head () {
shift &&
cmp_f="$1" &&
shift &&
- test_rebase_same_head_ $status_n $what_n $cmp_n "" "$*" &&
- test_rebase_same_head_ $status_f $what_f $cmp_f " --no-ff" "$*"
+ test_rebase_same_head_ $status_n $what_n $cmp_n " --am" "$*" &&
+ test_rebase_same_head_ $status_f $what_f $cmp_f " --am --no-ff" "$*"
test_rebase_same_head_ $status_n $what_n $cmp_n " --merge" "$*" &&
test_rebase_same_head_ $status_f $what_f $cmp_f " --merge --no-ff" "$*"
}
diff --git a/t/t3433-rebase-options-compatibility.sh b/t/t3433-rebase-options-compatibility.sh
index bd4d2d2f63..a07e1f276b 100755
--- a/t/t3433-rebase-options-compatibility.sh
+++ b/t/t3433-rebase-options-compatibility.sh
@@ -51,9 +51,9 @@ test_expect_success '--ignore-whitespace works with am backend' '
new line 2
line 3
EOF
- test_must_fail git rebase main side &&
+ test_must_fail git rebase --am main side &&
git rebase --abort &&
- git rebase --ignore-whitespace main side &&
+ git rebase --am --ignore-whitespace main side &&
test_cmp expect file
'
@@ -71,7 +71,7 @@ test_expect_success '--ignore-whitespace works with interactive backend' '
test_expect_success '--committer-date-is-author-date works with am backend' '
git commit --amend &&
- git rebase --committer-date-is-author-date HEAD^ &&
+ git rebase --am --committer-date-is-author-date HEAD^ &&
git show HEAD --pretty="format:%ai" >authortime &&
git show HEAD --pretty="format:%ci" >committertime &&
test_cmp authortime committertime
@@ -103,7 +103,7 @@ test_expect_success '--committer-date-is-author-date works with rebase -r' '
# sets to +0530.
test_expect_success '--ignore-date works with am backend' '
git commit --amend --date="$GIT_AUTHOR_DATE" &&
- git rebase --ignore-date HEAD^ &&
+ git rebase --am --ignore-date HEAD^ &&
git show HEAD --pretty="format:%ai" >authortime &&
grep "+0000" authortime
'
diff --git a/t/t5407-post-rewrite-hook.sh b/t/t5407-post-rewrite-hook.sh
index 7344253bfb..a8a73616e4 100755
--- a/t/t5407-post-rewrite-hook.sh
+++ b/t/t5407-post-rewrite-hook.sh
@@ -53,10 +53,10 @@ test_expect_success 'git commit --amend --no-post-rewrite' '
test ! -f post-rewrite.data
'
-test_expect_success 'git rebase' '
+test_expect_success 'git rebase --am' '
git reset --hard D &&
clear_hook_input &&
- test_must_fail git rebase --onto A B &&
+ test_must_fail git rebase --am --onto A B &&
echo C > foo &&
git add foo &&
git rebase --continue &&
@@ -68,10 +68,10 @@ test_expect_success 'git rebase' '
verify_hook_input
'
-test_expect_success 'git rebase --skip' '
+test_expect_success 'git rebase --am --skip' '
git reset --hard D &&
clear_hook_input &&
- test_must_fail git rebase --onto A B &&
+ test_must_fail git rebase --am --onto A B &&
test_must_fail git rebase --skip &&
echo D > foo &&
git add foo &&
@@ -84,10 +84,10 @@ test_expect_success 'git rebase --skip' '
verify_hook_input
'
-test_expect_success 'git rebase --skip the last one' '
+test_expect_success 'git rebase --am --skip the last one' '
git reset --hard F &&
clear_hook_input &&
- test_must_fail git rebase --onto D A &&
+ test_must_fail git rebase --am --onto D A &&
git rebase --skip &&
echo rebase >expected.args &&
cat >expected.data <<-EOF &&
diff --git a/t/t7512-status-help.sh b/t/t7512-status-help.sh
index 66d7a62797..d22b0acf2a 100755
--- a/t/t7512-status-help.sh
+++ b/t/t7512-status-help.sh
@@ -71,10 +71,10 @@ test_expect_success 'prepare for rebase conflicts' '
'
-test_expect_success 'status when rebase in progress before resolving conflicts' '
+test_expect_success 'status when rebase --am in progress before resolving conflicts' '
test_when_finished "git rebase --abort" &&
ONTO=$(git rev-parse --short HEAD^^) &&
- test_must_fail git rebase HEAD^ --onto HEAD^^ &&
+ test_must_fail git rebase --am HEAD^ --onto HEAD^^ &&
cat >expected <<EOF &&
rebase in progress; onto $ONTO
You are currently rebasing branch '\''rebase_conflicts'\'' on '\''$ONTO'\''.
@@ -94,11 +94,11 @@ EOF
'
-test_expect_success 'status when rebase in progress before rebase --continue' '
+test_expect_success 'status when rebase --am in progress before rebase --continue' '
git reset --hard rebase_conflicts &&
test_when_finished "git rebase --abort" &&
ONTO=$(git rev-parse --short HEAD^^) &&
- test_must_fail git rebase HEAD^ --onto HEAD^^ &&
+ test_must_fail git rebase --am HEAD^ --onto HEAD^^ &&
echo three >main.txt &&
git add main.txt &&
cat >expected <<EOF &&
@@ -688,7 +688,7 @@ EOF
'
-test_expect_success 'status when rebase conflicts with statushints disabled' '
+test_expect_success 'status when rebase --am conflicts with statushints disabled' '
git reset --hard master &&
git checkout -b statushints_disabled &&
test_when_finished "git config --local advice.statushints true" &&
@@ -698,7 +698,7 @@ test_expect_success 'status when rebase conflicts with statushints disabled' '
test_commit three_statushints main.txt three &&
test_when_finished "git rebase --abort" &&
ONTO=$(git rev-parse --short HEAD^^) &&
- test_must_fail git rebase HEAD^ --onto HEAD^^ &&
+ test_must_fail git rebase --am HEAD^ --onto HEAD^^ &&
cat >expected <<EOF &&
rebase in progress; onto $ONTO
You are currently rebasing branch '\''statushints_disabled'\'' on '\''$ONTO'\''.
--
gitgitgadget
^ permalink raw reply related [flat|nested] 161+ messages in thread
* [PATCH v3 13/15] rebase tests: repeat some tests using the merge backend instead of am
2019-12-24 19:54 ` [PATCH v3 00/15] rebase: make the default backend configurable Elijah Newren via GitGitGadget
` (11 preceding siblings ...)
2019-12-24 19:54 ` [PATCH v3 12/15] rebase tests: mark tests specific to the am-backend with --am Elijah Newren via GitGitGadget
@ 2019-12-24 19:54 ` Elijah Newren via GitGitGadget
2019-12-24 19:54 ` [PATCH v3 14/15] rebase: make the backend configurable via config setting Elijah Newren via GitGitGadget
` (2 subsequent siblings)
15 siblings, 0 replies; 161+ messages in thread
From: Elijah Newren via GitGitGadget @ 2019-12-24 19:54 UTC (permalink / raw)
To: git
Cc: Johannes.Schindelin, phillip.wood, liu.denton, gitster, plroskin,
alban.gruin, szeder.dev, Junio C Hamano, Elijah Newren
From: Elijah Newren <newren@gmail.com>
In order to ensure the merge/interactive backend gets similar coverage
to the am one, add some tests for cases where previously only the am
backend was tested.
Signed-off-by: Elijah Newren <newren@gmail.com>
---
t/t5520-pull.sh | 17 +++++++++++++++--
t/t6047-diff3-conflict-markers.sh | 13 +++++++++++--
2 files changed, 26 insertions(+), 4 deletions(-)
diff --git a/t/t5520-pull.sh b/t/t5520-pull.sh
index 602d996a33..3fff6a06fa 100755
--- a/t/t5520-pull.sh
+++ b/t/t5520-pull.sh
@@ -277,14 +277,27 @@ test_expect_success '--rebase' '
test_cmp expect actual
'
-test_expect_success '--rebase fast forward' '
+test_expect_success '--rebase (merge) fast forward' '
git reset --hard before-rebase &&
git checkout -b ff &&
echo another modification >file &&
git commit -m third file &&
git checkout to-rebase &&
- git pull --rebase . ff &&
+ git -c rebase.backend=merge pull --rebase . ff &&
+ test_cmp_rev HEAD ff &&
+
+ # The above only validates the result. Did we actually bypass rebase?
+ git reflog -1 >reflog.actual &&
+ sed "s/^[0-9a-f][0-9a-f]*/OBJID/" reflog.actual >reflog.fuzzy &&
+ echo "OBJID HEAD@{0}: pull --rebase . ff: Fast-forward" >reflog.expected &&
+ test_cmp reflog.expected reflog.fuzzy
+'
+
+test_expect_success '--rebase (am) fast forward' '
+ git reset --hard before-rebase &&
+
+ git -c rebase.backend=am pull --rebase . ff &&
test_cmp_rev HEAD ff &&
# The above only validates the result. Did we actually bypass rebase?
diff --git a/t/t6047-diff3-conflict-markers.sh b/t/t6047-diff3-conflict-markers.sh
index 860542aad0..d383ce8130 100755
--- a/t/t6047-diff3-conflict-markers.sh
+++ b/t/t6047-diff3-conflict-markers.sh
@@ -186,7 +186,7 @@ test_expect_success 'check multiple merge bases' '
)
'
-test_expect_success 'rebase describes fake ancestor base' '
+test_expect_success 'rebase --merge describes parent of commit being picked' '
test_create_repo rebase &&
(
cd rebase &&
@@ -194,7 +194,16 @@ test_expect_success 'rebase describes fake ancestor base' '
test_commit master file &&
git checkout -b side HEAD^ &&
test_commit side file &&
- test_must_fail git -c merge.conflictstyle=diff3 rebase master &&
+ test_must_fail git -c merge.conflictstyle=diff3 rebase --merge master &&
+ grep "||||||| parent of" file
+ )
+'
+
+test_expect_success 'rebase --am describes fake ancestor base' '
+ (
+ cd rebase &&
+ git rebase --abort &&
+ test_must_fail git -c merge.conflictstyle=diff3 rebase --am master &&
grep "||||||| constructed merge base" file
)
'
--
gitgitgadget
^ permalink raw reply related [flat|nested] 161+ messages in thread
* [PATCH v3 14/15] rebase: make the backend configurable via config setting
2019-12-24 19:54 ` [PATCH v3 00/15] rebase: make the default backend configurable Elijah Newren via GitGitGadget
` (12 preceding siblings ...)
2019-12-24 19:54 ` [PATCH v3 13/15] rebase tests: repeat some tests using the merge backend instead of am Elijah Newren via GitGitGadget
@ 2019-12-24 19:54 ` Elijah Newren via GitGitGadget
2019-12-24 19:54 ` [PATCH v3 15/15] rebase: change the default backend from "am" to "merge" Elijah Newren via GitGitGadget
2020-01-16 6:14 ` [PATCH v4 00/19] rebase: make the default backend configurable Elijah Newren via GitGitGadget
15 siblings, 0 replies; 161+ messages in thread
From: Elijah Newren via GitGitGadget @ 2019-12-24 19:54 UTC (permalink / raw)
To: git
Cc: Johannes.Schindelin, phillip.wood, liu.denton, gitster, plroskin,
alban.gruin, szeder.dev, Junio C Hamano, Elijah Newren
From: Elijah Newren <newren@gmail.com>
Signed-off-by: Elijah Newren <newren@gmail.com>
---
Documentation/config/rebase.txt | 8 ++++++++
builtin/rebase.c | 31 ++++++++++++++++++++++++-------
2 files changed, 32 insertions(+), 7 deletions(-)
diff --git a/Documentation/config/rebase.txt b/Documentation/config/rebase.txt
index d98e32d812..e6ae30c999 100644
--- a/Documentation/config/rebase.txt
+++ b/Documentation/config/rebase.txt
@@ -5,6 +5,14 @@ rebase.useBuiltin::
is always used. Setting this will emit a warning, to alert any
remaining users that setting this now does nothing.
+rebase.backend::
+ Default backend to use for rebasing. Possible choices are
+ 'am' or 'merge' (note that the merge backend is sometimes also
+ refered to as the interactive backend or the interactive
+ machinery elsewhere in the docs). Also, in the future, if the
+ merge backend gains all remaining capabilities of the am
+ backend, this setting may become unused.
+
rebase.stat::
Whether to show a diffstat of what changed upstream since the last
rebase. False by default.
diff --git a/builtin/rebase.c b/builtin/rebase.c
index b7915fc0cb..d602b2da4c 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -60,6 +60,7 @@ enum empty_type {
struct rebase_options {
enum rebase_type type;
enum empty_type empty;
+ const char *default_backend;
const char *state_dir;
struct commit *upstream;
const char *upstream_name;
@@ -103,6 +104,7 @@ struct rebase_options {
#define REBASE_OPTIONS_INIT { \
.type = REBASE_UNSPECIFIED, \
.empty = EMPTY_UNSPECIFIED, \
+ .default_backend = "am", \
.flags = REBASE_NO_QUIET, \
.git_am_opts = ARGV_ARRAY_INIT, \
.git_format_patch_opt = STRBUF_INIT \
@@ -1298,6 +1300,10 @@ static int rebase_config(const char *var, const char *value, void *data)
return 0;
}
+ if (!strcmp(var, "rebase.backend")) {
+ return git_config_string(&opts->default_backend, var, value);
+ }
+
return git_default_config(var, value, data);
}
@@ -1928,9 +1934,23 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
if (strcmp(options.git_am_opts.argv[i], "-q"))
break;
- if (is_interactive(&options) && i >= 0)
- die(_("cannot combine am options with either "
- "interactive or merge options"));
+ if (i >= 0) {
+ if (is_interactive(&options))
+ die(_("cannot combine am options with either "
+ "interactive or merge options"));
+ else
+ options.type = REBASE_AM;
+ }
+ }
+
+ if (options.type == REBASE_UNSPECIFIED) {
+ if (!strcmp(options.default_backend, "merge"))
+ options.type = REBASE_MERGE;
+ else if (!strcmp(options.default_backend, "am"))
+ options.type = REBASE_AM;
+ else
+ die(_("Unknown rebase backend: %s"),
+ options.default_backend);
}
switch (options.type) {
@@ -1943,10 +1963,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
options.state_dir = apply_dir();
break;
default:
- /* the default rebase backend is `--am` */
- options.type = REBASE_AM;
- options.state_dir = apply_dir();
- break;
+ BUG("options.type was just set above; should be unreachable.");
}
if (options.empty == EMPTY_UNSPECIFIED) {
--
gitgitgadget
^ permalink raw reply related [flat|nested] 161+ messages in thread
* [PATCH v3 15/15] rebase: change the default backend from "am" to "merge"
2019-12-24 19:54 ` [PATCH v3 00/15] rebase: make the default backend configurable Elijah Newren via GitGitGadget
` (13 preceding siblings ...)
2019-12-24 19:54 ` [PATCH v3 14/15] rebase: make the backend configurable via config setting Elijah Newren via GitGitGadget
@ 2019-12-24 19:54 ` Elijah Newren via GitGitGadget
2020-01-10 23:14 ` Jonathan Nieder
2020-01-16 6:14 ` [PATCH v4 00/19] rebase: make the default backend configurable Elijah Newren via GitGitGadget
15 siblings, 1 reply; 161+ messages in thread
From: Elijah Newren via GitGitGadget @ 2019-12-24 19:54 UTC (permalink / raw)
To: git
Cc: Johannes.Schindelin, phillip.wood, liu.denton, gitster, plroskin,
alban.gruin, szeder.dev, Junio C Hamano, Elijah Newren
From: Elijah Newren <newren@gmail.com>
The am-backend drops information and thus limits what we can do:
* lack of full tree information from the original commits means we
cannot do directory rename detection and warn users that they might
want to move some of their new files that they placed in old
directories to prevent their becoming orphaned.[1]
* reduction in context from only having a few lines beyond those
changed means that when context lines are non-unique we can apply
patches incorrectly.[2]
* lack of access to original commits means that conflict marker
annotation has less information available.
Also, the merge/interactive backend have far more abilities, appear to
currently have a slight performance advantage[3] and have room for more
optimizations than the am backend[4] (and work is underway to take
advantage of some of those possibilities).
[1] https://lore.kernel.org/git/xmqqh8jeh1id.fsf@gitster-ct.c.googlers.com/
[2] https://lore.kernel.org/git/CABPp-BGiu2nVMQY_t-rnFR5GQUz_ipyEE8oDocKeO+h+t4Mn4A@mail.gmail.com/
[3] https://public-inbox.org/git/CABPp-BF=ev03WgODk6TMQmuNoatg2kiEe5DR__gJ0OTVqHSnfQ@mail.gmail.com/
[4] https://lore.kernel.org/git/CABPp-BGh7yW69QwxQb13K0HM38NKmQif3A6C6UULEKYnkEJ5vA@mail.gmail.com/
Signed-off-by: Elijah Newren <newren@gmail.com>
---
Documentation/git-rebase.txt | 2 +-
builtin/rebase.c | 4 ++--
t/t5520-pull.sh | 10 ++++++----
t/t9106-git-svn-commit-diff-clobber.sh | 3 ++-
4 files changed, 11 insertions(+), 8 deletions(-)
diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index cf1ac2e359..e819889a31 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -309,7 +309,7 @@ See also INCOMPATIBLE OPTIONS below.
--merge::
Use merging strategies to rebase. When the recursive (default) merge
strategy is used, this allows rebase to be aware of renames on the
- upstream side.
+ upstream side. This is the default.
+
Note that a rebase merge works by replaying each commit from the working
branch on top of the <upstream> branch. Because of this, when a merge
diff --git a/builtin/rebase.c b/builtin/rebase.c
index d602b2da4c..938a8840b1 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -104,7 +104,7 @@ struct rebase_options {
#define REBASE_OPTIONS_INIT { \
.type = REBASE_UNSPECIFIED, \
.empty = EMPTY_UNSPECIFIED, \
- .default_backend = "am", \
+ .default_backend = "merge", \
.flags = REBASE_NO_QUIET, \
.git_am_opts = ARGV_ARRAY_INIT, \
.git_format_patch_opt = STRBUF_INIT \
@@ -1945,7 +1945,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
if (options.type == REBASE_UNSPECIFIED) {
if (!strcmp(options.default_backend, "merge"))
- options.type = REBASE_MERGE;
+ imply_interactive(&options, "--merge");
else if (!strcmp(options.default_backend, "am"))
options.type = REBASE_AM;
else
diff --git a/t/t5520-pull.sh b/t/t5520-pull.sh
index 3fff6a06fa..4f9e7f7ff6 100755
--- a/t/t5520-pull.sh
+++ b/t/t5520-pull.sh
@@ -340,7 +340,7 @@ test_expect_success '--rebase with conflicts shows advice' '
test_tick &&
git commit -m "Create conflict" seq.txt &&
test_must_fail git pull --rebase . seq 2>err >out &&
- test_i18ngrep "Resolve all conflicts manually" out
+ test_i18ngrep "Resolve all conflicts manually" err
'
test_expect_success 'failed --rebase shows advice' '
@@ -354,7 +354,7 @@ test_expect_success 'failed --rebase shows advice' '
git checkout -f -b fails-to-rebase HEAD^ &&
test_commit v2-without-cr file "2" file2-lf &&
test_must_fail git pull --rebase . diverging 2>err >out &&
- test_i18ngrep "Resolve all conflicts manually" out
+ test_i18ngrep "Resolve all conflicts manually" err
'
test_expect_success '--rebase fails with multiple branches' '
@@ -774,8 +774,10 @@ test_expect_success 'git pull --rebase does not reapply old patches' '
(
cd dst &&
test_must_fail git pull --rebase &&
- find .git/rebase-apply -name "000*" >patches &&
- test_line_count = 1 patches
+ cat .git/rebase-merge/done .git/rebase-merge/git-rebase-todo >work &&
+ grep -v -e \# -e ^$ work >patches &&
+ test_line_count = 1 patches &&
+ rm -f work
)
'
diff --git a/t/t9106-git-svn-commit-diff-clobber.sh b/t/t9106-git-svn-commit-diff-clobber.sh
index dbe8deac0d..aec45bca3b 100755
--- a/t/t9106-git-svn-commit-diff-clobber.sh
+++ b/t/t9106-git-svn-commit-diff-clobber.sh
@@ -92,7 +92,8 @@ test_expect_success 'multiple dcommit from git svn will not clobber svn' "
test_expect_success 'check that rebase really failed' '
- test -d .git/rebase-apply
+ git status >output &&
+ grep currently.rebasing output
'
test_expect_success 'resolve, continue the rebase and dcommit' "
--
gitgitgadget
^ permalink raw reply related [flat|nested] 161+ messages in thread
* Re: [PATCH v3 01/15] rebase: extend the options for handling of empty commits
2019-12-24 19:54 ` [PATCH v3 01/15] rebase: extend the options for handling of empty commits Elijah Newren via GitGitGadget
@ 2020-01-07 14:37 ` Phillip Wood
2020-01-07 19:15 ` Elijah Newren
0 siblings, 1 reply; 161+ messages in thread
From: Phillip Wood @ 2020-01-07 14:37 UTC (permalink / raw)
To: Elijah Newren via GitGitGadget, git
Cc: Johannes.Schindelin, phillip.wood, liu.denton, gitster, plroskin,
alban.gruin, szeder.dev, Elijah Newren
Hi Elijah
Thanks for working on this series, I think making the sequencer the
default backend is a good idea. I have a few reservations about this
path though...
On 24/12/2019 19:54, Elijah Newren via GitGitGadget wrote:
> From: Elijah Newren <newren@gmail.com>
>
> Extend the interactive machinery with the ability to handle the full
> spread of options for how to handle commits that either start or become
> empty (by "become empty" I mean the changes in a commit are a subset of
> changes that exist upstream, so the net effect of applying the commit is
> no changes). Introduce a new command line flag for selecting the
> desired behavior:
> --empty={drop,keep,ask}
> with the definitions:
> drop: drop empty commits
> keep: keep empty commits
> ask: provide the user a chance to interact and pick what to do with
> empty commits on a case-by-case basis
I think we want to distinguish between commits that are empty before
rebasing and those that become empty when they are rebased. --keep-empty
explicily only applies to commits that are already empty. Cherry-pick
has distinct options for those two cases. If I've explicitly created an
empty commit then I want to keep it but I don't want to keep commits
that become empty because the changes they contain are already upstream.
If we want an option that keeps commits that become empty (Off hand I
don't know why we would though) we should consider if that option should
disable --cherry-mark when we create the todo list so that it keeps all
commits that become empty when they're rebased.
> Note that traditionally, am-based rebases have always dropped commits
> that either started or became empty, while interactive-based rebases
> have defaulted to ask (and provided an option to keep commits that
> started empty). This difference made sense since users of an am-based
> rebase just wanted to quickly batch apply a sequence of commits, while
> users editing a todo list will likely want the chance to interact and
> handle unusual cases on a case-by-case basis.
I don't see why it makes sense to drop an empty commit that I've made
just because it is being rebased. I'm pretty sure the behavor of the
am-based rebase is a function of `git am` not being able to create empty
commits.
> However, not all rebases
> using the interactive machinery are explicitly interactive anymore. In
> particular --merge was always meant to behave more like --am: just
> rebase a batch of commits without popping up a todo list.
>
> If the --empty flag is not specified, pick defaults as follows:
> explicitly interactive: ask
> --exec: keep (exec is about checking existing commits, and often
> used without actually changing the base. Thus the
> expectation is that the user doesn't necessarily want
> anything to change; they just want to test).
> otherwise: drop
I'm not sure I like changing the behavior based on --exec, I see what
you're getting at but it has the potential to be confusing. What if I
want to rearrange the commits without changing the base - why must I
specify --empty=keep there but not if I add --exec to the command line?
> Also, this commit makes --keep-empty just imply --empty=keep, and hides
> it from help so that we aren't confusing users with different ways to do
> the same thing. (I could have added a --drop-empty flag, but then that
> invites users to specify both --keep-empty and --drop-empty and we have
> to add sanity checking around that; it seems cleaner to have a single
> multi-valued option.) This actually fixes --keep-empty too; previously,
> it only meant to sometimes keep empty commits, in particular commits
> which started empty would be kept. But it would still error out and ask
> the user what to do with commits that became empty. Now it keeps empty
> commits, as instructed.
It certainly changes the behavior of --keep-empty but I'm not sure it
"fixes" it. If I have some empty commits I want to keep as placeholders
then that's different from wanting to keep commits that become empty
because their changes are upstream but --cherry-mark didn't detect them.
In summary I'm in favor of making it easier to drop commits that become
empty but not tying that to the handling of commits that are empty
before they are rebased.
I'm also not happy that the deprecation of --keep-empty suddenly makes
--no-keep-empty an error.
>
> Signed-off-by: Elijah Newren <newren@gmail.com>
> ---
> Documentation/git-rebase.txt | 35 ++++++------
> builtin/rebase.c | 83 +++++++++++++++++++++++++---
> rebase-interactive.c | 4 +-
> rebase-interactive.h | 2 +-
> sequencer.c | 74 +++++++++++++++++++------
> sequencer.h | 6 ++-
> t/t3421-rebase-topology-linear.sh | 4 +-
> t/t3424-rebase-empty.sh | 89 +++++++++++++++++++++++++++++++
> t/t3427-rebase-subtree.sh | 16 +++---
> 9 files changed, 263 insertions(+), 50 deletions(-)
> create mode 100755 t/t3424-rebase-empty.sh
>
> diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
> index 1d0e2d27cc..ff32ca1080 100644
> --- a/Documentation/git-rebase.txt
> +++ b/Documentation/git-rebase.txt
> @@ -258,9 +258,25 @@ See also INCOMPATIBLE OPTIONS below.
> original branch. The index and working tree are also left
> unchanged as a result.
>
> +--empty={drop,keep,ask}::
> + How to handle commits that become empty (because they contain a
> + subset of already upstream changes) or start empty. With drop
> + (the default), commits that start or become empty are dropped.
> + With keep (implied by --exec), such commits are kept. With ask
> + (implied by --interactive), the rebase will halt when an empty
> + commit is applied allowing you to choose whether to drop it or
> + commit it. Also with ask, if the rebase is interactive then
> + commits which start empty will be commented out in the todo
> + action list (giving you a chance to uncomment).
> ++
> +Note that this has no effect on commits which are already upstream (as
> +can be checked via `git log --cherry-mark ...`), which are always
> +dropped by rebase.
> ++
> +See also INCOMPATIBLE OPTIONS below.
> +
> --keep-empty::
> - Keep the commits that do not change anything from its
> - parents in the result.
> + Deprecated alias for what is now known as --empty=keep.
> +
> See also INCOMPATIBLE OPTIONS below.
>
> @@ -569,6 +585,7 @@ are incompatible with the following options:
> * --interactive
> * --exec
> * --keep-empty
> + * --empty=
> * --edit-todo
> * --root when used in combination with --onto
>
> @@ -580,6 +597,7 @@ In addition, the following pairs of options are incompatible:
> * --preserve-merges and --ignore-whitespace
> * --preserve-merges and --committer-date-is-author-date
> * --preserve-merges and --ignore-date
> + * --preserve-merges and --empty=
> * --keep-base and --onto
> * --keep-base and --root
>
> @@ -588,19 +606,6 @@ BEHAVIORAL DIFFERENCES
>
> There are some subtle differences how the backends behave.
>
> -Empty commits
> -~~~~~~~~~~~~~
> -
> -The am backend drops any "empty" commits, regardless of whether the
> -commit started empty (had no changes relative to its parent to
> -start with) or ended empty (all changes were already applied
> -upstream in other commits).
> -
> -The interactive backend drops commits by default that
> -started empty and halts if it hits a commit that ended up empty.
> -The `--keep-empty` option exists for the interactive backend to allow
> -it to keep commits that started empty.
> -
> Directory rename detection
> ~~~~~~~~~~~~~~~~~~~~~~~~~~
>
> diff --git a/builtin/rebase.c b/builtin/rebase.c
> index ddf33bc9d4..6903249307 100644
> --- a/builtin/rebase.c
> +++ b/builtin/rebase.c
> @@ -50,8 +50,16 @@ enum rebase_type {
> REBASE_PRESERVE_MERGES
> };
>
> +enum empty_type {
> + EMPTY_UNSPECIFIED = -1,
> + EMPTY_DROP,
> + EMPTY_KEEP,
> + EMPTY_ASK
> +};
> +
> struct rebase_options {
> enum rebase_type type;
> + enum empty_type empty;
> const char *state_dir;
> struct commit *upstream;
> const char *upstream_name;
> @@ -77,7 +85,6 @@ struct rebase_options {
> const char *action;
> int signoff;
> int allow_rerere_autoupdate;
> - int keep_empty;
> int autosquash;
> int ignore_whitespace;
> char *gpg_sign_opt;
> @@ -95,6 +102,7 @@ struct rebase_options {
>
> #define REBASE_OPTIONS_INIT { \
> .type = REBASE_UNSPECIFIED, \
> + .empty = EMPTY_UNSPECIFIED, \
> .flags = REBASE_NO_QUIET, \
> .git_am_opts = ARGV_ARRAY_INIT, \
> .git_format_patch_opt = STRBUF_INIT \
> @@ -114,6 +122,10 @@ static struct replay_opts get_replay_opts(const struct rebase_options *opts)
> replay.allow_rerere_auto = opts->allow_rerere_autoupdate;
> replay.allow_empty = 1;
> replay.allow_empty_message = opts->allow_empty_message;
> + replay.drop_redundant_commits = (opts->empty == EMPTY_DROP);
> + replay.keep_redundant_commits = (opts->empty == EMPTY_KEEP);
> + replay.ask_on_initially_empty = (opts->empty == EMPTY_ASK &&
> + !(opts->flags & REBASE_INTERACTIVE_EXPLICIT));
> replay.verbose = opts->flags & REBASE_VERBOSE;
> replay.reschedule_failed_exec = opts->reschedule_failed_exec;
> replay.committer_date_is_author_date =
> @@ -389,7 +401,10 @@ static int run_rebase_interactive(struct rebase_options *opts,
>
> git_config_get_bool("rebase.abbreviatecommands", &abbreviate_commands);
>
> - flags |= opts->keep_empty ? TODO_LIST_KEEP_EMPTY : 0;
> + flags |= (opts->empty == EMPTY_DROP) ? TODO_LIST_DROP_EMPTY : 0;
> + flags |= (opts->empty == EMPTY_ASK &&
> + opts->flags & REBASE_INTERACTIVE_EXPLICIT) ?
> + TODO_LIST_ASK_EMPTY : 0;
> flags |= abbreviate_commands ? TODO_LIST_ABBREVIATE_CMDS : 0;
> flags |= opts->rebase_merges ? TODO_LIST_REBASE_MERGES : 0;
> flags |= opts->rebase_cousins > 0 ? TODO_LIST_REBASE_COUSINS : 0;
> @@ -453,6 +468,19 @@ static int run_rebase_interactive(struct rebase_options *opts,
> return ret;
> }
>
> +static int parse_opt_keep_empty(const struct option *opt, const char *arg,
> + int unset)
> +{
> + struct rebase_options *opts = opt->value;
> +
> + BUG_ON_OPT_NEG(unset);
> + BUG_ON_OPT_ARG(arg);
> +
> + opts->empty = EMPTY_KEEP;
> + opts->type = REBASE_INTERACTIVE;
> + return 0;
> +}
> +
> static const char * const builtin_rebase_interactive_usage[] = {
> N_("git rebase--interactive [<options>]"),
> NULL
> @@ -466,7 +494,10 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)
> struct option options[] = {
> OPT_NEGBIT(0, "ff", &opts.flags, N_("allow fast-forward"),
> REBASE_FORCE),
> - OPT_BOOL(0, "keep-empty", &opts.keep_empty, N_("keep empty commits")),
> + { OPTION_CALLBACK, 'k', "keep-empty", &options, NULL,
> + N_("(DEPRECATED) keep empty commits"),
> + PARSE_OPT_NOARG | PARSE_OPT_NONEG | PARSE_OPT_HIDDEN,
It is all very well deprecating --keep-empty but suddenly making
'--no-keep-empty' an error goes beyond deprecation. Also I'm not sure
it's worth changing these options as I think the only user is
git-rebase--preserve-merges.sh
Best Wishes
Phillip
> + parse_opt_keep_empty },
> OPT_BOOL(0, "allow-empty-message", &opts.allow_empty_message,
> N_("allow commits with empty messages")),
> OPT_BOOL(0, "rebase-merges", &opts.rebase_merges, N_("rebase merge commits")),
> @@ -1166,7 +1197,7 @@ static int run_specific_rebase(struct rebase_options *opts, enum action action)
> opts->allow_rerere_autoupdate ?
> opts->allow_rerere_autoupdate == RERERE_AUTOUPDATE ?
> "--rerere-autoupdate" : "--no-rerere-autoupdate" : "");
> - add_var(&script_snippet, "keep_empty", opts->keep_empty ? "yes" : "");
> + add_var(&script_snippet, "empty", opts->empty == EMPTY_KEEP ? "yes" : "");
> add_var(&script_snippet, "autosquash", opts->autosquash ? "t" : "");
> add_var(&script_snippet, "gpg_sign_opt", opts->gpg_sign_opt);
> add_var(&script_snippet, "cmd", opts->cmd);
> @@ -1360,6 +1391,29 @@ static int parse_opt_interactive(const struct option *opt, const char *arg,
> return 0;
> }
>
> +static enum empty_type parse_empty_value(const char *value)
> +{
> + if (!strcasecmp(value, "drop"))
> + return EMPTY_DROP;
> + else if (!strcasecmp(value, "keep"))
> + return EMPTY_KEEP;
> + else if (!strcasecmp(value, "ask"))
> + return EMPTY_ASK;
> +
> + die(_("unrecognized empty type '%s'; valid values are \"drop\", \"keep\", and \"ask\"."), value);
> +}
> +
> +static int parse_opt_empty(const struct option *opt, const char *arg, int unset)
> +{
> + struct rebase_options *options = opt->value;
> + enum empty_type value = parse_empty_value(arg);
> +
> + BUG_ON_OPT_NEG(unset);
> +
> + options->empty = value;
> + return 0;
> +}
> +
> static void NORETURN error_on_missing_default_upstream(void)
> {
> struct branch *current_branch = branch_get(NULL);
> @@ -1505,8 +1559,13 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
> "ignoring them"),
> REBASE_PRESERVE_MERGES, PARSE_OPT_HIDDEN),
> OPT_RERERE_AUTOUPDATE(&options.allow_rerere_autoupdate),
> - OPT_BOOL('k', "keep-empty", &options.keep_empty,
> - N_("preserve empty commits during rebase")),
> + OPT_CALLBACK_F(0, "empty", &options, N_("{drop,keep,ask}"),
> + N_("how to handle empty commits"),
> + PARSE_OPT_NONEG, parse_opt_empty),
> + { OPTION_CALLBACK, 'k', "keep-empty", &options, NULL,
> + N_("(DEPRECATED) keep empty commits"),
> + PARSE_OPT_NOARG | PARSE_OPT_NONEG | PARSE_OPT_HIDDEN,
> + parse_opt_keep_empty },
> OPT_BOOL(0, "autosquash", &options.autosquash,
> N_("move commits that begin with "
> "squash!/fixup! under -i")),
> @@ -1770,8 +1829,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
> if (!(options.flags & REBASE_NO_QUIET))
> argv_array_push(&options.git_am_opts, "-q");
>
> - if (options.keep_empty)
> - imply_interactive(&options, "--keep-empty");
> + if (options.empty != EMPTY_UNSPECIFIED)
> + imply_interactive(&options, "--empty");
>
> if (gpg_sign) {
> free(options.gpg_sign_opt);
> @@ -1856,6 +1915,14 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
> break;
> }
>
> + if (options.empty == EMPTY_UNSPECIFIED) {
> + if (options.flags & REBASE_INTERACTIVE_EXPLICIT)
> + options.empty = EMPTY_ASK;
> + else if (exec.nr > 0)
> + options.empty = EMPTY_KEEP;
> + else
> + options.empty = EMPTY_DROP;
> + }
> if (reschedule_failed_exec > 0 && !is_interactive(&options))
> die(_("--reschedule-failed-exec requires "
> "--exec or --interactive"));
> diff --git a/rebase-interactive.c b/rebase-interactive.c
> index aa18ae82b7..ad82bf77df 100644
> --- a/rebase-interactive.c
> +++ b/rebase-interactive.c
> @@ -28,7 +28,7 @@ static enum missing_commit_check_level get_missing_commit_check_level(void)
> return MISSING_COMMIT_CHECK_IGNORE;
> }
>
> -void append_todo_help(unsigned keep_empty, int command_count,
> +void append_todo_help(unsigned no_ask_empty, int command_count,
> const char *shortrevisions, const char *shortonto,
> struct strbuf *buf)
> {
> @@ -81,7 +81,7 @@ void append_todo_help(unsigned keep_empty, int command_count,
>
> strbuf_add_commented_lines(buf, msg, strlen(msg));
>
> - if (!keep_empty) {
> + if (!no_ask_empty) {
> msg = _("Note that empty commits are commented out");
> strbuf_add_commented_lines(buf, msg, strlen(msg));
> }
> diff --git a/rebase-interactive.h b/rebase-interactive.h
> index 44dbb06311..f531e00ba7 100644
> --- a/rebase-interactive.h
> +++ b/rebase-interactive.h
> @@ -5,7 +5,7 @@ struct strbuf;
> struct repository;
> struct todo_list;
>
> -void append_todo_help(unsigned keep_empty, int command_count,
> +void append_todo_help(unsigned no_ask_empty, int command_count,
> const char *shortrevisions, const char *shortonto,
> struct strbuf *buf);
> int edit_todo_list(struct repository *r, struct todo_list *todo_list,
> diff --git a/sequencer.c b/sequencer.c
> index 763ccbbc45..d2c11f34b7 100644
> --- a/sequencer.c
> +++ b/sequencer.c
> @@ -160,6 +160,9 @@ 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 GIT_PATH_FUNC(rebase_path_ask_on_initially_empty, "rebase-merge/ask_on_initially_empty")
>
> static int git_sequencer_config(const char *k, const char *v, void *cb)
> {
> @@ -1623,7 +1626,7 @@ static int allow_empty(struct repository *r,
> empty_commit = is_original_commit_empty(commit);
> if (empty_commit < 0)
> return empty_commit;
> - if (!empty_commit)
> + if (!empty_commit || opts->ask_on_initially_empty)
> return 0;
> else
> return 1;
> @@ -1837,7 +1840,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) {
> /*
> @@ -2042,13 +2045,20 @@ static int do_pick_commit(struct repository *r,
> goto leave;
> }
>
> - allow = allow_empty(r, opts, commit);
> - if (allow < 0) {
> - res = allow;
> - goto leave;
> - } else if (allow)
> - flags |= ALLOW_EMPTY;
> - if (!opts->no_commit) {
> + drop_commit = 0;
> + if (opts->drop_redundant_commits && is_index_unchanged(r)) {
> + drop_commit = 1;
> + fprintf(stderr, _("No changes -- Patch already applied."));
> + } else {
> + allow = allow_empty(r, opts, commit);
> + if (allow < 0) {
> + res = allow;
> + goto leave;
> + } else if (allow) {
> + flags |= ALLOW_EMPTY;
> + }
> + }
> + if (!opts->no_commit && !drop_commit) {
> if (author || command == TODO_REVERT || (flags & AMEND_MSG))
> res = do_commit(r, msg_file, author, opts, flags);
> else
> @@ -2501,9 +2511,15 @@ static int populate_opts_cb(const char *key, const char *value, void *data)
This function is used by cherry-pick/revert not rebase do we need to
change it?
> else if (!strcmp(key, "options.allow-empty-message"))
> opts->allow_empty_message =
> git_config_bool_or_int(key, value, &error_flag);
> + else if (!strcmp(key, "options.drop-redundant-commits"))
> + opts->drop_redundant_commits =
> + git_config_bool_or_int(key, value, &error_flag);
> else if (!strcmp(key, "options.keep-redundant-commits"))
> opts->keep_redundant_commits =
> git_config_bool_or_int(key, value, &error_flag);
> + else if (!strcmp(key, "options.ask_on_initially_empty"))
> + opts->ask_on_initially_empty =
> + git_config_bool_or_int(key, value, &error_flag);> else if (!strcmp(key, "options.signoff"))
> opts->signoff = git_config_bool_or_int(key, value, &error_flag);
> else if (!strcmp(key, "options.record-origin"))
> @@ -2612,6 +2628,15 @@ 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;
> +
> + if (file_exists(rebase_path_ask_on_initially_empty()))
> + opts->ask_on_initially_empty = 1;
> +
> read_strategy_opts(opts, &buf);
> strbuf_release(&buf);
>
> @@ -2695,6 +2720,12 @@ int write_basic_state(struct replay_opts *opts, const char *head_name,
> write_file(rebase_path_cdate_is_adate(), "%s", "");
> if (opts->ignore_date)
> write_file(rebase_path_ignore_date(), "%s", "");
> + 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->ask_on_initially_empty)
> + write_file(rebase_path_ask_on_initially_empty(), "%s", "");
> if (opts->reschedule_failed_exec)
> write_file(rebase_path_reschedule_failed_exec(), "%s", "");
>
> @@ -3033,9 +3064,15 @@ static int save_opts(struct replay_opts *opts)
again this is for cherry-pick/revert
> if (opts->allow_empty_message)
> res |= git_config_set_in_file_gently(opts_file,
> "options.allow-empty-message", "true");
> + if (opts->drop_redundant_commits)
> + res |= git_config_set_in_file_gently(opts_file,
> + "options.drop-redundant-commits", "true");
> if (opts->keep_redundant_commits)
> res |= git_config_set_in_file_gently(opts_file,
> "options.keep-redundant-commits", "true");
> + if (opts->ask_on_initially_empty)
> + res |= git_config_set_in_file_gently(opts_file,
> + "options.ask_on_initially_empty", "true");
> if (opts->signoff)
> res |= git_config_set_in_file_gently(opts_file,
> "options.signoff", "true");
> @@ -4691,7 +4728,8 @@ 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 drop_empty = flags & TODO_LIST_DROP_EMPTY;
> + int ask_empty = flags & TODO_LIST_ASK_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;
> @@ -4746,6 +4784,8 @@ static int make_script_with_merges(struct pretty_print_context *pp,
> is_empty = is_original_commit_empty(commit);
> if (!is_empty && (commit->object.flags & PATCHSAME))
> continue;
> + if (is_empty && drop_empty)
> + continue;
>
> strbuf_reset(&oneline);
> pretty_print_commit(pp, commit, &oneline);
> @@ -4754,7 +4794,7 @@ 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)
> + if (is_empty && ask_empty)
> strbuf_addf(&buf, "%c ", comment_line_char);
> strbuf_addf(&buf, "%s %s %s", cmd_pick,
> oid_to_hex(&commit->object.oid),
> @@ -4922,7 +4962,8 @@ 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;
> + int drop_empty = flags & TODO_LIST_DROP_EMPTY;
> + int ask_empty = flags & TODO_LIST_ASK_EMPTY;
> const char *insn = flags & TODO_LIST_ABBREVIATE_CMDS ? "p" : "pick";
> int rebase_merges = flags & TODO_LIST_REBASE_MERGES;
>
> @@ -4958,11 +4999,13 @@ 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)
> + if (is_empty && drop_empty)
> + continue;
> + if (is_empty && ask_empty)
> strbuf_addf(out, "%c ", comment_line_char);
> strbuf_addf(out, "%s %s ", insn,
> oid_to_hex(&commit->object.oid));
> @@ -5100,7 +5143,8 @@ 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(!(flags & TODO_LIST_ASK_EMPTY),
> + count_commands(todo_list),
> shortrevisions, shortonto, &buf);
>
> res = write_message(buf.buf, buf.len, file, 0);
> diff --git a/sequencer.h b/sequencer.h
> index e9a0e03ea2..1c3abb661c 100644
> --- a/sequencer.h
> +++ b/sequencer.h
> @@ -39,7 +39,9 @@ struct replay_opts {
> int allow_rerere_auto;
> int allow_empty;
> int allow_empty_message;
> + int drop_redundant_commits;
> int keep_redundant_commits;
> + int ask_on_initially_empty;
> int verbose;
> int quiet;
> int reschedule_failed_exec;
> @@ -134,7 +136,7 @@ int sequencer_rollback(struct repository *repo, struct replay_opts *opts);
> int sequencer_skip(struct repository *repo, struct replay_opts *opts);
> int sequencer_remove_state(struct replay_opts *opts);
>
> -#define TODO_LIST_KEEP_EMPTY (1U << 0)
> +/* #define TODO_LIST_KEEP_EMPTY (1U << 0) */ /* No longer used */
> #define TODO_LIST_SHORTEN_IDS (1U << 1)
> #define TODO_LIST_ABBREVIATE_CMDS (1U << 2)
> #define TODO_LIST_REBASE_MERGES (1U << 3)
> @@ -150,6 +152,8 @@ int sequencer_remove_state(struct replay_opts *opts);
> * `--onto`, we do not want to re-generate the root commits.
> */
> #define TODO_LIST_ROOT_WITH_ONTO (1U << 6)
> +#define TODO_LIST_DROP_EMPTY (1U << 7)
> +#define TODO_LIST_ASK_EMPTY (1U << 8)
>
>
> int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,
> diff --git a/t/t3421-rebase-topology-linear.sh b/t/t3421-rebase-topology-linear.sh
> index 325072b0a3..d23e0bf778 100755
> --- a/t/t3421-rebase-topology-linear.sh
> +++ b/t/t3421-rebase-topology-linear.sh
> @@ -230,7 +230,7 @@ test_run_rebase () {
> test_run_rebase success ''
> test_run_rebase success -m
> test_run_rebase success -i
> -test_have_prereq !REBASE_P || test_run_rebase failure -p
> +test_have_prereq !REBASE_P || test_run_rebase success -p
>
> test_run_rebase () {
> result=$1
> @@ -245,7 +245,7 @@ test_run_rebase () {
> test_run_rebase success ''
> test_run_rebase success -m
> test_run_rebase success -i
> -test_have_prereq !REBASE_P || test_run_rebase failure -p
> +test_have_prereq !REBASE_P || test_run_rebase success -p
> test_run_rebase success --rebase-merges
>
> # m
> diff --git a/t/t3424-rebase-empty.sh b/t/t3424-rebase-empty.sh
> new file mode 100755
> index 0000000000..9d52e1417f
> --- /dev/null
> +++ b/t/t3424-rebase-empty.sh
> @@ -0,0 +1,89 @@
> +#!/bin/sh
> +
> +test_description='git rebase of commits that start or become empty'
> +
> +. ./test-lib.sh
> +
> +test_expect_success 'setup test repository' '
> + test_write_lines 1 2 3 4 5 6 7 8 9 10 >numbers &&
> + test_write_lines A B C D E F G H I J >letters &&
> + git add numbers letters &&
> + git commit -m A &&
> +
> + git branch upstream &&
> + git branch localmods &&
> +
> + git checkout upstream &&
> + test_write_lines A B C D E >letters &&
> + git add letters &&
> + git commit -m B &&
> +
> + test_write_lines 1 2 3 4 five 6 7 8 9 ten >numbers &&
> + git add numbers &&
> + git commit -m C &&
> +
> + git checkout localmods &&
> + test_write_lines 1 2 3 4 five 6 7 8 9 10 >numbers &&
> + git add numbers &&
> + git commit -m C2 &&
> +
> + git commit --allow-empty -m D &&
> +
> + test_write_lines A B C D E >letters &&
> + git add letters &&
> + git commit -m "Five letters ought to be enough for anybody"
> +'
> +
> +test_expect_success 'rebase --merge --empty=drop' '
> + git checkout -B testing localmods &&
> + git rebase --merge --empty=drop upstream &&
> +
> + test_write_lines C B A >expect &&
> + git log --format=%s >actual &&
> + test_cmp expect actual
> +'
> +
> +test_expect_success 'rebase --merge --empty=keep' '
> + git checkout -B testing localmods &&
> + git rebase --merge --empty=keep upstream &&
> +
> + test_write_lines D C2 C B A >expect &&
> + git log --format=%s >actual &&
> + test_cmp expect actual
> +'
> +
> +test_expect_success 'rebase --merge --empty=ask' '
> + git checkout -B testing localmods &&
> + test_must_fail git rebase --merge --empty=ask upstream &&
> +
> + test_must_fail git rebase --skip &&
> + git commit --allow-empty &&
> + git rebase --continue &&
> +
> + test_write_lines D C B A >expect &&
> + git log --format=%s >actual &&
> + test_cmp expect actual
> +'
> +
> +GIT_SEQUENCE_EDITOR=: && export GIT_SEQUENCE_EDITOR
> +
> +test_expect_success 'rebase --interactive --empty=drop' '
> + git checkout -B testing localmods &&
> + git rebase --interactive --empty=drop upstream &&
> +
> + test_write_lines C B A >expect &&
> + git log --format=%s >actual &&
> + test_cmp expect actual
> +'
> +
> +test_expect_success 'rebase --interactive --empty=keep' '
> + git checkout -B testing localmods &&
> + git rebase --interactive --empty=keep upstream &&
> +
> + test_write_lines D C2 C B A >expect &&
> + git log --format=%s >actual &&
> + test_cmp expect actual
> +'
> +
> +
> +test_done
> diff --git a/t/t3427-rebase-subtree.sh b/t/t3427-rebase-subtree.sh
> index bec48e6a1f..468ebc1bef 100755
> --- a/t/t3427-rebase-subtree.sh
> +++ b/t/t3427-rebase-subtree.sh
> @@ -85,23 +85,27 @@ test_expect_failure REBASE_P 'Rebase -Xsubtree --keep-empty --preserve-merges --
> verbose test "$(commit_message HEAD)" = "Empty commit"
> '
>
> -test_expect_success 'Rebase -Xsubtree --keep-empty --onto commit' '
> +test_expect_success 'Rebase -Xsubtree --empty=ask --onto commit' '
> reset_rebase &&
> git checkout -b rebase-onto to-rebase &&
> - test_must_fail git rebase -Xsubtree=files_subtree --keep-empty --onto files-master master &&
> + test_must_fail git rebase -Xsubtree=files_subtree --empty=ask --onto files-master master &&
> : first pick results in no changes &&
> - git rebase --continue &&
> + test_must_fail git rebase --skip &&
> + : last pick was an empty commit that has no changes, but we want to keep it &&
> + git commit --allow-empty &&
> verbose test "$(commit_message HEAD~2)" = "master4" &&
> verbose test "$(commit_message HEAD~)" = "files_subtree/master5" &&
> verbose test "$(commit_message HEAD)" = "Empty commit"
> '
>
> -test_expect_success 'Rebase -Xsubtree --keep-empty --rebase-merges --onto commit' '
> +test_expect_success 'Rebase -Xsubtree --empty=ask --rebase-merges --onto commit' '
> reset_rebase &&
> git checkout -b rebase-merges-onto to-rebase &&
> - test_must_fail git rebase -Xsubtree=files_subtree --keep-empty --rebase-merges --onto files-master --root &&
> + test_must_fail git rebase -Xsubtree=files_subtree --empty=ask --rebase-merges --onto files-master --root &&
> : first pick results in no changes &&
> - git rebase --continue &&
> + test_must_fail git rebase --skip &&
> + : last pick was an empty commit that has no changes, but we want to keep it &&
> + git commit --allow-empty &&
> verbose test "$(commit_message HEAD~2)" = "master4" &&
> verbose test "$(commit_message HEAD~)" = "files_subtree/master5" &&
> verbose test "$(commit_message HEAD)" = "Empty commit"
>
^ permalink raw reply [flat|nested] 161+ messages in thread
* Re: [PATCH v3 10/15] rebase: add an --am option
2019-12-24 19:54 ` [PATCH v3 10/15] rebase: add an --am option Elijah Newren via GitGitGadget
@ 2020-01-07 14:43 ` Phillip Wood
2020-01-07 19:26 ` Elijah Newren
0 siblings, 1 reply; 161+ messages in thread
From: Phillip Wood @ 2020-01-07 14:43 UTC (permalink / raw)
To: Elijah Newren via GitGitGadget, git
Cc: Johannes.Schindelin, phillip.wood, liu.denton, gitster, plroskin,
alban.gruin, szeder.dev, Elijah Newren
Hi Elijah
On 24/12/2019 19:54, Elijah Newren via GitGitGadget wrote:
> From: Elijah Newren <newren@gmail.com>
>
> Currently, this option doesn't do anything except error out if any
> options requiring the interactive-backend are also passed. However,
> when we make the default backend configurable later in this series, this
> flag will provide a way to override the config setting.
I wonder if we really want to add a new option that we know we don't
want to keep in the long term. Could we not just default to the
interactive backend and fallback to the am backend if the user passes
any of the am-based options on the commandline? i.e. reverse the current
situation of defaulting to the am backend and selecting the interactive
backend when the user passes certain options.
Best Wishes
Phillip
> Signed-off-by: Elijah Newren <newren@gmail.com>
> ---
> Documentation/git-rebase.txt | 11 ++++++++++-
> builtin/rebase.c | 18 +++++++++++++++++-
> 2 files changed, 27 insertions(+), 2 deletions(-)
>
> diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
> index f1ace07c38..cf1ac2e359 100644
> --- a/Documentation/git-rebase.txt
> +++ b/Documentation/git-rebase.txt
> @@ -258,6 +258,13 @@ See also INCOMPATIBLE OPTIONS below.
> original branch. The index and working tree are also left
> unchanged as a result.
>
> +--am:
> + Use git-am internally to rebase. This option may become a
> + no-op in the future once the interactive backend handles
> + everything the am one does.
> ++
> +See also INCOMPATIBLE OPTIONS below.
> +
> --empty={drop,keep,ask}::
> How to handle commits that become empty (because they contain a
> subset of already upstream changes) or start empty. With drop
> @@ -372,7 +379,7 @@ See also INCOMPATIBLE OPTIONS below.
> Ensure at least <n> lines of surrounding context match before
> and after each change. When fewer lines of surrounding
> context exist they all must match. By default no context is
> - ever ignored.
> + ever ignored. Implies --am.
> +
> See also INCOMPATIBLE OPTIONS below.
>
> @@ -417,6 +424,7 @@ with `--keep-base` in order to drop those commits from your branch.
> --whitespace=<option>::
> This flag is passed to the 'git apply' program
> (see linkgit:git-apply[1]) that applies the patch.
> + Implies --am.
> +
> See also INCOMPATIBLE OPTIONS below.
>
> @@ -567,6 +575,7 @@ INCOMPATIBLE OPTIONS
>
> The following options:
>
> + * --am
> * --whitespace
> * -C
>
> diff --git a/builtin/rebase.c b/builtin/rebase.c
> index d2b99e9908..b7915fc0cb 100644
> --- a/builtin/rebase.c
> +++ b/builtin/rebase.c
> @@ -1361,6 +1361,18 @@ static int can_fast_forward(struct commit *onto, struct commit *upstream,
> return res && is_linear_history(onto, head);
> }
>
> +static int parse_opt_am(const struct option *opt, const char *arg, int unset)
> +{
> + struct rebase_options *opts = opt->value;
> +
> + BUG_ON_OPT_NEG(unset);
> + BUG_ON_OPT_ARG(arg);
> +
> + opts->type = REBASE_AM;
> +
> + return 0;
> +}
> +
> /* -i followed by -m is still -i */
> static int parse_opt_merge(const struct option *opt, const char *arg, int unset)
> {
> @@ -1546,6 +1558,10 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
> OPT_CMDMODE(0, "show-current-patch", &action,
> N_("show the patch file being applied or merged"),
> ACTION_SHOW_CURRENT_PATCH),
> + { OPTION_CALLBACK, 0, "am", &options, NULL,
> + N_("use apply-mail strategies to rebase"),
> + PARSE_OPT_NOARG | PARSE_OPT_NONEG,
> + parse_opt_am },
> { OPTION_CALLBACK, 'm', "merge", &options, NULL,
> N_("use merging strategies to rebase"),
> PARSE_OPT_NOARG | PARSE_OPT_NONEG,
> @@ -1906,7 +1922,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
> if (isatty(2) && options.flags & REBASE_NO_QUIET)
> strbuf_addstr(&options.git_format_patch_opt, " --progress");
>
> - if (options.git_am_opts.argc) {
> + if (options.git_am_opts.argc || options.type == REBASE_AM) {
> /* all am options except -q are compatible only with --am */
> for (i = options.git_am_opts.argc - 1; i >= 0; i--)
> if (strcmp(options.git_am_opts.argv[i], "-q"))
>
^ permalink raw reply [flat|nested] 161+ messages in thread
* Re: [PATCH v3 01/15] rebase: extend the options for handling of empty commits
2020-01-07 14:37 ` Phillip Wood
@ 2020-01-07 19:15 ` Elijah Newren
2020-01-08 14:27 ` Phillip Wood
0 siblings, 1 reply; 161+ messages in thread
From: Elijah Newren @ 2020-01-07 19:15 UTC (permalink / raw)
To: Phillip Wood
Cc: Elijah Newren via GitGitGadget, Git Mailing List,
Johannes Schindelin, Phillip Wood, Denton Liu, Junio C Hamano,
Pavel Roskin, Alban Gruin, SZEDER Gábor
Hi Phillip,
On Tue, Jan 7, 2020 at 6:37 AM Phillip Wood <phillip.wood123@gmail.com> wrote:
>
> Hi Elijah
>
> Thanks for working on this series, I think making the sequencer the
> default backend is a good idea. I have a few reservations about this
> path though...
Thanks for the feedback. You've provided some good food for thought.
While responding, I came up with an alternate proposal below which I
think may be more in line with what you want. Let me know...
>
> On 24/12/2019 19:54, Elijah Newren via GitGitGadget wrote:
> > From: Elijah Newren <newren@gmail.com>
> >
> > Extend the interactive machinery with the ability to handle the full
> > spread of options for how to handle commits that either start or become
> > empty (by "become empty" I mean the changes in a commit are a subset of
> > changes that exist upstream, so the net effect of applying the commit is
> > no changes). Introduce a new command line flag for selecting the
> > desired behavior:
> > --empty={drop,keep,ask}
> > with the definitions:
> > drop: drop empty commits
> > keep: keep empty commits
> > ask: provide the user a chance to interact and pick what to do with
> > empty commits on a case-by-case basis
>
> I think we want to distinguish between commits that are empty before
> rebasing and those that become empty when they are rebased. --keep-empty
> explicily only applies to commits that are already empty. Cherry-pick
I am open to changing how the empty handling works; I figured it was
the most likely area people might have feedback about.
However, I do strongly disagree with the "explicit" claim here. It is
possible that --keep-empty was designed to only apply to commits that
begin empty, but that is not at all explicit. The only place in the
documentation I could find that mentions the distinction at all was in
the "Behavioral Differences" section, which was written by me as part
of commit 0661e49aeb84 ("git-rebase.txt: document behavioral
differences between modes", 2018-06-27). Quoting from that commit
message:
"(In fact, it's not clear -- to me at least -- that these differences
were even desirable or intentional.) Document these differences."
I cannot find anywhere else making a distinction in either the
documentation or the commit messages or even any code comments, which
has left me wondering whether the distinction was intentional at all.
Things like commit b00bf1c9a8dd ("git-rebase: make
--allow-empty-message the default", 2018-06-27) give heavy precedence
to the assumption that git-rebase is often more happenstance than
design when it comes to edge cases like these.
> has distinct options for those two cases. If I've explicitly created an
> empty commit then I want to keep it but I don't want to keep commits
> that become empty because the changes they contain are already upstream.
>
> If we want an option that keeps commits that become empty (Off hand I
> don't know why we would though) we should consider if that option should
> disable --cherry-mark when we create the todo list so that it keeps all
> commits that become empty when they're rebased.
To answer the particular reason I have seen why folks might want to
keep both commits that start empty and become empty, but still use
--cherry-mark to discard commits that are already upstream:
* If cherry-mark can determine the commit was "already upstream",
then because of how cherry-mark works this means the upstream commit
message was about the *exact* same set of changes. Thus, the commit
messages can be assumed to be fully interchangeable (and are in fact
likely to be completely identical).
* If a commit does not start empty, but becomes so because its
changes were a subset of those upstream, then while there are clearly
no more changes that need to be applied, the commit messages can be
expected to differ -- either there will be an upstream commit that
included a bunch of other changes too, or the commit we are rebasing
will be spread across multiple upstream commits. If the project has
bad commit message hygiene (because e.g. they are using atrocious code
review tools like GitHub that don't allow you to review them), there's
a reasonable likelihood that the upstream version only talks about the
other changes in the commit instead of the fix we are rebasing. If
so, there may be useful information in this commit message that we
want to copy somewhere.
It's a somewhat rare usecase involving projects that generally aren't
careful with commit messages but which has some people working on it
who are careful. But even for these projects, the odds that a commit
message will just get tossed anyway seem fairly high, so I will not
strongly defend this choice; it just seemed like "--keep-empty" or
"--empty=keep" should mean "keep empty", not "sometimes keep empty".
So, if it'd be nice if we could come up with clear wording if we are
going to implement other possibilities.
Anyway, am I correctly understanding that you would like to see the
following matrix of options?
* Drop commits that either start or become empty [the current --empty=drop]
* Pause and ask users whether they want commits that either start or
become empty [the current --empty=ask]
* Keep commits that start empty, but drop ones that become empty [a
new option]
and possibly drop the current --empty=keep behavior?
> > Note that traditionally, am-based rebases have always dropped commits
> > that either started or became empty, while interactive-based rebases
> > have defaulted to ask (and provided an option to keep commits that
> > started empty). This difference made sense since users of an am-based
> > rebase just wanted to quickly batch apply a sequence of commits, while
> > users editing a todo list will likely want the chance to interact and
> > handle unusual cases on a case-by-case basis.
>
> I don't see why it makes sense to drop an empty commit that I've made
> just because it is being rebased.
Are you arguing that keeping commits that started empty should
actually be the default, i.e. that all three rebase backends have
traditionally had the wrong default?
> I'm pretty sure the behavor of the
> am-based rebase is a function of `git am` not being able to create empty
> commits.
That may be, but it's not quite clear. Junio kind of commented on
this in https://lore.kernel.org/git/xmqqfu1fswdh.fsf@gitster-ct.c.googlers.com/.
His comments that "lack of --keep-empty on the 'am' side is probably a
bug that wants to be fixed" might imply that he only sees it as a
useful option. If the behavior of the am-based rebase dropping
commits which started empty was solely a function of git-am not being
able to create empty commits, then the correct fix would be to make
keeping the commits which started empty as the default or maybe only
behavior rather than just making it an option.
I'd actually be open to changing the default here. We have a
precedent in doing so with commit b00bf1c9a8dd ("git-rebase: make
--allow-empty-message the default", 2018-06-27) where we stated that
really rare edge cases (which I think both empty commit messages and
commits which start empty qualify as) probably have the wrong default
in rebase. And changing the default would yield a much easier to
understand matrix of options:
* Drop commits that become empty (modification to --empty=drop in
that it keeps commits which started empty)
* Pause and ask users if commits become empty (modification to
--empty=ask in that it keeps commits which started empty)
* Keep commits that become empty, in addition to those that started
empty (--empty=keep).
> > However, not all rebases
> > using the interactive machinery are explicitly interactive anymore. In
> > particular --merge was always meant to behave more like --am: just
> > rebase a batch of commits without popping up a todo list.
> >
> > If the --empty flag is not specified, pick defaults as follows:
> > explicitly interactive: ask
> > --exec: keep (exec is about checking existing commits, and often
> > used without actually changing the base. Thus the
> > expectation is that the user doesn't necessarily want
> > anything to change; they just want to test).
> > otherwise: drop
>
> I'm not sure I like changing the behavior based on --exec, I see what
> you're getting at but it has the potential to be confusing. What if I
> want to rearrange the commits without changing the base - why must I
> specify --empty=keep there but not if I add --exec to the command line?
This was probably poorly explained and also poorly implemented. Let
me try from a slightly different angle, and come to a slightly
different result than I did previously:
Quoting from Junio in b00bf1c9a8dd ("git-rebase: make
--allow-empty-message the default", 2018-06-27),
'"am" based rebase is solely to transplant an existing history and
want to stop much less than "interactive" one whose purpose is to
polish a series before making it publishable, and asking for
confirmation ("this has become empty--do you want to drop it?") is
more appropriate from the workflow point of view'
I would modify Junio's wording to expand "am based rebase" to say that
if the user hasn't requested an interactive rebase or otherwise
explicitly signalled that they want some measure of interactivity
(e.g. specified --empty=ask), then we should not stop and ask how to
handle commits which started OR became empty. In other worse, it's
not just the am-based rebase that is about transplanting commits but
any rebase where interactivity isn't requested. I think the
traditional --keep-empty is quite broken in this regard since it does
stop and ask with commits that become empty. rebase -m, rebase
--exec, etc. should not stop when a commit becomes empty, there should
be some default it just uses unless the user also adds
--interactive/-i/--empty=ask.
But, if we take your above suggestion to make keeping the commits
which started empty not only the default but remove it from the
options, then this becomes much easier. The choices become (kind of
repeated from above):
[Keep the commits that started empty in all three cases, otherwise:]
* Drop commits that become empty (--empty=drop)
* Pause and ask users if commits become empty (--empty=ask)
* Keep commits that become empty (--empty=keep)
and the defaults are simply:
--interactive/-i: --empty=ask
everything else: --empty=drop
(And as a bonus, in the thread I've referenced twice above Junio
mentioned -- in respect to commits that become empty -- that he thinks
these are the right defaults. He didn't comment on the behavior of
commits that start empty, which seems to be your primary concern.)
> > Also, this commit makes --keep-empty just imply --empty=keep, and hides
> > it from help so that we aren't confusing users with different ways to do
> > the same thing. (I could have added a --drop-empty flag, but then that
> > invites users to specify both --keep-empty and --drop-empty and we have
> > to add sanity checking around that; it seems cleaner to have a single
> > multi-valued option.) This actually fixes --keep-empty too; previously,
> > it only meant to sometimes keep empty commits, in particular commits
> > which started empty would be kept. But it would still error out and ask
> > the user what to do with commits that became empty. Now it keeps empty
> > commits, as instructed.
>
> It certainly changes the behavior of --keep-empty but I'm not sure it
> "fixes" it. If I have some empty commits I want to keep as placeholders
> then that's different from wanting to keep commits that become empty
> because their changes are upstream but --cherry-mark didn't detect them.
>
> In summary I'm in favor of making it easier to drop commits that become
> empty but not tying that to the handling of commits that are empty
> before they are rebased.
Sounds like my modified proposal above may be in line with your tastes?
> I'm also not happy that the deprecation of --keep-empty suddenly makes
> --no-keep-empty an error.
We could translate that to --empty=drop; does that sound reasonable?
[...]
> > @@ -466,7 +494,10 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)
> > struct option options[] = {
> > OPT_NEGBIT(0, "ff", &opts.flags, N_("allow fast-forward"),
> > REBASE_FORCE),
> > - OPT_BOOL(0, "keep-empty", &opts.keep_empty, N_("keep empty commits")),
> > + { OPTION_CALLBACK, 'k', "keep-empty", &options, NULL,
> > + N_("(DEPRECATED) keep empty commits"),
> > + PARSE_OPT_NOARG | PARSE_OPT_NONEG | PARSE_OPT_HIDDEN,
>
> It is all very well deprecating --keep-empty but suddenly making
> '--no-keep-empty' an error goes beyond deprecation. Also I'm not sure
> it's worth changing these options as I think the only user is
> git-rebase--preserve-merges.sh
Side track: Since git-rebase--preserve-merges.sh is deprecated and we
want to get rid of it, and rebase-merges exists and is a better
implementation of the original idea, can we just translate rebase -p
into rebase -r and delete git-rebase--preserve-merges.sh? (With a few
wrinkles, such as telling users in the middle of an existing
preserve-merges-rebase that they either need to use an old version of
git to continue their rebase or else abort the rebase?)
> Best Wishes
>
> Phillip
>
[...]
> This function is used by cherry-pick/revert not rebase do we need to
> change it?
Possibly not; I can take a look. I had quite a difficult time trying
to figure out how to make sure that --empty options were saved and
restored across rebase --continue; it's not at all unlikely that I
modified more than I needed to "for consistency".
[...]
> > @@ -3033,9 +3064,15 @@ static int save_opts(struct replay_opts *opts)
>
> again this is for cherry-pick/revert
Okay, will double check this too.
[...]
^ permalink raw reply [flat|nested] 161+ messages in thread
* Re: [PATCH v3 10/15] rebase: add an --am option
2020-01-07 14:43 ` Phillip Wood
@ 2020-01-07 19:26 ` Elijah Newren
2020-01-07 20:11 ` Junio C Hamano
0 siblings, 1 reply; 161+ messages in thread
From: Elijah Newren @ 2020-01-07 19:26 UTC (permalink / raw)
To: Phillip Wood
Cc: Elijah Newren via GitGitGadget, Git Mailing List,
Johannes Schindelin, Denton Liu, Junio C Hamano, Pavel Roskin,
Alban Gruin, SZEDER Gábor
Hi Phillip,
On Tue, Jan 7, 2020 at 6:43 AM Phillip Wood <phillip.wood123@gmail.com> wrote:
>
> Hi Elijah
>
> On 24/12/2019 19:54, Elijah Newren via GitGitGadget wrote:
> > From: Elijah Newren <newren@gmail.com>
> >
> > Currently, this option doesn't do anything except error out if any
> > options requiring the interactive-backend are also passed. However,
> > when we make the default backend configurable later in this series, this
> > flag will provide a way to override the config setting.
>
> I wonder if we really want to add a new option that we know we don't
> want to keep in the long term. Could we not just default to the
> interactive backend and fallback to the am backend if the user passes
> any of the am-based options on the commandline? i.e. reverse the current
> situation of defaulting to the am backend and selecting the interactive
> backend when the user passes certain options.
Good question. For context, this option served a few purposes:
* Providing an escape hatch during the transition in case the merge
backend doesn't handle everything the am one does in our first
attempts (i.e. similar to how we allowed using the scripted rebase
even after providing a built-in one).
* Makes the documentation much more concise and clear (being able to
specify that various flags "imply --am" is simple, whereas removing
the flag makes me feel I need the full description of --am in each of
the few places a flag that would have implied it).
* Provides an easy way to make the plethora of am-specific rebase
tests use the am-backend.
However, I thought of this option before Junio suggested a
rebase.backend config setting, so we could just rely on that instead.
Thus, getting rid of the "--am" flag in detail would mean:
* I need to redo the test changes in this series to use "-c
rebase.backend=am" instead of "--am"
* It will be slightly harder for users to use the escape hatch in
one-off cases during the transition
* We need to figure out the right way to reword the documentation
The first two are pretty minor, so that probably means I just need to
come up with some good wording for the documentation (suggestions
welcome...)
Elijah
^ permalink raw reply [flat|nested] 161+ messages in thread
* Re: [PATCH v3 10/15] rebase: add an --am option
2020-01-07 19:26 ` Elijah Newren
@ 2020-01-07 20:11 ` Junio C Hamano
2020-01-08 14:32 ` Phillip Wood
0 siblings, 1 reply; 161+ messages in thread
From: Junio C Hamano @ 2020-01-07 20:11 UTC (permalink / raw)
To: Elijah Newren
Cc: Phillip Wood, Elijah Newren via GitGitGadget, Git Mailing List,
Johannes Schindelin, Denton Liu, Pavel Roskin, Alban Gruin,
SZEDER Gábor
Elijah Newren <newren@gmail.com> writes:
> However, I thought of this option before Junio suggested a
> rebase.backend config setting, so we could just rely on that instead.
> Thus, getting rid of the "--am" flag in detail would mean:
> * I need to redo the test changes in this series to use "-c
> rebase.backend=am" instead of "--am"
> * It will be slightly harder for users to use the escape hatch in
> one-off cases during the transition
> * We need to figure out the right way to reword the documentation
>
> The first two are pretty minor, so that probably means I just need to
> come up with some good wording for the documentation (suggestions
> welcome...)
It probably is a good idea to keep --am (and possibly --[no-]am) as
long as rebase.backend option exists. A configuration variable that
changes behaviour without allowing it to be overridden from the
command line is not a good idea.
^ permalink raw reply [flat|nested] 161+ messages in thread
* Re: [PATCH v3 01/15] rebase: extend the options for handling of empty commits
2020-01-07 19:15 ` Elijah Newren
@ 2020-01-08 14:27 ` Phillip Wood
2020-01-09 21:32 ` Johannes Schindelin
0 siblings, 1 reply; 161+ messages in thread
From: Phillip Wood @ 2020-01-08 14:27 UTC (permalink / raw)
To: Elijah Newren
Cc: Elijah Newren via GitGitGadget, Git Mailing List,
Johannes Schindelin, Phillip Wood, Denton Liu, Junio C Hamano,
Pavel Roskin, Alban Gruin, SZEDER Gábor
Hi Elijah
Thanks for such a detailed reply, I'll try and do it justice.
TLDR I like your new proposal
On 07/01/2020 19:15, Elijah Newren wrote:
> Hi Phillip,
>
> On Tue, Jan 7, 2020 at 6:37 AM Phillip Wood <phillip.wood123@gmail.com> wrote:
>>
>> Hi Elijah
>>
>> Thanks for working on this series, I think making the sequencer the
>> default backend is a good idea. I have a few reservations about this
>> path though...
>
> Thanks for the feedback. You've provided some good food for thought.
> While responding, I came up with an alternate proposal below which I
> think may be more in line with what you want. Let me know...
>
>>
>> On 24/12/2019 19:54, Elijah Newren via GitGitGadget wrote:
>>> From: Elijah Newren <newren@gmail.com>
>>>
>>> Extend the interactive machinery with the ability to handle the full
>>> spread of options for how to handle commits that either start or become
>>> empty (by "become empty" I mean the changes in a commit are a subset of
>>> changes that exist upstream, so the net effect of applying the commit is
>>> no changes). Introduce a new command line flag for selecting the
>>> desired behavior:
>>> --empty={drop,keep,ask}
>>> with the definitions:
>>> drop: drop empty commits
>>> keep: keep empty commits
>>> ask: provide the user a chance to interact and pick what to do with
>>> empty commits on a case-by-case basis
>>
>> I think we want to distinguish between commits that are empty before
>> rebasing and those that become empty when they are rebased. --keep-empty
>> explicily only applies to commits that are already empty. Cherry-pick
>
> I am open to changing how the empty handling works; I figured it was
> the most likely area people might have feedback about.
>
> However, I do strongly disagree with the "explicit" claim here. It is
> possible that --keep-empty was designed to only apply to commits that
> begin empty, but that is not at all explicit.
Maybe explicit wasn't the best choice of words. I was going off the
implementation which takes pains to only allow and empty commit when the
original was empty. This has been the case since the feature was
introduced in 90e1818f9a ("git-rebase: add keep_empty flag",
2012-04-20). I did some digging in the mail archive this morning and
found [1] which seems to confirm the intention was to preserve existing
empty commits, not keep those that become empty. This fits with the
discussion [2] which lead to this feature. The same patch series that
introduced --keep-empty for rebase introduced the two cherry-pick flags
--allow-empty and --keep-redundant-commits to distinguish between the
two cases and used `cherry-pick --allow-empty` to implement `rebase
--keep-empty` (and failed to provide a way to continue after stopping
for a conflict resolution)
[1] https://lore.kernel.org/git/5006614E.8090601@viscovery.net/
[2]
https://lore.kernel.org/git/20120323185205.GA11916@hmsreliant.think-freely.org/
> The only place in the
> documentation I could find that mentions the distinction at all was in
> the "Behavioral Differences" section, which was written by me as part
> of commit 0661e49aeb84 ("git-rebase.txt: document behavioral
> differences between modes", 2015-06-27). Quoting from that commit
> message:
>
> "(In fact, it's not clear -- to me at least -- that these differences
> were even desirable or intentional.) Document these differences."
>
> I cannot find anywhere else making a distinction in either the
> documentation or the commit messages or even any code comments, which
> has left me wondering whether the distinction was intentional at all.
> Things like commit b00bf1c9a8dd ("git-rebase: make
> --allow-empty-message the default", 2018-06-27) give heavy precedence
> to the assumption that git-rebase is often more happenstance than
> design when it comes to edge cases like these.
>
>> has distinct options for those two cases. If I've explicitly created an
>> empty commit then I want to keep it but I don't want to keep commits
>> that become empty because the changes they contain are already upstream.
>>
>> If we want an option that keeps commits that become empty (Off hand I
>> don't know why we would though) we should consider if that option should
>> disable --cherry-mark when we create the todo list so that it keeps all
>> commits that become empty when they're rebased.
>
> To answer the particular reason I have seen why folks might want to
> keep both commits that start empty and become empty, but still use
> --cherry-mark to discard commits that are already upstream:
> * If cherry-mark can determine the commit was "already upstream",
> then because of how cherry-mark works this means the upstream commit
> message was about the *exact* same set of changes. Thus, the commit
> messages can be assumed to be fully interchangeable (and are in fact
> likely to be completely identical).
> * If a commit does not start empty, but becomes so because its
> changes were a subset of those upstream, then while there are clearly
> no more changes that need to be applied, the commit messages can be
> expected to differ -- either there will be an upstream commit that
> included a bunch of other changes too, or the commit we are rebasing
> will be spread across multiple upstream commits. If the project has
> bad commit message hygiene (because e.g. they are using atrocious code
> review tools like GitHub that don't allow you to review them), there's
> a reasonable likelihood that the upstream version only talks about the
> other changes in the commit instead of the fix we are rebasing. If
> so, there may be useful information in this commit message that we
> want to copy somewhere.
That sounds reasonable, (I'm not sure what one would do with the commit
message though as creating an empty commit wont be any use for blame or
someone looking at the commit that introduced the change upstream. One
could use add a note to the upstream commit but then there's the problem
of sharing notes)
> It's a somewhat rare usecase involving projects that generally aren't
> careful with commit messages but which has some people working on it
> who are careful. But even for these projects, the odds that a commit
> message will just get tossed anyway seem fairly high, so I will not
> strongly defend this choice; it just seemed like "--keep-empty" or
> "--empty=keep" should mean "keep empty", not "sometimes keep empty".
> So, if it'd be nice if we could come up with clear wording if we are
> going to implement other possibilities.
>
> Anyway, am I correctly understanding that you would like to see the
> following matrix of options?
> * Drop commits that either start or become empty [the current --empty=drop]
Yes please
> * Pause and ask users whether they want commits that either start or
> become empty [the current --empty=ask]
I'd prefer an option to keep the commits that start empty and ask about
the ones that become empty (just like --keep-empty does now)
> * Keep commits that start empty, but drop ones that become empty [a
> new option]
Yes please
> and possibly drop the current --empty=keep behavior?
Yes as I'm not sure it's that useful, if users want to keep the
information from the message of a commit that becomes empty I'm not sure
just creating an empty commit is going to be much use in practice.
>>> Note that traditionally, am-based rebases have always dropped commits
>>> that either started or became empty, while interactive-based rebases
>>> have defaulted to ask (and provided an option to keep commits that
>>> started empty). This difference made sense since users of an am-based
>>> rebase just wanted to quickly batch apply a sequence of commits, while
>>> users editing a todo list will likely want the chance to interact and
>>> handle unusual cases on a case-by-case basis.
>>
>> I don't see why it makes sense to drop an empty commit that I've made
>> just because it is being rebased.
>
> Are you arguing that keeping commits that started empty should
> actually be the default, i.e. that all three rebase backends have
> traditionally had the wrong default?
Yes, I think rebase should preserve the commits that are being rebased
as far as possible and the current default is probably a historical accident
>> I'm pretty sure the behavor of the
>> am-based rebase is a function of `git am` not being able to create empty
>> commits.
>
> That may be, but it's not quite clear. Junio kind of commented on
> this in https://lore.kernel.org/git/xmqqfu1fswdh.fsf@gitster-ct.c.googlers.com/.
> His comments that "lack of --keep-empty on the 'am' side is probably a
> bug that wants to be fixed" might imply that he only sees it as a
> useful option. If the behavior of the am-based rebase dropping
> commits which started empty was solely a function of git-am not being
> able to create empty commits, then the correct fix would be to make
> keeping the commits which started empty as the default or maybe only
> behavior rather than just making it an option.
Yeah I'm not sure how Junio feels defaulting to keeping commits that
start empty
> I'd actually be open to changing the default here.
I'd be keen to do that but we should try and find out what others think.
> We have a
> precedent in doing so with commit b00bf1c9a8dd ("git-rebase: make
> --allow-empty-message the default", 2018-06-27) where we stated that
> really rare edge cases (which I think both empty commit messages and
> commits which start empty qualify as) probably have the wrong default
> in rebase. And changing the default would yield a much easier to
> understand matrix of options:
>
> * Drop commits that become empty (modification to --empty=drop in
> that it keeps commits which started empty)
> * Pause and ask users if commits become empty (modification to
> --empty=ask in that it keeps commits which started empty)
> * Keep commits that become empty, in addition to those that started
> empty (--empty=keep).
That matrix is easier to understand and covers all the cases I'm
interested in.
>>> However, not all rebases
>>> using the interactive machinery are explicitly interactive anymore. In
>>> particular --merge was always meant to behave more like --am: just
>>> rebase a batch of commits without popping up a todo list.
>>>
>>> If the --empty flag is not specified, pick defaults as follows:
>>> explicitly interactive: ask
>>> --exec: keep (exec is about checking existing commits, and often
>>> used without actually changing the base. Thus the
>>> expectation is that the user doesn't necessarily want
>>> anything to change; they just want to test).
>>> otherwise: drop
>>
>> I'm not sure I like changing the behavior based on --exec, I see what
>> you're getting at but it has the potential to be confusing. What if I
>> want to rearrange the commits without changing the base - why must I
>> specify --empty=keep there but not if I add --exec to the command line?
>
> This was probably poorly explained and also poorly implemented. Let
> me try from a slightly different angle, and come to a slightly
> different result than I did previously:
>
> Quoting from Junio in b00bf1c9a8dd ("git-rebase: make
> --allow-empty-message the default", 2018-06-27),
> '"am" based rebase is solely to transplant an existing history and
> want to stop much less than "interactive" one whose purpose is to
> polish a series before making it publishable, and asking for
> confirmation ("this has become empty--do you want to drop it?") is
> more appropriate from the workflow point of view'
>
> I would modify Junio's wording to expand "am based rebase" to say that
> if the user hasn't requested an interactive rebase or otherwise
> explicitly signalled that they want some measure of interactivity
> (e.g. specified --empty=ask), then we should not stop and ask how to
> handle commits which started OR became empty. In other worse, it's
> not just the am-based rebase that is about transplanting commits but
> any rebase where interactivity isn't requested. I think the
> traditional --keep-empty is quite broken in this regard since it does
> stop and ask with commits that become empty. rebase -m, rebase
> --exec, etc. should not stop when a commit becomes empty, there should
> be some default it just uses unless the user also adds
> --interactive/-i/--empty=ask.
>
> But, if we take your above suggestion to make keeping the commits
> which started empty not only the default but remove it from the
> options, then this becomes much easier. The choices become (kind of
> repeated from above):
>
> [Keep the commits that started empty in all three cases, otherwise:]
> * Drop commits that become empty (--empty=drop)
> * Pause and ask users if commits become empty (--empty=ask)
> * Keep commits that become empty (--empty=keep)
That's nice - I like it
> and the defaults are simply:
> --interactive/-i: --empty=ask
> everything else: --empty=drop
While I see the point about the difference between -i and the other
rebase modes any flavor of rebase can stop at any time due to conflicts
so it can never be fully automatic. Given that commits becoming empty is
a bit of a corner case I'm not sure the complexity of different defaults
is justified but I don't feel that strongly about it.
> (And as a bonus, in the thread I've referenced twice above Junio
> mentioned -- in respect to commits that become empty -- that he thinks
> these are the right defaults. He didn't comment on the behavior of
> commits that start empty, which seems to be your primary concern.)
>>> Also, this commit makes --keep-empty just imply --empty=keep, and hides
>>> it from help so that we aren't confusing users with different ways to do
>>> the same thing. (I could have added a --drop-empty flag, but then that
>>> invites users to specify both --keep-empty and --drop-empty and we have
>>> to add sanity checking around that; it seems cleaner to have a single
>>> multi-valued option.) This actually fixes --keep-empty too; previously,
>>> it only meant to sometimes keep empty commits, in particular commits
>>> which started empty would be kept. But it would still error out and ask
>>> the user what to do with commits that became empty. Now it keeps empty
>>> commits, as instructed.
>>
>> It certainly changes the behavior of --keep-empty but I'm not sure it
>> "fixes" it. If I have some empty commits I want to keep as placeholders
>> then that's different from wanting to keep commits that become empty
>> because their changes are upstream but --cherry-mark didn't detect them.
>>
>> In summary I'm in favor of making it easier to drop commits that become
>> empty but not tying that to the handling of commits that are empty
>> before they are rebased.
>
> Sounds like my modified proposal above may be in line with your tastes?
Yes. Keep commits that start empty and have an option to control what
happens to those that become empty (defaulting to --empty=ask for
interactive rebases at least).
>> I'm also not happy that the deprecation of --keep-empty suddenly makes
>> --no-keep-empty an error.
>
> We could translate that to --empty=drop; does that sound reasonable?
translating it to --empty=ask would keep the behavior the same even if
it sounds a bit odd. I don't think we want to change the behavior of
--[no-]keep-empty while it is deprecated.
>
> [...]
>>> @@ -466,7 +494,10 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)
>>> struct option options[] = {
>>> OPT_NEGBIT(0, "ff", &opts.flags, N_("allow fast-forward"),
>>> REBASE_FORCE),
>>> - OPT_BOOL(0, "keep-empty", &opts.keep_empty, N_("keep empty commits")),
>>> + { OPTION_CALLBACK, 'k', "keep-empty", &options, NULL,
>>> + N_("(DEPRECATED) keep empty commits"),
>>> + PARSE_OPT_NOARG | PARSE_OPT_NONEG | PARSE_OPT_HIDDEN,
>>
>> It is all very well deprecating --keep-empty but suddenly making
>> '--no-keep-empty' an error goes beyond deprecation. Also I'm not sure
>> it's worth changing these options as I think the only user is
>> git-rebase--preserve-merges.sh
>
> Side track: Since git-rebase--preserve-merges.sh is deprecated and we
> want to get rid of it, and rebase-merges exists and is a better
> implementation of the original idea, can we just translate rebase -p
> into rebase -r and delete git-rebase--preserve-merges.sh? (With a few
> wrinkles, such as telling users in the middle of an existing
> preserve-merges-rebase that they either need to use an old version of
> git to continue their rebase or else abort the rebase?)
dscho posted some patches in November or December moving along this
path, I'm not sure what the outcome was. It's a bit complicated by the
different todo list formats between -p and -r but I think the end game
should be to treat -p as -r as you suggest.
>
>> Best Wishes
>>
>> Phillip
>>
> [...]
>> This function is used by cherry-pick/revert not rebase do we need to
>> change it?
>
> Possibly not; I can take a look. I had quite a difficult time trying
> to figure out how to make sure that --empty options were saved and
> restored across rebase --continue; it's not at all unlikely that I
> modified more than I needed to "for consistency".
>
> [...]
>>> @@ -3033,9 +3064,15 @@ static int save_opts(struct replay_opts *opts)
>>
>> again this is for cherry-pick/revert
>
> Okay, will double check this too.
>
> [...]
>
^ permalink raw reply [flat|nested] 161+ messages in thread
* Re: [PATCH v3 10/15] rebase: add an --am option
2020-01-07 20:11 ` Junio C Hamano
@ 2020-01-08 14:32 ` Phillip Wood
2020-01-08 17:18 ` Junio C Hamano
0 siblings, 1 reply; 161+ messages in thread
From: Phillip Wood @ 2020-01-08 14:32 UTC (permalink / raw)
To: Junio C Hamano, Elijah Newren
Cc: Phillip Wood, Elijah Newren via GitGitGadget, Git Mailing List,
Johannes Schindelin, Denton Liu, Pavel Roskin, Alban Gruin,
SZEDER Gábor
On 07/01/2020 20:11, Junio C Hamano wrote:
> Elijah Newren <newren@gmail.com> writes:
>
>> However, I thought of this option before Junio suggested a
>> rebase.backend config setting, so we could just rely on that instead.
>> Thus, getting rid of the "--am" flag in detail would mean:
>> * I need to redo the test changes in this series to use "-c
>> rebase.backend=am" instead of "--am"
>> * It will be slightly harder for users to use the escape hatch in
>> one-off cases during the transition
>> * We need to figure out the right way to reword the documentation
>>
>> The first two are pretty minor, so that probably means I just need to
>> come up with some good wording for the documentation (suggestions
>> welcome...)
>
> It probably is a good idea to keep --am (and possibly --[no-]am) as
> long as rebase.backend option exists. A configuration variable that
> changes behaviour without allowing it to be overridden from the
> command line is not a good idea.
I view this change in the default backend as similar to the rewrite in C
in that it is an implementation detail we're changing that should be
transparent (and beneficial in terms of performance) to the user. There
we provided a configuration variable but not a command line option to
control if it was used or not. I don't see it as something the user will
want to change from rebase to rebase but perhaps I'm missing something.
Best Wishes
Phillip
^ permalink raw reply [flat|nested] 161+ messages in thread
* Re: [PATCH v3 10/15] rebase: add an --am option
2020-01-08 14:32 ` Phillip Wood
@ 2020-01-08 17:18 ` Junio C Hamano
2020-01-08 18:55 ` Phillip Wood
0 siblings, 1 reply; 161+ messages in thread
From: Junio C Hamano @ 2020-01-08 17:18 UTC (permalink / raw)
To: Phillip Wood
Cc: Elijah Newren, Phillip Wood, Elijah Newren via GitGitGadget,
Git Mailing List, Johannes Schindelin, Denton Liu, Pavel Roskin,
Alban Gruin, SZEDER Gábor
Phillip Wood <phillip.wood123@gmail.com> writes:
> I view this change in the default backend as similar to the rewrite in
> C in that it is an implementation detail we're changing that should be
> transparent (and beneficial in terms of performance) to the
> user. There we provided a configuration variable but not a command
> line option to control if it was used or not.
Do you mean things like GIT_TEST_ADD_I_USE_BUILTIN? I think it is
OK to have such an escape mechanism to allow people to opt out of
a new reimplementation until it matures, but I do not offhand recall
doing so with config.
^ permalink raw reply [flat|nested] 161+ messages in thread
* Re: [PATCH v3 10/15] rebase: add an --am option
2020-01-08 17:18 ` Junio C Hamano
@ 2020-01-08 18:55 ` Phillip Wood
0 siblings, 0 replies; 161+ messages in thread
From: Phillip Wood @ 2020-01-08 18:55 UTC (permalink / raw)
To: Junio C Hamano
Cc: Elijah Newren, Phillip Wood, Elijah Newren via GitGitGadget,
Git Mailing List, Johannes Schindelin, Denton Liu, Pavel Roskin,
Alban Gruin, SZEDER Gábor
On 08/01/2020 17:18, Junio C Hamano wrote:
> Phillip Wood <phillip.wood123@gmail.com> writes:
>
>> I view this change in the default backend as similar to the rewrite in
>> C in that it is an implementation detail we're changing that should be
>> transparent (and beneficial in terms of performance) to the
>> user. There we provided a configuration variable but not a command
>> line option to control if it was used or not.
>
> Do you mean things like GIT_TEST_ADD_I_USE_BUILTIN? I think it is
> OK to have such an escape mechanism to allow people to opt out of
> a new reimplementation until it matures, but I do not offhand recall
> doing so with config.
I was thinking of rebase.usebuiltin
Best Wishes
Phillip
^ permalink raw reply [flat|nested] 161+ messages in thread
* Re: [PATCH v3 01/15] rebase: extend the options for handling of empty commits
2020-01-08 14:27 ` Phillip Wood
@ 2020-01-09 21:32 ` Johannes Schindelin
0 siblings, 0 replies; 161+ messages in thread
From: Johannes Schindelin @ 2020-01-09 21:32 UTC (permalink / raw)
To: Phillip Wood
Cc: Elijah Newren, Elijah Newren via GitGitGadget, Git Mailing List,
Phillip Wood, Denton Liu, Junio C Hamano, Pavel Roskin,
Alban Gruin, SZEDER Gábor
Hi Phillip,
On Wed, 8 Jan 2020, Phillip Wood wrote:
> On 07/01/2020 19:15, Elijah Newren wrote:
> >
> > On Tue, Jan 7, 2020 at 6:37 AM Phillip Wood <phillip.wood123@gmail.com>
> > wrote:
> >
> > > On 24/12/2019 19:54, Elijah Newren via GitGitGadget wrote:
> > >
> > [...]
> > > > @@ -466,7 +494,10 @@ int cmd_rebase__interactive(int argc, const char
> > > > **argv, const char *prefix)
> > > > struct option options[] = {
> > > > OPT_NEGBIT(0, "ff", &opts.flags, N_("allow fast-forward"),
> > > > REBASE_FORCE),
> > > > - OPT_BOOL(0, "keep-empty", &opts.keep_empty, N_("keep empty
> > > > commits")),
> > > > + { OPTION_CALLBACK, 'k', "keep-empty", &options, NULL,
> > > > + N_("(DEPRECATED) keep empty commits"),
> > > > + PARSE_OPT_NOARG | PARSE_OPT_NONEG |
> > > > PARSE_OPT_HIDDEN,
> > >
> > > It is all very well deprecating --keep-empty but suddenly making
> > > '--no-keep-empty' an error goes beyond deprecation. Also I'm not sure
> > > it's worth changing these options as I think the only user is
> > > git-rebase--preserve-merges.sh
> >
> > Side track: Since git-rebase--preserve-merges.sh is deprecated and we
> > want to get rid of it, and rebase-merges exists and is a better
> > implementation of the original idea, can we just translate rebase -p
> > into rebase -r and delete git-rebase--preserve-merges.sh? (With a few
> > wrinkles, such as telling users in the middle of an existing
> > preserve-merges-rebase that they either need to use an old version of
> > git to continue their rebase or else abort the rebase?)
>
> dscho posted some patches in November or December moving along this path, I'm
> not sure what the outcome was. It's a bit complicated by the different todo
> list formats between -p and -r but I think the end game should be to treat -p
> as -r as you suggest.
I sent some patches to switch `git svn` over to `rebase -r`, but that was
only in non-interactive mode.
In interactive mode, the todo lists indeed look too different and I think
we'll just need to wait a year or so and then we can just delete
`git-rebase--preserve-merges.sh`.
Ciao,
Dscho
^ permalink raw reply [flat|nested] 161+ messages in thread
* Re: [PATCH v3 15/15] rebase: change the default backend from "am" to "merge"
2019-12-24 19:54 ` [PATCH v3 15/15] rebase: change the default backend from "am" to "merge" Elijah Newren via GitGitGadget
@ 2020-01-10 23:14 ` Jonathan Nieder
2020-01-11 1:16 ` Elijah Newren
0 siblings, 1 reply; 161+ messages in thread
From: Jonathan Nieder @ 2020-01-10 23:14 UTC (permalink / raw)
To: Elijah Newren via GitGitGadget
Cc: git, Johannes.Schindelin, phillip.wood, liu.denton, gitster,
plroskin, alban.gruin, szeder.dev, Elijah Newren
Hi,
Elijah Newren via GitGitGadget wrote:
> The am-backend drops information and thus limits what we can do:
>
> * lack of full tree information from the original commits means we
> cannot do directory rename detection and warn users that they might
> want to move some of their new files that they placed in old
> directories to prevent their becoming orphaned.[1]
> * reduction in context from only having a few lines beyond those
> changed means that when context lines are non-unique we can apply
> patches incorrectly.[2]
> * lack of access to original commits means that conflict marker
> annotation has less information available.
>
> Also, the merge/interactive backend have far more abilities, appear to
> currently have a slight performance advantage[3] and have room for more
> optimizations than the am backend[4] (and work is underway to take
> advantage of some of those possibilities).
>
> [1] https://lore.kernel.org/git/xmqqh8jeh1id.fsf@gitster-ct.c.googlers.com/
> [2] https://lore.kernel.org/git/CABPp-BGiu2nVMQY_t-rnFR5GQUz_ipyEE8oDocKeO+h+t4Mn4A@mail.gmail.com/
> [3] https://public-inbox.org/git/CABPp-BF=ev03WgODk6TMQmuNoatg2kiEe5DR__gJ0OTVqHSnfQ@mail.gmail.com/
> [4] https://lore.kernel.org/git/CABPp-BGh7yW69QwxQb13K0HM38NKmQif3A6C6UULEKYnkEJ5vA@mail.gmail.com/
>
> Signed-off-by: Elijah Newren <newren@gmail.com>
> ---
> Documentation/git-rebase.txt | 2 +-
> builtin/rebase.c | 4 ++--
> t/t5520-pull.sh | 10 ++++++----
> t/t9106-git-svn-commit-diff-clobber.sh | 3 ++-
> 4 files changed, 11 insertions(+), 8 deletions(-)
Thanks for writing this. We finally rolled this out to our internal
population at $DAYJOB and ran into a couple of issues:
1. "git rebase --am" does not invoke the post-commit hook, but "git
rebase --merge" does. Is this behavior change intended?
Noticed because jiri[1] installs a post-commit hook that warns
about commits on detached HEAD, so this change makes rebases more
noisy in repositories that were set up using jiri.
2. GIT_REFLOG_ACTION contains "rebase -i" even though the rebase is
not interactive.
3. In circumstances I haven't pinned down yet, we get the error
message "invalid date format: @@2592000 +0000":
$ git rebase --committer-date-is-author-date --onto branch_K branch_L~1 branch_L
$ git checkout --theirs file
$ git add file
$ git rebase --continue
fatal: invalid date format: @@2592000 +0000
error: could not commit staged changes.
This isn't reproducible without --committer-date-is-author-date.
More context (the test where it happens) is in [2].
4. I suspect the exit status in the "you need to resolve conflicts"
case has changed. With rebase --am, [3] would automatically
invoke rebase --abort when conflicts are present, but with rebase
--merge it does not.
Known?
Thanks,
Jonathan
[1] https://fuchsia.googlesource.com/jiri/+/60436c301224231cb99be41ce937dfc223bee272/project/manifest.go#1347
[2] https://source.chromium.org/chromium/chromium/tools/depot_tools/+/master:tests/git_common_test.py;l=721;drc=6b52dc21e166c46707b4c8eb26c74c70d4f9977e;bpv=1;bpt=0
[3] https://fuchsia.googlesource.com/jiri/+/60436c301224231cb99be41ce937dfc223bee272/project/project.go#1664
^ permalink raw reply [flat|nested] 161+ messages in thread
* Re: [PATCH v3 15/15] rebase: change the default backend from "am" to "merge"
2020-01-10 23:14 ` Jonathan Nieder
@ 2020-01-11 1:16 ` Elijah Newren
2020-01-11 14:41 ` Phillip Wood
0 siblings, 1 reply; 161+ messages in thread
From: Elijah Newren @ 2020-01-11 1:16 UTC (permalink / raw)
To: Jonathan Nieder
Cc: Elijah Newren via GitGitGadget, Git Mailing List,
Johannes Schindelin, Phillip Wood, Denton Liu, Junio C Hamano,
Pavel Roskin, Alban Gruin, SZEDER Gábor
Hi Jonathan,
On Fri, Jan 10, 2020 at 3:14 PM Jonathan Nieder <jrnieder@gmail.com> wrote:
>
> Hi,
>
> Elijah Newren via GitGitGadget wrote:
>
> > The am-backend drops information and thus limits what we can do:
> >
> > * lack of full tree information from the original commits means we
> > cannot do directory rename detection and warn users that they might
> > want to move some of their new files that they placed in old
> > directories to prevent their becoming orphaned.[1]
> > * reduction in context from only having a few lines beyond those
> > changed means that when context lines are non-unique we can apply
> > patches incorrectly.[2]
> > * lack of access to original commits means that conflict marker
> > annotation has less information available.
> >
> > Also, the merge/interactive backend have far more abilities, appear to
> > currently have a slight performance advantage[3] and have room for more
> > optimizations than the am backend[4] (and work is underway to take
> > advantage of some of those possibilities).
> >
> > [1] https://lore.kernel.org/git/xmqqh8jeh1id.fsf@gitster-ct.c.googlers.com/
> > [2] https://lore.kernel.org/git/CABPp-BGiu2nVMQY_t-rnFR5GQUz_ipyEE8oDocKeO+h+t4Mn4A@mail.gmail.com/
> > [3] https://public-inbox.org/git/CABPp-BF=ev03WgODk6TMQmuNoatg2kiEe5DR__gJ0OTVqHSnfQ@mail.gmail.com/
> > [4] https://lore.kernel.org/git/CABPp-BGh7yW69QwxQb13K0HM38NKmQif3A6C6UULEKYnkEJ5vA@mail.gmail.com/
> >
> > Signed-off-by: Elijah Newren <newren@gmail.com>
> > ---
> > Documentation/git-rebase.txt | 2 +-
> > builtin/rebase.c | 4 ++--
> > t/t5520-pull.sh | 10 ++++++----
> > t/t9106-git-svn-commit-diff-clobber.sh | 3 ++-
> > 4 files changed, 11 insertions(+), 8 deletions(-)
>
> Thanks for writing this. We finally rolled this out to our internal
> population at $DAYJOB and ran into a couple of issues:
Cool, thanks for testing it out.
> 1. "git rebase --am" does not invoke the post-commit hook, but "git
> rebase --merge" does. Is this behavior change intended?
>
> Noticed because jiri[1] installs a post-commit hook that warns
> about commits on detached HEAD, so this change makes rebases more
> noisy in repositories that were set up using jiri.
I've never used a post-commit hook or seen one in the wild. Certainly
wasn't intentional, but it's not clear to me if it's wrong or right
either. I don't see why it would make sense to distinguish between
any of git rebase --am/--merge/--interactive, but it isn't too
surprising that by historical accident the two rebase backends which
happened to call git-commit behind the scenes would call a post-commit
hook and the other rebase backend that didn't call git-commit
wouldn't.
But the big question here, is what is correct behavior? Should rebase
call the post-commit hook, or should it skip it? I haven't any clue
what the answer to that is.
> 2. GIT_REFLOG_ACTION contains "rebase -i" even though the rebase is
> not interactive.
Yep, as does --keep, --exec, --rebase-merges, etc. There are lots of
rebases which use the interactive machinery even if they aren't
explicitly interactive. I've never seen the "-i" in the reflog
message defined, but clearly it has always been used whenever the
interactive machinery was in play regardless of whether the rebase was
interactive. In that regard, I figured that --merge fit in rather
nicely. (And I noted the fact that reflog messages were different
between the backends among the "BEHAVIORAL DIFFERENCES" section of
git-rebase.txt). But if others think we should just drop the -i (much
as we did for the bash prompt), I'd be happy with that too. If we go
that route, I think I'd rather drop the -i in the reflog for all
rebases, not just the
using-the-interactive-machinery-but-not-explicitly-interactive ones.
> 3. In circumstances I haven't pinned down yet, we get the error
> message "invalid date format: @@2592000 +0000":
>
> $ git rebase --committer-date-is-author-date --onto branch_K branch_L~1 branch_L
> $ git checkout --theirs file
> $ git add file
> $ git rebase --continue
> fatal: invalid date format: @@2592000 +0000
> error: could not commit staged changes.
>
> This isn't reproducible without --committer-date-is-author-date.
> More context (the test where it happens) is in [2].
Interesting. Do you happen to know if this started happening with
ra/rebase-i-more-options, or did it just become an issue with
en/rebase-backend? I looked around at the link you provided and feel
a bit confused; I'm not sure which test does this or how I'd
reproduce.
> 4. I suspect the exit status in the "you need to resolve conflicts"
> case has changed. With rebase --am, [3] would automatically
> invoke rebase --abort when conflicts are present, but with rebase
> --merge it does not.
>
> Known?
Nope, but I would certainly hope that "you need to resolve conflicts"
would result in a non-zero exit status. If it doesn't, that sounds
like a bug in the interactive backend that we need to fix. I'll dig
in.
Thanks for the reports!
Elijah
^ permalink raw reply [flat|nested] 161+ messages in thread
* Re: [PATCH v3 15/15] rebase: change the default backend from "am" to "merge"
2020-01-11 1:16 ` Elijah Newren
@ 2020-01-11 14:41 ` Phillip Wood
2020-01-12 17:59 ` Johannes Schindelin
` (2 more replies)
0 siblings, 3 replies; 161+ messages in thread
From: Phillip Wood @ 2020-01-11 14:41 UTC (permalink / raw)
To: Elijah Newren, Jonathan Nieder
Cc: Elijah Newren via GitGitGadget, Git Mailing List,
Johannes Schindelin, Phillip Wood, Denton Liu, Junio C Hamano,
Pavel Roskin, Alban Gruin, SZEDER Gábor
Hi Jonathan & Elijah
On 11/01/2020 01:16, Elijah Newren wrote:
> Hi Jonathan,
>
> On Fri, Jan 10, 2020 at 3:14 PM Jonathan Nieder <jrnieder@gmail.com> wrote:
>>
>> Hi,
>>
>> Elijah Newren via GitGitGadget wrote:
>>
>>> The am-backend drops information and thus limits what we can do:
>>>
>>> * lack of full tree information from the original commits means we
>>> cannot do directory rename detection and warn users that they might
>>> want to move some of their new files that they placed in old
>>> directories to prevent their becoming orphaned.[1]
>>> * reduction in context from only having a few lines beyond those
>>> changed means that when context lines are non-unique we can apply
>>> patches incorrectly.[2]
>>> * lack of access to original commits means that conflict marker
>>> annotation has less information available.
>>>
>>> Also, the merge/interactive backend have far more abilities, appear to
>>> currently have a slight performance advantage[3] and have room for more
>>> optimizations than the am backend[4] (and work is underway to take
>>> advantage of some of those possibilities).
>>>
>>> [1] https://lore.kernel.org/git/xmqqh8jeh1id.fsf@gitster-ct.c.googlers.com/
>>> [2] https://lore.kernel.org/git/CABPp-BGiu2nVMQY_t-rnFR5GQUz_ipyEE8oDocKeO+h+t4Mn4A@mail.gmail.com/
>>> [3] https://public-inbox.org/git/CABPp-BF=ev03WgODk6TMQmuNoatg2kiEe5DR__gJ0OTVqHSnfQ@mail.gmail.com/
>>> [4] https://lore.kernel.org/git/CABPp-BGh7yW69QwxQb13K0HM38NKmQif3A6C6UULEKYnkEJ5vA@mail.gmail.com/
>>>
>>> Signed-off-by: Elijah Newren <newren@gmail.com>
>>> ---
>>> Documentation/git-rebase.txt | 2 +-
>>> builtin/rebase.c | 4 ++--
>>> t/t5520-pull.sh | 10 ++++++----
>>> t/t9106-git-svn-commit-diff-clobber.sh | 3 ++-
>>> 4 files changed, 11 insertions(+), 8 deletions(-)
>>
>> Thanks for writing this. We finally rolled this out to our internal
>> population at $DAYJOB and ran into a couple of issues:
>
> Cool, thanks for testing it out.
>
>> 1. "git rebase --am" does not invoke the post-commit hook, but "git
>> rebase --merge" does. Is this behavior change intended?
>>
>> Noticed because jiri[1] installs a post-commit hook that warns
>> about commits on detached HEAD, so this change makes rebases more
>> noisy in repositories that were set up using jiri.
Perhaps that hook could learn not to warn if a branch is being rebased?
git could be more helpful there by having a porcelain option to status
that prints the branch name if we're rebasing (`git worktree --list`
shows the branch correctly when it's being rebased but does not (yet - I
have a patch to do it) mark the current worktree so isn't very helpful.)
> I've never used a post-commit hook or seen one in the wild. Certainly
> wasn't intentional, but it's not clear to me if it's wrong or right
> either. I don't see why it would make sense to distinguish between
> any of git rebase --am/--merge/--interactive, but it isn't too
> surprising that by historical accident the two rebase backends which
> happened to call git-commit behind the scenes would call a post-commit
> hook and the other rebase backend that didn't call git-commit
> wouldn't.
Looking through the history the am based rebase has never run the
post-commit hook as am has its own set of hooks and the scripted version
used commit-tree. The merge based rebase ran `git commit` which ran the
post commit hook. The interactive rebase ran the hook until and I broke
it in a356ee4659b ("sequencer: try to commit without forking 'git
commit'", 2017-11-24) and after I fixed it in 4627bc777e ("sequencer:
run post-commit hook", 2019-10-15). As it was broken for two years with
no one noticing it can't be that popular.
> But the big question here, is what is correct behavior? Should rebase
> call the post-commit hook, or should it skip it? I haven't any clue
> what the answer to that is.
It's creating a new commit so I lean towards thinking it should run the
post-commit hook. As an example I have a post-commit hook that prints a
warning if a commit is created on a branch that is being rewritten by
one of my scripts in another worktree. There are pre-commit and
pre-rebase hooks to try and prevent that, but the warning is there as a
last resort if those hooks are by-passed.
>> 2. GIT_REFLOG_ACTION contains "rebase -i" even though the rebase is
>> not interactive.
If this is important to people I think it should be easy enough to set
GIT_REFLOG_ACTION to the appropriate string in builtin/rebase.c (so long
as it hasn't already been set by the user) rather than relying on
sequencer.c to do it.
> Yep, as does --keep, --exec, --rebase-merges, etc. There are lots of
> rebases which use the interactive machinery even if they aren't
> explicitly interactive. I've never seen the "-i" in the reflog
> message defined, but clearly it has always been used whenever the
> interactive machinery was in play regardless of whether the rebase was
> interactive. In that regard, I figured that --merge fit in rather
> nicely. (And I noted the fact that reflog messages were different
> between the backends among the "BEHAVIORAL DIFFERENCES" section of
> git-rebase.txt). But if others think we should just drop the -i (much
> as we did for the bash prompt), I'd be happy with that too. If we go
> that route, I think I'd rather drop the -i in the reflog for all
> rebases, not just the
> using-the-interactive-machinery-but-not-explicitly-interactive ones.
>
>> 3. In circumstances I haven't pinned down yet, we get the error
>> message "invalid date format: @@2592000 +0000":
>>
>> $ git rebase --committer-date-is-author-date --onto branch_K branch_L~1 branch_L
>> $ git checkout --theirs file
>> $ git add file
>> $ git rebase --continue
>> fatal: invalid date format: @@2592000 +0000
>> error: could not commit staged changes.
>>
>> This isn't reproducible without --committer-date-is-author-date.
>> More context (the test where it happens) is in [2].
>
> Interesting. Do you happen to know if this started happening with
> ra/rebase-i-more-options, or did it just become an issue with
> en/rebase-backend? I looked around at the link you provided and feel
> a bit confused; I'm not sure which test does this or how I'd
> reproduce.
I'm confused by the test as well. As ra/rebase-i-more-options only
touched the sequencer then any bugs would only show up in this test
(which runs a non-interactive rebase) once en/rbease-backend switched to
that backend. It seems likely that ra/rebase-i-more-options is to blame.
Jonathan - do you happen to know if your users create empty commits at
all? and if so what do they expect rebase to do with them (and any that
become empty when they are rebased) - cf
https://lore.kernel.org/git/<CABPp-BEH=9qejeqysHYE+AJ+JPaBympZizq-bx_OjArYFa4xUQ@mail.gmail.com>
Best Wishes
Phillip
>> 4. I suspect the exit status in the "you need to resolve conflicts"
>> case has changed. With rebase --am, [3] would automatically
>> invoke rebase --abort when conflicts are present, but with rebase
>> --merge it does not.
>>
>> Known?
>
> Nope, but I would certainly hope that "you need to resolve conflicts"
> would result in a non-zero exit status. If it doesn't, that sounds
> like a bug in the interactive backend that we need to fix. I'll dig
> in.
>
>
>
> Thanks for the reports!
> Elijah
>
^ permalink raw reply [flat|nested] 161+ messages in thread
* Re: [PATCH v3 15/15] rebase: change the default backend from "am" to "merge"
2020-01-11 14:41 ` Phillip Wood
@ 2020-01-12 17:59 ` Johannes Schindelin
2020-01-16 6:32 ` Elijah Newren
2020-01-12 21:23 ` Junio C Hamano
2020-01-15 19:50 ` Jonathan Nieder
2 siblings, 1 reply; 161+ messages in thread
From: Johannes Schindelin @ 2020-01-12 17:59 UTC (permalink / raw)
To: Phillip Wood
Cc: Elijah Newren, Jonathan Nieder, Elijah Newren via GitGitGadget,
Git Mailing List, Denton Liu, Junio C Hamano, Pavel Roskin,
Alban Gruin, SZEDER Gábor
Hi,
On Sat, 11 Jan 2020, Phillip Wood wrote:
> On 11/01/2020 01:16, Elijah Newren wrote:
> >
> > On Fri, Jan 10, 2020 at 3:14 PM Jonathan Nieder <jrnieder@gmail.com> wrote:
> > >
> > > Elijah Newren via GitGitGadget wrote:
> > >
> > > 1. "git rebase --am" does not invoke the post-commit hook, but "git
> > > rebase --merge" does. Is this behavior change intended?
> > >
> > > Noticed because jiri[1] installs a post-commit hook that warns
> > > about commits on detached HEAD, so this change makes rebases more
> > > noisy in repositories that were set up using jiri.
>
> Perhaps that hook could learn not to warn if a branch is being rebased?
> git could be more helpful there by having a porcelain option to status
> that prints the branch name if we're rebasing (`git worktree --list`
> shows the branch correctly when it's being rebased but does not (yet - I
> have a patch to do it) mark the current worktree so isn't very helpful.)
>
> > I've never used a post-commit hook or seen one in the wild. Certainly
> > wasn't intentional, but it's not clear to me if it's wrong or right
> > either. I don't see why it would make sense to distinguish between
> > any of git rebase --am/--merge/--interactive, but it isn't too
> > surprising that by historical accident the two rebase backends which
> > happened to call git-commit behind the scenes would call a post-commit
> > hook and the other rebase backend that didn't call git-commit
> > wouldn't.
>
> Looking through the history the am based rebase has never run the post-commit
> hook as am has its own set of hooks and the scripted version used commit-tree.
> The merge based rebase ran `git commit` which ran the post commit hook. The
> interactive rebase ran the hook until and I broke it in a356ee4659b
> ("sequencer: try to commit without forking 'git commit'", 2017-11-24) and
> after I fixed it in 4627bc777e ("sequencer: run post-commit hook",
> 2019-10-15). As it was broken for two years with no one noticing it can't be
> that popular.
Maybe a crazy idea, but maybe not: how about running the `post-commit`
hook _only_ if `--merge` was specified explicitly, and in that case (and
guarded behind a check verifying that the `post-commit` hook _actually_
exists _and_ is executable) warn the user that this hook won't be run in
future versions?
To make things better for users who actually want to run that hook during
rebases, we could introduce a config option, say,
`rebase.runPostCommitHook` that is a tri-state (`true`, `false`,
`onlyForDashDashMerge`, at first defaulting to the last, eventually to
`false`).
Crazy? Or helpful?
> > But the big question here, is what is correct behavior? Should rebase
> > call the post-commit hook, or should it skip it? I haven't any clue
> > what the answer to that is.
>
> It's creating a new commit so I lean towards thinking it should run the
> post-commit hook. As an example I have a post-commit hook that prints a
> warning if a commit is created on a branch that is being rewritten by
> one of my scripts in another worktree. There are pre-commit and
> pre-rebase hooks to try and prevent that, but the warning is there as a
> last resort if those hooks are by-passed.
I guess you're right, it is quite surprising that the `post-commit` hook
is _not_ run for `--am` rebases even though commits are created.
> > > 2. GIT_REFLOG_ACTION contains "rebase -i" even though the rebase is
> > > not interactive.
>
> If this is important to people I think it should be easy enough to set
> GIT_REFLOG_ACTION to the appropriate string in builtin/rebase.c (so long
> as it hasn't already been set by the user) rather than relying on
> sequencer.c to do it.
I agree (but won't have time to implement it, so maybe I should shut up
already...)
> > Yep, as does --keep, --exec, --rebase-merges, etc. There are lots of
> > rebases which use the interactive machinery even if they aren't
> > explicitly interactive. I've never seen the "-i" in the reflog
> > message defined, but clearly it has always been used whenever the
> > interactive machinery was in play regardless of whether the rebase was
> > interactive. In that regard, I figured that --merge fit in rather
> > nicely. (And I noted the fact that reflog messages were different
> > between the backends among the "BEHAVIORAL DIFFERENCES" section of
> > git-rebase.txt). But if others think we should just drop the -i (much
> > as we did for the bash prompt), I'd be happy with that too. If we go
> > that route, I think I'd rather drop the -i in the reflog for all
> > rebases, not just the
> > using-the-interactive-machinery-but-not-explicitly-interactive ones.
> >
> > > 3. In circumstances I haven't pinned down yet, we get the error
> > > message "invalid date format: @@2592000 +0000":
> > >
> > > $ git rebase --committer-date-is-author-date --onto branch_K
> > > branch_L~1 branch_L
> > > $ git checkout --theirs file
> > > $ git add file
> > > $ git rebase --continue
> > > fatal: invalid date format: @@2592000 +0000
> > > error: could not commit staged changes.
> > >
> > > This isn't reproducible without --committer-date-is-author-date.
> > > More context (the test where it happens) is in [2].
> >
> > Interesting. Do you happen to know if this started happening with
> > ra/rebase-i-more-options, or did it just become an issue with
> > en/rebase-backend? I looked around at the link you provided and feel
> > a bit confused; I'm not sure which test does this or how I'd
> > reproduce.
>
> I'm confused by the test as well. As ra/rebase-i-more-options only touched the
> sequencer then any bugs would only show up in this test (which runs a
> non-interactive rebase) once en/rbease-backend switched to that backend. It
> seems likely that ra/rebase-i-more-options is to blame.
>
> Jonathan - do you happen to know if your users create empty commits at all?
> and if so what do they expect rebase to do with them (and any that become
> empty when they are rebased) - cf
> https://lore.kernel.org/git/<CABPp-BEH=9qejeqysHYE+AJ+JPaBympZizq-bx_OjArYFa4xUQ@mail.gmail.com>
The double `@` looks very funny. I would be interested in seeing an MCVE.
> > > 4. I suspect the exit status in the "you need to resolve conflicts"
> > > case has changed. With rebase --am, [3] would automatically
> > > invoke rebase --abort when conflicts are present, but with rebase
> > > --merge it does not.
> > >
> > > Known?
> >
> > Nope, but I would certainly hope that "you need to resolve conflicts"
> > would result in a non-zero exit status. If it doesn't, that sounds
> > like a bug in the interactive backend that we need to fix. I'll dig
> > in.
Yes, exiting with status 0 would be a major bug, and I think it might even
be a bug that was introduced by me when I re-implemented the core loop of
the interactive rebase in C.
But to me it sounds as if 4. is not so about the exit code but about
aborting immediately. I do not recall seeing --am rebases to abort,
though, but to exit with error (and I saw the same behavior in interactive
rebases).
We will need to see a reduced concrete example (preferably as a new test
case) of the described behavior.
Ciao,
Dscho
^ permalink raw reply [flat|nested] 161+ messages in thread
* Re: [PATCH v3 15/15] rebase: change the default backend from "am" to "merge"
2020-01-11 14:41 ` Phillip Wood
2020-01-12 17:59 ` Johannes Schindelin
@ 2020-01-12 21:23 ` Junio C Hamano
2020-01-15 19:50 ` Jonathan Nieder
2 siblings, 0 replies; 161+ messages in thread
From: Junio C Hamano @ 2020-01-12 21:23 UTC (permalink / raw)
To: Phillip Wood
Cc: Elijah Newren, Jonathan Nieder, Elijah Newren via GitGitGadget,
Git Mailing List, Johannes Schindelin, Phillip Wood, Denton Liu,
Pavel Roskin, Alban Gruin, SZEDER Gábor
Phillip Wood <phillip.wood123@gmail.com> writes:
> Looking through the history the am based rebase has never run the
> post-commit hook as am has its own set of hooks and the scripted
> version used commit-tree.
>
>> But the big question here, is what is correct behavior? Should rebase
>> call the post-commit hook, or should it skip it? I haven't any clue
>> what the answer to that is.
>
> It's creating a new commit so I lean towards thinking it should run
> the post-commit hook. As an example I have a post-commit hook that
The reason why "am" did not run "post-commit" is because
"post-commit" was (originally) meant to be for cases where the end
user types "git commit", IOW, the notion of "since we are creating a
new commit object, let's run the post-commit hook" used to be
totally misguided way of thinking. The hook was "the user _ran_
'commit', so post-commit is run immediately afterwards".
These days, I think most of our tools subscribe to the newer "a
commit got created--have hook the chance to report it" idea, simply
because the original position is untenable---"git merge" may not
want to run the post-commit hook according to the initial design,
and it is fine to make it not to run it when clean automerge was
recorded, but sometimes the end users have to type "git commit" to
conclude a conflicted merge.
So from that point of view, I do not think it is too bad to bring
"am" into the new ear and start running post-commit from it.
The "pre-commit" hook is a different story, though. From purely
practical point of view, starting to run extra verification that may
veto new commits from getting created only because we changed the
implementation detail for some reason is a disservice to the users.
^ permalink raw reply [flat|nested] 161+ messages in thread
* Re: [PATCH v3 15/15] rebase: change the default backend from "am" to "merge"
2020-01-11 14:41 ` Phillip Wood
2020-01-12 17:59 ` Johannes Schindelin
2020-01-12 21:23 ` Junio C Hamano
@ 2020-01-15 19:50 ` Jonathan Nieder
2020-01-15 21:59 ` Emily Shaffer
2 siblings, 1 reply; 161+ messages in thread
From: Jonathan Nieder @ 2020-01-15 19:50 UTC (permalink / raw)
To: phillip.wood
Cc: Elijah Newren, Elijah Newren via GitGitGadget, Git Mailing List,
Johannes Schindelin, Denton Liu, Junio C Hamano, Pavel Roskin,
Alban Gruin, SZEDER Gábor
Hi,
Phillip Wood wrote:
> On 11/01/2020 01:16, Elijah Newren wrote:
>> But the big question here, is what is correct behavior? Should rebase
>> call the post-commit hook, or should it skip it? I haven't any clue
>> what the answer to that is.
>
> It's creating a new commit so I lean towards thinking it should run the
> post-commit hook. As an example I have a post-commit hook that prints a
> warning if a commit is created on a branch that is being rewritten by one of
> my scripts in another worktree. There are pre-commit and pre-rebase hooks to
> try and prevent that, but the warning is there as a last resort if those
> hooks are by-passed.
The initial design for the post-commit hook was
commit 89e2c5f17b901edf28a8bb778ece3f6c18bbb721
Author: Junio C Hamano <gitster@pobox.com>
Date: Thu Aug 18 17:20:08 2005 -0700
Add commit hook and make the verification customizable.
There are three hooks:
- 'pre-commit' is given an opportunity to inspect what is
being committed, before we invoke the EDITOR for the
commit message;
- 'commit-msg' is invoked on the commit log message after
the user prepares it;
- 'post-commit' is run after a successful commit is made.
The first two can interfere to stop the commit. The last one is
for after-the-fact notification.
The initial implementation was only in "git commit" and didn't affect
other commands, but that's an artifact of implementation, not
intention. The intention is more murky: certainly "creating a new
commit" is not the event we want to notify about (for example, "git
commit-tree" should not invoke the hook), but if I were designing it
today then all operations that create a new commit and update the
current branch with it might qualify.
Even that definition is a bit fuzzy: when I run "git rebase <upstream>
<branch>", am I updating <branch> or the current branch? Are
cherry-picks that carry over changes that were committed previously
new commits?
Worse, we have the body of existing post-commit hooks to contend with,
and each one may have made different assumptions about the semantics.
We do not have the luxury of designing the hook without regard to that
body of existing hooks today.
The hook that jiri installs is very simple. It wants to check when a
user has (interactively) made a commit on a detached HEAD, to let them
know that they might want to use "git checkout -b" afterward. With
this particular hook, the behavior is better when git rebase does not
invoke the hook, since in the context of a rebase, the user has no
need to use "git checkout -b" afterward. This is a regression, and I
think we need to take that seriously.
It doesn't seem like jiri is doing anything weird here, so there's a
reasonably high probability that other hooks would be affected in the
same way. How can we help authors of such hooks to handle this
change?
I don't think that starting to invoke the 'post-commit' hook even more
without going through that thought process is an acceptable fix.
Since the "right" semantics aren't obvious here, the first step is
probably to collect some typical examples of post-commit hooks, like
the example from the message I'm replying to (thank you!). Ideas for
where we can find more?
Thanks,
Jonathan
^ permalink raw reply [flat|nested] 161+ messages in thread
* Re: [PATCH v3 15/15] rebase: change the default backend from "am" to "merge"
2020-01-15 19:50 ` Jonathan Nieder
@ 2020-01-15 21:59 ` Emily Shaffer
0 siblings, 0 replies; 161+ messages in thread
From: Emily Shaffer @ 2020-01-15 21:59 UTC (permalink / raw)
To: Jonathan Nieder
Cc: phillip.wood, Elijah Newren, Elijah Newren via GitGitGadget,
Git Mailing List, Johannes Schindelin, Denton Liu,
Junio C Hamano, Pavel Roskin, Alban Gruin, SZEDER Gábor
On Wed, Jan 15, 2020 at 07:50:11PM +0000, Jonathan Nieder wrote:
> Hi,
>
> Phillip Wood wrote:
> > On 11/01/2020 01:16, Elijah Newren wrote:
>
> >> But the big question here, is what is correct behavior? Should rebase
> >> call the post-commit hook, or should it skip it? I haven't any clue
> >> what the answer to that is.
> >
> > It's creating a new commit so I lean towards thinking it should run the
> > post-commit hook. As an example I have a post-commit hook that prints a
> > warning if a commit is created on a branch that is being rewritten by one of
> > my scripts in another worktree. There are pre-commit and pre-rebase hooks to
> > try and prevent that, but the warning is there as a last resort if those
> > hooks are by-passed.
>
> The initial design for the post-commit hook was
>
> commit 89e2c5f17b901edf28a8bb778ece3f6c18bbb721
> Author: Junio C Hamano <gitster@pobox.com>
> Date: Thu Aug 18 17:20:08 2005 -0700
>
> Add commit hook and make the verification customizable.
>
> There are three hooks:
>
> - 'pre-commit' is given an opportunity to inspect what is
> being committed, before we invoke the EDITOR for the
> commit message;
>
> - 'commit-msg' is invoked on the commit log message after
> the user prepares it;
>
> - 'post-commit' is run after a successful commit is made.
>
> The first two can interfere to stop the commit. The last one is
> for after-the-fact notification.
>
> The initial implementation was only in "git commit" and didn't affect
> other commands, but that's an artifact of implementation, not
> intention. The intention is more murky: certainly "creating a new
> commit" is not the event we want to notify about (for example, "git
> commit-tree" should not invoke the hook), but if I were designing it
> today then all operations that create a new commit and update the
> current branch with it might qualify.
>
> Even that definition is a bit fuzzy: when I run "git rebase <upstream>
> <branch>", am I updating <branch> or the current branch? Are
> cherry-picks that carry over changes that were committed previously
> new commits?
>
> Worse, we have the body of existing post-commit hooks to contend with,
> and each one may have made different assumptions about the semantics.
> We do not have the luxury of designing the hook without regard to that
> body of existing hooks today.
This seems like the most important point to me. I took a very cursory
peek at some search results on Github
(https://github.com/search?l=&q=filename%3A%2Apost-commit%2A&type=Code)
and I see mostly shims - make sure certain files were included in the
commit, bring a worktree up to date, stage stuff that wasn't committed -
but again, that's a very brief glance at over 19000 probable post-commit
hooks. Of course it's not possible for us to examine all of them, but
having these 19k scripts being run lots of times when they didn't used
to during an operation as common as rebase seems a little scary.
> The hook that jiri installs is very simple. It wants to check when a
> user has (interactively) made a commit on a detached HEAD, to let them
> know that they might want to use "git checkout -b" afterward. With
> this particular hook, the behavior is better when git rebase does not
> invoke the hook, since in the context of a rebase, the user has no
> need to use "git checkout -b" afterward. This is a regression, and I
> think we need to take that seriously.
>
> It doesn't seem like jiri is doing anything weird here, so there's a
> reasonably high probability that other hooks would be affected in the
> same way. How can we help authors of such hooks to handle this
> change?
This part I do disagree with. Git warns (advice-optional) when I enter
detached HEAD state. When I leave my detached HEAD with some orphaned
commits within (via checkout), I get a warning unconditionally, and an
advice-optional tip to use 'git branch <name> <commit-id>'. The Jiri
hook is warning that these commits I'm making could be lost, but in fact
Git itself is telling me that (and how to save them) at the first moment
when they are actually in danger. So I think this particular example is
not a good one.
I'm more worried about ones like this, although in the rebase case it
may well be a no-op as I can't start a rebase with a dirty index:
https://github.com/alphagov/digitalmarketplace-runner/blob/f8524d9ee7465caef0571d8beb8dff4b25d10fe7/hooks/post-commit
Perhaps a good question is "what could be very catastrophic if it
happened during the middle of a rebase, but not such a bad thing if it
happened when I run 'git commit'?"
> I don't think that starting to invoke the 'post-commit' hook even more
> without going through that thought process is an acceptable fix.
> Since the "right" semantics aren't obvious here, the first step is
> probably to collect some typical examples of post-commit hooks, like
> the example from the message I'm replying to (thank you!). Ideas for
> where we can find more?
That Github search query I posted above is a pretty big haystack. I
suppose plenty of folks who contribute to Git have a userbase and might
be familiar with some workflows in use there, right?
- Emily
^ permalink raw reply [flat|nested] 161+ messages in thread
* [PATCH v4 00/19] rebase: make the default backend configurable
2019-12-24 19:54 ` [PATCH v3 00/15] rebase: make the default backend configurable Elijah Newren via GitGitGadget
` (14 preceding siblings ...)
2019-12-24 19:54 ` [PATCH v3 15/15] rebase: change the default backend from "am" to "merge" Elijah Newren via GitGitGadget
@ 2020-01-16 6:14 ` Elijah Newren via GitGitGadget
2020-01-16 6:14 ` [PATCH v4 01/19] git-rebase.txt: update description of --allow-empty-message Elijah Newren via GitGitGadget
` (20 more replies)
15 siblings, 21 replies; 161+ messages in thread
From: Elijah Newren via GitGitGadget @ 2020-01-16 6:14 UTC (permalink / raw)
To: git
Cc: Johannes.Schindelin, phillip.wood, liu.denton, gitster, plroskin,
alban.gruin, szeder.dev, jrnieder, emilyshaffer, Elijah Newren
This is a re-roll of en/rebase-backend which has been rebased on v2.25.0 and
updated to remove the dependence on ra/rebase-i-more-options, and also tries
to address feedback from Phillip, Junio, and Jonathan. This series does a
lot of work around making the default rebase backend configurable, and
switches the default from the am backend to the merge/interactive one.
Changes since v3:
* Rebased on origin/master and updated to remove the dependence on
ra/rebase-i-more-options.
* Added two new patches at the start of the series.
* Split the old first patch into two, while modifying them based on
Phillip's feedback (though slightly differently than discussed on the
list; instead of making --keep-empty a synonym for --empty=keep, I
instead kept backward compatibility by making it a no-op).
* I noted the post-commit hook in the differences between backends. Emily
is investigating what changes need to happen there, so I merely
documented the existing differences.
* dropped '-i' from the reflog messages; now they just refer to 'rebase'
Changes possibly missing from this version, for discussion:
* I did not remove the --am option as suggested by Phillip, since Junio and
Phillip were still discussing whether it is wanted/needed.
* I did not address the last two items Jonathan brought up as I couldn't
find enough information to reproduce or understand the problems.
Elijah Newren (19):
git-rebase.txt: update description of --allow-empty-message
t3404: directly test the behavior of interest
rebase (interactive-backend): make --keep-empty the default
rebase (interactive-backend): fix handling of commits that become
empty
t3406: simplify an already simple test
rebase, sequencer: remove the broken GIT_QUIET handling
rebase: make sure to pass along the quiet flag to the sequencer
rebase: fix handling of restrict_revision
t3432: make these tests work with either am or merge backends
rebase: allow more types of rebases to fast-forward
git-rebase.txt: add more details about behavioral differences of
backends
rebase: move incompatibility checks between backend options a bit
earlier
rebase: add an --am option
git-prompt: change the prompt for interactive-based rebases
rebase: drop '-i' from the reflog for interactive-based rebases
rebase tests: mark tests specific to the am-backend with --am
rebase tests: repeat some tests using the merge backend instead of am
rebase: make the backend configurable via config setting
rebase: change the default backend from "am" to "merge"
Documentation/config/rebase.txt | 8 ++
Documentation/git-rebase.txt | 150 +++++++++++++++++---
builtin/rebase.c | 186 +++++++++++++++++++------
contrib/completion/git-prompt.sh | 6 +-
rebase-interactive.c | 7 +-
rebase-interactive.h | 2 +-
sequencer.c | 84 ++++++-----
sequencer.h | 3 +-
t/t3400-rebase.sh | 36 ++++-
t/t3401-rebase-and-am-rename.sh | 4 +-
t/t3404-rebase-interactive.sh | 19 +--
t/t3406-rebase-message.sh | 19 ++-
t/t3407-rebase-abort.sh | 6 +-
t/t3420-rebase-autostash.sh | 2 +-
t/t3421-rebase-topology-linear.sh | 16 +--
t/t3424-rebase-empty.sh | 108 ++++++++++++++
t/t3425-rebase-topology-merges.sh | 8 +-
t/t3427-rebase-subtree.sh | 12 +-
t/t3432-rebase-fast-forward.sh | 54 ++++---
t/t5407-post-rewrite-hook.sh | 12 +-
t/t5520-pull.sh | 27 +++-
t/t6047-diff3-conflict-markers.sh | 13 +-
t/t7512-status-help.sh | 12 +-
t/t9106-git-svn-commit-diff-clobber.sh | 3 +-
t/t9903-bash-prompt.sh | 8 +-
25 files changed, 595 insertions(+), 210 deletions(-)
create mode 100755 t/t3424-rebase-empty.sh
base-commit: d0654dc308b0ba76dd8ed7bbb33c8d8f7aacd783
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-679%2Fnewren%2Frebase-fixes-v4
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-679/newren/rebase-fixes-v4
Pull-Request: https://github.com/git/git/pull/679
Range-diff vs v3:
-: ---------- > 1: 3ea48d5394 git-rebase.txt: update description of --allow-empty-message
-: ---------- > 2: 10fdd162a0 t3404: directly test the behavior of interest
1: 1c2b77e94d ! 3: 179f82ab83 rebase: extend the options for handling of empty commits
@@ -1,49 +1,46 @@
Author: Elijah Newren <newren@gmail.com>
- rebase: extend the options for handling of empty commits
+ rebase (interactive-backend): make --keep-empty the default
- Extend the interactive machinery with the ability to handle the full
- spread of options for how to handle commits that either start or become
- empty (by "become empty" I mean the changes in a commit are a subset of
- changes that exist upstream, so the net effect of applying the commit is
- no changes). Introduce a new command line flag for selecting the
- desired behavior:
- --empty={drop,keep,ask}
- with the definitions:
- drop: drop empty commits
- keep: keep empty commits
- ask: provide the user a chance to interact and pick what to do with
- empty commits on a case-by-case basis
+ Different rebase backends have different treatment for commits which
+ start empty (i.e. have no changes relative to their parent), and the
+ --keep-empty option was added at some point to allow adjusting behavior
+ for the interactive backend. The handling of commits which start empty
+ is actually quite similar to commit b00bf1c9a8dd (git-rebase: make
+ --allow-empty-message the default, 2018-06-27), which pointed out that
+ the behavior for various backends is often more happenstance than
+ design. The specific change made in that commit is actually quite
+ relevant as well and much of the logic there directly applies here.
- Note that traditionally, am-based rebases have always dropped commits
- that either started or became empty, while interactive-based rebases
- have defaulted to ask (and provided an option to keep commits that
- started empty). This difference made sense since users of an am-based
- rebase just wanted to quickly batch apply a sequence of commits, while
- users editing a todo list will likely want the chance to interact and
- handle unusual cases on a case-by-case basis. However, not all rebases
- using the interactive machinery are explicitly interactive anymore. In
- particular --merge was always meant to behave more like --am: just
- rebase a batch of commits without popping up a todo list.
+ It makes a lot of sense in 'git commit' to error out on the creation of
+ empty commits, unless an override flag is provided. However, once
+ someone determines that there is a rare case that merits using the
+ manual override to create such a commit, it is somewhere between
+ annoying and harmful to have to take extra steps to keep such
+ intentional commits around. Granted, empty commits are quite rare,
+ which is why handling of them doesn't get considered much and folks tend
+ to defer to existing (accidental) behavior and assume there was a reason
+ for it, leading them to just add flags (--keep-empty in this case) that
+ allow them to override the bad defaults. Fix the interactive backend so
+ that --keep-empty is the default, much like we did with
+ --allow-empty-message. The am backend should also be fixed to have
+ --keep-empty semantics for commits that start empty, but that is not
+ included in this patch other than a testcase documenting the failure.
- If the --empty flag is not specified, pick defaults as follows:
- explicitly interactive: ask
- --exec: keep (exec is about checking existing commits, and often
- used without actually changing the base. Thus the
- expectation is that the user doesn't necessarily want
- anything to change; they just want to test).
- otherwise: drop
-
- Also, this commit makes --keep-empty just imply --empty=keep, and hides
- it from help so that we aren't confusing users with different ways to do
- the same thing. (I could have added a --drop-empty flag, but then that
- invites users to specify both --keep-empty and --drop-empty and we have
- to add sanity checking around that; it seems cleaner to have a single
- multi-valued option.) This actually fixes --keep-empty too; previously,
- it only meant to sometimes keep empty commits, in particular commits
- which started empty would be kept. But it would still error out and ask
- the user what to do with commits that became empty. Now it keeps empty
- commits, as instructed.
+ Note that there was one test in t3421 which appears to have been written
+ expecting --keep-empty to not be the default as correct behavior. This
+ test was introduced in commit 00b8be5a4d38 ("add tests for rebasing of
+ empty commits", 2013-06-06), which was part of a series focusing on
+ rebase topology and which had an interesting original cover letter at
+ https://lore.kernel.org/git/1347949878-12578-1-git-send-email-martinvonz@gmail.com/
+ which noted
+ Your input especially appreciated on whether you agree with the
+ intent of the test cases.
+ and then went into a long example about how one of the many tests added
+ had several questions about whether it was correct. As such, I believe
+ most the tests in that series were about testing rebase topology with as
+ many different flags as possible and were not trying to state in general
+ how those flags should behave otherwise.
Signed-off-by: Elijah Newren <newren@gmail.com>
@@ -51,126 +48,62 @@
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@
- original branch. The index and working tree are also left
unchanged as a result.
-+--empty={drop,keep,ask}::
-+ How to handle commits that become empty (because they contain a
-+ subset of already upstream changes) or start empty. With drop
-+ (the default), commits that start or become empty are dropped.
-+ With keep (implied by --exec), such commits are kept. With ask
-+ (implied by --interactive), the rebase will halt when an empty
-+ commit is applied allowing you to choose whether to drop it or
-+ commit it. Also with ask, if the rebase is interactive then
-+ commits which start empty will be commented out in the todo
-+ action list (giving you a chance to uncomment).
-++
-+Note that this has no effect on commits which are already upstream (as
-+can be checked via `git log --cherry-mark ...`), which are always
-+dropped by rebase.
-++
-+See also INCOMPATIBLE OPTIONS below.
-+
--keep-empty::
- Keep the commits that do not change anything from its
- parents in the result.
-+ Deprecated alias for what is now known as --empty=keep.
++ No-op. Rebasing commits that started empty (had no change
++ relative to their parent) used to fail and this option would
++ override that behavior, allowing commits with empty changes to
++ be rebased. Now commits with no changes do not cause rebasing
++ to halt.
+
- See also INCOMPATIBLE OPTIONS below.
-
-@@
- * --interactive
- * --exec
- * --keep-empty
-+ * --empty=
- * --edit-todo
- * --root when used in combination with --onto
-
-@@
- * --preserve-merges and --ignore-whitespace
- * --preserve-merges and --committer-date-is-author-date
- * --preserve-merges and --ignore-date
-+ * --preserve-merges and --empty=
- * --keep-base and --onto
- * --keep-base and --root
+-See also INCOMPATIBLE OPTIONS below.
++See also BEHAVIORAL DIFFERENCES and INCOMPATIBLE OPTIONS below.
+ --allow-empty-message::
+ No-op. Rebasing commits with an empty message used to fail
@@
+ Empty commits
+ ~~~~~~~~~~~~~
- There are some subtle differences how the backends behave.
-
--Empty commits
--~~~~~~~~~~~~~
--
-The am backend drops any "empty" commits, regardless of whether the
-commit started empty (had no changes relative to its parent to
-start with) or ended empty (all changes were already applied
-upstream in other commits).
--
++The am backend unfortunately drops intentionally empty commits, i.e.
++commits that started empty, though these are rare in practice. It
++also drops commits that become empty and has no option for controlling
++this behavior.
+
-The interactive backend drops commits by default that
-started empty and halts if it hits a commit that ended up empty.
-The `--keep-empty` option exists for the interactive backend to allow
-it to keep commits that started empty.
--
++The interactive backend keeps intentionally empty commits.
++Unfortunately, it always halts whenever it runs across a commit that
++becomes empty, even when the rebase is not explicitly interactive.
+
Directory rename detection
~~~~~~~~~~~~~~~~~~~~~~~~~~
-
diff --git a/builtin/rebase.c b/builtin/rebase.c
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
-@@
- REBASE_PRESERVE_MERGES
- };
-
-+enum empty_type {
-+ EMPTY_UNSPECIFIED = -1,
-+ EMPTY_DROP,
-+ EMPTY_KEEP,
-+ EMPTY_ASK
-+};
-+
- struct rebase_options {
- enum rebase_type type;
-+ enum empty_type empty;
- const char *state_dir;
- struct commit *upstream;
- const char *upstream_name;
@@
const char *action;
int signoff;
int allow_rerere_autoupdate;
- int keep_empty;
int autosquash;
- int ignore_whitespace;
char *gpg_sign_opt;
-@@
-
- #define REBASE_OPTIONS_INIT { \
- .type = REBASE_UNSPECIFIED, \
-+ .empty = EMPTY_UNSPECIFIED, \
- .flags = REBASE_NO_QUIET, \
- .git_am_opts = ARGV_ARRAY_INIT, \
- .git_format_patch_opt = STRBUF_INIT \
-@@
- replay.allow_rerere_auto = opts->allow_rerere_autoupdate;
- replay.allow_empty = 1;
- replay.allow_empty_message = opts->allow_empty_message;
-+ replay.drop_redundant_commits = (opts->empty == EMPTY_DROP);
-+ replay.keep_redundant_commits = (opts->empty == EMPTY_KEEP);
-+ replay.ask_on_initially_empty = (opts->empty == EMPTY_ASK &&
-+ !(opts->flags & REBASE_INTERACTIVE_EXPLICIT));
- replay.verbose = opts->flags & REBASE_VERBOSE;
- replay.reschedule_failed_exec = opts->reschedule_failed_exec;
- replay.committer_date_is_author_date =
+ int autostash;
@@
git_config_get_bool("rebase.abbreviatecommands", &abbreviate_commands);
- flags |= opts->keep_empty ? TODO_LIST_KEEP_EMPTY : 0;
-+ flags |= (opts->empty == EMPTY_DROP) ? TODO_LIST_DROP_EMPTY : 0;
-+ flags |= (opts->empty == EMPTY_ASK &&
-+ opts->flags & REBASE_INTERACTIVE_EXPLICIT) ?
-+ TODO_LIST_ASK_EMPTY : 0;
flags |= abbreviate_commands ? TODO_LIST_ABBREVIATE_CMDS : 0;
flags |= opts->rebase_merges ? TODO_LIST_REBASE_MERGES : 0;
flags |= opts->rebase_cousins > 0 ? TODO_LIST_REBASE_COUSINS : 0;
@@ -183,10 +116,8 @@
+{
+ struct rebase_options *opts = opt->value;
+
-+ BUG_ON_OPT_NEG(unset);
+ BUG_ON_OPT_ARG(arg);
+
-+ opts->empty = EMPTY_KEEP;
+ opts->type = REBASE_INTERACTIVE;
+ return 0;
+}
@@ -201,62 +132,28 @@
- OPT_BOOL(0, "keep-empty", &opts.keep_empty, N_("keep empty commits")),
+ { OPTION_CALLBACK, 'k', "keep-empty", &options, NULL,
+ N_("(DEPRECATED) keep empty commits"),
-+ PARSE_OPT_NOARG | PARSE_OPT_NONEG | PARSE_OPT_HIDDEN,
++ PARSE_OPT_NOARG | PARSE_OPT_HIDDEN,
+ parse_opt_keep_empty },
- OPT_BOOL(0, "allow-empty-message", &opts.allow_empty_message,
- N_("allow commits with empty messages")),
- OPT_BOOL(0, "rebase-merges", &opts.rebase_merges, N_("rebase merge commits")),
+ OPT_BOOL_F(0, "allow-empty-message", &opts.allow_empty_message,
+ N_("allow commits with empty messages"),
+ PARSE_OPT_HIDDEN),
@@
opts->allow_rerere_autoupdate ?
opts->allow_rerere_autoupdate == RERERE_AUTOUPDATE ?
"--rerere-autoupdate" : "--no-rerere-autoupdate" : "");
- add_var(&script_snippet, "keep_empty", opts->keep_empty ? "yes" : "");
-+ add_var(&script_snippet, "empty", opts->empty == EMPTY_KEEP ? "yes" : "");
add_var(&script_snippet, "autosquash", opts->autosquash ? "t" : "");
add_var(&script_snippet, "gpg_sign_opt", opts->gpg_sign_opt);
add_var(&script_snippet, "cmd", opts->cmd);
-@@
- return 0;
- }
-
-+static enum empty_type parse_empty_value(const char *value)
-+{
-+ if (!strcasecmp(value, "drop"))
-+ return EMPTY_DROP;
-+ else if (!strcasecmp(value, "keep"))
-+ return EMPTY_KEEP;
-+ else if (!strcasecmp(value, "ask"))
-+ return EMPTY_ASK;
-+
-+ die(_("unrecognized empty type '%s'; valid values are \"drop\", \"keep\", and \"ask\"."), value);
-+}
-+
-+static int parse_opt_empty(const struct option *opt, const char *arg, int unset)
-+{
-+ struct rebase_options *options = opt->value;
-+ enum empty_type value = parse_empty_value(arg);
-+
-+ BUG_ON_OPT_NEG(unset);
-+
-+ options->empty = value;
-+ return 0;
-+}
-+
- static void NORETURN error_on_missing_default_upstream(void)
- {
- struct branch *current_branch = branch_get(NULL);
@@
"ignoring them"),
REBASE_PRESERVE_MERGES, PARSE_OPT_HIDDEN),
OPT_RERERE_AUTOUPDATE(&options.allow_rerere_autoupdate),
- OPT_BOOL('k', "keep-empty", &options.keep_empty,
- N_("preserve empty commits during rebase")),
-+ OPT_CALLBACK_F(0, "empty", &options, N_("{drop,keep,ask}"),
-+ N_("how to handle empty commits"),
-+ PARSE_OPT_NONEG, parse_opt_empty),
+ { OPTION_CALLBACK, 'k', "keep-empty", &options, NULL,
+ N_("(DEPRECATED) keep empty commits"),
-+ PARSE_OPT_NOARG | PARSE_OPT_NONEG | PARSE_OPT_HIDDEN,
++ PARSE_OPT_NOARG | PARSE_OPT_HIDDEN,
+ parse_opt_keep_empty },
OPT_BOOL(0, "autosquash", &options.autosquash,
N_("move commits that begin with "
@@ -267,26 +164,10 @@
- if (options.keep_empty)
- imply_interactive(&options, "--keep-empty");
-+ if (options.empty != EMPTY_UNSPECIFIED)
-+ imply_interactive(&options, "--empty");
-
+-
if (gpg_sign) {
free(options.gpg_sign_opt);
-@@
- break;
- }
-
-+ if (options.empty == EMPTY_UNSPECIFIED) {
-+ if (options.flags & REBASE_INTERACTIVE_EXPLICIT)
-+ options.empty = EMPTY_ASK;
-+ else if (exec.nr > 0)
-+ options.empty = EMPTY_KEEP;
-+ else
-+ options.empty = EMPTY_DROP;
-+ }
- if (reschedule_failed_exec > 0 && !is_interactive(&options))
- die(_("--reschedule-failed-exec requires "
- "--exec or --interactive"));
+ options.gpg_sign_opt = xstrfmt("-S%s", gpg_sign);
diff --git a/rebase-interactive.c b/rebase-interactive.c
--- a/rebase-interactive.c
@@ -296,19 +177,22 @@
}
-void append_todo_help(unsigned keep_empty, int command_count,
-+void append_todo_help(unsigned no_ask_empty, int command_count,
++void append_todo_help(int command_count,
const char *shortrevisions, const char *shortonto,
struct strbuf *buf)
{
@@
+ "the rebase will be aborted.\n\n");
strbuf_add_commented_lines(buf, msg, strlen(msg));
-
+-
- if (!keep_empty) {
-+ if (!no_ask_empty) {
- msg = _("Note that empty commits are commented out");
- strbuf_add_commented_lines(buf, msg, strlen(msg));
- }
+- msg = _("Note that empty commits are commented out");
+- strbuf_add_commented_lines(buf, msg, strlen(msg));
+- }
+ }
+
+ int edit_todo_list(struct repository *r, struct todo_list *todo_list,
diff --git a/rebase-interactive.h b/rebase-interactive.h
--- a/rebase-interactive.h
@@ -318,7 +202,7 @@
struct todo_list;
-void append_todo_help(unsigned keep_empty, int command_count,
-+void append_todo_help(unsigned no_ask_empty, int command_count,
++void append_todo_help(int command_count,
const char *shortrevisions, const char *shortonto,
struct strbuf *buf);
int edit_todo_list(struct repository *r, struct todo_list *todo_list,
@@ -327,157 +211,51 @@
--- a/sequencer.c
+++ b/sequencer.c
@@
- 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 GIT_PATH_FUNC(rebase_path_ask_on_initially_empty, "rebase-merge/ask_on_initially_empty")
-
- static int git_sequencer_config(const char *k, const char *v, void *cb)
+ struct replay_opts *opts,
+ struct commit *commit)
{
+- int index_unchanged, empty_commit;
++ int index_unchanged, originally_empty;
+
+ /*
+ * Three cases:
@@
- empty_commit = is_original_commit_empty(commit);
- if (empty_commit < 0)
- return empty_commit;
+ if (opts->keep_redundant_commits)
+ return 1;
+
+- empty_commit = is_original_commit_empty(commit);
+- if (empty_commit < 0)
+- return empty_commit;
- if (!empty_commit)
-+ if (!empty_commit || opts->ask_on_initially_empty)
++ originally_empty = is_original_commit_empty(commit);
++ if (originally_empty < 0)
++ return originally_empty;
++ if (!originally_empty)
return 0;
else
return 1;
-@@
- 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) {
- /*
-@@
- goto leave;
- }
-
-- allow = allow_empty(r, opts, commit);
-- if (allow < 0) {
-- res = allow;
-- goto leave;
-- } else if (allow)
-- flags |= ALLOW_EMPTY;
-- if (!opts->no_commit) {
-+ drop_commit = 0;
-+ if (opts->drop_redundant_commits && is_index_unchanged(r)) {
-+ drop_commit = 1;
-+ fprintf(stderr, _("No changes -- Patch already applied."));
-+ } else {
-+ allow = allow_empty(r, opts, commit);
-+ if (allow < 0) {
-+ res = allow;
-+ goto leave;
-+ } else if (allow) {
-+ flags |= ALLOW_EMPTY;
-+ }
-+ }
-+ if (!opts->no_commit && !drop_commit) {
- if (author || command == TODO_REVERT || (flags & AMEND_MSG))
- res = do_commit(r, msg_file, author, opts, flags);
- else
-@@
- else if (!strcmp(key, "options.allow-empty-message"))
- opts->allow_empty_message =
- git_config_bool_or_int(key, value, &error_flag);
-+ else if (!strcmp(key, "options.drop-redundant-commits"))
-+ opts->drop_redundant_commits =
-+ git_config_bool_or_int(key, value, &error_flag);
- else if (!strcmp(key, "options.keep-redundant-commits"))
- opts->keep_redundant_commits =
- git_config_bool_or_int(key, value, &error_flag);
-+ else if (!strcmp(key, "options.ask_on_initially_empty"))
-+ opts->ask_on_initially_empty =
-+ git_config_bool_or_int(key, value, &error_flag);
- else if (!strcmp(key, "options.signoff"))
- opts->signoff = git_config_bool_or_int(key, value, &error_flag);
- else if (!strcmp(key, "options.record-origin"))
-@@
- 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;
-+
-+ if (file_exists(rebase_path_ask_on_initially_empty()))
-+ opts->ask_on_initially_empty = 1;
-+
- read_strategy_opts(opts, &buf);
- strbuf_release(&buf);
-
-@@
- write_file(rebase_path_cdate_is_adate(), "%s", "");
- if (opts->ignore_date)
- write_file(rebase_path_ignore_date(), "%s", "");
-+ 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->ask_on_initially_empty)
-+ write_file(rebase_path_ask_on_initially_empty(), "%s", "");
- if (opts->reschedule_failed_exec)
- write_file(rebase_path_reschedule_failed_exec(), "%s", "");
-
-@@
- if (opts->allow_empty_message)
- res |= git_config_set_in_file_gently(opts_file,
- "options.allow-empty-message", "true");
-+ if (opts->drop_redundant_commits)
-+ res |= git_config_set_in_file_gently(opts_file,
-+ "options.drop-redundant-commits", "true");
- if (opts->keep_redundant_commits)
- res |= git_config_set_in_file_gently(opts_file,
- "options.keep-redundant-commits", "true");
-+ if (opts->ask_on_initially_empty)
-+ res |= git_config_set_in_file_gently(opts_file,
-+ "options.ask_on_initially_empty", "true");
- if (opts->signoff)
- res |= git_config_set_in_file_gently(opts_file,
- "options.signoff", "true");
@@
struct rev_info *revs, struct strbuf *out,
unsigned flags)
{
- int keep_empty = flags & TODO_LIST_KEEP_EMPTY;
-+ int drop_empty = flags & TODO_LIST_DROP_EMPTY;
-+ int ask_empty = flags & TODO_LIST_ASK_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;
-@@
- is_empty = is_original_commit_empty(commit);
- if (!is_empty && (commit->object.flags & PATCHSAME))
- continue;
-+ if (is_empty && drop_empty)
-+ continue;
-
- strbuf_reset(&oneline);
- pretty_print_commit(pp, commit, &oneline);
@@
if (!to_merge) {
/* non-merge commit: easy case */
strbuf_reset(&buf);
- if (!keep_empty && is_empty)
-+ if (is_empty && ask_empty)
- strbuf_addf(&buf, "%c ", comment_line_char);
+- strbuf_addf(&buf, "%c ", comment_line_char);
strbuf_addf(&buf, "%s %s %s", cmd_pick,
oid_to_hex(&commit->object.oid),
+ oneline.buf);
@@
struct pretty_print_context pp = {0};
struct rev_info revs;
struct commit *commit;
- int keep_empty = flags & TODO_LIST_KEEP_EMPTY;
-+ int drop_empty = flags & TODO_LIST_DROP_EMPTY;
-+ int ask_empty = flags & TODO_LIST_ASK_EMPTY;
const char *insn = flags & TODO_LIST_ABBREVIATE_CMDS ? "p" : "pick";
int rebase_merges = flags & TODO_LIST_REBASE_MERGES;
@@ -491,19 +269,16 @@
if (!is_empty && (commit->object.flags & PATCHSAME))
continue;
- if (!keep_empty && is_empty)
-+ if (is_empty && drop_empty)
-+ continue;
-+ if (is_empty && ask_empty)
- strbuf_addf(out, "%c ", comment_line_char);
+- 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);
@@
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(!(flags & TODO_LIST_ASK_EMPTY),
-+ count_commands(todo_list),
++ append_todo_help(count_commands(todo_list),
shortrevisions, shortonto, &buf);
res = write_message(buf.buf, buf.len, file, 0);
@@ -511,16 +286,6 @@
diff --git a/sequencer.h b/sequencer.h
--- a/sequencer.h
+++ b/sequencer.h
-@@
- int allow_rerere_auto;
- int allow_empty;
- int allow_empty_message;
-+ int drop_redundant_commits;
- int keep_redundant_commits;
-+ int ask_on_initially_empty;
- int verbose;
- int quiet;
- int reschedule_failed_exec;
@@
int sequencer_skip(struct repository *repo, struct replay_opts *opts);
int sequencer_remove_state(struct replay_opts *opts);
@@ -530,19 +295,34 @@
#define TODO_LIST_SHORTEN_IDS (1U << 1)
#define TODO_LIST_ABBREVIATE_CMDS (1U << 2)
#define TODO_LIST_REBASE_MERGES (1U << 3)
-@@
- * `--onto`, we do not want to re-generate the root commits.
- */
- #define TODO_LIST_ROOT_WITH_ONTO (1U << 6)
-+#define TODO_LIST_DROP_EMPTY (1U << 7)
-+#define TODO_LIST_ASK_EMPTY (1U << 8)
-
-
- int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,
diff --git a/t/t3421-rebase-topology-linear.sh b/t/t3421-rebase-topology-linear.sh
--- a/t/t3421-rebase-topology-linear.sh
+++ b/t/t3421-rebase-topology-linear.sh
+@@
+ test_run_rebase () {
+ result=$1
+ shift
+- test_expect_$result "rebase $* drops empty commit" "
++ test_expect_$result "rebase $* keeps begin-empty commits" "
+ reset_rebase &&
+- git rebase $* c l &&
+- test_cmp_rev c HEAD~2 &&
+- test_linear_range 'd l' c..
++ git rebase $* j l &&
++ test_cmp_rev c HEAD~4 &&
++ test_linear_range 'j d k l' c..
+ "
+ }
+-test_run_rebase success ''
++test_run_rebase failure ''
+ test_run_rebase success -m
+ test_run_rebase success -i
+-test_have_prereq !REBASE_P || test_run_rebase success -p
++test_have_prereq !REBASE_P || test_run_rebase failure -p
+
+ test_run_rebase () {
+ result=$1
@@
test_run_rebase success ''
test_run_rebase success -m
@@ -603,31 +383,22 @@
+ git commit -m "Five letters ought to be enough for anybody"
+'
+
-+test_expect_success 'rebase --merge --empty=drop' '
++test_expect_failure 'rebase (am-backend) with a variety of empty commits' '
++ test_when_finished "git rebase --abort" &&
+ git checkout -B testing localmods &&
-+ git rebase --merge --empty=drop upstream &&
-+
-+ test_write_lines C B A >expect &&
-+ git log --format=%s >actual &&
-+ test_cmp expect actual
-+'
++ # rebase (--am) should not drop commits that start empty
++ git rebase upstream &&
+
-+test_expect_success 'rebase --merge --empty=keep' '
-+ git checkout -B testing localmods &&
-+ git rebase --merge --empty=keep upstream &&
-+
-+ test_write_lines D C2 C B A >expect &&
++ test_write_lines D C B A >expect &&
+ git log --format=%s >actual &&
+ test_cmp expect actual
+'
+
-+test_expect_success 'rebase --merge --empty=ask' '
++test_expect_failure 'rebase --merge with a variety of empty commits' '
++ test_when_finished "git rebase --abort" &&
+ git checkout -B testing localmods &&
-+ test_must_fail git rebase --merge --empty=ask upstream &&
-+
-+ test_must_fail git rebase --skip &&
-+ git commit --allow-empty &&
-+ git rebase --continue &&
++ # rebase --merge should not halt on the commit that becomes empty
++ git rebase --merge upstream &&
+
+ test_write_lines D C B A >expect &&
+ git log --format=%s >actual &&
@@ -636,25 +407,17 @@
+
+GIT_SEQUENCE_EDITOR=: && export GIT_SEQUENCE_EDITOR
+
-+test_expect_success 'rebase --interactive --empty=drop' '
++test_expect_success 'rebase --interactive with a variety of empty commits' '
+ git checkout -B testing localmods &&
-+ git rebase --interactive --empty=drop upstream &&
++ test_must_fail git rebase --interactive upstream &&
+
-+ test_write_lines C B A >expect &&
-+ git log --format=%s >actual &&
-+ test_cmp expect actual
-+'
-+
-+test_expect_success 'rebase --interactive --empty=keep' '
-+ git checkout -B testing localmods &&
-+ git rebase --interactive --empty=keep upstream &&
++ git rebase --skip &&
+
-+ test_write_lines D C2 C B A >expect &&
++ test_write_lines D C B A >expect &&
+ git log --format=%s >actual &&
+ test_cmp expect actual
+'
+
-+
+test_done
diff --git a/t/t3427-rebase-subtree.sh b/t/t3427-rebase-subtree.sh
@@ -665,32 +428,28 @@
'
-test_expect_success 'Rebase -Xsubtree --keep-empty --onto commit' '
-+test_expect_success 'Rebase -Xsubtree --empty=ask --onto commit' '
++test_expect_success 'Rebase -Xsubtree --onto commit' '
reset_rebase &&
git checkout -b rebase-onto to-rebase &&
- test_must_fail git rebase -Xsubtree=files_subtree --keep-empty --onto files-master master &&
-+ test_must_fail git rebase -Xsubtree=files_subtree --empty=ask --onto files-master master &&
++ test_must_fail git rebase -Xsubtree=files_subtree --onto files-master master &&
: first pick results in no changes &&
- git rebase --continue &&
-+ test_must_fail git rebase --skip &&
-+ : last pick was an empty commit that has no changes, but we want to keep it &&
-+ git commit --allow-empty &&
++ git rebase --skip &&
verbose test "$(commit_message HEAD~2)" = "master4" &&
verbose test "$(commit_message HEAD~)" = "files_subtree/master5" &&
verbose test "$(commit_message HEAD)" = "Empty commit"
'
-test_expect_success 'Rebase -Xsubtree --keep-empty --rebase-merges --onto commit' '
-+test_expect_success 'Rebase -Xsubtree --empty=ask --rebase-merges --onto commit' '
++test_expect_success 'Rebase -Xsubtree --rebase-merges --onto commit' '
reset_rebase &&
git checkout -b rebase-merges-onto to-rebase &&
- test_must_fail git rebase -Xsubtree=files_subtree --keep-empty --rebase-merges --onto files-master --root &&
-+ test_must_fail git rebase -Xsubtree=files_subtree --empty=ask --rebase-merges --onto files-master --root &&
++ test_must_fail git rebase -Xsubtree=files_subtree --rebase-merges --onto files-master --root &&
: first pick results in no changes &&
- git rebase --continue &&
-+ test_must_fail git rebase --skip &&
-+ : last pick was an empty commit that has no changes, but we want to keep it &&
-+ git commit --allow-empty &&
++ git rebase --skip &&
verbose test "$(commit_message HEAD~2)" = "master4" &&
verbose test "$(commit_message HEAD~)" = "files_subtree/master5" &&
verbose test "$(commit_message HEAD)" = "Empty commit"
-: ---------- > 4: c9542a2abe rebase (interactive-backend): fix handling of commits that become empty
2: bd3c5ec155 = 5: 9f66229d5c t3406: simplify an already simple test
3: 49388b79fd = 6: 8d731fa39c rebase, sequencer: remove the broken GIT_QUIET handling
4: 478479358f ! 7: b6b6597eef rebase: make sure to pass along the quiet flag to the sequencer
@@ -8,13 +8,13 @@
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@
+ replay.allow_empty_message = opts->allow_empty_message;
+ replay.drop_redundant_commits = (opts->empty == EMPTY_DROP);
replay.keep_redundant_commits = (opts->empty == EMPTY_KEEP);
- replay.ask_on_initially_empty = (opts->empty == EMPTY_ASK &&
- !(opts->flags & REBASE_INTERACTIVE_EXPLICIT));
+ replay.quiet = !(opts->flags & REBASE_NO_QUIET);
replay.verbose = opts->flags & REBASE_VERBOSE;
replay.reschedule_failed_exec = opts->reschedule_failed_exec;
- replay.committer_date_is_author_date =
+ replay.gpg_sign = xstrdup_or_null(opts->gpg_sign_opt);
@@
N_("allow pre-rebase hook to run")),
OPT_NEGBIT('q', "quiet", &options.flags,
5: ee26f5a161 = 8: 0acefa988b rebase: fix handling of restrict_revision
6: 34a69def33 = 9: 8c5b5b5133 t3432: make these tests work with either am or merge backends
7: f2c92853b4 ! 10: b8c087d6fb rebase: allow more types of rebases to fast-forward
@@ -35,8 +35,8 @@
OPT_STRING(0, "onto", &options.onto_name,
N_("revision"),
@@
- options.ignore_date)
- options.flags |= REBASE_FORCE;
+ state_dir_base, cmd_live_rebase, buf.buf);
+ }
+ if ((options.flags & REBASE_INTERACTIVE_EXPLICIT) ||
+ (action != ACTION_NONE) ||
@@ -47,7 +47,9 @@
+
for (i = 0; i < options.git_am_opts.argc; i++) {
const char *option = options.git_am_opts.argv[i], *p;
- if (!strcmp(option, "--whitespace=fix") ||
+ if (!strcmp(option, "--committer-date-is-author-date") ||
+ !strcmp(option, "--ignore-date") ||
+ !strcmp(option, "--whitespace=fix") ||
!strcmp(option, "--whitespace=strip"))
- options.flags |= REBASE_FORCE;
+ allow_preemptive_ff = 0;
8: b307340f7c ! 11: b50a1741e0 git-rebase.txt: add more details about behavioral differences of backends
@@ -8,23 +8,24 @@
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@
- with `--keep-base` in order to drop those commits from your branch.
--ignore-whitespace::
-- Behaves differently depending on which backend is selected.
--+
--'am' backend: When applying a patch, ignore changes in whitespace in
--context lines if necessary.
--+
--'interactive' backend: Treat lines with only whitespace changes as
--unchanged for the sake of a three-way merge.
-+ Ignore whitespace-only changes in the commits being rebased,
-+ which may avoid "unnecessary" conflicts. (Both backends
-+ currently have differing edgecase bugs with this option; see
-+ BEHAVIORAL DIFFERENCES.)
-
--whitespace=<option>::
- This flag is passed to the 'git apply' program
+- These flag are passed to the 'git apply' program
++ These flags are passed to the 'git apply' program
+ (see linkgit:git-apply[1]) that applies the patch.
+ +
+ See also INCOMPATIBLE OPTIONS below.
+@@
+
+ * --committer-date-is-author-date
+ * --ignore-date
+- * --whitespace
+ * --ignore-whitespace
++ * --whitespace
+ * -C
+
+ are incompatible with the following options:
@@
Directory rename detection
~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -82,24 +83,14 @@
+The interactive backend works with the full commits on both sides of
+history and thus has no such limitations.
+
-+--ignore-whitespace
-+~~~~~~~~~~~~~~~~~~~
++Hooks
++~~~~~
+
-+The --ignore-whitespace option is supposed to ignore whitespace-only
-+changes if it allows the code to merge cleanly. Unfortunately, the
-+different backends implement this differently, and both have different
-+edge case bugs.
-++
-+'am' backend: When applying a patch, ignore changes in whitespace in
-+context lines if necessary. (Which implies that if the whitespace
-+change was not in the context lines but on a line with a real change,
-+then the rebase will still fail with "unnecessary" content conflicts.)
-++
-+'interactive' backend: Treat lines with only whitespace changes as
-+unchanged for the sake of a three-way merge. This means that if one
-+side made no changes and the commits being rebased had whitespace-only
-+changes, those whitespaces fixups will be discarded despite the fact
-+that they present no content conflict.
++The am backend has not traditionally called the post-commit hook,
++while the merge/interactive backend has. However, this was by
++accident of implementation rather than by design. Both backends
++should have the same behavior, though it is not clear which one is
++correct.
+
+Miscellaneous differences
+~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -123,19 +114,3 @@
include::merge-strategies.txt[]
-
- diff --git a/t/t3433-rebase-options-compatibility.sh b/t/t3433-rebase-options-compatibility.sh
- --- a/t/t3433-rebase-options-compatibility.sh
- +++ b/t/t3433-rebase-options-compatibility.sh
-@@
- GIT_AUTHOR_DATE="1999-04-02T08:03:20+05:30"
- export GIT_AUTHOR_DATE
-
--# This is a special case in which both am and interactive backends
--# provide the same output. It was done intentionally because
--# both the backends fall short of optimal behaviour.
-+# This is a common case in which both am and interactive backends
-+# provide the same output with --ignore-whitespace.
- test_expect_success 'setup' '
- git checkout -b topic &&
- q_to_tab >file <<-\EOF &&
9: 7c3f2e07f3 = 12: 58e6e4ffb3 rebase: move incompatibility checks between backend options a bit earlier
10: 1df11f0b51 ! 13: 5478c730ac rebase: add an --am option
@@ -24,8 +24,8 @@
+See also INCOMPATIBLE OPTIONS below.
+
--empty={drop,keep,ask}::
- How to handle commits that become empty (because they contain a
- subset of already upstream changes) or start empty. With drop
+ How to handle commits that are not empty to start and are not
+ clean cherry-picks of any upstream commit, but which become
@@
Ensure at least <n> lines of surrounding context match before
and after each change. When fewer lines of surrounding
@@ -37,7 +37,7 @@
@@
--whitespace=<option>::
- This flag is passed to the 'git apply' program
+ These flags are passed to the 'git apply' program
(see linkgit:git-apply[1]) that applies the patch.
+ Implies --am.
+
@@ -48,9 +48,9 @@
The following options:
+ * --am
- * --whitespace
- * -C
-
+ * --committer-date-is-author-date
+ * --ignore-date
+ * --ignore-whitespace
diff --git a/builtin/rebase.c b/builtin/rebase.c
--- a/builtin/rebase.c
11: ff43593211 = 14: db5e29bd81 git-prompt: change the prompt for interactive-based rebases
-: ---------- > 15: 413e190ac9 rebase: drop '-i' from the reflog for interactive-based rebases
12: 99388f24e5 ! 16: 170be283a8 rebase tests: mark tests specific to the am-backend with --am
@@ -228,40 +228,6 @@
test_rebase_same_head_ $status_f $what_f $cmp_f " --merge --no-ff" "$*"
}
- diff --git a/t/t3433-rebase-options-compatibility.sh b/t/t3433-rebase-options-compatibility.sh
- --- a/t/t3433-rebase-options-compatibility.sh
- +++ b/t/t3433-rebase-options-compatibility.sh
-@@
- new line 2
- line 3
- EOF
-- test_must_fail git rebase main side &&
-+ test_must_fail git rebase --am main side &&
- git rebase --abort &&
-- git rebase --ignore-whitespace main side &&
-+ git rebase --am --ignore-whitespace main side &&
- test_cmp expect file
- '
-
-@@
-
- test_expect_success '--committer-date-is-author-date works with am backend' '
- git commit --amend &&
-- git rebase --committer-date-is-author-date HEAD^ &&
-+ git rebase --am --committer-date-is-author-date HEAD^ &&
- git show HEAD --pretty="format:%ai" >authortime &&
- git show HEAD --pretty="format:%ci" >committertime &&
- test_cmp authortime committertime
-@@
- # sets to +0530.
- test_expect_success '--ignore-date works with am backend' '
- git commit --amend --date="$GIT_AUTHOR_DATE" &&
-- git rebase --ignore-date HEAD^ &&
-+ git rebase --am --ignore-date HEAD^ &&
- git show HEAD --pretty="format:%ai" >authortime &&
- grep "+0000" authortime
- '
-
diff --git a/t/t5407-post-rewrite-hook.sh b/t/t5407-post-rewrite-hook.sh
--- a/t/t5407-post-rewrite-hook.sh
+++ b/t/t5407-post-rewrite-hook.sh
13: c2ba6317bf = 17: 1e3d4066c4 rebase tests: repeat some tests using the merge backend instead of am
14: 8bec6df51a = 18: 9b4ac83d2d rebase: make the backend configurable via config setting
15: 044853fd61 = 19: 859a4a94d7 rebase: change the default backend from "am" to "merge"
--
gitgitgadget
^ permalink raw reply [flat|nested] 161+ messages in thread
* [PATCH v4 01/19] git-rebase.txt: update description of --allow-empty-message
2020-01-16 6:14 ` [PATCH v4 00/19] rebase: make the default backend configurable Elijah Newren via GitGitGadget
@ 2020-01-16 6:14 ` Elijah Newren via GitGitGadget
2020-02-09 15:59 ` Phillip Wood
2020-01-16 6:14 ` [PATCH v4 02/19] t3404: directly test the behavior of interest Elijah Newren via GitGitGadget
` (19 subsequent siblings)
20 siblings, 1 reply; 161+ messages in thread
From: Elijah Newren via GitGitGadget @ 2020-01-16 6:14 UTC (permalink / raw)
To: git
Cc: Johannes.Schindelin, phillip.wood, liu.denton, gitster, plroskin,
alban.gruin, szeder.dev, jrnieder, emilyshaffer, Elijah Newren,
Elijah Newren
From: Elijah Newren <newren@gmail.com>
Commit b00bf1c9a8dd ("git-rebase: make --allow-empty-message the
default", 2018-06-27) made --allow-empty-message the default and thus
turned --allow-empty-message into a no-op but did not update the
documentation to reflect this. Update the documentation now, and hide
the option from the normal -h output since it is not useful.
Signed-off-by: Elijah Newren <newren@gmail.com>
---
Documentation/git-rebase.txt | 7 ++++---
builtin/rebase.c | 12 +++++++-----
2 files changed, 11 insertions(+), 8 deletions(-)
diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index 0c4f038dd6..c83be7ffc2 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -265,9 +265,10 @@ See also INCOMPATIBLE OPTIONS below.
See also INCOMPATIBLE OPTIONS below.
--allow-empty-message::
- By default, rebasing commits with an empty message will fail.
- This option overrides that behavior, allowing commits with empty
- messages to be rebased.
+ No-op. Rebasing commits with an empty message used to fail
+ and this option would override that behavior, allowing commits
+ with empty messages to be rebased. Now commits with an empty
+ message do not cause rebasing to halt.
+
See also INCOMPATIBLE OPTIONS below.
diff --git a/builtin/rebase.c b/builtin/rebase.c
index 8081741f8a..faa4e0d406 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -453,8 +453,9 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)
OPT_NEGBIT(0, "ff", &opts.flags, N_("allow fast-forward"),
REBASE_FORCE),
OPT_BOOL(0, "keep-empty", &opts.keep_empty, N_("keep empty commits")),
- OPT_BOOL(0, "allow-empty-message", &opts.allow_empty_message,
- N_("allow commits with empty messages")),
+ OPT_BOOL_F(0, "allow-empty-message", &opts.allow_empty_message,
+ N_("allow commits with empty messages"),
+ PARSE_OPT_HIDDEN),
OPT_BOOL(0, "rebase-merges", &opts.rebase_merges, N_("rebase merge commits")),
OPT_BOOL(0, "rebase-cousins", &opts.rebase_cousins,
N_("keep original branch points of cousins")),
@@ -1495,9 +1496,10 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
OPT_STRING_LIST('x', "exec", &exec, N_("exec"),
N_("add exec lines after each commit of the "
"editable list")),
- OPT_BOOL(0, "allow-empty-message",
- &options.allow_empty_message,
- N_("allow rebasing commits with empty messages")),
+ OPT_BOOL_F(0, "allow-empty-message",
+ &options.allow_empty_message,
+ N_("allow rebasing commits with empty messages"),
+ PARSE_OPT_HIDDEN),
{OPTION_STRING, 'r', "rebase-merges", &rebase_merges,
N_("mode"),
N_("try to rebase merges instead of skipping them"),
--
gitgitgadget
^ permalink raw reply related [flat|nested] 161+ messages in thread
* [PATCH v4 02/19] t3404: directly test the behavior of interest
2020-01-16 6:14 ` [PATCH v4 00/19] rebase: make the default backend configurable Elijah Newren via GitGitGadget
2020-01-16 6:14 ` [PATCH v4 01/19] git-rebase.txt: update description of --allow-empty-message Elijah Newren via GitGitGadget
@ 2020-01-16 6:14 ` Elijah Newren via GitGitGadget
2020-01-16 6:14 ` [PATCH v4 03/19] rebase (interactive-backend): make --keep-empty the default Elijah Newren via GitGitGadget
` (18 subsequent siblings)
20 siblings, 0 replies; 161+ messages in thread
From: Elijah Newren via GitGitGadget @ 2020-01-16 6:14 UTC (permalink / raw)
To: git
Cc: Johannes.Schindelin, phillip.wood, liu.denton, gitster, plroskin,
alban.gruin, szeder.dev, jrnieder, emilyshaffer, Elijah Newren,
Elijah Newren
From: Elijah Newren <newren@gmail.com>
t3404.3 is a simple test added by commit d078c3910689 ("t3404: todo list
with commented-out commands only aborts", 2018-08-10) which was designed
to test a todo list that only contained commented-out commands. There
were two problems with this test: (1) its title did not reflect the
purpose of the test, and (2) it tested the desired behavior through a
side-effect of other functionality instead of directly testing the
desired behavior discussed in the commit message.
Modify the test to directly test the desired behavior and update the
test title.
Signed-off-by: Elijah Newren <newren@gmail.com>
---
t/t3404-rebase-interactive.sh | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
index ae6e55ce79..c41531f349 100755
--- a/t/t3404-rebase-interactive.sh
+++ b/t/t3404-rebase-interactive.sh
@@ -72,15 +72,16 @@ test_expect_success 'rebase --keep-empty' '
test_line_count = 6 actual
'
-test_expect_success 'rebase -i with empty HEAD' '
+test_expect_success 'rebase -i with empty todo list' '
cat >expect <<-\EOF &&
error: nothing to do
EOF
(
set_fake_editor &&
- test_must_fail env FAKE_LINES="1 exec_true" \
- git rebase -i HEAD^ >actual 2>&1
+ test_must_fail env FAKE_LINES="#" \
+ git rebase -i HEAD^ >output 2>&1
) &&
+ tail -n 1 output >actual && # Ignore output about changing todo list
test_i18ncmp expect actual
'
--
gitgitgadget
^ permalink raw reply related [flat|nested] 161+ messages in thread
* [PATCH v4 03/19] rebase (interactive-backend): make --keep-empty the default
2020-01-16 6:14 ` [PATCH v4 00/19] rebase: make the default backend configurable Elijah Newren via GitGitGadget
2020-01-16 6:14 ` [PATCH v4 01/19] git-rebase.txt: update description of --allow-empty-message Elijah Newren via GitGitGadget
2020-01-16 6:14 ` [PATCH v4 02/19] t3404: directly test the behavior of interest Elijah Newren via GitGitGadget
@ 2020-01-16 6:14 ` Elijah Newren via GitGitGadget
2020-02-09 15:59 ` Phillip Wood
2020-01-16 6:14 ` [PATCH v4 04/19] rebase (interactive-backend): fix handling of commits that become empty Elijah Newren via GitGitGadget
` (17 subsequent siblings)
20 siblings, 1 reply; 161+ messages in thread
From: Elijah Newren via GitGitGadget @ 2020-01-16 6:14 UTC (permalink / raw)
To: git
Cc: Johannes.Schindelin, phillip.wood, liu.denton, gitster, plroskin,
alban.gruin, szeder.dev, jrnieder, emilyshaffer, Elijah Newren,
Elijah Newren
From: Elijah Newren <newren@gmail.com>
Different rebase backends have different treatment for commits which
start empty (i.e. have no changes relative to their parent), and the
--keep-empty option was added at some point to allow adjusting behavior
for the interactive backend. The handling of commits which start empty
is actually quite similar to commit b00bf1c9a8dd (git-rebase: make
--allow-empty-message the default, 2018-06-27), which pointed out that
the behavior for various backends is often more happenstance than
design. The specific change made in that commit is actually quite
relevant as well and much of the logic there directly applies here.
It makes a lot of sense in 'git commit' to error out on the creation of
empty commits, unless an override flag is provided. However, once
someone determines that there is a rare case that merits using the
manual override to create such a commit, it is somewhere between
annoying and harmful to have to take extra steps to keep such
intentional commits around. Granted, empty commits are quite rare,
which is why handling of them doesn't get considered much and folks tend
to defer to existing (accidental) behavior and assume there was a reason
for it, leading them to just add flags (--keep-empty in this case) that
allow them to override the bad defaults. Fix the interactive backend so
that --keep-empty is the default, much like we did with
--allow-empty-message. The am backend should also be fixed to have
--keep-empty semantics for commits that start empty, but that is not
included in this patch other than a testcase documenting the failure.
Note that there was one test in t3421 which appears to have been written
expecting --keep-empty to not be the default as correct behavior. This
test was introduced in commit 00b8be5a4d38 ("add tests for rebasing of
empty commits", 2013-06-06), which was part of a series focusing on
rebase topology and which had an interesting original cover letter at
https://lore.kernel.org/git/1347949878-12578-1-git-send-email-martinvonz@gmail.com/
which noted
Your input especially appreciated on whether you agree with the
intent of the test cases.
and then went into a long example about how one of the many tests added
had several questions about whether it was correct. As such, I believe
most the tests in that series were about testing rebase topology with as
many different flags as possible and were not trying to state in general
how those flags should behave otherwise.
Signed-off-by: Elijah Newren <newren@gmail.com>
---
Documentation/git-rebase.txt | 24 ++++++-----
builtin/rebase.c | 28 ++++++++----
rebase-interactive.c | 7 +--
rebase-interactive.h | 2 +-
sequencer.c | 20 +++------
sequencer.h | 2 +-
t/t3421-rebase-topology-linear.sh | 16 +++----
t/t3424-rebase-empty.sh | 72 +++++++++++++++++++++++++++++++
t/t3427-rebase-subtree.sh | 12 +++---
9 files changed, 128 insertions(+), 55 deletions(-)
create mode 100755 t/t3424-rebase-empty.sh
diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index c83be7ffc2..1d19542d79 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -259,10 +259,13 @@ See also INCOMPATIBLE OPTIONS below.
unchanged as a result.
--keep-empty::
- Keep the commits that do not change anything from its
- parents in the result.
+ No-op. Rebasing commits that started empty (had no change
+ relative to their parent) used to fail and this option would
+ override that behavior, allowing commits with empty changes to
+ be rebased. Now commits with no changes do not cause rebasing
+ to halt.
+
-See also INCOMPATIBLE OPTIONS below.
+See also BEHAVIORAL DIFFERENCES and INCOMPATIBLE OPTIONS below.
--allow-empty-message::
No-op. Rebasing commits with an empty message used to fail
@@ -577,15 +580,14 @@ There are some subtle differences how the backends behave.
Empty commits
~~~~~~~~~~~~~
-The am backend drops any "empty" commits, regardless of whether the
-commit started empty (had no changes relative to its parent to
-start with) or ended empty (all changes were already applied
-upstream in other commits).
+The am backend unfortunately drops intentionally empty commits, i.e.
+commits that started empty, though these are rare in practice. It
+also drops commits that become empty and has no option for controlling
+this behavior.
-The interactive backend drops commits by default that
-started empty and halts if it hits a commit that ended up empty.
-The `--keep-empty` option exists for the interactive backend to allow
-it to keep commits that started empty.
+The interactive backend keeps intentionally empty commits.
+Unfortunately, it always halts whenever it runs across a commit that
+becomes empty, even when the rebase is not explicitly interactive.
Directory rename detection
~~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/builtin/rebase.c b/builtin/rebase.c
index faa4e0d406..537b3241ce 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -77,7 +77,6 @@ struct rebase_options {
const char *action;
int signoff;
int allow_rerere_autoupdate;
- int keep_empty;
int autosquash;
char *gpg_sign_opt;
int autostash;
@@ -375,7 +374,6 @@ static int run_rebase_interactive(struct rebase_options *opts,
git_config_get_bool("rebase.abbreviatecommands", &abbreviate_commands);
- flags |= opts->keep_empty ? TODO_LIST_KEEP_EMPTY : 0;
flags |= abbreviate_commands ? TODO_LIST_ABBREVIATE_CMDS : 0;
flags |= opts->rebase_merges ? TODO_LIST_REBASE_MERGES : 0;
flags |= opts->rebase_cousins > 0 ? TODO_LIST_REBASE_COUSINS : 0;
@@ -439,6 +437,17 @@ static int run_rebase_interactive(struct rebase_options *opts,
return ret;
}
+static int parse_opt_keep_empty(const struct option *opt, const char *arg,
+ int unset)
+{
+ struct rebase_options *opts = opt->value;
+
+ BUG_ON_OPT_ARG(arg);
+
+ opts->type = REBASE_INTERACTIVE;
+ return 0;
+}
+
static const char * const builtin_rebase_interactive_usage[] = {
N_("git rebase--interactive [<options>]"),
NULL
@@ -452,7 +461,10 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)
struct option options[] = {
OPT_NEGBIT(0, "ff", &opts.flags, N_("allow fast-forward"),
REBASE_FORCE),
- OPT_BOOL(0, "keep-empty", &opts.keep_empty, N_("keep empty commits")),
+ { OPTION_CALLBACK, 'k', "keep-empty", &options, NULL,
+ N_("(DEPRECATED) keep empty commits"),
+ PARSE_OPT_NOARG | PARSE_OPT_HIDDEN,
+ parse_opt_keep_empty },
OPT_BOOL_F(0, "allow-empty-message", &opts.allow_empty_message,
N_("allow commits with empty messages"),
PARSE_OPT_HIDDEN),
@@ -1145,7 +1157,6 @@ static int run_specific_rebase(struct rebase_options *opts, enum action action)
opts->allow_rerere_autoupdate ?
opts->allow_rerere_autoupdate == RERERE_AUTOUPDATE ?
"--rerere-autoupdate" : "--no-rerere-autoupdate" : "");
- add_var(&script_snippet, "keep_empty", opts->keep_empty ? "yes" : "");
add_var(&script_snippet, "autosquash", opts->autosquash ? "t" : "");
add_var(&script_snippet, "gpg_sign_opt", opts->gpg_sign_opt);
add_var(&script_snippet, "cmd", opts->cmd);
@@ -1483,8 +1494,10 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
"ignoring them"),
REBASE_PRESERVE_MERGES, PARSE_OPT_HIDDEN),
OPT_RERERE_AUTOUPDATE(&options.allow_rerere_autoupdate),
- OPT_BOOL('k', "keep-empty", &options.keep_empty,
- N_("preserve empty commits during rebase")),
+ { OPTION_CALLBACK, 'k', "keep-empty", &options, NULL,
+ N_("(DEPRECATED) keep empty commits"),
+ PARSE_OPT_NOARG | PARSE_OPT_HIDDEN,
+ parse_opt_keep_empty },
OPT_BOOL(0, "autosquash", &options.autosquash,
N_("move commits that begin with "
"squash!/fixup! under -i")),
@@ -1747,9 +1760,6 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
if (!(options.flags & REBASE_NO_QUIET))
argv_array_push(&options.git_am_opts, "-q");
- if (options.keep_empty)
- imply_interactive(&options, "--keep-empty");
-
if (gpg_sign) {
free(options.gpg_sign_opt);
options.gpg_sign_opt = xstrfmt("-S%s", gpg_sign);
diff --git a/rebase-interactive.c b/rebase-interactive.c
index aa18ae82b7..7e7b4061bf 100644
--- a/rebase-interactive.c
+++ b/rebase-interactive.c
@@ -28,7 +28,7 @@ static enum missing_commit_check_level get_missing_commit_check_level(void)
return MISSING_COMMIT_CHECK_IGNORE;
}
-void append_todo_help(unsigned keep_empty, int command_count,
+void append_todo_help(int command_count,
const char *shortrevisions, const char *shortonto,
struct strbuf *buf)
{
@@ -80,11 +80,6 @@ void append_todo_help(unsigned keep_empty, int command_count,
"the rebase will be aborted.\n\n");
strbuf_add_commented_lines(buf, msg, strlen(msg));
-
- if (!keep_empty) {
- msg = _("Note that empty commits are commented out");
- strbuf_add_commented_lines(buf, msg, strlen(msg));
- }
}
int edit_todo_list(struct repository *r, struct todo_list *todo_list,
diff --git a/rebase-interactive.h b/rebase-interactive.h
index 44dbb06311..05354ca341 100644
--- a/rebase-interactive.h
+++ b/rebase-interactive.h
@@ -5,7 +5,7 @@ struct strbuf;
struct repository;
struct todo_list;
-void append_todo_help(unsigned keep_empty, int command_count,
+void append_todo_help(int command_count,
const char *shortrevisions, const char *shortonto,
struct strbuf *buf);
int edit_todo_list(struct repository *r, struct todo_list *todo_list,
diff --git a/sequencer.c b/sequencer.c
index b9dbf1adb0..c21fc202b1 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -1489,7 +1489,7 @@ 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:
@@ -1513,10 +1513,10 @@ 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)
+ originally_empty = is_original_commit_empty(commit);
+ if (originally_empty < 0)
+ return originally_empty;
+ if (!originally_empty)
return 0;
else
return 1;
@@ -4566,7 +4566,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 +4628,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 +4794,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 +4829,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 +4969,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);
diff --git a/sequencer.h b/sequencer.h
index 9f9ae291e3..c165e0ff25 100644
--- a/sequencer.h
+++ b/sequencer.h
@@ -132,7 +132,7 @@ int sequencer_rollback(struct repository *repo, struct replay_opts *opts);
int sequencer_skip(struct repository *repo, struct replay_opts *opts);
int sequencer_remove_state(struct replay_opts *opts);
-#define TODO_LIST_KEEP_EMPTY (1U << 0)
+/* #define TODO_LIST_KEEP_EMPTY (1U << 0) */ /* No longer used */
#define TODO_LIST_SHORTEN_IDS (1U << 1)
#define TODO_LIST_ABBREVIATE_CMDS (1U << 2)
#define TODO_LIST_REBASE_MERGES (1U << 3)
diff --git a/t/t3421-rebase-topology-linear.sh b/t/t3421-rebase-topology-linear.sh
index 325072b0a3..57334dca7e 100755
--- a/t/t3421-rebase-topology-linear.sh
+++ b/t/t3421-rebase-topology-linear.sh
@@ -205,17 +205,17 @@ test_expect_success 'setup of linear history for empty commit tests' '
test_run_rebase () {
result=$1
shift
- test_expect_$result "rebase $* drops empty commit" "
+ test_expect_$result "rebase $* keeps begin-empty commits" "
reset_rebase &&
- git rebase $* c l &&
- test_cmp_rev c HEAD~2 &&
- test_linear_range 'd l' c..
+ git rebase $* j l &&
+ test_cmp_rev c HEAD~4 &&
+ test_linear_range 'j d k l' c..
"
}
-test_run_rebase success ''
+test_run_rebase failure ''
test_run_rebase success -m
test_run_rebase success -i
-test_have_prereq !REBASE_P || test_run_rebase success -p
+test_have_prereq !REBASE_P || test_run_rebase failure -p
test_run_rebase () {
result=$1
@@ -230,7 +230,7 @@ test_run_rebase () {
test_run_rebase success ''
test_run_rebase success -m
test_run_rebase success -i
-test_have_prereq !REBASE_P || test_run_rebase failure -p
+test_have_prereq !REBASE_P || test_run_rebase success -p
test_run_rebase () {
result=$1
@@ -245,7 +245,7 @@ test_run_rebase () {
test_run_rebase success ''
test_run_rebase success -m
test_run_rebase success -i
-test_have_prereq !REBASE_P || test_run_rebase failure -p
+test_have_prereq !REBASE_P || test_run_rebase success -p
test_run_rebase success --rebase-merges
# m
diff --git a/t/t3424-rebase-empty.sh b/t/t3424-rebase-empty.sh
new file mode 100755
index 0000000000..22d97e143b
--- /dev/null
+++ b/t/t3424-rebase-empty.sh
@@ -0,0 +1,72 @@
+#!/bin/sh
+
+test_description='git rebase of commits that start or become empty'
+
+. ./test-lib.sh
+
+test_expect_success 'setup test repository' '
+ test_write_lines 1 2 3 4 5 6 7 8 9 10 >numbers &&
+ test_write_lines A B C D E F G H I J >letters &&
+ git add numbers letters &&
+ git commit -m A &&
+
+ git branch upstream &&
+ git branch localmods &&
+
+ git checkout upstream &&
+ test_write_lines A B C D E >letters &&
+ git add letters &&
+ git commit -m B &&
+
+ test_write_lines 1 2 3 4 five 6 7 8 9 ten >numbers &&
+ git add numbers &&
+ git commit -m C &&
+
+ git checkout localmods &&
+ test_write_lines 1 2 3 4 five 6 7 8 9 10 >numbers &&
+ git add numbers &&
+ git commit -m C2 &&
+
+ git commit --allow-empty -m D &&
+
+ test_write_lines A B C D E >letters &&
+ git add letters &&
+ git commit -m "Five letters ought to be enough for anybody"
+'
+
+test_expect_failure 'rebase (am-backend) with a variety of empty commits' '
+ test_when_finished "git rebase --abort" &&
+ git checkout -B testing localmods &&
+ # rebase (--am) should not drop commits that start empty
+ git rebase upstream &&
+
+ test_write_lines D C B A >expect &&
+ git log --format=%s >actual &&
+ test_cmp expect actual
+'
+
+test_expect_failure 'rebase --merge with a variety of empty commits' '
+ test_when_finished "git rebase --abort" &&
+ git checkout -B testing localmods &&
+ # rebase --merge should not halt on the commit that becomes empty
+ git rebase --merge upstream &&
+
+ test_write_lines D C B A >expect &&
+ git log --format=%s >actual &&
+ test_cmp expect actual
+'
+
+GIT_SEQUENCE_EDITOR=: && export GIT_SEQUENCE_EDITOR
+
+test_expect_success 'rebase --interactive with a variety of empty commits' '
+ git checkout -B testing localmods &&
+ test_must_fail git rebase --interactive upstream &&
+
+ git rebase --skip &&
+
+ test_write_lines D C B A >expect &&
+ git log --format=%s >actual &&
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t3427-rebase-subtree.sh b/t/t3427-rebase-subtree.sh
index bec48e6a1f..8dceef61cf 100755
--- a/t/t3427-rebase-subtree.sh
+++ b/t/t3427-rebase-subtree.sh
@@ -85,23 +85,23 @@ test_expect_failure REBASE_P 'Rebase -Xsubtree --keep-empty --preserve-merges --
verbose test "$(commit_message HEAD)" = "Empty commit"
'
-test_expect_success 'Rebase -Xsubtree --keep-empty --onto commit' '
+test_expect_success 'Rebase -Xsubtree --onto commit' '
reset_rebase &&
git checkout -b rebase-onto to-rebase &&
- test_must_fail git rebase -Xsubtree=files_subtree --keep-empty --onto files-master master &&
+ test_must_fail git rebase -Xsubtree=files_subtree --onto files-master master &&
: first pick results in no changes &&
- git rebase --continue &&
+ git rebase --skip &&
verbose test "$(commit_message HEAD~2)" = "master4" &&
verbose test "$(commit_message HEAD~)" = "files_subtree/master5" &&
verbose test "$(commit_message HEAD)" = "Empty commit"
'
-test_expect_success 'Rebase -Xsubtree --keep-empty --rebase-merges --onto commit' '
+test_expect_success 'Rebase -Xsubtree --rebase-merges --onto commit' '
reset_rebase &&
git checkout -b rebase-merges-onto to-rebase &&
- test_must_fail git rebase -Xsubtree=files_subtree --keep-empty --rebase-merges --onto files-master --root &&
+ test_must_fail git rebase -Xsubtree=files_subtree --rebase-merges --onto files-master --root &&
: first pick results in no changes &&
- git rebase --continue &&
+ git rebase --skip &&
verbose test "$(commit_message HEAD~2)" = "master4" &&
verbose test "$(commit_message HEAD~)" = "files_subtree/master5" &&
verbose test "$(commit_message HEAD)" = "Empty commit"
--
gitgitgadget
^ permalink raw reply related [flat|nested] 161+ messages in thread
* [PATCH v4 04/19] rebase (interactive-backend): fix handling of commits that become empty
2020-01-16 6:14 ` [PATCH v4 00/19] rebase: make the default backend configurable Elijah Newren via GitGitGadget
` (2 preceding siblings ...)
2020-01-16 6:14 ` [PATCH v4 03/19] rebase (interactive-backend): make --keep-empty the default Elijah Newren via GitGitGadget
@ 2020-01-16 6:14 ` Elijah Newren via GitGitGadget
2020-02-10 14:27 ` Phillip Wood
2020-01-16 6:14 ` [PATCH v4 05/19] t3406: simplify an already simple test Elijah Newren via GitGitGadget
` (16 subsequent siblings)
20 siblings, 1 reply; 161+ messages in thread
From: Elijah Newren via GitGitGadget @ 2020-01-16 6:14 UTC (permalink / raw)
To: git
Cc: Johannes.Schindelin, phillip.wood, liu.denton, gitster, plroskin,
alban.gruin, szeder.dev, jrnieder, emilyshaffer, Elijah Newren,
Elijah Newren
From: Elijah Newren <newren@gmail.com>
As established in the previous commit and commit b00bf1c9a8dd
(git-rebase: make --allow-empty-message the default, 2018-06-27), the
behavior for rebase with different backends in various edge or corner
cases is often more happenstance than design. This commit addresses
another such corner case: commits which "become empty".
A careful reader may note that there are two types of commits which would
become empty due to a rebase:
* [clean cherry-pick] Commits which are clean cherry-picks of upstream
commits, as determined by `git log --cherry-mark ...`. Re-applying
these commits would result in an empty set of changes and a
duplicative commit message; i.e. these are commits that have
"already been applied" upstream.
* [become empty] Commits which are not empty to start, are not clean
cherry-picks of upstream commits, but which still become empty after
being rebased. This happens e.g. when a commit has changes which
are a strict subset of the changes in an upstream commit, or when
the changes of a commit can be found spread across or among several
upstream commits.
Clearly, in both cases the changes in the commit in question are found
upstream already, but the commit message may not be in the latter case.
When cherry-mark can determine a commit is already upstream, then
because of how cherry-mark works this means the upstream commit message
was about the *exact* same set of changes. Thus, the commit messages
can be assumed to be fully interchangeable (and are in fact likely to be
completely identical). As such, the clean cherry-pick case represents a
case when there is no information to be gained by keeping the extra
commit around. All rebase types have always dropped these commits, and
no one to my knowledge has ever requested that we do otherwise.
For many of the become empty cases (and likely even most), we will also
be able to drop the commit without loss of information -- but this isn't
quite always the case. Since these commits represent cases that were
not clean cherry-picks, there is no upstream commit message explaining
the same set of changes. Projects with good commit message hygiene will
likely have the explanation from our commit message contained within or
spread among the relevant upstream commits, but not all projects run
that way. As such, the commit message of the commit being rebased may
have reasoning that suggests additional changes that should be made to
adapt to the new base, or it may have information that someone wants to
add as a note to another commit, or perhaps someone even wants to create
an empty commit with the commit message as-is.
Junio commented on the "become-empty" types of commits as follows[1]:
WRT a change that ends up being empty (as opposed to a change that
is empty from the beginning), I'd think that the current behaviour
is desireable one. "am" based rebase is solely to transplant an
existing history and want to stop much less than "interactive" one
whose purpose is to polish a series before making it publishable,
and asking for confirmation ("this has become empty--do you want to
drop it?") is more appropriate from the workflow point of view.
[1] https://lore.kernel.org/git/xmqqfu1fswdh.fsf@gitster-ct.c.googlers.com/
I would simply add that his arguments for "am"-based rebases actually
apply to all non-explicitly-interactive rebases. Also, since we are
stating that different cases should have different defaults, it may be
worth providing a flag to allow users to select which behavior they want
for these commits.
Introduce a new command line flag for selecting the desired behavior:
--empty={drop,keep,ask}
with the definitions:
drop: drop commits which become empty
keep: keep commits which become empty
ask: provide the user a chance to interact and pick what to do with
commits which become empty on a case-by-case basis
In line with Junio's suggestion, if the --empty flag is not specified,
pick defaults as follows:
explicitly interactive: ask
otherwise: drop
Signed-off-by: Elijah Newren <newren@gmail.com>
---
Documentation/git-rebase.txt | 27 ++++++++++++++++---
builtin/rebase.c | 52 ++++++++++++++++++++++++++++++++++++
sequencer.c | 48 +++++++++++++++++++++++++--------
sequencer.h | 1 +
t/t3424-rebase-empty.sh | 50 +++++++++++++++++++++++++++++-----
t/t3427-rebase-subtree.sh | 8 +++---
6 files changed, 161 insertions(+), 25 deletions(-)
diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index 1d19542d79..551a91d764 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -258,6 +258,22 @@ See also INCOMPATIBLE OPTIONS below.
original branch. The index and working tree are also left
unchanged as a result.
+--empty={drop,keep,ask}::
+ How to handle commits that are not empty to start and are not
+ clean cherry-picks of any upstream commit, but which become
+ empty after rebasing (because they contain a subset of already
+ upstream changes). With drop (the default), commits that
+ become empty are dropped. With keep, such commits are kept.
+ With ask (implied by --interactive), the rebase will halt when
+ an empty commit is applied allowing you to choose whether to
+ drop it, edit files more, or just commit the empty changes.
++
+Note that commits which start empty are kept, and commits which are
+clean cherry-picks (as determined by `git log --cherry-mark ...`) are
+always dropped.
++
+See also INCOMPATIBLE OPTIONS below.
+
--keep-empty::
No-op. Rebasing commits that started empty (had no change
relative to their parent) used to fail and this option would
@@ -561,6 +577,7 @@ are incompatible with the following options:
* --interactive
* --exec
* --keep-empty
+ * --empty=
* --edit-todo
* --root when used in combination with --onto
@@ -569,6 +586,7 @@ In addition, the following pairs of options are incompatible:
* --preserve-merges and --interactive
* --preserve-merges and --signoff
* --preserve-merges and --rebase-merges
+ * --preserve-merges and --empty=
* --keep-base and --onto
* --keep-base and --root
@@ -585,9 +603,12 @@ commits that started empty, though these are rare in practice. It
also drops commits that become empty and has no option for controlling
this behavior.
-The interactive backend keeps intentionally empty commits.
-Unfortunately, it always halts whenever it runs across a commit that
-becomes empty, even when the rebase is not explicitly interactive.
+The interactive backend keeps intentionally empty commits. Similar to
+the am backend, by default the interactive backend drops commits that
+become empty unless -i/--interactive is specified (in which case it
+stops and asks the user what to do). The interactive backend also has
+an --empty={drop,keep,ask} option for changing the behavior of
+handling commits that become empty.
Directory rename detection
~~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/builtin/rebase.c b/builtin/rebase.c
index 537b3241ce..c299869e7b 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -50,8 +50,16 @@ enum rebase_type {
REBASE_PRESERVE_MERGES
};
+enum empty_type {
+ EMPTY_UNSPECIFIED = -1,
+ EMPTY_DROP,
+ EMPTY_KEEP,
+ EMPTY_ASK
+};
+
struct rebase_options {
enum rebase_type type;
+ enum empty_type empty;
const char *state_dir;
struct commit *upstream;
const char *upstream_name;
@@ -91,6 +99,7 @@ struct rebase_options {
#define REBASE_OPTIONS_INIT { \
.type = REBASE_UNSPECIFIED, \
+ .empty = EMPTY_UNSPECIFIED, \
.flags = REBASE_NO_QUIET, \
.git_am_opts = ARGV_ARRAY_INIT, \
.git_format_patch_opt = STRBUF_INIT \
@@ -109,6 +118,8 @@ static struct replay_opts get_replay_opts(const struct rebase_options *opts)
replay.allow_rerere_auto = opts->allow_rerere_autoupdate;
replay.allow_empty = 1;
replay.allow_empty_message = opts->allow_empty_message;
+ replay.drop_redundant_commits = (opts->empty == EMPTY_DROP);
+ replay.keep_redundant_commits = (opts->empty == EMPTY_KEEP);
replay.verbose = opts->flags & REBASE_VERBOSE;
replay.reschedule_failed_exec = opts->reschedule_failed_exec;
replay.gpg_sign = xstrdup_or_null(opts->gpg_sign_opt);
@@ -444,6 +455,10 @@ static int parse_opt_keep_empty(const struct option *opt, const char *arg,
BUG_ON_OPT_ARG(arg);
+ /*
+ * If we ever want to remap --keep-empty to --empty=keep, insert:
+ * opts->empty = unset ? EMPTY_UNSPECIFIED : EMPTY_KEEP;
+ */
opts->type = REBASE_INTERACTIVE;
return 0;
}
@@ -1350,6 +1365,29 @@ static int parse_opt_interactive(const struct option *opt, const char *arg,
return 0;
}
+static enum empty_type parse_empty_value(const char *value)
+{
+ if (!strcasecmp(value, "drop"))
+ return EMPTY_DROP;
+ else if (!strcasecmp(value, "keep"))
+ return EMPTY_KEEP;
+ else if (!strcasecmp(value, "ask"))
+ return EMPTY_ASK;
+
+ die(_("unrecognized empty type '%s'; valid values are \"drop\", \"keep\", and \"ask\"."), value);
+}
+
+static int parse_opt_empty(const struct option *opt, const char *arg, int unset)
+{
+ struct rebase_options *options = opt->value;
+ enum empty_type value = parse_empty_value(arg);
+
+ BUG_ON_OPT_NEG(unset);
+
+ options->empty = value;
+ return 0;
+}
+
static void NORETURN error_on_missing_default_upstream(void)
{
struct branch *current_branch = branch_get(NULL);
@@ -1494,6 +1532,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
"ignoring them"),
REBASE_PRESERVE_MERGES, PARSE_OPT_HIDDEN),
OPT_RERERE_AUTOUPDATE(&options.allow_rerere_autoupdate),
+ OPT_CALLBACK_F(0, "empty", &options, N_("{drop,keep,ask}"),
+ N_("how to handle empty commits"),
+ PARSE_OPT_NONEG, parse_opt_empty),
{ OPTION_CALLBACK, 'k', "keep-empty", &options, NULL,
N_("(DEPRECATED) keep empty commits"),
PARSE_OPT_NOARG | PARSE_OPT_HIDDEN,
@@ -1760,6 +1801,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
if (!(options.flags & REBASE_NO_QUIET))
argv_array_push(&options.git_am_opts, "-q");
+ if (options.empty != EMPTY_UNSPECIFIED)
+ imply_interactive(&options, "--empty");
+
if (gpg_sign) {
free(options.gpg_sign_opt);
options.gpg_sign_opt = xstrfmt("-S%s", gpg_sign);
@@ -1843,6 +1887,14 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
break;
}
+ if (options.empty == EMPTY_UNSPECIFIED) {
+ if (options.flags & REBASE_INTERACTIVE_EXPLICIT)
+ options.empty = EMPTY_ASK;
+ else if (exec.nr > 0)
+ options.empty = EMPTY_KEEP;
+ else
+ options.empty = EMPTY_DROP;
+ }
if (reschedule_failed_exec > 0 && !is_interactive(&options))
die(_("--reschedule-failed-exec requires "
"--exec or --interactive"));
diff --git a/sequencer.c b/sequencer.c
index c21fc202b1..354d0b5a38 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -158,6 +158,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)
{
@@ -1483,7 +1485,11 @@ 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,
@@ -1492,14 +1498,17 @@ static int allow_empty(struct repository *r,
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 */
@@ -1516,10 +1525,12 @@ static int allow_empty(struct repository *r,
originally_empty = is_original_commit_empty(commit);
if (originally_empty < 0)
return originally_empty;
- if (!originally_empty)
- return 0;
- else
+ if (originally_empty)
return 1;
+ else if (opts->drop_redundant_commits)
+ return 2;
+ else
+ return 0;
}
static struct {
@@ -1730,7 +1741,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) {
/*
@@ -1935,13 +1946,18 @@ 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, _("No changes -- Patch already applied.\n"));
+ } // 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
@@ -2495,6 +2511,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);
@@ -2574,6 +2596,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", "");
diff --git a/sequencer.h b/sequencer.h
index c165e0ff25..3b0ab9141f 100644
--- a/sequencer.h
+++ b/sequencer.h
@@ -39,6 +39,7 @@ struct replay_opts {
int allow_rerere_auto;
int allow_empty;
int allow_empty_message;
+ int drop_redundant_commits;
int keep_redundant_commits;
int verbose;
int quiet;
diff --git a/t/t3424-rebase-empty.sh b/t/t3424-rebase-empty.sh
index 22d97e143b..dcb4cb4751 100755
--- a/t/t3424-rebase-empty.sh
+++ b/t/t3424-rebase-empty.sh
@@ -34,7 +34,7 @@ test_expect_success 'setup test repository' '
git commit -m "Five letters ought to be enough for anybody"
'
-test_expect_failure 'rebase (am-backend) with a variety of empty commits' '
+test_expect_failure 'rebase (am-backend)' '
test_when_finished "git rebase --abort" &&
git checkout -B testing localmods &&
# rebase (--am) should not drop commits that start empty
@@ -45,11 +45,29 @@ test_expect_failure 'rebase (am-backend) with a variety of empty commits' '
test_cmp expect actual
'
-test_expect_failure 'rebase --merge with a variety of empty commits' '
- test_when_finished "git rebase --abort" &&
+test_expect_success 'rebase --merge --empty=drop' '
git checkout -B testing localmods &&
- # rebase --merge should not halt on the commit that becomes empty
- git rebase --merge upstream &&
+ git rebase --merge --empty=drop upstream &&
+
+ test_write_lines D C B A >expect &&
+ git log --format=%s >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'rebase --merge --empty=keep' '
+ git checkout -B testing localmods &&
+ git rebase --merge --empty=keep upstream &&
+
+ test_write_lines D C2 C B A >expect &&
+ git log --format=%s >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'rebase --merge --empty=ask' '
+ git checkout -B testing localmods &&
+ test_must_fail git rebase --merge --empty=ask upstream &&
+
+ git rebase --skip &&
test_write_lines D C B A >expect &&
git log --format=%s >actual &&
@@ -58,9 +76,27 @@ test_expect_failure 'rebase --merge with a variety of empty commits' '
GIT_SEQUENCE_EDITOR=: && export GIT_SEQUENCE_EDITOR
-test_expect_success 'rebase --interactive with a variety of empty commits' '
+test_expect_success 'rebase --interactive --empty=drop' '
+ git checkout -B testing localmods &&
+ git rebase --interactive --empty=drop upstream &&
+
+ test_write_lines D C B A >expect &&
+ git log --format=%s >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'rebase --interactive --empty=keep' '
+ git checkout -B testing localmods &&
+ git rebase --interactive --empty=keep upstream &&
+
+ test_write_lines D C2 C B A >expect &&
+ git log --format=%s >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'rebase --interactive --empty=ask' '
git checkout -B testing localmods &&
- test_must_fail git rebase --interactive upstream &&
+ test_must_fail git rebase --interactive --empty=ask upstream &&
git rebase --skip &&
diff --git a/t/t3427-rebase-subtree.sh b/t/t3427-rebase-subtree.sh
index 8dceef61cf..79e43a370b 100755
--- a/t/t3427-rebase-subtree.sh
+++ b/t/t3427-rebase-subtree.sh
@@ -85,10 +85,10 @@ test_expect_failure REBASE_P 'Rebase -Xsubtree --keep-empty --preserve-merges --
verbose test "$(commit_message HEAD)" = "Empty commit"
'
-test_expect_success 'Rebase -Xsubtree --onto commit' '
+test_expect_success 'Rebase -Xsubtree --empty=ask --onto commit' '
reset_rebase &&
git checkout -b rebase-onto to-rebase &&
- test_must_fail git rebase -Xsubtree=files_subtree --onto files-master master &&
+ test_must_fail git rebase -Xsubtree=files_subtree --empty=ask --onto files-master master &&
: first pick results in no changes &&
git rebase --skip &&
verbose test "$(commit_message HEAD~2)" = "master4" &&
@@ -96,10 +96,10 @@ test_expect_success 'Rebase -Xsubtree --onto commit' '
verbose test "$(commit_message HEAD)" = "Empty commit"
'
-test_expect_success 'Rebase -Xsubtree --rebase-merges --onto commit' '
+test_expect_success 'Rebase -Xsubtree --empty=ask --rebase-merges --onto commit' '
reset_rebase &&
git checkout -b rebase-merges-onto to-rebase &&
- test_must_fail git rebase -Xsubtree=files_subtree --rebase-merges --onto files-master --root &&
+ test_must_fail git rebase -Xsubtree=files_subtree --empty=ask --rebase-merges --onto files-master --root &&
: first pick results in no changes &&
git rebase --skip &&
verbose test "$(commit_message HEAD~2)" = "master4" &&
--
gitgitgadget
^ permalink raw reply related [flat|nested] 161+ messages in thread
* [PATCH v4 05/19] t3406: simplify an already simple test
2020-01-16 6:14 ` [PATCH v4 00/19] rebase: make the default backend configurable Elijah Newren via GitGitGadget
` (3 preceding siblings ...)
2020-01-16 6:14 ` [PATCH v4 04/19] rebase (interactive-backend): fix handling of commits that become empty Elijah Newren via GitGitGadget
@ 2020-01-16 6:14 ` Elijah Newren via GitGitGadget
2020-01-16 6:14 ` [PATCH v4 06/19] rebase, sequencer: remove the broken GIT_QUIET handling Elijah Newren via GitGitGadget
` (15 subsequent siblings)
20 siblings, 0 replies; 161+ messages in thread
From: Elijah Newren via GitGitGadget @ 2020-01-16 6:14 UTC (permalink / raw)
To: git
Cc: Johannes.Schindelin, phillip.wood, liu.denton, gitster, plroskin,
alban.gruin, szeder.dev, jrnieder, emilyshaffer, Elijah Newren,
Elijah Newren
From: Elijah Newren <newren@gmail.com>
When the merge backend was re-implemented on top of the interactive
backend, the output of rebase --merge changed a little. This change
allowed this test to be simplified, though it wasn't noticed until now.
Simplify the testcase a little.
Signed-off-by: Elijah Newren <newren@gmail.com>
---
t/t3406-rebase-message.sh | 7 ++-----
1 file changed, 2 insertions(+), 5 deletions(-)
diff --git a/t/t3406-rebase-message.sh b/t/t3406-rebase-message.sh
index b393e1e9fe..0c2c569f95 100755
--- a/t/t3406-rebase-message.sh
+++ b/t/t3406-rebase-message.sh
@@ -18,11 +18,8 @@ test_expect_success 'setup' '
'
test_expect_success 'rebase -m' '
- git rebase -m master >report &&
- >expect &&
- sed -n -e "/^Already applied: /p" \
- -e "/^Committed: /p" report >actual &&
- test_cmp expect actual
+ git rebase -m master >actual &&
+ test_must_be_empty actual
'
test_expect_success 'rebase against master twice' '
--
gitgitgadget
^ permalink raw reply related [flat|nested] 161+ messages in thread
* [PATCH v4 06/19] rebase, sequencer: remove the broken GIT_QUIET handling
2020-01-16 6:14 ` [PATCH v4 00/19] rebase: make the default backend configurable Elijah Newren via GitGitGadget
` (4 preceding siblings ...)
2020-01-16 6:14 ` [PATCH v4 05/19] t3406: simplify an already simple test Elijah Newren via GitGitGadget
@ 2020-01-16 6:14 ` Elijah Newren via GitGitGadget
2020-01-16 6:14 ` [PATCH v4 07/19] rebase: make sure to pass along the quiet flag to the sequencer Elijah Newren via GitGitGadget
` (14 subsequent siblings)
20 siblings, 0 replies; 161+ messages in thread
From: Elijah Newren via GitGitGadget @ 2020-01-16 6:14 UTC (permalink / raw)
To: git
Cc: Johannes.Schindelin, phillip.wood, liu.denton, gitster, plroskin,
alban.gruin, szeder.dev, jrnieder, emilyshaffer, Elijah Newren,
Elijah Newren
From: Elijah Newren <newren@gmail.com>
The GIT_QUIET environment variable was used to signal the non-am
backends that the rebase should perform quietly. The preserve-merges
backend does not make use of the quiet flag anywhere (other than to
write out its state whenever it writes state), and this mechanism was
broken in the conversion from shell to C. Since this environment
variable was specifically designed for scripts and the only backend that
would still use it is no longer a script, just gut this code.
A subsequent commit will fix --quiet for the interactive/merge backend
in a different way.
Signed-off-by: Elijah Newren <newren@gmail.com>
---
builtin/rebase.c | 6 ++----
sequencer.c | 6 ++----
2 files changed, 4 insertions(+), 8 deletions(-)
diff --git a/builtin/rebase.c b/builtin/rebase.c
index c299869e7b..57875485df 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -699,8 +699,8 @@ static int rebase_write_basic_state(struct rebase_options *opts)
opts->onto ? oid_to_hex(&opts->onto->object.oid) : "");
write_file(state_dir_path("orig-head", opts), "%s",
oid_to_hex(&opts->orig_head));
- write_file(state_dir_path("quiet", opts), "%s",
- opts->flags & REBASE_NO_QUIET ? "" : "t");
+ if (!(opts->flags & REBASE_NO_QUIET))
+ write_file(state_dir_path("quiet", opts), "%s", "");
if (opts->flags & REBASE_VERBOSE)
write_file(state_dir_path("verbose", opts), "%s", "");
if (opts->strategy)
@@ -1153,8 +1153,6 @@ static int run_specific_rebase(struct rebase_options *opts, enum action action)
add_var(&script_snippet, "revisions", opts->revisions);
add_var(&script_snippet, "restrict_revision", opts->restrict_revision ?
oid_to_hex(&opts->restrict_revision->object.oid) : NULL);
- add_var(&script_snippet, "GIT_QUIET",
- opts->flags & REBASE_NO_QUIET ? "" : "t");
sq_quote_argv_pretty(&buf, opts->git_am_opts.argv);
add_var(&script_snippet, "git_am_opt", buf.buf);
strbuf_release(&buf);
diff --git a/sequencer.c b/sequencer.c
index 354d0b5a38..078a68eaf3 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -2568,8 +2568,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)
@@ -2578,8 +2576,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)
--
gitgitgadget
^ permalink raw reply related [flat|nested] 161+ messages in thread
* [PATCH v4 07/19] rebase: make sure to pass along the quiet flag to the sequencer
2020-01-16 6:14 ` [PATCH v4 00/19] rebase: make the default backend configurable Elijah Newren via GitGitGadget
` (5 preceding siblings ...)
2020-01-16 6:14 ` [PATCH v4 06/19] rebase, sequencer: remove the broken GIT_QUIET handling Elijah Newren via GitGitGadget
@ 2020-01-16 6:14 ` Elijah Newren via GitGitGadget
2020-01-16 6:14 ` [PATCH v4 08/19] rebase: fix handling of restrict_revision Elijah Newren via GitGitGadget
` (13 subsequent siblings)
20 siblings, 0 replies; 161+ messages in thread
From: Elijah Newren via GitGitGadget @ 2020-01-16 6:14 UTC (permalink / raw)
To: git
Cc: Johannes.Schindelin, phillip.wood, liu.denton, gitster, plroskin,
alban.gruin, szeder.dev, jrnieder, emilyshaffer, Elijah Newren,
Elijah Newren
From: Elijah Newren <newren@gmail.com>
Signed-off-by: Elijah Newren <newren@gmail.com>
---
builtin/rebase.c | 3 ++-
t/t3400-rebase.sh | 8 +++++++-
2 files changed, 9 insertions(+), 2 deletions(-)
diff --git a/builtin/rebase.c b/builtin/rebase.c
index 57875485df..e8d518ac8d 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -120,6 +120,7 @@ static struct replay_opts get_replay_opts(const struct rebase_options *opts)
replay.allow_empty_message = opts->allow_empty_message;
replay.drop_redundant_commits = (opts->empty == EMPTY_DROP);
replay.keep_redundant_commits = (opts->empty == EMPTY_KEEP);
+ replay.quiet = !(opts->flags & REBASE_NO_QUIET);
replay.verbose = opts->flags & REBASE_VERBOSE;
replay.reschedule_failed_exec = opts->reschedule_failed_exec;
replay.gpg_sign = xstrdup_or_null(opts->gpg_sign_opt);
@@ -1476,7 +1477,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
N_("allow pre-rebase hook to run")),
OPT_NEGBIT('q', "quiet", &options.flags,
N_("be quiet. implies --no-stat"),
- REBASE_NO_QUIET| REBASE_VERBOSE | REBASE_DIFFSTAT),
+ REBASE_NO_QUIET | REBASE_VERBOSE | REBASE_DIFFSTAT),
OPT_BIT('v', "verbose", &options.flags,
N_("display a diffstat of what changed upstream"),
REBASE_NO_QUIET | REBASE_VERBOSE | REBASE_DIFFSTAT),
diff --git a/t/t3400-rebase.sh b/t/t3400-rebase.sh
index 221b35f2df..79762b989a 100755
--- a/t/t3400-rebase.sh
+++ b/t/t3400-rebase.sh
@@ -206,12 +206,18 @@ test_expect_success 'cherry-picked commits and fork-point work together' '
test_cmp expect D
'
-test_expect_success 'rebase -q is quiet' '
+test_expect_success 'rebase --am -q is quiet' '
git checkout -b quiet topic &&
git rebase -q master >output.out 2>&1 &&
test_must_be_empty output.out
'
+test_expect_success 'rebase --merge -q is quiet' '
+ git checkout -B quiet topic &&
+ git rebase --merge -q master >output.out 2>&1 &&
+ test_must_be_empty output.out
+'
+
test_expect_success 'Rebase a commit that sprinkles CRs in' '
(
echo "One" &&
--
gitgitgadget
^ permalink raw reply related [flat|nested] 161+ messages in thread
* [PATCH v4 08/19] rebase: fix handling of restrict_revision
2020-01-16 6:14 ` [PATCH v4 00/19] rebase: make the default backend configurable Elijah Newren via GitGitGadget
` (6 preceding siblings ...)
2020-01-16 6:14 ` [PATCH v4 07/19] rebase: make sure to pass along the quiet flag to the sequencer Elijah Newren via GitGitGadget
@ 2020-01-16 6:14 ` Elijah Newren via GitGitGadget
2020-01-16 6:14 ` [PATCH v4 09/19] t3432: make these tests work with either am or merge backends Elijah Newren via GitGitGadget
` (12 subsequent siblings)
20 siblings, 0 replies; 161+ messages in thread
From: Elijah Newren via GitGitGadget @ 2020-01-16 6:14 UTC (permalink / raw)
To: git
Cc: Johannes.Schindelin, phillip.wood, liu.denton, gitster, plroskin,
alban.gruin, szeder.dev, jrnieder, emilyshaffer, Elijah Newren,
Elijah Newren
From: Elijah Newren <newren@gmail.com>
restrict_revision in the original shell script was an excluded revision
range. It is also treated that way by the am-backend. In the
conversion from shell to C (see commit 6ab54d17be3f ("rebase -i:
implement the logic to initialize $revisions in C", 2018-08-28)), the
interactive-backend accidentally treated it as a positive revision
rather than a negated one.
This was missed as there were no tests in the testsuite that tested an
interactive rebase with fork-point behavior.
Signed-off-by: Elijah Newren <newren@gmail.com>
---
builtin/rebase.c | 4 ++--
t/t3400-rebase.sh | 20 +++++++++++++++++++-
2 files changed, 21 insertions(+), 3 deletions(-)
diff --git a/builtin/rebase.c b/builtin/rebase.c
index e8d518ac8d..fe1175af32 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -348,8 +348,8 @@ static int do_interactive_rebase(struct rebase_options *opts, unsigned flags)
argv_array_pushl(&make_script_args, "", revisions, NULL);
if (opts->restrict_revision)
- argv_array_push(&make_script_args,
- oid_to_hex(&opts->restrict_revision->object.oid));
+ argv_array_pushf(&make_script_args, "^%s",
+ oid_to_hex(&opts->restrict_revision->object.oid));
ret = sequencer_make_script(the_repository, &todo_list.buf,
make_script_args.argc, make_script_args.argv,
diff --git a/t/t3400-rebase.sh b/t/t3400-rebase.sh
index 79762b989a..71fd6396cd 100755
--- a/t/t3400-rebase.sh
+++ b/t/t3400-rebase.sh
@@ -165,11 +165,29 @@ test_expect_success 'rebase works with format.useAutoBase' '
git rebase master
'
-test_expect_success 'default to common base in @{upstream}s reflog if no upstream arg' '
+test_expect_success 'default to common base in @{upstream}s reflog if no upstream arg (--merge)' '
git checkout -b default-base master &&
git checkout -b default topic &&
git config branch.default.remote . &&
git config branch.default.merge refs/heads/default-base &&
+ git rebase --merge &&
+ git rev-parse --verify default-base >expect &&
+ git rev-parse default~1 >actual &&
+ test_cmp expect actual &&
+ git checkout default-base &&
+ git reset --hard HEAD^ &&
+ git checkout default &&
+ git rebase --merge &&
+ git rev-parse --verify default-base >expect &&
+ git rev-parse default~1 >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'default to common base in @{upstream}s reflog if no upstream arg' '
+ git checkout -B default-base master &&
+ git checkout -B default topic &&
+ git config branch.default.remote . &&
+ git config branch.default.merge refs/heads/default-base &&
git rebase &&
git rev-parse --verify default-base >expect &&
git rev-parse default~1 >actual &&
--
gitgitgadget
^ permalink raw reply related [flat|nested] 161+ messages in thread
* [PATCH v4 09/19] t3432: make these tests work with either am or merge backends
2020-01-16 6:14 ` [PATCH v4 00/19] rebase: make the default backend configurable Elijah Newren via GitGitGadget
` (7 preceding siblings ...)
2020-01-16 6:14 ` [PATCH v4 08/19] rebase: fix handling of restrict_revision Elijah Newren via GitGitGadget
@ 2020-01-16 6:14 ` Elijah Newren via GitGitGadget
2020-01-16 6:14 ` [PATCH v4 10/19] rebase: allow more types of rebases to fast-forward Elijah Newren via GitGitGadget
` (11 subsequent siblings)
20 siblings, 0 replies; 161+ messages in thread
From: Elijah Newren via GitGitGadget @ 2020-01-16 6:14 UTC (permalink / raw)
To: git
Cc: Johannes.Schindelin, phillip.wood, liu.denton, gitster, plroskin,
alban.gruin, szeder.dev, jrnieder, emilyshaffer, Elijah Newren,
Elijah Newren
From: Elijah Newren <newren@gmail.com>
t3432 had several stress tests for can_fast_forward(), whose intent was
to ensure we were using the optimization of just fast forwarding when
possible. However, these tests verified that fast forwards had happened
based on the output that rebase printed to the terminal. We can instead
test more directly that we actually fast-forwarded by checking the
reflog, which also has the side effect of making the tests applicable
for the merge/interactive backend.
This change does lose the distinction between "noop" and "noop-force",
but as stated in commit c9efc216830f ("t3432: test for --no-ff's
interaction with fast-forward", 2019-08-27) which introduced that
distinction: "These tests aren't supposed to endorse the status quo,
just test for what we're currently doing.".
This change does not actually run these tests with the merge/interactive
backend; instead this is just a preparatory commit. A subsequent commit
which fixes can_fast_forward() to work with that backend will then also
change t3432 to add tests of that backend as well.
Signed-off-by: Elijah Newren <newren@gmail.com>
---
t/t3432-rebase-fast-forward.sh | 48 ++++++++++++++++------------------
1 file changed, 22 insertions(+), 26 deletions(-)
diff --git a/t/t3432-rebase-fast-forward.sh b/t/t3432-rebase-fast-forward.sh
index 92f95b57da..7432c0e241 100755
--- a/t/t3432-rebase-fast-forward.sh
+++ b/t/t3432-rebase-fast-forward.sh
@@ -44,19 +44,15 @@ test_rebase_same_head_ () {
test_expect_$status "git rebase$flag $* with $changes is $what with $cmp HEAD" "
oldhead=\$(git rev-parse HEAD) &&
test_when_finished 'git reset --hard \$oldhead' &&
+ cp .git/logs/HEAD expect &&
git rebase$flag $* >stdout &&
if test $what = work
then
- # Must check this case first, for 'is up to
- # date, rebase forced[...]rewinding head' cases
- test_i18ngrep 'rewinding head' stdout
+ old=\$(wc -l <expect) &&
+ test_line_count '-gt' \$old .git/logs/HEAD
elif test $what = noop
then
- test_i18ngrep 'is up to date' stdout &&
- test_i18ngrep ! 'rebase forced' stdout
- elif test $what = noop-force
- then
- test_i18ngrep 'is up to date, rebase forced' stdout
+ test_cmp expect .git/logs/HEAD
fi &&
newhead=\$(git rev-parse HEAD) &&
if test $cmp = same
@@ -71,14 +67,14 @@ test_rebase_same_head_ () {
changes='no changes'
test_rebase_same_head success noop same success work same
-test_rebase_same_head success noop same success noop-force same master
-test_rebase_same_head success noop same success noop-force diff --onto B B
-test_rebase_same_head success noop same success noop-force diff --onto B... B
-test_rebase_same_head success noop same success noop-force same --onto master... master
-test_rebase_same_head success noop same success noop-force same --keep-base master
-test_rebase_same_head success noop same success noop-force same --keep-base
-test_rebase_same_head success noop same success noop-force same --no-fork-point
-test_rebase_same_head success noop same success noop-force same --keep-base --no-fork-point
+test_rebase_same_head success noop same success work same master
+test_rebase_same_head success noop same success work diff --onto B B
+test_rebase_same_head success noop same success work diff --onto B... B
+test_rebase_same_head success noop same success work same --onto master... master
+test_rebase_same_head success noop same success work same --keep-base master
+test_rebase_same_head success noop same success work same --keep-base
+test_rebase_same_head success noop same success work same --no-fork-point
+test_rebase_same_head success noop same success work same --keep-base --no-fork-point
test_rebase_same_head success noop same success work same --fork-point master
test_rebase_same_head success noop same success work diff --fork-point --onto B B
test_rebase_same_head success noop same success work diff --fork-point --onto B... B
@@ -91,14 +87,14 @@ test_expect_success 'add work same to side' '
changes='our changes'
test_rebase_same_head success noop same success work same
-test_rebase_same_head success noop same success noop-force same master
-test_rebase_same_head success noop same success noop-force diff --onto B B
-test_rebase_same_head success noop same success noop-force diff --onto B... B
-test_rebase_same_head success noop same success noop-force same --onto master... master
-test_rebase_same_head success noop same success noop-force same --keep-base master
-test_rebase_same_head success noop same success noop-force same --keep-base
-test_rebase_same_head success noop same success noop-force same --no-fork-point
-test_rebase_same_head success noop same success noop-force same --keep-base --no-fork-point
+test_rebase_same_head success noop same success work same master
+test_rebase_same_head success noop same success work diff --onto B B
+test_rebase_same_head success noop same success work diff --onto B... B
+test_rebase_same_head success noop same success work same --onto master... master
+test_rebase_same_head success noop same success work same --keep-base master
+test_rebase_same_head success noop same success work same --keep-base
+test_rebase_same_head success noop same success work same --no-fork-point
+test_rebase_same_head success noop same success work same --keep-base --no-fork-point
test_rebase_same_head success noop same success work same --fork-point master
test_rebase_same_head success noop same success work diff --fork-point --onto B B
test_rebase_same_head success noop same success work diff --fork-point --onto B... B
@@ -112,8 +108,8 @@ test_expect_success 'add work same to upstream' '
'
changes='our and their changes'
-test_rebase_same_head success noop same success noop-force diff --onto B B
-test_rebase_same_head success noop same success noop-force diff --onto B... B
+test_rebase_same_head success noop same success work diff --onto B B
+test_rebase_same_head success noop same success work diff --onto B... B
test_rebase_same_head success noop same success work diff --onto master... master
test_rebase_same_head success noop same success work diff --keep-base master
test_rebase_same_head success noop same success work diff --keep-base
--
gitgitgadget
^ permalink raw reply related [flat|nested] 161+ messages in thread
* [PATCH v4 10/19] rebase: allow more types of rebases to fast-forward
2020-01-16 6:14 ` [PATCH v4 00/19] rebase: make the default backend configurable Elijah Newren via GitGitGadget
` (8 preceding siblings ...)
2020-01-16 6:14 ` [PATCH v4 09/19] t3432: make these tests work with either am or merge backends Elijah Newren via GitGitGadget
@ 2020-01-16 6:14 ` Elijah Newren via GitGitGadget
2020-01-16 6:14 ` [PATCH v4 11/19] git-rebase.txt: add more details about behavioral differences of backends Elijah Newren via GitGitGadget
` (10 subsequent siblings)
20 siblings, 0 replies; 161+ messages in thread
From: Elijah Newren via GitGitGadget @ 2020-01-16 6:14 UTC (permalink / raw)
To: git
Cc: Johannes.Schindelin, phillip.wood, liu.denton, gitster, plroskin,
alban.gruin, szeder.dev, jrnieder, emilyshaffer, Elijah Newren,
Elijah Newren
From: Elijah Newren <newren@gmail.com>
In the past, we dis-allowed rebases using the interactive backend from
performing a fast-forward to short-circuit the rebase operation. This
made sense for explicitly interactive rebases and some implicitly
interactive rebases, but certainly became overly stringent when the
merge backend was re-implemented via the interactive backend.
Just as the am-based rebase has always had to disable the fast-forward
based on a variety of conditions or flags (e.g. --signoff, --whitespace,
etc.), we need to do the same but now with a few more options. However,
continuing to use REBASE_FORCE for tracking this is problematic because
the interactive backend used it for a different purpose. (When
REBASE_FORCE wasn't set, the interactive backend would not fast-forward
the whole series but would fast-forward individual "pick" commits at the
beginning of the todo list, and then a squash or something would cause
it to start generating new commits.) So, introduce a new
allow_preemptive_ff flag contained within cmd_rebase() and use it to
track whether we are going to allow a pre-emptive fast-forward that
short-circuits the whole rebase.
Signed-off-by: Elijah Newren <newren@gmail.com>
---
builtin/rebase.c | 18 ++++++++++++++----
t/t3432-rebase-fast-forward.sh | 2 ++
2 files changed, 16 insertions(+), 4 deletions(-)
diff --git a/builtin/rebase.c b/builtin/rebase.c
index fe1175af32..4b7d8fc908 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -1467,6 +1467,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
struct object_id squash_onto;
char *squash_onto_name = NULL;
int reschedule_failed_exec = -1;
+ int allow_preemptive_ff = 1;
struct option builtin_rebase_options[] = {
OPT_STRING(0, "onto", &options.onto_name,
N_("revision"),
@@ -1774,13 +1775,20 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
state_dir_base, cmd_live_rebase, buf.buf);
}
+ if ((options.flags & REBASE_INTERACTIVE_EXPLICIT) ||
+ (action != ACTION_NONE) ||
+ (exec.nr > 0) ||
+ options.autosquash) {
+ allow_preemptive_ff = 0;
+ }
+
for (i = 0; i < options.git_am_opts.argc; i++) {
const char *option = options.git_am_opts.argv[i], *p;
if (!strcmp(option, "--committer-date-is-author-date") ||
!strcmp(option, "--ignore-date") ||
!strcmp(option, "--whitespace=fix") ||
!strcmp(option, "--whitespace=strip"))
- options.flags |= REBASE_FORCE;
+ allow_preemptive_ff = 0;
else if (skip_prefix(option, "-C", &p)) {
while (*p)
if (!isdigit(*(p++)))
@@ -2116,12 +2124,14 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
/*
* Check if we are already based on onto with linear history,
* in which case we could fast-forward without replacing the commits
- * with new commits recreated by replaying their changes. This
- * optimization must not be done if this is an interactive rebase.
+ * with new commits recreated by replaying their changes.
+ *
+ * Note that can_fast_forward() initializes merge_base, so we have to
+ * call it before checking allow_preemptive_ff.
*/
if (can_fast_forward(options.onto, options.upstream, options.restrict_revision,
&options.orig_head, &merge_base) &&
- !is_interactive(&options)) {
+ allow_preemptive_ff) {
int flag;
if (!(options.flags & REBASE_FORCE)) {
diff --git a/t/t3432-rebase-fast-forward.sh b/t/t3432-rebase-fast-forward.sh
index 7432c0e241..40388ccf9f 100755
--- a/t/t3432-rebase-fast-forward.sh
+++ b/t/t3432-rebase-fast-forward.sh
@@ -30,6 +30,8 @@ test_rebase_same_head () {
shift &&
test_rebase_same_head_ $status_n $what_n $cmp_n "" "$*" &&
test_rebase_same_head_ $status_f $what_f $cmp_f " --no-ff" "$*"
+ test_rebase_same_head_ $status_n $what_n $cmp_n " --merge" "$*" &&
+ test_rebase_same_head_ $status_f $what_f $cmp_f " --merge --no-ff" "$*"
}
test_rebase_same_head_ () {
--
gitgitgadget
^ permalink raw reply related [flat|nested] 161+ messages in thread
* [PATCH v4 11/19] git-rebase.txt: add more details about behavioral differences of backends
2020-01-16 6:14 ` [PATCH v4 00/19] rebase: make the default backend configurable Elijah Newren via GitGitGadget
` (9 preceding siblings ...)
2020-01-16 6:14 ` [PATCH v4 10/19] rebase: allow more types of rebases to fast-forward Elijah Newren via GitGitGadget
@ 2020-01-16 6:14 ` Elijah Newren via GitGitGadget
2020-01-16 6:14 ` [PATCH v4 12/19] rebase: move incompatibility checks between backend options a bit earlier Elijah Newren via GitGitGadget
` (9 subsequent siblings)
20 siblings, 0 replies; 161+ messages in thread
From: Elijah Newren via GitGitGadget @ 2020-01-16 6:14 UTC (permalink / raw)
To: git
Cc: Johannes.Schindelin, phillip.wood, liu.denton, gitster, plroskin,
alban.gruin, szeder.dev, jrnieder, emilyshaffer, Elijah Newren,
Elijah Newren
From: Elijah Newren <newren@gmail.com>
Signed-off-by: Elijah Newren <newren@gmail.com>
---
Documentation/git-rebase.txt | 85 +++++++++++++++++++++++++++++++++---
1 file changed, 80 insertions(+), 5 deletions(-)
diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index 551a91d764..df02d76e2d 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -414,7 +414,7 @@ with `--keep-base` in order to drop those commits from your branch.
--ignore-whitespace::
--whitespace=<option>::
- These flag are passed to the 'git apply' program
+ These flags are passed to the 'git apply' program
(see linkgit:git-apply[1]) that applies the patch.
+
See also INCOMPATIBLE OPTIONS below.
@@ -561,8 +561,8 @@ The following options:
* --committer-date-is-author-date
* --ignore-date
- * --whitespace
* --ignore-whitespace
+ * --whitespace
* -C
are incompatible with the following options:
@@ -613,9 +613,84 @@ handling commits that become empty.
Directory rename detection
~~~~~~~~~~~~~~~~~~~~~~~~~~
-Directory rename heuristics are enabled in the merge and interactive
-backends. Due to the lack of accurate tree information, directory
-rename detection is disabled in the am backend.
+Due to the lack of accurate tree information (arising from
+constructing fake ancestors with the limited information available in
+patches), directory rename detection is disabled in the am backend.
+Disabled directory rename detection means that if one side of history
+renames a directory and the other adds new files to the old directory,
+then the new files will be left behind in the old directory without
+any warning at the time of rebasing that you may want to move these
+files into the new directory.
+
+Directory rename detection works with the merge and interactive
+backends to provide you warnings in such cases.
+
+Context
+~~~~~~~
+
+The am backend works by creating a sequence of patches (by calling
+`format-patch` internally), and then applying the patches in sequence
+(calling `am` internally). Patches are composed of multiple hunks,
+each with line numbers, a context region, and the actual changes. The
+line numbers have to be taken with some fuzz, since the other side
+will likely have inserted or deleted lines earlier in the file. The
+context region is meant to help find how to adjust the line numbers in
+order to apply the changes to the right lines. However, if multiple
+areas of the code have the same surrounding lines of context, the
+wrong one can be picked. There are real-world cases where this has
+caused commits to be reapplied incorrectly with no conflicts reported.
+Setting diff.context to a larger value may prevent such types of
+problems, but increases the chance of spurious conflicts (since it
+will require more lines of matching context to apply).
+
+The interactive backend works with a full copy of each relevant file,
+insulating it from these types of problems.
+
+Labelling of conflicts markers
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+When there are content conflicts, the merge machinery tries to
+annotate each side's conflict markers with the commits where the
+content came from. Since the am backend drops the original
+information about the rebased commits and their parents (and instead
+generates new fake commits based off limited information in the
+generated patches), those commits cannot be identified; instead it has
+to fall back to a commit summary. Also, when merge.conflictStyle is
+set to diff3, the am backend will use "constructed merge base" to
+label the content from the merge base, and thus provide no information
+about the merge base commit whatsoever.
+
+The interactive backend works with the full commits on both sides of
+history and thus has no such limitations.
+
+Hooks
+~~~~~
+
+The am backend has not traditionally called the post-commit hook,
+while the merge/interactive backend has. However, this was by
+accident of implementation rather than by design. Both backends
+should have the same behavior, though it is not clear which one is
+correct.
+
+Miscellaneous differences
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+There are a few more behavioral differences that most folks would
+probably consider inconsequential but which are mentioned for
+completeness:
+
+* Reflog: The two backends will use different wording when describing
+ the changes made in the reflog, though both will make use of the
+ word "rebase".
+
+* Progress, informational, and error messages: The two backends
+ provide slightly different progress and informational messages.
+ Also, the am backend writes error messages (such as "Your files
+ would be overwritten...") to stdout, while the interactive backend
+ writes them to stderr.
+
+* State directories: The two backends keep their state in different
+ directories under .git/
include::merge-strategies.txt[]
--
gitgitgadget
^ permalink raw reply related [flat|nested] 161+ messages in thread
* [PATCH v4 12/19] rebase: move incompatibility checks between backend options a bit earlier
2020-01-16 6:14 ` [PATCH v4 00/19] rebase: make the default backend configurable Elijah Newren via GitGitGadget
` (10 preceding siblings ...)
2020-01-16 6:14 ` [PATCH v4 11/19] git-rebase.txt: add more details about behavioral differences of backends Elijah Newren via GitGitGadget
@ 2020-01-16 6:14 ` Elijah Newren via GitGitGadget
2020-01-16 6:14 ` [PATCH v4 13/19] rebase: add an --am option Elijah Newren via GitGitGadget
` (8 subsequent siblings)
20 siblings, 0 replies; 161+ messages in thread
From: Elijah Newren via GitGitGadget @ 2020-01-16 6:14 UTC (permalink / raw)
To: git
Cc: Johannes.Schindelin, phillip.wood, liu.denton, gitster, plroskin,
alban.gruin, szeder.dev, jrnieder, emilyshaffer, Elijah Newren,
Elijah Newren
From: Elijah Newren <newren@gmail.com>
Signed-off-by: Elijah Newren <newren@gmail.com>
---
builtin/rebase.c | 22 +++++++++++-----------
1 file changed, 11 insertions(+), 11 deletions(-)
diff --git a/builtin/rebase.c b/builtin/rebase.c
index 4b7d8fc908..55a0b2a288 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -1878,6 +1878,17 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
if (isatty(2) && options.flags & REBASE_NO_QUIET)
strbuf_addstr(&options.git_format_patch_opt, " --progress");
+ if (options.git_am_opts.argc) {
+ /* all am options except -q are compatible only with --am */
+ for (i = options.git_am_opts.argc - 1; i >= 0; i--)
+ if (strcmp(options.git_am_opts.argv[i], "-q"))
+ break;
+
+ if (is_interactive(&options) && i >= 0)
+ die(_("cannot combine am options with either "
+ "interactive or merge options"));
+ }
+
switch (options.type) {
case REBASE_MERGE:
case REBASE_INTERACTIVE:
@@ -1908,17 +1919,6 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
if (reschedule_failed_exec >= 0)
options.reschedule_failed_exec = reschedule_failed_exec;
- if (options.git_am_opts.argc) {
- /* all am options except -q are compatible only with --am */
- for (i = options.git_am_opts.argc - 1; i >= 0; i--)
- if (strcmp(options.git_am_opts.argv[i], "-q"))
- break;
-
- if (is_interactive(&options) && i >= 0)
- die(_("cannot combine am options with either "
- "interactive or merge options"));
- }
-
if (options.signoff) {
if (options.type == REBASE_PRESERVE_MERGES)
die("cannot combine '--signoff' with "
--
gitgitgadget
^ permalink raw reply related [flat|nested] 161+ messages in thread
* [PATCH v4 13/19] rebase: add an --am option
2020-01-16 6:14 ` [PATCH v4 00/19] rebase: make the default backend configurable Elijah Newren via GitGitGadget
` (11 preceding siblings ...)
2020-01-16 6:14 ` [PATCH v4 12/19] rebase: move incompatibility checks between backend options a bit earlier Elijah Newren via GitGitGadget
@ 2020-01-16 6:14 ` Elijah Newren via GitGitGadget
2020-01-16 6:14 ` [PATCH v4 14/19] git-prompt: change the prompt for interactive-based rebases Elijah Newren via GitGitGadget
` (7 subsequent siblings)
20 siblings, 0 replies; 161+ messages in thread
From: Elijah Newren via GitGitGadget @ 2020-01-16 6:14 UTC (permalink / raw)
To: git
Cc: Johannes.Schindelin, phillip.wood, liu.denton, gitster, plroskin,
alban.gruin, szeder.dev, jrnieder, emilyshaffer, Elijah Newren,
Elijah Newren
From: Elijah Newren <newren@gmail.com>
Currently, this option doesn't do anything except error out if any
options requiring the interactive-backend are also passed. However,
when we make the default backend configurable later in this series, this
flag will provide a way to override the config setting.
Signed-off-by: Elijah Newren <newren@gmail.com>
---
Documentation/git-rebase.txt | 11 ++++++++++-
builtin/rebase.c | 18 +++++++++++++++++-
2 files changed, 27 insertions(+), 2 deletions(-)
diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index df02d76e2d..5230084be9 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -258,6 +258,13 @@ See also INCOMPATIBLE OPTIONS below.
original branch. The index and working tree are also left
unchanged as a result.
+--am:
+ Use git-am internally to rebase. This option may become a
+ no-op in the future once the interactive backend handles
+ everything the am one does.
++
+See also INCOMPATIBLE OPTIONS below.
+
--empty={drop,keep,ask}::
How to handle commits that are not empty to start and are not
clean cherry-picks of any upstream commit, but which become
@@ -376,7 +383,7 @@ See also INCOMPATIBLE OPTIONS below.
Ensure at least <n> lines of surrounding context match before
and after each change. When fewer lines of surrounding
context exist they all must match. By default no context is
- ever ignored.
+ ever ignored. Implies --am.
+
See also INCOMPATIBLE OPTIONS below.
@@ -416,6 +423,7 @@ with `--keep-base` in order to drop those commits from your branch.
--whitespace=<option>::
These flags are passed to the 'git apply' program
(see linkgit:git-apply[1]) that applies the patch.
+ Implies --am.
+
See also INCOMPATIBLE OPTIONS below.
@@ -559,6 +567,7 @@ INCOMPATIBLE OPTIONS
The following options:
+ * --am
* --committer-date-is-author-date
* --ignore-date
* --ignore-whitespace
diff --git a/builtin/rebase.c b/builtin/rebase.c
index 55a0b2a288..6884590258 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -1335,6 +1335,18 @@ static int can_fast_forward(struct commit *onto, struct commit *upstream,
return res && is_linear_history(onto, head);
}
+static int parse_opt_am(const struct option *opt, const char *arg, int unset)
+{
+ struct rebase_options *opts = opt->value;
+
+ BUG_ON_OPT_NEG(unset);
+ BUG_ON_OPT_ARG(arg);
+
+ opts->type = REBASE_AM;
+
+ return 0;
+}
+
/* -i followed by -m is still -i */
static int parse_opt_merge(const struct option *opt, const char *arg, int unset)
{
@@ -1519,6 +1531,10 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
OPT_CMDMODE(0, "show-current-patch", &action,
N_("show the patch file being applied or merged"),
ACTION_SHOW_CURRENT_PATCH),
+ { OPTION_CALLBACK, 0, "am", &options, NULL,
+ N_("use apply-mail strategies to rebase"),
+ PARSE_OPT_NOARG | PARSE_OPT_NONEG,
+ parse_opt_am },
{ OPTION_CALLBACK, 'm', "merge", &options, NULL,
N_("use merging strategies to rebase"),
PARSE_OPT_NOARG | PARSE_OPT_NONEG,
@@ -1878,7 +1894,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
if (isatty(2) && options.flags & REBASE_NO_QUIET)
strbuf_addstr(&options.git_format_patch_opt, " --progress");
- if (options.git_am_opts.argc) {
+ if (options.git_am_opts.argc || options.type == REBASE_AM) {
/* all am options except -q are compatible only with --am */
for (i = options.git_am_opts.argc - 1; i >= 0; i--)
if (strcmp(options.git_am_opts.argv[i], "-q"))
--
gitgitgadget
^ permalink raw reply related [flat|nested] 161+ messages in thread
* [PATCH v4 14/19] git-prompt: change the prompt for interactive-based rebases
2020-01-16 6:14 ` [PATCH v4 00/19] rebase: make the default backend configurable Elijah Newren via GitGitGadget
` (12 preceding siblings ...)
2020-01-16 6:14 ` [PATCH v4 13/19] rebase: add an --am option Elijah Newren via GitGitGadget
@ 2020-01-16 6:14 ` Elijah Newren via GitGitGadget
2020-01-16 6:14 ` [PATCH v4 15/19] rebase: drop '-i' from the reflog " Elijah Newren via GitGitGadget
` (6 subsequent siblings)
20 siblings, 0 replies; 161+ messages in thread
From: Elijah Newren via GitGitGadget @ 2020-01-16 6:14 UTC (permalink / raw)
To: git
Cc: Johannes.Schindelin, phillip.wood, liu.denton, gitster, plroskin,
alban.gruin, szeder.dev, jrnieder, emilyshaffer, Elijah Newren,
Elijah Newren
From: Elijah Newren <newren@gmail.com>
In the past, we had different prompts for different types of rebases:
REBASE: for am-based rebases
REBASE-m: for merge-based rebases
REBASE-i: for interactive-based rebases
It's not clear why this distinction was necessary or helpful; when the
prompt was added in commit e75201963f67 ("Improve bash prompt to detect
various states like an unfinished merge", 2007-09-30), it simply added
these three different types. Perhaps there was a useful purpose back
then, but there have been some changes:
* The merge backend was deleted after being implemented on top of the
interactive backend, causing the prompt for merge-based rebases to
change from REBASE-m to REBASE-i.
* The interactive backend is used for multiple different types of
non-interactive rebases, so the "-i" part of the prompt doesn't
really mean what it used to.
* Rebase backends have gained more abilities and have a great deal of
overlap, sometimes making it hard to distinguish them.
* Behavioral differences between the backends have also been ironed
out.
* We want to change the default backend from am to interactive, which
means people would get "REBASE-i" by default if we didn't change
the prompt, and only if they specified --am or --whitespace or -C
would they get the "REBASE" prompt.
* In the future, we plan to have "--whitespace", "-C", and even "--am"
run the interactive backend once it can handle everything the
am-backend can.
For all these reasons, make the prompt for any type of rebase just be
"REBASE".
Signed-off-by: Elijah Newren <newren@gmail.com>
---
contrib/completion/git-prompt.sh | 6 +-----
t/t9903-bash-prompt.sh | 8 ++++----
2 files changed, 5 insertions(+), 9 deletions(-)
diff --git a/contrib/completion/git-prompt.sh b/contrib/completion/git-prompt.sh
index 1d510cd47b..014cd7c3cf 100644
--- a/contrib/completion/git-prompt.sh
+++ b/contrib/completion/git-prompt.sh
@@ -429,11 +429,7 @@ __git_ps1 ()
__git_eread "$g/rebase-merge/head-name" b
__git_eread "$g/rebase-merge/msgnum" step
__git_eread "$g/rebase-merge/end" total
- if [ -f "$g/rebase-merge/interactive" ]; then
- r="|REBASE-i"
- else
- r="|REBASE-m"
- fi
+ r="|REBASE"
else
if [ -d "$g/rebase-apply" ]; then
__git_eread "$g/rebase-apply/next" step
diff --git a/t/t9903-bash-prompt.sh b/t/t9903-bash-prompt.sh
index 88bc733ad6..7ca35d358d 100755
--- a/t/t9903-bash-prompt.sh
+++ b/t/t9903-bash-prompt.sh
@@ -163,7 +163,7 @@ test_expect_success 'prompt - inside bare repository' '
'
test_expect_success 'prompt - interactive rebase' '
- printf " (b1|REBASE-i 2/3)" >expected &&
+ printf " (b1|REBASE 2/3)" >expected &&
write_script fake_editor.sh <<-\EOF &&
echo "exec echo" >"$1"
echo "edit $(git log -1 --format="%h")" >>"$1"
@@ -180,7 +180,7 @@ test_expect_success 'prompt - interactive rebase' '
'
test_expect_success 'prompt - rebase merge' '
- printf " (b2|REBASE-i 1/3)" >expected &&
+ printf " (b2|REBASE 1/3)" >expected &&
git checkout b2 &&
test_when_finished "git checkout master" &&
test_must_fail git rebase --merge b1 b2 &&
@@ -189,11 +189,11 @@ test_expect_success 'prompt - rebase merge' '
test_cmp expected "$actual"
'
-test_expect_success 'prompt - rebase' '
+test_expect_success 'prompt - rebase am' '
printf " (b2|REBASE 1/3)" >expected &&
git checkout b2 &&
test_when_finished "git checkout master" &&
- test_must_fail git rebase b1 b2 &&
+ test_must_fail git rebase --am b1 b2 &&
test_when_finished "git rebase --abort" &&
__git_ps1 >"$actual" &&
test_cmp expected "$actual"
--
gitgitgadget
^ permalink raw reply related [flat|nested] 161+ messages in thread
* [PATCH v4 15/19] rebase: drop '-i' from the reflog for interactive-based rebases
2020-01-16 6:14 ` [PATCH v4 00/19] rebase: make the default backend configurable Elijah Newren via GitGitGadget
` (13 preceding siblings ...)
2020-01-16 6:14 ` [PATCH v4 14/19] git-prompt: change the prompt for interactive-based rebases Elijah Newren via GitGitGadget
@ 2020-01-16 6:14 ` Elijah Newren via GitGitGadget
2020-01-16 6:14 ` [PATCH v4 16/19] rebase tests: mark tests specific to the am-backend with --am Elijah Newren via GitGitGadget
` (5 subsequent siblings)
20 siblings, 0 replies; 161+ messages in thread
From: Elijah Newren via GitGitGadget @ 2020-01-16 6:14 UTC (permalink / raw)
To: git
Cc: Johannes.Schindelin, phillip.wood, liu.denton, gitster, plroskin,
alban.gruin, szeder.dev, jrnieder, emilyshaffer, Elijah Newren,
Elijah Newren
From: Elijah Newren <newren@gmail.com>
A large variety of rebase types are supported by the interactive
machinery, not just the explicitly interactive ones. These all share
the same code and write the same reflog messages, but the "-i" moniker
in those messages doesn't really have much meaning. It also becomes
somewhat distracting once we switch the default from the am-backend to
the interactive one. Just remove the "-i" from these messages.
Signed-off-by: Elijah Newren <newren@gmail.com>
---
builtin/rebase.c | 2 +-
sequencer.c | 12 ++++++------
t/t3404-rebase-interactive.sh | 10 +++++-----
3 files changed, 12 insertions(+), 12 deletions(-)
diff --git a/builtin/rebase.c b/builtin/rebase.c
index 6884590258..ecd23d3ee7 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -1441,7 +1441,7 @@ static void set_reflog_action(struct rebase_options *options)
if (env && strcmp("rebase", env))
return; /* only override it if it is "rebase" */
- strbuf_addf(&buf, "rebase -i (%s)", options->action);
+ strbuf_addf(&buf, "rebase (%s)", options->action);
setenv(GIT_REFLOG_ACTION_ENVIRONMENT, buf.buf, 1);
strbuf_release(&buf);
}
diff --git a/sequencer.c b/sequencer.c
index 078a68eaf3..cca503c11b 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -290,7 +290,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 +324,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 +628,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)));
@@ -2885,8 +2885,8 @@ static int save_todo(struct todo_list *todo_list, struct replay_opts *opts)
int next = todo_list->current, offset, fd;
/*
- * rebase -i writes "git-rebase-todo" without the currently executing
- * command, appending it to "done" instead.
+ * interactive backend writes "git-rebase-todo" without the currently
+ * executing command, appending it to "done" instead.
*/
if (is_rebase_i(opts))
next++;
@@ -3197,7 +3197,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) {
diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
index c41531f349..a31583eb2f 100755
--- a/t/t3404-rebase-interactive.sh
+++ b/t/t3404-rebase-interactive.sh
@@ -223,7 +223,7 @@ test_expect_success 'reflog for the branch shows state before rebase' '
'
test_expect_success 'reflog for the branch shows correct finish message' '
- printf "rebase -i (finish): refs/heads/branch1 onto %s\n" \
+ printf "rebase (finish): refs/heads/branch1 onto %s\n" \
"$(git rev-parse branch2)" >expected &&
git log -g --pretty=%gs -1 refs/heads/branch1 >actual &&
test_cmp expected actual
@@ -1162,10 +1162,10 @@ test_expect_success 'rebase -i produces readable reflog' '
git branch -f branch-reflog-test H &&
git rebase -i --onto I F branch-reflog-test &&
cat >expect <<-\EOF &&
- rebase -i (finish): returning to refs/heads/branch-reflog-test
- rebase -i (pick): H
- rebase -i (pick): G
- rebase -i (start): checkout I
+ rebase (finish): returning to refs/heads/branch-reflog-test
+ rebase (pick): H
+ rebase (pick): G
+ rebase (start): checkout I
EOF
git reflog -n4 HEAD |
sed "s/[^:]*: //" >actual &&
--
gitgitgadget
^ permalink raw reply related [flat|nested] 161+ messages in thread
* [PATCH v4 16/19] rebase tests: mark tests specific to the am-backend with --am
2020-01-16 6:14 ` [PATCH v4 00/19] rebase: make the default backend configurable Elijah Newren via GitGitGadget
` (14 preceding siblings ...)
2020-01-16 6:14 ` [PATCH v4 15/19] rebase: drop '-i' from the reflog " Elijah Newren via GitGitGadget
@ 2020-01-16 6:14 ` Elijah Newren via GitGitGadget
2020-01-16 6:14 ` [PATCH v4 17/19] rebase tests: repeat some tests using the merge backend instead of am Elijah Newren via GitGitGadget
` (4 subsequent siblings)
20 siblings, 0 replies; 161+ messages in thread
From: Elijah Newren via GitGitGadget @ 2020-01-16 6:14 UTC (permalink / raw)
To: git
Cc: Johannes.Schindelin, phillip.wood, liu.denton, gitster, plroskin,
alban.gruin, szeder.dev, jrnieder, emilyshaffer, Elijah Newren,
Elijah Newren
From: Elijah Newren <newren@gmail.com>
We have many rebase tests in the testsuite, and often the same test is
repeated multiple times just testing different backends. For those
tests that were specifically trying to test the am backend, add the --am
flag.
Signed-off-by: Elijah Newren <newren@gmail.com>
---
t/t3400-rebase.sh | 10 +++++-----
t/t3401-rebase-and-am-rename.sh | 4 ++--
t/t3404-rebase-interactive.sh | 2 +-
t/t3406-rebase-message.sh | 12 ++++++------
t/t3407-rebase-abort.sh | 6 +++---
t/t3420-rebase-autostash.sh | 2 +-
t/t3425-rebase-topology-merges.sh | 8 ++++----
t/t3432-rebase-fast-forward.sh | 4 ++--
t/t5407-post-rewrite-hook.sh | 12 ++++++------
t/t7512-status-help.sh | 12 ++++++------
10 files changed, 36 insertions(+), 36 deletions(-)
diff --git a/t/t3400-rebase.sh b/t/t3400-rebase.sh
index 71fd6396cd..0a491f2363 100755
--- a/t/t3400-rebase.sh
+++ b/t/t3400-rebase.sh
@@ -183,19 +183,19 @@ test_expect_success 'default to common base in @{upstream}s reflog if no upstrea
test_cmp expect actual
'
-test_expect_success 'default to common base in @{upstream}s reflog if no upstream arg' '
+test_expect_success 'default to common base in @{upstream}s reflog if no upstream arg (--am)' '
git checkout -B default-base master &&
git checkout -B default topic &&
git config branch.default.remote . &&
git config branch.default.merge refs/heads/default-base &&
- git rebase &&
+ git rebase --am &&
git rev-parse --verify default-base >expect &&
git rev-parse default~1 >actual &&
test_cmp expect actual &&
git checkout default-base &&
git reset --hard HEAD^ &&
git checkout default &&
- git rebase &&
+ git rebase --am &&
git rev-parse --verify default-base >expect &&
git rev-parse default~1 >actual &&
test_cmp expect actual
@@ -226,7 +226,7 @@ test_expect_success 'cherry-picked commits and fork-point work together' '
test_expect_success 'rebase --am -q is quiet' '
git checkout -b quiet topic &&
- git rebase -q master >output.out 2>&1 &&
+ git rebase --am -q master >output.out 2>&1 &&
test_must_be_empty output.out
'
@@ -325,7 +325,7 @@ test_expect_success 'rebase --am and --show-current-patch' '
echo two >>init.t &&
git commit -a -m two &&
git tag two &&
- test_must_fail git rebase -f --onto init HEAD^ &&
+ test_must_fail git rebase --am -f --onto init HEAD^ &&
GIT_TRACE=1 git rebase --show-current-patch >/dev/null 2>stderr &&
grep "show.*$(git rev-parse two)" stderr
)
diff --git a/t/t3401-rebase-and-am-rename.sh b/t/t3401-rebase-and-am-rename.sh
index a0b9438b22..50803958fd 100755
--- a/t/t3401-rebase-and-am-rename.sh
+++ b/t/t3401-rebase-and-am-rename.sh
@@ -52,13 +52,13 @@ test_expect_success 'rebase --interactive: directory rename detected' '
)
'
-test_expect_failure 'rebase (am): directory rename detected' '
+test_expect_failure 'rebase --am: directory rename detected' '
(
cd dir-rename &&
git checkout B^0 &&
- git -c merge.directoryRenames=true rebase A &&
+ git -c merge.directoryRenames=true rebase --am A &&
git ls-files -s >out &&
test_line_count = 5 out &&
diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
index a31583eb2f..f964b2cd41 100755
--- a/t/t3404-rebase-interactive.sh
+++ b/t/t3404-rebase-interactive.sh
@@ -1138,7 +1138,7 @@ test_expect_success C_LOCALE_OUTPUT 'rebase --edit-todo does not work on non-int
git checkout conflict-branch &&
(
set_fake_editor &&
- test_must_fail git rebase -f --onto HEAD~2 HEAD~ &&
+ test_must_fail git rebase -f --am --onto HEAD~2 HEAD~ &&
test_must_fail git rebase --edit-todo
) &&
git rebase --abort
diff --git a/t/t3406-rebase-message.sh b/t/t3406-rebase-message.sh
index 0c2c569f95..7ce617fc1f 100755
--- a/t/t3406-rebase-message.sh
+++ b/t/t3406-rebase-message.sh
@@ -23,24 +23,24 @@ test_expect_success 'rebase -m' '
'
test_expect_success 'rebase against master twice' '
- git rebase master >out &&
+ git rebase --am master >out &&
test_i18ngrep "Current branch topic is up to date" out
'
test_expect_success 'rebase against master twice with --force' '
- git rebase --force-rebase master >out &&
+ git rebase --force-rebase --am master >out &&
test_i18ngrep "Current branch topic is up to date, rebase forced" out
'
test_expect_success 'rebase against master twice from another branch' '
git checkout topic^ &&
- git rebase master topic >out &&
+ git rebase --am master topic >out &&
test_i18ngrep "Current branch topic is up to date" out
'
test_expect_success 'rebase fast-forward to master' '
git checkout topic^ &&
- git rebase topic >out &&
+ git rebase --am topic >out &&
test_i18ngrep "Fast-forwarded HEAD to topic" out
'
@@ -89,7 +89,7 @@ test_expect_success 'GIT_REFLOG_ACTION' '
git checkout -b reflog-topic start &&
test_commit reflog-to-rebase &&
- git rebase reflog-onto &&
+ git rebase --am reflog-onto &&
git log -g --format=%gs -3 >actual &&
cat >expect <<-\EOF &&
rebase finished: returning to refs/heads/reflog-topic
@@ -99,7 +99,7 @@ test_expect_success 'GIT_REFLOG_ACTION' '
test_cmp expect actual &&
git checkout -b reflog-prefix reflog-to-rebase &&
- GIT_REFLOG_ACTION=change-the-reflog git rebase reflog-onto &&
+ GIT_REFLOG_ACTION=change-the-reflog git rebase --am reflog-onto &&
git log -g --format=%gs -3 >actual &&
cat >expect <<-\EOF &&
rebase finished: returning to refs/heads/reflog-prefix
diff --git a/t/t3407-rebase-abort.sh b/t/t3407-rebase-abort.sh
index 910f218284..3e31826170 100755
--- a/t/t3407-rebase-abort.sh
+++ b/t/t3407-rebase-abort.sh
@@ -96,14 +96,14 @@ testrebase() {
'
}
-testrebase "" .git/rebase-apply
+testrebase " --am" .git/rebase-apply
testrebase " --merge" .git/rebase-merge
-test_expect_success 'rebase --quit' '
+test_expect_success 'rebase --am --quit' '
cd "$work_dir" &&
# Clean up the state from the previous one
git reset --hard pre-rebase &&
- test_must_fail git rebase master &&
+ test_must_fail git rebase --am master &&
test_path_is_dir .git/rebase-apply &&
head_before=$(git rev-parse HEAD) &&
git rebase --quit &&
diff --git a/t/t3420-rebase-autostash.sh b/t/t3420-rebase-autostash.sh
index 5f7e73cf83..3816159e20 100755
--- a/t/t3420-rebase-autostash.sh
+++ b/t/t3420-rebase-autostash.sh
@@ -234,7 +234,7 @@ test_expect_success "rebase: noop rebase" '
git checkout feature-branch
'
-testrebase "" .git/rebase-apply
+testrebase " --am" .git/rebase-apply
testrebase " --merge" .git/rebase-merge
testrebase " --interactive" .git/rebase-merge
diff --git a/t/t3425-rebase-topology-merges.sh b/t/t3425-rebase-topology-merges.sh
index fd8efe84fe..19700b025b 100755
--- a/t/t3425-rebase-topology-merges.sh
+++ b/t/t3425-rebase-topology-merges.sh
@@ -54,7 +54,7 @@ test_run_rebase () {
test_linear_range 'n o' e..
"
}
-test_run_rebase success ''
+test_run_rebase success --am
test_run_rebase success -m
test_run_rebase success -i
@@ -70,7 +70,7 @@ test_run_rebase () {
test_linear_range "\'"$expected"\'" d..
"
}
-test_run_rebase success 'n o e' ''
+test_run_rebase success 'n o e' --am
test_run_rebase success 'n o e' -m
test_run_rebase success 'n o e' -i
@@ -86,7 +86,7 @@ test_run_rebase () {
test_linear_range "\'"$expected"\'" c..
"
}
-test_run_rebase success 'd n o e' ''
+test_run_rebase success 'd n o e' --am
test_run_rebase success 'd n o e' -m
test_run_rebase success 'd n o e' -i
@@ -102,7 +102,7 @@ test_run_rebase () {
test_linear_range "\'"$expected"\'" c..
"
}
-test_run_rebase success 'd n o e' ''
+test_run_rebase success 'd n o e' --am
test_run_rebase success 'd n o e' -m
test_run_rebase success 'd n o e' -i
diff --git a/t/t3432-rebase-fast-forward.sh b/t/t3432-rebase-fast-forward.sh
index 40388ccf9f..4b3cecce56 100755
--- a/t/t3432-rebase-fast-forward.sh
+++ b/t/t3432-rebase-fast-forward.sh
@@ -28,8 +28,8 @@ test_rebase_same_head () {
shift &&
cmp_f="$1" &&
shift &&
- test_rebase_same_head_ $status_n $what_n $cmp_n "" "$*" &&
- test_rebase_same_head_ $status_f $what_f $cmp_f " --no-ff" "$*"
+ test_rebase_same_head_ $status_n $what_n $cmp_n " --am" "$*" &&
+ test_rebase_same_head_ $status_f $what_f $cmp_f " --am --no-ff" "$*"
test_rebase_same_head_ $status_n $what_n $cmp_n " --merge" "$*" &&
test_rebase_same_head_ $status_f $what_f $cmp_f " --merge --no-ff" "$*"
}
diff --git a/t/t5407-post-rewrite-hook.sh b/t/t5407-post-rewrite-hook.sh
index 7344253bfb..a8a73616e4 100755
--- a/t/t5407-post-rewrite-hook.sh
+++ b/t/t5407-post-rewrite-hook.sh
@@ -53,10 +53,10 @@ test_expect_success 'git commit --amend --no-post-rewrite' '
test ! -f post-rewrite.data
'
-test_expect_success 'git rebase' '
+test_expect_success 'git rebase --am' '
git reset --hard D &&
clear_hook_input &&
- test_must_fail git rebase --onto A B &&
+ test_must_fail git rebase --am --onto A B &&
echo C > foo &&
git add foo &&
git rebase --continue &&
@@ -68,10 +68,10 @@ test_expect_success 'git rebase' '
verify_hook_input
'
-test_expect_success 'git rebase --skip' '
+test_expect_success 'git rebase --am --skip' '
git reset --hard D &&
clear_hook_input &&
- test_must_fail git rebase --onto A B &&
+ test_must_fail git rebase --am --onto A B &&
test_must_fail git rebase --skip &&
echo D > foo &&
git add foo &&
@@ -84,10 +84,10 @@ test_expect_success 'git rebase --skip' '
verify_hook_input
'
-test_expect_success 'git rebase --skip the last one' '
+test_expect_success 'git rebase --am --skip the last one' '
git reset --hard F &&
clear_hook_input &&
- test_must_fail git rebase --onto D A &&
+ test_must_fail git rebase --am --onto D A &&
git rebase --skip &&
echo rebase >expected.args &&
cat >expected.data <<-EOF &&
diff --git a/t/t7512-status-help.sh b/t/t7512-status-help.sh
index 66d7a62797..d22b0acf2a 100755
--- a/t/t7512-status-help.sh
+++ b/t/t7512-status-help.sh
@@ -71,10 +71,10 @@ test_expect_success 'prepare for rebase conflicts' '
'
-test_expect_success 'status when rebase in progress before resolving conflicts' '
+test_expect_success 'status when rebase --am in progress before resolving conflicts' '
test_when_finished "git rebase --abort" &&
ONTO=$(git rev-parse --short HEAD^^) &&
- test_must_fail git rebase HEAD^ --onto HEAD^^ &&
+ test_must_fail git rebase --am HEAD^ --onto HEAD^^ &&
cat >expected <<EOF &&
rebase in progress; onto $ONTO
You are currently rebasing branch '\''rebase_conflicts'\'' on '\''$ONTO'\''.
@@ -94,11 +94,11 @@ EOF
'
-test_expect_success 'status when rebase in progress before rebase --continue' '
+test_expect_success 'status when rebase --am in progress before rebase --continue' '
git reset --hard rebase_conflicts &&
test_when_finished "git rebase --abort" &&
ONTO=$(git rev-parse --short HEAD^^) &&
- test_must_fail git rebase HEAD^ --onto HEAD^^ &&
+ test_must_fail git rebase --am HEAD^ --onto HEAD^^ &&
echo three >main.txt &&
git add main.txt &&
cat >expected <<EOF &&
@@ -688,7 +688,7 @@ EOF
'
-test_expect_success 'status when rebase conflicts with statushints disabled' '
+test_expect_success 'status when rebase --am conflicts with statushints disabled' '
git reset --hard master &&
git checkout -b statushints_disabled &&
test_when_finished "git config --local advice.statushints true" &&
@@ -698,7 +698,7 @@ test_expect_success 'status when rebase conflicts with statushints disabled' '
test_commit three_statushints main.txt three &&
test_when_finished "git rebase --abort" &&
ONTO=$(git rev-parse --short HEAD^^) &&
- test_must_fail git rebase HEAD^ --onto HEAD^^ &&
+ test_must_fail git rebase --am HEAD^ --onto HEAD^^ &&
cat >expected <<EOF &&
rebase in progress; onto $ONTO
You are currently rebasing branch '\''statushints_disabled'\'' on '\''$ONTO'\''.
--
gitgitgadget
^ permalink raw reply related [flat|nested] 161+ messages in thread
* [PATCH v4 17/19] rebase tests: repeat some tests using the merge backend instead of am
2020-01-16 6:14 ` [PATCH v4 00/19] rebase: make the default backend configurable Elijah Newren via GitGitGadget
` (15 preceding siblings ...)
2020-01-16 6:14 ` [PATCH v4 16/19] rebase tests: mark tests specific to the am-backend with --am Elijah Newren via GitGitGadget
@ 2020-01-16 6:14 ` Elijah Newren via GitGitGadget
2020-01-16 6:14 ` [PATCH v4 18/19] rebase: make the backend configurable via config setting Elijah Newren via GitGitGadget
` (3 subsequent siblings)
20 siblings, 0 replies; 161+ messages in thread
From: Elijah Newren via GitGitGadget @ 2020-01-16 6:14 UTC (permalink / raw)
To: git
Cc: Johannes.Schindelin, phillip.wood, liu.denton, gitster, plroskin,
alban.gruin, szeder.dev, jrnieder, emilyshaffer, Elijah Newren,
Elijah Newren
From: Elijah Newren <newren@gmail.com>
In order to ensure the merge/interactive backend gets similar coverage
to the am one, add some tests for cases where previously only the am
backend was tested.
Signed-off-by: Elijah Newren <newren@gmail.com>
---
t/t5520-pull.sh | 17 +++++++++++++++--
t/t6047-diff3-conflict-markers.sh | 13 +++++++++++--
2 files changed, 26 insertions(+), 4 deletions(-)
diff --git a/t/t5520-pull.sh b/t/t5520-pull.sh
index 602d996a33..3fff6a06fa 100755
--- a/t/t5520-pull.sh
+++ b/t/t5520-pull.sh
@@ -277,14 +277,27 @@ test_expect_success '--rebase' '
test_cmp expect actual
'
-test_expect_success '--rebase fast forward' '
+test_expect_success '--rebase (merge) fast forward' '
git reset --hard before-rebase &&
git checkout -b ff &&
echo another modification >file &&
git commit -m third file &&
git checkout to-rebase &&
- git pull --rebase . ff &&
+ git -c rebase.backend=merge pull --rebase . ff &&
+ test_cmp_rev HEAD ff &&
+
+ # The above only validates the result. Did we actually bypass rebase?
+ git reflog -1 >reflog.actual &&
+ sed "s/^[0-9a-f][0-9a-f]*/OBJID/" reflog.actual >reflog.fuzzy &&
+ echo "OBJID HEAD@{0}: pull --rebase . ff: Fast-forward" >reflog.expected &&
+ test_cmp reflog.expected reflog.fuzzy
+'
+
+test_expect_success '--rebase (am) fast forward' '
+ git reset --hard before-rebase &&
+
+ git -c rebase.backend=am pull --rebase . ff &&
test_cmp_rev HEAD ff &&
# The above only validates the result. Did we actually bypass rebase?
diff --git a/t/t6047-diff3-conflict-markers.sh b/t/t6047-diff3-conflict-markers.sh
index 860542aad0..d383ce8130 100755
--- a/t/t6047-diff3-conflict-markers.sh
+++ b/t/t6047-diff3-conflict-markers.sh
@@ -186,7 +186,7 @@ test_expect_success 'check multiple merge bases' '
)
'
-test_expect_success 'rebase describes fake ancestor base' '
+test_expect_success 'rebase --merge describes parent of commit being picked' '
test_create_repo rebase &&
(
cd rebase &&
@@ -194,7 +194,16 @@ test_expect_success 'rebase describes fake ancestor base' '
test_commit master file &&
git checkout -b side HEAD^ &&
test_commit side file &&
- test_must_fail git -c merge.conflictstyle=diff3 rebase master &&
+ test_must_fail git -c merge.conflictstyle=diff3 rebase --merge master &&
+ grep "||||||| parent of" file
+ )
+'
+
+test_expect_success 'rebase --am describes fake ancestor base' '
+ (
+ cd rebase &&
+ git rebase --abort &&
+ test_must_fail git -c merge.conflictstyle=diff3 rebase --am master &&
grep "||||||| constructed merge base" file
)
'
--
gitgitgadget
^ permalink raw reply related [flat|nested] 161+ messages in thread
* [PATCH v4 18/19] rebase: make the backend configurable via config setting
2020-01-16 6:14 ` [PATCH v4 00/19] rebase: make the default backend configurable Elijah Newren via GitGitGadget
` (16 preceding siblings ...)
2020-01-16 6:14 ` [PATCH v4 17/19] rebase tests: repeat some tests using the merge backend instead of am Elijah Newren via GitGitGadget
@ 2020-01-16 6:14 ` Elijah Newren via GitGitGadget
2020-01-16 6:14 ` [PATCH v4 19/19] rebase: change the default backend from "am" to "merge" Elijah Newren via GitGitGadget
` (2 subsequent siblings)
20 siblings, 0 replies; 161+ messages in thread
From: Elijah Newren via GitGitGadget @ 2020-01-16 6:14 UTC (permalink / raw)
To: git
Cc: Johannes.Schindelin, phillip.wood, liu.denton, gitster, plroskin,
alban.gruin, szeder.dev, jrnieder, emilyshaffer, Elijah Newren,
Elijah Newren
From: Elijah Newren <newren@gmail.com>
Signed-off-by: Elijah Newren <newren@gmail.com>
---
Documentation/config/rebase.txt | 8 ++++++++
builtin/rebase.c | 31 ++++++++++++++++++++++++-------
2 files changed, 32 insertions(+), 7 deletions(-)
diff --git a/Documentation/config/rebase.txt b/Documentation/config/rebase.txt
index d98e32d812..e6ae30c999 100644
--- a/Documentation/config/rebase.txt
+++ b/Documentation/config/rebase.txt
@@ -5,6 +5,14 @@ rebase.useBuiltin::
is always used. Setting this will emit a warning, to alert any
remaining users that setting this now does nothing.
+rebase.backend::
+ Default backend to use for rebasing. Possible choices are
+ 'am' or 'merge' (note that the merge backend is sometimes also
+ refered to as the interactive backend or the interactive
+ machinery elsewhere in the docs). Also, in the future, if the
+ merge backend gains all remaining capabilities of the am
+ backend, this setting may become unused.
+
rebase.stat::
Whether to show a diffstat of what changed upstream since the last
rebase. False by default.
diff --git a/builtin/rebase.c b/builtin/rebase.c
index ecd23d3ee7..b96de5809e 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -60,6 +60,7 @@ enum empty_type {
struct rebase_options {
enum rebase_type type;
enum empty_type empty;
+ const char *default_backend;
const char *state_dir;
struct commit *upstream;
const char *upstream_name;
@@ -100,6 +101,7 @@ struct rebase_options {
#define REBASE_OPTIONS_INIT { \
.type = REBASE_UNSPECIFIED, \
.empty = EMPTY_UNSPECIFIED, \
+ .default_backend = "am", \
.flags = REBASE_NO_QUIET, \
.git_am_opts = ARGV_ARRAY_INIT, \
.git_format_patch_opt = STRBUF_INIT \
@@ -1272,6 +1274,10 @@ static int rebase_config(const char *var, const char *value, void *data)
return 0;
}
+ if (!strcmp(var, "rebase.backend")) {
+ return git_config_string(&opts->default_backend, var, value);
+ }
+
return git_default_config(var, value, data);
}
@@ -1900,9 +1906,23 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
if (strcmp(options.git_am_opts.argv[i], "-q"))
break;
- if (is_interactive(&options) && i >= 0)
- die(_("cannot combine am options with either "
- "interactive or merge options"));
+ if (i >= 0) {
+ if (is_interactive(&options))
+ die(_("cannot combine am options with either "
+ "interactive or merge options"));
+ else
+ options.type = REBASE_AM;
+ }
+ }
+
+ if (options.type == REBASE_UNSPECIFIED) {
+ if (!strcmp(options.default_backend, "merge"))
+ options.type = REBASE_MERGE;
+ else if (!strcmp(options.default_backend, "am"))
+ options.type = REBASE_AM;
+ else
+ die(_("Unknown rebase backend: %s"),
+ options.default_backend);
}
switch (options.type) {
@@ -1915,10 +1935,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
options.state_dir = apply_dir();
break;
default:
- /* the default rebase backend is `--am` */
- options.type = REBASE_AM;
- options.state_dir = apply_dir();
- break;
+ BUG("options.type was just set above; should be unreachable.");
}
if (options.empty == EMPTY_UNSPECIFIED) {
--
gitgitgadget
^ permalink raw reply related [flat|nested] 161+ messages in thread
* [PATCH v4 19/19] rebase: change the default backend from "am" to "merge"
2020-01-16 6:14 ` [PATCH v4 00/19] rebase: make the default backend configurable Elijah Newren via GitGitGadget
` (17 preceding siblings ...)
2020-01-16 6:14 ` [PATCH v4 18/19] rebase: make the backend configurable via config setting Elijah Newren via GitGitGadget
@ 2020-01-16 6:14 ` Elijah Newren via GitGitGadget
2020-01-17 16:58 ` [PATCH v4 00/19] rebase: make the default backend configurable Phillip Wood
2020-02-15 21:36 ` [PATCH v5 00/20] rebase: make the default backend configurable and change the default Elijah Newren via GitGitGadget
20 siblings, 0 replies; 161+ messages in thread
From: Elijah Newren via GitGitGadget @ 2020-01-16 6:14 UTC (permalink / raw)
To: git
Cc: Johannes.Schindelin, phillip.wood, liu.denton, gitster, plroskin,
alban.gruin, szeder.dev, jrnieder, emilyshaffer, Elijah Newren,
Elijah Newren
From: Elijah Newren <newren@gmail.com>
The am-backend drops information and thus limits what we can do:
* lack of full tree information from the original commits means we
cannot do directory rename detection and warn users that they might
want to move some of their new files that they placed in old
directories to prevent their becoming orphaned.[1]
* reduction in context from only having a few lines beyond those
changed means that when context lines are non-unique we can apply
patches incorrectly.[2]
* lack of access to original commits means that conflict marker
annotation has less information available.
Also, the merge/interactive backend have far more abilities, appear to
currently have a slight performance advantage[3] and have room for more
optimizations than the am backend[4] (and work is underway to take
advantage of some of those possibilities).
[1] https://lore.kernel.org/git/xmqqh8jeh1id.fsf@gitster-ct.c.googlers.com/
[2] https://lore.kernel.org/git/CABPp-BGiu2nVMQY_t-rnFR5GQUz_ipyEE8oDocKeO+h+t4Mn4A@mail.gmail.com/
[3] https://public-inbox.org/git/CABPp-BF=ev03WgODk6TMQmuNoatg2kiEe5DR__gJ0OTVqHSnfQ@mail.gmail.com/
[4] https://lore.kernel.org/git/CABPp-BGh7yW69QwxQb13K0HM38NKmQif3A6C6UULEKYnkEJ5vA@mail.gmail.com/
Signed-off-by: Elijah Newren <newren@gmail.com>
---
Documentation/git-rebase.txt | 2 +-
builtin/rebase.c | 4 ++--
t/t5520-pull.sh | 10 ++++++----
t/t9106-git-svn-commit-diff-clobber.sh | 3 ++-
4 files changed, 11 insertions(+), 8 deletions(-)
diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index 5230084be9..880e8bff5a 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -313,7 +313,7 @@ See also INCOMPATIBLE OPTIONS below.
--merge::
Use merging strategies to rebase. When the recursive (default) merge
strategy is used, this allows rebase to be aware of renames on the
- upstream side.
+ upstream side. This is the default.
+
Note that a rebase merge works by replaying each commit from the working
branch on top of the <upstream> branch. Because of this, when a merge
diff --git a/builtin/rebase.c b/builtin/rebase.c
index b96de5809e..5c5526f42e 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -101,7 +101,7 @@ struct rebase_options {
#define REBASE_OPTIONS_INIT { \
.type = REBASE_UNSPECIFIED, \
.empty = EMPTY_UNSPECIFIED, \
- .default_backend = "am", \
+ .default_backend = "merge", \
.flags = REBASE_NO_QUIET, \
.git_am_opts = ARGV_ARRAY_INIT, \
.git_format_patch_opt = STRBUF_INIT \
@@ -1917,7 +1917,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
if (options.type == REBASE_UNSPECIFIED) {
if (!strcmp(options.default_backend, "merge"))
- options.type = REBASE_MERGE;
+ imply_interactive(&options, "--merge");
else if (!strcmp(options.default_backend, "am"))
options.type = REBASE_AM;
else
diff --git a/t/t5520-pull.sh b/t/t5520-pull.sh
index 3fff6a06fa..4f9e7f7ff6 100755
--- a/t/t5520-pull.sh
+++ b/t/t5520-pull.sh
@@ -340,7 +340,7 @@ test_expect_success '--rebase with conflicts shows advice' '
test_tick &&
git commit -m "Create conflict" seq.txt &&
test_must_fail git pull --rebase . seq 2>err >out &&
- test_i18ngrep "Resolve all conflicts manually" out
+ test_i18ngrep "Resolve all conflicts manually" err
'
test_expect_success 'failed --rebase shows advice' '
@@ -354,7 +354,7 @@ test_expect_success 'failed --rebase shows advice' '
git checkout -f -b fails-to-rebase HEAD^ &&
test_commit v2-without-cr file "2" file2-lf &&
test_must_fail git pull --rebase . diverging 2>err >out &&
- test_i18ngrep "Resolve all conflicts manually" out
+ test_i18ngrep "Resolve all conflicts manually" err
'
test_expect_success '--rebase fails with multiple branches' '
@@ -774,8 +774,10 @@ test_expect_success 'git pull --rebase does not reapply old patches' '
(
cd dst &&
test_must_fail git pull --rebase &&
- find .git/rebase-apply -name "000*" >patches &&
- test_line_count = 1 patches
+ cat .git/rebase-merge/done .git/rebase-merge/git-rebase-todo >work &&
+ grep -v -e \# -e ^$ work >patches &&
+ test_line_count = 1 patches &&
+ rm -f work
)
'
diff --git a/t/t9106-git-svn-commit-diff-clobber.sh b/t/t9106-git-svn-commit-diff-clobber.sh
index dbe8deac0d..aec45bca3b 100755
--- a/t/t9106-git-svn-commit-diff-clobber.sh
+++ b/t/t9106-git-svn-commit-diff-clobber.sh
@@ -92,7 +92,8 @@ test_expect_success 'multiple dcommit from git svn will not clobber svn' "
test_expect_success 'check that rebase really failed' '
- test -d .git/rebase-apply
+ git status >output &&
+ grep currently.rebasing output
'
test_expect_success 'resolve, continue the rebase and dcommit' "
--
gitgitgadget
^ permalink raw reply related [flat|nested] 161+ messages in thread
* Re: [PATCH v3 15/15] rebase: change the default backend from "am" to "merge"
2020-01-12 17:59 ` Johannes Schindelin
@ 2020-01-16 6:32 ` Elijah Newren
2020-01-16 7:58 ` Jonathan Nieder
2020-01-16 10:48 ` Johannes Schindelin
0 siblings, 2 replies; 161+ messages in thread
From: Elijah Newren @ 2020-01-16 6:32 UTC (permalink / raw)
To: Johannes Schindelin
Cc: Phillip Wood, Jonathan Nieder, Elijah Newren via GitGitGadget,
Git Mailing List, Denton Liu, Junio C Hamano, Pavel Roskin,
Alban Gruin, SZEDER Gábor
Hi,
On Sun, Jan 12, 2020 at 9:59 AM Johannes Schindelin
<Johannes.Schindelin@gmx.de> wrote:
>
> Hi,
>
> On Sat, 11 Jan 2020, Phillip Wood wrote:
>
> > On 11/01/2020 01:16, Elijah Newren wrote:
> > >
> > > On Fri, Jan 10, 2020 at 3:14 PM Jonathan Nieder <jrnieder@gmail.com> wrote:
> > > >
> > > > Elijah Newren via GitGitGadget wrote:
> > > >
> > > > 1. "git rebase --am" does not invoke the post-commit hook, but "git
> > > > rebase --merge" does. Is this behavior change intended?
> > > >
> > > > Noticed because jiri[1] installs a post-commit hook that warns
> > > > about commits on detached HEAD, so this change makes rebases more
> > > > noisy in repositories that were set up using jiri.
> >
> > Perhaps that hook could learn not to warn if a branch is being rebased?
> > git could be more helpful there by having a porcelain option to status
> > that prints the branch name if we're rebasing (`git worktree --list`
> > shows the branch correctly when it's being rebased but does not (yet - I
> > have a patch to do it) mark the current worktree so isn't very helpful.)
> >
> > > I've never used a post-commit hook or seen one in the wild. Certainly
> > > wasn't intentional, but it's not clear to me if it's wrong or right
> > > either. I don't see why it would make sense to distinguish between
> > > any of git rebase --am/--merge/--interactive, but it isn't too
> > > surprising that by historical accident the two rebase backends which
> > > happened to call git-commit behind the scenes would call a post-commit
> > > hook and the other rebase backend that didn't call git-commit
> > > wouldn't.
> >
> > Looking through the history the am based rebase has never run the post-commit
> > hook as am has its own set of hooks and the scripted version used commit-tree.
> > The merge based rebase ran `git commit` which ran the post commit hook. The
> > interactive rebase ran the hook until and I broke it in a356ee4659b
> > ("sequencer: try to commit without forking 'git commit'", 2017-11-24) and
> > after I fixed it in 4627bc777e ("sequencer: run post-commit hook",
> > 2019-10-15). As it was broken for two years with no one noticing it can't be
> > that popular.
>
> Maybe a crazy idea, but maybe not: how about running the `post-commit`
> hook _only_ if `--merge` was specified explicitly, and in that case (and
> guarded behind a check verifying that the `post-commit` hook _actually_
> exists _and_ is executable) warn the user that this hook won't be run in
> future versions?
>
> To make things better for users who actually want to run that hook during
> rebases, we could introduce a config option, say,
> `rebase.runPostCommitHook` that is a tri-state (`true`, `false`,
> `onlyForDashDashMerge`, at first defaulting to the last, eventually to
> `false`).
>
> Crazy? Or helpful?
Seems crazy. Why would you want it just for --merge? If anything, I
would think --merge should be most like --am; if some mode of rebase
were to be considered special, I'd think it'd only be the _explicitly_
interactive case. But I don't see the justification for treating any
of the rebase modes differently. I think the hook should be on for
all of them, or off for all of them, and I could go either way.
(Honestly, it's tempting to just fix the fact that the interactive
backend needlessly forks a "git commit" process by having it commit on
its own much like builtin/merge.c does. Then omit calling the
post-commit hook and it behaves the same as the am backend and no one
in the world notices because no one in the world uses or cares about
that hook except a few people at Google who happen to be used to the
am-backend and even then aren't convinced whether invoking the hook or
not is right. And if that's wrong and someone has a solid argument
about why and the harm that not calling it does, then big deal, we add
calling the hook sometime in the future...)
> > > But the big question here, is what is correct behavior? Should rebase
> > > call the post-commit hook, or should it skip it? I haven't any clue
> > > what the answer to that is.
> >
> > It's creating a new commit so I lean towards thinking it should run the
> > post-commit hook. As an example I have a post-commit hook that prints a
> > warning if a commit is created on a branch that is being rewritten by
> > one of my scripts in another worktree. There are pre-commit and
> > pre-rebase hooks to try and prevent that, but the warning is there as a
> > last resort if those hooks are by-passed.
>
> I guess you're right, it is quite surprising that the `post-commit` hook
> is _not_ run for `--am` rebases even though commits are created.
I guess part of the problem is whether people think of it as "new
commits" or just any commits. "New commits" are created by "git
commit" and "git merge". rebase and cherry-pick just create
derivatives of existing commits. Given the existence of the
post-rewrite hook, one could argue that the distinction has merit.
I'm not sure either way, so I'm glad Emily started investigating. I'd
rather not deal with that can of worms... :-)
> > > > 2. GIT_REFLOG_ACTION contains "rebase -i" even though the rebase is
> > > > not interactive.
> >
> > If this is important to people I think it should be easy enough to set
> > GIT_REFLOG_ACTION to the appropriate string in builtin/rebase.c (so long
> > as it hasn't already been set by the user) rather than relying on
> > sequencer.c to do it.
>
> I agree (but won't have time to implement it, so maybe I should shut up
> already...)
>
> > > Yep, as does --keep, --exec, --rebase-merges, etc. There are lots of
> > > rebases which use the interactive machinery even if they aren't
> > > explicitly interactive. I've never seen the "-i" in the reflog
> > > message defined, but clearly it has always been used whenever the
> > > interactive machinery was in play regardless of whether the rebase was
> > > interactive. In that regard, I figured that --merge fit in rather
> > > nicely. (And I noted the fact that reflog messages were different
> > > between the backends among the "BEHAVIORAL DIFFERENCES" section of
> > > git-rebase.txt). But if others think we should just drop the -i (much
> > > as we did for the bash prompt), I'd be happy with that too. If we go
> > > that route, I think I'd rather drop the -i in the reflog for all
> > > rebases, not just the
> > > using-the-interactive-machinery-but-not-explicitly-interactive ones.
> > >
> > > > 3. In circumstances I haven't pinned down yet, we get the error
> > > > message "invalid date format: @@2592000 +0000":
> > > >
> > > > $ git rebase --committer-date-is-author-date --onto branch_K
> > > > branch_L~1 branch_L
> > > > $ git checkout --theirs file
> > > > $ git add file
> > > > $ git rebase --continue
> > > > fatal: invalid date format: @@2592000 +0000
> > > > error: could not commit staged changes.
> > > >
> > > > This isn't reproducible without --committer-date-is-author-date.
> > > > More context (the test where it happens) is in [2].
> > >
> > > Interesting. Do you happen to know if this started happening with
> > > ra/rebase-i-more-options, or did it just become an issue with
> > > en/rebase-backend? I looked around at the link you provided and feel
> > > a bit confused; I'm not sure which test does this or how I'd
> > > reproduce.
> >
> > I'm confused by the test as well. As ra/rebase-i-more-options only touched the
> > sequencer then any bugs would only show up in this test (which runs a
> > non-interactive rebase) once en/rbease-backend switched to that backend. It
> > seems likely that ra/rebase-i-more-options is to blame.
> >
> > Jonathan - do you happen to know if your users create empty commits at all?
> > and if so what do they expect rebase to do with them (and any that become
> > empty when they are rebased) - cf
> > https://lore.kernel.org/git/<CABPp-BEH=9qejeqysHYE+AJ+JPaBympZizq-bx_OjArYFa4xUQ@mail.gmail.com>
>
> The double `@` looks very funny. I would be interested in seeing an MCVE.
>
> > > > 4. I suspect the exit status in the "you need to resolve conflicts"
> > > > case has changed. With rebase --am, [3] would automatically
> > > > invoke rebase --abort when conflicts are present, but with rebase
> > > > --merge it does not.
> > > >
> > > > Known?
> > >
> > > Nope, but I would certainly hope that "you need to resolve conflicts"
> > > would result in a non-zero exit status. If it doesn't, that sounds
> > > like a bug in the interactive backend that we need to fix. I'll dig
> > > in.
>
> Yes, exiting with status 0 would be a major bug, and I think it might even
> be a bug that was introduced by me when I re-implemented the core loop of
> the interactive rebase in C.
Ooh, that sounds interesting. Do you have any more details? My
simple testing here shows that we exit with status 1, so we shouldn't
have that problem unless perhaps there was something else in next
(ra/rebase-i-more-options??) or some other special conditions that was
causing it.
> But to me it sounds as if 4. is not so about the exit code but about
> aborting immediately. I do not recall seeing --am rebases to abort,
> though, but to exit with error (and I saw the same behavior in interactive
> rebases).
The aborting "immediately" wasn't done by git but by the code calling
git; following Jonathan's link you'll see the code they are
complaining about is:
func tryRebase(jirix *jiri.X, project Project, branch string) (bool, error) {
scm := gitutil.New(jirix, gitutil.RootDirOpt(project.Path))
if err := scm.Rebase(branch); err != nil {
err := scm.RebaseAbort()
return false, err
}
return true, nil
}
but of course, the scm.Rebase() and scm.RebaseAbort() definitions
aren't within that file so I have no idea what is really being run.
> We will need to see a reduced concrete example (preferably as a new test
> case) of the described behavior.
Yes, please.
^ permalink raw reply [flat|nested] 161+ messages in thread
* Re: [PATCH v3 15/15] rebase: change the default backend from "am" to "merge"
2020-01-16 6:32 ` Elijah Newren
@ 2020-01-16 7:58 ` Jonathan Nieder
2020-01-16 8:06 ` Jonathan Nieder
` (2 more replies)
2020-01-16 10:48 ` Johannes Schindelin
1 sibling, 3 replies; 161+ messages in thread
From: Jonathan Nieder @ 2020-01-16 7:58 UTC (permalink / raw)
To: Elijah Newren
Cc: Johannes Schindelin, Phillip Wood,
Elijah Newren via GitGitGadget, Git Mailing List, Denton Liu,
Junio C Hamano, Pavel Roskin, Alban Gruin, SZEDER Gábor
Elijah Newren wrote:
> Then omit calling the
> post-commit hook and it behaves the same as the am backend and no one
> in the world notices because no one in the world uses or cares about
> that hook except a few people at Google who happen to be used to the
> am-backend
Just responding to this part: I know this was a bit of thinking out
loud, but the "just a few people at Google" bit is counter-productive.
The search Emily ran
<https://github.com/search?l=&q=filename%3A%2Apost-commit%2A&type=Code>
shows that it's fairly common to use a post-commit hook for
deployment, with scripts like
#!/bin/bash
unset GIT_INDEX_FILE
git --work-tree=/var/www/html --git-dir=/home/daniel/proj/.git checkout -f
or
#!/bin/bash
# Sync gh-pages branch with master
#########################################
git checkout gh-pages
git rm -rf -q .
git checkout master -- .
git add .
git commit -am "Syncing gh-pages with master"
git checkout master
And I'm not saying that selfishly --- obviously, from a selfish
perspective, what you're proposing would change behavior the least and
I'd end up with happy users. :) I'm just trying to help with updating
the list's collective model of user behavior.
(Actually, I want to remove jiri's post-commit hook --- so it is only
the example that revealed this behavior change and is not my
motivation for continuing to c