addition, the following pairs of options are incompatible:
* --keep-base and --onto
* --keep-base and --root
* --fork-point and --root
BEHAVIORAL DIFFERENCES
-----------------------
`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 '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`).
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|stop)` option for changing the behavior
of handling commits that become empty.
Directory rename detection
~~~~~~~~~~~~~~~~~~~~~~~~~~
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 offset, 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` or `zdiff3`, 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.