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
|
is always used. Setting this will emit a warning, to alert any
|
||||||
remaining users that setting this now does nothing.
|
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::
|
rebase.stat::
|
||||||
Whether to show a diffstat of what changed upstream since the last
|
Whether to show a diffstat of what changed upstream since the last
|
||||||
rebase. False by default.
|
rebase. False by default.
|
||||||
|
@ -258,16 +258,45 @@ See also INCOMPATIBLE OPTIONS below.
|
|||||||
original branch. The index and working tree are also left
|
original branch. The index and working tree are also left
|
||||||
unchanged as a result.
|
unchanged as a result.
|
||||||
|
|
||||||
--keep-empty::
|
--apply:
|
||||||
Keep the commits that do not change anything from its
|
Use applying strategies to rebase (calling `git-am`
|
||||||
parents in the result.
|
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.
|
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::
|
--allow-empty-message::
|
||||||
By default, rebasing commits with an empty message will fail.
|
No-op. Rebasing commits with an empty message used to fail
|
||||||
This option overrides that behavior, allowing commits with empty
|
and this option would override that behavior, allowing commits
|
||||||
messages to be rebased.
|
with empty messages to be rebased. Now commits with an empty
|
||||||
|
message do not cause rebasing to halt.
|
||||||
+
|
+
|
||||||
See also INCOMPATIBLE OPTIONS below.
|
See also INCOMPATIBLE OPTIONS below.
|
||||||
|
|
||||||
@ -286,7 +315,7 @@ See also INCOMPATIBLE OPTIONS below.
|
|||||||
--merge::
|
--merge::
|
||||||
Use merging strategies to rebase. When the recursive (default) merge
|
Use merging strategies to rebase. When the recursive (default) merge
|
||||||
strategy is used, this allows rebase to be aware of renames on the
|
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
|
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
|
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
|
Ensure at least <n> lines of surrounding context match before
|
||||||
and after each change. When fewer lines of surrounding
|
and after each change. When fewer lines of surrounding
|
||||||
context exist they all must match. By default no context is
|
context exist they all must match. By default no context is
|
||||||
ever ignored.
|
ever ignored. Implies --apply.
|
||||||
+
|
+
|
||||||
See also INCOMPATIBLE OPTIONS below.
|
See also INCOMPATIBLE OPTIONS below.
|
||||||
|
|
||||||
@ -394,8 +423,9 @@ with `--keep-base` in order to drop those commits from your branch.
|
|||||||
|
|
||||||
--ignore-whitespace::
|
--ignore-whitespace::
|
||||||
--whitespace=<option>::
|
--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 linkgit:git-apply[1]) that applies the patch.
|
||||||
|
Implies --apply.
|
||||||
+
|
+
|
||||||
See also INCOMPATIBLE OPTIONS below.
|
See also INCOMPATIBLE OPTIONS below.
|
||||||
|
|
||||||
@ -539,10 +569,11 @@ INCOMPATIBLE OPTIONS
|
|||||||
|
|
||||||
The following options:
|
The following options:
|
||||||
|
|
||||||
|
* --apply
|
||||||
* --committer-date-is-author-date
|
* --committer-date-is-author-date
|
||||||
* --ignore-date
|
* --ignore-date
|
||||||
* --whitespace
|
|
||||||
* --ignore-whitespace
|
* --ignore-whitespace
|
||||||
|
* --whitespace
|
||||||
* -C
|
* -C
|
||||||
|
|
||||||
are incompatible with the following options:
|
are incompatible with the following options:
|
||||||
@ -557,6 +588,7 @@ are incompatible with the following options:
|
|||||||
* --interactive
|
* --interactive
|
||||||
* --exec
|
* --exec
|
||||||
* --keep-empty
|
* --keep-empty
|
||||||
|
* --empty=
|
||||||
* --edit-todo
|
* --edit-todo
|
||||||
* --root when used in combination with --onto
|
* --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 --interactive
|
||||||
* --preserve-merges and --signoff
|
* --preserve-merges and --signoff
|
||||||
* --preserve-merges and --rebase-merges
|
* --preserve-merges and --rebase-merges
|
||||||
|
* --preserve-merges and --empty=
|
||||||
* --keep-base and --onto
|
* --keep-base and --onto
|
||||||
* --keep-base and --root
|
* --keep-base and --root
|
||||||
|
|
||||||
BEHAVIORAL DIFFERENCES
|
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
|
Empty commits
|
||||||
~~~~~~~~~~~~~
|
~~~~~~~~~~~~~
|
||||||
|
|
||||||
The am backend drops any "empty" commits, regardless of whether the
|
The apply backend unfortunately drops intentionally empty commits, i.e.
|
||||||
commit started empty (had no changes relative to its parent to
|
commits that started empty, though these are rare in practice. It
|
||||||
start with) or ended empty (all changes were already applied
|
also drops commits that become empty and has no option for controlling
|
||||||
upstream in other commits).
|
this behavior.
|
||||||
|
|
||||||
The interactive backend drops commits by default that
|
The merge backend keeps intentionally empty commits. Similar to the
|
||||||
started empty and halts if it hits a commit that ended up empty.
|
apply backend, by default the merge backend drops commits that become
|
||||||
The `--keep-empty` option exists for the interactive backend to allow
|
empty unless -i/--interactive is specified (in which case it stops and
|
||||||
it to keep commits that started empty.
|
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 detection
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Directory rename heuristics are enabled in the merge and interactive
|
Due to the lack of accurate tree information (arising from
|
||||||
backends. Due to the lack of accurate tree information, directory
|
constructing fake ancestors with the limited information available in
|
||||||
rename detection is disabled in the am backend.
|
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[]
|
include::merge-strategies.txt[]
|
||||||
|
|
||||||
|
271
builtin/rebase.c
271
builtin/rebase.c
@ -44,14 +44,22 @@ static GIT_PATH_FUNC(merge_dir, "rebase-merge")
|
|||||||
|
|
||||||
enum rebase_type {
|
enum rebase_type {
|
||||||
REBASE_UNSPECIFIED = -1,
|
REBASE_UNSPECIFIED = -1,
|
||||||
REBASE_AM,
|
REBASE_APPLY,
|
||||||
REBASE_MERGE,
|
REBASE_MERGE,
|
||||||
REBASE_INTERACTIVE,
|
|
||||||
REBASE_PRESERVE_MERGES
|
REBASE_PRESERVE_MERGES
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum empty_type {
|
||||||
|
EMPTY_UNSPECIFIED = -1,
|
||||||
|
EMPTY_DROP,
|
||||||
|
EMPTY_KEEP,
|
||||||
|
EMPTY_ASK
|
||||||
|
};
|
||||||
|
|
||||||
struct rebase_options {
|
struct rebase_options {
|
||||||
enum rebase_type type;
|
enum rebase_type type;
|
||||||
|
enum empty_type empty;
|
||||||
|
const char *default_backend;
|
||||||
const char *state_dir;
|
const char *state_dir;
|
||||||
struct commit *upstream;
|
struct commit *upstream;
|
||||||
const char *upstream_name;
|
const char *upstream_name;
|
||||||
@ -77,7 +85,6 @@ struct rebase_options {
|
|||||||
const char *action;
|
const char *action;
|
||||||
int signoff;
|
int signoff;
|
||||||
int allow_rerere_autoupdate;
|
int allow_rerere_autoupdate;
|
||||||
int keep_empty;
|
|
||||||
int autosquash;
|
int autosquash;
|
||||||
char *gpg_sign_opt;
|
char *gpg_sign_opt;
|
||||||
int autostash;
|
int autostash;
|
||||||
@ -92,6 +99,8 @@ struct rebase_options {
|
|||||||
|
|
||||||
#define REBASE_OPTIONS_INIT { \
|
#define REBASE_OPTIONS_INIT { \
|
||||||
.type = REBASE_UNSPECIFIED, \
|
.type = REBASE_UNSPECIFIED, \
|
||||||
|
.empty = EMPTY_UNSPECIFIED, \
|
||||||
|
.default_backend = "merge", \
|
||||||
.flags = REBASE_NO_QUIET, \
|
.flags = REBASE_NO_QUIET, \
|
||||||
.git_am_opts = ARGV_ARRAY_INIT, \
|
.git_am_opts = ARGV_ARRAY_INIT, \
|
||||||
.git_format_patch_opt = STRBUF_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_rerere_auto = opts->allow_rerere_autoupdate;
|
||||||
replay.allow_empty = 1;
|
replay.allow_empty = 1;
|
||||||
replay.allow_empty_message = opts->allow_empty_message;
|
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.verbose = opts->flags & REBASE_VERBOSE;
|
||||||
replay.reschedule_failed_exec = opts->reschedule_failed_exec;
|
replay.reschedule_failed_exec = opts->reschedule_failed_exec;
|
||||||
replay.gpg_sign = xstrdup_or_null(opts->gpg_sign_opt);
|
replay.gpg_sign = xstrdup_or_null(opts->gpg_sign_opt);
|
||||||
@ -329,7 +341,7 @@ static int do_interactive_rebase(struct rebase_options *opts, unsigned flags)
|
|||||||
|
|
||||||
argv_array_pushl(&make_script_args, "", revisions, NULL);
|
argv_array_pushl(&make_script_args, "", revisions, NULL);
|
||||||
if (opts->restrict_revision)
|
if (opts->restrict_revision)
|
||||||
argv_array_push(&make_script_args,
|
argv_array_pushf(&make_script_args, "^%s",
|
||||||
oid_to_hex(&opts->restrict_revision->object.oid));
|
oid_to_hex(&opts->restrict_revision->object.oid));
|
||||||
|
|
||||||
ret = sequencer_make_script(the_repository, &todo_list.buf,
|
ret = sequencer_make_script(the_repository, &todo_list.buf,
|
||||||
@ -359,7 +371,7 @@ static int do_interactive_rebase(struct rebase_options *opts, unsigned flags)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int run_rebase_interactive(struct rebase_options *opts,
|
static int run_sequencer_rebase(struct rebase_options *opts,
|
||||||
enum action command)
|
enum action command)
|
||||||
{
|
{
|
||||||
unsigned flags = 0;
|
unsigned flags = 0;
|
||||||
@ -367,7 +379,6 @@ static int run_rebase_interactive(struct rebase_options *opts,
|
|||||||
|
|
||||||
git_config_get_bool("rebase.abbreviatecommands", &abbreviate_commands);
|
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 |= abbreviate_commands ? TODO_LIST_ABBREVIATE_CMDS : 0;
|
||||||
flags |= opts->rebase_merges ? TODO_LIST_REBASE_MERGES : 0;
|
flags |= opts->rebase_merges ? TODO_LIST_REBASE_MERGES : 0;
|
||||||
flags |= opts->rebase_cousins > 0 ? TODO_LIST_REBASE_COUSINS : 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;
|
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[] = {
|
static const char * const builtin_rebase_interactive_usage[] = {
|
||||||
N_("git rebase--interactive [<options>]"),
|
N_("git rebase--interactive [<options>]"),
|
||||||
NULL
|
NULL
|
||||||
@ -444,9 +470,13 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)
|
|||||||
struct option options[] = {
|
struct option options[] = {
|
||||||
OPT_NEGBIT(0, "ff", &opts.flags, N_("allow fast-forward"),
|
OPT_NEGBIT(0, "ff", &opts.flags, N_("allow fast-forward"),
|
||||||
REBASE_FORCE),
|
REBASE_FORCE),
|
||||||
OPT_BOOL(0, "keep-empty", &opts.keep_empty, N_("keep empty commits")),
|
{ OPTION_CALLBACK, 'k', "keep-empty", &options, NULL,
|
||||||
OPT_BOOL(0, "allow-empty-message", &opts.allow_empty_message,
|
N_("(DEPRECATED) keep empty commits"),
|
||||||
N_("allow commits with empty messages")),
|
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-merges", &opts.rebase_merges, N_("rebase merge commits")),
|
||||||
OPT_BOOL(0, "rebase-cousins", &opts.rebase_cousins,
|
OPT_BOOL(0, "rebase-cousins", &opts.rebase_cousins,
|
||||||
N_("keep original branch points of 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 "
|
warning(_("--[no-]rebase-cousins has no effect without "
|
||||||
"--rebase-merges"));
|
"--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;
|
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) {
|
switch (opts->type) {
|
||||||
case REBASE_AM:
|
case REBASE_APPLY:
|
||||||
die(_("%s requires an interactive rebase"), option);
|
die(_("%s requires an interactive rebase"), option);
|
||||||
break;
|
break;
|
||||||
case REBASE_INTERACTIVE:
|
case REBASE_MERGE:
|
||||||
case REBASE_PRESERVE_MERGES:
|
case REBASE_PRESERVE_MERGES:
|
||||||
break;
|
break;
|
||||||
case REBASE_MERGE:
|
|
||||||
/* we now implement --merge via --interactive */
|
|
||||||
default:
|
default:
|
||||||
opts->type = REBASE_INTERACTIVE; /* implied */
|
opts->type = REBASE_MERGE; /* implied */
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -663,8 +691,8 @@ static int rebase_write_basic_state(struct rebase_options *opts)
|
|||||||
opts->onto ? oid_to_hex(&opts->onto->object.oid) : "");
|
opts->onto ? oid_to_hex(&opts->onto->object.oid) : "");
|
||||||
write_file(state_dir_path("orig-head", opts), "%s",
|
write_file(state_dir_path("orig-head", opts), "%s",
|
||||||
oid_to_hex(&opts->orig_head));
|
oid_to_hex(&opts->orig_head));
|
||||||
write_file(state_dir_path("quiet", opts), "%s",
|
if (!(opts->flags & REBASE_NO_QUIET))
|
||||||
opts->flags & REBASE_NO_QUIET ? "" : "t");
|
write_file(state_dir_path("quiet", opts), "%s", "");
|
||||||
if (opts->flags & REBASE_VERBOSE)
|
if (opts->flags & REBASE_VERBOSE)
|
||||||
write_file(state_dir_path("verbose", opts), "%s", "");
|
write_file(state_dir_path("verbose", opts), "%s", "");
|
||||||
if (opts->strategy)
|
if (opts->strategy)
|
||||||
@ -746,7 +774,7 @@ static int finish_rebase(struct rebase_options *opts)
|
|||||||
* user should see them.
|
* user should see them.
|
||||||
*/
|
*/
|
||||||
run_command_v_opt(argv_gc_auto, RUN_GIT_CMD);
|
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;
|
struct replay_opts replay = REPLAY_OPTS_INIT;
|
||||||
|
|
||||||
replay.action = REPLAY_INTERACTIVE_REBASE;
|
replay.action = REPLAY_INTERACTIVE_REBASE;
|
||||||
@ -1079,8 +1107,8 @@ static int run_specific_rebase(struct rebase_options *opts, enum action action)
|
|||||||
int status;
|
int status;
|
||||||
const char *backend, *backend_func;
|
const char *backend, *backend_func;
|
||||||
|
|
||||||
if (opts->type == REBASE_INTERACTIVE) {
|
if (opts->type == REBASE_MERGE) {
|
||||||
/* Run builtin interactive rebase */
|
/* Run sequencer-based rebase */
|
||||||
setenv("GIT_CHERRY_PICK_HELP", resolvemsg, 1);
|
setenv("GIT_CHERRY_PICK_HELP", resolvemsg, 1);
|
||||||
if (!(opts->flags & REBASE_INTERACTIVE_EXPLICIT)) {
|
if (!(opts->flags & REBASE_INTERACTIVE_EXPLICIT)) {
|
||||||
setenv("GIT_SEQUENCE_EDITOR", ":", 1);
|
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;
|
opts->gpg_sign_opt = tmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
status = run_rebase_interactive(opts, action);
|
status = run_sequencer_rebase(opts, action);
|
||||||
goto finished_rebase;
|
goto finished_rebase;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opts->type == REBASE_AM) {
|
if (opts->type == REBASE_APPLY) {
|
||||||
status = run_am(opts);
|
status = run_am(opts);
|
||||||
goto finished_rebase;
|
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, "revisions", opts->revisions);
|
||||||
add_var(&script_snippet, "restrict_revision", opts->restrict_revision ?
|
add_var(&script_snippet, "restrict_revision", opts->restrict_revision ?
|
||||||
oid_to_hex(&opts->restrict_revision->object.oid) : NULL);
|
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);
|
sq_quote_argv_pretty(&buf, opts->git_am_opts.argv);
|
||||||
add_var(&script_snippet, "git_am_opt", buf.buf);
|
add_var(&script_snippet, "git_am_opt", buf.buf);
|
||||||
strbuf_release(&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 ?
|
||||||
opts->allow_rerere_autoupdate == RERERE_AUTOUPDATE ?
|
opts->allow_rerere_autoupdate == RERERE_AUTOUPDATE ?
|
||||||
"--rerere-autoupdate" : "--no-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, "autosquash", opts->autosquash ? "t" : "");
|
||||||
add_var(&script_snippet, "gpg_sign_opt", opts->gpg_sign_opt);
|
add_var(&script_snippet, "gpg_sign_opt", opts->gpg_sign_opt);
|
||||||
add_var(&script_snippet, "cmd", opts->cmd);
|
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",
|
add_var(&script_snippet, "git_format_patch_opt",
|
||||||
opts->git_format_patch_opt.buf);
|
opts->git_format_patch_opt.buf);
|
||||||
|
|
||||||
if (is_interactive(opts) &&
|
if (is_merge(opts) &&
|
||||||
!(opts->flags & REBASE_INTERACTIVE_EXPLICIT)) {
|
!(opts->flags & REBASE_INTERACTIVE_EXPLICIT)) {
|
||||||
strbuf_addstr(&script_snippet,
|
strbuf_addstr(&script_snippet,
|
||||||
"GIT_SEQUENCE_EDITOR=:; export GIT_SEQUENCE_EDITOR; ");
|
"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:
|
finished_rebase:
|
||||||
if (opts->dont_finish_rebase)
|
if (opts->dont_finish_rebase)
|
||||||
; /* do nothing */
|
; /* do nothing */
|
||||||
else if (opts->type == REBASE_INTERACTIVE)
|
else if (opts->type == REBASE_MERGE)
|
||||||
; /* interactive rebase cleans up after itself */
|
; /* merge backend cleans up after itself */
|
||||||
else if (status == 0) {
|
else if (status == 0) {
|
||||||
if (!file_exists(state_dir_path("stopped-sha", opts)))
|
if (!file_exists(state_dir_path("stopped-sha", opts)))
|
||||||
finish_rebase(opts);
|
finish_rebase(opts);
|
||||||
@ -1238,6 +1263,10 @@ static int rebase_config(const char *var, const char *value, void *data)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!strcmp(var, "rebase.backend")) {
|
||||||
|
return git_config_string(&opts->default_backend, var, value);
|
||||||
|
}
|
||||||
|
|
||||||
return git_default_config(var, value, data);
|
return git_default_config(var, value, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1301,6 +1330,18 @@ done:
|
|||||||
return res && is_linear_history(onto, head);
|
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 */
|
/* -i followed by -m is still -i */
|
||||||
static int parse_opt_merge(const struct option *opt, const char *arg, int unset)
|
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_NEG(unset);
|
||||||
BUG_ON_OPT_ARG(arg);
|
BUG_ON_OPT_ARG(arg);
|
||||||
|
|
||||||
if (!is_interactive(opts))
|
if (!is_merge(opts))
|
||||||
opts->type = REBASE_MERGE;
|
opts->type = REBASE_MERGE;
|
||||||
|
|
||||||
return 0;
|
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_NEG(unset);
|
||||||
BUG_ON_OPT_ARG(arg);
|
BUG_ON_OPT_ARG(arg);
|
||||||
|
|
||||||
opts->type = REBASE_INTERACTIVE;
|
opts->type = REBASE_MERGE;
|
||||||
opts->flags |= REBASE_INTERACTIVE_EXPLICIT;
|
opts->flags |= REBASE_INTERACTIVE_EXPLICIT;
|
||||||
|
|
||||||
return 0;
|
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)
|
static void NORETURN error_on_missing_default_upstream(void)
|
||||||
{
|
{
|
||||||
struct branch *current_branch = branch_get(NULL);
|
struct branch *current_branch = branch_get(NULL);
|
||||||
@ -1365,14 +1429,14 @@ static void set_reflog_action(struct rebase_options *options)
|
|||||||
const char *env;
|
const char *env;
|
||||||
struct strbuf buf = STRBUF_INIT;
|
struct strbuf buf = STRBUF_INIT;
|
||||||
|
|
||||||
if (!is_interactive(options))
|
if (!is_merge(options))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
env = getenv(GIT_REFLOG_ACTION_ENVIRONMENT);
|
env = getenv(GIT_REFLOG_ACTION_ENVIRONMENT);
|
||||||
if (env && strcmp("rebase", env))
|
if (env && strcmp("rebase", env))
|
||||||
return; /* only override it if it is "rebase" */
|
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);
|
setenv(GIT_REFLOG_ACTION_ENVIRONMENT, buf.buf, 1);
|
||||||
strbuf_release(&buf);
|
strbuf_release(&buf);
|
||||||
}
|
}
|
||||||
@ -1410,6 +1474,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
|
|||||||
struct object_id squash_onto;
|
struct object_id squash_onto;
|
||||||
char *squash_onto_name = NULL;
|
char *squash_onto_name = NULL;
|
||||||
int reschedule_failed_exec = -1;
|
int reschedule_failed_exec = -1;
|
||||||
|
int allow_preemptive_ff = 1;
|
||||||
struct option builtin_rebase_options[] = {
|
struct option builtin_rebase_options[] = {
|
||||||
OPT_STRING(0, "onto", &options.onto_name,
|
OPT_STRING(0, "onto", &options.onto_name,
|
||||||
N_("revision"),
|
N_("revision"),
|
||||||
@ -1420,7 +1485,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
|
|||||||
N_("allow pre-rebase hook to run")),
|
N_("allow pre-rebase hook to run")),
|
||||||
OPT_NEGBIT('q', "quiet", &options.flags,
|
OPT_NEGBIT('q', "quiet", &options.flags,
|
||||||
N_("be quiet. implies --no-stat"),
|
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,
|
OPT_BIT('v', "verbose", &options.flags,
|
||||||
N_("display a diffstat of what changed upstream"),
|
N_("display a diffstat of what changed upstream"),
|
||||||
REBASE_NO_QUIET | REBASE_VERBOSE | REBASE_DIFFSTAT),
|
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,
|
OPT_CMDMODE(0, "show-current-patch", &action,
|
||||||
N_("show the patch file being applied or merged"),
|
N_("show the patch file being applied or merged"),
|
||||||
ACTION_SHOW_CURRENT_PATCH),
|
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,
|
{ OPTION_CALLBACK, 'm', "merge", &options, NULL,
|
||||||
N_("use merging strategies to rebase"),
|
N_("use merging strategies to rebase"),
|
||||||
PARSE_OPT_NOARG | PARSE_OPT_NONEG,
|
PARSE_OPT_NOARG | PARSE_OPT_NONEG,
|
||||||
@ -1474,8 +1543,13 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
|
|||||||
"ignoring them"),
|
"ignoring them"),
|
||||||
REBASE_PRESERVE_MERGES, PARSE_OPT_HIDDEN),
|
REBASE_PRESERVE_MERGES, PARSE_OPT_HIDDEN),
|
||||||
OPT_RERERE_AUTOUPDATE(&options.allow_rerere_autoupdate),
|
OPT_RERERE_AUTOUPDATE(&options.allow_rerere_autoupdate),
|
||||||
OPT_BOOL('k', "keep-empty", &options.keep_empty,
|
OPT_CALLBACK_F(0, "empty", &options, N_("{drop,keep,ask}"),
|
||||||
N_("preserve empty commits during rebase")),
|
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,
|
OPT_BOOL(0, "autosquash", &options.autosquash,
|
||||||
N_("move commits that begin with "
|
N_("move commits that begin with "
|
||||||
"squash!/fixup! under -i")),
|
"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"),
|
OPT_STRING_LIST('x', "exec", &exec, N_("exec"),
|
||||||
N_("add exec lines after each commit of the "
|
N_("add exec lines after each commit of the "
|
||||||
"editable list")),
|
"editable list")),
|
||||||
OPT_BOOL(0, "allow-empty-message",
|
OPT_BOOL_F(0, "allow-empty-message",
|
||||||
&options.allow_empty_message,
|
&options.allow_empty_message,
|
||||||
N_("allow rebasing commits with empty messages")),
|
N_("allow rebasing commits with empty messages"),
|
||||||
|
PARSE_OPT_HIDDEN),
|
||||||
{OPTION_STRING, 'r', "rebase-merges", &rebase_merges,
|
{OPTION_STRING, 'r', "rebase-merges", &rebase_merges,
|
||||||
N_("mode"),
|
N_("mode"),
|
||||||
N_("try to rebase merges instead of skipping them"),
|
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."));
|
die(_("It looks like 'git am' is in progress. Cannot rebase."));
|
||||||
|
|
||||||
if (is_directory(apply_dir())) {
|
if (is_directory(apply_dir())) {
|
||||||
options.type = REBASE_AM;
|
options.type = REBASE_APPLY;
|
||||||
options.state_dir = apply_dir();
|
options.state_dir = apply_dir();
|
||||||
} else if (is_directory(merge_dir())) {
|
} else if (is_directory(merge_dir())) {
|
||||||
strbuf_reset(&buf);
|
strbuf_reset(&buf);
|
||||||
@ -1541,7 +1616,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
|
|||||||
strbuf_reset(&buf);
|
strbuf_reset(&buf);
|
||||||
strbuf_addf(&buf, "%s/interactive", merge_dir());
|
strbuf_addf(&buf, "%s/interactive", merge_dir());
|
||||||
if(file_exists(buf.buf)) {
|
if(file_exists(buf.buf)) {
|
||||||
options.type = REBASE_INTERACTIVE;
|
options.type = REBASE_MERGE;
|
||||||
options.flags |= REBASE_INTERACTIVE_EXPLICIT;
|
options.flags |= REBASE_INTERACTIVE_EXPLICIT;
|
||||||
} else
|
} else
|
||||||
options.type = REBASE_MERGE;
|
options.type = REBASE_MERGE;
|
||||||
@ -1581,12 +1656,12 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
|
|||||||
die(_("No rebase in progress?"));
|
die(_("No rebase in progress?"));
|
||||||
setenv(GIT_REFLOG_ACTION_ENVIRONMENT, "rebase", 0);
|
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 "
|
die(_("The --edit-todo action can only be used during "
|
||||||
"interactive rebase."));
|
"interactive rebase."));
|
||||||
|
|
||||||
if (trace2_is_enabled()) {
|
if (trace2_is_enabled()) {
|
||||||
if (is_interactive(&options))
|
if (is_merge(&options))
|
||||||
trace2_cmd_mode("interactive");
|
trace2_cmd_mode("interactive");
|
||||||
else if (exec.nr)
|
else if (exec.nr)
|
||||||
trace2_cmd_mode("interactive-exec");
|
trace2_cmd_mode("interactive-exec");
|
||||||
@ -1662,7 +1737,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
|
|||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
case ACTION_QUIT: {
|
case ACTION_QUIT: {
|
||||||
if (options.type == REBASE_INTERACTIVE) {
|
if (options.type == REBASE_MERGE) {
|
||||||
struct replay_opts replay = REPLAY_OPTS_INIT;
|
struct replay_opts replay = REPLAY_OPTS_INIT;
|
||||||
|
|
||||||
replay.action = REPLAY_INTERACTIVE_REBASE;
|
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);
|
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++) {
|
for (i = 0; i < options.git_am_opts.argc; i++) {
|
||||||
const char *option = options.git_am_opts.argv[i], *p;
|
const char *option = options.git_am_opts.argv[i], *p;
|
||||||
if (!strcmp(option, "--committer-date-is-author-date") ||
|
if (!strcmp(option, "--committer-date-is-author-date") ||
|
||||||
!strcmp(option, "--ignore-date") ||
|
!strcmp(option, "--ignore-date") ||
|
||||||
!strcmp(option, "--whitespace=fix") ||
|
!strcmp(option, "--whitespace=fix") ||
|
||||||
!strcmp(option, "--whitespace=strip"))
|
!strcmp(option, "--whitespace=strip"))
|
||||||
options.flags |= REBASE_FORCE;
|
allow_preemptive_ff = 0;
|
||||||
else if (skip_prefix(option, "-C", &p)) {
|
else if (skip_prefix(option, "-C", &p)) {
|
||||||
while (*p)
|
while (*p)
|
||||||
if (!isdigit(*(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))
|
if (!(options.flags & REBASE_NO_QUIET))
|
||||||
argv_array_push(&options.git_am_opts, "-q");
|
argv_array_push(&options.git_am_opts, "-q");
|
||||||
|
|
||||||
if (options.keep_empty)
|
if (options.empty != EMPTY_UNSPECIFIED)
|
||||||
imply_interactive(&options, "--keep-empty");
|
imply_merge(&options, "--empty");
|
||||||
|
|
||||||
if (gpg_sign) {
|
if (gpg_sign) {
|
||||||
free(options.gpg_sign_opt);
|
free(options.gpg_sign_opt);
|
||||||
@ -1748,7 +1830,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
|
|||||||
if (exec.nr) {
|
if (exec.nr) {
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
imply_interactive(&options, "--exec");
|
imply_merge(&options, "--exec");
|
||||||
|
|
||||||
strbuf_reset(&buf);
|
strbuf_reset(&buf);
|
||||||
for (i = 0; i < exec.nr; i++)
|
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))
|
else if (strcmp("no-rebase-cousins", rebase_merges))
|
||||||
die(_("Unknown mode: %s"), rebase_merges);
|
die(_("Unknown mode: %s"), rebase_merges);
|
||||||
options.rebase_merges = 1;
|
options.rebase_merges = 1;
|
||||||
imply_interactive(&options, "--rebase-merges");
|
imply_merge(&options, "--rebase-merges");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strategy_options.nr) {
|
if (strategy_options.nr) {
|
||||||
@ -1783,10 +1865,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
|
|||||||
if (options.strategy) {
|
if (options.strategy) {
|
||||||
options.strategy = xstrdup(options.strategy);
|
options.strategy = xstrdup(options.strategy);
|
||||||
switch (options.type) {
|
switch (options.type) {
|
||||||
case REBASE_AM:
|
case REBASE_APPLY:
|
||||||
die(_("--strategy requires --merge or --interactive"));
|
die(_("--strategy requires --merge or --interactive"));
|
||||||
case REBASE_MERGE:
|
case REBASE_MERGE:
|
||||||
case REBASE_INTERACTIVE:
|
|
||||||
case REBASE_PRESERVE_MERGES:
|
case REBASE_PRESERVE_MERGES:
|
||||||
/* compatible */
|
/* compatible */
|
||||||
break;
|
break;
|
||||||
@ -1799,46 +1880,64 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (options.type == REBASE_MERGE)
|
if (options.type == REBASE_MERGE)
|
||||||
imply_interactive(&options, "--merge");
|
imply_merge(&options, "--merge");
|
||||||
|
|
||||||
if (options.root && !options.onto_name)
|
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)
|
if (isatty(2) && options.flags & REBASE_NO_QUIET)
|
||||||
strbuf_addstr(&options.git_format_patch_opt, " --progress");
|
strbuf_addstr(&options.git_format_patch_opt, " --progress");
|
||||||
|
|
||||||
switch (options.type) {
|
if (options.git_am_opts.argc || options.type == REBASE_APPLY) {
|
||||||
case REBASE_MERGE:
|
/* all am options except -q are compatible only with --apply */
|
||||||
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 */
|
|
||||||
for (i = options.git_am_opts.argc - 1; i >= 0; i--)
|
for (i = options.git_am_opts.argc - 1; i >= 0; i--)
|
||||||
if (strcmp(options.git_am_opts.argv[i], "-q"))
|
if (strcmp(options.git_am_opts.argv[i], "-q"))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (is_interactive(&options) && i >= 0)
|
if (i >= 0) {
|
||||||
die(_("cannot combine am options with either "
|
if (is_merge(&options))
|
||||||
"interactive or 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.signoff) {
|
||||||
if (options.type == REBASE_PRESERVE_MERGES)
|
if (options.type == REBASE_PRESERVE_MERGES)
|
||||||
@ -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,
|
* Check if we are already based on onto with linear history,
|
||||||
* in which case we could fast-forward without replacing the commits
|
* in which case we could fast-forward without replacing the commits
|
||||||
* with new commits recreated by replaying their changes. This
|
* with new commits recreated by replaying their changes.
|
||||||
* optimization must not be done if this is an interactive rebase.
|
*
|
||||||
|
* 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,
|
if (can_fast_forward(options.onto, options.upstream, options.restrict_revision,
|
||||||
&options.orig_head, &merge_base) &&
|
&options.orig_head, &merge_base) &&
|
||||||
!is_interactive(&options)) {
|
allow_preemptive_ff) {
|
||||||
int flag;
|
int flag;
|
||||||
|
|
||||||
if (!(options.flags & REBASE_FORCE)) {
|
if (!(options.flags & REBASE_FORCE)) {
|
||||||
@ -2133,7 +2234,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
|
|||||||
diff_flush(&opts);
|
diff_flush(&opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_interactive(&options))
|
if (is_merge(&options))
|
||||||
goto run_rebase;
|
goto run_rebase;
|
||||||
|
|
||||||
/* Detach HEAD and reset the tree */
|
/* 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/head-name" b
|
||||||
__git_eread "$g/rebase-merge/msgnum" step
|
__git_eread "$g/rebase-merge/msgnum" step
|
||||||
__git_eread "$g/rebase-merge/end" total
|
__git_eread "$g/rebase-merge/end" total
|
||||||
if [ -f "$g/rebase-merge/interactive" ]; then
|
r="|REBASE"
|
||||||
r="|REBASE-i"
|
|
||||||
else
|
|
||||||
r="|REBASE-m"
|
|
||||||
fi
|
|
||||||
else
|
else
|
||||||
if [ -d "$g/rebase-apply" ]; then
|
if [ -d "$g/rebase-apply" ]; then
|
||||||
__git_eread "$g/rebase-apply/next" step
|
__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;
|
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,
|
const char *shortrevisions, const char *shortonto,
|
||||||
struct strbuf *buf)
|
struct strbuf *buf)
|
||||||
{
|
{
|
||||||
@ -87,11 +87,6 @@ void append_todo_help(unsigned keep_empty, int command_count,
|
|||||||
"the rebase will be aborted.\n\n");
|
"the rebase will be aborted.\n\n");
|
||||||
|
|
||||||
strbuf_add_commented_lines(buf, msg, strlen(msg));
|
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,
|
int edit_todo_list(struct repository *r, struct todo_list *todo_list,
|
||||||
|
@ -5,7 +5,7 @@ struct strbuf;
|
|||||||
struct repository;
|
struct repository;
|
||||||
struct todo_list;
|
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,
|
const char *shortrevisions, const char *shortonto,
|
||||||
struct strbuf *buf);
|
struct strbuf *buf);
|
||||||
int edit_todo_list(struct repository *r, struct todo_list *todo_list,
|
int edit_todo_list(struct repository *r, struct todo_list *todo_list,
|
||||||
|
80
sequencer.c
80
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_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_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_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)
|
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');
|
char *eol = strchr(p, '\n');
|
||||||
if (eol)
|
if (eol)
|
||||||
*eol = '\0';
|
*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);
|
warning(_("could not delete '%s'"), p);
|
||||||
ret = -1;
|
ret = -1;
|
||||||
}
|
}
|
||||||
@ -324,7 +326,7 @@ static const char *action_name(const struct replay_opts *opts)
|
|||||||
case REPLAY_PICK:
|
case REPLAY_PICK:
|
||||||
return N_("cherry-pick");
|
return N_("cherry-pick");
|
||||||
case REPLAY_INTERACTIVE_REBASE:
|
case REPLAY_INTERACTIVE_REBASE:
|
||||||
return N_("rebase -i");
|
return N_("rebase");
|
||||||
}
|
}
|
||||||
die(_("unknown action: %d"), opts->action);
|
die(_("unknown action: %d"), opts->action);
|
||||||
}
|
}
|
||||||
@ -628,7 +630,7 @@ static int do_recursive_merge(struct repository *r,
|
|||||||
COMMIT_LOCK | SKIP_IF_UNCHANGED))
|
COMMIT_LOCK | SKIP_IF_UNCHANGED))
|
||||||
/*
|
/*
|
||||||
* TRANSLATORS: %s will be "revert", "cherry-pick" or
|
* TRANSLATORS: %s will be "revert", "cherry-pick" or
|
||||||
* "rebase -i".
|
* "rebase".
|
||||||
*/
|
*/
|
||||||
return error(_("%s: Unable to write new index file"),
|
return error(_("%s: Unable to write new index file"),
|
||||||
_(action_name(opts)));
|
_(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,
|
static int allow_empty(struct repository *r,
|
||||||
struct replay_opts *opts,
|
struct replay_opts *opts,
|
||||||
struct commit *commit)
|
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.
|
* (1) we do not allow empty at all and error out.
|
||||||
*
|
*
|
||||||
* (2) we allow ones that were initially empty, but
|
* (2) we allow ones that were initially empty, and
|
||||||
* forbid the ones that become empty;
|
* 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)
|
if (!opts->allow_empty)
|
||||||
return 0; /* let "git commit" barf as necessary */
|
return 0; /* let "git commit" barf as necessary */
|
||||||
@ -1515,13 +1524,15 @@ static int allow_empty(struct repository *r,
|
|||||||
if (opts->keep_redundant_commits)
|
if (opts->keep_redundant_commits)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
empty_commit = is_original_commit_empty(commit);
|
originally_empty = is_original_commit_empty(commit);
|
||||||
if (empty_commit < 0)
|
if (originally_empty < 0)
|
||||||
return empty_commit;
|
return originally_empty;
|
||||||
if (!empty_commit)
|
if (originally_empty)
|
||||||
return 0;
|
|
||||||
else
|
|
||||||
return 1;
|
return 1;
|
||||||
|
else if (opts->drop_redundant_commits)
|
||||||
|
return 2;
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct {
|
static struct {
|
||||||
@ -1732,7 +1743,7 @@ static int do_pick_commit(struct repository *r,
|
|||||||
char *author = NULL;
|
char *author = NULL;
|
||||||
struct commit_message msg = { NULL, NULL, NULL, NULL };
|
struct commit_message msg = { NULL, NULL, NULL, NULL };
|
||||||
struct strbuf msgbuf = STRBUF_INIT;
|
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) {
|
if (opts->no_commit) {
|
||||||
/*
|
/*
|
||||||
@ -1937,13 +1948,20 @@ static int do_pick_commit(struct repository *r,
|
|||||||
goto leave;
|
goto leave;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
drop_commit = 0;
|
||||||
allow = allow_empty(r, opts, commit);
|
allow = allow_empty(r, opts, commit);
|
||||||
if (allow < 0) {
|
if (allow < 0) {
|
||||||
res = allow;
|
res = allow;
|
||||||
goto leave;
|
goto leave;
|
||||||
} else if (allow)
|
} else if (allow == 1) {
|
||||||
flags |= ALLOW_EMPTY;
|
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))
|
if (author || command == TODO_REVERT || (flags & AMEND_MSG))
|
||||||
res = do_commit(r, msg_file, author, opts, flags);
|
res = do_commit(r, msg_file, author, opts, flags);
|
||||||
else
|
else
|
||||||
@ -2498,6 +2516,12 @@ static int read_populate_opts(struct replay_opts *opts)
|
|||||||
if (file_exists(rebase_path_reschedule_failed_exec()))
|
if (file_exists(rebase_path_reschedule_failed_exec()))
|
||||||
opts->reschedule_failed_exec = 1;
|
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);
|
read_strategy_opts(opts, &buf);
|
||||||
strbuf_release(&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,
|
int write_basic_state(struct replay_opts *opts, const char *head_name,
|
||||||
struct commit *onto, const char *orig_head)
|
struct commit *onto, const char *orig_head)
|
||||||
{
|
{
|
||||||
const char *quiet = getenv("GIT_QUIET");
|
|
||||||
|
|
||||||
if (head_name)
|
if (head_name)
|
||||||
write_file(rebase_path_head_name(), "%s\n", head_name);
|
write_file(rebase_path_head_name(), "%s\n", head_name);
|
||||||
if (onto)
|
if (onto)
|
||||||
@ -2559,8 +2581,8 @@ int write_basic_state(struct replay_opts *opts, const char *head_name,
|
|||||||
if (orig_head)
|
if (orig_head)
|
||||||
write_file(rebase_path_orig_head(), "%s\n", orig_head);
|
write_file(rebase_path_orig_head(), "%s\n", orig_head);
|
||||||
|
|
||||||
if (quiet)
|
if (opts->quiet)
|
||||||
write_file(rebase_path_quiet(), "%s\n", quiet);
|
write_file(rebase_path_quiet(), "%s", "");
|
||||||
if (opts->verbose)
|
if (opts->verbose)
|
||||||
write_file(rebase_path_verbose(), "%s", "");
|
write_file(rebase_path_verbose(), "%s", "");
|
||||||
if (opts->strategy)
|
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);
|
write_file(rebase_path_gpg_sign_opt(), "-S%s\n", opts->gpg_sign);
|
||||||
if (opts->signoff)
|
if (opts->signoff)
|
||||||
write_file(rebase_path_signoff(), "--signoff\n");
|
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)
|
if (opts->reschedule_failed_exec)
|
||||||
write_file(rebase_path_reschedule_failed_exec(), "%s", "");
|
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);
|
return error(_("illegal label name: '%.*s'"), len, name);
|
||||||
|
|
||||||
strbuf_addf(&ref_name, "refs/rewritten/%.*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);
|
transaction = ref_store_transaction_begin(refs, &err);
|
||||||
if (!transaction) {
|
if (!transaction) {
|
||||||
@ -4563,7 +4589,6 @@ static int make_script_with_merges(struct pretty_print_context *pp,
|
|||||||
struct rev_info *revs, struct strbuf *out,
|
struct rev_info *revs, struct strbuf *out,
|
||||||
unsigned flags)
|
unsigned flags)
|
||||||
{
|
{
|
||||||
int keep_empty = flags & TODO_LIST_KEEP_EMPTY;
|
|
||||||
int rebase_cousins = flags & TODO_LIST_REBASE_COUSINS;
|
int rebase_cousins = flags & TODO_LIST_REBASE_COUSINS;
|
||||||
int root_with_onto = flags & TODO_LIST_ROOT_WITH_ONTO;
|
int root_with_onto = flags & TODO_LIST_ROOT_WITH_ONTO;
|
||||||
struct strbuf buf = STRBUF_INIT, oneline = STRBUF_INIT;
|
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) {
|
if (!to_merge) {
|
||||||
/* non-merge commit: easy case */
|
/* non-merge commit: easy case */
|
||||||
strbuf_reset(&buf);
|
strbuf_reset(&buf);
|
||||||
if (!keep_empty && is_empty)
|
|
||||||
strbuf_addf(&buf, "%c ", comment_line_char);
|
|
||||||
strbuf_addf(&buf, "%s %s %s", cmd_pick,
|
strbuf_addf(&buf, "%s %s %s", cmd_pick,
|
||||||
oid_to_hex(&commit->object.oid),
|
oid_to_hex(&commit->object.oid),
|
||||||
oneline.buf);
|
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 pretty_print_context pp = {0};
|
||||||
struct rev_info revs;
|
struct rev_info revs;
|
||||||
struct commit *commit;
|
struct commit *commit;
|
||||||
int keep_empty = flags & TODO_LIST_KEEP_EMPTY;
|
|
||||||
const char *insn = flags & TODO_LIST_ABBREVIATE_CMDS ? "p" : "pick";
|
const char *insn = flags & TODO_LIST_ABBREVIATE_CMDS ? "p" : "pick";
|
||||||
int rebase_merges = flags & TODO_LIST_REBASE_MERGES;
|
int rebase_merges = flags & TODO_LIST_REBASE_MERGES;
|
||||||
|
|
||||||
@ -4834,8 +4856,6 @@ int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,
|
|||||||
|
|
||||||
if (!is_empty && (commit->object.flags & PATCHSAME))
|
if (!is_empty && (commit->object.flags & PATCHSAME))
|
||||||
continue;
|
continue;
|
||||||
if (!keep_empty && is_empty)
|
|
||||||
strbuf_addf(out, "%c ", comment_line_char);
|
|
||||||
strbuf_addf(out, "%s %s ", insn,
|
strbuf_addf(out, "%s %s ", insn,
|
||||||
oid_to_hex(&commit->object.oid));
|
oid_to_hex(&commit->object.oid));
|
||||||
pretty_print_commit(&pp, commit, out);
|
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);
|
todo_list_to_strbuf(r, todo_list, &buf, num, flags);
|
||||||
if (flags & TODO_LIST_APPEND_TODO_HELP)
|
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);
|
shortrevisions, shortonto, &buf);
|
||||||
|
|
||||||
res = write_message(buf.buf, buf.len, file, 0);
|
res = write_message(buf.buf, buf.len, file, 0);
|
||||||
|
@ -40,6 +40,7 @@ struct replay_opts {
|
|||||||
int allow_rerere_auto;
|
int allow_rerere_auto;
|
||||||
int allow_empty;
|
int allow_empty;
|
||||||
int allow_empty_message;
|
int allow_empty_message;
|
||||||
|
int drop_redundant_commits;
|
||||||
int keep_redundant_commits;
|
int keep_redundant_commits;
|
||||||
int verbose;
|
int verbose;
|
||||||
int quiet;
|
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_skip(struct repository *repo, struct replay_opts *opts);
|
||||||
int sequencer_remove_state(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_SHORTEN_IDS (1U << 1)
|
||||||
#define TODO_LIST_ABBREVIATE_CMDS (1U << 2)
|
#define TODO_LIST_ABBREVIATE_CMDS (1U << 2)
|
||||||
#define TODO_LIST_REBASE_MERGES (1U << 3)
|
#define TODO_LIST_REBASE_MERGES (1U << 3)
|
||||||
|
@ -165,19 +165,37 @@ test_expect_success 'rebase works with format.useAutoBase' '
|
|||||||
git rebase master
|
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-base master &&
|
||||||
git checkout -b default topic &&
|
git checkout -b default topic &&
|
||||||
git config branch.default.remote . &&
|
git config branch.default.remote . &&
|
||||||
git config branch.default.merge refs/heads/default-base &&
|
git config branch.default.merge refs/heads/default-base &&
|
||||||
git rebase &&
|
git rebase --merge &&
|
||||||
git rev-parse --verify default-base >expect &&
|
git rev-parse --verify default-base >expect &&
|
||||||
git rev-parse default~1 >actual &&
|
git rev-parse default~1 >actual &&
|
||||||
test_cmp expect actual &&
|
test_cmp expect actual &&
|
||||||
git checkout default-base &&
|
git checkout default-base &&
|
||||||
git reset --hard HEAD^ &&
|
git reset --hard HEAD^ &&
|
||||||
git checkout default &&
|
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 --verify default-base >expect &&
|
||||||
git rev-parse default~1 >actual &&
|
git rev-parse default~1 >actual &&
|
||||||
test_cmp expect 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_cmp expect D
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success 'rebase -q is quiet' '
|
test_expect_success 'rebase --apply -q is quiet' '
|
||||||
git checkout -b quiet topic &&
|
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
|
test_must_be_empty output.out
|
||||||
'
|
'
|
||||||
|
|
||||||
@ -291,7 +315,7 @@ EOF
|
|||||||
test_cmp From_.msg out
|
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 &&
|
test_create_repo conflict-apply &&
|
||||||
(
|
(
|
||||||
cd conflict-apply &&
|
cd conflict-apply &&
|
||||||
@ -301,13 +325,13 @@ test_expect_success 'rebase --am and --show-current-patch' '
|
|||||||
echo two >>init.t &&
|
echo two >>init.t &&
|
||||||
git commit -a -m two &&
|
git commit -a -m two &&
|
||||||
git tag 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 &&
|
GIT_TRACE=1 git rebase --show-current-patch >/dev/null 2>stderr &&
|
||||||
grep "show.*$(git rev-parse two)" 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 &&
|
test_create_repo attributes &&
|
||||||
(
|
(
|
||||||
cd 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 &&
|
cd dir-rename &&
|
||||||
|
|
||||||
git checkout B^0 &&
|
git checkout B^0 &&
|
||||||
|
|
||||||
git -c merge.directoryRenames=true rebase A &&
|
git -c merge.directoryRenames=true rebase --apply A &&
|
||||||
|
|
||||||
git ls-files -s >out &&
|
git ls-files -s >out &&
|
||||||
test_line_count = 5 out &&
|
test_line_count = 5 out &&
|
||||||
|
@ -72,15 +72,16 @@ test_expect_success 'rebase --keep-empty' '
|
|||||||
test_line_count = 6 actual
|
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 &&
|
cat >expect <<-\EOF &&
|
||||||
error: nothing to do
|
error: nothing to do
|
||||||
EOF
|
EOF
|
||||||
(
|
(
|
||||||
set_fake_editor &&
|
set_fake_editor &&
|
||||||
test_must_fail env FAKE_LINES="1 exec_true" \
|
test_must_fail env FAKE_LINES="#" \
|
||||||
git rebase -i HEAD^ >actual 2>&1
|
git rebase -i HEAD^ >output 2>&1
|
||||||
) &&
|
) &&
|
||||||
|
tail -n 1 output >actual && # Ignore output about changing todo list
|
||||||
test_i18ncmp expect actual
|
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' '
|
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 rev-parse branch2)" >expected &&
|
||||||
git log -g --pretty=%gs -1 refs/heads/branch1 >actual &&
|
git log -g --pretty=%gs -1 refs/heads/branch1 >actual &&
|
||||||
test_cmp expected 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 &&
|
git checkout conflict-branch &&
|
||||||
(
|
(
|
||||||
set_fake_editor &&
|
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
|
test_must_fail git rebase --edit-todo
|
||||||
) &&
|
) &&
|
||||||
git rebase --abort
|
git rebase --abort
|
||||||
@ -1161,10 +1162,10 @@ test_expect_success 'rebase -i produces readable reflog' '
|
|||||||
git branch -f branch-reflog-test H &&
|
git branch -f branch-reflog-test H &&
|
||||||
git rebase -i --onto I F branch-reflog-test &&
|
git rebase -i --onto I F branch-reflog-test &&
|
||||||
cat >expect <<-\EOF &&
|
cat >expect <<-\EOF &&
|
||||||
rebase -i (finish): returning to refs/heads/branch-reflog-test
|
rebase (finish): returning to refs/heads/branch-reflog-test
|
||||||
rebase -i (pick): H
|
rebase (pick): H
|
||||||
rebase -i (pick): G
|
rebase (pick): G
|
||||||
rebase -i (start): checkout I
|
rebase (start): checkout I
|
||||||
EOF
|
EOF
|
||||||
git reflog -n4 HEAD |
|
git reflog -n4 HEAD |
|
||||||
sed "s/[^:]*: //" >actual &&
|
sed "s/[^:]*: //" >actual &&
|
||||||
|
@ -18,32 +18,29 @@ test_expect_success 'setup' '
|
|||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success 'rebase -m' '
|
test_expect_success 'rebase -m' '
|
||||||
git rebase -m master >report &&
|
git rebase -m master >actual &&
|
||||||
>expect &&
|
test_must_be_empty actual
|
||||||
sed -n -e "/^Already applied: /p" \
|
|
||||||
-e "/^Committed: /p" report >actual &&
|
|
||||||
test_cmp expect actual
|
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success 'rebase against master twice' '
|
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_i18ngrep "Current branch topic is up to date" out
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success 'rebase against master twice with --force' '
|
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_i18ngrep "Current branch topic is up to date, rebase forced" out
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success 'rebase against master twice from another branch' '
|
test_expect_success 'rebase against master twice from another branch' '
|
||||||
git checkout topic^ &&
|
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_i18ngrep "Current branch topic is up to date" out
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success 'rebase fast-forward to master' '
|
test_expect_success 'rebase fast-forward to master' '
|
||||||
git checkout topic^ &&
|
git checkout topic^ &&
|
||||||
git rebase topic >out &&
|
git rebase --apply topic >out &&
|
||||||
test_i18ngrep "Fast-forwarded HEAD to 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 &&
|
git checkout -b reflog-topic start &&
|
||||||
test_commit reflog-to-rebase &&
|
test_commit reflog-to-rebase &&
|
||||||
|
|
||||||
git rebase reflog-onto &&
|
git rebase --apply reflog-onto &&
|
||||||
git log -g --format=%gs -3 >actual &&
|
git log -g --format=%gs -3 >actual &&
|
||||||
cat >expect <<-\EOF &&
|
cat >expect <<-\EOF &&
|
||||||
rebase finished: returning to refs/heads/reflog-topic
|
rebase finished: returning to refs/heads/reflog-topic
|
||||||
@ -102,7 +99,7 @@ test_expect_success 'GIT_REFLOG_ACTION' '
|
|||||||
test_cmp expect actual &&
|
test_cmp expect actual &&
|
||||||
|
|
||||||
git checkout -b reflog-prefix reflog-to-rebase &&
|
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 &&
|
git log -g --format=%gs -3 >actual &&
|
||||||
cat >expect <<-\EOF &&
|
cat >expect <<-\EOF &&
|
||||||
rebase finished: returning to refs/heads/reflog-prefix
|
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
|
testrebase " --merge" .git/rebase-merge
|
||||||
|
|
||||||
test_expect_success 'rebase --quit' '
|
test_expect_success 'rebase --apply --quit' '
|
||||||
cd "$work_dir" &&
|
cd "$work_dir" &&
|
||||||
# Clean up the state from the previous one
|
# Clean up the state from the previous one
|
||||||
git reset --hard pre-rebase &&
|
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 &&
|
test_path_is_dir .git/rebase-apply &&
|
||||||
head_before=$(git rev-parse HEAD) &&
|
head_before=$(git rev-parse HEAD) &&
|
||||||
git rebase --quit &&
|
git rebase --quit &&
|
||||||
|
@ -34,7 +34,7 @@ test_expect_success setup '
|
|||||||
remove_progress_re="$(printf "s/.*\\r//")"
|
remove_progress_re="$(printf "s/.*\\r//")"
|
||||||
'
|
'
|
||||||
|
|
||||||
create_expected_success_am () {
|
create_expected_success_apply () {
|
||||||
cat >expected <<-EOF
|
cat >expected <<-EOF
|
||||||
$(grep "^Created autostash: [0-9a-f][0-9a-f]*\$" actual)
|
$(grep "^Created autostash: [0-9a-f][0-9a-f]*\$" actual)
|
||||||
First, rewinding head to replay your work on top of it...
|
First, rewinding head to replay your work on top of it...
|
||||||
@ -44,7 +44,7 @@ create_expected_success_am () {
|
|||||||
EOF
|
EOF
|
||||||
}
|
}
|
||||||
|
|
||||||
create_expected_success_interactive () {
|
create_expected_success_merge () {
|
||||||
q_to_cr >expected <<-EOF
|
q_to_cr >expected <<-EOF
|
||||||
$(grep "^Created autostash: [0-9a-f][0-9a-f]*\$" actual)
|
$(grep "^Created autostash: [0-9a-f][0-9a-f]*\$" actual)
|
||||||
Applied autostash.
|
Applied autostash.
|
||||||
@ -52,7 +52,7 @@ create_expected_success_interactive () {
|
|||||||
EOF
|
EOF
|
||||||
}
|
}
|
||||||
|
|
||||||
create_expected_failure_am () {
|
create_expected_failure_apply () {
|
||||||
cat >expected <<-EOF
|
cat >expected <<-EOF
|
||||||
$(grep "^Created autostash: [0-9a-f][0-9a-f]*\$" actual)
|
$(grep "^Created autostash: [0-9a-f][0-9a-f]*\$" actual)
|
||||||
First, rewinding head to replay your work on top of it...
|
First, rewinding head to replay your work on top of it...
|
||||||
@ -64,7 +64,7 @@ create_expected_failure_am () {
|
|||||||
EOF
|
EOF
|
||||||
}
|
}
|
||||||
|
|
||||||
create_expected_failure_interactive () {
|
create_expected_failure_merge () {
|
||||||
cat >expected <<-EOF
|
cat >expected <<-EOF
|
||||||
$(grep "^Created autostash: [0-9a-f][0-9a-f]*\$" actual)
|
$(grep "^Created autostash: [0-9a-f][0-9a-f]*\$" actual)
|
||||||
Applying autostash resulted in conflicts.
|
Applying autostash resulted in conflicts.
|
||||||
@ -101,9 +101,9 @@ testrebase () {
|
|||||||
|
|
||||||
test_expect_success "rebase$type --autostash: check output" '
|
test_expect_success "rebase$type --autostash: check output" '
|
||||||
test_when_finished git branch -D rebased-feature-branch &&
|
test_when_finished git branch -D rebased-feature-branch &&
|
||||||
suffix=${type#\ --} && suffix=${suffix:-am} &&
|
suffix=${type#\ --} && suffix=${suffix:-apply} &&
|
||||||
if test ${suffix} = "merge"; then
|
if test ${suffix} = "interactive"; then
|
||||||
suffix=interactive
|
suffix=merge
|
||||||
fi &&
|
fi &&
|
||||||
create_expected_success_$suffix &&
|
create_expected_success_$suffix &&
|
||||||
sed "$remove_progress_re" <actual >actual2 &&
|
sed "$remove_progress_re" <actual >actual2 &&
|
||||||
@ -202,9 +202,9 @@ testrebase () {
|
|||||||
|
|
||||||
test_expect_success "rebase$type: check output with conflicting stash" '
|
test_expect_success "rebase$type: check output with conflicting stash" '
|
||||||
test_when_finished git branch -D rebased-feature-branch &&
|
test_when_finished git branch -D rebased-feature-branch &&
|
||||||
suffix=${type#\ --} && suffix=${suffix:-am} &&
|
suffix=${type#\ --} && suffix=${suffix:-apply} &&
|
||||||
if test ${suffix} = "merge"; then
|
if test ${suffix} = "interactive"; then
|
||||||
suffix=interactive
|
suffix=merge
|
||||||
fi &&
|
fi &&
|
||||||
create_expected_failure_$suffix &&
|
create_expected_failure_$suffix &&
|
||||||
sed "$remove_progress_re" <actual >actual2 &&
|
sed "$remove_progress_re" <actual >actual2 &&
|
||||||
@ -234,7 +234,7 @@ test_expect_success "rebase: noop rebase" '
|
|||||||
git checkout feature-branch
|
git checkout feature-branch
|
||||||
'
|
'
|
||||||
|
|
||||||
testrebase "" .git/rebase-apply
|
testrebase " --apply" .git/rebase-apply
|
||||||
testrebase " --merge" .git/rebase-merge
|
testrebase " --merge" .git/rebase-merge
|
||||||
testrebase " --interactive" .git/rebase-merge
|
testrebase " --interactive" .git/rebase-merge
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ test_run_rebase () {
|
|||||||
test_linear_range 'd e' c..
|
test_linear_range 'd e' c..
|
||||||
"
|
"
|
||||||
}
|
}
|
||||||
test_run_rebase success ''
|
test_run_rebase success --apply
|
||||||
test_run_rebase success -m
|
test_run_rebase success -m
|
||||||
test_run_rebase success -i
|
test_run_rebase success -i
|
||||||
test_have_prereq !REBASE_P || test_run_rebase success -p
|
test_have_prereq !REBASE_P || test_run_rebase success -p
|
||||||
@ -50,7 +50,7 @@ test_run_rebase () {
|
|||||||
test_cmp_rev e HEAD
|
test_cmp_rev e HEAD
|
||||||
"
|
"
|
||||||
}
|
}
|
||||||
test_run_rebase success ''
|
test_run_rebase success --apply
|
||||||
test_run_rebase success -m
|
test_run_rebase success -m
|
||||||
test_run_rebase success -i
|
test_run_rebase success -i
|
||||||
test_have_prereq !REBASE_P || test_run_rebase success -p
|
test_have_prereq !REBASE_P || test_run_rebase success -p
|
||||||
@ -66,7 +66,7 @@ test_run_rebase () {
|
|||||||
test_linear_range 'd e' b..
|
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 --fork-point
|
||||||
test_run_rebase success -m
|
test_run_rebase success -m
|
||||||
test_run_rebase success -i
|
test_run_rebase success -i
|
||||||
@ -83,7 +83,7 @@ test_run_rebase () {
|
|||||||
test_linear_range 'd e' branch-b..
|
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 --fork-point
|
||||||
test_run_rebase success -m
|
test_run_rebase success -m
|
||||||
test_run_rebase success -i
|
test_run_rebase success -i
|
||||||
@ -98,7 +98,7 @@ test_run_rebase () {
|
|||||||
test_cmp_rev e HEAD
|
test_cmp_rev e HEAD
|
||||||
"
|
"
|
||||||
}
|
}
|
||||||
test_run_rebase success ''
|
test_run_rebase success --apply
|
||||||
test_run_rebase success --fork-point
|
test_run_rebase success --fork-point
|
||||||
test_run_rebase success -m
|
test_run_rebase success -m
|
||||||
test_run_rebase success -i
|
test_run_rebase success -i
|
||||||
@ -139,7 +139,7 @@ test_run_rebase () {
|
|||||||
test_linear_range 'd i' h..
|
test_linear_range 'd i' h..
|
||||||
"
|
"
|
||||||
}
|
}
|
||||||
test_run_rebase success ''
|
test_run_rebase success --apply
|
||||||
test_run_rebase success -m
|
test_run_rebase success -m
|
||||||
test_run_rebase success -i
|
test_run_rebase success -i
|
||||||
test_have_prereq !REBASE_P || test_run_rebase success -p
|
test_have_prereq !REBASE_P || test_run_rebase success -p
|
||||||
@ -154,7 +154,7 @@ test_run_rebase () {
|
|||||||
test_linear_range 'd' h..
|
test_linear_range 'd' h..
|
||||||
"
|
"
|
||||||
}
|
}
|
||||||
test_run_rebase success ''
|
test_run_rebase success --apply
|
||||||
test_run_rebase success -m
|
test_run_rebase success -m
|
||||||
test_run_rebase success -i
|
test_run_rebase success -i
|
||||||
test_have_prereq !REBASE_P || test_run_rebase success -p
|
test_have_prereq !REBASE_P || test_run_rebase success -p
|
||||||
@ -169,7 +169,7 @@ test_run_rebase () {
|
|||||||
test_linear_range 'd i' f..
|
test_linear_range 'd i' f..
|
||||||
"
|
"
|
||||||
}
|
}
|
||||||
test_run_rebase success ''
|
test_run_rebase success --apply
|
||||||
test_run_rebase success -m
|
test_run_rebase success -m
|
||||||
test_run_rebase success -i
|
test_run_rebase success -i
|
||||||
test_have_prereq !REBASE_P || test_run_rebase success -p
|
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_linear_range 'd gp i' h..
|
||||||
"
|
"
|
||||||
}
|
}
|
||||||
test_run_rebase success ''
|
test_run_rebase success --apply
|
||||||
test_run_rebase success -m
|
test_run_rebase success -m
|
||||||
test_run_rebase success -i
|
test_run_rebase success -i
|
||||||
test_have_prereq !REBASE_P || test_run_rebase success -p
|
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 () {
|
test_run_rebase () {
|
||||||
result=$1
|
result=$1
|
||||||
shift
|
shift
|
||||||
test_expect_$result "rebase $* drops empty commit" "
|
test_expect_$result "rebase $* keeps begin-empty commits" "
|
||||||
reset_rebase &&
|
reset_rebase &&
|
||||||
git rebase $* c l &&
|
git rebase $* j l &&
|
||||||
test_cmp_rev c HEAD~2 &&
|
test_cmp_rev c HEAD~4 &&
|
||||||
test_linear_range 'd l' c..
|
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 -m
|
||||||
test_run_rebase success -i
|
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 () {
|
test_run_rebase () {
|
||||||
result=$1
|
result=$1
|
||||||
@ -227,10 +227,10 @@ test_run_rebase () {
|
|||||||
test_linear_range 'd k l' c..
|
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 -m
|
||||||
test_run_rebase success -i
|
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 () {
|
test_run_rebase () {
|
||||||
result=$1
|
result=$1
|
||||||
@ -242,10 +242,10 @@ test_run_rebase () {
|
|||||||
test_linear_range 'd k l' j..
|
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 -m
|
||||||
test_run_rebase success -i
|
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
|
test_run_rebase success --rebase-merges
|
||||||
|
|
||||||
# m
|
# m
|
||||||
@ -282,7 +282,7 @@ test_run_rebase () {
|
|||||||
test_linear_range 'x y' c..
|
test_linear_range 'x y' c..
|
||||||
"
|
"
|
||||||
}
|
}
|
||||||
test_run_rebase success ''
|
test_run_rebase success --apply
|
||||||
test_run_rebase success -m
|
test_run_rebase success -m
|
||||||
test_run_rebase success -i
|
test_run_rebase success -i
|
||||||
test_have_prereq !REBASE_P || test_run_rebase success -p
|
test_have_prereq !REBASE_P || test_run_rebase success -p
|
||||||
@ -297,7 +297,7 @@ test_run_rebase () {
|
|||||||
test_linear_range 'x y' c..
|
test_linear_range 'x y' c..
|
||||||
"
|
"
|
||||||
}
|
}
|
||||||
test_run_rebase success ''
|
test_run_rebase success --apply
|
||||||
test_run_rebase success -m
|
test_run_rebase success -m
|
||||||
test_run_rebase success -i
|
test_run_rebase success -i
|
||||||
test_have_prereq !REBASE_P || test_run_rebase failure -p
|
test_have_prereq !REBASE_P || test_run_rebase failure -p
|
||||||
@ -312,7 +312,7 @@ test_run_rebase () {
|
|||||||
test_linear_range 'x y' m..
|
test_linear_range 'x y' m..
|
||||||
"
|
"
|
||||||
}
|
}
|
||||||
test_run_rebase success ''
|
test_run_rebase success --apply
|
||||||
test_run_rebase success -m
|
test_run_rebase success -m
|
||||||
test_run_rebase success -i
|
test_run_rebase success -i
|
||||||
test_have_prereq !REBASE_P || test_run_rebase success -p
|
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 -m
|
||||||
test_run_rebase success -i
|
test_run_rebase success -i
|
||||||
test_have_prereq !REBASE_P || test_run_rebase failure -p
|
test_have_prereq !REBASE_P || test_run_rebase failure -p
|
||||||
@ -343,7 +343,7 @@ test_run_rebase () {
|
|||||||
test_linear_range 'x y' m..
|
test_linear_range 'x y' m..
|
||||||
"
|
"
|
||||||
}
|
}
|
||||||
test_run_rebase success ''
|
test_run_rebase success --apply
|
||||||
test_run_rebase success -m
|
test_run_rebase success -m
|
||||||
test_run_rebase success -i
|
test_run_rebase success -i
|
||||||
test_have_prereq !REBASE_P || test_run_rebase failure -p
|
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_linear_range 'n o' e..
|
||||||
"
|
"
|
||||||
}
|
}
|
||||||
test_run_rebase success ''
|
test_run_rebase success --apply
|
||||||
test_run_rebase success -m
|
test_run_rebase success -m
|
||||||
test_run_rebase success -i
|
test_run_rebase success -i
|
||||||
|
|
||||||
@ -70,7 +70,7 @@ test_run_rebase () {
|
|||||||
test_linear_range "\'"$expected"\'" d..
|
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' -m
|
||||||
test_run_rebase success 'n o e' -i
|
test_run_rebase success 'n o e' -i
|
||||||
|
|
||||||
@ -86,7 +86,7 @@ test_run_rebase () {
|
|||||||
test_linear_range "\'"$expected"\'" c..
|
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' -m
|
||||||
test_run_rebase success 'd n o e' -i
|
test_run_rebase success 'd n o e' -i
|
||||||
|
|
||||||
@ -102,7 +102,7 @@ test_run_rebase () {
|
|||||||
test_linear_range "\'"$expected"\'" c..
|
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' -m
|
||||||
test_run_rebase success 'd n o e' -i
|
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"
|
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 &&
|
reset_rebase &&
|
||||||
git checkout -b rebase-onto to-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 &&
|
: first pick results in no changes &&
|
||||||
git rebase --continue &&
|
git rebase --skip &&
|
||||||
verbose test "$(commit_message HEAD~2)" = "master4" &&
|
verbose test "$(commit_message HEAD~2)" = "master4" &&
|
||||||
verbose test "$(commit_message HEAD~)" = "files_subtree/master5" &&
|
verbose test "$(commit_message HEAD~)" = "files_subtree/master5" &&
|
||||||
verbose test "$(commit_message HEAD)" = "Empty commit"
|
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 &&
|
reset_rebase &&
|
||||||
git checkout -b rebase-merges-onto to-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 &&
|
: first pick results in no changes &&
|
||||||
git rebase --continue &&
|
git rebase --skip &&
|
||||||
verbose test "$(commit_message HEAD~2)" = "master4" &&
|
verbose test "$(commit_message HEAD~2)" = "master4" &&
|
||||||
verbose test "$(commit_message HEAD~)" = "files_subtree/master5" &&
|
verbose test "$(commit_message HEAD~)" = "files_subtree/master5" &&
|
||||||
verbose test "$(commit_message HEAD)" = "Empty commit"
|
verbose test "$(commit_message HEAD)" = "Empty commit"
|
||||||
|
@ -28,8 +28,10 @@ test_rebase_same_head () {
|
|||||||
shift &&
|
shift &&
|
||||||
cmp_f="$1" &&
|
cmp_f="$1" &&
|
||||||
shift &&
|
shift &&
|
||||||
test_rebase_same_head_ $status_n $what_n $cmp_n "" "$*" &&
|
test_rebase_same_head_ $status_n $what_n $cmp_n " --apply" "$*" &&
|
||||||
test_rebase_same_head_ $status_f $what_f $cmp_f " --no-ff" "$*"
|
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_ () {
|
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" "
|
test_expect_$status "git rebase$flag $* with $changes is $what with $cmp HEAD" "
|
||||||
oldhead=\$(git rev-parse HEAD) &&
|
oldhead=\$(git rev-parse HEAD) &&
|
||||||
test_when_finished 'git reset --hard \$oldhead' &&
|
test_when_finished 'git reset --hard \$oldhead' &&
|
||||||
|
cp .git/logs/HEAD expect &&
|
||||||
git rebase$flag $* >stdout &&
|
git rebase$flag $* >stdout &&
|
||||||
if test $what = work
|
if test $what = work
|
||||||
then
|
then
|
||||||
# Must check this case first, for 'is up to
|
old=\$(wc -l <expect) &&
|
||||||
# date, rebase forced[...]rewinding head' cases
|
test_line_count '-gt' \$old .git/logs/HEAD
|
||||||
test_i18ngrep 'rewinding head' stdout
|
|
||||||
elif test $what = noop
|
elif test $what = noop
|
||||||
then
|
then
|
||||||
test_i18ngrep 'is up to date' stdout &&
|
test_cmp expect .git/logs/HEAD
|
||||||
test_i18ngrep ! 'rebase forced' stdout
|
|
||||||
elif test $what = noop-force
|
|
||||||
then
|
|
||||||
test_i18ngrep 'is up to date, rebase forced' stdout
|
|
||||||
fi &&
|
fi &&
|
||||||
newhead=\$(git rev-parse HEAD) &&
|
newhead=\$(git rev-parse HEAD) &&
|
||||||
if test $cmp = same
|
if test $cmp = same
|
||||||
@ -71,14 +69,14 @@ test_rebase_same_head_ () {
|
|||||||
|
|
||||||
changes='no changes'
|
changes='no changes'
|
||||||
test_rebase_same_head success noop same success work same
|
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 work same master
|
||||||
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 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 noop-force same --onto master... master
|
test_rebase_same_head success noop same success work 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 work 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 work 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 work 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 --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 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
|
||||||
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'
|
changes='our changes'
|
||||||
test_rebase_same_head success noop same success work same
|
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 work same master
|
||||||
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 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 noop-force same --onto master... master
|
test_rebase_same_head success noop same success work 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 work 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 work 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 work 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 --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 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
|
||||||
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'
|
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 work 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 master... master
|
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 master
|
||||||
test_rebase_same_head success noop same success work diff --keep-base
|
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 ! -f post-rewrite.data
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success 'git rebase' '
|
test_expect_success 'git rebase --apply' '
|
||||||
git reset --hard D &&
|
git reset --hard D &&
|
||||||
clear_hook_input &&
|
clear_hook_input &&
|
||||||
test_must_fail git rebase --onto A B &&
|
test_must_fail git rebase --apply --onto A B &&
|
||||||
echo C > foo &&
|
echo C > foo &&
|
||||||
git add foo &&
|
git add foo &&
|
||||||
git rebase --continue &&
|
git rebase --continue &&
|
||||||
@ -68,10 +68,10 @@ test_expect_success 'git rebase' '
|
|||||||
verify_hook_input
|
verify_hook_input
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success 'git rebase --skip' '
|
test_expect_success 'git rebase --apply --skip' '
|
||||||
git reset --hard D &&
|
git reset --hard D &&
|
||||||
clear_hook_input &&
|
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 &&
|
test_must_fail git rebase --skip &&
|
||||||
echo D > foo &&
|
echo D > foo &&
|
||||||
git add foo &&
|
git add foo &&
|
||||||
@ -84,10 +84,10 @@ test_expect_success 'git rebase --skip' '
|
|||||||
verify_hook_input
|
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 &&
|
git reset --hard F &&
|
||||||
clear_hook_input &&
|
clear_hook_input &&
|
||||||
test_must_fail git rebase --onto D A &&
|
test_must_fail git rebase --apply --onto D A &&
|
||||||
git rebase --skip &&
|
git rebase --skip &&
|
||||||
echo rebase >expected.args &&
|
echo rebase >expected.args &&
|
||||||
cat >expected.data <<-EOF &&
|
cat >expected.data <<-EOF &&
|
||||||
@ -128,7 +128,7 @@ test_expect_success 'git rebase -m --skip' '
|
|||||||
verify_hook_input
|
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 &&
|
git reset --hard D &&
|
||||||
clear_hook_input &&
|
clear_hook_input &&
|
||||||
test_must_fail git rebase --keep-empty --onto A B &&
|
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
|
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 &&
|
git reset --hard D &&
|
||||||
clear_hook_input &&
|
clear_hook_input &&
|
||||||
test_must_fail git rebase --keep-empty --onto A B &&
|
test_must_fail git rebase --keep-empty --onto A B &&
|
||||||
|
@ -277,14 +277,27 @@ test_expect_success '--rebase' '
|
|||||||
test_cmp expect actual
|
test_cmp expect actual
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success '--rebase fast forward' '
|
test_expect_success '--rebase (merge) fast forward' '
|
||||||
git reset --hard before-rebase &&
|
git reset --hard before-rebase &&
|
||||||
git checkout -b ff &&
|
git checkout -b ff &&
|
||||||
echo another modification >file &&
|
echo another modification >file &&
|
||||||
git commit -m third file &&
|
git commit -m third file &&
|
||||||
|
|
||||||
git checkout to-rebase &&
|
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 &&
|
test_cmp_rev HEAD ff &&
|
||||||
|
|
||||||
# The above only validates the result. Did we actually bypass rebase?
|
# 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 &&
|
test_tick &&
|
||||||
git commit -m "Create conflict" seq.txt &&
|
git commit -m "Create conflict" seq.txt &&
|
||||||
test_must_fail git pull --rebase . seq 2>err >out &&
|
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' '
|
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^ &&
|
git checkout -f -b fails-to-rebase HEAD^ &&
|
||||||
test_commit v2-without-cr file "2" file2-lf &&
|
test_commit v2-without-cr file "2" file2-lf &&
|
||||||
test_must_fail git pull --rebase . diverging 2>err >out &&
|
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' '
|
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 &&
|
cd dst &&
|
||||||
test_must_fail git pull --rebase &&
|
test_must_fail git pull --rebase &&
|
||||||
find .git/rebase-apply -name "000*" >patches &&
|
cat .git/rebase-merge/done .git/rebase-merge/git-rebase-todo >work &&
|
||||||
test_line_count = 1 patches
|
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 &&
|
test_create_repo rebase &&
|
||||||
(
|
(
|
||||||
cd rebase &&
|
cd rebase &&
|
||||||
@ -194,7 +194,16 @@ test_expect_success 'rebase describes fake ancestor base' '
|
|||||||
test_commit master file &&
|
test_commit master file &&
|
||||||
git checkout -b side HEAD^ &&
|
git checkout -b side HEAD^ &&
|
||||||
test_commit side file &&
|
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
|
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" &&
|
test_when_finished "git rebase --abort" &&
|
||||||
ONTO=$(git rev-parse --short HEAD^^) &&
|
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 &&
|
cat >expected <<EOF &&
|
||||||
rebase in progress; onto $ONTO
|
rebase in progress; onto $ONTO
|
||||||
You are currently rebasing branch '\''rebase_conflicts'\'' on '\''$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 &&
|
git reset --hard rebase_conflicts &&
|
||||||
test_when_finished "git rebase --abort" &&
|
test_when_finished "git rebase --abort" &&
|
||||||
ONTO=$(git rev-parse --short HEAD^^) &&
|
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 &&
|
echo three >main.txt &&
|
||||||
git add main.txt &&
|
git add main.txt &&
|
||||||
cat >expected <<EOF &&
|
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 reset --hard master &&
|
||||||
git checkout -b statushints_disabled &&
|
git checkout -b statushints_disabled &&
|
||||||
test_when_finished "git config --local advice.statushints true" &&
|
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_commit three_statushints main.txt three &&
|
||||||
test_when_finished "git rebase --abort" &&
|
test_when_finished "git rebase --abort" &&
|
||||||
ONTO=$(git rev-parse --short HEAD^^) &&
|
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 &&
|
cat >expected <<EOF &&
|
||||||
rebase in progress; onto $ONTO
|
rebase in progress; onto $ONTO
|
||||||
You are currently rebasing branch '\''statushints_disabled'\'' on '\''$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_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' "
|
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' '
|
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 &&
|
write_script fake_editor.sh <<-\EOF &&
|
||||||
echo "exec echo" >"$1"
|
echo "exec echo" >"$1"
|
||||||
echo "edit $(git log -1 --format="%h")" >>"$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' '
|
test_expect_success 'prompt - rebase merge' '
|
||||||
printf " (b2|REBASE-i 1/3)" >expected &&
|
printf " (b2|REBASE 1/3)" >expected &&
|
||||||
git checkout b2 &&
|
git checkout b2 &&
|
||||||
test_when_finished "git checkout master" &&
|
test_when_finished "git checkout master" &&
|
||||||
test_must_fail git rebase --merge b1 b2 &&
|
test_must_fail git rebase --merge b1 b2 &&
|
||||||
@ -189,11 +189,11 @@ test_expect_success 'prompt - rebase merge' '
|
|||||||
test_cmp expected "$actual"
|
test_cmp expected "$actual"
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success 'prompt - rebase' '
|
test_expect_success 'prompt - rebase am' '
|
||||||
printf " (b2|REBASE 1/3)" >expected &&
|
printf " (b2|REBASE 1/3)" >expected &&
|
||||||
git checkout b2 &&
|
git checkout b2 &&
|
||||||
test_when_finished "git checkout master" &&
|
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" &&
|
test_when_finished "git rebase --abort" &&
|
||||||
__git_ps1 >"$actual" &&
|
__git_ps1 >"$actual" &&
|
||||||
test_cmp expected "$actual"
|
test_cmp expected "$actual"
|
||||||
|
Loading…
Reference in New Issue
Block a user