diff options
Diffstat (limited to 'Documentation/git-rebase.txt')
-rw-r--r-- | Documentation/git-rebase.txt | 245 |
1 files changed, 216 insertions, 29 deletions
diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt index 639a4179d1..794f2f39f1 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 reflog. +--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. @@ -443,8 +510,8 @@ the `rebase-cousins` mode is turned on, such commits are instead rebased onto `<upstream>` (or `<onto>`, if specified). + The `--rebase-merges` mode is similar in spirit to the deprecated -`--preserve-merges`, but in contrast to that option works well in interactive -rebases: commits can be reordered, inserted and dropped at will. +`--preserve-merges` but works with interactive rebases, +where commits can be reordered, inserted and dropped at will. + It is currently only possible to recreate the merge commits using the `recursive` merge strategy; Different merge strategies can be used only via @@ -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 |