mirror of
https://github.com/git/git.git
synced 2024-11-23 18:05:29 +08:00
Merge branch 'en/rebase-backend'
"git rebase" has learned to use the merge backend (i.e. the machinery that drives "rebase -i") by default, while allowing "--apply" option to use the "apply" backend (e.g. the moral equivalent of "format-patch piped to am"). The rebase.backend configuration variable can be set to customize. * en/rebase-backend: rebase: rename the two primary rebase backends rebase: change the default backend from "am" to "merge" rebase: make the backend configurable via config setting rebase tests: repeat some tests using the merge backend instead of am rebase tests: mark tests specific to the am-backend with --am rebase: drop '-i' from the reflog for interactive-based rebases git-prompt: change the prompt for interactive-based rebases rebase: add an --am option rebase: move incompatibility checks between backend options a bit earlier git-rebase.txt: add more details about behavioral differences of backends rebase: allow more types of rebases to fast-forward t3432: make these tests work with either am or merge backends rebase: fix handling of restrict_revision rebase: make sure to pass along the quiet flag to the sequencer rebase, sequencer: remove the broken GIT_QUIET handling t3406: simplify an already simple test rebase (interactive-backend): fix handling of commits that become empty rebase (interactive-backend): make --keep-empty the default t3404: directly test the behavior of interest git-rebase.txt: update description of --allow-empty-message
This commit is contained in:
commit
8c22bd9ff9
@ -5,6 +5,12 @@ 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
|
||||
'apply' or 'merge'. In the future, if the merge backend gains
|
||||
all remaining capabilities of the apply backend, this setting
|
||||
may become unused.
|
||||
|
||||
rebase.stat::
|
||||
Whether to show a diffstat of what changed upstream since the last
|
||||
rebase. False by default.
|
||||
|
@ -258,16 +258,45 @@ See also INCOMPATIBLE OPTIONS below.
|
||||
original branch. The index and working tree are also left
|
||||
unchanged as a result.
|
||||
|
||||
--keep-empty::
|
||||
Keep the commits that do not change anything from its
|
||||
parents in the result.
|
||||
--apply:
|
||||
Use applying strategies to rebase (calling `git-am`
|
||||
internally). This option may become a no-op in the future
|
||||
once the merge backend handles everything the apply 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
|
||||
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.
|
||||
Other options, like --exec, will use the default of drop unless
|
||||
-i/--interactive is explicitly specified.
|
||||
+
|
||||
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
|
||||
override that behavior, allowing commits with empty changes to
|
||||
be rebased. Now commits with no changes do not cause rebasing
|
||||
to halt.
|
||||
+
|
||||
See also BEHAVIORAL DIFFERENCES and 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.
|
||||
|
||||
@ -286,7 +315,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
|
||||
@ -356,7 +385,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 --apply.
|
||||
+
|
||||
See also INCOMPATIBLE OPTIONS below.
|
||||
|
||||
@ -394,8 +423,9 @@ 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.
|
||||
Implies --apply.
|
||||
+
|
||||
See also INCOMPATIBLE OPTIONS below.
|
||||
|
||||
@ -539,10 +569,11 @@ INCOMPATIBLE OPTIONS
|
||||
|
||||
The following options:
|
||||
|
||||
* --apply
|
||||
* --committer-date-is-author-date
|
||||
* --ignore-date
|
||||
* --whitespace
|
||||
* --ignore-whitespace
|
||||
* --whitespace
|
||||
* -C
|
||||
|
||||
are incompatible with the following options:
|
||||
@ -557,6 +588,7 @@ are incompatible with the following options:
|
||||
* --interactive
|
||||
* --exec
|
||||
* --keep-empty
|
||||
* --empty=
|
||||
* --edit-todo
|
||||
* --root when used in combination with --onto
|
||||
|
||||
@ -565,33 +597,127 @@ 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
|
||||
|
||||
BEHAVIORAL DIFFERENCES
|
||||
-----------------------
|
||||
|
||||
There are some subtle differences how the backends behave.
|
||||
git rebase has two primary backends: apply and merge. (The apply
|
||||
backend used to known as the 'am' backend, but the name led to
|
||||
confusion as it looks like a verb instead of a noun. Also, the merge
|
||||
backend used to be known as the interactive backend, but it is now
|
||||
used for non-interactive cases as well. Both were renamed based on
|
||||
lower-level functionality that underpinned each.) There are some
|
||||
subtle differences in how these two 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 apply 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 merge backend keeps intentionally empty commits. Similar to the
|
||||
apply backend, by default the merge backend drops commits that become
|
||||
empty unless -i/--interactive is specified (in which case it stops and
|
||||
asks the user what to do). The merge backend also has an
|
||||
--empty={drop,keep,ask} option for changing the behavior of 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 apply 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 backend to provide you
|
||||
warnings in such cases.
|
||||
|
||||
Context
|
||||
~~~~~~~
|
||||
|
||||
The apply 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 merge 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 apply 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 apply 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 merge backend works with the full commits on both sides of history
|
||||
and thus has no such limitations.
|
||||
|
||||
Hooks
|
||||
~~~~~
|
||||
|
||||
The apply backend has not traditionally called the post-commit hook,
|
||||
while the merge 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.
|
||||
|
||||
Interruptability
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
The apply backend has safety problems with an ill-timed interrupt; if
|
||||
the user presses Ctrl-C at the wrong time to try to abort the rebase,
|
||||
the rebase can enter a state where it cannot be aborted with a
|
||||
subsequent `git rebase --abort`. The merge backend does not appear to
|
||||
suffer from the same shortcoming. (See
|
||||
https://lore.kernel.org/git/20200207132152.GC2868@szeder.dev/ for
|
||||
details.)
|
||||
|
||||
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 apply backend writes error messages (such as "Your files
|
||||
would be overwritten...") to stdout, while the merge backend writes
|
||||
them to stderr.
|
||||
|
||||
* State directories: The two backends keep their state in different
|
||||
directories under .git/
|
||||
|
||||
include::merge-strategies.txt[]
|
||||
|
||||
|
275
builtin/rebase.c
275
builtin/rebase.c
@ -44,14 +44,22 @@ static GIT_PATH_FUNC(merge_dir, "rebase-merge")
|
||||
|
||||
enum rebase_type {
|
||||
REBASE_UNSPECIFIED = -1,
|
||||
REBASE_AM,
|
||||
REBASE_APPLY,
|
||||
REBASE_MERGE,
|
||||
REBASE_INTERACTIVE,
|
||||
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 *default_backend;
|
||||
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;
|
||||
char *gpg_sign_opt;
|
||||
int autostash;
|
||||
@ -92,6 +99,8 @@ struct rebase_options {
|
||||
|
||||
#define REBASE_OPTIONS_INIT { \
|
||||
.type = REBASE_UNSPECIFIED, \
|
||||
.empty = EMPTY_UNSPECIFIED, \
|
||||
.default_backend = "merge", \
|
||||
.flags = REBASE_NO_QUIET, \
|
||||
.git_am_opts = ARGV_ARRAY_INIT, \
|
||||
.git_format_patch_opt = STRBUF_INIT \
|
||||
@ -110,6 +119,9 @@ 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.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);
|
||||
@ -329,8 +341,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,
|
||||
@ -359,7 +371,7 @@ static int do_interactive_rebase(struct rebase_options *opts, unsigned flags)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int run_rebase_interactive(struct rebase_options *opts,
|
||||
static int run_sequencer_rebase(struct rebase_options *opts,
|
||||
enum action command)
|
||||
{
|
||||
unsigned flags = 0;
|
||||
@ -367,7 +379,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;
|
||||
@ -431,6 +442,21 @@ 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);
|
||||
|
||||
/*
|
||||
* If we ever want to remap --keep-empty to --empty=keep, insert:
|
||||
* opts->empty = unset ? EMPTY_UNSPECIFIED : EMPTY_KEEP;
|
||||
*/
|
||||
opts->type = REBASE_MERGE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char * const builtin_rebase_interactive_usage[] = {
|
||||
N_("git rebase--interactive [<options>]"),
|
||||
NULL
|
||||
@ -444,9 +470,13 @@ 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")),
|
||||
OPT_BOOL(0, "allow-empty-message", &opts.allow_empty_message,
|
||||
N_("allow commits with empty messages")),
|
||||
{ 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),
|
||||
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")),
|
||||
@ -516,28 +546,26 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)
|
||||
warning(_("--[no-]rebase-cousins has no effect without "
|
||||
"--rebase-merges"));
|
||||
|
||||
return !!run_rebase_interactive(&opts, command);
|
||||
return !!run_sequencer_rebase(&opts, command);
|
||||
}
|
||||
|
||||
static int is_interactive(struct rebase_options *opts)
|
||||
static int is_merge(struct rebase_options *opts)
|
||||
{
|
||||
return opts->type == REBASE_INTERACTIVE ||
|
||||
return opts->type == REBASE_MERGE ||
|
||||
opts->type == REBASE_PRESERVE_MERGES;
|
||||
}
|
||||
|
||||
static void imply_interactive(struct rebase_options *opts, const char *option)
|
||||
static void imply_merge(struct rebase_options *opts, const char *option)
|
||||
{
|
||||
switch (opts->type) {
|
||||
case REBASE_AM:
|
||||
case REBASE_APPLY:
|
||||
die(_("%s requires an interactive rebase"), option);
|
||||
break;
|
||||
case REBASE_INTERACTIVE:
|
||||
case REBASE_MERGE:
|
||||
case REBASE_PRESERVE_MERGES:
|
||||
break;
|
||||
case REBASE_MERGE:
|
||||
/* we now implement --merge via --interactive */
|
||||
default:
|
||||
opts->type = REBASE_INTERACTIVE; /* implied */
|
||||
opts->type = REBASE_MERGE; /* implied */
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -663,8 +691,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)
|
||||
@ -746,7 +774,7 @@ static int finish_rebase(struct rebase_options *opts)
|
||||
* user should see them.
|
||||
*/
|
||||
run_command_v_opt(argv_gc_auto, RUN_GIT_CMD);
|
||||
if (opts->type == REBASE_INTERACTIVE) {
|
||||
if (opts->type == REBASE_MERGE) {
|
||||
struct replay_opts replay = REPLAY_OPTS_INIT;
|
||||
|
||||
replay.action = REPLAY_INTERACTIVE_REBASE;
|
||||
@ -1079,8 +1107,8 @@ static int run_specific_rebase(struct rebase_options *opts, enum action action)
|
||||
int status;
|
||||
const char *backend, *backend_func;
|
||||
|
||||
if (opts->type == REBASE_INTERACTIVE) {
|
||||
/* Run builtin interactive rebase */
|
||||
if (opts->type == REBASE_MERGE) {
|
||||
/* Run sequencer-based rebase */
|
||||
setenv("GIT_CHERRY_PICK_HELP", resolvemsg, 1);
|
||||
if (!(opts->flags & REBASE_INTERACTIVE_EXPLICIT)) {
|
||||
setenv("GIT_SEQUENCE_EDITOR", ":", 1);
|
||||
@ -1093,11 +1121,11 @@ static int run_specific_rebase(struct rebase_options *opts, enum action action)
|
||||
opts->gpg_sign_opt = tmp;
|
||||
}
|
||||
|
||||
status = run_rebase_interactive(opts, action);
|
||||
status = run_sequencer_rebase(opts, action);
|
||||
goto finished_rebase;
|
||||
}
|
||||
|
||||
if (opts->type == REBASE_AM) {
|
||||
if (opts->type == REBASE_APPLY) {
|
||||
status = run_am(opts);
|
||||
goto finished_rebase;
|
||||
}
|
||||
@ -1117,8 +1145,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);
|
||||
@ -1136,7 +1162,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);
|
||||
@ -1154,7 +1179,7 @@ static int run_specific_rebase(struct rebase_options *opts, enum action action)
|
||||
add_var(&script_snippet, "git_format_patch_opt",
|
||||
opts->git_format_patch_opt.buf);
|
||||
|
||||
if (is_interactive(opts) &&
|
||||
if (is_merge(opts) &&
|
||||
!(opts->flags & REBASE_INTERACTIVE_EXPLICIT)) {
|
||||
strbuf_addstr(&script_snippet,
|
||||
"GIT_SEQUENCE_EDITOR=:; export GIT_SEQUENCE_EDITOR; ");
|
||||
@ -1179,8 +1204,8 @@ static int run_specific_rebase(struct rebase_options *opts, enum action action)
|
||||
finished_rebase:
|
||||
if (opts->dont_finish_rebase)
|
||||
; /* do nothing */
|
||||
else if (opts->type == REBASE_INTERACTIVE)
|
||||
; /* interactive rebase cleans up after itself */
|
||||
else if (opts->type == REBASE_MERGE)
|
||||
; /* merge backend cleans up after itself */
|
||||
else if (status == 0) {
|
||||
if (!file_exists(state_dir_path("stopped-sha", opts)))
|
||||
finish_rebase(opts);
|
||||
@ -1238,6 +1263,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);
|
||||
}
|
||||
|
||||
@ -1301,6 +1330,18 @@ done:
|
||||
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_APPLY;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* -i followed by -m is still -i */
|
||||
static int parse_opt_merge(const struct option *opt, const char *arg, int unset)
|
||||
{
|
||||
@ -1309,7 +1350,7 @@ static int parse_opt_merge(const struct option *opt, const char *arg, int unset)
|
||||
BUG_ON_OPT_NEG(unset);
|
||||
BUG_ON_OPT_ARG(arg);
|
||||
|
||||
if (!is_interactive(opts))
|
||||
if (!is_merge(opts))
|
||||
opts->type = REBASE_MERGE;
|
||||
|
||||
return 0;
|
||||
@ -1324,12 +1365,35 @@ static int parse_opt_interactive(const struct option *opt, const char *arg,
|
||||
BUG_ON_OPT_NEG(unset);
|
||||
BUG_ON_OPT_ARG(arg);
|
||||
|
||||
opts->type = REBASE_INTERACTIVE;
|
||||
opts->type = REBASE_MERGE;
|
||||
opts->flags |= REBASE_INTERACTIVE_EXPLICIT;
|
||||
|
||||
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);
|
||||
@ -1365,14 +1429,14 @@ static void set_reflog_action(struct rebase_options *options)
|
||||
const char *env;
|
||||
struct strbuf buf = STRBUF_INIT;
|
||||
|
||||
if (!is_interactive(options))
|
||||
if (!is_merge(options))
|
||||
return;
|
||||
|
||||
env = getenv(GIT_REFLOG_ACTION_ENVIRONMENT);
|
||||
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);
|
||||
}
|
||||
@ -1410,6 +1474,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"),
|
||||
@ -1420,7 +1485,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),
|
||||
@ -1461,6 +1526,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, "apply", &options, NULL,
|
||||
N_("use apply 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,
|
||||
@ -1474,8 +1543,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 commits that become empty"),
|
||||
PARSE_OPT_NONEG, parse_opt_empty),
|
||||
{ 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")),
|
||||
@ -1487,9 +1561,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"),
|
||||
@ -1529,7 +1604,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
|
||||
die(_("It looks like 'git am' is in progress. Cannot rebase."));
|
||||
|
||||
if (is_directory(apply_dir())) {
|
||||
options.type = REBASE_AM;
|
||||
options.type = REBASE_APPLY;
|
||||
options.state_dir = apply_dir();
|
||||
} else if (is_directory(merge_dir())) {
|
||||
strbuf_reset(&buf);
|
||||
@ -1541,7 +1616,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
|
||||
strbuf_reset(&buf);
|
||||
strbuf_addf(&buf, "%s/interactive", merge_dir());
|
||||
if(file_exists(buf.buf)) {
|
||||
options.type = REBASE_INTERACTIVE;
|
||||
options.type = REBASE_MERGE;
|
||||
options.flags |= REBASE_INTERACTIVE_EXPLICIT;
|
||||
} else
|
||||
options.type = REBASE_MERGE;
|
||||
@ -1581,12 +1656,12 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
|
||||
die(_("No rebase in progress?"));
|
||||
setenv(GIT_REFLOG_ACTION_ENVIRONMENT, "rebase", 0);
|
||||
|
||||
if (action == ACTION_EDIT_TODO && !is_interactive(&options))
|
||||
if (action == ACTION_EDIT_TODO && !is_merge(&options))
|
||||
die(_("The --edit-todo action can only be used during "
|
||||
"interactive rebase."));
|
||||
|
||||
if (trace2_is_enabled()) {
|
||||
if (is_interactive(&options))
|
||||
if (is_merge(&options))
|
||||
trace2_cmd_mode("interactive");
|
||||
else if (exec.nr)
|
||||
trace2_cmd_mode("interactive-exec");
|
||||
@ -1662,7 +1737,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
|
||||
goto cleanup;
|
||||
}
|
||||
case ACTION_QUIT: {
|
||||
if (options.type == REBASE_INTERACTIVE) {
|
||||
if (options.type == REBASE_MERGE) {
|
||||
struct replay_opts replay = REPLAY_OPTS_INIT;
|
||||
|
||||
replay.action = REPLAY_INTERACTIVE_REBASE;
|
||||
@ -1711,13 +1786,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++)))
|
||||
@ -1737,8 +1819,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_merge(&options, "--empty");
|
||||
|
||||
if (gpg_sign) {
|
||||
free(options.gpg_sign_opt);
|
||||
@ -1748,7 +1830,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
|
||||
if (exec.nr) {
|
||||
int i;
|
||||
|
||||
imply_interactive(&options, "--exec");
|
||||
imply_merge(&options, "--exec");
|
||||
|
||||
strbuf_reset(&buf);
|
||||
for (i = 0; i < exec.nr; i++)
|
||||
@ -1764,7 +1846,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
|
||||
else if (strcmp("no-rebase-cousins", rebase_merges))
|
||||
die(_("Unknown mode: %s"), rebase_merges);
|
||||
options.rebase_merges = 1;
|
||||
imply_interactive(&options, "--rebase-merges");
|
||||
imply_merge(&options, "--rebase-merges");
|
||||
}
|
||||
|
||||
if (strategy_options.nr) {
|
||||
@ -1783,10 +1865,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
|
||||
if (options.strategy) {
|
||||
options.strategy = xstrdup(options.strategy);
|
||||
switch (options.type) {
|
||||
case REBASE_AM:
|
||||
case REBASE_APPLY:
|
||||
die(_("--strategy requires --merge or --interactive"));
|
||||
case REBASE_MERGE:
|
||||
case REBASE_INTERACTIVE:
|
||||
case REBASE_PRESERVE_MERGES:
|
||||
/* compatible */
|
||||
break;
|
||||
@ -1799,47 +1880,65 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
|
||||
}
|
||||
|
||||
if (options.type == REBASE_MERGE)
|
||||
imply_interactive(&options, "--merge");
|
||||
imply_merge(&options, "--merge");
|
||||
|
||||
if (options.root && !options.onto_name)
|
||||
imply_interactive(&options, "--root without --onto");
|
||||
imply_merge(&options, "--root without --onto");
|
||||
|
||||
if (isatty(2) && options.flags & REBASE_NO_QUIET)
|
||||
strbuf_addstr(&options.git_format_patch_opt, " --progress");
|
||||
|
||||
switch (options.type) {
|
||||
case REBASE_MERGE:
|
||||
case REBASE_INTERACTIVE:
|
||||
case REBASE_PRESERVE_MERGES:
|
||||
options.state_dir = merge_dir();
|
||||
break;
|
||||
case REBASE_AM:
|
||||
options.state_dir = apply_dir();
|
||||
break;
|
||||
default:
|
||||
/* the default rebase backend is `--am` */
|
||||
options.type = REBASE_AM;
|
||||
options.state_dir = apply_dir();
|
||||
break;
|
||||
}
|
||||
|
||||
if (reschedule_failed_exec > 0 && !is_interactive(&options))
|
||||
die(_("--reschedule-failed-exec requires "
|
||||
"--exec or --interactive"));
|
||||
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 */
|
||||
if (options.git_am_opts.argc || options.type == REBASE_APPLY) {
|
||||
/* all am options except -q are compatible only with --apply */
|
||||
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 (i >= 0) {
|
||||
if (is_merge(&options))
|
||||
die(_("cannot combine apply options with "
|
||||
"merge options"));
|
||||
else
|
||||
options.type = REBASE_APPLY;
|
||||
}
|
||||
}
|
||||
|
||||
if (options.type == REBASE_UNSPECIFIED) {
|
||||
if (!strcmp(options.default_backend, "merge"))
|
||||
imply_merge(&options, "--merge");
|
||||
else if (!strcmp(options.default_backend, "apply"))
|
||||
options.type = REBASE_APPLY;
|
||||
else
|
||||
die(_("Unknown rebase backend: %s"),
|
||||
options.default_backend);
|
||||
}
|
||||
|
||||
switch (options.type) {
|
||||
case REBASE_MERGE:
|
||||
case REBASE_PRESERVE_MERGES:
|
||||
options.state_dir = merge_dir();
|
||||
break;
|
||||
case REBASE_APPLY:
|
||||
options.state_dir = apply_dir();
|
||||
break;
|
||||
default:
|
||||
BUG("options.type was just set above; should be unreachable.");
|
||||
}
|
||||
|
||||
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_merge(&options))
|
||||
die(_("--reschedule-failed-exec requires "
|
||||
"--exec or --interactive"));
|
||||
if (reschedule_failed_exec >= 0)
|
||||
options.reschedule_failed_exec = reschedule_failed_exec;
|
||||
|
||||
if (options.signoff) {
|
||||
if (options.type == REBASE_PRESERVE_MERGES)
|
||||
die("cannot combine '--signoff' with "
|
||||
@ -2045,12 +2144,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)) {
|
||||
@ -2133,7 +2234,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
|
||||
diff_flush(&opts);
|
||||
}
|
||||
|
||||
if (is_interactive(&options))
|
||||
if (is_merge(&options))
|
||||
goto run_rebase;
|
||||
|
||||
/* Detach HEAD and reset the tree */
|
||||
|
@ -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
|
||||
|
@ -35,7 +35,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)
|
||||
{
|
||||
@ -87,11 +87,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,
|
||||
|
@ -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,
|
||||
|
82
sequencer.c
82
sequencer.c
@ -160,6 +160,8 @@ static GIT_PATH_FUNC(rebase_path_strategy, "rebase-merge/strategy")
|
||||
static GIT_PATH_FUNC(rebase_path_strategy_opts, "rebase-merge/strategy_opts")
|
||||
static GIT_PATH_FUNC(rebase_path_allow_rerere_autoupdate, "rebase-merge/allow_rerere_autoupdate")
|
||||
static GIT_PATH_FUNC(rebase_path_reschedule_failed_exec, "rebase-merge/reschedule-failed-exec")
|
||||
static GIT_PATH_FUNC(rebase_path_drop_redundant_commits, "rebase-merge/drop_redundant_commits")
|
||||
static GIT_PATH_FUNC(rebase_path_keep_redundant_commits, "rebase-merge/keep_redundant_commits")
|
||||
|
||||
static int git_sequencer_config(const char *k, const char *v, void *cb)
|
||||
{
|
||||
@ -290,7 +292,7 @@ int sequencer_remove_state(struct replay_opts *opts)
|
||||
char *eol = strchr(p, '\n');
|
||||
if (eol)
|
||||
*eol = '\0';
|
||||
if (delete_ref("(rebase -i) cleanup", p, NULL, 0) < 0) {
|
||||
if (delete_ref("(rebase) cleanup", p, NULL, 0) < 0) {
|
||||
warning(_("could not delete '%s'"), p);
|
||||
ret = -1;
|
||||
}
|
||||
@ -324,7 +326,7 @@ static const char *action_name(const struct replay_opts *opts)
|
||||
case REPLAY_PICK:
|
||||
return N_("cherry-pick");
|
||||
case REPLAY_INTERACTIVE_REBASE:
|
||||
return N_("rebase -i");
|
||||
return N_("rebase");
|
||||
}
|
||||
die(_("unknown action: %d"), opts->action);
|
||||
}
|
||||
@ -628,7 +630,7 @@ static int do_recursive_merge(struct repository *r,
|
||||
COMMIT_LOCK | SKIP_IF_UNCHANGED))
|
||||
/*
|
||||
* TRANSLATORS: %s will be "revert", "cherry-pick" or
|
||||
* "rebase -i".
|
||||
* "rebase".
|
||||
*/
|
||||
return error(_("%s: Unable to write new index file"),
|
||||
_(action_name(opts)));
|
||||
@ -1485,23 +1487,30 @@ static int is_original_commit_empty(struct commit *commit)
|
||||
}
|
||||
|
||||
/*
|
||||
* Do we run "git commit" with "--allow-empty"?
|
||||
* Should empty commits be allowed? Return status:
|
||||
* <0: Error in is_index_unchanged(r) or is_original_commit_empty(commit)
|
||||
* 0: Halt on empty commit
|
||||
* 1: Allow empty commit
|
||||
* 2: Drop empty commit
|
||||
*/
|
||||
static int allow_empty(struct repository *r,
|
||||
struct replay_opts *opts,
|
||||
struct commit *commit)
|
||||
{
|
||||
int index_unchanged, empty_commit;
|
||||
int index_unchanged, originally_empty;
|
||||
|
||||
/*
|
||||
* Three cases:
|
||||
* Four cases:
|
||||
*
|
||||
* (1) we do not allow empty at all and error out.
|
||||
*
|
||||
* (2) we allow ones that were initially empty, but
|
||||
* forbid the ones that become empty;
|
||||
* (2) we allow ones that were initially empty, and
|
||||
* just drop the ones that become empty
|
||||
*
|
||||
* (3) we allow both.
|
||||
* (3) we allow ones that were initially empty, but
|
||||
* halt for the ones that become empty;
|
||||
*
|
||||
* (4) we allow both.
|
||||
*/
|
||||
if (!opts->allow_empty)
|
||||
return 0; /* let "git commit" barf as necessary */
|
||||
@ -1515,13 +1524,15 @@ static int allow_empty(struct repository *r,
|
||||
if (opts->keep_redundant_commits)
|
||||
return 1;
|
||||
|
||||
empty_commit = is_original_commit_empty(commit);
|
||||
if (empty_commit < 0)
|
||||
return empty_commit;
|
||||
if (!empty_commit)
|
||||
return 0;
|
||||
else
|
||||
originally_empty = is_original_commit_empty(commit);
|
||||
if (originally_empty < 0)
|
||||
return originally_empty;
|
||||
if (originally_empty)
|
||||
return 1;
|
||||
else if (opts->drop_redundant_commits)
|
||||
return 2;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct {
|
||||
@ -1732,7 +1743,7 @@ static int do_pick_commit(struct repository *r,
|
||||
char *author = NULL;
|
||||
struct commit_message msg = { NULL, NULL, NULL, NULL };
|
||||
struct strbuf msgbuf = STRBUF_INIT;
|
||||
int res, unborn = 0, reword = 0, allow;
|
||||
int res, unborn = 0, reword = 0, allow, drop_commit;
|
||||
|
||||
if (opts->no_commit) {
|
||||
/*
|
||||
@ -1937,13 +1948,20 @@ static int do_pick_commit(struct repository *r,
|
||||
goto leave;
|
||||
}
|
||||
|
||||
drop_commit = 0;
|
||||
allow = allow_empty(r, opts, commit);
|
||||
if (allow < 0) {
|
||||
res = allow;
|
||||
goto leave;
|
||||
} else if (allow)
|
||||
} else if (allow == 1) {
|
||||
flags |= ALLOW_EMPTY;
|
||||
if (!opts->no_commit) {
|
||||
} else if (allow == 2) {
|
||||
drop_commit = 1;
|
||||
fprintf(stderr,
|
||||
_("dropping %s %s -- patch contents already upstream\n"),
|
||||
oid_to_hex(&commit->object.oid), msg.subject);
|
||||
} /* else allow == 0 and there's nothing special to do */
|
||||
if (!opts->no_commit && !drop_commit) {
|
||||
if (author || command == TODO_REVERT || (flags & AMEND_MSG))
|
||||
res = do_commit(r, msg_file, author, opts, flags);
|
||||
else
|
||||
@ -2498,6 +2516,12 @@ static int read_populate_opts(struct replay_opts *opts)
|
||||
if (file_exists(rebase_path_reschedule_failed_exec()))
|
||||
opts->reschedule_failed_exec = 1;
|
||||
|
||||
if (file_exists(rebase_path_drop_redundant_commits()))
|
||||
opts->drop_redundant_commits = 1;
|
||||
|
||||
if (file_exists(rebase_path_keep_redundant_commits()))
|
||||
opts->keep_redundant_commits = 1;
|
||||
|
||||
read_strategy_opts(opts, &buf);
|
||||
strbuf_release(&buf);
|
||||
|
||||
@ -2549,8 +2573,6 @@ static void write_strategy_opts(struct replay_opts *opts)
|
||||
int write_basic_state(struct replay_opts *opts, const char *head_name,
|
||||
struct commit *onto, const char *orig_head)
|
||||
{
|
||||
const char *quiet = getenv("GIT_QUIET");
|
||||
|
||||
if (head_name)
|
||||
write_file(rebase_path_head_name(), "%s\n", head_name);
|
||||
if (onto)
|
||||
@ -2559,8 +2581,8 @@ int write_basic_state(struct replay_opts *opts, const char *head_name,
|
||||
if (orig_head)
|
||||
write_file(rebase_path_orig_head(), "%s\n", orig_head);
|
||||
|
||||
if (quiet)
|
||||
write_file(rebase_path_quiet(), "%s\n", quiet);
|
||||
if (opts->quiet)
|
||||
write_file(rebase_path_quiet(), "%s", "");
|
||||
if (opts->verbose)
|
||||
write_file(rebase_path_verbose(), "%s", "");
|
||||
if (opts->strategy)
|
||||
@ -2577,6 +2599,10 @@ int write_basic_state(struct replay_opts *opts, const char *head_name,
|
||||
write_file(rebase_path_gpg_sign_opt(), "-S%s\n", opts->gpg_sign);
|
||||
if (opts->signoff)
|
||||
write_file(rebase_path_signoff(), "--signoff\n");
|
||||
if (opts->drop_redundant_commits)
|
||||
write_file(rebase_path_drop_redundant_commits(), "%s", "");
|
||||
if (opts->keep_redundant_commits)
|
||||
write_file(rebase_path_keep_redundant_commits(), "%s", "");
|
||||
if (opts->reschedule_failed_exec)
|
||||
write_file(rebase_path_reschedule_failed_exec(), "%s", "");
|
||||
|
||||
@ -3176,7 +3202,7 @@ static int do_label(struct repository *r, const char *name, int len)
|
||||
return error(_("illegal label name: '%.*s'"), len, name);
|
||||
|
||||
strbuf_addf(&ref_name, "refs/rewritten/%.*s", len, name);
|
||||
strbuf_addf(&msg, "rebase -i (label) '%.*s'", len, name);
|
||||
strbuf_addf(&msg, "rebase (label) '%.*s'", len, name);
|
||||
|
||||
transaction = ref_store_transaction_begin(refs, &err);
|
||||
if (!transaction) {
|
||||
@ -4563,7 +4589,6 @@ static int make_script_with_merges(struct pretty_print_context *pp,
|
||||
struct rev_info *revs, struct strbuf *out,
|
||||
unsigned flags)
|
||||
{
|
||||
int keep_empty = flags & TODO_LIST_KEEP_EMPTY;
|
||||
int rebase_cousins = flags & TODO_LIST_REBASE_COUSINS;
|
||||
int root_with_onto = flags & TODO_LIST_ROOT_WITH_ONTO;
|
||||
struct strbuf buf = STRBUF_INIT, oneline = STRBUF_INIT;
|
||||
@ -4626,8 +4651,6 @@ static int make_script_with_merges(struct pretty_print_context *pp,
|
||||
if (!to_merge) {
|
||||
/* non-merge commit: easy case */
|
||||
strbuf_reset(&buf);
|
||||
if (!keep_empty && is_empty)
|
||||
strbuf_addf(&buf, "%c ", comment_line_char);
|
||||
strbuf_addf(&buf, "%s %s %s", cmd_pick,
|
||||
oid_to_hex(&commit->object.oid),
|
||||
oneline.buf);
|
||||
@ -4794,7 +4817,6 @@ int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,
|
||||
struct pretty_print_context pp = {0};
|
||||
struct rev_info revs;
|
||||
struct commit *commit;
|
||||
int keep_empty = flags & TODO_LIST_KEEP_EMPTY;
|
||||
const char *insn = flags & TODO_LIST_ABBREVIATE_CMDS ? "p" : "pick";
|
||||
int rebase_merges = flags & TODO_LIST_REBASE_MERGES;
|
||||
|
||||
@ -4830,12 +4852,10 @@ int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,
|
||||
return make_script_with_merges(&pp, &revs, out, flags);
|
||||
|
||||
while ((commit = get_revision(&revs))) {
|
||||
int is_empty = is_original_commit_empty(commit);
|
||||
int is_empty = is_original_commit_empty(commit);
|
||||
|
||||
if (!is_empty && (commit->object.flags & PATCHSAME))
|
||||
continue;
|
||||
if (!keep_empty && is_empty)
|
||||
strbuf_addf(out, "%c ", comment_line_char);
|
||||
strbuf_addf(out, "%s %s ", insn,
|
||||
oid_to_hex(&commit->object.oid));
|
||||
pretty_print_commit(&pp, commit, out);
|
||||
@ -4972,7 +4992,7 @@ int todo_list_write_to_file(struct repository *r, struct todo_list *todo_list,
|
||||
|
||||
todo_list_to_strbuf(r, todo_list, &buf, num, flags);
|
||||
if (flags & TODO_LIST_APPEND_TODO_HELP)
|
||||
append_todo_help(flags & TODO_LIST_KEEP_EMPTY, count_commands(todo_list),
|
||||
append_todo_help(count_commands(todo_list),
|
||||
shortrevisions, shortonto, &buf);
|
||||
|
||||
res = write_message(buf.buf, buf.len, file, 0);
|
||||
|
@ -40,6 +40,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;
|
||||
@ -133,7 +134,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)
|
||||
|
@ -165,19 +165,37 @@ 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 &&
|
||||
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 &&
|
||||
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 (--apply)' '
|
||||
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 --apply &&
|
||||
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 --apply &&
|
||||
git rev-parse --verify default-base >expect &&
|
||||
git rev-parse default~1 >actual &&
|
||||
test_cmp expect actual
|
||||
@ -206,9 +224,15 @@ 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 --apply -q is quiet' '
|
||||
git checkout -b quiet topic &&
|
||||
git rebase -q master >output.out 2>&1 &&
|
||||
git rebase --apply -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
|
||||
'
|
||||
|
||||
@ -291,7 +315,7 @@ EOF
|
||||
test_cmp From_.msg out
|
||||
'
|
||||
|
||||
test_expect_success 'rebase --am and --show-current-patch' '
|
||||
test_expect_success 'rebase --apply and --show-current-patch' '
|
||||
test_create_repo conflict-apply &&
|
||||
(
|
||||
cd conflict-apply &&
|
||||
@ -301,13 +325,13 @@ 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 --apply -f --onto init HEAD^ &&
|
||||
GIT_TRACE=1 git rebase --show-current-patch >/dev/null 2>stderr &&
|
||||
grep "show.*$(git rev-parse two)" stderr
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success 'rebase --am and .gitattributes' '
|
||||
test_expect_success 'rebase --apply and .gitattributes' '
|
||||
test_create_repo attributes &&
|
||||
(
|
||||
cd attributes &&
|
||||
|
@ -52,13 +52,13 @@ test_expect_success 'rebase --interactive: directory rename detected' '
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_failure 'rebase (am): directory rename detected' '
|
||||
test_expect_failure 'rebase --apply: directory rename detected' '
|
||||
(
|
||||
cd dir-rename &&
|
||||
|
||||
git checkout B^0 &&
|
||||
|
||||
git -c merge.directoryRenames=true rebase A &&
|
||||
git -c merge.directoryRenames=true rebase --apply A &&
|
||||
|
||||
git ls-files -s >out &&
|
||||
test_line_count = 5 out &&
|
||||
|
@ -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
|
||||
'
|
||||
|
||||
@ -222,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
|
||||
@ -1137,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 --apply --onto HEAD~2 HEAD~ &&
|
||||
test_must_fail git rebase --edit-todo
|
||||
) &&
|
||||
git rebase --abort
|
||||
@ -1161,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 &&
|
||||
|
@ -18,32 +18,29 @@ 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' '
|
||||
git rebase master >out &&
|
||||
git rebase --apply 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 --apply 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 --apply 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 --apply topic >out &&
|
||||
test_i18ngrep "Fast-forwarded HEAD to topic" out
|
||||
'
|
||||
|
||||
@ -92,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 --apply reflog-onto &&
|
||||
git log -g --format=%gs -3 >actual &&
|
||||
cat >expect <<-\EOF &&
|
||||
rebase finished: returning to refs/heads/reflog-topic
|
||||
@ -102,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 --apply reflog-onto &&
|
||||
git log -g --format=%gs -3 >actual &&
|
||||
cat >expect <<-\EOF &&
|
||||
rebase finished: returning to refs/heads/reflog-prefix
|
||||
|
@ -96,14 +96,14 @@ testrebase() {
|
||||
'
|
||||
}
|
||||
|
||||
testrebase "" .git/rebase-apply
|
||||
testrebase " --apply" .git/rebase-apply
|
||||
testrebase " --merge" .git/rebase-merge
|
||||
|
||||
test_expect_success 'rebase --quit' '
|
||||
test_expect_success 'rebase --apply --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 --apply master &&
|
||||
test_path_is_dir .git/rebase-apply &&
|
||||
head_before=$(git rev-parse HEAD) &&
|
||||
git rebase --quit &&
|
||||
|
@ -34,7 +34,7 @@ test_expect_success setup '
|
||||
remove_progress_re="$(printf "s/.*\\r//")"
|
||||
'
|
||||
|
||||
create_expected_success_am () {
|
||||
create_expected_success_apply () {
|
||||
cat >expected <<-EOF
|
||||
$(grep "^Created autostash: [0-9a-f][0-9a-f]*\$" actual)
|
||||
First, rewinding head to replay your work on top of it...
|
||||
@ -44,7 +44,7 @@ create_expected_success_am () {
|
||||
EOF
|
||||
}
|
||||
|
||||
create_expected_success_interactive () {
|
||||
create_expected_success_merge () {
|
||||
q_to_cr >expected <<-EOF
|
||||
$(grep "^Created autostash: [0-9a-f][0-9a-f]*\$" actual)
|
||||
Applied autostash.
|
||||
@ -52,7 +52,7 @@ create_expected_success_interactive () {
|
||||
EOF
|
||||
}
|
||||
|
||||
create_expected_failure_am () {
|
||||
create_expected_failure_apply () {
|
||||
cat >expected <<-EOF
|
||||
$(grep "^Created autostash: [0-9a-f][0-9a-f]*\$" actual)
|
||||
First, rewinding head to replay your work on top of it...
|
||||
@ -64,7 +64,7 @@ create_expected_failure_am () {
|
||||
EOF
|
||||
}
|
||||
|
||||
create_expected_failure_interactive () {
|
||||
create_expected_failure_merge () {
|
||||
cat >expected <<-EOF
|
||||
$(grep "^Created autostash: [0-9a-f][0-9a-f]*\$" actual)
|
||||
Applying autostash resulted in conflicts.
|
||||
@ -101,9 +101,9 @@ testrebase () {
|
||||
|
||||
test_expect_success "rebase$type --autostash: check output" '
|
||||
test_when_finished git branch -D rebased-feature-branch &&
|
||||
suffix=${type#\ --} && suffix=${suffix:-am} &&
|
||||
if test ${suffix} = "merge"; then
|
||||
suffix=interactive
|
||||
suffix=${type#\ --} && suffix=${suffix:-apply} &&
|
||||
if test ${suffix} = "interactive"; then
|
||||
suffix=merge
|
||||
fi &&
|
||||
create_expected_success_$suffix &&
|
||||
sed "$remove_progress_re" <actual >actual2 &&
|
||||
@ -202,9 +202,9 @@ testrebase () {
|
||||
|
||||
test_expect_success "rebase$type: check output with conflicting stash" '
|
||||
test_when_finished git branch -D rebased-feature-branch &&
|
||||
suffix=${type#\ --} && suffix=${suffix:-am} &&
|
||||
if test ${suffix} = "merge"; then
|
||||
suffix=interactive
|
||||
suffix=${type#\ --} && suffix=${suffix:-apply} &&
|
||||
if test ${suffix} = "interactive"; then
|
||||
suffix=merge
|
||||
fi &&
|
||||
create_expected_failure_$suffix &&
|
||||
sed "$remove_progress_re" <actual >actual2 &&
|
||||
@ -234,7 +234,7 @@ test_expect_success "rebase: noop rebase" '
|
||||
git checkout feature-branch
|
||||
'
|
||||
|
||||
testrebase "" .git/rebase-apply
|
||||
testrebase " --apply" .git/rebase-apply
|
||||
testrebase " --merge" .git/rebase-merge
|
||||
testrebase " --interactive" .git/rebase-merge
|
||||
|
||||
|
@ -26,7 +26,7 @@ test_run_rebase () {
|
||||
test_linear_range 'd e' c..
|
||||
"
|
||||
}
|
||||
test_run_rebase success ''
|
||||
test_run_rebase success --apply
|
||||
test_run_rebase success -m
|
||||
test_run_rebase success -i
|
||||
test_have_prereq !REBASE_P || test_run_rebase success -p
|
||||
@ -50,7 +50,7 @@ test_run_rebase () {
|
||||
test_cmp_rev e HEAD
|
||||
"
|
||||
}
|
||||
test_run_rebase success ''
|
||||
test_run_rebase success --apply
|
||||
test_run_rebase success -m
|
||||
test_run_rebase success -i
|
||||
test_have_prereq !REBASE_P || test_run_rebase success -p
|
||||
@ -66,7 +66,7 @@ test_run_rebase () {
|
||||
test_linear_range 'd e' b..
|
||||
"
|
||||
}
|
||||
test_run_rebase success ''
|
||||
test_run_rebase success --apply
|
||||
test_run_rebase success --fork-point
|
||||
test_run_rebase success -m
|
||||
test_run_rebase success -i
|
||||
@ -83,7 +83,7 @@ test_run_rebase () {
|
||||
test_linear_range 'd e' branch-b..
|
||||
"
|
||||
}
|
||||
test_run_rebase success ''
|
||||
test_run_rebase success --apply
|
||||
test_run_rebase success --fork-point
|
||||
test_run_rebase success -m
|
||||
test_run_rebase success -i
|
||||
@ -98,7 +98,7 @@ test_run_rebase () {
|
||||
test_cmp_rev e HEAD
|
||||
"
|
||||
}
|
||||
test_run_rebase success ''
|
||||
test_run_rebase success --apply
|
||||
test_run_rebase success --fork-point
|
||||
test_run_rebase success -m
|
||||
test_run_rebase success -i
|
||||
@ -139,7 +139,7 @@ test_run_rebase () {
|
||||
test_linear_range 'd i' h..
|
||||
"
|
||||
}
|
||||
test_run_rebase success ''
|
||||
test_run_rebase success --apply
|
||||
test_run_rebase success -m
|
||||
test_run_rebase success -i
|
||||
test_have_prereq !REBASE_P || test_run_rebase success -p
|
||||
@ -154,7 +154,7 @@ test_run_rebase () {
|
||||
test_linear_range 'd' h..
|
||||
"
|
||||
}
|
||||
test_run_rebase success ''
|
||||
test_run_rebase success --apply
|
||||
test_run_rebase success -m
|
||||
test_run_rebase success -i
|
||||
test_have_prereq !REBASE_P || test_run_rebase success -p
|
||||
@ -169,7 +169,7 @@ test_run_rebase () {
|
||||
test_linear_range 'd i' f..
|
||||
"
|
||||
}
|
||||
test_run_rebase success ''
|
||||
test_run_rebase success --apply
|
||||
test_run_rebase success -m
|
||||
test_run_rebase success -i
|
||||
test_have_prereq !REBASE_P || test_run_rebase success -p
|
||||
@ -184,7 +184,7 @@ test_run_rebase () {
|
||||
test_linear_range 'd gp i' h..
|
||||
"
|
||||
}
|
||||
test_run_rebase success ''
|
||||
test_run_rebase success --apply
|
||||
test_run_rebase success -m
|
||||
test_run_rebase success -i
|
||||
test_have_prereq !REBASE_P || test_run_rebase success -p
|
||||
@ -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 --apply
|
||||
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
|
||||
@ -227,10 +227,10 @@ test_run_rebase () {
|
||||
test_linear_range 'd k l' c..
|
||||
"
|
||||
}
|
||||
test_run_rebase success ''
|
||||
test_run_rebase success --apply
|
||||
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
|
||||
@ -242,10 +242,10 @@ test_run_rebase () {
|
||||
test_linear_range 'd k l' j..
|
||||
"
|
||||
}
|
||||
test_run_rebase success ''
|
||||
test_run_rebase success --apply
|
||||
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
|
||||
@ -282,7 +282,7 @@ test_run_rebase () {
|
||||
test_linear_range 'x y' c..
|
||||
"
|
||||
}
|
||||
test_run_rebase success ''
|
||||
test_run_rebase success --apply
|
||||
test_run_rebase success -m
|
||||
test_run_rebase success -i
|
||||
test_have_prereq !REBASE_P || test_run_rebase success -p
|
||||
@ -297,7 +297,7 @@ test_run_rebase () {
|
||||
test_linear_range 'x y' c..
|
||||
"
|
||||
}
|
||||
test_run_rebase success ''
|
||||
test_run_rebase success --apply
|
||||
test_run_rebase success -m
|
||||
test_run_rebase success -i
|
||||
test_have_prereq !REBASE_P || test_run_rebase failure -p
|
||||
@ -312,7 +312,7 @@ test_run_rebase () {
|
||||
test_linear_range 'x y' m..
|
||||
"
|
||||
}
|
||||
test_run_rebase success ''
|
||||
test_run_rebase success --apply
|
||||
test_run_rebase success -m
|
||||
test_run_rebase success -i
|
||||
test_have_prereq !REBASE_P || test_run_rebase success -p
|
||||
@ -328,7 +328,7 @@ test_run_rebase () {
|
||||
"
|
||||
}
|
||||
|
||||
test_run_rebase success ''
|
||||
test_run_rebase success --apply
|
||||
test_run_rebase success -m
|
||||
test_run_rebase success -i
|
||||
test_have_prereq !REBASE_P || test_run_rebase failure -p
|
||||
@ -343,7 +343,7 @@ test_run_rebase () {
|
||||
test_linear_range 'x y' m..
|
||||
"
|
||||
}
|
||||
test_run_rebase success ''
|
||||
test_run_rebase success --apply
|
||||
test_run_rebase success -m
|
||||
test_run_rebase success -i
|
||||
test_have_prereq !REBASE_P || test_run_rebase failure -p
|
||||
|
126
t/t3424-rebase-empty.sh
Executable file
126
t/t3424-rebase-empty.sh
Executable file
@ -0,0 +1,126 @@
|
||||
#!/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 (apply-backend)' '
|
||||
test_when_finished "git rebase --abort" &&
|
||||
git checkout -B testing localmods &&
|
||||
# rebase (--apply) should not drop commits that start empty
|
||||
git rebase --apply upstream &&
|
||||
|
||||
test_write_lines D C B A >expect &&
|
||||
git log --format=%s >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'rebase --merge --empty=drop' '
|
||||
git checkout -B testing localmods &&
|
||||
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 uses default of --empty=drop' '
|
||||
git checkout -B testing localmods &&
|
||||
git rebase --merge 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 &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
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 --empty=ask upstream &&
|
||||
|
||||
git rebase --skip &&
|
||||
|
||||
test_write_lines D C B A >expect &&
|
||||
git log --format=%s >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'rebase --interactive uses default of --empty=ask' '
|
||||
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
|
@ -54,7 +54,7 @@ test_run_rebase () {
|
||||
test_linear_range 'n o' e..
|
||||
"
|
||||
}
|
||||
test_run_rebase success ''
|
||||
test_run_rebase success --apply
|
||||
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' --apply
|
||||
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' --apply
|
||||
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' --apply
|
||||
test_run_rebase success 'd n o e' -m
|
||||
test_run_rebase success 'd n o e' -i
|
||||
|
||||
|
@ -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 --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 &&
|
||||
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' '
|
||||
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 &&
|
||||
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"
|
||||
|
@ -28,8 +28,10 @@ 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 " --apply" "$*" &&
|
||||
test_rebase_same_head_ $status_f $what_f $cmp_f " --apply --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_ () {
|
||||
@ -44,19 +46,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 +69,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 +89,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 +110,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
|
||||
|
@ -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 --apply' '
|
||||
git reset --hard D &&
|
||||
clear_hook_input &&
|
||||
test_must_fail git rebase --onto A B &&
|
||||
test_must_fail git rebase --apply --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 --apply --skip' '
|
||||
git reset --hard D &&
|
||||
clear_hook_input &&
|
||||
test_must_fail git rebase --onto A B &&
|
||||
test_must_fail git rebase --apply --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 --apply --skip the last one' '
|
||||
git reset --hard F &&
|
||||
clear_hook_input &&
|
||||
test_must_fail git rebase --onto D A &&
|
||||
test_must_fail git rebase --apply --onto D A &&
|
||||
git rebase --skip &&
|
||||
echo rebase >expected.args &&
|
||||
cat >expected.data <<-EOF &&
|
||||
@ -128,7 +128,7 @@ test_expect_success 'git rebase -m --skip' '
|
||||
verify_hook_input
|
||||
'
|
||||
|
||||
test_expect_success 'git rebase with implicit use of interactive backend' '
|
||||
test_expect_success 'git rebase with implicit use of merge backend' '
|
||||
git reset --hard D &&
|
||||
clear_hook_input &&
|
||||
test_must_fail git rebase --keep-empty --onto A B &&
|
||||
@ -143,7 +143,7 @@ test_expect_success 'git rebase with implicit use of interactive backend' '
|
||||
verify_hook_input
|
||||
'
|
||||
|
||||
test_expect_success 'git rebase --skip with implicit use of interactive backend' '
|
||||
test_expect_success 'git rebase --skip with implicit use of merge backend' '
|
||||
git reset --hard D &&
|
||||
clear_hook_input &&
|
||||
test_must_fail git rebase --keep-empty --onto A B &&
|
||||
|
@ -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=apply pull --rebase . ff &&
|
||||
test_cmp_rev HEAD ff &&
|
||||
|
||||
# The above only validates the result. Did we actually bypass rebase?
|
||||
@ -327,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' '
|
||||
@ -341,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' '
|
||||
@ -761,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
|
||||
)
|
||||
'
|
||||
|
||||
|
@ -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 --apply describes fake ancestor base' '
|
||||
(
|
||||
cd rebase &&
|
||||
git rebase --abort &&
|
||||
test_must_fail git -c merge.conflictstyle=diff3 rebase --apply master &&
|
||||
grep "||||||| constructed merge base" file
|
||||
)
|
||||
'
|
||||
|
@ -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 --apply 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 --apply 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 --apply 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 --apply 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 --apply 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 --apply HEAD^ --onto HEAD^^ &&
|
||||
cat >expected <<EOF &&
|
||||
rebase in progress; onto $ONTO
|
||||
You are currently rebasing branch '\''statushints_disabled'\'' on '\''$ONTO'\''.
|
||||
|
@ -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' "
|
||||
|
@ -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 --apply b1 b2 &&
|
||||
test_when_finished "git rebase --abort" &&
|
||||
__git_ps1 >"$actual" &&
|
||||
test_cmp expected "$actual"
|
||||
|
Loading…
Reference in New Issue
Block a user