2018-08-06 19:31:09 +00:00
|
|
|
/*
|
|
|
|
* "git rebase" builtin command
|
|
|
|
*
|
|
|
|
* Copyright (c) 2018 Pratik Karki
|
|
|
|
*/
|
|
|
|
|
2022-11-19 13:07:38 +00:00
|
|
|
#define USE_THE_INDEX_VARIABLE
|
2018-08-06 19:31:09 +00:00
|
|
|
#include "builtin.h"
|
2023-03-21 06:25:58 +00:00
|
|
|
#include "abspath.h"
|
2023-03-21 06:26:03 +00:00
|
|
|
#include "environment.h"
|
2023-03-21 06:25:54 +00:00
|
|
|
#include "gettext.h"
|
2023-02-24 00:09:27 +00:00
|
|
|
#include "hex.h"
|
2018-08-06 19:31:09 +00:00
|
|
|
#include "run-command.h"
|
|
|
|
#include "exec-cmd.h"
|
2020-07-28 20:23:39 +00:00
|
|
|
#include "strvec.h"
|
2018-08-06 19:31:09 +00:00
|
|
|
#include "dir.h"
|
2018-08-06 19:31:11 +00:00
|
|
|
#include "packfile.h"
|
|
|
|
#include "refs.h"
|
|
|
|
#include "quote.h"
|
|
|
|
#include "config.h"
|
|
|
|
#include "cache-tree.h"
|
|
|
|
#include "unpack-trees.h"
|
|
|
|
#include "lockfile.h"
|
2023-04-11 07:41:53 +00:00
|
|
|
#include "object-file.h"
|
2023-04-11 07:41:49 +00:00
|
|
|
#include "object-name.h"
|
2018-09-04 21:27:07 +00:00
|
|
|
#include "parse-options.h"
|
2018-09-04 21:27:09 +00:00
|
|
|
#include "commit.h"
|
2018-09-04 21:27:13 +00:00
|
|
|
#include "diff.h"
|
2018-09-04 21:27:14 +00:00
|
|
|
#include "wt-status.h"
|
2018-09-04 21:27:16 +00:00
|
|
|
#include "revision.h"
|
2018-11-02 02:04:53 +00:00
|
|
|
#include "commit-reach.h"
|
2018-08-08 15:06:17 +00:00
|
|
|
#include "rerere.h"
|
2018-11-12 23:26:01 +00:00
|
|
|
#include "branch.h"
|
2019-04-17 14:30:37 +00:00
|
|
|
#include "sequencer.h"
|
|
|
|
#include "rebase-interactive.h"
|
2020-04-07 14:28:00 +00:00
|
|
|
#include "reset.h"
|
2023-04-11 03:00:38 +00:00
|
|
|
#include "trace2.h"
|
2021-12-22 03:59:32 +00:00
|
|
|
#include "hook.h"
|
2023-03-21 06:26:01 +00:00
|
|
|
#include "wrapper.h"
|
2018-09-04 21:27:07 +00:00
|
|
|
|
|
|
|
static char const * const builtin_rebase_usage[] = {
|
rebase: teach rebase --keep-base
A common scenario is if a user is working on a topic branch and they
wish to make some changes to intermediate commits or autosquash, they
would run something such as
git rebase -i --onto master... master
in order to preserve the merge base. This is useful when contributing a
patch series to the Git mailing list, one often starts on top of the
current 'master'. While developing the patches, 'master' is also
developed further and it is sometimes not the best idea to keep rebasing
on top of 'master', but to keep the base commit as-is.
In addition to this, a user wishing to test individual commits in a
topic branch without changing anything may run
git rebase -x ./test.sh master... master
Since rebasing onto the merge base of the branch and the upstream is
such a common case, introduce the --keep-base option as a shortcut.
This allows us to rewrite the above as
git rebase -i --keep-base master
and
git rebase -x ./test.sh --keep-base master
respectively.
Add tests to ensure --keep-base works correctly in the normal case and
fails when there are multiple merge bases, both in regular and
interactive mode. Also, test to make sure conflicting options cause
rebase to fail. While we're adding test cases, add a missing
set_fake_editor call to 'rebase -i --onto master...side'.
While we're documenting the --keep-base option, change an instance of
"merge-base" to "merge base", which is the consistent spelling.
Helped-by: Eric Sunshine <sunshine@sunshineco.com>
Helped-by: Junio C Hamano <gitster@pobox.com>
Helped-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Helped-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Signed-off-by: Denton Liu <liu.denton@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-08-27 05:38:06 +00:00
|
|
|
N_("git rebase [-i] [options] [--exec <cmd>] "
|
|
|
|
"[--onto <newbase> | --keep-base] [<upstream> [<branch>]]"),
|
2018-09-04 21:27:07 +00:00
|
|
|
N_("git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] "
|
|
|
|
"--root [<branch>]"),
|
2022-01-31 22:07:48 +00:00
|
|
|
"git rebase --continue | --abort | --skip | --edit-todo",
|
2018-09-04 21:27:07 +00:00
|
|
|
NULL
|
|
|
|
};
|
2018-08-06 19:31:11 +00:00
|
|
|
|
2019-04-17 14:30:37 +00:00
|
|
|
static GIT_PATH_FUNC(path_squash_onto, "rebase-merge/squash-onto")
|
|
|
|
static GIT_PATH_FUNC(path_interactive, "rebase-merge/interactive")
|
2018-08-06 19:31:11 +00:00
|
|
|
static GIT_PATH_FUNC(apply_dir, "rebase-apply")
|
|
|
|
static GIT_PATH_FUNC(merge_dir, "rebase-merge")
|
|
|
|
|
|
|
|
enum rebase_type {
|
|
|
|
REBASE_UNSPECIFIED = -1,
|
2020-02-15 21:36:41 +00:00
|
|
|
REBASE_APPLY,
|
2021-09-07 21:05:06 +00:00
|
|
|
REBASE_MERGE
|
2018-08-06 19:31:11 +00:00
|
|
|
};
|
2018-08-06 19:31:09 +00:00
|
|
|
|
rebase (interactive-backend): fix handling of commits that become empty
As established in the previous commit and commit b00bf1c9a8dd
(git-rebase: make --allow-empty-message the default, 2018-06-27), the
behavior for rebase with different backends in various edge or corner
cases is often more happenstance than design. This commit addresses
another such corner case: commits which "become empty".
A careful reader may note that there are two types of commits which would
become empty due to a rebase:
* [clean cherry-pick] Commits which are clean cherry-picks of upstream
commits, as determined by `git log --cherry-mark ...`. Re-applying
these commits would result in an empty set of changes and a
duplicative commit message; i.e. these are commits that have
"already been applied" upstream.
* [become empty] Commits which are not empty to start, are not clean
cherry-picks of upstream commits, but which still become empty after
being rebased. This happens e.g. when a commit has changes which
are a strict subset of the changes in an upstream commit, or when
the changes of a commit can be found spread across or among several
upstream commits.
Clearly, in both cases the changes in the commit in question are found
upstream already, but the commit message may not be in the latter case.
When cherry-mark can determine a commit is already upstream, then
because of how cherry-mark works this means the upstream commit message
was about the *exact* same set of changes. Thus, the commit messages
can be assumed to be fully interchangeable (and are in fact likely to be
completely identical). As such, the clean cherry-pick case represents a
case when there is no information to be gained by keeping the extra
commit around. All rebase types have always dropped these commits, and
no one to my knowledge has ever requested that we do otherwise.
For many of the become empty cases (and likely even most), we will also
be able to drop the commit without loss of information -- but this isn't
quite always the case. Since these commits represent cases that were
not clean cherry-picks, there is no upstream commit message explaining
the same set of changes. Projects with good commit message hygiene will
likely have the explanation from our commit message contained within or
spread among the relevant upstream commits, but not all projects run
that way. As such, the commit message of the commit being rebased may
have reasoning that suggests additional changes that should be made to
adapt to the new base, or it may have information that someone wants to
add as a note to another commit, or perhaps someone even wants to create
an empty commit with the commit message as-is.
Junio commented on the "become-empty" types of commits as follows[1]:
WRT a change that ends up being empty (as opposed to a change that
is empty from the beginning), I'd think that the current behaviour
is desireable one. "am" based rebase is solely to transplant an
existing history and want to stop much less than "interactive" one
whose purpose is to polish a series before making it publishable,
and asking for confirmation ("this has become empty--do you want to
drop it?") is more appropriate from the workflow point of view.
[1] https://lore.kernel.org/git/xmqqfu1fswdh.fsf@gitster-ct.c.googlers.com/
I would simply add that his arguments for "am"-based rebases actually
apply to all non-explicitly-interactive rebases. Also, since we are
stating that different cases should have different defaults, it may be
worth providing a flag to allow users to select which behavior they want
for these commits.
Introduce a new command line flag for selecting the desired behavior:
--empty={drop,keep,ask}
with the definitions:
drop: drop commits which become empty
keep: keep commits which become empty
ask: provide the user a chance to interact and pick what to do with
commits which become empty on a case-by-case basis
In line with Junio's suggestion, if the --empty flag is not specified,
pick defaults as follows:
explicitly interactive: ask
otherwise: drop
Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-02-15 21:36:25 +00:00
|
|
|
enum empty_type {
|
|
|
|
EMPTY_UNSPECIFIED = -1,
|
|
|
|
EMPTY_DROP,
|
|
|
|
EMPTY_KEEP,
|
|
|
|
EMPTY_ASK
|
|
|
|
};
|
|
|
|
|
2022-10-12 09:35:12 +00:00
|
|
|
enum action {
|
|
|
|
ACTION_NONE = 0,
|
|
|
|
ACTION_CONTINUE,
|
|
|
|
ACTION_SKIP,
|
|
|
|
ACTION_ABORT,
|
|
|
|
ACTION_QUIT,
|
|
|
|
ACTION_EDIT_TODO,
|
|
|
|
ACTION_SHOW_CURRENT_PATCH
|
|
|
|
};
|
|
|
|
|
|
|
|
static const char *action_names[] = {
|
|
|
|
"undefined",
|
|
|
|
"continue",
|
|
|
|
"skip",
|
|
|
|
"abort",
|
|
|
|
"quit",
|
|
|
|
"edit_todo",
|
|
|
|
"show_current_patch"
|
|
|
|
};
|
|
|
|
|
2018-08-06 19:31:11 +00:00
|
|
|
struct rebase_options {
|
|
|
|
enum rebase_type type;
|
rebase (interactive-backend): fix handling of commits that become empty
As established in the previous commit and commit b00bf1c9a8dd
(git-rebase: make --allow-empty-message the default, 2018-06-27), the
behavior for rebase with different backends in various edge or corner
cases is often more happenstance than design. This commit addresses
another such corner case: commits which "become empty".
A careful reader may note that there are two types of commits which would
become empty due to a rebase:
* [clean cherry-pick] Commits which are clean cherry-picks of upstream
commits, as determined by `git log --cherry-mark ...`. Re-applying
these commits would result in an empty set of changes and a
duplicative commit message; i.e. these are commits that have
"already been applied" upstream.
* [become empty] Commits which are not empty to start, are not clean
cherry-picks of upstream commits, but which still become empty after
being rebased. This happens e.g. when a commit has changes which
are a strict subset of the changes in an upstream commit, or when
the changes of a commit can be found spread across or among several
upstream commits.
Clearly, in both cases the changes in the commit in question are found
upstream already, but the commit message may not be in the latter case.
When cherry-mark can determine a commit is already upstream, then
because of how cherry-mark works this means the upstream commit message
was about the *exact* same set of changes. Thus, the commit messages
can be assumed to be fully interchangeable (and are in fact likely to be
completely identical). As such, the clean cherry-pick case represents a
case when there is no information to be gained by keeping the extra
commit around. All rebase types have always dropped these commits, and
no one to my knowledge has ever requested that we do otherwise.
For many of the become empty cases (and likely even most), we will also
be able to drop the commit without loss of information -- but this isn't
quite always the case. Since these commits represent cases that were
not clean cherry-picks, there is no upstream commit message explaining
the same set of changes. Projects with good commit message hygiene will
likely have the explanation from our commit message contained within or
spread among the relevant upstream commits, but not all projects run
that way. As such, the commit message of the commit being rebased may
have reasoning that suggests additional changes that should be made to
adapt to the new base, or it may have information that someone wants to
add as a note to another commit, or perhaps someone even wants to create
an empty commit with the commit message as-is.
Junio commented on the "become-empty" types of commits as follows[1]:
WRT a change that ends up being empty (as opposed to a change that
is empty from the beginning), I'd think that the current behaviour
is desireable one. "am" based rebase is solely to transplant an
existing history and want to stop much less than "interactive" one
whose purpose is to polish a series before making it publishable,
and asking for confirmation ("this has become empty--do you want to
drop it?") is more appropriate from the workflow point of view.
[1] https://lore.kernel.org/git/xmqqfu1fswdh.fsf@gitster-ct.c.googlers.com/
I would simply add that his arguments for "am"-based rebases actually
apply to all non-explicitly-interactive rebases. Also, since we are
stating that different cases should have different defaults, it may be
worth providing a flag to allow users to select which behavior they want
for these commits.
Introduce a new command line flag for selecting the desired behavior:
--empty={drop,keep,ask}
with the definitions:
drop: drop commits which become empty
keep: keep commits which become empty
ask: provide the user a chance to interact and pick what to do with
commits which become empty on a case-by-case basis
In line with Junio's suggestion, if the --empty flag is not specified,
pick defaults as follows:
explicitly interactive: ask
otherwise: drop
Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-02-15 21:36:25 +00:00
|
|
|
enum empty_type empty;
|
2020-02-15 21:36:39 +00:00
|
|
|
const char *default_backend;
|
2018-08-06 19:31:11 +00:00
|
|
|
const char *state_dir;
|
|
|
|
struct commit *upstream;
|
|
|
|
const char *upstream_name;
|
2018-09-04 21:27:10 +00:00
|
|
|
const char *upstream_arg;
|
2018-08-06 19:31:11 +00:00
|
|
|
char *head_name;
|
2022-10-17 13:17:41 +00:00
|
|
|
struct commit *orig_head;
|
2018-08-06 19:31:11 +00:00
|
|
|
struct commit *onto;
|
|
|
|
const char *onto_name;
|
|
|
|
const char *revisions;
|
2018-09-04 21:27:21 +00:00
|
|
|
const char *switch_to;
|
2019-07-31 15:18:49 +00:00
|
|
|
int root, root_with_onto;
|
2018-09-04 22:00:12 +00:00
|
|
|
struct object_id *squash_onto;
|
2018-08-06 19:31:11 +00:00
|
|
|
struct commit *restrict_revision;
|
|
|
|
int dont_finish_rebase;
|
2018-09-04 21:27:12 +00:00
|
|
|
enum {
|
|
|
|
REBASE_NO_QUIET = 1<<0,
|
2018-09-04 21:27:13 +00:00
|
|
|
REBASE_VERBOSE = 1<<1,
|
|
|
|
REBASE_DIFFSTAT = 1<<2,
|
2018-09-04 21:27:17 +00:00
|
|
|
REBASE_FORCE = 1<<3,
|
2018-09-04 21:27:18 +00:00
|
|
|
REBASE_INTERACTIVE_EXPLICIT = 1<<4,
|
2018-09-04 21:27:12 +00:00
|
|
|
} flags;
|
2020-07-28 20:24:27 +00:00
|
|
|
struct strvec git_am_opts;
|
2022-10-12 09:35:12 +00:00
|
|
|
enum action action;
|
2022-11-09 14:21:58 +00:00
|
|
|
char *reflog_action;
|
2018-09-04 21:59:50 +00:00
|
|
|
int signoff;
|
2018-09-04 21:59:52 +00:00
|
|
|
int allow_rerere_autoupdate;
|
rebase: reinstate --no-keep-empty
Commit d48e5e21da ("rebase (interactive-backend): make --keep-empty the
default", 2020-02-15) turned --keep-empty (for keeping commits which
start empty) into the default. The logic underpinning that commit was:
1) 'git commit' errors out on the creation of empty commits without an
override flag
2) Once someone determines that the override is worthwhile, it's
annoying and/or harmful to required them to take extra steps in
order to keep such commits around (and to repeat such steps with
every rebase).
While the logic on which the decision was made is sound, the result was
a bit of an overcorrection. Instead of jumping to having --keep-empty
being the default, it jumped to making --keep-empty the only available
behavior. There was a simple workaround, though, which was thought to
be good enough at the time. People could still drop commits which
started empty the same way the could drop any commits: by firing up an
interactive rebase and picking out the commits they didn't want from the
list. However, there are cases where external tools might create enough
empty commits that picking all of them out is painful. As such, having
a flag to automatically remove start-empty commits may be beneficial.
Provide users a way to drop commits which start empty using a flag that
existed for years: --no-keep-empty. Interpret --keep-empty as
countermanding any previous --no-keep-empty, but otherwise leaving
--keep-empty as the default.
This might lead to some slight weirdness since commands like
git rebase --empty=drop --keep-empty
git rebase --empty=keep --no-keep-empty
look really weird despite making perfect sense (the first will drop
commits which become empty, but keep commits that started empty; the
second will keep commits which become empty, but drop commits which
started empty). However, --no-keep-empty was named years ago and we are
predominantly keeping it for backward compatibility; also we suspect it
will only be used rarely since folks already have a simple way to drop
commits they don't want with an interactive rebase.
Reported-by: Bryan Turner <bturner@atlassian.com>
Reported-by: Sami Boukortt <sami@boukortt.com>
Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-04-11 02:44:25 +00:00
|
|
|
int keep_empty;
|
2018-09-04 21:59:58 +00:00
|
|
|
int autosquash;
|
2018-09-04 22:00:00 +00:00
|
|
|
char *gpg_sign_opt;
|
2018-09-04 22:00:02 +00:00
|
|
|
int autostash;
|
2020-08-17 17:40:02 +00:00
|
|
|
int committer_date_is_author_date;
|
2020-08-17 17:40:03 +00:00
|
|
|
int ignore_date;
|
2023-01-12 16:50:01 +00:00
|
|
|
struct string_list exec;
|
2018-09-04 22:00:05 +00:00
|
|
|
int allow_empty_message;
|
2018-09-04 22:00:07 +00:00
|
|
|
int rebase_merges, rebase_cousins;
|
2023-04-10 09:08:29 +00:00
|
|
|
char *strategy;
|
|
|
|
struct string_list strategy_opts;
|
2018-08-08 15:36:33 +00:00
|
|
|
struct strbuf git_format_patch_opt;
|
2018-12-10 19:04:58 +00:00
|
|
|
int reschedule_failed_exec;
|
2020-04-11 02:44:27 +00:00
|
|
|
int reapply_cherry_picks;
|
2021-02-23 07:18:40 +00:00
|
|
|
int fork_point;
|
2022-07-19 18:33:39 +00:00
|
|
|
int update_refs;
|
2023-01-25 04:03:54 +00:00
|
|
|
int config_autosquash;
|
2023-03-26 03:06:36 +00:00
|
|
|
int config_rebase_merges;
|
2023-01-25 04:03:54 +00:00
|
|
|
int config_update_refs;
|
2018-08-06 19:31:11 +00:00
|
|
|
};
|
|
|
|
|
2019-04-17 14:30:41 +00:00
|
|
|
#define REBASE_OPTIONS_INIT { \
|
|
|
|
.type = REBASE_UNSPECIFIED, \
|
rebase (interactive-backend): fix handling of commits that become empty
As established in the previous commit and commit b00bf1c9a8dd
(git-rebase: make --allow-empty-message the default, 2018-06-27), the
behavior for rebase with different backends in various edge or corner
cases is often more happenstance than design. This commit addresses
another such corner case: commits which "become empty".
A careful reader may note that there are two types of commits which would
become empty due to a rebase:
* [clean cherry-pick] Commits which are clean cherry-picks of upstream
commits, as determined by `git log --cherry-mark ...`. Re-applying
these commits would result in an empty set of changes and a
duplicative commit message; i.e. these are commits that have
"already been applied" upstream.
* [become empty] Commits which are not empty to start, are not clean
cherry-picks of upstream commits, but which still become empty after
being rebased. This happens e.g. when a commit has changes which
are a strict subset of the changes in an upstream commit, or when
the changes of a commit can be found spread across or among several
upstream commits.
Clearly, in both cases the changes in the commit in question are found
upstream already, but the commit message may not be in the latter case.
When cherry-mark can determine a commit is already upstream, then
because of how cherry-mark works this means the upstream commit message
was about the *exact* same set of changes. Thus, the commit messages
can be assumed to be fully interchangeable (and are in fact likely to be
completely identical). As such, the clean cherry-pick case represents a
case when there is no information to be gained by keeping the extra
commit around. All rebase types have always dropped these commits, and
no one to my knowledge has ever requested that we do otherwise.
For many of the become empty cases (and likely even most), we will also
be able to drop the commit without loss of information -- but this isn't
quite always the case. Since these commits represent cases that were
not clean cherry-picks, there is no upstream commit message explaining
the same set of changes. Projects with good commit message hygiene will
likely have the explanation from our commit message contained within or
spread among the relevant upstream commits, but not all projects run
that way. As such, the commit message of the commit being rebased may
have reasoning that suggests additional changes that should be made to
adapt to the new base, or it may have information that someone wants to
add as a note to another commit, or perhaps someone even wants to create
an empty commit with the commit message as-is.
Junio commented on the "become-empty" types of commits as follows[1]:
WRT a change that ends up being empty (as opposed to a change that
is empty from the beginning), I'd think that the current behaviour
is desireable one. "am" based rebase is solely to transplant an
existing history and want to stop much less than "interactive" one
whose purpose is to polish a series before making it publishable,
and asking for confirmation ("this has become empty--do you want to
drop it?") is more appropriate from the workflow point of view.
[1] https://lore.kernel.org/git/xmqqfu1fswdh.fsf@gitster-ct.c.googlers.com/
I would simply add that his arguments for "am"-based rebases actually
apply to all non-explicitly-interactive rebases. Also, since we are
stating that different cases should have different defaults, it may be
worth providing a flag to allow users to select which behavior they want
for these commits.
Introduce a new command line flag for selecting the desired behavior:
--empty={drop,keep,ask}
with the definitions:
drop: drop commits which become empty
keep: keep commits which become empty
ask: provide the user a chance to interact and pick what to do with
commits which become empty on a case-by-case basis
In line with Junio's suggestion, if the --empty flag is not specified,
pick defaults as follows:
explicitly interactive: ask
otherwise: drop
Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-02-15 21:36:25 +00:00
|
|
|
.empty = EMPTY_UNSPECIFIED, \
|
rebase: reinstate --no-keep-empty
Commit d48e5e21da ("rebase (interactive-backend): make --keep-empty the
default", 2020-02-15) turned --keep-empty (for keeping commits which
start empty) into the default. The logic underpinning that commit was:
1) 'git commit' errors out on the creation of empty commits without an
override flag
2) Once someone determines that the override is worthwhile, it's
annoying and/or harmful to required them to take extra steps in
order to keep such commits around (and to repeat such steps with
every rebase).
While the logic on which the decision was made is sound, the result was
a bit of an overcorrection. Instead of jumping to having --keep-empty
being the default, it jumped to making --keep-empty the only available
behavior. There was a simple workaround, though, which was thought to
be good enough at the time. People could still drop commits which
started empty the same way the could drop any commits: by firing up an
interactive rebase and picking out the commits they didn't want from the
list. However, there are cases where external tools might create enough
empty commits that picking all of them out is painful. As such, having
a flag to automatically remove start-empty commits may be beneficial.
Provide users a way to drop commits which start empty using a flag that
existed for years: --no-keep-empty. Interpret --keep-empty as
countermanding any previous --no-keep-empty, but otherwise leaving
--keep-empty as the default.
This might lead to some slight weirdness since commands like
git rebase --empty=drop --keep-empty
git rebase --empty=keep --no-keep-empty
look really weird despite making perfect sense (the first will drop
commits which become empty, but keep commits that started empty; the
second will keep commits which become empty, but drop commits which
started empty). However, --no-keep-empty was named years ago and we are
predominantly keeping it for backward compatibility; also we suspect it
will only be used rarely since folks already have a simple way to drop
commits they don't want with an interactive rebase.
Reported-by: Bryan Turner <bturner@atlassian.com>
Reported-by: Sami Boukortt <sami@boukortt.com>
Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-04-11 02:44:25 +00:00
|
|
|
.keep_empty = 1, \
|
2020-02-15 21:36:40 +00:00
|
|
|
.default_backend = "merge", \
|
2019-04-17 14:30:41 +00:00
|
|
|
.flags = REBASE_NO_QUIET, \
|
2020-07-28 20:24:27 +00:00
|
|
|
.git_am_opts = STRVEC_INIT, \
|
2023-01-12 16:50:01 +00:00
|
|
|
.exec = STRING_LIST_INIT_NODUP, \
|
2021-02-23 07:18:40 +00:00
|
|
|
.git_format_patch_opt = STRBUF_INIT, \
|
|
|
|
.fork_point = -1, \
|
2023-01-25 04:03:53 +00:00
|
|
|
.reapply_cherry_picks = -1, \
|
|
|
|
.allow_empty_message = 1, \
|
2023-01-25 04:03:54 +00:00
|
|
|
.autosquash = -1, \
|
|
|
|
.config_autosquash = -1, \
|
2023-03-26 03:06:36 +00:00
|
|
|
.rebase_merges = -1, \
|
|
|
|
.config_rebase_merges = -1, \
|
2023-01-25 04:03:54 +00:00
|
|
|
.update_refs = -1, \
|
|
|
|
.config_update_refs = -1, \
|
2023-04-10 09:08:29 +00:00
|
|
|
.strategy_opts = STRING_LIST_INIT_NODUP,\
|
2019-04-17 14:30:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static struct replay_opts get_replay_opts(const struct rebase_options *opts)
|
|
|
|
{
|
|
|
|
struct replay_opts replay = REPLAY_OPTS_INIT;
|
|
|
|
|
|
|
|
replay.action = REPLAY_INTERACTIVE_REBASE;
|
2020-11-02 23:45:34 +00:00
|
|
|
replay.strategy = NULL;
|
2019-04-17 14:30:41 +00:00
|
|
|
sequencer_init_config(&replay);
|
|
|
|
|
|
|
|
replay.signoff = opts->signoff;
|
|
|
|
replay.allow_ff = !(opts->flags & REBASE_FORCE);
|
|
|
|
if (opts->allow_rerere_autoupdate)
|
|
|
|
replay.allow_rerere_auto = opts->allow_rerere_autoupdate;
|
|
|
|
replay.allow_empty = 1;
|
|
|
|
replay.allow_empty_message = opts->allow_empty_message;
|
rebase (interactive-backend): fix handling of commits that become empty
As established in the previous commit and commit b00bf1c9a8dd
(git-rebase: make --allow-empty-message the default, 2018-06-27), the
behavior for rebase with different backends in various edge or corner
cases is often more happenstance than design. This commit addresses
another such corner case: commits which "become empty".
A careful reader may note that there are two types of commits which would
become empty due to a rebase:
* [clean cherry-pick] Commits which are clean cherry-picks of upstream
commits, as determined by `git log --cherry-mark ...`. Re-applying
these commits would result in an empty set of changes and a
duplicative commit message; i.e. these are commits that have
"already been applied" upstream.
* [become empty] Commits which are not empty to start, are not clean
cherry-picks of upstream commits, but which still become empty after
being rebased. This happens e.g. when a commit has changes which
are a strict subset of the changes in an upstream commit, or when
the changes of a commit can be found spread across or among several
upstream commits.
Clearly, in both cases the changes in the commit in question are found
upstream already, but the commit message may not be in the latter case.
When cherry-mark can determine a commit is already upstream, then
because of how cherry-mark works this means the upstream commit message
was about the *exact* same set of changes. Thus, the commit messages
can be assumed to be fully interchangeable (and are in fact likely to be
completely identical). As such, the clean cherry-pick case represents a
case when there is no information to be gained by keeping the extra
commit around. All rebase types have always dropped these commits, and
no one to my knowledge has ever requested that we do otherwise.
For many of the become empty cases (and likely even most), we will also
be able to drop the commit without loss of information -- but this isn't
quite always the case. Since these commits represent cases that were
not clean cherry-picks, there is no upstream commit message explaining
the same set of changes. Projects with good commit message hygiene will
likely have the explanation from our commit message contained within or
spread among the relevant upstream commits, but not all projects run
that way. As such, the commit message of the commit being rebased may
have reasoning that suggests additional changes that should be made to
adapt to the new base, or it may have information that someone wants to
add as a note to another commit, or perhaps someone even wants to create
an empty commit with the commit message as-is.
Junio commented on the "become-empty" types of commits as follows[1]:
WRT a change that ends up being empty (as opposed to a change that
is empty from the beginning), I'd think that the current behaviour
is desireable one. "am" based rebase is solely to transplant an
existing history and want to stop much less than "interactive" one
whose purpose is to polish a series before making it publishable,
and asking for confirmation ("this has become empty--do you want to
drop it?") is more appropriate from the workflow point of view.
[1] https://lore.kernel.org/git/xmqqfu1fswdh.fsf@gitster-ct.c.googlers.com/
I would simply add that his arguments for "am"-based rebases actually
apply to all non-explicitly-interactive rebases. Also, since we are
stating that different cases should have different defaults, it may be
worth providing a flag to allow users to select which behavior they want
for these commits.
Introduce a new command line flag for selecting the desired behavior:
--empty={drop,keep,ask}
with the definitions:
drop: drop commits which become empty
keep: keep commits which become empty
ask: provide the user a chance to interact and pick what to do with
commits which become empty on a case-by-case basis
In line with Junio's suggestion, if the --empty flag is not specified,
pick defaults as follows:
explicitly interactive: ask
otherwise: drop
Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-02-15 21:36:25 +00:00
|
|
|
replay.drop_redundant_commits = (opts->empty == EMPTY_DROP);
|
|
|
|
replay.keep_redundant_commits = (opts->empty == EMPTY_KEEP);
|
2020-02-15 21:36:28 +00:00
|
|
|
replay.quiet = !(opts->flags & REBASE_NO_QUIET);
|
2019-04-17 14:30:41 +00:00
|
|
|
replay.verbose = opts->flags & REBASE_VERBOSE;
|
|
|
|
replay.reschedule_failed_exec = opts->reschedule_failed_exec;
|
2020-08-17 17:40:02 +00:00
|
|
|
replay.committer_date_is_author_date =
|
|
|
|
opts->committer_date_is_author_date;
|
2020-08-17 17:40:03 +00:00
|
|
|
replay.ignore_date = opts->ignore_date;
|
2019-04-17 14:30:41 +00:00
|
|
|
replay.gpg_sign = xstrdup_or_null(opts->gpg_sign_opt);
|
2022-11-09 14:21:58 +00:00
|
|
|
replay.reflog_action = xstrdup(opts->reflog_action);
|
2020-11-02 23:45:34 +00:00
|
|
|
if (opts->strategy)
|
2021-07-25 13:08:29 +00:00
|
|
|
replay.strategy = xstrdup_or_null(opts->strategy);
|
2020-11-02 23:45:34 +00:00
|
|
|
else if (!replay.strategy && replay.default_strategy) {
|
|
|
|
replay.strategy = replay.default_strategy;
|
|
|
|
replay.default_strategy = NULL;
|
|
|
|
}
|
2020-07-13 10:10:41 +00:00
|
|
|
|
2023-04-10 09:08:29 +00:00
|
|
|
for (size_t i = 0; i < opts->strategy_opts.nr; i++)
|
|
|
|
strvec_push(&replay.xopts, opts->strategy_opts.items[i].string);
|
2019-04-17 14:30:41 +00:00
|
|
|
|
2019-11-24 17:43:31 +00:00
|
|
|
if (opts->squash_onto) {
|
|
|
|
oidcpy(&replay.squash_onto, opts->squash_onto);
|
|
|
|
replay.have_squash_onto = 1;
|
|
|
|
}
|
|
|
|
|
2019-04-17 14:30:41 +00:00
|
|
|
return replay;
|
|
|
|
}
|
|
|
|
|
2019-04-17 14:30:37 +00:00
|
|
|
static int edit_todo_file(unsigned flags)
|
|
|
|
{
|
|
|
|
const char *todo_file = rebase_path_todo();
|
|
|
|
struct todo_list todo_list = TODO_LIST_INIT,
|
|
|
|
new_todo = TODO_LIST_INIT;
|
|
|
|
int res = 0;
|
|
|
|
|
|
|
|
if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)
|
|
|
|
return error_errno(_("could not read '%s'."), todo_file);
|
|
|
|
|
|
|
|
strbuf_stripspace(&todo_list.buf, 1);
|
|
|
|
res = edit_todo_list(the_repository, &todo_list, &new_todo, NULL, NULL, flags);
|
|
|
|
if (!res && todo_list_write_to_file(the_repository, &new_todo, todo_file,
|
|
|
|
NULL, NULL, -1, flags & ~(TODO_LIST_SHORTEN_IDS)))
|
|
|
|
res = error_errno(_("could not write '%s'"), todo_file);
|
|
|
|
|
|
|
|
todo_list_release(&todo_list);
|
|
|
|
todo_list_release(&new_todo);
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2019-04-17 14:30:39 +00:00
|
|
|
static int get_revision_ranges(struct commit *upstream, struct commit *onto,
|
2020-11-04 15:29:40 +00:00
|
|
|
struct object_id *orig_head, char **revisions,
|
|
|
|
char **shortrevisions)
|
2019-04-17 14:30:37 +00:00
|
|
|
{
|
2019-04-17 14:30:39 +00:00
|
|
|
struct commit *base_rev = upstream ? upstream : onto;
|
|
|
|
const char *shorthead;
|
2019-04-17 14:30:37 +00:00
|
|
|
|
2019-04-17 14:30:39 +00:00
|
|
|
*revisions = xstrfmt("%s...%s", oid_to_hex(&base_rev->object.oid),
|
2020-11-04 15:29:40 +00:00
|
|
|
oid_to_hex(orig_head));
|
2019-04-17 14:30:37 +00:00
|
|
|
|
2023-03-28 13:58:46 +00:00
|
|
|
shorthead = repo_find_unique_abbrev(the_repository, orig_head,
|
|
|
|
DEFAULT_ABBREV);
|
2019-04-17 14:30:37 +00:00
|
|
|
|
|
|
|
if (upstream) {
|
|
|
|
const char *shortrev;
|
|
|
|
|
2023-03-28 13:58:46 +00:00
|
|
|
shortrev = repo_find_unique_abbrev(the_repository,
|
|
|
|
&base_rev->object.oid,
|
|
|
|
DEFAULT_ABBREV);
|
2019-04-17 14:30:37 +00:00
|
|
|
|
|
|
|
*shortrevisions = xstrfmt("%s..%s", shortrev, shorthead);
|
|
|
|
} else
|
|
|
|
*shortrevisions = xstrdup(shorthead);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int init_basic_state(struct replay_opts *opts, const char *head_name,
|
2020-11-04 15:29:39 +00:00
|
|
|
struct commit *onto,
|
|
|
|
const struct object_id *orig_head)
|
2019-04-17 14:30:37 +00:00
|
|
|
{
|
|
|
|
FILE *interactive;
|
|
|
|
|
2019-04-17 14:30:38 +00:00
|
|
|
if (!is_directory(merge_dir()) && mkdir_in_gitdir(merge_dir()))
|
|
|
|
return error_errno(_("could not create temporary %s"), merge_dir());
|
2019-04-17 14:30:37 +00:00
|
|
|
|
|
|
|
delete_reflog("REBASE_HEAD");
|
|
|
|
|
|
|
|
interactive = fopen(path_interactive(), "w");
|
|
|
|
if (!interactive)
|
|
|
|
return error_errno(_("could not mark as interactive"));
|
|
|
|
fclose(interactive);
|
|
|
|
|
|
|
|
return write_basic_state(opts, head_name, onto, orig_head);
|
|
|
|
}
|
|
|
|
|
2019-04-17 14:30:42 +00:00
|
|
|
static int do_interactive_rebase(struct rebase_options *opts, unsigned flags)
|
2019-04-17 14:30:37 +00:00
|
|
|
{
|
2023-02-06 19:08:06 +00:00
|
|
|
int ret = -1;
|
2019-04-17 14:30:37 +00:00
|
|
|
char *revisions = NULL, *shortrevisions = NULL;
|
2020-07-28 20:24:27 +00:00
|
|
|
struct strvec make_script_args = STRVEC_INIT;
|
2019-04-17 14:30:37 +00:00
|
|
|
struct todo_list todo_list = TODO_LIST_INIT;
|
2019-04-17 14:30:42 +00:00
|
|
|
struct replay_opts replay = get_replay_opts(opts);
|
2019-04-17 14:30:37 +00:00
|
|
|
|
2022-10-17 13:17:41 +00:00
|
|
|
if (get_revision_ranges(opts->upstream, opts->onto, &opts->orig_head->object.oid,
|
2020-11-04 15:29:40 +00:00
|
|
|
&revisions, &shortrevisions))
|
2023-02-06 19:08:06 +00:00
|
|
|
goto cleanup;
|
2019-04-17 14:30:37 +00:00
|
|
|
|
2019-04-17 14:30:44 +00:00
|
|
|
if (init_basic_state(&replay,
|
|
|
|
opts->head_name ? opts->head_name : "detached HEAD",
|
2023-02-06 19:08:06 +00:00
|
|
|
opts->onto, &opts->orig_head->object.oid))
|
|
|
|
goto cleanup;
|
2019-04-17 14:30:37 +00:00
|
|
|
|
2019-04-17 14:30:42 +00:00
|
|
|
if (!opts->upstream && opts->squash_onto)
|
2019-04-17 14:30:40 +00:00
|
|
|
write_file(path_squash_onto(), "%s\n",
|
2019-04-17 14:30:42 +00:00
|
|
|
oid_to_hex(opts->squash_onto));
|
2019-04-17 14:30:37 +00:00
|
|
|
|
2020-07-28 20:24:27 +00:00
|
|
|
strvec_pushl(&make_script_args, "", revisions, NULL);
|
2019-04-17 14:30:42 +00:00
|
|
|
if (opts->restrict_revision)
|
2020-07-28 20:24:27 +00:00
|
|
|
strvec_pushf(&make_script_args, "^%s",
|
strvec: fix indentation in renamed calls
Code which split an argv_array call across multiple lines, like:
argv_array_pushl(&args, "one argument",
"another argument", "and more",
NULL);
was recently mechanically renamed to use strvec, which results in
mis-matched indentation like:
strvec_pushl(&args, "one argument",
"another argument", "and more",
NULL);
Let's fix these up to align the arguments with the opening paren. I did
this manually by sifting through the results of:
git jump grep 'strvec_.*,$'
and liberally applying my editor's auto-format. Most of the changes are
of the form shown above, though I also normalized a few that had
originally used a single-tab indentation (rather than our usual style of
aligning with the open paren). I also rewrapped a couple of obvious
cases (e.g., where previously too-long lines became short enough to fit
on one), but I wasn't aggressive about it. In cases broken to three or
more lines, the grouping of arguments is sometimes meaningful, and it
wasn't worth my time or reviewer time to ponder each case individually.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-07-28 20:26:31 +00:00
|
|
|
oid_to_hex(&opts->restrict_revision->object.oid));
|
2019-04-17 14:30:37 +00:00
|
|
|
|
|
|
|
ret = sequencer_make_script(the_repository, &todo_list.buf,
|
2020-07-29 00:37:20 +00:00
|
|
|
make_script_args.nr, make_script_args.v,
|
2019-04-17 14:30:37 +00:00
|
|
|
flags);
|
|
|
|
|
|
|
|
if (ret)
|
|
|
|
error(_("could not generate todo list"));
|
|
|
|
else {
|
2022-11-19 13:07:38 +00:00
|
|
|
discard_index(&the_index);
|
2019-04-17 14:30:37 +00:00
|
|
|
if (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf,
|
|
|
|
&todo_list))
|
|
|
|
BUG("unusable todo list");
|
|
|
|
|
2019-04-17 14:30:42 +00:00
|
|
|
ret = complete_action(the_repository, &replay, flags,
|
2020-11-04 15:29:38 +00:00
|
|
|
shortrevisions, opts->onto_name, opts->onto,
|
2023-01-12 16:50:01 +00:00
|
|
|
&opts->orig_head->object.oid, &opts->exec,
|
2022-10-31 01:04:42 +00:00
|
|
|
opts->autosquash, opts->update_refs, &todo_list);
|
2019-04-17 14:30:37 +00:00
|
|
|
}
|
|
|
|
|
2023-02-06 19:08:06 +00:00
|
|
|
cleanup:
|
2023-02-06 19:08:08 +00:00
|
|
|
replay_opts_release(&replay);
|
2019-04-17 14:30:37 +00:00
|
|
|
free(revisions);
|
|
|
|
free(shortrevisions);
|
|
|
|
todo_list_release(&todo_list);
|
2020-07-28 20:24:27 +00:00
|
|
|
strvec_clear(&make_script_args);
|
2019-04-17 14:30:37 +00:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2022-10-12 09:35:12 +00:00
|
|
|
static int run_sequencer_rebase(struct rebase_options *opts)
|
2019-04-17 14:30:44 +00:00
|
|
|
{
|
|
|
|
unsigned flags = 0;
|
|
|
|
int abbreviate_commands = 0, ret = 0;
|
|
|
|
|
|
|
|
git_config_get_bool("rebase.abbreviatecommands", &abbreviate_commands);
|
|
|
|
|
rebase: reinstate --no-keep-empty
Commit d48e5e21da ("rebase (interactive-backend): make --keep-empty the
default", 2020-02-15) turned --keep-empty (for keeping commits which
start empty) into the default. The logic underpinning that commit was:
1) 'git commit' errors out on the creation of empty commits without an
override flag
2) Once someone determines that the override is worthwhile, it's
annoying and/or harmful to required them to take extra steps in
order to keep such commits around (and to repeat such steps with
every rebase).
While the logic on which the decision was made is sound, the result was
a bit of an overcorrection. Instead of jumping to having --keep-empty
being the default, it jumped to making --keep-empty the only available
behavior. There was a simple workaround, though, which was thought to
be good enough at the time. People could still drop commits which
started empty the same way the could drop any commits: by firing up an
interactive rebase and picking out the commits they didn't want from the
list. However, there are cases where external tools might create enough
empty commits that picking all of them out is painful. As such, having
a flag to automatically remove start-empty commits may be beneficial.
Provide users a way to drop commits which start empty using a flag that
existed for years: --no-keep-empty. Interpret --keep-empty as
countermanding any previous --no-keep-empty, but otherwise leaving
--keep-empty as the default.
This might lead to some slight weirdness since commands like
git rebase --empty=drop --keep-empty
git rebase --empty=keep --no-keep-empty
look really weird despite making perfect sense (the first will drop
commits which become empty, but keep commits that started empty; the
second will keep commits which become empty, but drop commits which
started empty). However, --no-keep-empty was named years ago and we are
predominantly keeping it for backward compatibility; also we suspect it
will only be used rarely since folks already have a simple way to drop
commits they don't want with an interactive rebase.
Reported-by: Bryan Turner <bturner@atlassian.com>
Reported-by: Sami Boukortt <sami@boukortt.com>
Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-04-11 02:44:25 +00:00
|
|
|
flags |= opts->keep_empty ? TODO_LIST_KEEP_EMPTY : 0;
|
2019-04-17 14:30:44 +00:00
|
|
|
flags |= abbreviate_commands ? TODO_LIST_ABBREVIATE_CMDS : 0;
|
|
|
|
flags |= opts->rebase_merges ? TODO_LIST_REBASE_MERGES : 0;
|
|
|
|
flags |= opts->rebase_cousins > 0 ? TODO_LIST_REBASE_COUSINS : 0;
|
2019-07-31 15:18:49 +00:00
|
|
|
flags |= opts->root_with_onto ? TODO_LIST_ROOT_WITH_ONTO : 0;
|
2020-04-11 02:44:27 +00:00
|
|
|
flags |= opts->reapply_cherry_picks ? TODO_LIST_REAPPLY_CHERRY_PICKS : 0;
|
2021-08-30 21:46:02 +00:00
|
|
|
flags |= opts->flags & REBASE_NO_QUIET ? TODO_LIST_WARN_SKIPPED_CHERRY_PICKS : 0;
|
2019-04-17 14:30:44 +00:00
|
|
|
|
2022-10-12 09:35:12 +00:00
|
|
|
switch (opts->action) {
|
2019-04-17 14:30:44 +00:00
|
|
|
case ACTION_NONE: {
|
|
|
|
if (!opts->onto && !opts->upstream)
|
|
|
|
die(_("a base commit must be provided with --upstream or --onto"));
|
|
|
|
|
|
|
|
ret = do_interactive_rebase(opts, flags);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case ACTION_SKIP: {
|
|
|
|
struct string_list merge_rr = STRING_LIST_INIT_DUP;
|
|
|
|
|
|
|
|
rerere_clear(the_repository, &merge_rr);
|
|
|
|
}
|
|
|
|
/* fallthrough */
|
|
|
|
case ACTION_CONTINUE: {
|
|
|
|
struct replay_opts replay_opts = get_replay_opts(opts);
|
|
|
|
|
|
|
|
ret = sequencer_continue(the_repository, &replay_opts);
|
2023-02-06 19:08:08 +00:00
|
|
|
replay_opts_release(&replay_opts);
|
2019-04-17 14:30:44 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case ACTION_EDIT_TODO:
|
|
|
|
ret = edit_todo_file(flags);
|
|
|
|
break;
|
|
|
|
case ACTION_SHOW_CURRENT_PATCH: {
|
|
|
|
struct child_process cmd = CHILD_PROCESS_INIT;
|
|
|
|
|
|
|
|
cmd.git_cmd = 1;
|
2020-07-28 20:24:27 +00:00
|
|
|
strvec_pushl(&cmd.args, "show", "REBASE_HEAD", "--", NULL);
|
2019-04-17 14:30:44 +00:00
|
|
|
ret = run_command(&cmd);
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
2022-10-12 09:35:12 +00:00
|
|
|
BUG("invalid command '%d'", opts->action);
|
2019-04-17 14:30:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
rebase: reinstate --no-keep-empty
Commit d48e5e21da ("rebase (interactive-backend): make --keep-empty the
default", 2020-02-15) turned --keep-empty (for keeping commits which
start empty) into the default. The logic underpinning that commit was:
1) 'git commit' errors out on the creation of empty commits without an
override flag
2) Once someone determines that the override is worthwhile, it's
annoying and/or harmful to required them to take extra steps in
order to keep such commits around (and to repeat such steps with
every rebase).
While the logic on which the decision was made is sound, the result was
a bit of an overcorrection. Instead of jumping to having --keep-empty
being the default, it jumped to making --keep-empty the only available
behavior. There was a simple workaround, though, which was thought to
be good enough at the time. People could still drop commits which
started empty the same way the could drop any commits: by firing up an
interactive rebase and picking out the commits they didn't want from the
list. However, there are cases where external tools might create enough
empty commits that picking all of them out is painful. As such, having
a flag to automatically remove start-empty commits may be beneficial.
Provide users a way to drop commits which start empty using a flag that
existed for years: --no-keep-empty. Interpret --keep-empty as
countermanding any previous --no-keep-empty, but otherwise leaving
--keep-empty as the default.
This might lead to some slight weirdness since commands like
git rebase --empty=drop --keep-empty
git rebase --empty=keep --no-keep-empty
look really weird despite making perfect sense (the first will drop
commits which become empty, but keep commits that started empty; the
second will keep commits which become empty, but drop commits which
started empty). However, --no-keep-empty was named years ago and we are
predominantly keeping it for backward compatibility; also we suspect it
will only be used rarely since folks already have a simple way to drop
commits they don't want with an interactive rebase.
Reported-by: Bryan Turner <bturner@atlassian.com>
Reported-by: Sami Boukortt <sami@boukortt.com>
Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-04-11 02:44:25 +00:00
|
|
|
static void imply_merge(struct rebase_options *opts, const char *option);
|
rebase (interactive-backend): make --keep-empty the default
Different rebase backends have different treatment for commits which
start empty (i.e. have no changes relative to their parent), and the
--keep-empty option was added at some point to allow adjusting behavior.
The handling of commits which start empty is actually quite similar to
commit b00bf1c9a8dd (git-rebase: make --allow-empty-message the default,
2018-06-27), which pointed out that the behavior for various backends is
often more happenstance than design. The specific change made in that
commit is actually quite relevant as well and much of the logic there
directly applies here.
It makes a lot of sense in 'git commit' to error out on the creation of
empty commits, unless an override flag is provided. However, once
someone determines that there is a rare case that merits using the
manual override to create such a commit, it is somewhere between
annoying and harmful to have to take extra steps to keep such
intentional commits around. Granted, empty commits are quite rare,
which is why handling of them doesn't get considered much and folks tend
to defer to existing (accidental) behavior and assume there was a reason
for it, leading them to just add flags (--keep-empty in this case) that
allow them to override the bad defaults. Fix the interactive backend so
that --keep-empty is the default, much like we did with
--allow-empty-message. The am backend should also be fixed to have
--keep-empty semantics for commits that start empty, but that is not
included in this patch other than a testcase documenting the failure.
Note that there was one test in t3421 which appears to have been written
expecting --keep-empty to not be the default as correct behavior. This
test was introduced in commit 00b8be5a4d38 ("add tests for rebasing of
empty commits", 2013-06-06), which was part of a series focusing on
rebase topology and which had an interesting original cover letter at
https://lore.kernel.org/git/1347949878-12578-1-git-send-email-martinvonz@gmail.com/
which noted
Your input especially appreciated on whether you agree with the
intent of the test cases.
and then went into a long example about how one of the many tests added
had several questions about whether it was correct. As such, I believe
most the tests in that series were about testing rebase topology with as
many different flags as possible and were not trying to state in general
how those flags should behave otherwise.
Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-02-15 21:36:24 +00:00
|
|
|
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);
|
|
|
|
|
rebase: reinstate --no-keep-empty
Commit d48e5e21da ("rebase (interactive-backend): make --keep-empty the
default", 2020-02-15) turned --keep-empty (for keeping commits which
start empty) into the default. The logic underpinning that commit was:
1) 'git commit' errors out on the creation of empty commits without an
override flag
2) Once someone determines that the override is worthwhile, it's
annoying and/or harmful to required them to take extra steps in
order to keep such commits around (and to repeat such steps with
every rebase).
While the logic on which the decision was made is sound, the result was
a bit of an overcorrection. Instead of jumping to having --keep-empty
being the default, it jumped to making --keep-empty the only available
behavior. There was a simple workaround, though, which was thought to
be good enough at the time. People could still drop commits which
started empty the same way the could drop any commits: by firing up an
interactive rebase and picking out the commits they didn't want from the
list. However, there are cases where external tools might create enough
empty commits that picking all of them out is painful. As such, having
a flag to automatically remove start-empty commits may be beneficial.
Provide users a way to drop commits which start empty using a flag that
existed for years: --no-keep-empty. Interpret --keep-empty as
countermanding any previous --no-keep-empty, but otherwise leaving
--keep-empty as the default.
This might lead to some slight weirdness since commands like
git rebase --empty=drop --keep-empty
git rebase --empty=keep --no-keep-empty
look really weird despite making perfect sense (the first will drop
commits which become empty, but keep commits that started empty; the
second will keep commits which become empty, but drop commits which
started empty). However, --no-keep-empty was named years ago and we are
predominantly keeping it for backward compatibility; also we suspect it
will only be used rarely since folks already have a simple way to drop
commits they don't want with an interactive rebase.
Reported-by: Bryan Turner <bturner@atlassian.com>
Reported-by: Sami Boukortt <sami@boukortt.com>
Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-04-11 02:44:25 +00:00
|
|
|
imply_merge(opts, unset ? "--no-keep-empty" : "--keep-empty");
|
|
|
|
opts->keep_empty = !unset;
|
2020-02-15 21:36:41 +00:00
|
|
|
opts->type = REBASE_MERGE;
|
rebase (interactive-backend): make --keep-empty the default
Different rebase backends have different treatment for commits which
start empty (i.e. have no changes relative to their parent), and the
--keep-empty option was added at some point to allow adjusting behavior.
The handling of commits which start empty is actually quite similar to
commit b00bf1c9a8dd (git-rebase: make --allow-empty-message the default,
2018-06-27), which pointed out that the behavior for various backends is
often more happenstance than design. The specific change made in that
commit is actually quite relevant as well and much of the logic there
directly applies here.
It makes a lot of sense in 'git commit' to error out on the creation of
empty commits, unless an override flag is provided. However, once
someone determines that there is a rare case that merits using the
manual override to create such a commit, it is somewhere between
annoying and harmful to have to take extra steps to keep such
intentional commits around. Granted, empty commits are quite rare,
which is why handling of them doesn't get considered much and folks tend
to defer to existing (accidental) behavior and assume there was a reason
for it, leading them to just add flags (--keep-empty in this case) that
allow them to override the bad defaults. Fix the interactive backend so
that --keep-empty is the default, much like we did with
--allow-empty-message. The am backend should also be fixed to have
--keep-empty semantics for commits that start empty, but that is not
included in this patch other than a testcase documenting the failure.
Note that there was one test in t3421 which appears to have been written
expecting --keep-empty to not be the default as correct behavior. This
test was introduced in commit 00b8be5a4d38 ("add tests for rebasing of
empty commits", 2013-06-06), which was part of a series focusing on
rebase topology and which had an interesting original cover letter at
https://lore.kernel.org/git/1347949878-12578-1-git-send-email-martinvonz@gmail.com/
which noted
Your input especially appreciated on whether you agree with the
intent of the test cases.
and then went into a long example about how one of the many tests added
had several questions about whether it was correct. As such, I believe
most the tests in that series were about testing rebase topology with as
many different flags as possible and were not trying to state in general
how those flags should behave otherwise.
Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-02-15 21:36:24 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-02-15 21:36:41 +00:00
|
|
|
static int is_merge(struct rebase_options *opts)
|
2018-09-04 21:27:16 +00:00
|
|
|
{
|
2021-09-07 21:05:06 +00:00
|
|
|
return opts->type == REBASE_MERGE;
|
2018-09-04 21:27:16 +00:00
|
|
|
}
|
|
|
|
|
2020-02-15 21:36:41 +00:00
|
|
|
static void imply_merge(struct rebase_options *opts, const char *option)
|
2018-09-04 21:59:57 +00:00
|
|
|
{
|
|
|
|
switch (opts->type) {
|
2020-02-15 21:36:41 +00:00
|
|
|
case REBASE_APPLY:
|
2020-04-11 02:44:26 +00:00
|
|
|
die(_("%s requires the merge backend"), option);
|
2018-09-04 21:59:57 +00:00
|
|
|
break;
|
2020-02-15 21:36:41 +00:00
|
|
|
case REBASE_MERGE:
|
2018-09-04 21:59:57 +00:00
|
|
|
break;
|
|
|
|
default:
|
2020-02-15 21:36:41 +00:00
|
|
|
opts->type = REBASE_MERGE; /* implied */
|
2018-09-04 21:59:57 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-06 19:31:11 +00:00
|
|
|
/* Returns the filename prefixed by the state_dir */
|
|
|
|
static const char *state_dir_path(const char *filename, struct rebase_options *opts)
|
|
|
|
{
|
|
|
|
static struct strbuf path = STRBUF_INIT;
|
|
|
|
static size_t prefix_len;
|
|
|
|
|
|
|
|
if (!prefix_len) {
|
|
|
|
strbuf_addf(&path, "%s/", opts->state_dir);
|
|
|
|
prefix_len = path.len;
|
|
|
|
}
|
|
|
|
|
|
|
|
strbuf_setlen(&path, prefix_len);
|
|
|
|
strbuf_addstr(&path, filename);
|
|
|
|
return path.buf;
|
|
|
|
}
|
|
|
|
|
2018-08-08 15:06:16 +00:00
|
|
|
/* Initialize the rebase options from the state directory. */
|
|
|
|
static int read_basic_state(struct rebase_options *opts)
|
|
|
|
{
|
|
|
|
struct strbuf head_name = STRBUF_INIT;
|
|
|
|
struct strbuf buf = STRBUF_INIT;
|
|
|
|
struct object_id oid;
|
|
|
|
|
2020-04-07 14:27:55 +00:00
|
|
|
if (!read_oneliner(&head_name, state_dir_path("head-name", opts),
|
|
|
|
READ_ONELINER_WARN_MISSING) ||
|
|
|
|
!read_oneliner(&buf, state_dir_path("onto", opts),
|
|
|
|
READ_ONELINER_WARN_MISSING))
|
2018-08-08 15:06:16 +00:00
|
|
|
return -1;
|
|
|
|
opts->head_name = starts_with(head_name.buf, "refs/") ?
|
|
|
|
xstrdup(head_name.buf) : NULL;
|
|
|
|
strbuf_release(&head_name);
|
2022-10-17 13:17:40 +00:00
|
|
|
if (get_oid_hex(buf.buf, &oid) ||
|
|
|
|
!(opts->onto = lookup_commit_object(the_repository, &oid)))
|
|
|
|
return error(_("invalid onto: '%s'"), buf.buf);
|
2018-08-08 15:06:16 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* We always write to orig-head, but interactive rebase used to write to
|
|
|
|
* head. Fall back to reading from head to cover for the case that the
|
|
|
|
* user upgraded git with an ongoing interactive rebase.
|
|
|
|
*/
|
|
|
|
strbuf_reset(&buf);
|
|
|
|
if (file_exists(state_dir_path("orig-head", opts))) {
|
2020-04-07 14:27:55 +00:00
|
|
|
if (!read_oneliner(&buf, state_dir_path("orig-head", opts),
|
|
|
|
READ_ONELINER_WARN_MISSING))
|
2018-08-08 15:06:16 +00:00
|
|
|
return -1;
|
2020-04-07 14:27:55 +00:00
|
|
|
} else if (!read_oneliner(&buf, state_dir_path("head", opts),
|
|
|
|
READ_ONELINER_WARN_MISSING))
|
2018-08-08 15:06:16 +00:00
|
|
|
return -1;
|
2022-10-17 13:17:41 +00:00
|
|
|
if (get_oid_hex(buf.buf, &oid) ||
|
|
|
|
!(opts->orig_head = lookup_commit_object(the_repository, &oid)))
|
2018-08-08 15:06:16 +00:00
|
|
|
return error(_("invalid orig-head: '%s'"), buf.buf);
|
|
|
|
|
git-rebase, sequencer: extend --quiet option for the interactive machinery
While 'quiet' and 'interactive' may sound like antonyms, the interactive
machinery actually has logic that implements several
interactive_rebase=implied cases (--exec, --keep-empty, --rebase-merges)
which won't pop up an editor. The rewrite of interactive rebase in C
added a quiet option, though it only turns stats off. Since we want to
make the interactive machinery also take over for git-rebase--merge, it
should fully implement the --quiet option.
git-rebase--interactive was already somewhat quieter than
git-rebase--merge and git-rebase--am, possibly because cherry-pick has
just traditionally been quieter. As such, we only drop a few
informational messages -- "Rebasing (n/m)" and "Successfully rebased..."
Also, for simplicity, remove the differences in how quiet and verbose
options were recorded. Having one be signalled by the presence of a
"verbose" file in the state_dir, while the other was signalled by the
contents of a "quiet" file was just weirdly inconsistent. (This
inconsistency pre-dated the rewrite into C.) Make them consistent by
having them both key off the presence of the file.
Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-12-11 16:11:36 +00:00
|
|
|
if (file_exists(state_dir_path("quiet", opts)))
|
2018-08-08 15:06:16 +00:00
|
|
|
opts->flags &= ~REBASE_NO_QUIET;
|
|
|
|
else
|
|
|
|
opts->flags |= REBASE_NO_QUIET;
|
|
|
|
|
|
|
|
if (file_exists(state_dir_path("verbose", opts)))
|
|
|
|
opts->flags |= REBASE_VERBOSE;
|
|
|
|
|
2018-09-04 21:59:50 +00:00
|
|
|
if (file_exists(state_dir_path("signoff", opts))) {
|
|
|
|
opts->signoff = 1;
|
|
|
|
opts->flags |= REBASE_FORCE;
|
|
|
|
}
|
|
|
|
|
2018-09-04 21:59:52 +00:00
|
|
|
if (file_exists(state_dir_path("allow_rerere_autoupdate", opts))) {
|
|
|
|
strbuf_reset(&buf);
|
2020-04-07 14:27:55 +00:00
|
|
|
if (!read_oneliner(&buf, state_dir_path("allow_rerere_autoupdate", opts),
|
|
|
|
READ_ONELINER_WARN_MISSING))
|
2018-09-04 21:59:52 +00:00
|
|
|
return -1;
|
|
|
|
if (!strcmp(buf.buf, "--rerere-autoupdate"))
|
2019-04-17 14:30:36 +00:00
|
|
|
opts->allow_rerere_autoupdate = RERERE_AUTOUPDATE;
|
2018-09-04 21:59:52 +00:00
|
|
|
else if (!strcmp(buf.buf, "--no-rerere-autoupdate"))
|
2019-04-17 14:30:36 +00:00
|
|
|
opts->allow_rerere_autoupdate = RERERE_NOAUTOUPDATE;
|
2018-09-04 21:59:52 +00:00
|
|
|
else
|
|
|
|
warning(_("ignoring invalid allow_rerere_autoupdate: "
|
|
|
|
"'%s'"), buf.buf);
|
2019-04-17 14:30:36 +00:00
|
|
|
}
|
2018-09-04 21:59:52 +00:00
|
|
|
|
2018-09-04 22:00:00 +00:00
|
|
|
if (file_exists(state_dir_path("gpg_sign_opt", opts))) {
|
|
|
|
strbuf_reset(&buf);
|
2020-04-07 14:27:55 +00:00
|
|
|
if (!read_oneliner(&buf, state_dir_path("gpg_sign_opt", opts),
|
|
|
|
READ_ONELINER_WARN_MISSING))
|
2018-09-04 22:00:00 +00:00
|
|
|
return -1;
|
|
|
|
free(opts->gpg_sign_opt);
|
|
|
|
opts->gpg_sign_opt = xstrdup(buf.buf);
|
|
|
|
}
|
|
|
|
|
2018-08-08 15:06:16 +00:00
|
|
|
strbuf_release(&buf);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-04-17 14:30:35 +00:00
|
|
|
static int rebase_write_basic_state(struct rebase_options *opts)
|
2019-01-18 15:09:27 +00:00
|
|
|
{
|
|
|
|
write_file(state_dir_path("head-name", opts), "%s",
|
|
|
|
opts->head_name ? opts->head_name : "detached HEAD");
|
|
|
|
write_file(state_dir_path("onto", opts), "%s",
|
|
|
|
opts->onto ? oid_to_hex(&opts->onto->object.oid) : "");
|
|
|
|
write_file(state_dir_path("orig-head", opts), "%s",
|
2022-10-17 13:17:41 +00:00
|
|
|
oid_to_hex(&opts->orig_head->object.oid));
|
2020-02-15 21:36:27 +00:00
|
|
|
if (!(opts->flags & REBASE_NO_QUIET))
|
|
|
|
write_file(state_dir_path("quiet", opts), "%s", "");
|
2019-01-18 15:09:27 +00:00
|
|
|
if (opts->flags & REBASE_VERBOSE)
|
|
|
|
write_file(state_dir_path("verbose", opts), "%s", "");
|
2019-04-17 14:30:36 +00:00
|
|
|
if (opts->allow_rerere_autoupdate > 0)
|
2019-01-18 15:09:27 +00:00
|
|
|
write_file(state_dir_path("allow_rerere_autoupdate", opts),
|
|
|
|
"-%s-rerere-autoupdate",
|
2019-04-17 14:30:36 +00:00
|
|
|
opts->allow_rerere_autoupdate == RERERE_AUTOUPDATE ?
|
|
|
|
"" : "-no");
|
2019-01-18 15:09:27 +00:00
|
|
|
if (opts->gpg_sign_opt)
|
|
|
|
write_file(state_dir_path("gpg_sign_opt", opts), "%s",
|
|
|
|
opts->gpg_sign_opt);
|
|
|
|
if (opts->signoff)
|
2019-12-20 18:53:56 +00:00
|
|
|
write_file(state_dir_path("signoff", opts), "--signoff");
|
2019-01-18 15:09:27 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-08-06 19:31:11 +00:00
|
|
|
static int finish_rebase(struct rebase_options *opts)
|
|
|
|
{
|
|
|
|
struct strbuf dir = STRBUF_INIT;
|
2019-05-14 18:03:47 +00:00
|
|
|
int ret = 0;
|
2018-08-06 19:31:11 +00:00
|
|
|
|
|
|
|
delete_ref(NULL, "REBASE_HEAD", NULL, REF_NO_DEREF);
|
2021-03-20 00:03:52 +00:00
|
|
|
unlink(git_path_auto_merge(the_repository));
|
2020-04-07 14:27:58 +00:00
|
|
|
apply_autostash(state_dir_path("autostash", opts));
|
2018-08-06 19:31:11 +00:00
|
|
|
/*
|
2020-09-17 18:11:44 +00:00
|
|
|
* We ignore errors in 'git maintenance run --auto', since the
|
2018-08-06 19:31:11 +00:00
|
|
|
* user should see them.
|
|
|
|
*/
|
2020-09-17 18:11:44 +00:00
|
|
|
run_auto_maintenance(!(opts->flags & (REBASE_NO_QUIET|REBASE_VERBOSE)));
|
2020-02-15 21:36:41 +00:00
|
|
|
if (opts->type == REBASE_MERGE) {
|
2019-05-14 18:03:49 +00:00
|
|
|
struct replay_opts replay = REPLAY_OPTS_INIT;
|
2018-08-06 19:31:11 +00:00
|
|
|
|
2019-05-14 18:03:49 +00:00
|
|
|
replay.action = REPLAY_INTERACTIVE_REBASE;
|
|
|
|
ret = sequencer_remove_state(&replay);
|
2023-02-06 19:08:08 +00:00
|
|
|
replay_opts_release(&replay);
|
2019-05-14 18:03:49 +00:00
|
|
|
} else {
|
|
|
|
strbuf_addstr(&dir, opts->state_dir);
|
|
|
|
if (remove_dir_recursively(&dir, 0))
|
|
|
|
ret = error(_("could not remove '%s'"),
|
|
|
|
opts->state_dir);
|
|
|
|
strbuf_release(&dir);
|
|
|
|
}
|
2018-08-06 19:31:11 +00:00
|
|
|
|
2019-05-14 18:03:47 +00:00
|
|
|
return ret;
|
2018-08-06 19:31:11 +00:00
|
|
|
}
|
|
|
|
|
2019-01-18 15:09:27 +00:00
|
|
|
static int move_to_original_branch(struct rebase_options *opts)
|
|
|
|
{
|
2022-01-26 13:05:47 +00:00
|
|
|
struct strbuf branch_reflog = STRBUF_INIT, head_reflog = STRBUF_INIT;
|
2022-01-26 13:05:46 +00:00
|
|
|
struct reset_head_opts ropts = { 0 };
|
2019-01-18 15:09:27 +00:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (!opts->head_name)
|
|
|
|
return 0; /* nothing to move back to */
|
|
|
|
|
|
|
|
if (!opts->onto)
|
|
|
|
BUG("move_to_original_branch without onto");
|
|
|
|
|
2022-10-12 09:35:10 +00:00
|
|
|
strbuf_addf(&branch_reflog, "%s (finish): %s onto %s",
|
2022-11-09 14:21:58 +00:00
|
|
|
opts->reflog_action,
|
2019-01-18 15:09:27 +00:00
|
|
|
opts->head_name, oid_to_hex(&opts->onto->object.oid));
|
2022-10-12 09:35:10 +00:00
|
|
|
strbuf_addf(&head_reflog, "%s (finish): returning to %s",
|
2022-11-09 14:21:58 +00:00
|
|
|
opts->reflog_action, opts->head_name);
|
2022-01-26 13:05:46 +00:00
|
|
|
ropts.branch = opts->head_name;
|
|
|
|
ropts.flags = RESET_HEAD_REFS_ONLY;
|
2022-01-26 13:05:47 +00:00
|
|
|
ropts.branch_msg = branch_reflog.buf;
|
2022-01-26 13:05:46 +00:00
|
|
|
ropts.head_msg = head_reflog.buf;
|
|
|
|
ret = reset_head(the_repository, &ropts);
|
2019-01-18 15:09:27 +00:00
|
|
|
|
2022-01-26 13:05:47 +00:00
|
|
|
strbuf_release(&branch_reflog);
|
2019-01-18 15:09:27 +00:00
|
|
|
strbuf_release(&head_reflog);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2018-10-05 15:54:38 +00:00
|
|
|
static const char *resolvemsg =
|
|
|
|
N_("Resolve all conflicts manually, mark them as resolved with\n"
|
|
|
|
"\"git add/rm <conflicted_files>\", then run \"git rebase --continue\".\n"
|
|
|
|
"You can instead skip this commit: run \"git rebase --skip\".\n"
|
|
|
|
"To abort and get back to the state before \"git rebase\", run "
|
|
|
|
"\"git rebase --abort\".");
|
|
|
|
|
2019-01-18 15:09:27 +00:00
|
|
|
static int run_am(struct rebase_options *opts)
|
|
|
|
{
|
|
|
|
struct child_process am = CHILD_PROCESS_INIT;
|
|
|
|
struct child_process format_patch = CHILD_PROCESS_INIT;
|
|
|
|
struct strbuf revisions = STRBUF_INIT;
|
|
|
|
int status;
|
|
|
|
char *rebased_patches;
|
|
|
|
|
|
|
|
am.git_cmd = 1;
|
2020-07-28 20:24:27 +00:00
|
|
|
strvec_push(&am.args, "am");
|
2022-10-12 09:35:10 +00:00
|
|
|
strvec_pushf(&am.env, GIT_REFLOG_ACTION_ENVIRONMENT "=%s (pick)",
|
2022-11-09 14:21:58 +00:00
|
|
|
opts->reflog_action);
|
2022-10-12 09:35:12 +00:00
|
|
|
if (opts->action == ACTION_CONTINUE) {
|
2020-07-28 20:24:27 +00:00
|
|
|
strvec_push(&am.args, "--resolved");
|
|
|
|
strvec_pushf(&am.args, "--resolvemsg=%s", resolvemsg);
|
2019-01-18 15:09:27 +00:00
|
|
|
if (opts->gpg_sign_opt)
|
2020-07-28 20:24:27 +00:00
|
|
|
strvec_push(&am.args, opts->gpg_sign_opt);
|
2019-01-18 15:09:27 +00:00
|
|
|
status = run_command(&am);
|
|
|
|
if (status)
|
|
|
|
return status;
|
|
|
|
|
|
|
|
return move_to_original_branch(opts);
|
|
|
|
}
|
2022-10-12 09:35:12 +00:00
|
|
|
if (opts->action == ACTION_SKIP) {
|
2020-07-28 20:24:27 +00:00
|
|
|
strvec_push(&am.args, "--skip");
|
|
|
|
strvec_pushf(&am.args, "--resolvemsg=%s", resolvemsg);
|
2019-01-18 15:09:27 +00:00
|
|
|
status = run_command(&am);
|
|
|
|
if (status)
|
|
|
|
return status;
|
|
|
|
|
|
|
|
return move_to_original_branch(opts);
|
|
|
|
}
|
2022-10-12 09:35:12 +00:00
|
|
|
if (opts->action == ACTION_SHOW_CURRENT_PATCH) {
|
2020-07-28 20:24:27 +00:00
|
|
|
strvec_push(&am.args, "--show-current-patch");
|
2019-01-18 15:09:27 +00:00
|
|
|
return run_command(&am);
|
|
|
|
}
|
|
|
|
|
|
|
|
strbuf_addf(&revisions, "%s...%s",
|
|
|
|
oid_to_hex(opts->root ?
|
|
|
|
/* this is now equivalent to !opts->upstream */
|
|
|
|
&opts->onto->object.oid :
|
|
|
|
&opts->upstream->object.oid),
|
2022-10-17 13:17:41 +00:00
|
|
|
oid_to_hex(&opts->orig_head->object.oid));
|
2019-01-18 15:09:27 +00:00
|
|
|
|
|
|
|
rebased_patches = xstrdup(git_path("rebased-patches"));
|
|
|
|
format_patch.out = open(rebased_patches,
|
|
|
|
O_WRONLY | O_CREAT | O_TRUNC, 0666);
|
|
|
|
if (format_patch.out < 0) {
|
|
|
|
status = error_errno(_("could not open '%s' for writing"),
|
|
|
|
rebased_patches);
|
|
|
|
free(rebased_patches);
|
2020-07-28 20:24:27 +00:00
|
|
|
strvec_clear(&am.args);
|
2019-01-18 15:09:27 +00:00
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
format_patch.git_cmd = 1;
|
2020-07-28 20:24:27 +00:00
|
|
|
strvec_pushl(&format_patch.args, "format-patch", "-k", "--stdout",
|
strvec: fix indentation in renamed calls
Code which split an argv_array call across multiple lines, like:
argv_array_pushl(&args, "one argument",
"another argument", "and more",
NULL);
was recently mechanically renamed to use strvec, which results in
mis-matched indentation like:
strvec_pushl(&args, "one argument",
"another argument", "and more",
NULL);
Let's fix these up to align the arguments with the opening paren. I did
this manually by sifting through the results of:
git jump grep 'strvec_.*,$'
and liberally applying my editor's auto-format. Most of the changes are
of the form shown above, though I also normalized a few that had
originally used a single-tab indentation (rather than our usual style of
aligning with the open paren). I also rewrapped a couple of obvious
cases (e.g., where previously too-long lines became short enough to fit
on one), but I wasn't aggressive about it. In cases broken to three or
more lines, the grouping of arguments is sometimes meaningful, and it
wasn't worth my time or reviewer time to ponder each case individually.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-07-28 20:26:31 +00:00
|
|
|
"--full-index", "--cherry-pick", "--right-only",
|
2023-03-13 19:54:11 +00:00
|
|
|
"--default-prefix", "--no-renames",
|
strvec: fix indentation in renamed calls
Code which split an argv_array call across multiple lines, like:
argv_array_pushl(&args, "one argument",
"another argument", "and more",
NULL);
was recently mechanically renamed to use strvec, which results in
mis-matched indentation like:
strvec_pushl(&args, "one argument",
"another argument", "and more",
NULL);
Let's fix these up to align the arguments with the opening paren. I did
this manually by sifting through the results of:
git jump grep 'strvec_.*,$'
and liberally applying my editor's auto-format. Most of the changes are
of the form shown above, though I also normalized a few that had
originally used a single-tab indentation (rather than our usual style of
aligning with the open paren). I also rewrapped a couple of obvious
cases (e.g., where previously too-long lines became short enough to fit
on one), but I wasn't aggressive about it. In cases broken to three or
more lines, the grouping of arguments is sometimes meaningful, and it
wasn't worth my time or reviewer time to ponder each case individually.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-07-28 20:26:31 +00:00
|
|
|
"--no-cover-letter", "--pretty=mboxrd", "--topo-order",
|
|
|
|
"--no-base", NULL);
|
2019-01-18 15:09:27 +00:00
|
|
|
if (opts->git_format_patch_opt.len)
|
2020-07-28 20:24:27 +00:00
|
|
|
strvec_split(&format_patch.args,
|
strvec: fix indentation in renamed calls
Code which split an argv_array call across multiple lines, like:
argv_array_pushl(&args, "one argument",
"another argument", "and more",
NULL);
was recently mechanically renamed to use strvec, which results in
mis-matched indentation like:
strvec_pushl(&args, "one argument",
"another argument", "and more",
NULL);
Let's fix these up to align the arguments with the opening paren. I did
this manually by sifting through the results of:
git jump grep 'strvec_.*,$'
and liberally applying my editor's auto-format. Most of the changes are
of the form shown above, though I also normalized a few that had
originally used a single-tab indentation (rather than our usual style of
aligning with the open paren). I also rewrapped a couple of obvious
cases (e.g., where previously too-long lines became short enough to fit
on one), but I wasn't aggressive about it. In cases broken to three or
more lines, the grouping of arguments is sometimes meaningful, and it
wasn't worth my time or reviewer time to ponder each case individually.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-07-28 20:26:31 +00:00
|
|
|
opts->git_format_patch_opt.buf);
|
2020-07-28 20:24:27 +00:00
|
|
|
strvec_push(&format_patch.args, revisions.buf);
|
2019-01-18 15:09:27 +00:00
|
|
|
if (opts->restrict_revision)
|
2020-07-28 20:24:27 +00:00
|
|
|
strvec_pushf(&format_patch.args, "^%s",
|
strvec: fix indentation in renamed calls
Code which split an argv_array call across multiple lines, like:
argv_array_pushl(&args, "one argument",
"another argument", "and more",
NULL);
was recently mechanically renamed to use strvec, which results in
mis-matched indentation like:
strvec_pushl(&args, "one argument",
"another argument", "and more",
NULL);
Let's fix these up to align the arguments with the opening paren. I did
this manually by sifting through the results of:
git jump grep 'strvec_.*,$'
and liberally applying my editor's auto-format. Most of the changes are
of the form shown above, though I also normalized a few that had
originally used a single-tab indentation (rather than our usual style of
aligning with the open paren). I also rewrapped a couple of obvious
cases (e.g., where previously too-long lines became short enough to fit
on one), but I wasn't aggressive about it. In cases broken to three or
more lines, the grouping of arguments is sometimes meaningful, and it
wasn't worth my time or reviewer time to ponder each case individually.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-07-28 20:26:31 +00:00
|
|
|
oid_to_hex(&opts->restrict_revision->object.oid));
|
2019-01-18 15:09:27 +00:00
|
|
|
|
|
|
|
status = run_command(&format_patch);
|
|
|
|
if (status) {
|
2022-01-26 13:05:46 +00:00
|
|
|
struct reset_head_opts ropts = { 0 };
|
2019-01-18 15:09:27 +00:00
|
|
|
unlink(rebased_patches);
|
|
|
|
free(rebased_patches);
|
2020-07-28 20:24:27 +00:00
|
|
|
strvec_clear(&am.args);
|
2019-01-18 15:09:27 +00:00
|
|
|
|
2022-10-17 13:17:41 +00:00
|
|
|
ropts.oid = &opts->orig_head->object.oid;
|
2022-01-26 13:05:46 +00:00
|
|
|
ropts.branch = opts->head_name;
|
2022-11-09 14:21:58 +00:00
|
|
|
ropts.default_reflog_action = opts->reflog_action;
|
2022-01-26 13:05:46 +00:00
|
|
|
reset_head(the_repository, &ropts);
|
2019-01-18 15:09:27 +00:00
|
|
|
error(_("\ngit encountered an error while preparing the "
|
|
|
|
"patches to replay\n"
|
|
|
|
"these revisions:\n"
|
|
|
|
"\n %s\n\n"
|
|
|
|
"As a result, git cannot rebase them."),
|
|
|
|
opts->revisions);
|
|
|
|
|
|
|
|
strbuf_release(&revisions);
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
strbuf_release(&revisions);
|
|
|
|
|
|
|
|
am.in = open(rebased_patches, O_RDONLY);
|
|
|
|
if (am.in < 0) {
|
|
|
|
status = error_errno(_("could not open '%s' for reading"),
|
|
|
|
rebased_patches);
|
|
|
|
free(rebased_patches);
|
2020-07-28 20:24:27 +00:00
|
|
|
strvec_clear(&am.args);
|
2019-01-18 15:09:27 +00:00
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
2020-07-29 00:37:20 +00:00
|
|
|
strvec_pushv(&am.args, opts->git_am_opts.v);
|
2020-07-28 20:24:27 +00:00
|
|
|
strvec_push(&am.args, "--rebasing");
|
|
|
|
strvec_pushf(&am.args, "--resolvemsg=%s", resolvemsg);
|
|
|
|
strvec_push(&am.args, "--patch-format=mboxrd");
|
2019-04-17 14:30:36 +00:00
|
|
|
if (opts->allow_rerere_autoupdate == RERERE_AUTOUPDATE)
|
2020-07-28 20:24:27 +00:00
|
|
|
strvec_push(&am.args, "--rerere-autoupdate");
|
2019-04-17 14:30:36 +00:00
|
|
|
else if (opts->allow_rerere_autoupdate == RERERE_NOAUTOUPDATE)
|
2020-07-28 20:24:27 +00:00
|
|
|
strvec_push(&am.args, "--no-rerere-autoupdate");
|
2019-01-18 15:09:27 +00:00
|
|
|
if (opts->gpg_sign_opt)
|
2020-07-28 20:24:27 +00:00
|
|
|
strvec_push(&am.args, opts->gpg_sign_opt);
|
2019-01-18 15:09:27 +00:00
|
|
|
status = run_command(&am);
|
|
|
|
unlink(rebased_patches);
|
|
|
|
free(rebased_patches);
|
|
|
|
|
|
|
|
if (!status) {
|
|
|
|
return move_to_original_branch(opts);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (is_directory(opts->state_dir))
|
2019-04-17 14:30:35 +00:00
|
|
|
rebase_write_basic_state(opts);
|
2019-01-18 15:09:27 +00:00
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
2022-10-12 09:35:12 +00:00
|
|
|
static int run_specific_rebase(struct rebase_options *opts)
|
2018-08-06 19:31:11 +00:00
|
|
|
{
|
|
|
|
int status;
|
|
|
|
|
2020-02-15 21:36:41 +00:00
|
|
|
if (opts->type == REBASE_MERGE) {
|
|
|
|
/* Run sequencer-based rebase */
|
2019-04-17 14:30:44 +00:00
|
|
|
setenv("GIT_CHERRY_PICK_HELP", resolvemsg, 1);
|
2018-10-05 15:54:38 +00:00
|
|
|
if (!(opts->flags & REBASE_INTERACTIVE_EXPLICIT)) {
|
2019-04-17 14:30:44 +00:00
|
|
|
setenv("GIT_SEQUENCE_EDITOR", ":", 1);
|
2018-10-05 15:54:38 +00:00
|
|
|
opts->autosquash = 0;
|
|
|
|
}
|
2019-04-17 14:30:44 +00:00
|
|
|
if (opts->gpg_sign_opt) {
|
|
|
|
/* remove the leading "-S" */
|
|
|
|
char *tmp = xstrdup(opts->gpg_sign_opt + 2);
|
|
|
|
free(opts->gpg_sign_opt);
|
|
|
|
opts->gpg_sign_opt = tmp;
|
|
|
|
}
|
2018-10-05 15:54:38 +00:00
|
|
|
|
2022-10-12 09:35:12 +00:00
|
|
|
status = run_sequencer_rebase(opts);
|
2021-09-07 21:05:06 +00:00
|
|
|
} else if (opts->type == REBASE_APPLY)
|
2019-01-18 15:09:27 +00:00
|
|
|
status = run_am(opts);
|
2021-09-07 21:05:06 +00:00
|
|
|
else
|
2018-08-06 19:31:11 +00:00
|
|
|
BUG("Unhandled rebase type %d", opts->type);
|
|
|
|
|
|
|
|
if (opts->dont_finish_rebase)
|
|
|
|
; /* do nothing */
|
2020-02-15 21:36:41 +00:00
|
|
|
else if (opts->type == REBASE_MERGE)
|
|
|
|
; /* merge backend cleans up after itself */
|
2018-08-06 19:31:11 +00:00
|
|
|
else if (status == 0) {
|
|
|
|
if (!file_exists(state_dir_path("stopped-sha", opts)))
|
|
|
|
finish_rebase(opts);
|
|
|
|
} else if (status == 2) {
|
|
|
|
struct strbuf dir = STRBUF_INIT;
|
|
|
|
|
2020-04-07 14:27:58 +00:00
|
|
|
apply_autostash(state_dir_path("autostash", opts));
|
2018-08-06 19:31:11 +00:00
|
|
|
strbuf_addstr(&dir, opts->state_dir);
|
|
|
|
remove_dir_recursively(&dir, 0);
|
|
|
|
strbuf_release(&dir);
|
|
|
|
die("Nothing to do");
|
|
|
|
}
|
|
|
|
|
|
|
|
return status ? -1 : 0;
|
|
|
|
}
|
|
|
|
|
2023-03-26 03:06:36 +00:00
|
|
|
static void parse_rebase_merges_value(struct rebase_options *options, const char *value)
|
|
|
|
{
|
|
|
|
if (!strcmp("no-rebase-cousins", value))
|
|
|
|
options->rebase_cousins = 0;
|
|
|
|
else if (!strcmp("rebase-cousins", value))
|
|
|
|
options->rebase_cousins = 1;
|
|
|
|
else
|
|
|
|
die(_("Unknown rebase-merges mode: %s"), value);
|
|
|
|
}
|
|
|
|
|
2018-09-04 21:27:13 +00:00
|
|
|
static int rebase_config(const char *var, const char *value, void *data)
|
|
|
|
{
|
|
|
|
struct rebase_options *opts = data;
|
|
|
|
|
|
|
|
if (!strcmp(var, "rebase.stat")) {
|
|
|
|
if (git_config_bool(var, value))
|
|
|
|
opts->flags |= REBASE_DIFFSTAT;
|
|
|
|
else
|
2019-05-21 17:50:20 +00:00
|
|
|
opts->flags &= ~REBASE_DIFFSTAT;
|
2018-09-04 21:27:13 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-09-04 21:59:58 +00:00
|
|
|
if (!strcmp(var, "rebase.autosquash")) {
|
2023-01-25 04:03:54 +00:00
|
|
|
opts->config_autosquash = git_config_bool(var, value);
|
2018-09-04 21:59:58 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-09-04 22:00:00 +00:00
|
|
|
if (!strcmp(var, "commit.gpgsign")) {
|
|
|
|
free(opts->gpg_sign_opt);
|
|
|
|
opts->gpg_sign_opt = git_config_bool(var, value) ?
|
|
|
|
xstrdup("-S") : NULL;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-09-04 22:00:02 +00:00
|
|
|
if (!strcmp(var, "rebase.autostash")) {
|
|
|
|
opts->autostash = git_config_bool(var, value);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2023-03-26 03:06:36 +00:00
|
|
|
if (!strcmp(var, "rebase.rebasemerges")) {
|
|
|
|
opts->config_rebase_merges = git_parse_maybe_bool(value);
|
|
|
|
if (opts->config_rebase_merges < 0) {
|
|
|
|
opts->config_rebase_merges = 1;
|
|
|
|
parse_rebase_merges_value(opts, value);
|
|
|
|
} else {
|
|
|
|
opts->rebase_cousins = 0;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2022-07-19 18:33:42 +00:00
|
|
|
if (!strcmp(var, "rebase.updaterefs")) {
|
2023-01-25 04:03:54 +00:00
|
|
|
opts->config_update_refs = git_config_bool(var, value);
|
2022-07-19 18:33:42 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-12-10 19:04:59 +00:00
|
|
|
if (!strcmp(var, "rebase.reschedulefailedexec")) {
|
|
|
|
opts->reschedule_failed_exec = git_config_bool(var, value);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-02-23 07:18:40 +00:00
|
|
|
if (!strcmp(var, "rebase.forkpoint")) {
|
|
|
|
opts->fork_point = git_config_bool(var, value) ? -1 : 0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-02-15 21:36:39 +00:00
|
|
|
if (!strcmp(var, "rebase.backend")) {
|
|
|
|
return git_config_string(&opts->default_backend, var, value);
|
|
|
|
}
|
|
|
|
|
2018-09-04 21:27:13 +00:00
|
|
|
return git_default_config(var, value, data);
|
|
|
|
}
|
|
|
|
|
2022-01-26 13:05:36 +00:00
|
|
|
static int checkout_up_to_date(struct rebase_options *options)
|
|
|
|
{
|
|
|
|
struct strbuf buf = STRBUF_INIT;
|
2022-01-26 13:05:46 +00:00
|
|
|
struct reset_head_opts ropts = { 0 };
|
2022-01-26 13:05:36 +00:00
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
strbuf_addf(&buf, "%s: checkout %s",
|
2022-11-09 14:21:58 +00:00
|
|
|
options->reflog_action, options->switch_to);
|
2022-10-17 13:17:41 +00:00
|
|
|
ropts.oid = &options->orig_head->object.oid;
|
2022-01-26 13:05:46 +00:00
|
|
|
ropts.branch = options->head_name;
|
|
|
|
ropts.flags = RESET_HEAD_RUN_POST_CHECKOUT_HOOK;
|
2022-03-18 13:54:03 +00:00
|
|
|
if (!ropts.branch)
|
|
|
|
ropts.flags |= RESET_HEAD_DETACH;
|
2022-01-26 13:05:46 +00:00
|
|
|
ropts.head_msg = buf.buf;
|
|
|
|
if (reset_head(the_repository, &ropts) < 0)
|
2022-01-26 13:05:36 +00:00
|
|
|
ret = error(_("could not switch to %s"), options->switch_to);
|
|
|
|
strbuf_release(&buf);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2018-09-04 21:27:16 +00:00
|
|
|
/*
|
|
|
|
* Determines whether the commits in from..to are linear, i.e. contain
|
|
|
|
* no merge commits. This function *expects* `from` to be an ancestor of
|
|
|
|
* `to`.
|
|
|
|
*/
|
|
|
|
static int is_linear_history(struct commit *from, struct commit *to)
|
|
|
|
{
|
|
|
|
while (to && to != from) {
|
2023-03-28 13:58:48 +00:00
|
|
|
repo_parse_commit(the_repository, to);
|
2018-09-04 21:27:16 +00:00
|
|
|
if (!to->parents)
|
|
|
|
return 1;
|
|
|
|
if (to->parents->next)
|
|
|
|
return 0;
|
|
|
|
to = to->parents->item;
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
rebase: fast-forward --onto in more cases
Before, when we had the following graph,
A---B---C (master)
\
D (side)
running 'git rebase --onto master... master side' would result in D
being always rebased, no matter what. However, the desired behavior is
that rebase should notice that this is fast-forwardable and do that
instead.
Add detection to `can_fast_forward` so that this case can be detected
and a fast-forward will be performed. First of all, rewrite the function
to use gotos which simplifies the logic. Next, since the
options.upstream &&
!oidcmp(&options.upstream->object.oid, &options.onto->object.oid)
conditions were removed in `cmd_rebase`, we reintroduce a substitute in
`can_fast_forward`. In particular, checking the merge bases of
`upstream` and `head` fixes a failing case in t3416.
The abbreviated graph for t3416 is as follows:
F---G topic
/
A---B---C---D---E master
and the failing command was
git rebase --onto master...topic F topic
Before, Git would see that there was one merge base (C), and the merge
and onto were the same so it would incorrectly return 1, indicating that
we could fast-forward. This would cause the rebased graph to be 'ABCFG'
when we were expecting 'ABCG'.
With the additional logic, we detect that upstream and head's merge base
is F. Since onto isn't F, it means we're not rebasing the full set of
commits from master..topic. Since we're excluding some commits, a
fast-forward cannot be performed and so we correctly return 0.
Add '-f' to test cases that failed as a result of this change because
they were not expecting a fast-forward so that a rebase is forced.
Helped-by: Phillip Wood <phillip.wood@dunelm.org.uk>
Signed-off-by: Denton Liu <liu.denton@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-08-27 05:37:59 +00:00
|
|
|
static int can_fast_forward(struct commit *onto, struct commit *upstream,
|
2019-08-27 05:38:01 +00:00
|
|
|
struct commit *restrict_revision,
|
2022-10-17 13:17:42 +00:00
|
|
|
struct commit *head, struct object_id *branch_base)
|
2018-09-04 21:27:16 +00:00
|
|
|
{
|
2019-08-27 05:37:56 +00:00
|
|
|
struct commit_list *merge_bases = NULL;
|
|
|
|
int res = 0;
|
2018-09-04 21:27:16 +00:00
|
|
|
|
2022-10-17 13:17:43 +00:00
|
|
|
if (is_null_oid(branch_base))
|
|
|
|
goto done; /* fill_branch_base() found multiple merge bases */
|
2018-09-04 21:27:16 +00:00
|
|
|
|
2022-10-17 13:17:42 +00:00
|
|
|
if (!oideq(branch_base, &onto->object.oid))
|
2019-08-27 05:37:56 +00:00
|
|
|
goto done;
|
|
|
|
|
2022-10-17 13:17:42 +00:00
|
|
|
if (restrict_revision && !oideq(&restrict_revision->object.oid, branch_base))
|
2019-08-27 05:38:01 +00:00
|
|
|
goto done;
|
|
|
|
|
rebase: fast-forward --onto in more cases
Before, when we had the following graph,
A---B---C (master)
\
D (side)
running 'git rebase --onto master... master side' would result in D
being always rebased, no matter what. However, the desired behavior is
that rebase should notice that this is fast-forwardable and do that
instead.
Add detection to `can_fast_forward` so that this case can be detected
and a fast-forward will be performed. First of all, rewrite the function
to use gotos which simplifies the logic. Next, since the
options.upstream &&
!oidcmp(&options.upstream->object.oid, &options.onto->object.oid)
conditions were removed in `cmd_rebase`, we reintroduce a substitute in
`can_fast_forward`. In particular, checking the merge bases of
`upstream` and `head` fixes a failing case in t3416.
The abbreviated graph for t3416 is as follows:
F---G topic
/
A---B---C---D---E master
and the failing command was
git rebase --onto master...topic F topic
Before, Git would see that there was one merge base (C), and the merge
and onto were the same so it would incorrectly return 1, indicating that
we could fast-forward. This would cause the rebased graph to be 'ABCFG'
when we were expecting 'ABCG'.
With the additional logic, we detect that upstream and head's merge base
is F. Since onto isn't F, it means we're not rebasing the full set of
commits from master..topic. Since we're excluding some commits, a
fast-forward cannot be performed and so we correctly return 0.
Add '-f' to test cases that failed as a result of this change because
they were not expecting a fast-forward so that a rebase is forced.
Helped-by: Phillip Wood <phillip.wood@dunelm.org.uk>
Signed-off-by: Denton Liu <liu.denton@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-08-27 05:37:59 +00:00
|
|
|
if (!upstream)
|
|
|
|
goto done;
|
|
|
|
|
2023-03-28 13:58:47 +00:00
|
|
|
merge_bases = repo_get_merge_bases(the_repository, upstream, head);
|
rebase: fast-forward --onto in more cases
Before, when we had the following graph,
A---B---C (master)
\
D (side)
running 'git rebase --onto master... master side' would result in D
being always rebased, no matter what. However, the desired behavior is
that rebase should notice that this is fast-forwardable and do that
instead.
Add detection to `can_fast_forward` so that this case can be detected
and a fast-forward will be performed. First of all, rewrite the function
to use gotos which simplifies the logic. Next, since the
options.upstream &&
!oidcmp(&options.upstream->object.oid, &options.onto->object.oid)
conditions were removed in `cmd_rebase`, we reintroduce a substitute in
`can_fast_forward`. In particular, checking the merge bases of
`upstream` and `head` fixes a failing case in t3416.
The abbreviated graph for t3416 is as follows:
F---G topic
/
A---B---C---D---E master
and the failing command was
git rebase --onto master...topic F topic
Before, Git would see that there was one merge base (C), and the merge
and onto were the same so it would incorrectly return 1, indicating that
we could fast-forward. This would cause the rebased graph to be 'ABCFG'
when we were expecting 'ABCG'.
With the additional logic, we detect that upstream and head's merge base
is F. Since onto isn't F, it means we're not rebasing the full set of
commits from master..topic. Since we're excluding some commits, a
fast-forward cannot be performed and so we correctly return 0.
Add '-f' to test cases that failed as a result of this change because
they were not expecting a fast-forward so that a rebase is forced.
Helped-by: Phillip Wood <phillip.wood@dunelm.org.uk>
Signed-off-by: Denton Liu <liu.denton@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-08-27 05:37:59 +00:00
|
|
|
if (!merge_bases || merge_bases->next)
|
|
|
|
goto done;
|
|
|
|
|
|
|
|
if (!oideq(&onto->object.oid, &merge_bases->item->object.oid))
|
|
|
|
goto done;
|
|
|
|
|
2019-08-27 05:37:56 +00:00
|
|
|
res = 1;
|
|
|
|
|
|
|
|
done:
|
2018-09-04 21:27:16 +00:00
|
|
|
free_commit_list(merge_bases);
|
|
|
|
return res && is_linear_history(onto, head);
|
|
|
|
}
|
|
|
|
|
2022-10-17 13:17:43 +00:00
|
|
|
static void fill_branch_base(struct rebase_options *options,
|
|
|
|
struct object_id *branch_base)
|
|
|
|
{
|
|
|
|
struct commit_list *merge_bases = NULL;
|
|
|
|
|
2023-03-28 13:58:47 +00:00
|
|
|
merge_bases = repo_get_merge_bases(the_repository, options->onto,
|
|
|
|
options->orig_head);
|
2022-10-17 13:17:43 +00:00
|
|
|
if (!merge_bases || merge_bases->next)
|
|
|
|
oidcpy(branch_base, null_oid());
|
|
|
|
else
|
|
|
|
oidcpy(branch_base, &merge_bases->item->object.oid);
|
|
|
|
|
|
|
|
free_commit_list(merge_bases);
|
|
|
|
}
|
|
|
|
|
2020-02-15 21:36:34 +00:00
|
|
|
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);
|
|
|
|
|
2023-01-25 04:03:46 +00:00
|
|
|
if (opts->type != REBASE_UNSPECIFIED && opts->type != REBASE_APPLY)
|
|
|
|
die(_("apply options and merge options cannot be used together"));
|
|
|
|
|
2020-02-15 21:36:41 +00:00
|
|
|
opts->type = REBASE_APPLY;
|
2020-02-15 21:36:34 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-09-04 21:59:49 +00:00
|
|
|
/* -i followed by -m is still -i */
|
|
|
|
static int parse_opt_merge(const struct option *opt, const char *arg, int unset)
|
|
|
|
{
|
|
|
|
struct rebase_options *opts = opt->value;
|
|
|
|
|
assert NOARG/NONEG behavior of parse-options callbacks
When we define a parse-options callback, the flags we put in the option
struct must match what the callback expects. For example, a callback
which does not handle the "unset" parameter should only be used with
PARSE_OPT_NONEG. But since the callback and the option struct are not
defined next to each other, it's easy to get this wrong (as earlier
patches in this series show).
Fortunately, the compiler can help us here: compiling with
-Wunused-parameters can show us which callbacks ignore their "unset"
parameters (and likewise, ones that ignore "arg" expect to be triggered
with PARSE_OPT_NOARG).
But after we've inspected a callback and determined that all of its
callers use the right flags, what do we do next? We'd like to silence
the compiler warning, but do so in a way that will catch any wrong calls
in the future.
We can do that by actually checking those variables and asserting that
they match our expectations. Because this is such a common pattern,
we'll introduce some helper macros. The resulting messages aren't
as descriptive as we could make them, but the file/line information from
BUG() is enough to identify the problem (and anyway, the point is that
these should never be seen).
Each of the annotated callbacks in this patch triggers
-Wunused-parameters, and was manually inspected to make sure all callers
use the correct options (so none of these BUGs should be triggerable).
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-11-05 06:45:42 +00:00
|
|
|
BUG_ON_OPT_NEG(unset);
|
|
|
|
BUG_ON_OPT_ARG(arg);
|
|
|
|
|
2023-01-25 04:03:46 +00:00
|
|
|
if (opts->type != REBASE_UNSPECIFIED && opts->type != REBASE_MERGE)
|
|
|
|
die(_("apply options and merge options cannot be used together"));
|
|
|
|
|
|
|
|
opts->type = REBASE_MERGE;
|
2018-09-04 21:59:49 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-09-07 21:05:10 +00:00
|
|
|
/* -i followed by -r is still explicitly interactive, but -r alone is not */
|
2018-09-04 21:59:49 +00:00
|
|
|
static int parse_opt_interactive(const struct option *opt, const char *arg,
|
|
|
|
int unset)
|
|
|
|
{
|
|
|
|
struct rebase_options *opts = opt->value;
|
|
|
|
|
assert NOARG/NONEG behavior of parse-options callbacks
When we define a parse-options callback, the flags we put in the option
struct must match what the callback expects. For example, a callback
which does not handle the "unset" parameter should only be used with
PARSE_OPT_NONEG. But since the callback and the option struct are not
defined next to each other, it's easy to get this wrong (as earlier
patches in this series show).
Fortunately, the compiler can help us here: compiling with
-Wunused-parameters can show us which callbacks ignore their "unset"
parameters (and likewise, ones that ignore "arg" expect to be triggered
with PARSE_OPT_NOARG).
But after we've inspected a callback and determined that all of its
callers use the right flags, what do we do next? We'd like to silence
the compiler warning, but do so in a way that will catch any wrong calls
in the future.
We can do that by actually checking those variables and asserting that
they match our expectations. Because this is such a common pattern,
we'll introduce some helper macros. The resulting messages aren't
as descriptive as we could make them, but the file/line information from
BUG() is enough to identify the problem (and anyway, the point is that
these should never be seen).
Each of the annotated callbacks in this patch triggers
-Wunused-parameters, and was manually inspected to make sure all callers
use the correct options (so none of these BUGs should be triggerable).
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-11-05 06:45:42 +00:00
|
|
|
BUG_ON_OPT_NEG(unset);
|
|
|
|
BUG_ON_OPT_ARG(arg);
|
|
|
|
|
2023-01-25 04:03:46 +00:00
|
|
|
if (opts->type != REBASE_UNSPECIFIED && opts->type != REBASE_MERGE)
|
|
|
|
die(_("apply options and merge options cannot be used together"));
|
|
|
|
|
2020-02-15 21:36:41 +00:00
|
|
|
opts->type = REBASE_MERGE;
|
2018-09-04 21:59:49 +00:00
|
|
|
opts->flags |= REBASE_INTERACTIVE_EXPLICIT;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
rebase (interactive-backend): fix handling of commits that become empty
As established in the previous commit and commit b00bf1c9a8dd
(git-rebase: make --allow-empty-message the default, 2018-06-27), the
behavior for rebase with different backends in various edge or corner
cases is often more happenstance than design. This commit addresses
another such corner case: commits which "become empty".
A careful reader may note that there are two types of commits which would
become empty due to a rebase:
* [clean cherry-pick] Commits which are clean cherry-picks of upstream
commits, as determined by `git log --cherry-mark ...`. Re-applying
these commits would result in an empty set of changes and a
duplicative commit message; i.e. these are commits that have
"already been applied" upstream.
* [become empty] Commits which are not empty to start, are not clean
cherry-picks of upstream commits, but which still become empty after
being rebased. This happens e.g. when a commit has changes which
are a strict subset of the changes in an upstream commit, or when
the changes of a commit can be found spread across or among several
upstream commits.
Clearly, in both cases the changes in the commit in question are found
upstream already, but the commit message may not be in the latter case.
When cherry-mark can determine a commit is already upstream, then
because of how cherry-mark works this means the upstream commit message
was about the *exact* same set of changes. Thus, the commit messages
can be assumed to be fully interchangeable (and are in fact likely to be
completely identical). As such, the clean cherry-pick case represents a
case when there is no information to be gained by keeping the extra
commit around. All rebase types have always dropped these commits, and
no one to my knowledge has ever requested that we do otherwise.
For many of the become empty cases (and likely even most), we will also
be able to drop the commit without loss of information -- but this isn't
quite always the case. Since these commits represent cases that were
not clean cherry-picks, there is no upstream commit message explaining
the same set of changes. Projects with good commit message hygiene will
likely have the explanation from our commit message contained within or
spread among the relevant upstream commits, but not all projects run
that way. As such, the commit message of the commit being rebased may
have reasoning that suggests additional changes that should be made to
adapt to the new base, or it may have information that someone wants to
add as a note to another commit, or perhaps someone even wants to create
an empty commit with the commit message as-is.
Junio commented on the "become-empty" types of commits as follows[1]:
WRT a change that ends up being empty (as opposed to a change that
is empty from the beginning), I'd think that the current behaviour
is desireable one. "am" based rebase is solely to transplant an
existing history and want to stop much less than "interactive" one
whose purpose is to polish a series before making it publishable,
and asking for confirmation ("this has become empty--do you want to
drop it?") is more appropriate from the workflow point of view.
[1] https://lore.kernel.org/git/xmqqfu1fswdh.fsf@gitster-ct.c.googlers.com/
I would simply add that his arguments for "am"-based rebases actually
apply to all non-explicitly-interactive rebases. Also, since we are
stating that different cases should have different defaults, it may be
worth providing a flag to allow users to select which behavior they want
for these commits.
Introduce a new command line flag for selecting the desired behavior:
--empty={drop,keep,ask}
with the definitions:
drop: drop commits which become empty
keep: keep commits which become empty
ask: provide the user a chance to interact and pick what to do with
commits which become empty on a case-by-case basis
In line with Junio's suggestion, if the --empty flag is not specified,
pick defaults as follows:
explicitly interactive: ask
otherwise: drop
Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-02-15 21:36:25 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2023-03-26 03:06:36 +00:00
|
|
|
static int parse_opt_rebase_merges(const struct option *opt, const char *arg, int unset)
|
|
|
|
{
|
|
|
|
struct rebase_options *options = opt->value;
|
|
|
|
|
|
|
|
options->rebase_merges = !unset;
|
|
|
|
options->rebase_cousins = 0;
|
|
|
|
|
|
|
|
if (arg) {
|
|
|
|
if (!*arg) {
|
|
|
|
warning(_("--rebase-merges with an empty string "
|
|
|
|
"argument is deprecated and will stop "
|
|
|
|
"working in a future version of Git. Use "
|
|
|
|
"--rebase-merges without an argument "
|
|
|
|
"instead, which does the same thing."));
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
parse_rebase_merges_value(options, arg);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-08-08 15:36:30 +00:00
|
|
|
static void NORETURN error_on_missing_default_upstream(void)
|
|
|
|
{
|
|
|
|
struct branch *current_branch = branch_get(NULL);
|
|
|
|
|
|
|
|
printf(_("%s\n"
|
|
|
|
"Please specify which branch you want to rebase against.\n"
|
|
|
|
"See git-rebase(1) for details.\n"
|
|
|
|
"\n"
|
|
|
|
" git rebase '<branch>'\n"
|
|
|
|
"\n"),
|
|
|
|
current_branch ? _("There is no tracking information for "
|
|
|
|
"the current branch.") :
|
|
|
|
_("You are not currently on a branch."));
|
|
|
|
|
|
|
|
if (current_branch) {
|
|
|
|
const char *remote = current_branch->remote_name;
|
|
|
|
|
|
|
|
if (!remote)
|
|
|
|
remote = _("<remote>");
|
|
|
|
|
|
|
|
printf(_("If you wish to set tracking information for this "
|
|
|
|
"branch you can do so with:\n"
|
|
|
|
"\n"
|
|
|
|
" git branch --set-upstream-to=%s/<branch> %s\n"
|
|
|
|
"\n"),
|
|
|
|
remote, current_branch->name);
|
|
|
|
}
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
2019-01-29 18:43:27 +00:00
|
|
|
static int check_exec_cmd(const char *cmd)
|
|
|
|
{
|
|
|
|
if (strchr(cmd, '\n'))
|
|
|
|
return error(_("exec commands cannot contain newlines"));
|
|
|
|
|
|
|
|
/* Does the command consist purely of whitespace? */
|
|
|
|
if (!cmd[strspn(cmd, " \t\r\f\v")])
|
|
|
|
return error(_("empty exec command"));
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-08-06 19:31:09 +00:00
|
|
|
int cmd_rebase(int argc, const char **argv, const char *prefix)
|
|
|
|
{
|
2019-04-17 14:30:41 +00:00
|
|
|
struct rebase_options options = REBASE_OPTIONS_INIT;
|
2018-08-06 19:31:11 +00:00
|
|
|
const char *branch_name;
|
2018-08-08 15:06:16 +00:00
|
|
|
int ret, flags, total_argc, in_progress = 0;
|
rebase: teach rebase --keep-base
A common scenario is if a user is working on a topic branch and they
wish to make some changes to intermediate commits or autosquash, they
would run something such as
git rebase -i --onto master... master
in order to preserve the merge base. This is useful when contributing a
patch series to the Git mailing list, one often starts on top of the
current 'master'. While developing the patches, 'master' is also
developed further and it is sometimes not the best idea to keep rebasing
on top of 'master', but to keep the base commit as-is.
In addition to this, a user wishing to test individual commits in a
topic branch without changing anything may run
git rebase -x ./test.sh master... master
Since rebasing onto the merge base of the branch and the upstream is
such a common case, introduce the --keep-base option as a shortcut.
This allows us to rewrite the above as
git rebase -i --keep-base master
and
git rebase -x ./test.sh --keep-base master
respectively.
Add tests to ensure --keep-base works correctly in the normal case and
fails when there are multiple merge bases, both in regular and
interactive mode. Also, test to make sure conflicting options cause
rebase to fail. While we're adding test cases, add a missing
set_fake_editor call to 'rebase -i --onto master...side'.
While we're documenting the --keep-base option, change an instance of
"merge-base" to "merge base", which is the consistent spelling.
Helped-by: Eric Sunshine <sunshine@sunshineco.com>
Helped-by: Junio C Hamano <gitster@pobox.com>
Helped-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Helped-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Signed-off-by: Denton Liu <liu.denton@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-08-27 05:38:06 +00:00
|
|
|
int keep_base = 0;
|
2018-09-04 21:27:10 +00:00
|
|
|
int ok_to_skip_pre_rebase = 0;
|
2018-08-06 19:31:11 +00:00
|
|
|
struct strbuf msg = STRBUF_INIT;
|
|
|
|
struct strbuf revisions = STRBUF_INIT;
|
2018-09-04 21:27:18 +00:00
|
|
|
struct strbuf buf = STRBUF_INIT;
|
2022-10-17 13:17:42 +00:00
|
|
|
struct object_id branch_base;
|
2020-07-13 10:10:41 +00:00
|
|
|
int ignore_whitespace = 0;
|
2018-09-04 22:00:00 +00:00
|
|
|
const char *gpg_sign = NULL;
|
2018-09-04 22:00:12 +00:00
|
|
|
struct object_id squash_onto;
|
|
|
|
char *squash_onto_name = NULL;
|
2023-02-06 19:08:10 +00:00
|
|
|
char *keep_base_onto_name = NULL;
|
2019-07-01 11:58:15 +00:00
|
|
|
int reschedule_failed_exec = -1;
|
2020-02-15 21:36:31 +00:00
|
|
|
int allow_preemptive_ff = 1;
|
2021-09-07 21:05:06 +00:00
|
|
|
int preserve_merges_selected = 0;
|
2022-01-26 13:05:46 +00:00
|
|
|
struct reset_head_opts ropts = { 0 };
|
2018-09-04 21:27:07 +00:00
|
|
|
struct option builtin_rebase_options[] = {
|
|
|
|
OPT_STRING(0, "onto", &options.onto_name,
|
|
|
|
N_("revision"),
|
|
|
|
N_("rebase onto given branch instead of upstream")),
|
rebase: teach rebase --keep-base
A common scenario is if a user is working on a topic branch and they
wish to make some changes to intermediate commits or autosquash, they
would run something such as
git rebase -i --onto master... master
in order to preserve the merge base. This is useful when contributing a
patch series to the Git mailing list, one often starts on top of the
current 'master'. While developing the patches, 'master' is also
developed further and it is sometimes not the best idea to keep rebasing
on top of 'master', but to keep the base commit as-is.
In addition to this, a user wishing to test individual commits in a
topic branch without changing anything may run
git rebase -x ./test.sh master... master
Since rebasing onto the merge base of the branch and the upstream is
such a common case, introduce the --keep-base option as a shortcut.
This allows us to rewrite the above as
git rebase -i --keep-base master
and
git rebase -x ./test.sh --keep-base master
respectively.
Add tests to ensure --keep-base works correctly in the normal case and
fails when there are multiple merge bases, both in regular and
interactive mode. Also, test to make sure conflicting options cause
rebase to fail. While we're adding test cases, add a missing
set_fake_editor call to 'rebase -i --onto master...side'.
While we're documenting the --keep-base option, change an instance of
"merge-base" to "merge base", which is the consistent spelling.
Helped-by: Eric Sunshine <sunshine@sunshineco.com>
Helped-by: Junio C Hamano <gitster@pobox.com>
Helped-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Helped-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Signed-off-by: Denton Liu <liu.denton@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-08-27 05:38:06 +00:00
|
|
|
OPT_BOOL(0, "keep-base", &keep_base,
|
|
|
|
N_("use the merge-base of upstream and branch as the current base")),
|
2018-09-04 21:27:10 +00:00
|
|
|
OPT_BOOL(0, "no-verify", &ok_to_skip_pre_rebase,
|
|
|
|
N_("allow pre-rebase hook to run")),
|
2018-09-04 21:27:12 +00:00
|
|
|
OPT_NEGBIT('q', "quiet", &options.flags,
|
|
|
|
N_("be quiet. implies --no-stat"),
|
2020-02-15 21:36:28 +00:00
|
|
|
REBASE_NO_QUIET | REBASE_VERBOSE | REBASE_DIFFSTAT),
|
2018-09-04 21:27:13 +00:00
|
|
|
OPT_BIT('v', "verbose", &options.flags,
|
|
|
|
N_("display a diffstat of what changed upstream"),
|
|
|
|
REBASE_NO_QUIET | REBASE_VERBOSE | REBASE_DIFFSTAT),
|
|
|
|
{OPTION_NEGBIT, 'n', "no-stat", &options.flags, NULL,
|
|
|
|
N_("do not show diffstat of what changed upstream"),
|
|
|
|
PARSE_OPT_NOARG, NULL, REBASE_DIFFSTAT },
|
2018-09-04 21:59:50 +00:00
|
|
|
OPT_BOOL(0, "signoff", &options.signoff,
|
Documentation: stylistically normalize references to Signed-off-by:
Ted reported an old typo in the git-commit.txt and merge-options.txt.
Namely, the phrase "Signed-off-by line" was used without either a
definite nor indefinite article.
Upon examination, it seems that the documentation (including items in
Documentation/, but also option help strings) have been quite
inconsistent on usage when referring to `Signed-off-by`.
First, very few places used a definite or indefinite article with the
phrase "Signed-off-by line", but that was the initial typo that led
to this investigation. So, normalize using either an indefinite or
definite article consistently.
The original phrasing, in Commit 3f971fc425b (Documentation updates,
2005-08-14), is "Add Signed-off-by line". Commit 6f855371a53 (Add
--signoff, --check, and long option-names. 2005-12-09) switched to
using "Add `Signed-off-by:` line", but didn't normalize the former
commit to match. Later commits seem to have cut and pasted from one
or the other, which is likely how the usage became so inconsistent.
Junio stated on the git mailing list in
<xmqqy2k1dfoh.fsf@gitster.c.googlers.com> a preference to leave off
the colon. Thus, prefer `Signed-off-by` (with backticks) for the
documentation files and Signed-off-by (without backticks) for option
help strings.
Additionally, Junio argued that "trailer" is now the standard term to
refer to `Signed-off-by`, saying that "becomes plenty clear that we
are not talking about any random line in the log message". As such,
prefer "trailer" over "line" anywhere the former word fits.
However, leave alone those few places in documentation that use
Signed-off-by to refer to the process (rather than the specific
trailer), or in places where mail headers are generally discussed in
comparison with Signed-off-by.
Reported-by: "Theodore Y. Ts'o" <tytso@mit.edu>
Signed-off-by: Bradley M. Kuhn <bkuhn@sfconservancy.org>
Acked-by: Taylor Blau <me@ttaylorr.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-10-20 01:03:55 +00:00
|
|
|
N_("add a Signed-off-by trailer to each commit")),
|
2020-08-17 17:40:02 +00:00
|
|
|
OPT_BOOL(0, "committer-date-is-author-date",
|
|
|
|
&options.committer_date_is_author_date,
|
|
|
|
N_("make committer date match author date")),
|
2020-08-17 17:40:04 +00:00
|
|
|
OPT_BOOL(0, "reset-author-date", &options.ignore_date,
|
2020-08-17 17:40:03 +00:00
|
|
|
N_("ignore author date and use current date")),
|
2020-08-17 17:40:04 +00:00
|
|
|
OPT_HIDDEN_BOOL(0, "ignore-date", &options.ignore_date,
|
|
|
|
N_("synonym of --reset-author-date")),
|
2018-11-14 16:25:29 +00:00
|
|
|
OPT_PASSTHRU_ARGV('C', NULL, &options.git_am_opts, N_("n"),
|
|
|
|
N_("passed to 'git apply'"), 0),
|
2020-07-13 10:10:41 +00:00
|
|
|
OPT_BOOL(0, "ignore-whitespace", &ignore_whitespace,
|
|
|
|
N_("ignore changes in whitespace")),
|
2018-11-14 16:25:29 +00:00
|
|
|
OPT_PASSTHRU_ARGV(0, "whitespace", &options.git_am_opts,
|
|
|
|
N_("action"), N_("passed to 'git apply'"), 0),
|
2018-09-04 21:27:17 +00:00
|
|
|
OPT_BIT('f', "force-rebase", &options.flags,
|
|
|
|
N_("cherry-pick all commits, even if unchanged"),
|
|
|
|
REBASE_FORCE),
|
|
|
|
OPT_BIT(0, "no-ff", &options.flags,
|
|
|
|
N_("cherry-pick all commits, even if unchanged"),
|
|
|
|
REBASE_FORCE),
|
2022-10-12 09:35:12 +00:00
|
|
|
OPT_CMDMODE(0, "continue", &options.action, N_("continue"),
|
2018-08-08 15:06:16 +00:00
|
|
|
ACTION_CONTINUE),
|
2022-10-12 09:35:12 +00:00
|
|
|
OPT_CMDMODE(0, "skip", &options.action,
|
2018-08-08 15:06:17 +00:00
|
|
|
N_("skip current patch and continue"), ACTION_SKIP),
|
2022-10-12 09:35:12 +00:00
|
|
|
OPT_CMDMODE(0, "abort", &options.action,
|
2018-08-08 15:06:18 +00:00
|
|
|
N_("abort and check out the original branch"),
|
|
|
|
ACTION_ABORT),
|
2022-10-12 09:35:12 +00:00
|
|
|
OPT_CMDMODE(0, "quit", &options.action,
|
2018-08-08 15:06:19 +00:00
|
|
|
N_("abort but keep HEAD where it is"), ACTION_QUIT),
|
2022-10-12 09:35:12 +00:00
|
|
|
OPT_CMDMODE(0, "edit-todo", &options.action, N_("edit the todo list "
|
2018-08-08 15:06:20 +00:00
|
|
|
"during an interactive rebase"), ACTION_EDIT_TODO),
|
2022-10-12 09:35:12 +00:00
|
|
|
OPT_CMDMODE(0, "show-current-patch", &options.action,
|
2018-08-08 15:06:20 +00:00
|
|
|
N_("show the patch file being applied or merged"),
|
|
|
|
ACTION_SHOW_CURRENT_PATCH),
|
Use OPT_CALLBACK and OPT_CALLBACK_F
In the codebase, there are many options which use OPTION_CALLBACK in a
plain ol' struct definition. However, we have the OPT_CALLBACK and
OPT_CALLBACK_F macros which are meant to abstract these plain struct
definitions away. These macros are useful as they semantically signal to
developers that these are just normal callback option with nothing fancy
happening.
Replace plain struct definitions of OPTION_CALLBACK with OPT_CALLBACK or
OPT_CALLBACK_F where applicable. The heavy lifting was done using the
following (disgusting) shell script:
#!/bin/sh
do_replacement () {
tr '\n' '\r' |
sed -e 's/{\s*OPTION_CALLBACK,\s*\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\s*0,\(\s*[^[:space:]}]*\)\s*}/OPT_CALLBACK(\1,\2,\3,\4,\5,\6)/g' |
sed -e 's/{\s*OPTION_CALLBACK,\s*\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\(\s*[^[:space:]}]*\)\s*}/OPT_CALLBACK_F(\1,\2,\3,\4,\5,\6,\7)/g' |
tr '\r' '\n'
}
for f in $(git ls-files \*.c)
do
do_replacement <"$f" >"$f.tmp"
mv "$f.tmp" "$f"
done
The result was manually inspected and then reformatted to match the
style of the surrounding code. Finally, using
`git grep OPTION_CALLBACK \*.c`, leftover results which were not handled
by the script were manually transformed.
Signed-off-by: Denton Liu <liu.denton@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-04-28 08:36:28 +00:00
|
|
|
OPT_CALLBACK_F(0, "apply", &options, NULL,
|
2020-02-15 21:36:41 +00:00
|
|
|
N_("use apply strategies to rebase"),
|
2020-02-15 21:36:34 +00:00
|
|
|
PARSE_OPT_NOARG | PARSE_OPT_NONEG,
|
Use OPT_CALLBACK and OPT_CALLBACK_F
In the codebase, there are many options which use OPTION_CALLBACK in a
plain ol' struct definition. However, we have the OPT_CALLBACK and
OPT_CALLBACK_F macros which are meant to abstract these plain struct
definitions away. These macros are useful as they semantically signal to
developers that these are just normal callback option with nothing fancy
happening.
Replace plain struct definitions of OPTION_CALLBACK with OPT_CALLBACK or
OPT_CALLBACK_F where applicable. The heavy lifting was done using the
following (disgusting) shell script:
#!/bin/sh
do_replacement () {
tr '\n' '\r' |
sed -e 's/{\s*OPTION_CALLBACK,\s*\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\s*0,\(\s*[^[:space:]}]*\)\s*}/OPT_CALLBACK(\1,\2,\3,\4,\5,\6)/g' |
sed -e 's/{\s*OPTION_CALLBACK,\s*\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\(\s*[^[:space:]}]*\)\s*}/OPT_CALLBACK_F(\1,\2,\3,\4,\5,\6,\7)/g' |
tr '\r' '\n'
}
for f in $(git ls-files \*.c)
do
do_replacement <"$f" >"$f.tmp"
mv "$f.tmp" "$f"
done
The result was manually inspected and then reformatted to match the
style of the surrounding code. Finally, using
`git grep OPTION_CALLBACK \*.c`, leftover results which were not handled
by the script were manually transformed.
Signed-off-by: Denton Liu <liu.denton@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-04-28 08:36:28 +00:00
|
|
|
parse_opt_am),
|
|
|
|
OPT_CALLBACK_F('m', "merge", &options, NULL,
|
2018-09-04 21:59:49 +00:00
|
|
|
N_("use merging strategies to rebase"),
|
|
|
|
PARSE_OPT_NOARG | PARSE_OPT_NONEG,
|
Use OPT_CALLBACK and OPT_CALLBACK_F
In the codebase, there are many options which use OPTION_CALLBACK in a
plain ol' struct definition. However, we have the OPT_CALLBACK and
OPT_CALLBACK_F macros which are meant to abstract these plain struct
definitions away. These macros are useful as they semantically signal to
developers that these are just normal callback option with nothing fancy
happening.
Replace plain struct definitions of OPTION_CALLBACK with OPT_CALLBACK or
OPT_CALLBACK_F where applicable. The heavy lifting was done using the
following (disgusting) shell script:
#!/bin/sh
do_replacement () {
tr '\n' '\r' |
sed -e 's/{\s*OPTION_CALLBACK,\s*\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\s*0,\(\s*[^[:space:]}]*\)\s*}/OPT_CALLBACK(\1,\2,\3,\4,\5,\6)/g' |
sed -e 's/{\s*OPTION_CALLBACK,\s*\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\(\s*[^[:space:]}]*\)\s*}/OPT_CALLBACK_F(\1,\2,\3,\4,\5,\6,\7)/g' |
tr '\r' '\n'
}
for f in $(git ls-files \*.c)
do
do_replacement <"$f" >"$f.tmp"
mv "$f.tmp" "$f"
done
The result was manually inspected and then reformatted to match the
style of the surrounding code. Finally, using
`git grep OPTION_CALLBACK \*.c`, leftover results which were not handled
by the script were manually transformed.
Signed-off-by: Denton Liu <liu.denton@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-04-28 08:36:28 +00:00
|
|
|
parse_opt_merge),
|
|
|
|
OPT_CALLBACK_F('i', "interactive", &options, NULL,
|
2018-09-04 21:59:49 +00:00
|
|
|
N_("let the user edit the list of commits to rebase"),
|
|
|
|
PARSE_OPT_NOARG | PARSE_OPT_NONEG,
|
Use OPT_CALLBACK and OPT_CALLBACK_F
In the codebase, there are many options which use OPTION_CALLBACK in a
plain ol' struct definition. However, we have the OPT_CALLBACK and
OPT_CALLBACK_F macros which are meant to abstract these plain struct
definitions away. These macros are useful as they semantically signal to
developers that these are just normal callback option with nothing fancy
happening.
Replace plain struct definitions of OPTION_CALLBACK with OPT_CALLBACK or
OPT_CALLBACK_F where applicable. The heavy lifting was done using the
following (disgusting) shell script:
#!/bin/sh
do_replacement () {
tr '\n' '\r' |
sed -e 's/{\s*OPTION_CALLBACK,\s*\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\s*0,\(\s*[^[:space:]}]*\)\s*}/OPT_CALLBACK(\1,\2,\3,\4,\5,\6)/g' |
sed -e 's/{\s*OPTION_CALLBACK,\s*\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\(\s*[^[:space:]}]*\)\s*}/OPT_CALLBACK_F(\1,\2,\3,\4,\5,\6,\7)/g' |
tr '\r' '\n'
}
for f in $(git ls-files \*.c)
do
do_replacement <"$f" >"$f.tmp"
mv "$f.tmp" "$f"
done
The result was manually inspected and then reformatted to match the
style of the surrounding code. Finally, using
`git grep OPTION_CALLBACK \*.c`, leftover results which were not handled
by the script were manually transformed.
Signed-off-by: Denton Liu <liu.denton@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-04-28 08:36:28 +00:00
|
|
|
parse_opt_interactive),
|
2021-09-07 21:05:06 +00:00
|
|
|
OPT_SET_INT_F('p', "preserve-merges", &preserve_merges_selected,
|
2022-06-04 11:17:46 +00:00
|
|
|
N_("(REMOVED) was: try to recreate merges "
|
|
|
|
"instead of ignoring them"),
|
2021-09-07 21:05:06 +00:00
|
|
|
1, PARSE_OPT_HIDDEN),
|
2019-04-17 14:30:36 +00:00
|
|
|
OPT_RERERE_AUTOUPDATE(&options.allow_rerere_autoupdate),
|
2020-03-11 06:55:27 +00:00
|
|
|
OPT_CALLBACK_F(0, "empty", &options, "{drop,keep,ask}",
|
rebase (interactive-backend): fix handling of commits that become empty
As established in the previous commit and commit b00bf1c9a8dd
(git-rebase: make --allow-empty-message the default, 2018-06-27), the
behavior for rebase with different backends in various edge or corner
cases is often more happenstance than design. This commit addresses
another such corner case: commits which "become empty".
A careful reader may note that there are two types of commits which would
become empty due to a rebase:
* [clean cherry-pick] Commits which are clean cherry-picks of upstream
commits, as determined by `git log --cherry-mark ...`. Re-applying
these commits would result in an empty set of changes and a
duplicative commit message; i.e. these are commits that have
"already been applied" upstream.
* [become empty] Commits which are not empty to start, are not clean
cherry-picks of upstream commits, but which still become empty after
being rebased. This happens e.g. when a commit has changes which
are a strict subset of the changes in an upstream commit, or when
the changes of a commit can be found spread across or among several
upstream commits.
Clearly, in both cases the changes in the commit in question are found
upstream already, but the commit message may not be in the latter case.
When cherry-mark can determine a commit is already upstream, then
because of how cherry-mark works this means the upstream commit message
was about the *exact* same set of changes. Thus, the commit messages
can be assumed to be fully interchangeable (and are in fact likely to be
completely identical). As such, the clean cherry-pick case represents a
case when there is no information to be gained by keeping the extra
commit around. All rebase types have always dropped these commits, and
no one to my knowledge has ever requested that we do otherwise.
For many of the become empty cases (and likely even most), we will also
be able to drop the commit without loss of information -- but this isn't
quite always the case. Since these commits represent cases that were
not clean cherry-picks, there is no upstream commit message explaining
the same set of changes. Projects with good commit message hygiene will
likely have the explanation from our commit message contained within or
spread among the relevant upstream commits, but not all projects run
that way. As such, the commit message of the commit being rebased may
have reasoning that suggests additional changes that should be made to
adapt to the new base, or it may have information that someone wants to
add as a note to another commit, or perhaps someone even wants to create
an empty commit with the commit message as-is.
Junio commented on the "become-empty" types of commits as follows[1]:
WRT a change that ends up being empty (as opposed to a change that
is empty from the beginning), I'd think that the current behaviour
is desireable one. "am" based rebase is solely to transplant an
existing history and want to stop much less than "interactive" one
whose purpose is to polish a series before making it publishable,
and asking for confirmation ("this has become empty--do you want to
drop it?") is more appropriate from the workflow point of view.
[1] https://lore.kernel.org/git/xmqqfu1fswdh.fsf@gitster-ct.c.googlers.com/
I would simply add that his arguments for "am"-based rebases actually
apply to all non-explicitly-interactive rebases. Also, since we are
stating that different cases should have different defaults, it may be
worth providing a flag to allow users to select which behavior they want
for these commits.
Introduce a new command line flag for selecting the desired behavior:
--empty={drop,keep,ask}
with the definitions:
drop: drop commits which become empty
keep: keep commits which become empty
ask: provide the user a chance to interact and pick what to do with
commits which become empty on a case-by-case basis
In line with Junio's suggestion, if the --empty flag is not specified,
pick defaults as follows:
explicitly interactive: ask
otherwise: drop
Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-02-15 21:36:25 +00:00
|
|
|
N_("how to handle commits that become empty"),
|
|
|
|
PARSE_OPT_NONEG, parse_opt_empty),
|
Use OPT_CALLBACK and OPT_CALLBACK_F
In the codebase, there are many options which use OPTION_CALLBACK in a
plain ol' struct definition. However, we have the OPT_CALLBACK and
OPT_CALLBACK_F macros which are meant to abstract these plain struct
definitions away. These macros are useful as they semantically signal to
developers that these are just normal callback option with nothing fancy
happening.
Replace plain struct definitions of OPTION_CALLBACK with OPT_CALLBACK or
OPT_CALLBACK_F where applicable. The heavy lifting was done using the
following (disgusting) shell script:
#!/bin/sh
do_replacement () {
tr '\n' '\r' |
sed -e 's/{\s*OPTION_CALLBACK,\s*\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\s*0,\(\s*[^[:space:]}]*\)\s*}/OPT_CALLBACK(\1,\2,\3,\4,\5,\6)/g' |
sed -e 's/{\s*OPTION_CALLBACK,\s*\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\(\s*[^[:space:]}]*\)\s*}/OPT_CALLBACK_F(\1,\2,\3,\4,\5,\6,\7)/g' |
tr '\r' '\n'
}
for f in $(git ls-files \*.c)
do
do_replacement <"$f" >"$f.tmp"
mv "$f.tmp" "$f"
done
The result was manually inspected and then reformatted to match the
style of the surrounding code. Finally, using
`git grep OPTION_CALLBACK \*.c`, leftover results which were not handled
by the script were manually transformed.
Signed-off-by: Denton Liu <liu.denton@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-04-28 08:36:28 +00:00
|
|
|
OPT_CALLBACK_F('k', "keep-empty", &options, NULL,
|
rebase: reinstate --no-keep-empty
Commit d48e5e21da ("rebase (interactive-backend): make --keep-empty the
default", 2020-02-15) turned --keep-empty (for keeping commits which
start empty) into the default. The logic underpinning that commit was:
1) 'git commit' errors out on the creation of empty commits without an
override flag
2) Once someone determines that the override is worthwhile, it's
annoying and/or harmful to required them to take extra steps in
order to keep such commits around (and to repeat such steps with
every rebase).
While the logic on which the decision was made is sound, the result was
a bit of an overcorrection. Instead of jumping to having --keep-empty
being the default, it jumped to making --keep-empty the only available
behavior. There was a simple workaround, though, which was thought to
be good enough at the time. People could still drop commits which
started empty the same way the could drop any commits: by firing up an
interactive rebase and picking out the commits they didn't want from the
list. However, there are cases where external tools might create enough
empty commits that picking all of them out is painful. As such, having
a flag to automatically remove start-empty commits may be beneficial.
Provide users a way to drop commits which start empty using a flag that
existed for years: --no-keep-empty. Interpret --keep-empty as
countermanding any previous --no-keep-empty, but otherwise leaving
--keep-empty as the default.
This might lead to some slight weirdness since commands like
git rebase --empty=drop --keep-empty
git rebase --empty=keep --no-keep-empty
look really weird despite making perfect sense (the first will drop
commits which become empty, but keep commits that started empty; the
second will keep commits which become empty, but drop commits which
started empty). However, --no-keep-empty was named years ago and we are
predominantly keeping it for backward compatibility; also we suspect it
will only be used rarely since folks already have a simple way to drop
commits they don't want with an interactive rebase.
Reported-by: Bryan Turner <bturner@atlassian.com>
Reported-by: Sami Boukortt <sami@boukortt.com>
Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-04-11 02:44:25 +00:00
|
|
|
N_("keep commits which start empty"),
|
rebase (interactive-backend): make --keep-empty the default
Different rebase backends have different treatment for commits which
start empty (i.e. have no changes relative to their parent), and the
--keep-empty option was added at some point to allow adjusting behavior.
The handling of commits which start empty is actually quite similar to
commit b00bf1c9a8dd (git-rebase: make --allow-empty-message the default,
2018-06-27), which pointed out that the behavior for various backends is
often more happenstance than design. The specific change made in that
commit is actually quite relevant as well and much of the logic there
directly applies here.
It makes a lot of sense in 'git commit' to error out on the creation of
empty commits, unless an override flag is provided. However, once
someone determines that there is a rare case that merits using the
manual override to create such a commit, it is somewhere between
annoying and harmful to have to take extra steps to keep such
intentional commits around. Granted, empty commits are quite rare,
which is why handling of them doesn't get considered much and folks tend
to defer to existing (accidental) behavior and assume there was a reason
for it, leading them to just add flags (--keep-empty in this case) that
allow them to override the bad defaults. Fix the interactive backend so
that --keep-empty is the default, much like we did with
--allow-empty-message. The am backend should also be fixed to have
--keep-empty semantics for commits that start empty, but that is not
included in this patch other than a testcase documenting the failure.
Note that there was one test in t3421 which appears to have been written
expecting --keep-empty to not be the default as correct behavior. This
test was introduced in commit 00b8be5a4d38 ("add tests for rebasing of
empty commits", 2013-06-06), which was part of a series focusing on
rebase topology and which had an interesting original cover letter at
https://lore.kernel.org/git/1347949878-12578-1-git-send-email-martinvonz@gmail.com/
which noted
Your input especially appreciated on whether you agree with the
intent of the test cases.
and then went into a long example about how one of the many tests added
had several questions about whether it was correct. As such, I believe
most the tests in that series were about testing rebase topology with as
many different flags as possible and were not trying to state in general
how those flags should behave otherwise.
Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-02-15 21:36:24 +00:00
|
|
|
PARSE_OPT_NOARG | PARSE_OPT_HIDDEN,
|
Use OPT_CALLBACK and OPT_CALLBACK_F
In the codebase, there are many options which use OPTION_CALLBACK in a
plain ol' struct definition. However, we have the OPT_CALLBACK and
OPT_CALLBACK_F macros which are meant to abstract these plain struct
definitions away. These macros are useful as they semantically signal to
developers that these are just normal callback option with nothing fancy
happening.
Replace plain struct definitions of OPTION_CALLBACK with OPT_CALLBACK or
OPT_CALLBACK_F where applicable. The heavy lifting was done using the
following (disgusting) shell script:
#!/bin/sh
do_replacement () {
tr '\n' '\r' |
sed -e 's/{\s*OPTION_CALLBACK,\s*\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\s*0,\(\s*[^[:space:]}]*\)\s*}/OPT_CALLBACK(\1,\2,\3,\4,\5,\6)/g' |
sed -e 's/{\s*OPTION_CALLBACK,\s*\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\),\(\s*[^[:space:]}]*\)\s*}/OPT_CALLBACK_F(\1,\2,\3,\4,\5,\6,\7)/g' |
tr '\r' '\n'
}
for f in $(git ls-files \*.c)
do
do_replacement <"$f" >"$f.tmp"
mv "$f.tmp" "$f"
done
The result was manually inspected and then reformatted to match the
style of the surrounding code. Finally, using
`git grep OPTION_CALLBACK \*.c`, leftover results which were not handled
by the script were manually transformed.
Signed-off-by: Denton Liu <liu.denton@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-04-28 08:36:28 +00:00
|
|
|
parse_opt_keep_empty),
|
2018-09-04 21:59:58 +00:00
|
|
|
OPT_BOOL(0, "autosquash", &options.autosquash,
|
|
|
|
N_("move commits that begin with "
|
|
|
|
"squash!/fixup! under -i")),
|
2022-07-19 18:33:39 +00:00
|
|
|
OPT_BOOL(0, "update-refs", &options.update_refs,
|
|
|
|
N_("update branches that point to commits "
|
|
|
|
"that are being rebased")),
|
2018-09-04 22:00:00 +00:00
|
|
|
{ OPTION_STRING, 'S', "gpg-sign", &gpg_sign, N_("key-id"),
|
|
|
|
N_("GPG-sign commits"),
|
|
|
|
PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
|
2020-04-07 14:28:07 +00:00
|
|
|
OPT_AUTOSTASH(&options.autostash),
|
2023-01-12 16:50:01 +00:00
|
|
|
OPT_STRING_LIST('x', "exec", &options.exec, N_("exec"),
|
2018-09-04 22:00:04 +00:00
|
|
|
N_("add exec lines after each commit of the "
|
|
|
|
"editable list")),
|
2020-01-16 06:14:15 +00:00
|
|
|
OPT_BOOL_F(0, "allow-empty-message",
|
|
|
|
&options.allow_empty_message,
|
|
|
|
N_("allow rebasing commits with empty messages"),
|
|
|
|
PARSE_OPT_HIDDEN),
|
2023-03-26 03:06:36 +00:00
|
|
|
OPT_CALLBACK_F('r', "rebase-merges", &options, N_("mode"),
|
2018-09-04 22:00:07 +00:00
|
|
|
N_("try to rebase merges instead of skipping them"),
|
2023-03-26 03:06:36 +00:00
|
|
|
PARSE_OPT_OPTARG, parse_opt_rebase_merges),
|
2021-02-23 07:18:40 +00:00
|
|
|
OPT_BOOL(0, "fork-point", &options.fork_point,
|
2018-09-04 22:00:09 +00:00
|
|
|
N_("use 'merge-base --fork-point' to refine upstream")),
|
2018-09-04 22:00:11 +00:00
|
|
|
OPT_STRING('s', "strategy", &options.strategy,
|
|
|
|
N_("strategy"), N_("use the given merge strategy")),
|
2023-04-10 09:08:29 +00:00
|
|
|
OPT_STRING_LIST('X', "strategy-option", &options.strategy_opts,
|
2018-09-04 22:00:11 +00:00
|
|
|
N_("option"),
|
|
|
|
N_("pass the argument through to the merge "
|
|
|
|
"strategy")),
|
2018-09-04 22:00:12 +00:00
|
|
|
OPT_BOOL(0, "root", &options.root,
|
|
|
|
N_("rebase all reachable commits up to the root(s)")),
|
2018-12-10 19:04:58 +00:00
|
|
|
OPT_BOOL(0, "reschedule-failed-exec",
|
2019-07-01 11:58:15 +00:00
|
|
|
&reschedule_failed_exec,
|
2018-12-10 19:04:58 +00:00
|
|
|
N_("automatically re-schedule any `exec` that fails")),
|
2020-04-11 02:44:27 +00:00
|
|
|
OPT_BOOL(0, "reapply-cherry-picks", &options.reapply_cherry_picks,
|
|
|
|
N_("apply all changes, even those already present upstream")),
|
2018-09-04 21:27:07 +00:00
|
|
|
OPT_END(),
|
|
|
|
};
|
2018-11-14 16:25:29 +00:00
|
|
|
int i;
|
2018-08-06 19:31:11 +00:00
|
|
|
|
2018-09-04 21:27:07 +00:00
|
|
|
if (argc == 2 && !strcmp(argv[1], "-h"))
|
|
|
|
usage_with_options(builtin_rebase_usage,
|
|
|
|
builtin_rebase_options);
|
|
|
|
|
2021-09-08 11:24:01 +00:00
|
|
|
prepare_repo_settings(the_repository);
|
|
|
|
the_repository->settings.command_requires_full_index = 0;
|
|
|
|
|
2018-09-04 21:27:13 +00:00
|
|
|
git_config(rebase_config, &options);
|
2020-04-03 10:28:02 +00:00
|
|
|
/* options.gpg_sign_opt will be either "-S" or NULL */
|
|
|
|
gpg_sign = options.gpg_sign_opt ? "" : NULL;
|
|
|
|
FREE_AND_NULL(options.gpg_sign_opt);
|
2018-09-04 21:27:13 +00:00
|
|
|
|
2018-08-08 15:06:22 +00:00
|
|
|
strbuf_reset(&buf);
|
|
|
|
strbuf_addf(&buf, "%s/applying", apply_dir());
|
|
|
|
if(file_exists(buf.buf))
|
|
|
|
die(_("It looks like 'git am' is in progress. Cannot rebase."));
|
|
|
|
|
2018-09-04 21:27:18 +00:00
|
|
|
if (is_directory(apply_dir())) {
|
2020-02-15 21:36:41 +00:00
|
|
|
options.type = REBASE_APPLY;
|
2018-09-04 21:27:18 +00:00
|
|
|
options.state_dir = apply_dir();
|
|
|
|
} else if (is_directory(merge_dir())) {
|
|
|
|
strbuf_reset(&buf);
|
|
|
|
strbuf_addf(&buf, "%s/rewritten", merge_dir());
|
2022-10-12 09:35:12 +00:00
|
|
|
if (!(options.action == ACTION_ABORT) && is_directory(buf.buf)) {
|
2022-06-04 11:17:49 +00:00
|
|
|
die(_("`rebase --preserve-merges` (-p) is no longer supported.\n"
|
2022-06-04 11:17:47 +00:00
|
|
|
"Use `git rebase --abort` to terminate current rebase.\n"
|
2022-06-04 11:17:49 +00:00
|
|
|
"Or downgrade to v2.33, or earlier, to complete the rebase."));
|
2018-09-04 21:27:18 +00:00
|
|
|
} else {
|
|
|
|
strbuf_reset(&buf);
|
|
|
|
strbuf_addf(&buf, "%s/interactive", merge_dir());
|
2022-04-18 17:27:21 +00:00
|
|
|
options.type = REBASE_MERGE;
|
|
|
|
if (file_exists(buf.buf))
|
2018-09-04 21:27:18 +00:00
|
|
|
options.flags |= REBASE_INTERACTIVE_EXPLICIT;
|
|
|
|
}
|
|
|
|
options.state_dir = merge_dir();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (options.type != REBASE_UNSPECIFIED)
|
|
|
|
in_progress = 1;
|
|
|
|
|
2018-08-08 15:06:16 +00:00
|
|
|
total_argc = argc;
|
2018-09-04 21:27:07 +00:00
|
|
|
argc = parse_options(argc, argv, prefix,
|
|
|
|
builtin_rebase_options,
|
|
|
|
builtin_rebase_usage, 0);
|
|
|
|
|
2021-09-07 21:05:06 +00:00
|
|
|
if (preserve_merges_selected)
|
2022-06-04 11:17:48 +00:00
|
|
|
die(_("--preserve-merges was replaced by --rebase-merges\n"
|
2022-06-16 17:18:58 +00:00
|
|
|
"Note: Your `pull.rebase` configuration may also be set to 'preserve',\n"
|
2022-06-04 11:17:48 +00:00
|
|
|
"which is no longer supported; use 'merges' instead"));
|
2021-09-07 21:05:06 +00:00
|
|
|
|
2022-10-12 09:35:12 +00:00
|
|
|
if (options.action != ACTION_NONE && total_argc != 2) {
|
2018-08-08 15:06:16 +00:00
|
|
|
usage_with_options(builtin_rebase_usage,
|
|
|
|
builtin_rebase_options);
|
|
|
|
}
|
|
|
|
|
2018-09-04 21:27:07 +00:00
|
|
|
if (argc > 2)
|
|
|
|
usage_with_options(builtin_rebase_usage,
|
|
|
|
builtin_rebase_options);
|
2018-08-06 19:31:11 +00:00
|
|
|
|
rebase: teach rebase --keep-base
A common scenario is if a user is working on a topic branch and they
wish to make some changes to intermediate commits or autosquash, they
would run something such as
git rebase -i --onto master... master
in order to preserve the merge base. This is useful when contributing a
patch series to the Git mailing list, one often starts on top of the
current 'master'. While developing the patches, 'master' is also
developed further and it is sometimes not the best idea to keep rebasing
on top of 'master', but to keep the base commit as-is.
In addition to this, a user wishing to test individual commits in a
topic branch without changing anything may run
git rebase -x ./test.sh master... master
Since rebasing onto the merge base of the branch and the upstream is
such a common case, introduce the --keep-base option as a shortcut.
This allows us to rewrite the above as
git rebase -i --keep-base master
and
git rebase -x ./test.sh --keep-base master
respectively.
Add tests to ensure --keep-base works correctly in the normal case and
fails when there are multiple merge bases, both in regular and
interactive mode. Also, test to make sure conflicting options cause
rebase to fail. While we're adding test cases, add a missing
set_fake_editor call to 'rebase -i --onto master...side'.
While we're documenting the --keep-base option, change an instance of
"merge-base" to "merge base", which is the consistent spelling.
Helped-by: Eric Sunshine <sunshine@sunshineco.com>
Helped-by: Junio C Hamano <gitster@pobox.com>
Helped-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Helped-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Signed-off-by: Denton Liu <liu.denton@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-08-27 05:38:06 +00:00
|
|
|
if (keep_base) {
|
|
|
|
if (options.onto_name)
|
2022-01-05 20:02:16 +00:00
|
|
|
die(_("options '%s' and '%s' cannot be used together"), "--keep-base", "--onto");
|
rebase: teach rebase --keep-base
A common scenario is if a user is working on a topic branch and they
wish to make some changes to intermediate commits or autosquash, they
would run something such as
git rebase -i --onto master... master
in order to preserve the merge base. This is useful when contributing a
patch series to the Git mailing list, one often starts on top of the
current 'master'. While developing the patches, 'master' is also
developed further and it is sometimes not the best idea to keep rebasing
on top of 'master', but to keep the base commit as-is.
In addition to this, a user wishing to test individual commits in a
topic branch without changing anything may run
git rebase -x ./test.sh master... master
Since rebasing onto the merge base of the branch and the upstream is
such a common case, introduce the --keep-base option as a shortcut.
This allows us to rewrite the above as
git rebase -i --keep-base master
and
git rebase -x ./test.sh --keep-base master
respectively.
Add tests to ensure --keep-base works correctly in the normal case and
fails when there are multiple merge bases, both in regular and
interactive mode. Also, test to make sure conflicting options cause
rebase to fail. While we're adding test cases, add a missing
set_fake_editor call to 'rebase -i --onto master...side'.
While we're documenting the --keep-base option, change an instance of
"merge-base" to "merge base", which is the consistent spelling.
Helped-by: Eric Sunshine <sunshine@sunshineco.com>
Helped-by: Junio C Hamano <gitster@pobox.com>
Helped-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Helped-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Signed-off-by: Denton Liu <liu.denton@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-08-27 05:38:06 +00:00
|
|
|
if (options.root)
|
2022-01-05 20:02:16 +00:00
|
|
|
die(_("options '%s' and '%s' cannot be used together"), "--keep-base", "--root");
|
2022-10-17 13:17:45 +00:00
|
|
|
/*
|
|
|
|
* --keep-base defaults to --no-fork-point to keep the
|
|
|
|
* base the same.
|
|
|
|
*/
|
|
|
|
if (options.fork_point < 0)
|
|
|
|
options.fork_point = 0;
|
rebase: teach rebase --keep-base
A common scenario is if a user is working on a topic branch and they
wish to make some changes to intermediate commits or autosquash, they
would run something such as
git rebase -i --onto master... master
in order to preserve the merge base. This is useful when contributing a
patch series to the Git mailing list, one often starts on top of the
current 'master'. While developing the patches, 'master' is also
developed further and it is sometimes not the best idea to keep rebasing
on top of 'master', but to keep the base commit as-is.
In addition to this, a user wishing to test individual commits in a
topic branch without changing anything may run
git rebase -x ./test.sh master... master
Since rebasing onto the merge base of the branch and the upstream is
such a common case, introduce the --keep-base option as a shortcut.
This allows us to rewrite the above as
git rebase -i --keep-base master
and
git rebase -x ./test.sh --keep-base master
respectively.
Add tests to ensure --keep-base works correctly in the normal case and
fails when there are multiple merge bases, both in regular and
interactive mode. Also, test to make sure conflicting options cause
rebase to fail. While we're adding test cases, add a missing
set_fake_editor call to 'rebase -i --onto master...side'.
While we're documenting the --keep-base option, change an instance of
"merge-base" to "merge base", which is the consistent spelling.
Helped-by: Eric Sunshine <sunshine@sunshineco.com>
Helped-by: Junio C Hamano <gitster@pobox.com>
Helped-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Helped-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Signed-off-by: Denton Liu <liu.denton@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-08-27 05:38:06 +00:00
|
|
|
}
|
2021-02-23 07:18:40 +00:00
|
|
|
if (options.root && options.fork_point > 0)
|
2022-01-05 20:02:16 +00:00
|
|
|
die(_("options '%s' and '%s' cannot be used together"), "--root", "--fork-point");
|
2020-04-27 17:59:49 +00:00
|
|
|
|
2022-10-12 09:35:12 +00:00
|
|
|
if (options.action != ACTION_NONE && !in_progress)
|
2018-08-08 15:06:21 +00:00
|
|
|
die(_("No rebase in progress?"));
|
|
|
|
|
2022-10-12 09:35:12 +00:00
|
|
|
if (options.action == ACTION_EDIT_TODO && !is_merge(&options))
|
2018-08-08 15:06:20 +00:00
|
|
|
die(_("The --edit-todo action can only be used during "
|
|
|
|
"interactive rebase."));
|
|
|
|
|
2019-02-22 22:25:10 +00:00
|
|
|
if (trace2_is_enabled()) {
|
2020-02-15 21:36:41 +00:00
|
|
|
if (is_merge(&options))
|
2019-02-22 22:25:10 +00:00
|
|
|
trace2_cmd_mode("interactive");
|
2023-01-12 16:50:01 +00:00
|
|
|
else if (options.exec.nr)
|
2019-02-22 22:25:10 +00:00
|
|
|
trace2_cmd_mode("interactive-exec");
|
|
|
|
else
|
2022-10-12 09:35:12 +00:00
|
|
|
trace2_cmd_mode(action_names[options.action]);
|
2019-02-22 22:25:10 +00:00
|
|
|
}
|
|
|
|
|
2022-11-09 14:21:58 +00:00
|
|
|
options.reflog_action = getenv(GIT_REFLOG_ACTION_ENVIRONMENT);
|
|
|
|
options.reflog_action =
|
|
|
|
xstrdup(options.reflog_action ? options.reflog_action : "rebase");
|
|
|
|
|
2022-10-12 09:35:12 +00:00
|
|
|
switch (options.action) {
|
2018-08-08 15:06:16 +00:00
|
|
|
case ACTION_CONTINUE: {
|
|
|
|
struct object_id head;
|
|
|
|
struct lock_file lock_file = LOCK_INIT;
|
|
|
|
int fd;
|
|
|
|
|
|
|
|
/* Sanity check */
|
2023-03-28 13:58:46 +00:00
|
|
|
if (repo_get_oid(the_repository, "HEAD", &head))
|
2018-08-08 15:06:16 +00:00
|
|
|
die(_("Cannot read HEAD"));
|
|
|
|
|
2022-11-19 13:07:38 +00:00
|
|
|
fd = repo_hold_locked_index(the_repository, &lock_file, 0);
|
2019-01-12 02:13:26 +00:00
|
|
|
if (repo_read_index(the_repository) < 0)
|
2018-08-08 15:06:16 +00:00
|
|
|
die(_("could not read index"));
|
|
|
|
refresh_index(the_repository->index, REFRESH_QUIET, NULL, NULL,
|
|
|
|
NULL);
|
|
|
|
if (0 <= fd)
|
2019-01-12 02:13:27 +00:00
|
|
|
repo_update_index_if_able(the_repository, &lock_file);
|
2018-08-08 15:06:16 +00:00
|
|
|
rollback_lock_file(&lock_file);
|
|
|
|
|
2018-11-10 05:48:49 +00:00
|
|
|
if (has_unstaged_changes(the_repository, 1)) {
|
2018-08-08 15:06:16 +00:00
|
|
|
puts(_("You must edit all merge conflicts and then\n"
|
|
|
|
"mark them as resolved using git add"));
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
if (read_basic_state(&options))
|
|
|
|
exit(1);
|
|
|
|
goto run_rebase;
|
|
|
|
}
|
2018-08-08 15:06:17 +00:00
|
|
|
case ACTION_SKIP: {
|
|
|
|
struct string_list merge_rr = STRING_LIST_INIT_DUP;
|
|
|
|
|
2018-11-10 05:49:09 +00:00
|
|
|
rerere_clear(the_repository, &merge_rr);
|
2018-08-08 15:06:17 +00:00
|
|
|
string_list_clear(&merge_rr, 1);
|
2022-01-26 13:05:46 +00:00
|
|
|
ropts.flags = RESET_HEAD_HARD;
|
|
|
|
if (reset_head(the_repository, &ropts) < 0)
|
2018-08-08 15:06:17 +00:00
|
|
|
die(_("could not discard worktree changes"));
|
2019-03-29 10:38:59 +00:00
|
|
|
remove_branch_state(the_repository, 0);
|
2018-08-08 15:06:17 +00:00
|
|
|
if (read_basic_state(&options))
|
|
|
|
exit(1);
|
|
|
|
goto run_rebase;
|
|
|
|
}
|
2018-08-08 15:06:18 +00:00
|
|
|
case ACTION_ABORT: {
|
|
|
|
struct string_list merge_rr = STRING_LIST_INIT_DUP;
|
2022-10-12 09:35:11 +00:00
|
|
|
struct strbuf head_msg = STRBUF_INIT;
|
2018-08-08 15:06:18 +00:00
|
|
|
|
2018-11-10 05:49:09 +00:00
|
|
|
rerere_clear(the_repository, &merge_rr);
|
2018-08-08 15:06:18 +00:00
|
|
|
string_list_clear(&merge_rr, 1);
|
|
|
|
|
|
|
|
if (read_basic_state(&options))
|
|
|
|
exit(1);
|
2022-10-12 09:35:11 +00:00
|
|
|
|
|
|
|
strbuf_addf(&head_msg, "%s (abort): returning to %s",
|
2022-11-09 14:21:58 +00:00
|
|
|
options.reflog_action,
|
2022-10-12 09:35:11 +00:00
|
|
|
options.head_name ? options.head_name
|
|
|
|
: oid_to_hex(&options.orig_head->object.oid));
|
2022-10-17 13:17:41 +00:00
|
|
|
ropts.oid = &options.orig_head->object.oid;
|
2022-10-12 09:35:11 +00:00
|
|
|
ropts.head_msg = head_msg.buf;
|
2022-01-26 13:05:46 +00:00
|
|
|
ropts.branch = options.head_name;
|
|
|
|
ropts.flags = RESET_HEAD_HARD;
|
|
|
|
if (reset_head(the_repository, &ropts) < 0)
|
2018-08-08 15:06:18 +00:00
|
|
|
die(_("could not move back to %s"),
|
2022-10-17 13:17:41 +00:00
|
|
|
oid_to_hex(&options.orig_head->object.oid));
|
2022-11-08 18:17:48 +00:00
|
|
|
strbuf_release(&head_msg);
|
2019-03-29 10:38:59 +00:00
|
|
|
remove_branch_state(the_repository, 0);
|
2021-09-21 10:24:05 +00:00
|
|
|
ret = finish_rebase(&options);
|
2018-08-08 15:06:18 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
2018-08-08 15:06:19 +00:00
|
|
|
case ACTION_QUIT: {
|
2020-04-28 09:31:31 +00:00
|
|
|
save_autostash(state_dir_path("autostash", &options));
|
2020-02-15 21:36:41 +00:00
|
|
|
if (options.type == REBASE_MERGE) {
|
2019-05-14 18:03:49 +00:00
|
|
|
struct replay_opts replay = REPLAY_OPTS_INIT;
|
|
|
|
|
|
|
|
replay.action = REPLAY_INTERACTIVE_REBASE;
|
2021-09-21 10:24:05 +00:00
|
|
|
ret = sequencer_remove_state(&replay);
|
2023-02-06 19:08:08 +00:00
|
|
|
replay_opts_release(&replay);
|
2019-05-14 18:03:49 +00:00
|
|
|
} else {
|
|
|
|
strbuf_reset(&buf);
|
|
|
|
strbuf_addstr(&buf, options.state_dir);
|
2021-09-21 10:24:05 +00:00
|
|
|
ret = remove_dir_recursively(&buf, 0);
|
2019-05-14 18:03:49 +00:00
|
|
|
if (ret)
|
|
|
|
error(_("could not remove '%s'"),
|
|
|
|
options.state_dir);
|
|
|
|
}
|
2018-08-08 15:06:19 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
2018-08-08 15:06:20 +00:00
|
|
|
case ACTION_EDIT_TODO:
|
|
|
|
options.dont_finish_rebase = 1;
|
|
|
|
goto run_rebase;
|
|
|
|
case ACTION_SHOW_CURRENT_PATCH:
|
|
|
|
options.dont_finish_rebase = 1;
|
|
|
|
goto run_rebase;
|
2019-04-17 14:30:43 +00:00
|
|
|
case ACTION_NONE:
|
2018-08-08 15:06:20 +00:00
|
|
|
break;
|
2018-08-08 15:06:16 +00:00
|
|
|
default:
|
2022-10-12 09:35:12 +00:00
|
|
|
BUG("action: %d", options.action);
|
2018-08-08 15:06:16 +00:00
|
|
|
}
|
|
|
|
|
2018-09-04 21:27:18 +00:00
|
|
|
/* Make sure no rebase is in progress */
|
|
|
|
if (in_progress) {
|
|
|
|
const char *last_slash = strrchr(options.state_dir, '/');
|
|
|
|
const char *state_dir_base =
|
|
|
|
last_slash ? last_slash + 1 : options.state_dir;
|
|
|
|
const char *cmd_live_rebase =
|
|
|
|
"git rebase (--continue | --abort | --skip)";
|
|
|
|
strbuf_reset(&buf);
|
|
|
|
strbuf_addf(&buf, "rm -fr \"%s\"", options.state_dir);
|
|
|
|
die(_("It seems that there is already a %s directory, and\n"
|
|
|
|
"I wonder if you are in the middle of another rebase. "
|
|
|
|
"If that is the\n"
|
|
|
|
"case, please try\n\t%s\n"
|
|
|
|
"If that is not the case, please\n\t%s\n"
|
|
|
|
"and run me again. I am stopping in case you still "
|
|
|
|
"have something\n"
|
|
|
|
"valuable there.\n"),
|
|
|
|
state_dir_base, cmd_live_rebase, buf.buf);
|
|
|
|
}
|
|
|
|
|
2020-02-15 21:36:31 +00:00
|
|
|
if ((options.flags & REBASE_INTERACTIVE_EXPLICIT) ||
|
2022-10-12 09:35:12 +00:00
|
|
|
(options.action != ACTION_NONE) ||
|
2023-01-12 16:50:01 +00:00
|
|
|
(options.exec.nr > 0) ||
|
2023-01-25 04:03:54 +00:00
|
|
|
(options.autosquash == -1 && options.config_autosquash == 1) ||
|
|
|
|
options.autosquash == 1) {
|
2020-02-15 21:36:31 +00:00
|
|
|
allow_preemptive_ff = 0;
|
|
|
|
}
|
2020-08-17 17:40:03 +00:00
|
|
|
if (options.committer_date_is_author_date || options.ignore_date)
|
2020-08-17 17:40:02 +00:00
|
|
|
options.flags |= REBASE_FORCE;
|
2020-02-15 21:36:31 +00:00
|
|
|
|
2020-07-29 00:37:20 +00:00
|
|
|
for (i = 0; i < options.git_am_opts.nr; i++) {
|
|
|
|
const char *option = options.git_am_opts.v[i], *p;
|
2020-08-17 17:40:03 +00:00
|
|
|
if (!strcmp(option, "--whitespace=fix") ||
|
2018-11-14 16:25:29 +00:00
|
|
|
!strcmp(option, "--whitespace=strip"))
|
2020-02-15 21:36:31 +00:00
|
|
|
allow_preemptive_ff = 0;
|
2018-11-14 16:25:31 +00:00
|
|
|
else if (skip_prefix(option, "-C", &p)) {
|
|
|
|
while (*p)
|
|
|
|
if (!isdigit(*(p++)))
|
|
|
|
die(_("switch `C' expects a "
|
|
|
|
"numerical value"));
|
|
|
|
} else if (skip_prefix(option, "--whitespace=", &p)) {
|
|
|
|
if (*p && strcmp(p, "warn") && strcmp(p, "nowarn") &&
|
|
|
|
strcmp(p, "error") && strcmp(p, "error-all"))
|
|
|
|
die("Invalid whitespace option: '%s'", p);
|
|
|
|
}
|
2018-09-04 21:59:53 +00:00
|
|
|
}
|
|
|
|
|
2023-01-12 16:50:01 +00:00
|
|
|
for (i = 0; i < options.exec.nr; i++)
|
|
|
|
if (check_exec_cmd(options.exec.items[i].string))
|
2019-01-29 18:43:27 +00:00
|
|
|
exit(1);
|
|
|
|
|
2018-11-14 16:25:29 +00:00
|
|
|
if (!(options.flags & REBASE_NO_QUIET))
|
2020-07-28 20:24:27 +00:00
|
|
|
strvec_push(&options.git_am_opts, "-q");
|
2018-09-04 21:59:56 +00:00
|
|
|
|
rebase (interactive-backend): fix handling of commits that become empty
As established in the previous commit and commit b00bf1c9a8dd
(git-rebase: make --allow-empty-message the default, 2018-06-27), the
behavior for rebase with different backends in various edge or corner
cases is often more happenstance than design. This commit addresses
another such corner case: commits which "become empty".
A careful reader may note that there are two types of commits which would
become empty due to a rebase:
* [clean cherry-pick] Commits which are clean cherry-picks of upstream
commits, as determined by `git log --cherry-mark ...`. Re-applying
these commits would result in an empty set of changes and a
duplicative commit message; i.e. these are commits that have
"already been applied" upstream.
* [become empty] Commits which are not empty to start, are not clean
cherry-picks of upstream commits, but which still become empty after
being rebased. This happens e.g. when a commit has changes which
are a strict subset of the changes in an upstream commit, or when
the changes of a commit can be found spread across or among several
upstream commits.
Clearly, in both cases the changes in the commit in question are found
upstream already, but the commit message may not be in the latter case.
When cherry-mark can determine a commit is already upstream, then
because of how cherry-mark works this means the upstream commit message
was about the *exact* same set of changes. Thus, the commit messages
can be assumed to be fully interchangeable (and are in fact likely to be
completely identical). As such, the clean cherry-pick case represents a
case when there is no information to be gained by keeping the extra
commit around. All rebase types have always dropped these commits, and
no one to my knowledge has ever requested that we do otherwise.
For many of the become empty cases (and likely even most), we will also
be able to drop the commit without loss of information -- but this isn't
quite always the case. Since these commits represent cases that were
not clean cherry-picks, there is no upstream commit message explaining
the same set of changes. Projects with good commit message hygiene will
likely have the explanation from our commit message contained within or
spread among the relevant upstream commits, but not all projects run
that way. As such, the commit message of the commit being rebased may
have reasoning that suggests additional changes that should be made to
adapt to the new base, or it may have information that someone wants to
add as a note to another commit, or perhaps someone even wants to create
an empty commit with the commit message as-is.
Junio commented on the "become-empty" types of commits as follows[1]:
WRT a change that ends up being empty (as opposed to a change that
is empty from the beginning), I'd think that the current behaviour
is desireable one. "am" based rebase is solely to transplant an
existing history and want to stop much less than "interactive" one
whose purpose is to polish a series before making it publishable,
and asking for confirmation ("this has become empty--do you want to
drop it?") is more appropriate from the workflow point of view.
[1] https://lore.kernel.org/git/xmqqfu1fswdh.fsf@gitster-ct.c.googlers.com/
I would simply add that his arguments for "am"-based rebases actually
apply to all non-explicitly-interactive rebases. Also, since we are
stating that different cases should have different defaults, it may be
worth providing a flag to allow users to select which behavior they want
for these commits.
Introduce a new command line flag for selecting the desired behavior:
--empty={drop,keep,ask}
with the definitions:
drop: drop commits which become empty
keep: keep commits which become empty
ask: provide the user a chance to interact and pick what to do with
commits which become empty on a case-by-case basis
In line with Junio's suggestion, if the --empty flag is not specified,
pick defaults as follows:
explicitly interactive: ask
otherwise: drop
Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-02-15 21:36:25 +00:00
|
|
|
if (options.empty != EMPTY_UNSPECIFIED)
|
2020-02-15 21:36:41 +00:00
|
|
|
imply_merge(&options, "--empty");
|
2018-09-04 21:59:57 +00:00
|
|
|
|
2023-01-25 04:03:49 +00:00
|
|
|
if (options.reapply_cherry_picks < 0)
|
|
|
|
/*
|
|
|
|
* We default to --no-reapply-cherry-picks unless
|
|
|
|
* --keep-base is given; when --keep-base is given, we want
|
|
|
|
* to default to --reapply-cherry-picks.
|
|
|
|
*/
|
|
|
|
options.reapply_cherry_picks = keep_base;
|
|
|
|
else if (!keep_base)
|
|
|
|
/*
|
|
|
|
* The apply backend always searches for and drops cherry
|
|
|
|
* picks. This is often not wanted with --keep-base, so
|
|
|
|
* --keep-base allows --reapply-cherry-picks to be
|
|
|
|
* simulated by altering the upstream such that
|
|
|
|
* cherry-picks cannot be detected and thus all commits are
|
|
|
|
* reapplied. Thus, --[no-]reapply-cherry-picks is
|
|
|
|
* supported when --keep-base is specified, but not when
|
|
|
|
* --keep-base is left out.
|
|
|
|
*/
|
|
|
|
imply_merge(&options, options.reapply_cherry_picks ?
|
|
|
|
"--reapply-cherry-picks" :
|
|
|
|
"--no-reapply-cherry-picks");
|
2020-04-11 02:44:27 +00:00
|
|
|
|
2020-04-03 10:28:02 +00:00
|
|
|
if (gpg_sign)
|
2018-09-04 22:00:00 +00:00
|
|
|
options.gpg_sign_opt = xstrfmt("-S%s", gpg_sign);
|
|
|
|
|
2023-01-12 16:50:01 +00:00
|
|
|
if (options.exec.nr)
|
2020-02-15 21:36:41 +00:00
|
|
|
imply_merge(&options, "--exec");
|
2018-09-04 22:00:04 +00:00
|
|
|
|
2020-07-13 10:10:41 +00:00
|
|
|
if (options.type == REBASE_APPLY) {
|
|
|
|
if (ignore_whitespace)
|
2020-09-03 19:37:01 +00:00
|
|
|
strvec_push(&options.git_am_opts,
|
|
|
|
"--ignore-whitespace");
|
2020-08-17 17:40:02 +00:00
|
|
|
if (options.committer_date_is_author_date)
|
2020-09-03 19:37:01 +00:00
|
|
|
strvec_push(&options.git_am_opts,
|
|
|
|
"--committer-date-is-author-date");
|
2020-08-17 17:40:03 +00:00
|
|
|
if (options.ignore_date)
|
2020-09-03 19:37:01 +00:00
|
|
|
strvec_push(&options.git_am_opts, "--ignore-date");
|
2020-07-13 10:10:41 +00:00
|
|
|
} else {
|
2021-09-07 21:05:09 +00:00
|
|
|
/* REBASE_MERGE */
|
2020-07-13 10:10:41 +00:00
|
|
|
if (ignore_whitespace) {
|
2023-04-10 09:08:29 +00:00
|
|
|
string_list_append(&options.strategy_opts,
|
2020-07-13 10:10:41 +00:00
|
|
|
"ignore-space-change");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-10 09:08:29 +00:00
|
|
|
if (options.strategy_opts.nr && !options.strategy)
|
|
|
|
options.strategy = "ort";
|
2018-09-04 22:00:11 +00:00
|
|
|
|
|
|
|
if (options.strategy) {
|
|
|
|
options.strategy = xstrdup(options.strategy);
|
|
|
|
switch (options.type) {
|
2020-02-15 21:36:41 +00:00
|
|
|
case REBASE_APPLY:
|
2018-09-04 22:00:11 +00:00
|
|
|
die(_("--strategy requires --merge or --interactive"));
|
|
|
|
case REBASE_MERGE:
|
|
|
|
/* compatible */
|
|
|
|
break;
|
|
|
|
case REBASE_UNSPECIFIED:
|
|
|
|
options.type = REBASE_MERGE;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
BUG("unhandled rebase type (%d)", options.type);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
rebase: implement --merge via the interactive machinery
As part of an ongoing effort to make rebase have more uniform behavior,
modify the merge backend to behave like the interactive one, by
re-implementing it on top of the latter.
Interactive rebases are implemented in terms of cherry-pick rather than
the merge-recursive builtin, but cherry-pick also calls into the
recursive merge machinery by default and can accept special merge
strategies and/or special strategy options. As such, there really is
not any need for having both git-rebase--merge and
git-rebase--interactive anymore. Delete git-rebase--merge.sh and
instead implement it in builtin/rebase.c.
This results in a few deliberate but small user-visible changes:
* The progress output is modified (see t3406 and t3420 for examples)
* A few known test failures are now fixed (see t3421)
* bash-prompt during a rebase --merge is now REBASE-i instead of
REBASE-m. Reason: The prompt is a reflection of the backend in use;
this allows users to report an issue to the git mailing list with
the appropriate backend information, and allows advanced users to
know where to search for relevant control files. (see t9903)
testcase modification notes:
t3406: --interactive and --merge had slightly different progress output
while running; adjust a test to match the new expectation
t3420: these test precise output while running, but rebase--am,
rebase--merge, and rebase--interactive all were built on very
different commands (am, merge-recursive, cherry-pick), so the
tests expected different output for each type. Now we expect
--merge and --interactive to have the same output.
t3421: --interactive fixes some bugs in --merge! Wahoo!
t9903: --merge uses the interactive backend so the prompt expected is
now REBASE-i.
Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-12-11 16:11:39 +00:00
|
|
|
if (options.type == REBASE_MERGE)
|
2020-02-15 21:36:41 +00:00
|
|
|
imply_merge(&options, "--merge");
|
rebase: implement --merge via the interactive machinery
As part of an ongoing effort to make rebase have more uniform behavior,
modify the merge backend to behave like the interactive one, by
re-implementing it on top of the latter.
Interactive rebases are implemented in terms of cherry-pick rather than
the merge-recursive builtin, but cherry-pick also calls into the
recursive merge machinery by default and can accept special merge
strategies and/or special strategy options. As such, there really is
not any need for having both git-rebase--merge and
git-rebase--interactive anymore. Delete git-rebase--merge.sh and
instead implement it in builtin/rebase.c.
This results in a few deliberate but small user-visible changes:
* The progress output is modified (see t3406 and t3420 for examples)
* A few known test failures are now fixed (see t3421)
* bash-prompt during a rebase --merge is now REBASE-i instead of
REBASE-m. Reason: The prompt is a reflection of the backend in use;
this allows users to report an issue to the git mailing list with
the appropriate backend information, and allows advanced users to
know where to search for relevant control files. (see t9903)
testcase modification notes:
t3406: --interactive and --merge had slightly different progress output
while running; adjust a test to match the new expectation
t3420: these test precise output while running, but rebase--am,
rebase--merge, and rebase--interactive all were built on very
different commands (am, merge-recursive, cherry-pick), so the
tests expected different output for each type. Now we expect
--merge and --interactive to have the same output.
t3421: --interactive fixes some bugs in --merge! Wahoo!
t9903: --merge uses the interactive backend so the prompt expected is
now REBASE-i.
Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-12-11 16:11:39 +00:00
|
|
|
|
2018-09-04 22:00:12 +00:00
|
|
|
if (options.root && !options.onto_name)
|
2020-02-15 21:36:41 +00:00
|
|
|
imply_merge(&options, "--root without --onto");
|
2018-09-04 22:00:12 +00:00
|
|
|
|
2018-08-08 15:36:33 +00:00
|
|
|
if (isatty(2) && options.flags & REBASE_NO_QUIET)
|
|
|
|
strbuf_addstr(&options.git_format_patch_opt, " --progress");
|
|
|
|
|
2020-07-29 00:37:20 +00:00
|
|
|
if (options.git_am_opts.nr || options.type == REBASE_APPLY) {
|
2020-02-15 21:36:41 +00:00
|
|
|
/* all am options except -q are compatible only with --apply */
|
2020-07-29 00:37:20 +00:00
|
|
|
for (i = options.git_am_opts.nr - 1; i >= 0; i--)
|
|
|
|
if (strcmp(options.git_am_opts.v[i], "-q"))
|
2020-02-15 21:36:33 +00:00
|
|
|
break;
|
|
|
|
|
2023-01-25 04:03:54 +00:00
|
|
|
if (i >= 0 || options.type == REBASE_APPLY) {
|
2020-02-15 21:36:41 +00:00
|
|
|
if (is_merge(&options))
|
2022-01-05 20:02:24 +00:00
|
|
|
die(_("apply options and merge options "
|
|
|
|
"cannot be used together"));
|
2023-01-25 04:03:54 +00:00
|
|
|
else if (options.autosquash == -1 && options.config_autosquash == 1)
|
2023-02-26 18:00:29 +00:00
|
|
|
die(_("apply options are incompatible with rebase.autoSquash. Consider adding --no-autosquash"));
|
2023-03-26 03:06:36 +00:00
|
|
|
else if (options.rebase_merges == -1 && options.config_rebase_merges == 1)
|
|
|
|
die(_("apply options are incompatible with rebase.rebaseMerges. Consider adding --no-rebase-merges"));
|
2023-01-25 04:03:54 +00:00
|
|
|
else if (options.update_refs == -1 && options.config_update_refs == 1)
|
|
|
|
die(_("apply options are incompatible with rebase.updateRefs. Consider adding --no-update-refs"));
|
2020-02-15 21:36:39 +00:00
|
|
|
else
|
2020-02-15 21:36:41 +00:00
|
|
|
options.type = REBASE_APPLY;
|
2020-02-15 21:36:39 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-25 04:03:54 +00:00
|
|
|
if (options.update_refs == 1)
|
2023-01-25 04:03:45 +00:00
|
|
|
imply_merge(&options, "--update-refs");
|
2023-01-25 04:03:54 +00:00
|
|
|
options.update_refs = (options.update_refs >= 0) ? options.update_refs :
|
|
|
|
((options.config_update_refs >= 0) ? options.config_update_refs : 0);
|
2023-01-25 04:03:45 +00:00
|
|
|
|
2023-03-26 03:06:36 +00:00
|
|
|
if (options.rebase_merges == 1)
|
|
|
|
imply_merge(&options, "--rebase-merges");
|
|
|
|
options.rebase_merges = (options.rebase_merges >= 0) ? options.rebase_merges :
|
|
|
|
((options.config_rebase_merges >= 0) ? options.config_rebase_merges : 0);
|
|
|
|
|
2023-01-25 04:03:54 +00:00
|
|
|
if (options.autosquash == 1)
|
2023-01-25 04:03:50 +00:00
|
|
|
imply_merge(&options, "--autosquash");
|
2023-01-25 04:03:54 +00:00
|
|
|
options.autosquash = (options.autosquash >= 0) ? options.autosquash :
|
|
|
|
((options.config_autosquash >= 0) ? options.config_autosquash : 0);
|
2023-01-25 04:03:50 +00:00
|
|
|
|
2020-02-15 21:36:39 +00:00
|
|
|
if (options.type == REBASE_UNSPECIFIED) {
|
|
|
|
if (!strcmp(options.default_backend, "merge"))
|
2020-02-15 21:36:41 +00:00
|
|
|
imply_merge(&options, "--merge");
|
|
|
|
else if (!strcmp(options.default_backend, "apply"))
|
|
|
|
options.type = REBASE_APPLY;
|
2020-02-15 21:36:39 +00:00
|
|
|
else
|
|
|
|
die(_("Unknown rebase backend: %s"),
|
|
|
|
options.default_backend);
|
2020-02-15 21:36:33 +00:00
|
|
|
}
|
|
|
|
|
2020-11-02 23:45:34 +00:00
|
|
|
if (options.type == REBASE_MERGE &&
|
|
|
|
!options.strategy &&
|
|
|
|
getenv("GIT_TEST_MERGE_ALGORITHM"))
|
|
|
|
options.strategy = xstrdup(getenv("GIT_TEST_MERGE_ALGORITHM"));
|
|
|
|
|
2018-08-06 19:31:11 +00:00
|
|
|
switch (options.type) {
|
|
|
|
case REBASE_MERGE:
|
|
|
|
options.state_dir = merge_dir();
|
|
|
|
break;
|
2020-02-15 21:36:41 +00:00
|
|
|
case REBASE_APPLY:
|
2018-08-06 19:31:11 +00:00
|
|
|
options.state_dir = apply_dir();
|
|
|
|
break;
|
|
|
|
default:
|
2020-02-15 21:36:39 +00:00
|
|
|
BUG("options.type was just set above; should be unreachable.");
|
2018-08-06 19:31:11 +00:00
|
|
|
}
|
|
|
|
|
rebase (interactive-backend): fix handling of commits that become empty
As established in the previous commit and commit b00bf1c9a8dd
(git-rebase: make --allow-empty-message the default, 2018-06-27), the
behavior for rebase with different backends in various edge or corner
cases is often more happenstance than design. This commit addresses
another such corner case: commits which "become empty".
A careful reader may note that there are two types of commits which would
become empty due to a rebase:
* [clean cherry-pick] Commits which are clean cherry-picks of upstream
commits, as determined by `git log --cherry-mark ...`. Re-applying
these commits would result in an empty set of changes and a
duplicative commit message; i.e. these are commits that have
"already been applied" upstream.
* [become empty] Commits which are not empty to start, are not clean
cherry-picks of upstream commits, but which still become empty after
being rebased. This happens e.g. when a commit has changes which
are a strict subset of the changes in an upstream commit, or when
the changes of a commit can be found spread across or among several
upstream commits.
Clearly, in both cases the changes in the commit in question are found
upstream already, but the commit message may not be in the latter case.
When cherry-mark can determine a commit is already upstream, then
because of how cherry-mark works this means the upstream commit message
was about the *exact* same set of changes. Thus, the commit messages
can be assumed to be fully interchangeable (and are in fact likely to be
completely identical). As such, the clean cherry-pick case represents a
case when there is no information to be gained by keeping the extra
commit around. All rebase types have always dropped these commits, and
no one to my knowledge has ever requested that we do otherwise.
For many of the become empty cases (and likely even most), we will also
be able to drop the commit without loss of information -- but this isn't
quite always the case. Since these commits represent cases that were
not clean cherry-picks, there is no upstream commit message explaining
the same set of changes. Projects with good commit message hygiene will
likely have the explanation from our commit message contained within or
spread among the relevant upstream commits, but not all projects run
that way. As such, the commit message of the commit being rebased may
have reasoning that suggests additional changes that should be made to
adapt to the new base, or it may have information that someone wants to
add as a note to another commit, or perhaps someone even wants to create
an empty commit with the commit message as-is.
Junio commented on the "become-empty" types of commits as follows[1]:
WRT a change that ends up being empty (as opposed to a change that
is empty from the beginning), I'd think that the current behaviour
is desireable one. "am" based rebase is solely to transplant an
existing history and want to stop much less than "interactive" one
whose purpose is to polish a series before making it publishable,
and asking for confirmation ("this has become empty--do you want to
drop it?") is more appropriate from the workflow point of view.
[1] https://lore.kernel.org/git/xmqqfu1fswdh.fsf@gitster-ct.c.googlers.com/
I would simply add that his arguments for "am"-based rebases actually
apply to all non-explicitly-interactive rebases. Also, since we are
stating that different cases should have different defaults, it may be
worth providing a flag to allow users to select which behavior they want
for these commits.
Introduce a new command line flag for selecting the desired behavior:
--empty={drop,keep,ask}
with the definitions:
drop: drop commits which become empty
keep: keep commits which become empty
ask: provide the user a chance to interact and pick what to do with
commits which become empty on a case-by-case basis
In line with Junio's suggestion, if the --empty flag is not specified,
pick defaults as follows:
explicitly interactive: ask
otherwise: drop
Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-02-15 21:36:25 +00:00
|
|
|
if (options.empty == EMPTY_UNSPECIFIED) {
|
|
|
|
if (options.flags & REBASE_INTERACTIVE_EXPLICIT)
|
|
|
|
options.empty = EMPTY_ASK;
|
2023-01-12 16:50:01 +00:00
|
|
|
else if (options.exec.nr > 0)
|
rebase (interactive-backend): fix handling of commits that become empty
As established in the previous commit and commit b00bf1c9a8dd
(git-rebase: make --allow-empty-message the default, 2018-06-27), the
behavior for rebase with different backends in various edge or corner
cases is often more happenstance than design. This commit addresses
another such corner case: commits which "become empty".
A careful reader may note that there are two types of commits which would
become empty due to a rebase:
* [clean cherry-pick] Commits which are clean cherry-picks of upstream
commits, as determined by `git log --cherry-mark ...`. Re-applying
these commits would result in an empty set of changes and a
duplicative commit message; i.e. these are commits that have
"already been applied" upstream.
* [become empty] Commits which are not empty to start, are not clean
cherry-picks of upstream commits, but which still become empty after
being rebased. This happens e.g. when a commit has changes which
are a strict subset of the changes in an upstream commit, or when
the changes of a commit can be found spread across or among several
upstream commits.
Clearly, in both cases the changes in the commit in question are found
upstream already, but the commit message may not be in the latter case.
When cherry-mark can determine a commit is already upstream, then
because of how cherry-mark works this means the upstream commit message
was about the *exact* same set of changes. Thus, the commit messages
can be assumed to be fully interchangeable (and are in fact likely to be
completely identical). As such, the clean cherry-pick case represents a
case when there is no information to be gained by keeping the extra
commit around. All rebase types have always dropped these commits, and
no one to my knowledge has ever requested that we do otherwise.
For many of the become empty cases (and likely even most), we will also
be able to drop the commit without loss of information -- but this isn't
quite always the case. Since these commits represent cases that were
not clean cherry-picks, there is no upstream commit message explaining
the same set of changes. Projects with good commit message hygiene will
likely have the explanation from our commit message contained within or
spread among the relevant upstream commits, but not all projects run
that way. As such, the commit message of the commit being rebased may
have reasoning that suggests additional changes that should be made to
adapt to the new base, or it may have information that someone wants to
add as a note to another commit, or perhaps someone even wants to create
an empty commit with the commit message as-is.
Junio commented on the "become-empty" types of commits as follows[1]:
WRT a change that ends up being empty (as opposed to a change that
is empty from the beginning), I'd think that the current behaviour
is desireable one. "am" based rebase is solely to transplant an
existing history and want to stop much less than "interactive" one
whose purpose is to polish a series before making it publishable,
and asking for confirmation ("this has become empty--do you want to
drop it?") is more appropriate from the workflow point of view.
[1] https://lore.kernel.org/git/xmqqfu1fswdh.fsf@gitster-ct.c.googlers.com/
I would simply add that his arguments for "am"-based rebases actually
apply to all non-explicitly-interactive rebases. Also, since we are
stating that different cases should have different defaults, it may be
worth providing a flag to allow users to select which behavior they want
for these commits.
Introduce a new command line flag for selecting the desired behavior:
--empty={drop,keep,ask}
with the definitions:
drop: drop commits which become empty
keep: keep commits which become empty
ask: provide the user a chance to interact and pick what to do with
commits which become empty on a case-by-case basis
In line with Junio's suggestion, if the --empty flag is not specified,
pick defaults as follows:
explicitly interactive: ask
otherwise: drop
Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-02-15 21:36:25 +00:00
|
|
|
options.empty = EMPTY_KEEP;
|
|
|
|
else
|
|
|
|
options.empty = EMPTY_DROP;
|
|
|
|
}
|
2020-02-15 21:36:41 +00:00
|
|
|
if (reschedule_failed_exec > 0 && !is_merge(&options))
|
2019-07-01 11:58:15 +00:00
|
|
|
die(_("--reschedule-failed-exec requires "
|
|
|
|
"--exec or --interactive"));
|
|
|
|
if (reschedule_failed_exec >= 0)
|
|
|
|
options.reschedule_failed_exec = reschedule_failed_exec;
|
2018-12-10 19:04:58 +00:00
|
|
|
|
2018-09-04 21:59:50 +00:00
|
|
|
if (options.signoff) {
|
2020-07-28 20:24:27 +00:00
|
|
|
strvec_push(&options.git_am_opts, "--signoff");
|
2018-09-04 21:59:50 +00:00
|
|
|
options.flags |= REBASE_FORCE;
|
|
|
|
}
|
|
|
|
|
2018-08-06 19:31:11 +00:00
|
|
|
if (!options.root) {
|
2018-08-08 15:36:30 +00:00
|
|
|
if (argc < 1) {
|
|
|
|
struct branch *branch;
|
|
|
|
|
|
|
|
branch = branch_get(NULL);
|
|
|
|
options.upstream_name = branch_get_upstream(branch,
|
|
|
|
NULL);
|
|
|
|
if (!options.upstream_name)
|
|
|
|
error_on_missing_default_upstream();
|
2021-02-23 07:18:40 +00:00
|
|
|
if (options.fork_point < 0)
|
|
|
|
options.fork_point = 1;
|
2018-08-08 15:36:30 +00:00
|
|
|
} else {
|
2018-09-04 21:27:07 +00:00
|
|
|
options.upstream_name = argv[0];
|
2018-08-06 19:31:11 +00:00
|
|
|
argc--;
|
|
|
|
argv++;
|
|
|
|
if (!strcmp(options.upstream_name, "-"))
|
|
|
|
options.upstream_name = "@{-1}";
|
|
|
|
}
|
2021-09-21 10:24:06 +00:00
|
|
|
options.upstream =
|
|
|
|
lookup_commit_reference_by_name(options.upstream_name);
|
2018-08-06 19:31:11 +00:00
|
|
|
if (!options.upstream)
|
|
|
|
die(_("invalid upstream '%s'"), options.upstream_name);
|
2018-09-04 21:27:10 +00:00
|
|
|
options.upstream_arg = options.upstream_name;
|
2018-09-04 22:00:12 +00:00
|
|
|
} else {
|
|
|
|
if (!options.onto_name) {
|
|
|
|
if (commit_tree("", 0, the_hash_algo->empty_tree, NULL,
|
|
|
|
&squash_onto, NULL, NULL) < 0)
|
|
|
|
die(_("Could not create new root commit"));
|
|
|
|
options.squash_onto = &squash_onto;
|
|
|
|
options.onto_name = squash_onto_name =
|
|
|
|
xstrdup(oid_to_hex(&squash_onto));
|
2019-07-31 15:18:49 +00:00
|
|
|
} else
|
|
|
|
options.root_with_onto = 1;
|
|
|
|
|
2018-09-04 22:00:12 +00:00
|
|
|
options.upstream_name = NULL;
|
|
|
|
options.upstream = NULL;
|
|
|
|
if (argc > 1)
|
|
|
|
usage_with_options(builtin_rebase_usage,
|
|
|
|
builtin_rebase_options);
|
|
|
|
options.upstream_arg = "--root";
|
|
|
|
}
|
2018-08-06 19:31:11 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If the branch to rebase is given, that is the branch we will rebase
|
|
|
|
* branch_name -- branch/commit being rebased, or
|
|
|
|
* HEAD (already detached)
|
|
|
|
* orig_head -- commit object name of tip of the branch before rebasing
|
2018-09-04 21:27:20 +00:00
|
|
|
* head_name -- refs/heads/<that-branch> or NULL (detached HEAD)
|
2018-08-06 19:31:11 +00:00
|
|
|
*/
|
2018-09-04 21:27:21 +00:00
|
|
|
if (argc == 1) {
|
|
|
|
/* Is it "rebase other branchname" or "rebase other commit"? */
|
2022-10-17 13:17:41 +00:00
|
|
|
struct object_id branch_oid;
|
2018-09-04 21:27:21 +00:00
|
|
|
branch_name = argv[0];
|
|
|
|
options.switch_to = argv[0];
|
|
|
|
|
|
|
|
/* Is it a local branch? */
|
|
|
|
strbuf_reset(&buf);
|
|
|
|
strbuf_addf(&buf, "refs/heads/%s", branch_name);
|
2022-10-17 13:17:41 +00:00
|
|
|
if (!read_ref(buf.buf, &branch_oid)) {
|
2020-02-23 10:14:07 +00:00
|
|
|
die_if_checked_out(buf.buf, 1);
|
2018-09-04 21:27:21 +00:00
|
|
|
options.head_name = xstrdup(buf.buf);
|
2022-10-17 13:17:41 +00:00
|
|
|
options.orig_head =
|
|
|
|
lookup_commit_object(the_repository,
|
|
|
|
&branch_oid);
|
2018-09-04 21:27:21 +00:00
|
|
|
/* If not is it a valid ref (branch or commit)? */
|
2021-09-21 10:24:07 +00:00
|
|
|
} else {
|
2022-10-17 13:17:41 +00:00
|
|
|
options.orig_head =
|
2021-09-21 10:24:07 +00:00
|
|
|
lookup_commit_reference_by_name(branch_name);
|
2018-09-04 21:27:21 +00:00
|
|
|
options.head_name = NULL;
|
2021-09-21 10:24:07 +00:00
|
|
|
}
|
2022-10-17 13:17:41 +00:00
|
|
|
if (!options.orig_head)
|
|
|
|
die(_("no such branch/commit '%s'"), branch_name);
|
2018-09-04 21:27:21 +00:00
|
|
|
} else if (argc == 0) {
|
2018-08-06 19:31:11 +00:00
|
|
|
/* Do not need to switch branches, we are already on it. */
|
|
|
|
options.head_name =
|
|
|
|
xstrdup_or_null(resolve_ref_unsafe("HEAD", 0, NULL,
|
|
|
|
&flags));
|
|
|
|
if (!options.head_name)
|
|
|
|
die(_("No such ref: %s"), "HEAD");
|
|
|
|
if (flags & REF_ISSYMREF) {
|
|
|
|
if (!skip_prefix(options.head_name,
|
|
|
|
"refs/heads/", &branch_name))
|
|
|
|
branch_name = options.head_name;
|
|
|
|
|
|
|
|
} else {
|
2019-04-06 11:34:21 +00:00
|
|
|
FREE_AND_NULL(options.head_name);
|
2018-08-06 19:31:11 +00:00
|
|
|
branch_name = "HEAD";
|
|
|
|
}
|
2022-10-17 13:17:41 +00:00
|
|
|
options.orig_head = lookup_commit_reference_by_name("HEAD");
|
|
|
|
if (!options.orig_head)
|
|
|
|
die(_("Could not resolve HEAD to a commit"));
|
2018-09-04 21:27:21 +00:00
|
|
|
} else
|
|
|
|
BUG("unexpected number of arguments left to parse");
|
2018-08-06 19:31:11 +00:00
|
|
|
|
2022-04-21 04:42:33 +00:00
|
|
|
/* Make sure the branch to rebase onto is valid. */
|
|
|
|
if (keep_base) {
|
|
|
|
strbuf_reset(&buf);
|
|
|
|
strbuf_addstr(&buf, options.upstream_name);
|
|
|
|
strbuf_addstr(&buf, "...");
|
|
|
|
strbuf_addstr(&buf, branch_name);
|
2023-02-06 19:08:10 +00:00
|
|
|
options.onto_name = keep_base_onto_name = xstrdup(buf.buf);
|
2022-04-21 04:42:33 +00:00
|
|
|
} else if (!options.onto_name)
|
|
|
|
options.onto_name = options.upstream_name;
|
|
|
|
if (strstr(options.onto_name, "...")) {
|
2023-03-28 13:58:46 +00:00
|
|
|
if (repo_get_oid_mb(the_repository, options.onto_name, &branch_base) < 0) {
|
2022-04-21 04:42:33 +00:00
|
|
|
if (keep_base)
|
|
|
|
die(_("'%s': need exactly one merge base with branch"),
|
|
|
|
options.upstream_name);
|
|
|
|
else
|
|
|
|
die(_("'%s': need exactly one merge base"),
|
|
|
|
options.onto_name);
|
|
|
|
}
|
2022-10-17 13:17:42 +00:00
|
|
|
options.onto = lookup_commit_or_die(&branch_base,
|
2022-04-21 04:42:33 +00:00
|
|
|
options.onto_name);
|
|
|
|
} else {
|
|
|
|
options.onto =
|
|
|
|
lookup_commit_reference_by_name(options.onto_name);
|
|
|
|
if (!options.onto)
|
|
|
|
die(_("Does not point to a valid commit '%s'"),
|
|
|
|
options.onto_name);
|
2022-10-17 13:17:43 +00:00
|
|
|
fill_branch_base(&options, &branch_base);
|
2022-04-21 04:42:33 +00:00
|
|
|
}
|
|
|
|
|
2022-10-17 13:17:44 +00:00
|
|
|
if (keep_base && options.reapply_cherry_picks)
|
|
|
|
options.upstream = options.onto;
|
|
|
|
|
2022-10-17 13:17:41 +00:00
|
|
|
if (options.fork_point > 0)
|
2018-09-04 22:00:09 +00:00
|
|
|
options.restrict_revision =
|
2022-10-17 13:17:41 +00:00
|
|
|
get_fork_point(options.upstream_name, options.orig_head);
|
2018-09-04 22:00:09 +00:00
|
|
|
|
2019-01-12 02:13:26 +00:00
|
|
|
if (repo_read_index(the_repository) < 0)
|
2018-09-04 21:27:14 +00:00
|
|
|
die(_("could not read index"));
|
|
|
|
|
2022-01-26 13:05:44 +00:00
|
|
|
if (options.autostash)
|
|
|
|
create_autostash(the_repository,
|
|
|
|
state_dir_path("autostash", &options));
|
|
|
|
|
2018-09-04 22:00:02 +00:00
|
|
|
|
2018-11-10 05:48:49 +00:00
|
|
|
if (require_clean_work_tree(the_repository, "rebase",
|
2018-09-04 21:27:14 +00:00
|
|
|
_("Please commit or stash them."), 1, 1)) {
|
2021-09-21 10:24:05 +00:00
|
|
|
ret = -1;
|
2018-09-04 21:27:14 +00:00
|
|
|
goto cleanup;
|
2018-08-06 19:31:11 +00:00
|
|
|
}
|
|
|
|
|
2018-09-04 21:27:16 +00:00
|
|
|
/*
|
|
|
|
* Now we are rebasing commits upstream..orig_head (or with --root,
|
|
|
|
* everything leading up to orig_head) on top of onto.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check if we are already based on onto with linear history,
|
rebase: fast-forward --onto in more cases
Before, when we had the following graph,
A---B---C (master)
\
D (side)
running 'git rebase --onto master... master side' would result in D
being always rebased, no matter what. However, the desired behavior is
that rebase should notice that this is fast-forwardable and do that
instead.
Add detection to `can_fast_forward` so that this case can be detected
and a fast-forward will be performed. First of all, rewrite the function
to use gotos which simplifies the logic. Next, since the
options.upstream &&
!oidcmp(&options.upstream->object.oid, &options.onto->object.oid)
conditions were removed in `cmd_rebase`, we reintroduce a substitute in
`can_fast_forward`. In particular, checking the merge bases of
`upstream` and `head` fixes a failing case in t3416.
The abbreviated graph for t3416 is as follows:
F---G topic
/
A---B---C---D---E master
and the failing command was
git rebase --onto master...topic F topic
Before, Git would see that there was one merge base (C), and the merge
and onto were the same so it would incorrectly return 1, indicating that
we could fast-forward. This would cause the rebased graph to be 'ABCFG'
when we were expecting 'ABCG'.
With the additional logic, we detect that upstream and head's merge base
is F. Since onto isn't F, it means we're not rebasing the full set of
commits from master..topic. Since we're excluding some commits, a
fast-forward cannot be performed and so we correctly return 0.
Add '-f' to test cases that failed as a result of this change because
they were not expecting a fast-forward so that a rebase is forced.
Helped-by: Phillip Wood <phillip.wood@dunelm.org.uk>
Signed-off-by: Denton Liu <liu.denton@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-08-27 05:37:59 +00:00
|
|
|
* in which case we could fast-forward without replacing the commits
|
2020-02-15 21:36:31 +00:00
|
|
|
* with new commits recreated by replaying their changes.
|
2018-09-04 21:27:16 +00:00
|
|
|
*/
|
2022-10-17 13:17:43 +00:00
|
|
|
if (allow_preemptive_ff &&
|
|
|
|
can_fast_forward(options.onto, options.upstream, options.restrict_revision,
|
|
|
|
options.orig_head, &branch_base)) {
|
2018-09-04 21:27:16 +00:00
|
|
|
int flag;
|
|
|
|
|
2018-09-04 21:27:17 +00:00
|
|
|
if (!(options.flags & REBASE_FORCE)) {
|
2018-09-04 21:27:21 +00:00
|
|
|
/* Lazily switch to the target branch if needed... */
|
|
|
|
if (options.switch_to) {
|
2022-01-26 13:05:36 +00:00
|
|
|
ret = checkout_up_to_date(&options);
|
|
|
|
if (ret)
|
2018-09-04 21:27:21 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2018-09-04 21:27:17 +00:00
|
|
|
if (!(options.flags & REBASE_NO_QUIET))
|
|
|
|
; /* be quiet */
|
|
|
|
else if (!strcmp(branch_name, "HEAD") &&
|
|
|
|
resolve_ref_unsafe("HEAD", 0, NULL, &flag))
|
|
|
|
puts(_("HEAD is up to date."));
|
|
|
|
else
|
|
|
|
printf(_("Current branch %s is up to date.\n"),
|
|
|
|
branch_name);
|
2021-09-21 10:24:05 +00:00
|
|
|
ret = finish_rebase(&options);
|
2018-09-04 21:27:17 +00:00
|
|
|
goto cleanup;
|
|
|
|
} else if (!(options.flags & REBASE_NO_QUIET))
|
2018-09-04 21:27:16 +00:00
|
|
|
; /* be quiet */
|
|
|
|
else if (!strcmp(branch_name, "HEAD") &&
|
|
|
|
resolve_ref_unsafe("HEAD", 0, NULL, &flag))
|
|
|
|
puts(_("HEAD is up to date, rebase forced."));
|
|
|
|
else
|
|
|
|
printf(_("Current branch %s is up to date, rebase "
|
|
|
|
"forced.\n"), branch_name);
|
|
|
|
}
|
|
|
|
|
2018-09-04 21:27:10 +00:00
|
|
|
/* If a hook exists, give it a chance to interrupt*/
|
|
|
|
if (!ok_to_skip_pre_rebase &&
|
2021-12-22 03:59:32 +00:00
|
|
|
run_hooks_l("pre-rebase", options.upstream_arg,
|
2018-09-04 21:27:10 +00:00
|
|
|
argc ? argv[0] : NULL, NULL))
|
|
|
|
die(_("The pre-rebase hook refused to rebase."));
|
|
|
|
|
2018-09-04 21:27:13 +00:00
|
|
|
if (options.flags & REBASE_DIFFSTAT) {
|
|
|
|
struct diff_options opts;
|
|
|
|
|
2018-11-29 13:01:54 +00:00
|
|
|
if (options.flags & REBASE_VERBOSE) {
|
2022-10-17 13:17:42 +00:00
|
|
|
if (is_null_oid(&branch_base))
|
2018-11-29 13:01:54 +00:00
|
|
|
printf(_("Changes to %s:\n"),
|
|
|
|
oid_to_hex(&options.onto->object.oid));
|
|
|
|
else
|
|
|
|
printf(_("Changes from %s to %s:\n"),
|
2022-10-17 13:17:42 +00:00
|
|
|
oid_to_hex(&branch_base),
|
2018-11-29 13:01:54 +00:00
|
|
|
oid_to_hex(&options.onto->object.oid));
|
|
|
|
}
|
2018-09-04 21:27:13 +00:00
|
|
|
|
|
|
|
/* We want color (if set), but no pager */
|
2023-03-28 13:58:49 +00:00
|
|
|
repo_diff_setup(the_repository, &opts);
|
2018-09-04 21:27:13 +00:00
|
|
|
opts.stat_width = -1; /* use full terminal width */
|
|
|
|
opts.stat_graph_width = -1; /* respect statGraphWidth config */
|
|
|
|
opts.output_format |=
|
|
|
|
DIFF_FORMAT_SUMMARY | DIFF_FORMAT_DIFFSTAT;
|
|
|
|
opts.detect_rename = DIFF_DETECT_RENAME;
|
|
|
|
diff_setup_done(&opts);
|
2022-10-17 13:17:42 +00:00
|
|
|
diff_tree_oid(is_null_oid(&branch_base) ?
|
|
|
|
the_hash_algo->empty_tree : &branch_base,
|
2018-11-29 13:01:54 +00:00
|
|
|
&options.onto->object.oid, "", &opts);
|
2018-09-04 21:27:13 +00:00
|
|
|
diffcore_std(&opts);
|
|
|
|
diff_flush(&opts);
|
|
|
|
}
|
|
|
|
|
2020-02-15 21:36:41 +00:00
|
|
|
if (is_merge(&options))
|
2018-09-04 21:59:49 +00:00
|
|
|
goto run_rebase;
|
|
|
|
|
2018-09-04 21:27:13 +00:00
|
|
|
/* Detach HEAD and reset the tree */
|
|
|
|
if (options.flags & REBASE_NO_QUIET)
|
|
|
|
printf(_("First, rewinding head to replay your work on top of "
|
|
|
|
"it...\n"));
|
|
|
|
|
2022-10-12 09:35:10 +00:00
|
|
|
strbuf_addf(&msg, "%s (start): checkout %s",
|
2022-11-09 14:21:58 +00:00
|
|
|
options.reflog_action, options.onto_name);
|
2022-01-26 13:05:46 +00:00
|
|
|
ropts.oid = &options.onto->object.oid;
|
2022-10-17 13:17:41 +00:00
|
|
|
ropts.orig_head = &options.orig_head->object.oid,
|
2022-01-26 13:05:46 +00:00
|
|
|
ropts.flags = RESET_HEAD_DETACH | RESET_ORIG_HEAD |
|
|
|
|
RESET_HEAD_RUN_POST_CHECKOUT_HOOK;
|
|
|
|
ropts.head_msg = msg.buf;
|
2022-11-09 14:21:58 +00:00
|
|
|
ropts.default_reflog_action = options.reflog_action;
|
2022-01-26 13:05:46 +00:00
|
|
|
if (reset_head(the_repository, &ropts))
|
2018-08-06 19:31:11 +00:00
|
|
|
die(_("Could not detach HEAD"));
|
|
|
|
strbuf_release(&msg);
|
|
|
|
|
2018-08-08 15:36:32 +00:00
|
|
|
/*
|
|
|
|
* If the onto is a proper descendant of the tip of the branch, then
|
|
|
|
* we just fast-forwarded.
|
|
|
|
*/
|
2022-10-17 13:17:42 +00:00
|
|
|
if (oideq(&branch_base, &options.orig_head->object.oid)) {
|
2018-11-30 18:11:45 +00:00
|
|
|
printf(_("Fast-forwarded %s to %s.\n"),
|
2018-08-08 15:36:32 +00:00
|
|
|
branch_name, options.onto_name);
|
2022-10-12 09:35:05 +00:00
|
|
|
move_to_original_branch(&options);
|
2021-09-21 10:24:05 +00:00
|
|
|
ret = finish_rebase(&options);
|
2018-08-08 15:36:32 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2018-08-06 19:31:11 +00:00
|
|
|
strbuf_addf(&revisions, "%s..%s",
|
|
|
|
options.root ? oid_to_hex(&options.onto->object.oid) :
|
|
|
|
(options.restrict_revision ?
|
|
|
|
oid_to_hex(&options.restrict_revision->object.oid) :
|
|
|
|
oid_to_hex(&options.upstream->object.oid)),
|
2022-10-17 13:17:41 +00:00
|
|
|
oid_to_hex(&options.orig_head->object.oid));
|
2018-08-06 19:31:11 +00:00
|
|
|
|
|
|
|
options.revisions = revisions.buf;
|
|
|
|
|
2018-08-08 15:06:16 +00:00
|
|
|
run_rebase:
|
2022-10-12 09:35:12 +00:00
|
|
|
ret = run_specific_rebase(&options);
|
2018-08-06 19:31:11 +00:00
|
|
|
|
2018-09-04 21:27:14 +00:00
|
|
|
cleanup:
|
2019-05-14 18:03:46 +00:00
|
|
|
strbuf_release(&buf);
|
2018-08-06 19:31:11 +00:00
|
|
|
strbuf_release(&revisions);
|
2022-11-09 14:21:58 +00:00
|
|
|
free(options.reflog_action);
|
2018-08-06 19:31:11 +00:00
|
|
|
free(options.head_name);
|
built-ins & libs & helpers: add/move destructors, fix leaks
Fix various leaks in built-ins, libraries and a test helper here we
were missing a call to strbuf_release(), string_list_clear() etc, or
were calling them after a potential "return".
Comments on individual changes:
- builtin/checkout.c: Fix a memory leak that was introduced in [1]. A
sibling leak introduced in [2] was recently fixed in [3]. As with [3]
we should be using the wt_status_state_free_buffers() API introduced
in [4].
- builtin/repack.c: Fix a leak that's been here since this use of
"strbuf_release()" was added in a1bbc6c0176 (repack: rewrite the shell
script in C, 2013-09-15). We don't use the variable for anything
except this loop, so we can instead free it right afterwards.
- builtin/rev-parse: Fix a leak that's been here since this code was
added in 21d47835386 (Add a parseopt mode to git-rev-parse to bring
parse-options to shell scripts., 2007-11-04).
- builtin/stash.c: Fix a couple of leaks that have been here since
this code was added in d4788af875c (stash: convert create to builtin,
2019-02-25), we strbuf_release()'d only some of the "struct strbuf" we
allocated earlier in the function, let's release all of them.
- ref-filter.c: Fix a leak in 482c1191869 (gpg-interface: improve
interface for parsing tags, 2021-02-11), we don't use the "payload"
variable that we ask parse_signature() to populate for us, so let's
free it.
- t/helper/test-fake-ssh.c: Fix a leak that's been here since this
code was added in 3064d5a38c7 (mingw: fix t5601-clone.sh,
2016-01-27). Let's free the "struct strbuf" as soon as we don't need
it anymore.
1. c45f0f525de (switch: reject if some operation is in progress,
2019-03-29)
2. 2708ce62d21 (branch: sort detached HEAD based on a flag,
2021-01-07)
3. abcac2e19fa (ref-filter.c: fix a leak in get_head_description,
2022-09-25)
4. 962dd7ebc3e (wt-status: introduce wt_status_state_free_buffers(),
2020-09-27).
Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Signed-off-by: Taylor Blau <me@ttaylorr.com>
2022-11-08 18:17:42 +00:00
|
|
|
strvec_clear(&options.git_am_opts);
|
2018-09-04 22:00:00 +00:00
|
|
|
free(options.gpg_sign_opt);
|
2023-01-12 16:50:01 +00:00
|
|
|
string_list_clear(&options.exec, 0);
|
2021-07-25 13:08:29 +00:00
|
|
|
free(options.strategy);
|
2023-04-10 09:08:29 +00:00
|
|
|
string_list_clear(&options.strategy_opts, 0);
|
2021-04-25 14:16:18 +00:00
|
|
|
strbuf_release(&options.git_format_patch_opt);
|
2018-09-04 22:00:12 +00:00
|
|
|
free(squash_onto_name);
|
2023-02-06 19:08:10 +00:00
|
|
|
free(keep_base_onto_name);
|
2021-09-21 10:24:05 +00:00
|
|
|
return !!ret;
|
2018-08-06 19:31:09 +00:00
|
|
|
}
|