git-commit-vandalism/builtin/rebase.c

1831 lines
52 KiB
C
Raw Normal View History

/*
* "git rebase" builtin command
*
* Copyright (c) 2018 Pratik Karki
*/
#define USE_THE_INDEX_COMPATIBILITY_MACROS
#include "builtin.h"
#include "run-command.h"
#include "exec-cmd.h"
#include "strvec.h"
#include "dir.h"
#include "packfile.h"
#include "refs.h"
#include "quote.h"
#include "config.h"
#include "cache-tree.h"
#include "unpack-trees.h"
#include "lockfile.h"
#include "parse-options.h"
#include "commit.h"
#include "diff.h"
#include "wt-status.h"
#include "revision.h"
#include "commit-reach.h"
#include "rerere.h"
#include "branch.h"
#include "sequencer.h"
#include "rebase-interactive.h"
#include "reset.h"
#include "hook.h"
#define DEFAULT_REFLOG_ACTION "rebase"
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 07:38:06 +02:00
N_("git rebase [-i] [options] [--exec <cmd>] "
"[--onto <newbase> | --keep-base] [<upstream> [<branch>]]"),
N_("git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] "
"--root [<branch>]"),
"git rebase --continue | --abort | --skip | --edit-todo",
NULL
};
static GIT_PATH_FUNC(path_squash_onto, "rebase-merge/squash-onto")
static GIT_PATH_FUNC(path_interactive, "rebase-merge/interactive")
static GIT_PATH_FUNC(apply_dir, "rebase-apply")
static GIT_PATH_FUNC(merge_dir, "rebase-merge")
enum rebase_type {
REBASE_UNSPECIFIED = -1,
rebase: rename the two primary rebase backends Two related changes, with separate rationale for each: Rename the 'interactive' backend to 'merge' because: * 'interactive' as a name caused confusion; this backend has been used for many kinds of non-interactive rebases, and will probably be used in the future for more non-interactive rebases than interactive ones given that we are making it the default. * 'interactive' is not the underlying strategy; merging is. * the directory where state is stored is not called .git/rebase-interactive but .git/rebase-merge. Rename the 'am' backend to 'apply' because: * Few users are familiar with git-am as a reference point. * Related to the above, the name 'am' makes sentences in the documentation harder for users to read and comprehend (they may read it as the verb from "I am"); avoiding this difficult places a large burden on anyone writing documentation about this backend to be very careful with quoting and sentence structure and often forces annoying redundancy to try to avoid such problems. * Users stumble over pronunciation ("am" as in "I am a person not a backend" or "am" as in "the first and thirteenth letters in the alphabet in order are "A-M"); this may drive confusion when one user tries to explain to another what they are doing. * While "am" is the tool driving this backend, the tool driving git-am is git-apply, and since we are driving towards lower-level tools for the naming of the merge backend we may as well do so here too. * The directory where state is stored has never been called .git/rebase-am, it was always called .git/rebase-apply. For all the reasons listed above: * Modify the documentation to refer to the backends with the new names * Provide a brief note in the documentation connecting the new names to the old names in case users run across the old names anywhere (e.g. in old release notes or older versions of the documentation) * Change the (new) --am command line flag to --apply * Rename some enums, variables, and functions to reinforce the new backend names for us as well. Signed-off-by: Elijah Newren <newren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-02-15 22:36:41 +01:00
REBASE_APPLY,
REBASE_MERGE
};
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 22:36:25 +01:00
enum empty_type {
EMPTY_UNSPECIFIED = -1,
EMPTY_DROP,
EMPTY_KEEP,
EMPTY_ASK
};
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 22:36:25 +01:00
enum empty_type empty;
const char *default_backend;
const char *state_dir;
struct commit *upstream;
const char *upstream_name;
const char *upstream_arg;
char *head_name;
struct object_id orig_head;
struct commit *onto;
const char *onto_name;
const char *revisions;
const char *switch_to;
int root, root_with_onto;
struct object_id *squash_onto;
struct commit *restrict_revision;
int dont_finish_rebase;
enum {
REBASE_NO_QUIET = 1<<0,
REBASE_VERBOSE = 1<<1,
REBASE_DIFFSTAT = 1<<2,
REBASE_FORCE = 1<<3,
REBASE_INTERACTIVE_EXPLICIT = 1<<4,
} flags;
struct strvec git_am_opts;
const char *action;
int signoff;
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 04:44:25 +02:00
int keep_empty;
int autosquash;
char *gpg_sign_opt;
int autostash;
int committer_date_is_author_date;
int ignore_date;
char *cmd;
int allow_empty_message;
int rebase_merges, rebase_cousins;
char *strategy, *strategy_opts;
struct strbuf git_format_patch_opt;
int reschedule_failed_exec;
int reapply_cherry_picks;
int fork_point;
};
#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 22:36:25 +01: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 04:44:25 +02:00
.keep_empty = 1, \
.default_backend = "merge", \
.flags = REBASE_NO_QUIET, \
.git_am_opts = STRVEC_INIT, \
.git_format_patch_opt = STRBUF_INIT, \
.fork_point = -1, \
}
static struct replay_opts get_replay_opts(const struct rebase_options *opts)
{
struct replay_opts replay = REPLAY_OPTS_INIT;
replay.action = REPLAY_INTERACTIVE_REBASE;
replay.strategy = NULL;
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 22:36:25 +01:00
replay.drop_redundant_commits = (opts->empty == EMPTY_DROP);
replay.keep_redundant_commits = (opts->empty == EMPTY_KEEP);
replay.quiet = !(opts->flags & REBASE_NO_QUIET);
replay.verbose = opts->flags & REBASE_VERBOSE;
replay.reschedule_failed_exec = opts->reschedule_failed_exec;
replay.committer_date_is_author_date =
opts->committer_date_is_author_date;
replay.ignore_date = opts->ignore_date;
replay.gpg_sign = xstrdup_or_null(opts->gpg_sign_opt);
if (opts->strategy)
builtin/rebase: fix options.strategy memory lifecycle - cmd_rebase populates rebase_options.strategy with newly allocated strings, hence we need to free those strings at the end of cmd_rebase to avoid a leak. - In some cases: get_replay_opts() is called, which prepares replay_opts using data from rebase_options. We used to simply copy the pointer from rebase_options.strategy, however that would now result in a double-free because sequencer_remove_state() is eventually used to free replay_opts.strategy. To avoid this we xstrdup() strategy when adding it to replay_opts. The original leak happens because we always populate rebase_options.strategy, but we don't always enter the path that calls get_replay_opts() and later sequencer_remove_state() - in other words we'd always allocate a new string into rebase_options.strategy but only sometimes did we free it. We now make sure that rebase_options and replay_opts both own their own copies of strategy, and each copy is free'd independently. This was first seen when running t0021 with LSAN, but t2012 helped catch the fact that we can't just free(options.strategy) at the end of cmd_rebase (as that can cause a double-free). LSAN output from t0021: LSAN output from t0021: Direct leak of 4 byte(s) in 1 object(s) allocated from: #0 0x486804 in strdup ../projects/compiler-rt/lib/asan/asan_interceptors.cpp:452:3 #1 0xa71eb8 in xstrdup wrapper.c:29:14 #2 0x61b1cc in cmd_rebase builtin/rebase.c:1779:22 #3 0x4ce83e in run_builtin git.c:475:11 #4 0x4ccafe in handle_builtin git.c:729:3 #5 0x4cb01c in run_argv git.c:818:4 #6 0x4cb01c in cmd_main git.c:949:19 #7 0x6b3fad in main common-main.c:52:11 #8 0x7f267b512349 in __libc_start_main (/lib64/libc.so.6+0x24349) SUMMARY: AddressSanitizer: 4 byte(s) leaked in 1 allocation(s). Signed-off-by: Andrzej Hunt <andrzej@ahunt.org> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-07-25 15:08:29 +02:00
replay.strategy = xstrdup_or_null(opts->strategy);
else if (!replay.strategy && replay.default_strategy) {
replay.strategy = replay.default_strategy;
replay.default_strategy = NULL;
}
if (opts->strategy_opts)
parse_strategy_opts(&replay, opts->strategy_opts);
if (opts->squash_onto) {
oidcpy(&replay.squash_onto, opts->squash_onto);
replay.have_squash_onto = 1;
}
return replay;
}
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" };
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;
}
static int get_revision_ranges(struct commit *upstream, struct commit *onto,
struct object_id *orig_head, char **revisions,
char **shortrevisions)
{
struct commit *base_rev = upstream ? upstream : onto;
const char *shorthead;
*revisions = xstrfmt("%s...%s", oid_to_hex(&base_rev->object.oid),
oid_to_hex(orig_head));
rebase -i: stop checking out the tip of the branch to rebase One of the first things done when using a sequencer-based rebase (ie. `rebase -i', `rebase -r', or `rebase -m') is to make a todo list. This requires knowledge of the commit range to rebase. To get the oid of the last commit of the range, the tip of the branch to rebase is checked out with prepare_branch_to_be_rebased(), then the oid of the head is read. After this, the tip of the branch is not even modified. The `am' backend, on the other hand, does not check out the branch. On big repositories, it's a performance penalty: with `rebase -i', the user may have to wait before editing the todo list while git is extracting the branch silently, and "quiet" rebases will be slower than `am'. Since we already have the oid of the tip of the branch in `opts->orig_head', it's useless to switch to this commit. This removes the call to prepare_branch_to_be_rebased() in do_interactive_rebase(), and adds a `orig_head' parameter to get_revision_ranges(). prepare_branch_to_be_rebased() is removed as it is no longer used. This introduces a visible change: as we do not switch on the tip of the branch to rebase, no reflog entry is created at the beginning of the rebase for it. Unscientific performance measurements, performed on linux.git, are as follow: Before this patch: $ time git rebase -m --onto v4.18 463fa44eec2fef50~ 463fa44eec2fef50 real 0m8,940s user 0m6,830s sys 0m2,121s After this patch: $ time git rebase -m --onto v4.18 463fa44eec2fef50~ 463fa44eec2fef50 real 0m1,834s user 0m0,916s sys 0m0,206s Reported-by: SZEDER Gábor <szeder.dev@gmail.com> Signed-off-by: Alban Gruin <alban.gruin@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-01-24 16:05:00 +01:00
shorthead = find_unique_abbrev(orig_head, DEFAULT_ABBREV);
if (upstream) {
const char *shortrev;
shortrev = find_unique_abbrev(&base_rev->object.oid,
DEFAULT_ABBREV);
*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,
struct commit *onto,
const struct object_id *orig_head)
{
FILE *interactive;
if (!is_directory(merge_dir()) && mkdir_in_gitdir(merge_dir()))
return error_errno(_("could not create temporary %s"), merge_dir());
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);
}
static void split_exec_commands(const char *cmd, struct string_list *commands)
{
if (cmd && *cmd) {
string_list_split(commands, cmd, '\n', -1);
/* rebase.c adds a new line to cmd after every command,
* so here the last command is always empty */
string_list_remove_empty_items(commands, 0);
}
}
static int do_interactive_rebase(struct rebase_options *opts, unsigned flags)
{
int ret;
char *revisions = NULL, *shortrevisions = NULL;
struct strvec make_script_args = STRVEC_INIT;
struct todo_list todo_list = TODO_LIST_INIT;
struct replay_opts replay = get_replay_opts(opts);
struct string_list commands = STRING_LIST_INIT_DUP;
rebase -i: stop checking out the tip of the branch to rebase One of the first things done when using a sequencer-based rebase (ie. `rebase -i', `rebase -r', or `rebase -m') is to make a todo list. This requires knowledge of the commit range to rebase. To get the oid of the last commit of the range, the tip of the branch to rebase is checked out with prepare_branch_to_be_rebased(), then the oid of the head is read. After this, the tip of the branch is not even modified. The `am' backend, on the other hand, does not check out the branch. On big repositories, it's a performance penalty: with `rebase -i', the user may have to wait before editing the todo list while git is extracting the branch silently, and "quiet" rebases will be slower than `am'. Since we already have the oid of the tip of the branch in `opts->orig_head', it's useless to switch to this commit. This removes the call to prepare_branch_to_be_rebased() in do_interactive_rebase(), and adds a `orig_head' parameter to get_revision_ranges(). prepare_branch_to_be_rebased() is removed as it is no longer used. This introduces a visible change: as we do not switch on the tip of the branch to rebase, no reflog entry is created at the beginning of the rebase for it. Unscientific performance measurements, performed on linux.git, are as follow: Before this patch: $ time git rebase -m --onto v4.18 463fa44eec2fef50~ 463fa44eec2fef50 real 0m8,940s user 0m6,830s sys 0m2,121s After this patch: $ time git rebase -m --onto v4.18 463fa44eec2fef50~ 463fa44eec2fef50 real 0m1,834s user 0m0,916s sys 0m0,206s Reported-by: SZEDER Gábor <szeder.dev@gmail.com> Signed-off-by: Alban Gruin <alban.gruin@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-01-24 16:05:00 +01:00
if (get_revision_ranges(opts->upstream, opts->onto, &opts->orig_head,
&revisions, &shortrevisions))
return -1;
if (init_basic_state(&replay,
opts->head_name ? opts->head_name : "detached HEAD",
opts->onto, &opts->orig_head)) {
free(revisions);
free(shortrevisions);
return -1;
}
if (!opts->upstream && opts->squash_onto)
write_file(path_squash_onto(), "%s\n",
oid_to_hex(opts->squash_onto));
strvec_pushl(&make_script_args, "", revisions, NULL);
if (opts->restrict_revision)
strvec_pushf(&make_script_args, "^%s",
oid_to_hex(&opts->restrict_revision->object.oid));
ret = sequencer_make_script(the_repository, &todo_list.buf,
make_script_args.nr, make_script_args.v,
flags);
if (ret)
error(_("could not generate todo list"));
else {
discard_cache();
if (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf,
&todo_list))
BUG("unusable todo list");
split_exec_commands(opts->cmd, &commands);
ret = complete_action(the_repository, &replay, flags,
shortrevisions, opts->onto_name, opts->onto,
&opts->orig_head, &commands, opts->autosquash,
&todo_list);
}
string_list_clear(&commands, 0);
free(revisions);
free(shortrevisions);
todo_list_release(&todo_list);
strvec_clear(&make_script_args);
return ret;
}
rebase: rename the two primary rebase backends Two related changes, with separate rationale for each: Rename the 'interactive' backend to 'merge' because: * 'interactive' as a name caused confusion; this backend has been used for many kinds of non-interactive rebases, and will probably be used in the future for more non-interactive rebases than interactive ones given that we are making it the default. * 'interactive' is not the underlying strategy; merging is. * the directory where state is stored is not called .git/rebase-interactive but .git/rebase-merge. Rename the 'am' backend to 'apply' because: * Few users are familiar with git-am as a reference point. * Related to the above, the name 'am' makes sentences in the documentation harder for users to read and comprehend (they may read it as the verb from "I am"); avoiding this difficult places a large burden on anyone writing documentation about this backend to be very careful with quoting and sentence structure and often forces annoying redundancy to try to avoid such problems. * Users stumble over pronunciation ("am" as in "I am a person not a backend" or "am" as in "the first and thirteenth letters in the alphabet in order are "A-M"); this may drive confusion when one user tries to explain to another what they are doing. * While "am" is the tool driving this backend, the tool driving git-am is git-apply, and since we are driving towards lower-level tools for the naming of the merge backend we may as well do so here too. * The directory where state is stored has never been called .git/rebase-am, it was always called .git/rebase-apply. For all the reasons listed above: * Modify the documentation to refer to the backends with the new names * Provide a brief note in the documentation connecting the new names to the old names in case users run across the old names anywhere (e.g. in old release notes or older versions of the documentation) * Change the (new) --am command line flag to --apply * Rename some enums, variables, and functions to reinforce the new backend names for us as well. Signed-off-by: Elijah Newren <newren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-02-15 22:36:41 +01:00
static int run_sequencer_rebase(struct rebase_options *opts,
enum action command)
{
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 04:44:25 +02:00
flags |= opts->keep_empty ? TODO_LIST_KEEP_EMPTY : 0;
flags |= abbreviate_commands ? TODO_LIST_ABBREVIATE_CMDS : 0;
flags |= opts->rebase_merges ? TODO_LIST_REBASE_MERGES : 0;
flags |= opts->rebase_cousins > 0 ? TODO_LIST_REBASE_COUSINS : 0;
flags |= opts->root_with_onto ? TODO_LIST_ROOT_WITH_ONTO : 0;
flags |= opts->reapply_cherry_picks ? TODO_LIST_REAPPLY_CHERRY_PICKS : 0;
flags |= opts->flags & REBASE_NO_QUIET ? TODO_LIST_WARN_SKIPPED_CHERRY_PICKS : 0;
switch (command) {
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);
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;
strvec_pushl(&cmd.args, "show", "REBASE_HEAD", "--", NULL);
ret = run_command(&cmd);
break;
}
default:
BUG("invalid command '%d'", command);
}
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 04:44:25 +02: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 22:36:24 +01: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 04:44:25 +02:00
imply_merge(opts, unset ? "--no-keep-empty" : "--keep-empty");
opts->keep_empty = !unset;
rebase: rename the two primary rebase backends Two related changes, with separate rationale for each: Rename the 'interactive' backend to 'merge' because: * 'interactive' as a name caused confusion; this backend has been used for many kinds of non-interactive rebases, and will probably be used in the future for more non-interactive rebases than interactive ones given that we are making it the default. * 'interactive' is not the underlying strategy; merging is. * the directory where state is stored is not called .git/rebase-interactive but .git/rebase-merge. Rename the 'am' backend to 'apply' because: * Few users are familiar with git-am as a reference point. * Related to the above, the name 'am' makes sentences in the documentation harder for users to read and comprehend (they may read it as the verb from "I am"); avoiding this difficult places a large burden on anyone writing documentation about this backend to be very careful with quoting and sentence structure and often forces annoying redundancy to try to avoid such problems. * Users stumble over pronunciation ("am" as in "I am a person not a backend" or "am" as in "the first and thirteenth letters in the alphabet in order are "A-M"); this may drive confusion when one user tries to explain to another what they are doing. * While "am" is the tool driving this backend, the tool driving git-am is git-apply, and since we are driving towards lower-level tools for the naming of the merge backend we may as well do so here too. * The directory where state is stored has never been called .git/rebase-am, it was always called .git/rebase-apply. For all the reasons listed above: * Modify the documentation to refer to the backends with the new names * Provide a brief note in the documentation connecting the new names to the old names in case users run across the old names anywhere (e.g. in old release notes or older versions of the documentation) * Change the (new) --am command line flag to --apply * Rename some enums, variables, and functions to reinforce the new backend names for us as well. Signed-off-by: Elijah Newren <newren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-02-15 22:36:41 +01: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 22:36:24 +01:00
return 0;
}
rebase: rename the two primary rebase backends Two related changes, with separate rationale for each: Rename the 'interactive' backend to 'merge' because: * 'interactive' as a name caused confusion; this backend has been used for many kinds of non-interactive rebases, and will probably be used in the future for more non-interactive rebases than interactive ones given that we are making it the default. * 'interactive' is not the underlying strategy; merging is. * the directory where state is stored is not called .git/rebase-interactive but .git/rebase-merge. Rename the 'am' backend to 'apply' because: * Few users are familiar with git-am as a reference point. * Related to the above, the name 'am' makes sentences in the documentation harder for users to read and comprehend (they may read it as the verb from "I am"); avoiding this difficult places a large burden on anyone writing documentation about this backend to be very careful with quoting and sentence structure and often forces annoying redundancy to try to avoid such problems. * Users stumble over pronunciation ("am" as in "I am a person not a backend" or "am" as in "the first and thirteenth letters in the alphabet in order are "A-M"); this may drive confusion when one user tries to explain to another what they are doing. * While "am" is the tool driving this backend, the tool driving git-am is git-apply, and since we are driving towards lower-level tools for the naming of the merge backend we may as well do so here too. * The directory where state is stored has never been called .git/rebase-am, it was always called .git/rebase-apply. For all the reasons listed above: * Modify the documentation to refer to the backends with the new names * Provide a brief note in the documentation connecting the new names to the old names in case users run across the old names anywhere (e.g. in old release notes or older versions of the documentation) * Change the (new) --am command line flag to --apply * Rename some enums, variables, and functions to reinforce the new backend names for us as well. Signed-off-by: Elijah Newren <newren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-02-15 22:36:41 +01:00
static int is_merge(struct rebase_options *opts)
{
return opts->type == REBASE_MERGE;
}
rebase: rename the two primary rebase backends Two related changes, with separate rationale for each: Rename the 'interactive' backend to 'merge' because: * 'interactive' as a name caused confusion; this backend has been used for many kinds of non-interactive rebases, and will probably be used in the future for more non-interactive rebases than interactive ones given that we are making it the default. * 'interactive' is not the underlying strategy; merging is. * the directory where state is stored is not called .git/rebase-interactive but .git/rebase-merge. Rename the 'am' backend to 'apply' because: * Few users are familiar with git-am as a reference point. * Related to the above, the name 'am' makes sentences in the documentation harder for users to read and comprehend (they may read it as the verb from "I am"); avoiding this difficult places a large burden on anyone writing documentation about this backend to be very careful with quoting and sentence structure and often forces annoying redundancy to try to avoid such problems. * Users stumble over pronunciation ("am" as in "I am a person not a backend" or "am" as in "the first and thirteenth letters in the alphabet in order are "A-M"); this may drive confusion when one user tries to explain to another what they are doing. * While "am" is the tool driving this backend, the tool driving git-am is git-apply, and since we are driving towards lower-level tools for the naming of the merge backend we may as well do so here too. * The directory where state is stored has never been called .git/rebase-am, it was always called .git/rebase-apply. For all the reasons listed above: * Modify the documentation to refer to the backends with the new names * Provide a brief note in the documentation connecting the new names to the old names in case users run across the old names anywhere (e.g. in old release notes or older versions of the documentation) * Change the (new) --am command line flag to --apply * Rename some enums, variables, and functions to reinforce the new backend names for us as well. Signed-off-by: Elijah Newren <newren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-02-15 22:36:41 +01:00
static void imply_merge(struct rebase_options *opts, const char *option)
{
switch (opts->type) {
rebase: rename the two primary rebase backends Two related changes, with separate rationale for each: Rename the 'interactive' backend to 'merge' because: * 'interactive' as a name caused confusion; this backend has been used for many kinds of non-interactive rebases, and will probably be used in the future for more non-interactive rebases than interactive ones given that we are making it the default. * 'interactive' is not the underlying strategy; merging is. * the directory where state is stored is not called .git/rebase-interactive but .git/rebase-merge. Rename the 'am' backend to 'apply' because: * Few users are familiar with git-am as a reference point. * Related to the above, the name 'am' makes sentences in the documentation harder for users to read and comprehend (they may read it as the verb from "I am"); avoiding this difficult places a large burden on anyone writing documentation about this backend to be very careful with quoting and sentence structure and often forces annoying redundancy to try to avoid such problems. * Users stumble over pronunciation ("am" as in "I am a person not a backend" or "am" as in "the first and thirteenth letters in the alphabet in order are "A-M"); this may drive confusion when one user tries to explain to another what they are doing. * While "am" is the tool driving this backend, the tool driving git-am is git-apply, and since we are driving towards lower-level tools for the naming of the merge backend we may as well do so here too. * The directory where state is stored has never been called .git/rebase-am, it was always called .git/rebase-apply. For all the reasons listed above: * Modify the documentation to refer to the backends with the new names * Provide a brief note in the documentation connecting the new names to the old names in case users run across the old names anywhere (e.g. in old release notes or older versions of the documentation) * Change the (new) --am command line flag to --apply * Rename some enums, variables, and functions to reinforce the new backend names for us as well. Signed-off-by: Elijah Newren <newren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-02-15 22:36:41 +01:00
case REBASE_APPLY:
die(_("%s requires the merge backend"), option);
break;
rebase: rename the two primary rebase backends Two related changes, with separate rationale for each: Rename the 'interactive' backend to 'merge' because: * 'interactive' as a name caused confusion; this backend has been used for many kinds of non-interactive rebases, and will probably be used in the future for more non-interactive rebases than interactive ones given that we are making it the default. * 'interactive' is not the underlying strategy; merging is. * the directory where state is stored is not called .git/rebase-interactive but .git/rebase-merge. Rename the 'am' backend to 'apply' because: * Few users are familiar with git-am as a reference point. * Related to the above, the name 'am' makes sentences in the documentation harder for users to read and comprehend (they may read it as the verb from "I am"); avoiding this difficult places a large burden on anyone writing documentation about this backend to be very careful with quoting and sentence structure and often forces annoying redundancy to try to avoid such problems. * Users stumble over pronunciation ("am" as in "I am a person not a backend" or "am" as in "the first and thirteenth letters in the alphabet in order are "A-M"); this may drive confusion when one user tries to explain to another what they are doing. * While "am" is the tool driving this backend, the tool driving git-am is git-apply, and since we are driving towards lower-level tools for the naming of the merge backend we may as well do so here too. * The directory where state is stored has never been called .git/rebase-am, it was always called .git/rebase-apply. For all the reasons listed above: * Modify the documentation to refer to the backends with the new names * Provide a brief note in the documentation connecting the new names to the old names in case users run across the old names anywhere (e.g. in old release notes or older versions of the documentation) * Change the (new) --am command line flag to --apply * Rename some enums, variables, and functions to reinforce the new backend names for us as well. Signed-off-by: Elijah Newren <newren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-02-15 22:36:41 +01:00
case REBASE_MERGE:
break;
default:
rebase: rename the two primary rebase backends Two related changes, with separate rationale for each: Rename the 'interactive' backend to 'merge' because: * 'interactive' as a name caused confusion; this backend has been used for many kinds of non-interactive rebases, and will probably be used in the future for more non-interactive rebases than interactive ones given that we are making it the default. * 'interactive' is not the underlying strategy; merging is. * the directory where state is stored is not called .git/rebase-interactive but .git/rebase-merge. Rename the 'am' backend to 'apply' because: * Few users are familiar with git-am as a reference point. * Related to the above, the name 'am' makes sentences in the documentation harder for users to read and comprehend (they may read it as the verb from "I am"); avoiding this difficult places a large burden on anyone writing documentation about this backend to be very careful with quoting and sentence structure and often forces annoying redundancy to try to avoid such problems. * Users stumble over pronunciation ("am" as in "I am a person not a backend" or "am" as in "the first and thirteenth letters in the alphabet in order are "A-M"); this may drive confusion when one user tries to explain to another what they are doing. * While "am" is the tool driving this backend, the tool driving git-am is git-apply, and since we are driving towards lower-level tools for the naming of the merge backend we may as well do so here too. * The directory where state is stored has never been called .git/rebase-am, it was always called .git/rebase-apply. For all the reasons listed above: * Modify the documentation to refer to the backends with the new names * Provide a brief note in the documentation connecting the new names to the old names in case users run across the old names anywhere (e.g. in old release notes or older versions of the documentation) * Change the (new) --am command line flag to --apply * Rename some enums, variables, and functions to reinforce the new backend names for us as well. Signed-off-by: Elijah Newren <newren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-02-15 22:36:41 +01:00
opts->type = REBASE_MERGE; /* implied */
break;
}
}
/* 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;
}
/* 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;
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))
return -1;
opts->head_name = starts_with(head_name.buf, "refs/") ?
xstrdup(head_name.buf) : NULL;
strbuf_release(&head_name);
if (get_oid(buf.buf, &oid))
return error(_("could not get 'onto': '%s'"), buf.buf);
opts->onto = lookup_commit_or_die(&oid, buf.buf);
/*
* 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))) {
if (!read_oneliner(&buf, state_dir_path("orig-head", opts),
READ_ONELINER_WARN_MISSING))
return -1;
} else if (!read_oneliner(&buf, state_dir_path("head", opts),
READ_ONELINER_WARN_MISSING))
return -1;
if (get_oid(buf.buf, &opts->orig_head))
return error(_("invalid orig-head: '%s'"), buf.buf);
if (file_exists(state_dir_path("quiet", opts)))
opts->flags &= ~REBASE_NO_QUIET;
else
opts->flags |= REBASE_NO_QUIET;
if (file_exists(state_dir_path("verbose", opts)))
opts->flags |= REBASE_VERBOSE;
if (file_exists(state_dir_path("signoff", opts))) {
opts->signoff = 1;
opts->flags |= REBASE_FORCE;
}
if (file_exists(state_dir_path("allow_rerere_autoupdate", opts))) {
strbuf_reset(&buf);
if (!read_oneliner(&buf, state_dir_path("allow_rerere_autoupdate", opts),
READ_ONELINER_WARN_MISSING))
return -1;
if (!strcmp(buf.buf, "--rerere-autoupdate"))
opts->allow_rerere_autoupdate = RERERE_AUTOUPDATE;
else if (!strcmp(buf.buf, "--no-rerere-autoupdate"))
opts->allow_rerere_autoupdate = RERERE_NOAUTOUPDATE;
else
warning(_("ignoring invalid allow_rerere_autoupdate: "
"'%s'"), buf.buf);
}
if (file_exists(state_dir_path("gpg_sign_opt", opts))) {
strbuf_reset(&buf);
if (!read_oneliner(&buf, state_dir_path("gpg_sign_opt", opts),
READ_ONELINER_WARN_MISSING))
return -1;
free(opts->gpg_sign_opt);
opts->gpg_sign_opt = xstrdup(buf.buf);
}
if (file_exists(state_dir_path("strategy", opts))) {
strbuf_reset(&buf);
if (!read_oneliner(&buf, state_dir_path("strategy", opts),
READ_ONELINER_WARN_MISSING))
return -1;
free(opts->strategy);
opts->strategy = xstrdup(buf.buf);
}
if (file_exists(state_dir_path("strategy_opts", opts))) {
strbuf_reset(&buf);
if (!read_oneliner(&buf, state_dir_path("strategy_opts", opts),
READ_ONELINER_WARN_MISSING))
return -1;
free(opts->strategy_opts);
opts->strategy_opts = xstrdup(buf.buf);
}
strbuf_release(&buf);
return 0;
}
static int rebase_write_basic_state(struct rebase_options *opts)
{
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",
oid_to_hex(&opts->orig_head));
if (!(opts->flags & REBASE_NO_QUIET))
write_file(state_dir_path("quiet", opts), "%s", "");
if (opts->flags & REBASE_VERBOSE)
write_file(state_dir_path("verbose", opts), "%s", "");
if (opts->strategy)
write_file(state_dir_path("strategy", opts), "%s",
opts->strategy);
if (opts->strategy_opts)
write_file(state_dir_path("strategy_opts", opts), "%s",
opts->strategy_opts);
if (opts->allow_rerere_autoupdate > 0)
write_file(state_dir_path("allow_rerere_autoupdate", opts),
"-%s-rerere-autoupdate",
opts->allow_rerere_autoupdate == RERERE_AUTOUPDATE ?
"" : "-no");
if (opts->gpg_sign_opt)
write_file(state_dir_path("gpg_sign_opt", opts), "%s",
opts->gpg_sign_opt);
if (opts->signoff)
write_file(state_dir_path("signoff", opts), "--signoff");
return 0;
}
static int finish_rebase(struct rebase_options *opts)
{
struct strbuf dir = STRBUF_INIT;
int ret = 0;
delete_ref(NULL, "REBASE_HEAD", NULL, REF_NO_DEREF);
unlink(git_path_auto_merge(the_repository));
apply_autostash(state_dir_path("autostash", opts));
/*
* We ignore errors in 'git maintenance run --auto', since the
* user should see them.
*/
run_auto_maintenance(!(opts->flags & (REBASE_NO_QUIET|REBASE_VERBOSE)));
rebase: rename the two primary rebase backends Two related changes, with separate rationale for each: Rename the 'interactive' backend to 'merge' because: * 'interactive' as a name caused confusion; this backend has been used for many kinds of non-interactive rebases, and will probably be used in the future for more non-interactive rebases than interactive ones given that we are making it the default. * 'interactive' is not the underlying strategy; merging is. * the directory where state is stored is not called .git/rebase-interactive but .git/rebase-merge. Rename the 'am' backend to 'apply' because: * Few users are familiar with git-am as a reference point. * Related to the above, the name 'am' makes sentences in the documentation harder for users to read and comprehend (they may read it as the verb from "I am"); avoiding this difficult places a large burden on anyone writing documentation about this backend to be very careful with quoting and sentence structure and often forces annoying redundancy to try to avoid such problems. * Users stumble over pronunciation ("am" as in "I am a person not a backend" or "am" as in "the first and thirteenth letters in the alphabet in order are "A-M"); this may drive confusion when one user tries to explain to another what they are doing. * While "am" is the tool driving this backend, the tool driving git-am is git-apply, and since we are driving towards lower-level tools for the naming of the merge backend we may as well do so here too. * The directory where state is stored has never been called .git/rebase-am, it was always called .git/rebase-apply. For all the reasons listed above: * Modify the documentation to refer to the backends with the new names * Provide a brief note in the documentation connecting the new names to the old names in case users run across the old names anywhere (e.g. in old release notes or older versions of the documentation) * Change the (new) --am command line flag to --apply * Rename some enums, variables, and functions to reinforce the new backend names for us as well. Signed-off-by: Elijah Newren <newren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-02-15 22:36:41 +01:00
if (opts->type == REBASE_MERGE) {
struct replay_opts replay = REPLAY_OPTS_INIT;
replay.action = REPLAY_INTERACTIVE_REBASE;
ret = sequencer_remove_state(&replay);
} 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);
}
return ret;
}
static int move_to_original_branch(struct rebase_options *opts)
{
struct strbuf branch_reflog = STRBUF_INIT, head_reflog = STRBUF_INIT;
struct reset_head_opts ropts = { 0 };
int ret;
if (!opts->head_name)
return 0; /* nothing to move back to */
if (!opts->onto)
BUG("move_to_original_branch without onto");
strbuf_addf(&branch_reflog, "rebase finished: %s onto %s",
opts->head_name, oid_to_hex(&opts->onto->object.oid));
strbuf_addf(&head_reflog, "rebase finished: returning to %s",
opts->head_name);
ropts.branch = opts->head_name;
ropts.flags = RESET_HEAD_REFS_ONLY;
ropts.branch_msg = branch_reflog.buf;
ropts.head_msg = head_reflog.buf;
ret = reset_head(the_repository, &ropts);
strbuf_release(&branch_reflog);
strbuf_release(&head_reflog);
return ret;
}
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\".");
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;
strvec_push(&am.args, "am");
if (opts->action && !strcmp("continue", opts->action)) {
strvec_push(&am.args, "--resolved");
strvec_pushf(&am.args, "--resolvemsg=%s", resolvemsg);
if (opts->gpg_sign_opt)
strvec_push(&am.args, opts->gpg_sign_opt);
status = run_command(&am);
if (status)
return status;
return move_to_original_branch(opts);
}
if (opts->action && !strcmp("skip", opts->action)) {
strvec_push(&am.args, "--skip");
strvec_pushf(&am.args, "--resolvemsg=%s", resolvemsg);
status = run_command(&am);
if (status)
return status;
return move_to_original_branch(opts);
}
if (opts->action && !strcmp("show-current-patch", opts->action)) {
strvec_push(&am.args, "--show-current-patch");
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),
oid_to_hex(&opts->orig_head));
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);
strvec_clear(&am.args);
return status;
}
format_patch.git_cmd = 1;
strvec_pushl(&format_patch.args, "format-patch", "-k", "--stdout",
"--full-index", "--cherry-pick", "--right-only",
"--src-prefix=a/", "--dst-prefix=b/", "--no-renames",
"--no-cover-letter", "--pretty=mboxrd", "--topo-order",
"--no-base", NULL);
if (opts->git_format_patch_opt.len)
strvec_split(&format_patch.args,
opts->git_format_patch_opt.buf);
strvec_push(&format_patch.args, revisions.buf);
if (opts->restrict_revision)
strvec_pushf(&format_patch.args, "^%s",
oid_to_hex(&opts->restrict_revision->object.oid));
status = run_command(&format_patch);
if (status) {
struct reset_head_opts ropts = { 0 };
unlink(rebased_patches);
free(rebased_patches);
strvec_clear(&am.args);
ropts.oid = &opts->orig_head;
ropts.branch = opts->head_name;
ropts.default_reflog_action = DEFAULT_REFLOG_ACTION;
reset_head(the_repository, &ropts);
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);
strvec_clear(&am.args);
return status;
}
strvec_pushv(&am.args, opts->git_am_opts.v);
strvec_push(&am.args, "--rebasing");
strvec_pushf(&am.args, "--resolvemsg=%s", resolvemsg);
strvec_push(&am.args, "--patch-format=mboxrd");
if (opts->allow_rerere_autoupdate == RERERE_AUTOUPDATE)
strvec_push(&am.args, "--rerere-autoupdate");
else if (opts->allow_rerere_autoupdate == RERERE_NOAUTOUPDATE)
strvec_push(&am.args, "--no-rerere-autoupdate");
if (opts->gpg_sign_opt)
strvec_push(&am.args, opts->gpg_sign_opt);
status = run_command(&am);
unlink(rebased_patches);
free(rebased_patches);
if (!status) {
return move_to_original_branch(opts);
}
if (is_directory(opts->state_dir))
rebase_write_basic_state(opts);
return status;
}
static int run_specific_rebase(struct rebase_options *opts, enum action action)
{
int status;
rebase: rename the two primary rebase backends Two related changes, with separate rationale for each: Rename the 'interactive' backend to 'merge' because: * 'interactive' as a name caused confusion; this backend has been used for many kinds of non-interactive rebases, and will probably be used in the future for more non-interactive rebases than interactive ones given that we are making it the default. * 'interactive' is not the underlying strategy; merging is. * the directory where state is stored is not called .git/rebase-interactive but .git/rebase-merge. Rename the 'am' backend to 'apply' because: * Few users are familiar with git-am as a reference point. * Related to the above, the name 'am' makes sentences in the documentation harder for users to read and comprehend (they may read it as the verb from "I am"); avoiding this difficult places a large burden on anyone writing documentation about this backend to be very careful with quoting and sentence structure and often forces annoying redundancy to try to avoid such problems. * Users stumble over pronunciation ("am" as in "I am a person not a backend" or "am" as in "the first and thirteenth letters in the alphabet in order are "A-M"); this may drive confusion when one user tries to explain to another what they are doing. * While "am" is the tool driving this backend, the tool driving git-am is git-apply, and since we are driving towards lower-level tools for the naming of the merge backend we may as well do so here too. * The directory where state is stored has never been called .git/rebase-am, it was always called .git/rebase-apply. For all the reasons listed above: * Modify the documentation to refer to the backends with the new names * Provide a brief note in the documentation connecting the new names to the old names in case users run across the old names anywhere (e.g. in old release notes or older versions of the documentation) * Change the (new) --am command line flag to --apply * Rename some enums, variables, and functions to reinforce the new backend names for us as well. Signed-off-by: Elijah Newren <newren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-02-15 22:36:41 +01:00
if (opts->type == REBASE_MERGE) {
/* Run sequencer-based rebase */
setenv("GIT_CHERRY_PICK_HELP", resolvemsg, 1);
if (!(opts->flags & REBASE_INTERACTIVE_EXPLICIT)) {
setenv("GIT_SEQUENCE_EDITOR", ":", 1);
opts->autosquash = 0;
}
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;
}
rebase: rename the two primary rebase backends Two related changes, with separate rationale for each: Rename the 'interactive' backend to 'merge' because: * 'interactive' as a name caused confusion; this backend has been used for many kinds of non-interactive rebases, and will probably be used in the future for more non-interactive rebases than interactive ones given that we are making it the default. * 'interactive' is not the underlying strategy; merging is. * the directory where state is stored is not called .git/rebase-interactive but .git/rebase-merge. Rename the 'am' backend to 'apply' because: * Few users are familiar with git-am as a reference point. * Related to the above, the name 'am' makes sentences in the documentation harder for users to read and comprehend (they may read it as the verb from "I am"); avoiding this difficult places a large burden on anyone writing documentation about this backend to be very careful with quoting and sentence structure and often forces annoying redundancy to try to avoid such problems. * Users stumble over pronunciation ("am" as in "I am a person not a backend" or "am" as in "the first and thirteenth letters in the alphabet in order are "A-M"); this may drive confusion when one user tries to explain to another what they are doing. * While "am" is the tool driving this backend, the tool driving git-am is git-apply, and since we are driving towards lower-level tools for the naming of the merge backend we may as well do so here too. * The directory where state is stored has never been called .git/rebase-am, it was always called .git/rebase-apply. For all the reasons listed above: * Modify the documentation to refer to the backends with the new names * Provide a brief note in the documentation connecting the new names to the old names in case users run across the old names anywhere (e.g. in old release notes or older versions of the documentation) * Change the (new) --am command line flag to --apply * Rename some enums, variables, and functions to reinforce the new backend names for us as well. Signed-off-by: Elijah Newren <newren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-02-15 22:36:41 +01:00
status = run_sequencer_rebase(opts, action);
} else if (opts->type == REBASE_APPLY)
status = run_am(opts);
else
BUG("Unhandled rebase type %d", opts->type);
if (opts->dont_finish_rebase)
; /* do nothing */
rebase: rename the two primary rebase backends Two related changes, with separate rationale for each: Rename the 'interactive' backend to 'merge' because: * 'interactive' as a name caused confusion; this backend has been used for many kinds of non-interactive rebases, and will probably be used in the future for more non-interactive rebases than interactive ones given that we are making it the default. * 'interactive' is not the underlying strategy; merging is. * the directory where state is stored is not called .git/rebase-interactive but .git/rebase-merge. Rename the 'am' backend to 'apply' because: * Few users are familiar with git-am as a reference point. * Related to the above, the name 'am' makes sentences in the documentation harder for users to read and comprehend (they may read it as the verb from "I am"); avoiding this difficult places a large burden on anyone writing documentation about this backend to be very careful with quoting and sentence structure and often forces annoying redundancy to try to avoid such problems. * Users stumble over pronunciation ("am" as in "I am a person not a backend" or "am" as in "the first and thirteenth letters in the alphabet in order are "A-M"); this may drive confusion when one user tries to explain to another what they are doing. * While "am" is the tool driving this backend, the tool driving git-am is git-apply, and since we are driving towards lower-level tools for the naming of the merge backend we may as well do so here too. * The directory where state is stored has never been called .git/rebase-am, it was always called .git/rebase-apply. For all the reasons listed above: * Modify the documentation to refer to the backends with the new names * Provide a brief note in the documentation connecting the new names to the old names in case users run across the old names anywhere (e.g. in old release notes or older versions of the documentation) * Change the (new) --am command line flag to --apply * Rename some enums, variables, and functions to reinforce the new backend names for us as well. Signed-off-by: Elijah Newren <newren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-02-15 22:36:41 +01:00
else if (opts->type == REBASE_MERGE)
; /* merge backend cleans up after itself */
else if (status == 0) {
if (!file_exists(state_dir_path("stopped-sha", opts)))
finish_rebase(opts);
} else if (status == 2) {
struct strbuf dir = STRBUF_INIT;
apply_autostash(state_dir_path("autostash", opts));
strbuf_addstr(&dir, opts->state_dir);
remove_dir_recursively(&dir, 0);
strbuf_release(&dir);
die("Nothing to do");
}
return status ? -1 : 0;
}
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
opts->flags &= ~REBASE_DIFFSTAT;
return 0;
}
if (!strcmp(var, "rebase.autosquash")) {
opts->autosquash = git_config_bool(var, value);
return 0;
}
if (!strcmp(var, "commit.gpgsign")) {
free(opts->gpg_sign_opt);
opts->gpg_sign_opt = git_config_bool(var, value) ?
xstrdup("-S") : NULL;
return 0;
}
if (!strcmp(var, "rebase.autostash")) {
opts->autostash = git_config_bool(var, value);
return 0;
}
if (!strcmp(var, "rebase.reschedulefailedexec")) {
opts->reschedule_failed_exec = git_config_bool(var, value);
return 0;
}
if (!strcmp(var, "rebase.forkpoint")) {
opts->fork_point = git_config_bool(var, value) ? -1 : 0;
return 0;
}
if (!strcmp(var, "rebase.backend")) {
return git_config_string(&opts->default_backend, var, value);
}
return git_default_config(var, value, data);
}
static int checkout_up_to_date(struct rebase_options *options)
{
struct strbuf buf = STRBUF_INIT;
struct reset_head_opts ropts = { 0 };
int ret = 0;
strbuf_addf(&buf, "%s: checkout %s",
getenv(GIT_REFLOG_ACTION_ENVIRONMENT),
options->switch_to);
ropts.oid = &options->orig_head;
ropts.branch = options->head_name;
ropts.flags = RESET_HEAD_RUN_POST_CHECKOUT_HOOK;
rebase: set REF_HEAD_DETACH in checkout_up_to_date() "git rebase A B" where B is not a commit should behave as if the HEAD got detached at B and then the detached HEAD got rebased on top of A. A bug however overwrites the current branch to point at B, when B is a descendant of A (i.e. the rebase ends up being a fast-forward). See [1] for the original bug report. The callstack from checkout_up_to_date() is the following: cmd_rebase() -> checkout_up_to_date() -> reset_head() -> update_refs() -> update_ref() When B is not a valid branch but an oid, rebase sets the head_name of rebase_options to NULL. This value gets passed down this call chain through the branch member of reset_head_opts also getting set to NULL all the way to update_refs(). Then update_refs() checks ropts.branch to decide whether or not to switch branches. If ropts.branch is NULL, it calls update_ref() to update HEAD. At this point however, from rebase's point of view, we want a detached HEAD. But, since checkout_up_to_date() does not set the RESET_HEAD_DETACH flag, the update_ref() call will deference HEAD and update the branch its pointing to. We want the HEAD detached at B instead. Fix this bug by adding the RESET_HEAD_DETACH flag in checkout_up_to_date if B is not a valid branch, so that once reset_head() calls update_refs(), it calls update_ref() with REF_NO_DEREF which updates HEAD directly intead of deferencing it and updating the branch that HEAD points to. Also add a test to ensure the correct behavior. [1] https://lore.kernel.org/git/YiokTm3GxIZQQUow@newk/ Reported-by: Michael McClimon <michael@mcclimon.org> Signed-off-by: John Cai <johncai86@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2022-03-18 14:54:03 +01:00
if (!ropts.branch)
ropts.flags |= RESET_HEAD_DETACH;
ropts.head_msg = buf.buf;
if (reset_head(the_repository, &ropts) < 0)
ret = error(_("could not switch to %s"), options->switch_to);
strbuf_release(&buf);
return ret;
}
/*
* 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) {
parse_commit(to);
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 07:37:59 +02:00
static int can_fast_forward(struct commit *onto, struct commit *upstream,
struct commit *restrict_revision,
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 07:37:59 +02:00
struct object_id *head_oid, struct object_id *merge_base)
{
struct commit *head = lookup_commit(the_repository, head_oid);
struct commit_list *merge_bases = NULL;
int res = 0;
if (!head)
goto done;
merge_bases = get_merge_bases(onto, head);
if (!merge_bases || merge_bases->next) {
oidcpy(merge_base, null_oid());
goto done;
}
oidcpy(merge_base, &merge_bases->item->object.oid);
if (!oideq(merge_base, &onto->object.oid))
goto done;
if (restrict_revision && !oideq(&restrict_revision->object.oid, merge_base))
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 07:37:59 +02:00
if (!upstream)
goto done;
free_commit_list(merge_bases);
merge_bases = get_merge_bases(upstream, head);
if (!merge_bases || merge_bases->next)
goto done;
if (!oideq(&onto->object.oid, &merge_bases->item->object.oid))
goto done;
res = 1;
done:
free_commit_list(merge_bases);
return res && is_linear_history(onto, head);
}
static int parse_opt_am(const struct option *opt, const char *arg, int unset)
{
struct rebase_options *opts = opt->value;
BUG_ON_OPT_NEG(unset);
BUG_ON_OPT_ARG(arg);
rebase: rename the two primary rebase backends Two related changes, with separate rationale for each: Rename the 'interactive' backend to 'merge' because: * 'interactive' as a name caused confusion; this backend has been used for many kinds of non-interactive rebases, and will probably be used in the future for more non-interactive rebases than interactive ones given that we are making it the default. * 'interactive' is not the underlying strategy; merging is. * the directory where state is stored is not called .git/rebase-interactive but .git/rebase-merge. Rename the 'am' backend to 'apply' because: * Few users are familiar with git-am as a reference point. * Related to the above, the name 'am' makes sentences in the documentation harder for users to read and comprehend (they may read it as the verb from "I am"); avoiding this difficult places a large burden on anyone writing documentation about this backend to be very careful with quoting and sentence structure and often forces annoying redundancy to try to avoid such problems. * Users stumble over pronunciation ("am" as in "I am a person not a backend" or "am" as in "the first and thirteenth letters in the alphabet in order are "A-M"); this may drive confusion when one user tries to explain to another what they are doing. * While "am" is the tool driving this backend, the tool driving git-am is git-apply, and since we are driving towards lower-level tools for the naming of the merge backend we may as well do so here too. * The directory where state is stored has never been called .git/rebase-am, it was always called .git/rebase-apply. For all the reasons listed above: * Modify the documentation to refer to the backends with the new names * Provide a brief note in the documentation connecting the new names to the old names in case users run across the old names anywhere (e.g. in old release notes or older versions of the documentation) * Change the (new) --am command line flag to --apply * Rename some enums, variables, and functions to reinforce the new backend names for us as well. Signed-off-by: Elijah Newren <newren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-02-15 22:36:41 +01:00
opts->type = REBASE_APPLY;
return 0;
}
/* -i followed by -m is still -i */
static int parse_opt_merge(const struct option *opt, const char *arg, int unset)
{
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 07:45:42 +01:00
BUG_ON_OPT_NEG(unset);
BUG_ON_OPT_ARG(arg);
rebase: rename the two primary rebase backends Two related changes, with separate rationale for each: Rename the 'interactive' backend to 'merge' because: * 'interactive' as a name caused confusion; this backend has been used for many kinds of non-interactive rebases, and will probably be used in the future for more non-interactive rebases than interactive ones given that we are making it the default. * 'interactive' is not the underlying strategy; merging is. * the directory where state is stored is not called .git/rebase-interactive but .git/rebase-merge. Rename the 'am' backend to 'apply' because: * Few users are familiar with git-am as a reference point. * Related to the above, the name 'am' makes sentences in the documentation harder for users to read and comprehend (they may read it as the verb from "I am"); avoiding this difficult places a large burden on anyone writing documentation about this backend to be very careful with quoting and sentence structure and often forces annoying redundancy to try to avoid such problems. * Users stumble over pronunciation ("am" as in "I am a person not a backend" or "am" as in "the first and thirteenth letters in the alphabet in order are "A-M"); this may drive confusion when one user tries to explain to another what they are doing. * While "am" is the tool driving this backend, the tool driving git-am is git-apply, and since we are driving towards lower-level tools for the naming of the merge backend we may as well do so here too. * The directory where state is stored has never been called .git/rebase-am, it was always called .git/rebase-apply. For all the reasons listed above: * Modify the documentation to refer to the backends with the new names * Provide a brief note in the documentation connecting the new names to the old names in case users run across the old names anywhere (e.g. in old release notes or older versions of the documentation) * Change the (new) --am command line flag to --apply * Rename some enums, variables, and functions to reinforce the new backend names for us as well. Signed-off-by: Elijah Newren <newren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-02-15 22:36:41 +01:00
if (!is_merge(opts))
opts->type = REBASE_MERGE;
return 0;
}
/* -i followed by -r is still explicitly interactive, but -r alone is not */
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 07:45:42 +01:00
BUG_ON_OPT_NEG(unset);
BUG_ON_OPT_ARG(arg);
rebase: rename the two primary rebase backends Two related changes, with separate rationale for each: Rename the 'interactive' backend to 'merge' because: * 'interactive' as a name caused confusion; this backend has been used for many kinds of non-interactive rebases, and will probably be used in the future for more non-interactive rebases than interactive ones given that we are making it the default. * 'interactive' is not the underlying strategy; merging is. * the directory where state is stored is not called .git/rebase-interactive but .git/rebase-merge. Rename the 'am' backend to 'apply' because: * Few users are familiar with git-am as a reference point. * Related to the above, the name 'am' makes sentences in the documentation harder for users to read and comprehend (they may read it as the verb from "I am"); avoiding this difficult places a large burden on anyone writing documentation about this backend to be very careful with quoting and sentence structure and often forces annoying redundancy to try to avoid such problems. * Users stumble over pronunciation ("am" as in "I am a person not a backend" or "am" as in "the first and thirteenth letters in the alphabet in order are "A-M"); this may drive confusion when one user tries to explain to another what they are doing. * While "am" is the tool driving this backend, the tool driving git-am is git-apply, and since we are driving towards lower-level tools for the naming of the merge backend we may as well do so here too. * The directory where state is stored has never been called .git/rebase-am, it was always called .git/rebase-apply. For all the reasons listed above: * Modify the documentation to refer to the backends with the new names * Provide a brief note in the documentation connecting the new names to the old names in case users run across the old names anywhere (e.g. in old release notes or older versions of the documentation) * Change the (new) --am command line flag to --apply * Rename some enums, variables, and functions to reinforce the new backend names for us as well. Signed-off-by: Elijah Newren <newren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-02-15 22:36:41 +01:00
opts->type = REBASE_MERGE;
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 22:36:25 +01: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;
}
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);
}
static void set_reflog_action(struct rebase_options *options)
{
const char *env;
struct strbuf buf = STRBUF_INIT;
rebase: rename the two primary rebase backends Two related changes, with separate rationale for each: Rename the 'interactive' backend to 'merge' because: * 'interactive' as a name caused confusion; this backend has been used for many kinds of non-interactive rebases, and will probably be used in the future for more non-interactive rebases than interactive ones given that we are making it the default. * 'interactive' is not the underlying strategy; merging is. * the directory where state is stored is not called .git/rebase-interactive but .git/rebase-merge. Rename the 'am' backend to 'apply' because: * Few users are familiar with git-am as a reference point. * Related to the above, the name 'am' makes sentences in the documentation harder for users to read and comprehend (they may read it as the verb from "I am"); avoiding this difficult places a large burden on anyone writing documentation about this backend to be very careful with quoting and sentence structure and often forces annoying redundancy to try to avoid such problems. * Users stumble over pronunciation ("am" as in "I am a person not a backend" or "am" as in "the first and thirteenth letters in the alphabet in order are "A-M"); this may drive confusion when one user tries to explain to another what they are doing. * While "am" is the tool driving this backend, the tool driving git-am is git-apply, and since we are driving towards lower-level tools for the naming of the merge backend we may as well do so here too. * The directory where state is stored has never been called .git/rebase-am, it was always called .git/rebase-apply. For all the reasons listed above: * Modify the documentation to refer to the backends with the new names * Provide a brief note in the documentation connecting the new names to the old names in case users run across the old names anywhere (e.g. in old release notes or older versions of the documentation) * Change the (new) --am command line flag to --apply * Rename some enums, variables, and functions to reinforce the new backend names for us as well. Signed-off-by: Elijah Newren <newren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-02-15 22:36:41 +01:00
if (!is_merge(options))
return;
env = getenv(GIT_REFLOG_ACTION_ENVIRONMENT);
if (env && strcmp("rebase", env))
return; /* only override it if it is "rebase" */
strbuf_addf(&buf, "rebase (%s)", options->action);
setenv(GIT_REFLOG_ACTION_ENVIRONMENT, buf.buf, 1);
strbuf_release(&buf);
}
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;
}
int cmd_rebase(int argc, const char **argv, const char *prefix)
{
struct rebase_options options = REBASE_OPTIONS_INIT;
const char *branch_name;
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 07:38:06 +02:00
int keep_base = 0;
int ok_to_skip_pre_rebase = 0;
struct strbuf msg = STRBUF_INIT;
struct strbuf revisions = STRBUF_INIT;
struct strbuf buf = STRBUF_INIT;
struct object_id merge_base;
int ignore_whitespace = 0;
enum action action = ACTION_NONE;
const char *gpg_sign = NULL;
struct string_list exec = STRING_LIST_INIT_NODUP;
const char *rebase_merges = NULL;
struct string_list strategy_options = STRING_LIST_INIT_NODUP;
struct object_id squash_onto;
char *squash_onto_name = NULL;
int reschedule_failed_exec = -1;
int allow_preemptive_ff = 1;
int preserve_merges_selected = 0;
struct reset_head_opts ropts = { 0 };
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 07:38:06 +02:00
OPT_BOOL(0, "keep-base", &keep_base,
N_("use the merge-base of upstream and branch as the current base")),
OPT_BOOL(0, "no-verify", &ok_to_skip_pre_rebase,
N_("allow pre-rebase hook to run")),
OPT_NEGBIT('q', "quiet", &options.flags,
N_("be quiet. implies --no-stat"),
REBASE_NO_QUIET | REBASE_VERBOSE | REBASE_DIFFSTAT),
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 },
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 03:03:55 +02:00
N_("add a Signed-off-by trailer to each commit")),
OPT_BOOL(0, "committer-date-is-author-date",
&options.committer_date_is_author_date,
N_("make committer date match author date")),
OPT_BOOL(0, "reset-author-date", &options.ignore_date,
N_("ignore author date and use current date")),
OPT_HIDDEN_BOOL(0, "ignore-date", &options.ignore_date,
N_("synonym of --reset-author-date")),
OPT_PASSTHRU_ARGV('C', NULL, &options.git_am_opts, N_("n"),
N_("passed to 'git apply'"), 0),
OPT_BOOL(0, "ignore-whitespace", &ignore_whitespace,
N_("ignore changes in whitespace")),
OPT_PASSTHRU_ARGV(0, "whitespace", &options.git_am_opts,
N_("action"), N_("passed to 'git apply'"), 0),
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),
OPT_CMDMODE(0, "continue", &action, N_("continue"),
ACTION_CONTINUE),
OPT_CMDMODE(0, "skip", &action,
N_("skip current patch and continue"), ACTION_SKIP),
OPT_CMDMODE(0, "abort", &action,
N_("abort and check out the original branch"),
ACTION_ABORT),
OPT_CMDMODE(0, "quit", &action,
N_("abort but keep HEAD where it is"), ACTION_QUIT),
OPT_CMDMODE(0, "edit-todo", &action, N_("edit the todo list "
"during an interactive rebase"), ACTION_EDIT_TODO),
OPT_CMDMODE(0, "show-current-patch", &action,
N_("show the patch file being applied or merged"),
ACTION_SHOW_CURRENT_PATCH),
OPT_CALLBACK_F(0, "apply", &options, NULL,
rebase: rename the two primary rebase backends Two related changes, with separate rationale for each: Rename the 'interactive' backend to 'merge' because: * 'interactive' as a name caused confusion; this backend has been used for many kinds of non-interactive rebases, and will probably be used in the future for more non-interactive rebases than interactive ones given that we are making it the default. * 'interactive' is not the underlying strategy; merging is. * the directory where state is stored is not called .git/rebase-interactive but .git/rebase-merge. Rename the 'am' backend to 'apply' because: * Few users are familiar with git-am as a reference point. * Related to the above, the name 'am' makes sentences in the documentation harder for users to read and comprehend (they may read it as the verb from "I am"); avoiding this difficult places a large burden on anyone writing documentation about this backend to be very careful with quoting and sentence structure and often forces annoying redundancy to try to avoid such problems. * Users stumble over pronunciation ("am" as in "I am a person not a backend" or "am" as in "the first and thirteenth letters in the alphabet in order are "A-M"); this may drive confusion when one user tries to explain to another what they are doing. * While "am" is the tool driving this backend, the tool driving git-am is git-apply, and since we are driving towards lower-level tools for the naming of the merge backend we may as well do so here too. * The directory where state is stored has never been called .git/rebase-am, it was always called .git/rebase-apply. For all the reasons listed above: * Modify the documentation to refer to the backends with the new names * Provide a brief note in the documentation connecting the new names to the old names in case users run across the old names anywhere (e.g. in old release notes or older versions of the documentation) * Change the (new) --am command line flag to --apply * Rename some enums, variables, and functions to reinforce the new backend names for us as well. Signed-off-by: Elijah Newren <newren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-02-15 22:36:41 +01:00
N_("use apply strategies to rebase"),
PARSE_OPT_NOARG | PARSE_OPT_NONEG,
parse_opt_am),
OPT_CALLBACK_F('m', "merge", &options, NULL,
N_("use merging strategies to rebase"),
PARSE_OPT_NOARG | PARSE_OPT_NONEG,
parse_opt_merge),
OPT_CALLBACK_F('i', "interactive", &options, NULL,
N_("let the user edit the list of commits to rebase"),
PARSE_OPT_NOARG | PARSE_OPT_NONEG,
parse_opt_interactive),
OPT_SET_INT_F('p', "preserve-merges", &preserve_merges_selected,
N_("(REMOVED) was: try to recreate merges "
"instead of ignoring them"),
1, PARSE_OPT_HIDDEN),
OPT_RERERE_AUTOUPDATE(&options.allow_rerere_autoupdate),
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 22:36:25 +01:00
N_("how to handle commits that become empty"),
PARSE_OPT_NONEG, parse_opt_empty),
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 04:44:25 +02: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 22:36:24 +01:00
PARSE_OPT_NOARG | PARSE_OPT_HIDDEN,
parse_opt_keep_empty),
OPT_BOOL(0, "autosquash", &options.autosquash,
N_("move commits that begin with "
"squash!/fixup! under -i")),
{ OPTION_STRING, 'S', "gpg-sign", &gpg_sign, N_("key-id"),
N_("GPG-sign commits"),
PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
OPT_AUTOSTASH(&options.autostash),
OPT_STRING_LIST('x', "exec", &exec, N_("exec"),
N_("add exec lines after each commit of the "
"editable list")),
OPT_BOOL_F(0, "allow-empty-message",
&options.allow_empty_message,
N_("allow rebasing commits with empty messages"),
PARSE_OPT_HIDDEN),
{OPTION_STRING, 'r', "rebase-merges", &rebase_merges,
N_("mode"),
N_("try to rebase merges instead of skipping them"),
PARSE_OPT_OPTARG, NULL, (intptr_t)""},
OPT_BOOL(0, "fork-point", &options.fork_point,
N_("use 'merge-base --fork-point' to refine upstream")),
OPT_STRING('s', "strategy", &options.strategy,
N_("strategy"), N_("use the given merge strategy")),
OPT_STRING_LIST('X', "strategy-option", &strategy_options,
N_("option"),
N_("pass the argument through to the merge "
"strategy")),
OPT_BOOL(0, "root", &options.root,
N_("rebase all reachable commits up to the root(s)")),
OPT_BOOL(0, "reschedule-failed-exec",
&reschedule_failed_exec,
N_("automatically re-schedule any `exec` that fails")),
OPT_BOOL(0, "reapply-cherry-picks", &options.reapply_cherry_picks,
N_("apply all changes, even those already present upstream")),
OPT_END(),
};
int i;
if (argc == 2 && !strcmp(argv[1], "-h"))
usage_with_options(builtin_rebase_usage,
builtin_rebase_options);
prepare_repo_settings(the_repository);
the_repository->settings.command_requires_full_index = 0;
options.allow_empty_message = 1;
git_config(rebase_config, &options);
/* options.gpg_sign_opt will be either "-S" or NULL */
gpg_sign = options.gpg_sign_opt ? "" : NULL;
FREE_AND_NULL(options.gpg_sign_opt);
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."));
if (is_directory(apply_dir())) {
rebase: rename the two primary rebase backends Two related changes, with separate rationale for each: Rename the 'interactive' backend to 'merge' because: * 'interactive' as a name caused confusion; this backend has been used for many kinds of non-interactive rebases, and will probably be used in the future for more non-interactive rebases than interactive ones given that we are making it the default. * 'interactive' is not the underlying strategy; merging is. * the directory where state is stored is not called .git/rebase-interactive but .git/rebase-merge. Rename the 'am' backend to 'apply' because: * Few users are familiar with git-am as a reference point. * Related to the above, the name 'am' makes sentences in the documentation harder for users to read and comprehend (they may read it as the verb from "I am"); avoiding this difficult places a large burden on anyone writing documentation about this backend to be very careful with quoting and sentence structure and often forces annoying redundancy to try to avoid such problems. * Users stumble over pronunciation ("am" as in "I am a person not a backend" or "am" as in "the first and thirteenth letters in the alphabet in order are "A-M"); this may drive confusion when one user tries to explain to another what they are doing. * While "am" is the tool driving this backend, the tool driving git-am is git-apply, and since we are driving towards lower-level tools for the naming of the merge backend we may as well do so here too. * The directory where state is stored has never been called .git/rebase-am, it was always called .git/rebase-apply. For all the reasons listed above: * Modify the documentation to refer to the backends with the new names * Provide a brief note in the documentation connecting the new names to the old names in case users run across the old names anywhere (e.g. in old release notes or older versions of the documentation) * Change the (new) --am command line flag to --apply * Rename some enums, variables, and functions to reinforce the new backend names for us as well. Signed-off-by: Elijah Newren <newren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-02-15 22:36:41 +01:00
options.type = REBASE_APPLY;
options.state_dir = apply_dir();
} else if (is_directory(merge_dir())) {
strbuf_reset(&buf);
strbuf_addf(&buf, "%s/rewritten", merge_dir());
if (!(action == ACTION_ABORT) && is_directory(buf.buf)) {
die(_("`rebase --preserve-merges` (-p) is no longer supported.\n"
"Use `git rebase --abort` to terminate current rebase.\n"
"Or downgrade to v2.33, or earlier, to complete the rebase."));
} else {
strbuf_reset(&buf);
strbuf_addf(&buf, "%s/interactive", merge_dir());
options.type = REBASE_MERGE;
if (file_exists(buf.buf))
options.flags |= REBASE_INTERACTIVE_EXPLICIT;
}
options.state_dir = merge_dir();
}
if (options.type != REBASE_UNSPECIFIED)
in_progress = 1;
total_argc = argc;
argc = parse_options(argc, argv, prefix,
builtin_rebase_options,
builtin_rebase_usage, 0);
if (preserve_merges_selected)
die(_("--preserve-merges was replaced by --rebase-merges\n"
"Note: Your `pull.rebase` configuration may also be set to 'preserve',\n"
"which is no longer supported; use 'merges' instead"));
if (action != ACTION_NONE && total_argc != 2) {
usage_with_options(builtin_rebase_usage,
builtin_rebase_options);
}
if (argc > 2)
usage_with_options(builtin_rebase_usage,
builtin_rebase_options);
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 07:38:06 +02:00
if (keep_base) {
if (options.onto_name)
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 07:38:06 +02:00
if (options.root)
die(_("options '%s' and '%s' cannot be used together"), "--keep-base", "--root");
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 07:38:06 +02:00
}
if (options.root && options.fork_point > 0)
die(_("options '%s' and '%s' cannot be used together"), "--root", "--fork-point");
if (action != ACTION_NONE && !in_progress)
die(_("No rebase in progress?"));
setenv(GIT_REFLOG_ACTION_ENVIRONMENT, "rebase", 0);
rebase: rename the two primary rebase backends Two related changes, with separate rationale for each: Rename the 'interactive' backend to 'merge' because: * 'interactive' as a name caused confusion; this backend has been used for many kinds of non-interactive rebases, and will probably be used in the future for more non-interactive rebases than interactive ones given that we are making it the default. * 'interactive' is not the underlying strategy; merging is. * the directory where state is stored is not called .git/rebase-interactive but .git/rebase-merge. Rename the 'am' backend to 'apply' because: * Few users are familiar with git-am as a reference point. * Related to the above, the name 'am' makes sentences in the documentation harder for users to read and comprehend (they may read it as the verb from "I am"); avoiding this difficult places a large burden on anyone writing documentation about this backend to be very careful with quoting and sentence structure and often forces annoying redundancy to try to avoid such problems. * Users stumble over pronunciation ("am" as in "I am a person not a backend" or "am" as in "the first and thirteenth letters in the alphabet in order are "A-M"); this may drive confusion when one user tries to explain to another what they are doing. * While "am" is the tool driving this backend, the tool driving git-am is git-apply, and since we are driving towards lower-level tools for the naming of the merge backend we may as well do so here too. * The directory where state is stored has never been called .git/rebase-am, it was always called .git/rebase-apply. For all the reasons listed above: * Modify the documentation to refer to the backends with the new names * Provide a brief note in the documentation connecting the new names to the old names in case users run across the old names anywhere (e.g. in old release notes or older versions of the documentation) * Change the (new) --am command line flag to --apply * Rename some enums, variables, and functions to reinforce the new backend names for us as well. Signed-off-by: Elijah Newren <newren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-02-15 22:36:41 +01:00
if (action == ACTION_EDIT_TODO && !is_merge(&options))
die(_("The --edit-todo action can only be used during "
"interactive rebase."));
if (trace2_is_enabled()) {
rebase: rename the two primary rebase backends Two related changes, with separate rationale for each: Rename the 'interactive' backend to 'merge' because: * 'interactive' as a name caused confusion; this backend has been used for many kinds of non-interactive rebases, and will probably be used in the future for more non-interactive rebases than interactive ones given that we are making it the default. * 'interactive' is not the underlying strategy; merging is. * the directory where state is stored is not called .git/rebase-interactive but .git/rebase-merge. Rename the 'am' backend to 'apply' because: * Few users are familiar with git-am as a reference point. * Related to the above, the name 'am' makes sentences in the documentation harder for users to read and comprehend (they may read it as the verb from "I am"); avoiding this difficult places a large burden on anyone writing documentation about this backend to be very careful with quoting and sentence structure and often forces annoying redundancy to try to avoid such problems. * Users stumble over pronunciation ("am" as in "I am a person not a backend" or "am" as in "the first and thirteenth letters in the alphabet in order are "A-M"); this may drive confusion when one user tries to explain to another what they are doing. * While "am" is the tool driving this backend, the tool driving git-am is git-apply, and since we are driving towards lower-level tools for the naming of the merge backend we may as well do so here too. * The directory where state is stored has never been called .git/rebase-am, it was always called .git/rebase-apply. For all the reasons listed above: * Modify the documentation to refer to the backends with the new names * Provide a brief note in the documentation connecting the new names to the old names in case users run across the old names anywhere (e.g. in old release notes or older versions of the documentation) * Change the (new) --am command line flag to --apply * Rename some enums, variables, and functions to reinforce the new backend names for us as well. Signed-off-by: Elijah Newren <newren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-02-15 22:36:41 +01:00
if (is_merge(&options))
trace2_cmd_mode("interactive");
else if (exec.nr)
trace2_cmd_mode("interactive-exec");
else
trace2_cmd_mode(action_names[action]);
}
switch (action) {
case ACTION_CONTINUE: {
struct object_id head;
struct lock_file lock_file = LOCK_INIT;
int fd;
options.action = "continue";
set_reflog_action(&options);
/* Sanity check */
if (get_oid("HEAD", &head))
die(_("Cannot read HEAD"));
fd = hold_locked_index(&lock_file, 0);
if (repo_read_index(the_repository) < 0)
die(_("could not read index"));
refresh_index(the_repository->index, REFRESH_QUIET, NULL, NULL,
NULL);
if (0 <= fd)
repo_update_index_if_able(the_repository, &lock_file);
rollback_lock_file(&lock_file);
if (has_unstaged_changes(the_repository, 1)) {
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;
}
case ACTION_SKIP: {
struct string_list merge_rr = STRING_LIST_INIT_DUP;
options.action = "skip";
set_reflog_action(&options);
rerere_clear(the_repository, &merge_rr);
string_list_clear(&merge_rr, 1);
ropts.flags = RESET_HEAD_HARD;
if (reset_head(the_repository, &ropts) < 0)
die(_("could not discard worktree changes"));
remove_branch_state(the_repository, 0);
if (read_basic_state(&options))
exit(1);
goto run_rebase;
}
case ACTION_ABORT: {
struct string_list merge_rr = STRING_LIST_INIT_DUP;
options.action = "abort";
set_reflog_action(&options);
rerere_clear(the_repository, &merge_rr);
string_list_clear(&merge_rr, 1);
if (read_basic_state(&options))
exit(1);
ropts.oid = &options.orig_head;
ropts.branch = options.head_name;
ropts.flags = RESET_HEAD_HARD;
ropts.default_reflog_action = DEFAULT_REFLOG_ACTION;
if (reset_head(the_repository, &ropts) < 0)
die(_("could not move back to %s"),
oid_to_hex(&options.orig_head));
remove_branch_state(the_repository, 0);
ret = finish_rebase(&options);
goto cleanup;
}
case ACTION_QUIT: {
save_autostash(state_dir_path("autostash", &options));
rebase: rename the two primary rebase backends Two related changes, with separate rationale for each: Rename the 'interactive' backend to 'merge' because: * 'interactive' as a name caused confusion; this backend has been used for many kinds of non-interactive rebases, and will probably be used in the future for more non-interactive rebases than interactive ones given that we are making it the default. * 'interactive' is not the underlying strategy; merging is. * the directory where state is stored is not called .git/rebase-interactive but .git/rebase-merge. Rename the 'am' backend to 'apply' because: * Few users are familiar with git-am as a reference point. * Related to the above, the name 'am' makes sentences in the documentation harder for users to read and comprehend (they may read it as the verb from "I am"); avoiding this difficult places a large burden on anyone writing documentation about this backend to be very careful with quoting and sentence structure and often forces annoying redundancy to try to avoid such problems. * Users stumble over pronunciation ("am" as in "I am a person not a backend" or "am" as in "the first and thirteenth letters in the alphabet in order are "A-M"); this may drive confusion when one user tries to explain to another what they are doing. * While "am" is the tool driving this backend, the tool driving git-am is git-apply, and since we are driving towards lower-level tools for the naming of the merge backend we may as well do so here too. * The directory where state is stored has never been called .git/rebase-am, it was always called .git/rebase-apply. For all the reasons listed above: * Modify the documentation to refer to the backends with the new names * Provide a brief note in the documentation connecting the new names to the old names in case users run across the old names anywhere (e.g. in old release notes or older versions of the documentation) * Change the (new) --am command line flag to --apply * Rename some enums, variables, and functions to reinforce the new backend names for us as well. Signed-off-by: Elijah Newren <newren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-02-15 22:36:41 +01:00
if (options.type == REBASE_MERGE) {
struct replay_opts replay = REPLAY_OPTS_INIT;
replay.action = REPLAY_INTERACTIVE_REBASE;
ret = sequencer_remove_state(&replay);
} else {
strbuf_reset(&buf);
strbuf_addstr(&buf, options.state_dir);
ret = remove_dir_recursively(&buf, 0);
if (ret)
error(_("could not remove '%s'"),
options.state_dir);
}
goto cleanup;
}
case ACTION_EDIT_TODO:
options.action = "edit-todo";
options.dont_finish_rebase = 1;
goto run_rebase;
case ACTION_SHOW_CURRENT_PATCH:
options.action = "show-current-patch";
options.dont_finish_rebase = 1;
goto run_rebase;
case ACTION_NONE:
break;
default:
BUG("action: %d", action);
}
/* 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);
}
if ((options.flags & REBASE_INTERACTIVE_EXPLICIT) ||
(action != ACTION_NONE) ||
(exec.nr > 0) ||
options.autosquash) {
allow_preemptive_ff = 0;
}
if (options.committer_date_is_author_date || options.ignore_date)
options.flags |= REBASE_FORCE;
for (i = 0; i < options.git_am_opts.nr; i++) {
const char *option = options.git_am_opts.v[i], *p;
if (!strcmp(option, "--whitespace=fix") ||
!strcmp(option, "--whitespace=strip"))
allow_preemptive_ff = 0;
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);
}
}
for (i = 0; i < exec.nr; i++)
if (check_exec_cmd(exec.items[i].string))
exit(1);
if (!(options.flags & REBASE_NO_QUIET))
strvec_push(&options.git_am_opts, "-q");
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 22:36:25 +01:00
if (options.empty != EMPTY_UNSPECIFIED)
rebase: rename the two primary rebase backends Two related changes, with separate rationale for each: Rename the 'interactive' backend to 'merge' because: * 'interactive' as a name caused confusion; this backend has been used for many kinds of non-interactive rebases, and will probably be used in the future for more non-interactive rebases than interactive ones given that we are making it the default. * 'interactive' is not the underlying strategy; merging is. * the directory where state is stored is not called .git/rebase-interactive but .git/rebase-merge. Rename the 'am' backend to 'apply' because: * Few users are familiar with git-am as a reference point. * Related to the above, the name 'am' makes sentences in the documentation harder for users to read and comprehend (they may read it as the verb from "I am"); avoiding this difficult places a large burden on anyone writing documentation about this backend to be very careful with quoting and sentence structure and often forces annoying redundancy to try to avoid such problems. * Users stumble over pronunciation ("am" as in "I am a person not a backend" or "am" as in "the first and thirteenth letters in the alphabet in order are "A-M"); this may drive confusion when one user tries to explain to another what they are doing. * While "am" is the tool driving this backend, the tool driving git-am is git-apply, and since we are driving towards lower-level tools for the naming of the merge backend we may as well do so here too. * The directory where state is stored has never been called .git/rebase-am, it was always called .git/rebase-apply. For all the reasons listed above: * Modify the documentation to refer to the backends with the new names * Provide a brief note in the documentation connecting the new names to the old names in case users run across the old names anywhere (e.g. in old release notes or older versions of the documentation) * Change the (new) --am command line flag to --apply * Rename some enums, variables, and functions to reinforce the new backend names for us as well. Signed-off-by: Elijah Newren <newren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-02-15 22:36:41 +01:00
imply_merge(&options, "--empty");
if (options.reapply_cherry_picks)
imply_merge(&options, "--reapply-cherry-picks");
if (gpg_sign)
options.gpg_sign_opt = xstrfmt("-S%s", gpg_sign);
if (exec.nr) {
int i;
rebase: rename the two primary rebase backends Two related changes, with separate rationale for each: Rename the 'interactive' backend to 'merge' because: * 'interactive' as a name caused confusion; this backend has been used for many kinds of non-interactive rebases, and will probably be used in the future for more non-interactive rebases than interactive ones given that we are making it the default. * 'interactive' is not the underlying strategy; merging is. * the directory where state is stored is not called .git/rebase-interactive but .git/rebase-merge. Rename the 'am' backend to 'apply' because: * Few users are familiar with git-am as a reference point. * Related to the above, the name 'am' makes sentences in the documentation harder for users to read and comprehend (they may read it as the verb from "I am"); avoiding this difficult places a large burden on anyone writing documentation about this backend to be very careful with quoting and sentence structure and often forces annoying redundancy to try to avoid such problems. * Users stumble over pronunciation ("am" as in "I am a person not a backend" or "am" as in "the first and thirteenth letters in the alphabet in order are "A-M"); this may drive confusion when one user tries to explain to another what they are doing. * While "am" is the tool driving this backend, the tool driving git-am is git-apply, and since we are driving towards lower-level tools for the naming of the merge backend we may as well do so here too. * The directory where state is stored has never been called .git/rebase-am, it was always called .git/rebase-apply. For all the reasons listed above: * Modify the documentation to refer to the backends with the new names * Provide a brief note in the documentation connecting the new names to the old names in case users run across the old names anywhere (e.g. in old release notes or older versions of the documentation) * Change the (new) --am command line flag to --apply * Rename some enums, variables, and functions to reinforce the new backend names for us as well. Signed-off-by: Elijah Newren <newren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-02-15 22:36:41 +01:00
imply_merge(&options, "--exec");
strbuf_reset(&buf);
for (i = 0; i < exec.nr; i++)
strbuf_addf(&buf, "exec %s\n", exec.items[i].string);
options.cmd = xstrdup(buf.buf);
}
if (rebase_merges) {
if (!*rebase_merges)
; /* default mode; do nothing */
else if (!strcmp("rebase-cousins", rebase_merges))
options.rebase_cousins = 1;
else if (strcmp("no-rebase-cousins", rebase_merges))
die(_("Unknown mode: %s"), rebase_merges);
options.rebase_merges = 1;
rebase: rename the two primary rebase backends Two related changes, with separate rationale for each: Rename the 'interactive' backend to 'merge' because: * 'interactive' as a name caused confusion; this backend has been used for many kinds of non-interactive rebases, and will probably be used in the future for more non-interactive rebases than interactive ones given that we are making it the default. * 'interactive' is not the underlying strategy; merging is. * the directory where state is stored is not called .git/rebase-interactive but .git/rebase-merge. Rename the 'am' backend to 'apply' because: * Few users are familiar with git-am as a reference point. * Related to the above, the name 'am' makes sentences in the documentation harder for users to read and comprehend (they may read it as the verb from "I am"); avoiding this difficult places a large burden on anyone writing documentation about this backend to be very careful with quoting and sentence structure and often forces annoying redundancy to try to avoid such problems. * Users stumble over pronunciation ("am" as in "I am a person not a backend" or "am" as in "the first and thirteenth letters in the alphabet in order are "A-M"); this may drive confusion when one user tries to explain to another what they are doing. * While "am" is the tool driving this backend, the tool driving git-am is git-apply, and since we are driving towards lower-level tools for the naming of the merge backend we may as well do so here too. * The directory where state is stored has never been called .git/rebase-am, it was always called .git/rebase-apply. For all the reasons listed above: * Modify the documentation to refer to the backends with the new names * Provide a brief note in the documentation connecting the new names to the old names in case users run across the old names anywhere (e.g. in old release notes or older versions of the documentation) * Change the (new) --am command line flag to --apply * Rename some enums, variables, and functions to reinforce the new backend names for us as well. Signed-off-by: Elijah Newren <newren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-02-15 22:36:41 +01:00
imply_merge(&options, "--rebase-merges");
}
if (options.type == REBASE_APPLY) {
if (ignore_whitespace)
strvec_push(&options.git_am_opts,
"--ignore-whitespace");
if (options.committer_date_is_author_date)
strvec_push(&options.git_am_opts,
"--committer-date-is-author-date");
if (options.ignore_date)
strvec_push(&options.git_am_opts, "--ignore-date");
} else {
/* REBASE_MERGE */
if (ignore_whitespace) {
string_list_append(&strategy_options,
"ignore-space-change");
}
}
if (strategy_options.nr) {
int i;
if (!options.strategy)
Change default merge backend from recursive to ort There are a few reasons to switch the default: * Correctness * Extensibility * Performance I'll provide some summaries about each. === Correctness === The original impetus for a new merge backend was to fix issues that were difficult to fix within recursive's design. The success with this goal is perhaps most easily demonstrated by running the following: $ git grep -2 KNOWN_FAILURE t/ | grep -A 4 GIT_TEST_MERGE_ALGORITHM $ git grep test_expect_merge_algorithm.failure.success t/ $ git grep test_expect_merge_algorithm.success.failure t/ In order, these greps show: * Seven sets of submodule tests (10 total tests) that fail with recursive but succeed with ort * 22 other tests that fail with recursive, but succeed with ort * 0 tests that pass with recursive, but fail with ort === Extensibility === Being able to perform merges without touching the working tree or index makes it possible to create new features that were difficult with the old backend: * Merging, cherry-picking, rebasing, reverting in bare repositories... or just on branches that aren't checked out. * `git diff AUTO_MERGE` -- ability to see what changes the user has made to resolve conflicts so far (see commit 5291828df8 ("merge-ort: write $GIT_DIR/AUTO_MERGE whenever we hit a conflict", 2021-03-20) * A --remerge-diff option for log/show, used to show diffs for merges that display the difference between what an automatic merge would have created and what was recorded in the merge. (This option will often result in an empty diff because many merges are clean, but for the non-clean ones it will show how conflicts were fixed including the removal of conflict markers, and also show additional changes made outside of conflict regions to e.g. fix semantic conflicts.) * A --remerge-diff-only option for log/show, similar to --remerge-diff but also showing how cherry-picks or reverts differed from what an automatic cherry-pick or revert would provide. The last three have been implemented already (though only one has been submitted upstream so far; the others were waiting for performance work to complete), and I still plan to implement the first one. === Performance === I'll quote from the summary of my final optimization for merge-ort (while fixing the testcase name from 'no-renames' to 'few-renames'): Timings Infinite merge- merge- Parallelism recursive recursive of rename merge-ort v2.30.0 current detection current ---------- --------- ----------- --------- few-renames: 18.912 s 18.030 s 11.699 s 198.3 ms mega-renames: 5964.031 s 361.281 s 203.886 s 661.8 ms just-one-mega: 149.583 s 11.009 s 7.553 s 264.6 ms Speedup factors Infinite merge- merge- Parallelism recursive recursive of rename v2.30.0 current detection merge-ort ---------- --------- ----------- --------- few-renames: 1 1.05 1.6 95 mega-renames: 1 16.5 29 9012 just-one-mega: 1 13.6 20 565 And, for partial clone users: Factor reduction in number of objects needed Infinite merge- merge- Parallelism recursive recursive of rename v2.30.0 current detection merge-ort ---------- --------- ----------- --------- mega-renames: 1 1 1 181.3 Signed-off-by: Elijah Newren <newren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-08-04 07:38:01 +02:00
options.strategy = "ort";
strbuf_reset(&buf);
for (i = 0; i < strategy_options.nr; i++)
strbuf_addf(&buf, " --%s",
strategy_options.items[i].string);
options.strategy_opts = xstrdup(buf.buf);
}
if (options.strategy) {
options.strategy = xstrdup(options.strategy);
switch (options.type) {
rebase: rename the two primary rebase backends Two related changes, with separate rationale for each: Rename the 'interactive' backend to 'merge' because: * 'interactive' as a name caused confusion; this backend has been used for many kinds of non-interactive rebases, and will probably be used in the future for more non-interactive rebases than interactive ones given that we are making it the default. * 'interactive' is not the underlying strategy; merging is. * the directory where state is stored is not called .git/rebase-interactive but .git/rebase-merge. Rename the 'am' backend to 'apply' because: * Few users are familiar with git-am as a reference point. * Related to the above, the name 'am' makes sentences in the documentation harder for users to read and comprehend (they may read it as the verb from "I am"); avoiding this difficult places a large burden on anyone writing documentation about this backend to be very careful with quoting and sentence structure and often forces annoying redundancy to try to avoid such problems. * Users stumble over pronunciation ("am" as in "I am a person not a backend" or "am" as in "the first and thirteenth letters in the alphabet in order are "A-M"); this may drive confusion when one user tries to explain to another what they are doing. * While "am" is the tool driving this backend, the tool driving git-am is git-apply, and since we are driving towards lower-level tools for the naming of the merge backend we may as well do so here too. * The directory where state is stored has never been called .git/rebase-am, it was always called .git/rebase-apply. For all the reasons listed above: * Modify the documentation to refer to the backends with the new names * Provide a brief note in the documentation connecting the new names to the old names in case users run across the old names anywhere (e.g. in old release notes or older versions of the documentation) * Change the (new) --am command line flag to --apply * Rename some enums, variables, and functions to reinforce the new backend names for us as well. Signed-off-by: Elijah Newren <newren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-02-15 22:36:41 +01:00
case REBASE_APPLY:
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 17:11:39 +01:00
if (options.type == REBASE_MERGE)
rebase: rename the two primary rebase backends Two related changes, with separate rationale for each: Rename the 'interactive' backend to 'merge' because: * 'interactive' as a name caused confusion; this backend has been used for many kinds of non-interactive rebases, and will probably be used in the future for more non-interactive rebases than interactive ones given that we are making it the default. * 'interactive' is not the underlying strategy; merging is. * the directory where state is stored is not called .git/rebase-interactive but .git/rebase-merge. Rename the 'am' backend to 'apply' because: * Few users are familiar with git-am as a reference point. * Related to the above, the name 'am' makes sentences in the documentation harder for users to read and comprehend (they may read it as the verb from "I am"); avoiding this difficult places a large burden on anyone writing documentation about this backend to be very careful with quoting and sentence structure and often forces annoying redundancy to try to avoid such problems. * Users stumble over pronunciation ("am" as in "I am a person not a backend" or "am" as in "the first and thirteenth letters in the alphabet in order are "A-M"); this may drive confusion when one user tries to explain to another what they are doing. * While "am" is the tool driving this backend, the tool driving git-am is git-apply, and since we are driving towards lower-level tools for the naming of the merge backend we may as well do so here too. * The directory where state is stored has never been called .git/rebase-am, it was always called .git/rebase-apply. For all the reasons listed above: * Modify the documentation to refer to the backends with the new names * Provide a brief note in the documentation connecting the new names to the old names in case users run across the old names anywhere (e.g. in old release notes or older versions of the documentation) * Change the (new) --am command line flag to --apply * Rename some enums, variables, and functions to reinforce the new backend names for us as well. Signed-off-by: Elijah Newren <newren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-02-15 22:36:41 +01: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 17:11:39 +01:00
if (options.root && !options.onto_name)
rebase: rename the two primary rebase backends Two related changes, with separate rationale for each: Rename the 'interactive' backend to 'merge' because: * 'interactive' as a name caused confusion; this backend has been used for many kinds of non-interactive rebases, and will probably be used in the future for more non-interactive rebases than interactive ones given that we are making it the default. * 'interactive' is not the underlying strategy; merging is. * the directory where state is stored is not called .git/rebase-interactive but .git/rebase-merge. Rename the 'am' backend to 'apply' because: * Few users are familiar with git-am as a reference point. * Related to the above, the name 'am' makes sentences in the documentation harder for users to read and comprehend (they may read it as the verb from "I am"); avoiding this difficult places a large burden on anyone writing documentation about this backend to be very careful with quoting and sentence structure and often forces annoying redundancy to try to avoid such problems. * Users stumble over pronunciation ("am" as in "I am a person not a backend" or "am" as in "the first and thirteenth letters in the alphabet in order are "A-M"); this may drive confusion when one user tries to explain to another what they are doing. * While "am" is the tool driving this backend, the tool driving git-am is git-apply, and since we are driving towards lower-level tools for the naming of the merge backend we may as well do so here too. * The directory where state is stored has never been called .git/rebase-am, it was always called .git/rebase-apply. For all the reasons listed above: * Modify the documentation to refer to the backends with the new names * Provide a brief note in the documentation connecting the new names to the old names in case users run across the old names anywhere (e.g. in old release notes or older versions of the documentation) * Change the (new) --am command line flag to --apply * Rename some enums, variables, and functions to reinforce the new backend names for us as well. Signed-off-by: Elijah Newren <newren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-02-15 22:36:41 +01:00
imply_merge(&options, "--root without --onto");
if (isatty(2) && options.flags & REBASE_NO_QUIET)
strbuf_addstr(&options.git_format_patch_opt, " --progress");
if (options.git_am_opts.nr || options.type == REBASE_APPLY) {
rebase: rename the two primary rebase backends Two related changes, with separate rationale for each: Rename the 'interactive' backend to 'merge' because: * 'interactive' as a name caused confusion; this backend has been used for many kinds of non-interactive rebases, and will probably be used in the future for more non-interactive rebases than interactive ones given that we are making it the default. * 'interactive' is not the underlying strategy; merging is. * the directory where state is stored is not called .git/rebase-interactive but .git/rebase-merge. Rename the 'am' backend to 'apply' because: * Few users are familiar with git-am as a reference point. * Related to the above, the name 'am' makes sentences in the documentation harder for users to read and comprehend (they may read it as the verb from "I am"); avoiding this difficult places a large burden on anyone writing documentation about this backend to be very careful with quoting and sentence structure and often forces annoying redundancy to try to avoid such problems. * Users stumble over pronunciation ("am" as in "I am a person not a backend" or "am" as in "the first and thirteenth letters in the alphabet in order are "A-M"); this may drive confusion when one user tries to explain to another what they are doing. * While "am" is the tool driving this backend, the tool driving git-am is git-apply, and since we are driving towards lower-level tools for the naming of the merge backend we may as well do so here too. * The directory where state is stored has never been called .git/rebase-am, it was always called .git/rebase-apply. For all the reasons listed above: * Modify the documentation to refer to the backends with the new names * Provide a brief note in the documentation connecting the new names to the old names in case users run across the old names anywhere (e.g. in old release notes or older versions of the documentation) * Change the (new) --am command line flag to --apply * Rename some enums, variables, and functions to reinforce the new backend names for us as well. Signed-off-by: Elijah Newren <newren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-02-15 22:36:41 +01:00
/* all am options except -q are compatible only with --apply */
for (i = options.git_am_opts.nr - 1; i >= 0; i--)
if (strcmp(options.git_am_opts.v[i], "-q"))
break;
if (i >= 0) {
rebase: rename the two primary rebase backends Two related changes, with separate rationale for each: Rename the 'interactive' backend to 'merge' because: * 'interactive' as a name caused confusion; this backend has been used for many kinds of non-interactive rebases, and will probably be used in the future for more non-interactive rebases than interactive ones given that we are making it the default. * 'interactive' is not the underlying strategy; merging is. * the directory where state is stored is not called .git/rebase-interactive but .git/rebase-merge. Rename the 'am' backend to 'apply' because: * Few users are familiar with git-am as a reference point. * Related to the above, the name 'am' makes sentences in the documentation harder for users to read and comprehend (they may read it as the verb from "I am"); avoiding this difficult places a large burden on anyone writing documentation about this backend to be very careful with quoting and sentence structure and often forces annoying redundancy to try to avoid such problems. * Users stumble over pronunciation ("am" as in "I am a person not a backend" or "am" as in "the first and thirteenth letters in the alphabet in order are "A-M"); this may drive confusion when one user tries to explain to another what they are doing. * While "am" is the tool driving this backend, the tool driving git-am is git-apply, and since we are driving towards lower-level tools for the naming of the merge backend we may as well do so here too. * The directory where state is stored has never been called .git/rebase-am, it was always called .git/rebase-apply. For all the reasons listed above: * Modify the documentation to refer to the backends with the new names * Provide a brief note in the documentation connecting the new names to the old names in case users run across the old names anywhere (e.g. in old release notes or older versions of the documentation) * Change the (new) --am command line flag to --apply * Rename some enums, variables, and functions to reinforce the new backend names for us as well. Signed-off-by: Elijah Newren <newren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-02-15 22:36:41 +01:00
if (is_merge(&options))
die(_("apply options and merge options "
"cannot be used together"));
else
rebase: rename the two primary rebase backends Two related changes, with separate rationale for each: Rename the 'interactive' backend to 'merge' because: * 'interactive' as a name caused confusion; this backend has been used for many kinds of non-interactive rebases, and will probably be used in the future for more non-interactive rebases than interactive ones given that we are making it the default. * 'interactive' is not the underlying strategy; merging is. * the directory where state is stored is not called .git/rebase-interactive but .git/rebase-merge. Rename the 'am' backend to 'apply' because: * Few users are familiar with git-am as a reference point. * Related to the above, the name 'am' makes sentences in the documentation harder for users to read and comprehend (they may read it as the verb from "I am"); avoiding this difficult places a large burden on anyone writing documentation about this backend to be very careful with quoting and sentence structure and often forces annoying redundancy to try to avoid such problems. * Users stumble over pronunciation ("am" as in "I am a person not a backend" or "am" as in "the first and thirteenth letters in the alphabet in order are "A-M"); this may drive confusion when one user tries to explain to another what they are doing. * While "am" is the tool driving this backend, the tool driving git-am is git-apply, and since we are driving towards lower-level tools for the naming of the merge backend we may as well do so here too. * The directory where state is stored has never been called .git/rebase-am, it was always called .git/rebase-apply. For all the reasons listed above: * Modify the documentation to refer to the backends with the new names * Provide a brief note in the documentation connecting the new names to the old names in case users run across the old names anywhere (e.g. in old release notes or older versions of the documentation) * Change the (new) --am command line flag to --apply * Rename some enums, variables, and functions to reinforce the new backend names for us as well. Signed-off-by: Elijah Newren <newren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-02-15 22:36:41 +01:00
options.type = REBASE_APPLY;
}
}
if (options.type == REBASE_UNSPECIFIED) {
if (!strcmp(options.default_backend, "merge"))
rebase: rename the two primary rebase backends Two related changes, with separate rationale for each: Rename the 'interactive' backend to 'merge' because: * 'interactive' as a name caused confusion; this backend has been used for many kinds of non-interactive rebases, and will probably be used in the future for more non-interactive rebases than interactive ones given that we are making it the default. * 'interactive' is not the underlying strategy; merging is. * the directory where state is stored is not called .git/rebase-interactive but .git/rebase-merge. Rename the 'am' backend to 'apply' because: * Few users are familiar with git-am as a reference point. * Related to the above, the name 'am' makes sentences in the documentation harder for users to read and comprehend (they may read it as the verb from "I am"); avoiding this difficult places a large burden on anyone writing documentation about this backend to be very careful with quoting and sentence structure and often forces annoying redundancy to try to avoid such problems. * Users stumble over pronunciation ("am" as in "I am a person not a backend" or "am" as in "the first and thirteenth letters in the alphabet in order are "A-M"); this may drive confusion when one user tries to explain to another what they are doing. * While "am" is the tool driving this backend, the tool driving git-am is git-apply, and since we are driving towards lower-level tools for the naming of the merge backend we may as well do so here too. * The directory where state is stored has never been called .git/rebase-am, it was always called .git/rebase-apply. For all the reasons listed above: * Modify the documentation to refer to the backends with the new names * Provide a brief note in the documentation connecting the new names to the old names in case users run across the old names anywhere (e.g. in old release notes or older versions of the documentation) * Change the (new) --am command line flag to --apply * Rename some enums, variables, and functions to reinforce the new backend names for us as well. Signed-off-by: Elijah Newren <newren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-02-15 22:36:41 +01:00
imply_merge(&options, "--merge");
else if (!strcmp(options.default_backend, "apply"))
options.type = REBASE_APPLY;
else
die(_("Unknown rebase backend: %s"),
options.default_backend);
}
if (options.type == REBASE_MERGE &&
!options.strategy &&
getenv("GIT_TEST_MERGE_ALGORITHM"))
options.strategy = xstrdup(getenv("GIT_TEST_MERGE_ALGORITHM"));
switch (options.type) {
case REBASE_MERGE:
options.state_dir = merge_dir();
break;
rebase: rename the two primary rebase backends Two related changes, with separate rationale for each: Rename the 'interactive' backend to 'merge' because: * 'interactive' as a name caused confusion; this backend has been used for many kinds of non-interactive rebases, and will probably be used in the future for more non-interactive rebases than interactive ones given that we are making it the default. * 'interactive' is not the underlying strategy; merging is. * the directory where state is stored is not called .git/rebase-interactive but .git/rebase-merge. Rename the 'am' backend to 'apply' because: * Few users are familiar with git-am as a reference point. * Related to the above, the name 'am' makes sentences in the documentation harder for users to read and comprehend (they may read it as the verb from "I am"); avoiding this difficult places a large burden on anyone writing documentation about this backend to be very careful with quoting and sentence structure and often forces annoying redundancy to try to avoid such problems. * Users stumble over pronunciation ("am" as in "I am a person not a backend" or "am" as in "the first and thirteenth letters in the alphabet in order are "A-M"); this may drive confusion when one user tries to explain to another what they are doing. * While "am" is the tool driving this backend, the tool driving git-am is git-apply, and since we are driving towards lower-level tools for the naming of the merge backend we may as well do so here too. * The directory where state is stored has never been called .git/rebase-am, it was always called .git/rebase-apply. For all the reasons listed above: * Modify the documentation to refer to the backends with the new names * Provide a brief note in the documentation connecting the new names to the old names in case users run across the old names anywhere (e.g. in old release notes or older versions of the documentation) * Change the (new) --am command line flag to --apply * Rename some enums, variables, and functions to reinforce the new backend names for us as well. Signed-off-by: Elijah Newren <newren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-02-15 22:36:41 +01:00
case REBASE_APPLY:
options.state_dir = apply_dir();
break;
default:
BUG("options.type was just set above; should be unreachable.");
}
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 22:36:25 +01:00
if (options.empty == EMPTY_UNSPECIFIED) {
if (options.flags & REBASE_INTERACTIVE_EXPLICIT)
options.empty = EMPTY_ASK;
else if (exec.nr > 0)
options.empty = EMPTY_KEEP;
else
options.empty = EMPTY_DROP;
}
rebase: rename the two primary rebase backends Two related changes, with separate rationale for each: Rename the 'interactive' backend to 'merge' because: * 'interactive' as a name caused confusion; this backend has been used for many kinds of non-interactive rebases, and will probably be used in the future for more non-interactive rebases than interactive ones given that we are making it the default. * 'interactive' is not the underlying strategy; merging is. * the directory where state is stored is not called .git/rebase-interactive but .git/rebase-merge. Rename the 'am' backend to 'apply' because: * Few users are familiar with git-am as a reference point. * Related to the above, the name 'am' makes sentences in the documentation harder for users to read and comprehend (they may read it as the verb from "I am"); avoiding this difficult places a large burden on anyone writing documentation about this backend to be very careful with quoting and sentence structure and often forces annoying redundancy to try to avoid such problems. * Users stumble over pronunciation ("am" as in "I am a person not a backend" or "am" as in "the first and thirteenth letters in the alphabet in order are "A-M"); this may drive confusion when one user tries to explain to another what they are doing. * While "am" is the tool driving this backend, the tool driving git-am is git-apply, and since we are driving towards lower-level tools for the naming of the merge backend we may as well do so here too. * The directory where state is stored has never been called .git/rebase-am, it was always called .git/rebase-apply. For all the reasons listed above: * Modify the documentation to refer to the backends with the new names * Provide a brief note in the documentation connecting the new names to the old names in case users run across the old names anywhere (e.g. in old release notes or older versions of the documentation) * Change the (new) --am command line flag to --apply * Rename some enums, variables, and functions to reinforce the new backend names for us as well. Signed-off-by: Elijah Newren <newren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-02-15 22:36:41 +01:00
if (reschedule_failed_exec > 0 && !is_merge(&options))
die(_("--reschedule-failed-exec requires "
"--exec or --interactive"));
if (reschedule_failed_exec >= 0)
options.reschedule_failed_exec = reschedule_failed_exec;
if (options.signoff) {
strvec_push(&options.git_am_opts, "--signoff");
options.flags |= REBASE_FORCE;
}
if (!options.root) {
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();
if (options.fork_point < 0)
options.fork_point = 1;
} else {
options.upstream_name = argv[0];
argc--;
argv++;
if (!strcmp(options.upstream_name, "-"))
options.upstream_name = "@{-1}";
}
options.upstream =
lookup_commit_reference_by_name(options.upstream_name);
if (!options.upstream)
die(_("invalid upstream '%s'"), options.upstream_name);
options.upstream_arg = options.upstream_name;
} 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));
} else
options.root_with_onto = 1;
options.upstream_name = NULL;
options.upstream = NULL;
if (argc > 1)
usage_with_options(builtin_rebase_usage,
builtin_rebase_options);
options.upstream_arg = "--root";
}
/*
* 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
* head_name -- refs/heads/<that-branch> or NULL (detached HEAD)
*/
if (argc == 1) {
/* Is it "rebase other branchname" or "rebase other commit"? */
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);
if (!read_ref(buf.buf, &options.orig_head)) {
die_if_checked_out(buf.buf, 1);
options.head_name = xstrdup(buf.buf);
/* If not is it a valid ref (branch or commit)? */
} else {
struct commit *commit =
lookup_commit_reference_by_name(branch_name);
if (!commit)
die(_("no such branch/commit '%s'"),
branch_name);
oidcpy(&options.orig_head, &commit->object.oid);
options.head_name = NULL;
}
} else if (argc == 0) {
/* 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 {
FREE_AND_NULL(options.head_name);
branch_name = "HEAD";
}
if (get_oid("HEAD", &options.orig_head))
die(_("Could not resolve HEAD to a revision"));
} else
BUG("unexpected number of arguments left to parse");
/* 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);
options.onto_name = xstrdup(buf.buf);
} else if (!options.onto_name)
options.onto_name = options.upstream_name;
if (strstr(options.onto_name, "...")) {
if (get_oid_mb(options.onto_name, &merge_base) < 0) {
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);
}
options.onto = lookup_commit_or_die(&merge_base,
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);
}
if (options.fork_point > 0) {
struct commit *head =
lookup_commit_reference(the_repository,
&options.orig_head);
options.restrict_revision =
get_fork_point(options.upstream_name, head);
}
if (repo_read_index(the_repository) < 0)
die(_("could not read index"));
if (options.autostash)
create_autostash(the_repository,
state_dir_path("autostash", &options));
if (require_clean_work_tree(the_repository, "rebase",
_("Please commit or stash them."), 1, 1)) {
ret = -1;
goto cleanup;
}
/*
* 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 07:37:59 +02:00
* in which case we could fast-forward without replacing the commits
* with new commits recreated by replaying their changes.
*
* Note that can_fast_forward() initializes merge_base, so we have to
* call it before checking allow_preemptive_ff.
*/
if (can_fast_forward(options.onto, options.upstream, options.restrict_revision,
&options.orig_head, &merge_base) &&
allow_preemptive_ff) {
int flag;
if (!(options.flags & REBASE_FORCE)) {
/* Lazily switch to the target branch if needed... */
if (options.switch_to) {
ret = checkout_up_to_date(&options);
if (ret)
goto cleanup;
}
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);
ret = finish_rebase(&options);
goto cleanup;
} else 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, rebase forced."));
else
printf(_("Current branch %s is up to date, rebase "
"forced.\n"), branch_name);
}
/* If a hook exists, give it a chance to interrupt*/
if (!ok_to_skip_pre_rebase &&
run_hooks_l("pre-rebase", options.upstream_arg,
argc ? argv[0] : NULL, NULL))
die(_("The pre-rebase hook refused to rebase."));
if (options.flags & REBASE_DIFFSTAT) {
struct diff_options opts;
if (options.flags & REBASE_VERBOSE) {
if (is_null_oid(&merge_base))
printf(_("Changes to %s:\n"),
oid_to_hex(&options.onto->object.oid));
else
printf(_("Changes from %s to %s:\n"),
oid_to_hex(&merge_base),
oid_to_hex(&options.onto->object.oid));
}
/* We want color (if set), but no pager */
diff_setup(&opts);
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);
diff_tree_oid(is_null_oid(&merge_base) ?
the_hash_algo->empty_tree : &merge_base,
&options.onto->object.oid, "", &opts);
diffcore_std(&opts);
diff_flush(&opts);
}
rebase: rename the two primary rebase backends Two related changes, with separate rationale for each: Rename the 'interactive' backend to 'merge' because: * 'interactive' as a name caused confusion; this backend has been used for many kinds of non-interactive rebases, and will probably be used in the future for more non-interactive rebases than interactive ones given that we are making it the default. * 'interactive' is not the underlying strategy; merging is. * the directory where state is stored is not called .git/rebase-interactive but .git/rebase-merge. Rename the 'am' backend to 'apply' because: * Few users are familiar with git-am as a reference point. * Related to the above, the name 'am' makes sentences in the documentation harder for users to read and comprehend (they may read it as the verb from "I am"); avoiding this difficult places a large burden on anyone writing documentation about this backend to be very careful with quoting and sentence structure and often forces annoying redundancy to try to avoid such problems. * Users stumble over pronunciation ("am" as in "I am a person not a backend" or "am" as in "the first and thirteenth letters in the alphabet in order are "A-M"); this may drive confusion when one user tries to explain to another what they are doing. * While "am" is the tool driving this backend, the tool driving git-am is git-apply, and since we are driving towards lower-level tools for the naming of the merge backend we may as well do so here too. * The directory where state is stored has never been called .git/rebase-am, it was always called .git/rebase-apply. For all the reasons listed above: * Modify the documentation to refer to the backends with the new names * Provide a brief note in the documentation connecting the new names to the old names in case users run across the old names anywhere (e.g. in old release notes or older versions of the documentation) * Change the (new) --am command line flag to --apply * Rename some enums, variables, and functions to reinforce the new backend names for us as well. Signed-off-by: Elijah Newren <newren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-02-15 22:36:41 +01:00
if (is_merge(&options))
goto run_rebase;
/* 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"));
strbuf_addf(&msg, "%s: checkout %s",
getenv(GIT_REFLOG_ACTION_ENVIRONMENT), options.onto_name);
ropts.oid = &options.onto->object.oid;
ropts.orig_head = &options.orig_head,
ropts.flags = RESET_HEAD_DETACH | RESET_ORIG_HEAD |
RESET_HEAD_RUN_POST_CHECKOUT_HOOK;
ropts.head_msg = msg.buf;
ropts.default_reflog_action = DEFAULT_REFLOG_ACTION;
if (reset_head(the_repository, &ropts))
die(_("Could not detach HEAD"));
strbuf_release(&msg);
/*
* If the onto is a proper descendant of the tip of the branch, then
* we just fast-forwarded.
*/
strbuf_reset(&msg);
if (oideq(&merge_base, &options.orig_head)) {
printf(_("Fast-forwarded %s to %s.\n"),
branch_name, options.onto_name);
strbuf_addf(&msg, "rebase finished: %s onto %s",
options.head_name ? options.head_name : "detached HEAD",
oid_to_hex(&options.onto->object.oid));
memset(&ropts, 0, sizeof(ropts));
ropts.branch = options.head_name;
ropts.flags = RESET_HEAD_REFS_ONLY;
ropts.head_msg = msg.buf;
reset_head(the_repository, &ropts);
strbuf_release(&msg);
ret = finish_rebase(&options);
goto cleanup;
}
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)),
oid_to_hex(&options.orig_head));
options.revisions = revisions.buf;
run_rebase:
ret = run_specific_rebase(&options, action);
cleanup:
strbuf_release(&buf);
strbuf_release(&revisions);
free(options.head_name);
free(options.gpg_sign_opt);
free(options.cmd);
builtin/rebase: fix options.strategy memory lifecycle - cmd_rebase populates rebase_options.strategy with newly allocated strings, hence we need to free those strings at the end of cmd_rebase to avoid a leak. - In some cases: get_replay_opts() is called, which prepares replay_opts using data from rebase_options. We used to simply copy the pointer from rebase_options.strategy, however that would now result in a double-free because sequencer_remove_state() is eventually used to free replay_opts.strategy. To avoid this we xstrdup() strategy when adding it to replay_opts. The original leak happens because we always populate rebase_options.strategy, but we don't always enter the path that calls get_replay_opts() and later sequencer_remove_state() - in other words we'd always allocate a new string into rebase_options.strategy but only sometimes did we free it. We now make sure that rebase_options and replay_opts both own their own copies of strategy, and each copy is free'd independently. This was first seen when running t0021 with LSAN, but t2012 helped catch the fact that we can't just free(options.strategy) at the end of cmd_rebase (as that can cause a double-free). LSAN output from t0021: LSAN output from t0021: Direct leak of 4 byte(s) in 1 object(s) allocated from: #0 0x486804 in strdup ../projects/compiler-rt/lib/asan/asan_interceptors.cpp:452:3 #1 0xa71eb8 in xstrdup wrapper.c:29:14 #2 0x61b1cc in cmd_rebase builtin/rebase.c:1779:22 #3 0x4ce83e in run_builtin git.c:475:11 #4 0x4ccafe in handle_builtin git.c:729:3 #5 0x4cb01c in run_argv git.c:818:4 #6 0x4cb01c in cmd_main git.c:949:19 #7 0x6b3fad in main common-main.c:52:11 #8 0x7f267b512349 in __libc_start_main (/lib64/libc.so.6+0x24349) SUMMARY: AddressSanitizer: 4 byte(s) leaked in 1 allocation(s). Signed-off-by: Andrzej Hunt <andrzej@ahunt.org> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-07-25 15:08:29 +02:00
free(options.strategy);
strbuf_release(&options.git_format_patch_opt);
free(squash_onto_name);
return !!ret;
}