summaryrefslogtreecommitdiff
path: root/Documentation/git-rebase.txt
diff options
context:
space:
mode:
Diffstat (limited to 'Documentation/git-rebase.txt')
-rw-r--r--Documentation/git-rebase.txt241
1 files changed, 214 insertions, 27 deletions
diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index 0c4f038dd6..4624cfd288 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -256,18 +256,79 @@ See also INCOMPATIBLE OPTIONS below.
--quit::
Abort the rebase operation but HEAD is not reset back to the
original branch. The index and working tree are also left
- unchanged as a result.
+ unchanged as a result. If a temporary stash entry was created
+ using --autostash, it will be saved to the stash list.
+--apply:
+ Use applying strategies to rebase (calling `git-am`
+ internally). This option may become a no-op in the future
+ once the merge backend handles everything the apply one does.
++
+See also INCOMPATIBLE OPTIONS below.
+
+--empty={drop,keep,ask}::
+ How to handle commits that are not empty to start and are not
+ clean cherry-picks of any upstream commit, but which become
+ empty after rebasing (because they contain a subset of already
+ upstream changes). With drop (the default), commits that
+ become empty are dropped. With keep, such commits are kept.
+ With ask (implied by --interactive), the rebase will halt when
+ an empty commit is applied allowing you to choose whether to
+ drop it, edit files more, or just commit the empty changes.
+ Other options, like --exec, will use the default of drop unless
+ -i/--interactive is explicitly specified.
++
+Note that commits which start empty are kept (unless --no-keep-empty
+is specified), and commits which are clean cherry-picks (as determined
+by `git log --cherry-mark ...`) are detected and dropped as a
+preliminary step (unless --reapply-cherry-picks is passed).
++
+See also INCOMPATIBLE OPTIONS below.
+
+--no-keep-empty::
--keep-empty::
- Keep the commits that do not change anything from its
- parents in the result.
+ Do not keep commits that start empty before the rebase
+ (i.e. that do not change anything from its parent) in the
+ result. The default is to keep commits which start empty,
+ since creating such commits requires passing the --allow-empty
+ override flag to `git commit`, signifying that a user is very
+ intentionally creating such a commit and thus wants to keep
+ it.
++
+Usage of this flag will probably be rare, since you can get rid of
+commits that start empty by just firing up an interactive rebase and
+removing the lines corresponding to the commits you don't want. This
+flag exists as a convenient shortcut, such as for cases where external
+tools generate many empty commits and you want them all removed.
++
+For commits which do not start empty but become empty after rebasing,
+see the --empty flag.
++
+See also INCOMPATIBLE OPTIONS below.
+
+--reapply-cherry-picks::
+--no-reapply-cherry-picks::
+ Reapply all clean cherry-picks of any upstream commit instead
+ of preemptively dropping them. (If these commits then become
+ empty after rebasing, because they contain a subset of already
+ upstream changes, the behavior towards them is controlled by
+ the `--empty` flag.)
++
+By default (or if `--no-reapply-cherry-picks` is given), these commits
+will be automatically dropped. Because this necessitates reading all
+upstream commits, this can be expensive in repos with a large number
+of upstream commits that need to be read.
++
+`--reapply-cherry-picks` allows rebase to forgo reading all upstream
+commits, potentially improving performance.
+
See also INCOMPATIBLE OPTIONS below.
--allow-empty-message::
- By default, rebasing commits with an empty message will fail.
- This option overrides that behavior, allowing commits with empty
- messages to be rebased.
+ No-op. Rebasing commits with an empty message used to fail
+ and this option would override that behavior, allowing commits
+ with empty messages to be rebased. Now commits with an empty
+ message do not cause rebasing to halt.
+
See also INCOMPATIBLE OPTIONS below.
@@ -286,7 +347,7 @@ See also INCOMPATIBLE OPTIONS below.
--merge::
Use merging strategies to rebase. When the recursive (default) merge
strategy is used, this allows rebase to be aware of renames on the
- upstream side.
+ upstream side. This is the default.
+
Note that a rebase merge works by replaying each commit from the working
branch on top of the <upstream> branch. Because of this, when a merge
@@ -325,9 +386,12 @@ See also INCOMPATIBLE OPTIONS below.
-S[<keyid>]::
--gpg-sign[=<keyid>]::
+--no-gpg-sign::
GPG-sign commits. The `keyid` argument is optional and
defaults to the committer identity; if specified, it must be
- stuck to the option without a space.
+ stuck to the option without a space. `--no-gpg-sign` is useful to
+ countermand both `commit.gpgSign` configuration variable, and
+ earlier `--gpg-sign`.
-q::
--quiet::
@@ -356,7 +420,7 @@ See also INCOMPATIBLE OPTIONS below.
Ensure at least <n> lines of surrounding context match before
and after each change. When fewer lines of surrounding
context exist they all must match. By default no context is
- ever ignored.
+ ever ignored. Implies --apply.
+
See also INCOMPATIBLE OPTIONS below.
@@ -385,17 +449,20 @@ When --fork-point is active, 'fork_point' will be used instead of
<branch>` command (see linkgit:git-merge-base[1]). If 'fork_point'
ends up being empty, the <upstream> will be used as a fallback.
+
-If either <upstream> or --root is given on the command line, then the
-default is `--no-fork-point`, otherwise the default is `--fork-point`.
+If <upstream> is given on the command line, then the default is
+`--no-fork-point`, otherwise the default is `--fork-point`.
+
If your branch was based on <upstream> but <upstream> was rewound and
your branch contains commits which were dropped, this option can be used
with `--keep-base` in order to drop those commits from your branch.
++
+See also INCOMPATIBLE OPTIONS below.
--ignore-whitespace::
--whitespace=<option>::
- These flag are passed to the 'git apply' program
+ These flags are passed to the 'git apply' program
(see linkgit:git-apply[1]) that applies the patch.
+ Implies --apply.
+
See also INCOMPATIBLE OPTIONS below.
@@ -539,10 +606,11 @@ INCOMPATIBLE OPTIONS
The following options:
+ * --apply
* --committer-date-is-author-date
* --ignore-date
- * --whitespace
* --ignore-whitespace
+ * --whitespace
* -C
are incompatible with the following options:
@@ -556,7 +624,9 @@ are incompatible with the following options:
* --preserve-merges
* --interactive
* --exec
- * --keep-empty
+ * --no-keep-empty
+ * --empty=
+ * --reapply-cherry-picks
* --edit-todo
* --root when used in combination with --onto
@@ -565,33 +635,149 @@ In addition, the following pairs of options are incompatible:
* --preserve-merges and --interactive
* --preserve-merges and --signoff
* --preserve-merges and --rebase-merges
+ * --preserve-merges and --empty=
* --keep-base and --onto
* --keep-base and --root
+ * --fork-point and --root
BEHAVIORAL DIFFERENCES
-----------------------
-There are some subtle differences how the backends behave.
+git rebase has two primary backends: apply and merge. (The apply
+backend used to be known as the 'am' backend, but the name led to
+confusion as it looks like a verb instead of a noun. Also, the merge
+backend used to be known as the interactive backend, but it is now
+used for non-interactive cases as well. Both were renamed based on
+lower-level functionality that underpinned each.) There are some
+subtle differences in how these two backends behave:
Empty commits
~~~~~~~~~~~~~
-The am backend drops any "empty" commits, regardless of whether the
-commit started empty (had no changes relative to its parent to
-start with) or ended empty (all changes were already applied
-upstream in other commits).
+The apply backend unfortunately drops intentionally empty commits, i.e.
+commits that started empty, though these are rare in practice. It
+also drops commits that become empty and has no option for controlling
+this behavior.
+
+The merge backend keeps intentionally empty commits by default (though
+with -i they are marked as empty in the todo list editor, or they can
+be dropped automatically with --no-keep-empty).
-The interactive backend drops commits by default that
-started empty and halts if it hits a commit that ended up empty.
-The `--keep-empty` option exists for the interactive backend to allow
-it to keep commits that started empty.
+Similar to the apply backend, by default the merge backend drops
+commits that become empty unless -i/--interactive is specified (in
+which case it stops and asks the user what to do). The merge backend
+also has an --empty={drop,keep,ask} option for changing the behavior
+of handling commits that become empty.
Directory rename detection
~~~~~~~~~~~~~~~~~~~~~~~~~~
-Directory rename heuristics are enabled in the merge and interactive
-backends. Due to the lack of accurate tree information, directory
-rename detection is disabled in the am backend.
+Due to the lack of accurate tree information (arising from
+constructing fake ancestors with the limited information available in
+patches), directory rename detection is disabled in the apply backend.
+Disabled directory rename detection means that if one side of history
+renames a directory and the other adds new files to the old directory,
+then the new files will be left behind in the old directory without
+any warning at the time of rebasing that you may want to move these
+files into the new directory.
+
+Directory rename detection works with the merge backend to provide you
+warnings in such cases.
+
+Context
+~~~~~~~
+
+The apply backend works by creating a sequence of patches (by calling
+`format-patch` internally), and then applying the patches in sequence
+(calling `am` internally). Patches are composed of multiple hunks,
+each with line numbers, a context region, and the actual changes. The
+line numbers have to be taken with some fuzz, since the other side
+will likely have inserted or deleted lines earlier in the file. The
+context region is meant to help find how to adjust the line numbers in
+order to apply the changes to the right lines. However, if multiple
+areas of the code have the same surrounding lines of context, the
+wrong one can be picked. There are real-world cases where this has
+caused commits to be reapplied incorrectly with no conflicts reported.
+Setting diff.context to a larger value may prevent such types of
+problems, but increases the chance of spurious conflicts (since it
+will require more lines of matching context to apply).
+
+The merge backend works with a full copy of each relevant file,
+insulating it from these types of problems.
+
+Labelling of conflicts markers
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+When there are content conflicts, the merge machinery tries to
+annotate each side's conflict markers with the commits where the
+content came from. Since the apply backend drops the original
+information about the rebased commits and their parents (and instead
+generates new fake commits based off limited information in the
+generated patches), those commits cannot be identified; instead it has
+to fall back to a commit summary. Also, when merge.conflictStyle is
+set to diff3, the apply backend will use "constructed merge base" to
+label the content from the merge base, and thus provide no information
+about the merge base commit whatsoever.
+
+The merge backend works with the full commits on both sides of history
+and thus has no such limitations.
+
+Hooks
+~~~~~
+
+The apply backend has not traditionally called the post-commit hook,
+while the merge backend has. Both have called the post-checkout hook,
+though the merge backend has squelched its output. Further, both
+backends only call the post-checkout hook with the starting point
+commit of the rebase, not the intermediate commits nor the final
+commit. In each case, the calling of these hooks was by accident of
+implementation rather than by design (both backends were originally
+implemented as shell scripts and happened to invoke other commands
+like 'git checkout' or 'git commit' that would call the hooks). Both
+backends should have the same behavior, though it is not entirely
+clear which, if any, is correct. We will likely make rebase stop
+calling either of these hooks in the future.
+
+Interruptability
+~~~~~~~~~~~~~~~~
+
+The apply backend has safety problems with an ill-timed interrupt; if
+the user presses Ctrl-C at the wrong time to try to abort the rebase,
+the rebase can enter a state where it cannot be aborted with a
+subsequent `git rebase --abort`. The merge backend does not appear to
+suffer from the same shortcoming. (See
+https://lore.kernel.org/git/20200207132152.GC2868@szeder.dev/ for
+details.)
+
+Commit Rewording
+~~~~~~~~~~~~~~~~
+
+When a conflict occurs while rebasing, rebase stops and asks the user
+to resolve. Since the user may need to make notable changes while
+resolving conflicts, after conflicts are resolved and the user has run
+`git rebase --continue`, the rebase should open an editor and ask the
+user to update the commit message. The merge backend does this, while
+the apply backend blindly applies the original commit message.
+
+Miscellaneous differences
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+There are a few more behavioral differences that most folks would
+probably consider inconsequential but which are mentioned for
+completeness:
+
+* Reflog: The two backends will use different wording when describing
+ the changes made in the reflog, though both will make use of the
+ word "rebase".
+
+* Progress, informational, and error messages: The two backends
+ provide slightly different progress and informational messages.
+ Also, the apply backend writes error messages (such as "Your files
+ would be overwritten...") to stdout, while the merge backend writes
+ them to stderr.
+
+* State directories: The two backends keep their state in different
+ directories under .git/
include::merge-strategies.txt[]
@@ -866,7 +1052,8 @@ Only works if the changes (patch IDs based on the diff contents) on
'subsystem' did.
In that case, the fix is easy because 'git rebase' knows to skip
-changes that are already present in the new upstream. So if you say
+changes that are already present in the new upstream (unless
+`--reapply-cherry-picks` is given). So if you say
(assuming you're on 'topic')
------------
$ git rebase subsystem