NAME

git-rebase - Reapply commits on top of another base tip

SYNOPSIS

  1. git rebase [-i | --interactive] [<options>] [--exec <cmd>] [--onto <newbase>]
  2. [<upstream> [<branch>]]
  3. git rebase [-i | --interactive] [<options>] [--exec <cmd>] [--onto <newbase>]
  4. --root [<branch>]
  5. git rebase (--continue | --skip | --abort | --quit | --edit-todo | --show-current-patch)

DESCRIPTION

If <branch> is specified, git rebase will perform an automaticgit switch <branch> before doing anything else. Otherwiseit remains on the current branch.

If <upstream> is not specified, the upstream configured inbranch.<name>.remote and branch.<name>.merge options will be used (seegit-config[1] for details) and the —fork-point option isassumed. If you are currently not on any branch or if the currentbranch does not have a configured upstream, the rebase will abort.

All changes made by commits in the current branch but that are notin <upstream> are saved to a temporary area. This is the same setof commits that would be shown by git log <upstream>..HEAD; or bygit log 'fork_point'..HEAD, if —fork-point is active (see thedescription on —fork-point below); or by git log HEAD, if the—root option is specified.

The current branch is reset to <upstream>, or <newbase> if the—onto option was supplied. This has the exact same effect asgit reset —hard <upstream> (or <newbase>). ORIG_HEAD is setto point at the tip of the branch before the reset.

The commits that were previously saved into the temporary area arethen reapplied to the current branch, one by one, in order. Note thatany commits in HEAD which introduce the same textual changes as a commitin HEAD..<upstream> are omitted (i.e., a patch already accepted upstreamwith a different commit message or timestamp will be skipped).

It is possible that a merge failure will prevent this process from beingcompletely automatic. You will have to resolve any such merge failureand run git rebase —continue. Another option is to bypass the committhat caused the merge failure with git rebase —skip. To check out theoriginal <branch> and remove the .git/rebase-apply working files, use thecommand git rebase —abort instead.

Assume the following history exists and the current branch is "topic":

  1. A---B---C topic
  2. /
  3. D---E---F---G master

From this point, the result of either of the following commands:

  1. git rebase master
  2. git rebase master topic

would be:

  1. A'--B'--C' topic
  2. /
  3. D---E---F---G master

NOTE: The latter form is just a short-hand of git checkout topicfollowed by git rebase master. When rebase exits topic willremain the checked-out branch.

If the upstream branch already contains a change you have made (e.g.,because you mailed a patch which was applied upstream), then that commitwill be skipped. For example, running git rebase master on thefollowing history (in which A' and A introduce the same set of changes,but have different committer information):

  1. A---B---C topic
  2. /
  3. D---E---A'---F master

will result in:

  1. B'---C' topic
  2. /
  3. D---E---A'---F master

Here is how you would transplant a topic branch based on onebranch to another, to pretend that you forked the topic branchfrom the latter branch, using rebase —onto.

First let’s assume your topic is based on branch next.For example, a feature developed in topic depends on somefunctionality which is found in next.

  1. o---o---o---o---o master
  2. \
  3. o---o---o---o---o next
  4. \
  5. o---o---o topic

We want to make topic forked from branch master; for example,because the functionality on which topic depends was merged into themore stable master branch. We want our tree to look like this:

  1. o---o---o---o---o master
  2. | \
  3. | o'--o'--o' topic
  4. \
  5. o---o---o---o---o next

We can get this using the following command:

  1. git rebase --onto master next topic

Another example of —onto option is to rebase part of abranch. If we have the following situation:

  1. H---I---J topicB
  2. /
  3. E---F---G topicA
  4. /
  5. A---B---C---D master

then the command

  1. git rebase --onto master topicA topicB

would result in:

  1. H'--I'--J' topicB
  2. /
  3. | E---F---G topicA
  4. |/
  5. A---B---C---D master

This is useful when topicB does not depend on topicA.

A range of commits could also be removed with rebase. If we havethe following situation:

  1. E---F---G---H---I---J topicA

then the command

  1. git rebase --onto topicA~5 topicA~3 topicA

would result in the removal of commits F and G:

  1. E---H'---I'---J' topicA

This is useful if F and G were flawed in some way, or should not bepart of topicA. Note that the argument to —onto and the <upstream>parameter can be any valid commit-ish.

In case of conflict, git rebase will stop at the first problematic commitand leave conflict markers in the tree. You can use git diff to locatethe markers (<<<<<<) and make edits to resolve the conflict. For eachfile you edit, you need to tell Git that the conflict has been resolved,typically this would be done with

  1. git add <filename>

After resolving the conflict manually and updating the index with thedesired resolution, you can continue the rebasing process with

  1. git rebase --continue

Alternatively, you can undo the git rebase with

  1. git rebase --abort

CONFIGURATION

  • rebase.useBuiltin
  • Unused configuration variable. Used in Git versions 2.20 and2.21 as an escape hatch to enable the legacy shellscriptimplementation of rebase. Now the built-in rewrite of it in Cis always used. Setting this will emit a warning, to alert anyremaining users that setting this now does nothing.

  • rebase.stat

  • Whether to show a diffstat of what changed upstream since the lastrebase. False by default.

  • rebase.autoSquash

  • If set to true enable —autosquash option by default.

  • rebase.autoStash

  • When set to true, automatically create a temporary stash entrybefore the operation begins, and apply it after the operationends. This means that you can run rebase on a dirty worktree.However, use with care: the final stash application after asuccessful rebase might result in non-trivial conflicts.This option can be overridden by the —no-autostash and—autostash options of git-rebase[1].Defaults to false.

  • rebase.missingCommitsCheck

  • If set to "warn", git rebase -i will print a warning if somecommits are removed (e.g. a line was deleted), however therebase will still proceed. If set to "error", it will printthe previous warning and stop the rebase, git rebase—edit-todo can then be used to correct the error. If set to"ignore", no checking is done.To drop a commit without warning or error, use the dropcommand in the todo list.Defaults to "ignore".

  • rebase.instructionFormat

  • A format string, as specified in git-log[1], to be used for thetodo list during an interactive rebase. The format willautomatically have the long commit hash prepended to the format.

  • rebase.abbreviateCommands

  • If set to true, git rebase will use abbreviated command names in thetodo list resulting in something like this:
  1. p deadbee The oneline of the commit
  2. p fa1afe1 The oneline of the next commit
  3. ...

instead of:

  1. pick deadbee The oneline of the commit
  2. pick fa1afe1 The oneline of the next commit
  3. ...

Defaults to false.

  • rebase.rescheduleFailedExec
  • Automatically reschedule exec commands that failed. This only makessense in interactive mode (or when an —exec option was provided).This is the same as specifying the —reschedule-failed-exec option.

OPTIONS

  • —onto
  • Starting point at which to create the new commits. If the—onto option is not specified, the starting point is. May be any valid commit, and not just anexisting branch name.

As a special case, you may use "A…B" as a shortcut for themerge base of A and B if there is exactly one merge base. You canleave out at most one of A and B, in which case it defaults to HEAD.

  • Upstream branch to compare against. May be any valid commit,not just an existing branch name. Defaults to the configuredupstream for the current branch.

  • Working branch; defaults to HEAD.

  • —continue

  • Restart the rebasing process after having resolved a merge conflict.

  • —abort

  • Abort the rebase operation and reset HEAD to the originalbranch. If was provided when the rebase operation wasstarted, then HEAD will be reset to . Otherwise HEADwill be reset to where it was when the rebase operation wasstarted.

  • —quit

  • Abort the rebase operation but HEAD is not reset back to theoriginal branch. The index and working tree are also leftunchanged as a result.

  • —keep-empty

  • Keep the commits that do not change anything from itsparents in the result.

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 emptymessages to be rebased.

See also INCOMPATIBLE OPTIONS below.

  • —skip
  • Restart the rebasing process by skipping the current patch.

  • —edit-todo

  • Edit the todo list during an interactive rebase.

  • —show-current-patch

  • Show the current patch in an interactive rebase or when rebaseis stopped because of conflicts. This is the equivalent ofgit show REBASE_HEAD.

  • -m

  • —merge
  • Use merging strategies to rebase. When the recursive (default) mergestrategy is used, this allows rebase to be aware of renames on theupstream side.

Note that a rebase merge works by replaying each commit from the workingbranch on top of the branch. Because of this, when a mergeconflict happens, the side reported as ours is the so-far rebasedseries, starting with , and theirs is the working branch. Inother words, the sides are swapped.

See also INCOMPATIBLE OPTIONS below.

  • -s
  • —strategy=
  • Use the given merge strategy.If there is no -s option git merge-recursive is usedinstead. This implies —merge.

Because git rebase replays each commit from the working branchon top of the branch using the given strategy, usingthe ours strategy simply empties all patches from the ,which makes little sense.

See also INCOMPATIBLE OPTIONS below.

  • -X
  • —strategy-option=
  • Pass the through to the merge strategy.This implies —merge and, if no strategy has beenspecified, -s recursive. Note the reversal of ours andtheirs as noted above for the -m option.

See also INCOMPATIBLE OPTIONS below.

  • —rerere-autoupdate
  • —no-rerere-autoupdate
  • Allow the rerere mechanism to update the index with theresult of auto-conflict resolution if possible.

  • -S[]

  • —gpg-sign[=]
  • GPG-sign commits. The keyid argument is optional anddefaults to the committer identity; if specified, it must bestuck to the option without a space.

  • -q

  • —quiet
  • Be quiet. Implies —no-stat.

  • -v

  • —verbose
  • Be verbose. Implies —stat.

  • —stat

  • Show a diffstat of what changed upstream since the last rebase. Thediffstat is also controlled by the configuration option rebase.stat.

  • -n

  • —no-stat
  • Do not show a diffstat as part of the rebase process.

  • —no-verify

  • This option bypasses the pre-rebase hook. See also githooks[5].

  • —verify

  • Allows the pre-rebase hook to run, which is the default. This option canbe used to override —no-verify. See also githooks[5].

  • -C

  • Ensure at least lines of surrounding context match beforeand after each change. When fewer lines of surroundingcontext exist they all must match. By default no context isever ignored.

See also INCOMPATIBLE OPTIONS below.

  • —no-ff
  • —force-rebase
  • -f
  • Individually replay all rebased commits instead of fast-forwardingover the unchanged ones. This ensures that the entire history ofthe rebased branch is composed of new commits.

You may find this helpful after reverting a topic branch merge, as this optionrecreates the topic branch with fresh commits so it can be remergedsuccessfully without needing to "revert the reversion" (see therevert-a-faulty-merge How-To fordetails).

  • —fork-point
  • —no-fork-point
  • Use reflog to find a better common ancestor between and when calculating which commits have beenintroduced by .

When —fork-point is active, fork_point will be used instead of to calculate the set of commits to rebase, wherefork_point is the result of git merge-base —fork-point <upstream><branch> command (see git-merge-base[1]). If _fork_point_ends up being empty, the will be used as a fallback.

If either or —root is given on the command line, then thedefault is —no-fork-point, otherwise the default is —fork-point.

  • —ignore-whitespace
  • —whitespace=
  • These flag are passed to the git apply program(see git-apply[1]) that applies the patch.

See also INCOMPATIBLE OPTIONS below.

  • —committer-date-is-author-date
  • —ignore-date
  • These flags are passed to git am to easily change the datesof the rebased commits (see git-am[1]).

See also INCOMPATIBLE OPTIONS below.

  • —signoff
  • Add a Signed-off-by: trailer to all the rebased commits. Notethat if —interactive is given then only commits marked to bepicked, edited or reworded will have the trailer added.

See also INCOMPATIBLE OPTIONS below.

  • -i
  • —interactive
  • Make a list of the commits which are about to be rebased. Let theuser edit that list before rebasing. This mode can also be used tosplit commits (see SPLITTING COMMITS below).

The commit list format can be changed by setting the configuration optionrebase.instructionFormat. A customized instruction format will automaticallyhave the long commit hash prepended to the format.

See also INCOMPATIBLE OPTIONS below.

  • -r
  • —rebase-merges[=(rebase-cousins|no-rebase-cousins)]
  • By default, a rebase will simply drop merge commits from the todolist, and put the rebased commits into a single, linear branch.With —rebase-merges, the rebase will instead try to preservethe branching structure within the commits that are to be rebased,by recreating the merge commits. Any resolved merge conflicts ormanual amendments in these merge commits will have to beresolved/re-applied manually.

By default, or when no-rebase-cousins was specified, commits which do nothave <upstream> as direct ancestor will keep their original branch point,i.e. commits that would be excluded by git-log[1]'s—ancestry-path option will keep their original ancestry by default. Ifthe rebase-cousins mode is turned on, such commits are instead rebasedonto <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 interactiverebases: commits can be reordered, inserted and dropped at will.

It is currently only possible to recreate the merge commits using therecursive merge strategy; Different merge strategies can be used only viaexplicit exec git merge -s <strategy> […] commands.

See also REBASING MERGES and INCOMPATIBLE OPTIONS below.

  • -p
  • —preserve-merges
  • [DEPRECATED: use —rebase-merges instead] Recreate merge commitsinstead of flattening the history by replaying commits a merge commitintroduces. Merge conflict resolutions or manual amendments to mergecommits are not preserved.

This uses the —interactive machinery internally, but combining itwith the —interactive option explicitly is generally not a goodidea unless you know what you are doing (see BUGS below).

See also INCOMPATIBLE OPTIONS below.

  • -x
  • —exec
  • Append "exec " after each line creating a commit in thefinal history. will be interpreted as one or more shellcommands. Any command that fails will interrupt the rebase,with exit code 1.

You may execute several commands by either using one instance of —execwith several commands:

  1. git rebase -i --exec "cmd1 && cmd2 && ..."

or by giving more than one —exec:

  1. git rebase -i --exec "cmd1" --exec "cmd2" --exec ...

If —autosquash is used, "exec" lines will not be appended forthe intermediate commits, and will only appear at the end of eachsquash/fixup series.

This uses the —interactive machinery internally, but it can be runwithout an explicit —interactive.

See also INCOMPATIBLE OPTIONS below.

  • —root
  • Rebase all commits reachable from , instead oflimiting them with an . This allows you to rebasethe root commit(s) on a branch. When used with —onto, itwill skip changes already contained in (instead of) whereas without —onto it will operate on every change.When used together with both —onto and —preserve-merges,all root commits will be rewritten to have as parentinstead.

See also INCOMPATIBLE OPTIONS below.

  • —autosquash
  • —no-autosquash
  • When the commit log message begins with "squash! …​" (or"fixup! …​"), and there is already a commit in the todo list thatmatches the same , automatically modify the todo list of rebase-i so that the commit marked for squashing comes right after thecommit to be modified, and change the action of the moved commitfrom pick to squash (or fixup). A commit matches the ifthe commit subject matches, or if the refers to the commit’shash. As a fall-back, partial matches of the commit subject work,too. The recommended way to create fixup/squash commits is by usingthe —fixup/—squash options of git-commit[1].

If the —autosquash option is enabled by default using theconfiguration variable rebase.autoSquash, this option can beused to override and disable this setting.

See also INCOMPATIBLE OPTIONS below.

  • —autostash
  • —no-autostash
  • Automatically create a temporary stash entry before the operationbegins, and apply it after the operation ends. This meansthat you can run rebase on a dirty worktree. However, usewith care: the final stash application after a successfulrebase might result in non-trivial conflicts.

  • —reschedule-failed-exec

  • —no-reschedule-failed-exec
  • Automatically reschedule exec commands that failed. This only makessense in interactive mode (or when an —exec option was provided).

INCOMPATIBLE OPTIONS

The following options:

  • —committer-date-is-author-date

  • —ignore-date

  • —whitespace

  • —ignore-whitespace

  • -C

are incompatible with the following options:

  • —merge

  • —strategy

  • —strategy-option

  • —allow-empty-message

  • —[no-]autosquash

  • —rebase-merges

  • —preserve-merges

  • —interactive

  • —exec

  • —keep-empty

  • —edit-todo

  • —root when used in combination with —onto

In addition, the following pairs of options are incompatible:

  • —preserve-merges and —interactive

  • —preserve-merges and —signoff

  • —preserve-merges and —rebase-merges

  • —rebase-merges and —strategy

  • —rebase-merges and —strategy-option

BEHAVIORAL DIFFERENCES

There are some subtle differences how the backends behave.

Empty commits

The am backend drops any "empty" commits, regardless of whether thecommit started empty (had no changes relative to its parent tostart with) or ended empty (all changes were already appliedupstream in other commits).

The interactive backend drops commits by default thatstarted empty and halts if it hits a commit that ended up empty.The —keep-empty option exists for the interactive backend to allowit to keep commits that started empty.

Directory rename detection

Directory rename heuristics are enabled in the merge and interactivebackends. Due to the lack of accurate tree information, directoryrename detection is disabled in the am backend.

MERGE STRATEGIES

The merge mechanism (git merge and git pull commands) allows thebackend merge strategies to be chosen with -s option. Some strategiescan also take their own options, which can be passed by giving -X<option>arguments to git merge and/or git pull.

  • resolve
  • This can only resolve two heads (i.e. the current branchand another branch you pulled from) using a 3-way mergealgorithm. It tries to carefully detect criss-crossmerge ambiguities and is considered generally safe andfast.

  • recursive

  • This can only resolve two heads using a 3-way mergealgorithm. When there is more than one commonancestor that can be used for 3-way merge, it creates amerged tree of the common ancestors and uses that asthe reference tree for the 3-way merge. This has beenreported to result in fewer merge conflicts withoutcausing mismerges by tests done on actual merge commitstaken from Linux 2.6 kernel development history.Additionally this can detect and handle merges involvingrenames, but currently cannot make use of detectedcopies. This is the default merge strategy when pullingor merging one branch.

The recursive strategy can take the following options:

  • ours
  • This option forces conflicting hunks to be auto-resolved cleanly byfavoring our version. Changes from the other tree that do notconflict with our side are reflected to the merge result.For a binary file, the entire contents are taken from our side.

This should not be confused with the ours merge strategy, which does noteven look at what the other tree contains at all. It discards everythingthe other tree did, declaring our history contains all that happened in it.

  • theirs
  • This is the opposite of ours; note that, unlike ours, there isno theirs merge strategy to confuse this merge option with.

  • patience

  • With this option, merge-recursive spends a little extra timeto avoid mismerges that sometimes occur due to unimportantmatching lines (e.g., braces from distinct functions). Usethis when the branches to be merged have diverged wildly.See also git-diff[1] —patience.

  • diff-algorithm=[patience|minimal|histogram|myers]

  • Tells merge-recursive to use a different diff algorithm, whichcan help avoid mismerges that occur due to unimportant matchinglines (such as braces from distinct functions). See alsogit-diff[1] —diff-algorithm.

  • ignore-space-change

  • ignore-all-space
  • ignore-space-at-eol
  • ignore-cr-at-eol
  • Treats lines with the indicated type of whitespace change asunchanged for the sake of a three-way merge. Whitespacechanges mixed with other changes to a line are not ignored.See also git-diff[1] -b, -w,—ignore-space-at-eol, and —ignore-cr-at-eol.
  1. -

If their version only introduces whitespace changes to a line,our version is used;

  1. -

If our version introduces whitespace changes but their_version includes a substantial change, _their version is used;

  1. -

Otherwise, the merge proceeds in the usual way.

  • renormalize
  • This runs a virtual check-out and check-in of all three stagesof a file when resolving a three-way merge. This option ismeant to be used when merging branches with different cleanfilters or end-of-line normalization rules. See "Mergingbranches with differing checkin/checkout attributes" ingitattributes[5] for details.

  • no-renormalize

  • Disables the renormalize option. This overrides themerge.renormalize configuration variable.

  • no-renames

  • Turn off rename detection. This overrides the merge.renamesconfiguration variable.See also git-diff[1] —no-renames.

  • find-renames[=]

  • Turn on rename detection, optionally setting the similaritythreshold. This is the default. This overrides themerge.renames configuration variable.See also git-diff[1] —find-renames.

  • rename-threshold=

  • Deprecated synonym for find-renames=<n>.

  • subtree[=]

  • This option is a more advanced form of subtree strategy, wherethe strategy makes a guess on how two trees must be shifted tomatch with each other when merging. Instead, the specified pathis prefixed (or stripped from the beginning) to make the shape oftwo trees to match.
  • octopus
  • This resolves cases with more than two heads, but refuses to doa complex merge that needs manual resolution. It isprimarily meant to be used for bundling topic branchheads together. This is the default merge strategy whenpulling or merging more than one branch.

  • ours

  • This resolves any number of heads, but the resulting tree of themerge is always that of the current branch head, effectivelyignoring all changes from all other branches. It is meant tobe used to supersede old development history of sidebranches. Note that this is different from the -Xours option tothe recursive merge strategy.

  • subtree

  • This is a modified recursive strategy. When merging trees A andB, if B corresponds to a subtree of A, B is first adjusted tomatch the tree structure of A, instead of reading the trees atthe same level. This adjustment is also done to the commonancestor tree.

With the strategies that use 3-way merge (including the default, recursive),if a change is made on both branches, but later reverted on one of thebranches, that change will be present in the merged result; some people findthis behavior confusing. It occurs because only the heads and the merge baseare considered when performing a merge, not the individual commits. The mergealgorithm therefore considers the reverted change as no change at all, andsubstitutes the changed version instead.

NOTES

You should understand the implications of using git rebase on arepository that you share. See also RECOVERING FROM UPSTREAM REBASEbelow.

When the git-rebase command is run, it will first execute a "pre-rebase"hook if one exists. You can use this hook to do sanity checks andreject the rebase if it isn’t appropriate. Please see the templatepre-rebase hook script for an example.

Upon completion, <branch> will be the current branch.

INTERACTIVE MODE

Rebasing interactively means that you have a chance to edit the commitswhich are rebased. You can reorder the commits, and you canremove them (weeding out bad or otherwise unwanted patches).

The interactive mode is meant for this type of workflow:

  • have a wonderful idea

  • hack on the code

  • prepare a series for submission

  • submit

where point 2. consists of several instances of

a) regular use

  • finish something worthy of a commit

  • commit

b) independent fixup

  • realize that something does not work

  • fix that

  • commit it

Sometimes the thing fixed in b.2. cannot be amended to the not-quiteperfect commit it fixes, because that commit is buried deeply in apatch series. That is exactly what interactive rebase is for: use itafter plenty of "a"s and "b"s, by rearranging and editingcommits, and squashing multiple commits into one.

Start it with the last commit you want to retain as-is:

  1. git rebase -i <after-this-commit>

An editor will be fired up with all the commits in your current branch(ignoring merge commits), which come after the given commit. You canreorder the commits in this list to your heart’s content, and you canremove them. The list looks more or less like this:

  1. pick deadbee The oneline of this commit
  2. pick fa1afe1 The oneline of the next commit
  3. ...

The oneline descriptions are purely for your pleasure; git rebase willnot look at them but at the commit names ("deadbee" and "fa1afe1" in thisexample), so do not delete or edit the names.

By replacing the command "pick" with the command "edit", you can tellgit rebase to stop after applying that commit, so that you can editthe files and/or the commit message, amend the commit, and continuerebasing.

To interrupt the rebase (just like an "edit" command would do, but withoutcherry-picking any commit first), use the "break" command.

If you just want to edit the commit message for a commit, replace thecommand "pick" with the command "reword".

To drop a commit, replace the command "pick" with "drop", or justdelete the matching line.

If you want to fold two or more commits into one, replace the command"pick" for the second and subsequent commits with "squash" or "fixup".If the commits had different authors, the folded commit will beattributed to the author of the first commit. The suggested commitmessage for the folded commit is the concatenation of the commitmessages of the first commit and of those with the "squash" command,but omits the commit messages of commits with the "fixup" command.

git rebase will stop when "pick" has been replaced with "edit" orwhen a command fails due to merge errors. When you are done editingand/or resolving conflicts you can continue with git rebase —continue.

For example, if you want to reorder the last 5 commits, such that whatwas HEAD~4 becomes the new HEAD. To achieve that, you would callgit rebase like this:

  1. $ git rebase -i HEAD~5

And move the first patch to the end of the list.

You might want to recreate merge commits, e.g. if you have a historylike this:

  1. X
  2. \
  3. A---M---B
  4. /
  5. ---o---O---P---Q

Suppose you want to rebase the side branch starting at "A" to "Q". Makesure that the current HEAD is "B", and call

  1. $ git rebase -i -r --onto Q O

Reordering and editing commits usually creates untested intermediatesteps. You may want to check that your history editing did not breakanything by running a test, or at least recompiling at intermediatepoints in history by using the "exec" command (shortcut "x"). You maydo so by creating a todo list like this one:

  1. pick deadbee Implement feature XXX
  2. fixup f1a5c00 Fix to feature XXX
  3. exec make
  4. pick c0ffeee The oneline of the next commit
  5. edit deadbab The oneline of the commit after
  6. exec cd subdir; make test
  7. ...

The interactive rebase will stop when a command fails (i.e. exits withnon-0 status) to give you an opportunity to fix the problem. You cancontinue with git rebase —continue.

The "exec" command launches the command in a shell (the one specifiedin $SHELL, or the default shell if $SHELL is not set), so you canuse shell features (like "cd", ">", ";" …​). The command is run fromthe root of the working tree.

  1. $ git rebase -i --exec "make test"

This command lets you check that intermediate commits are compilable.The todo list becomes like that:

  1. pick 5928aea one
  2. exec make test
  3. pick 04d0fda two
  4. exec make test
  5. pick ba46169 three
  6. exec make test
  7. pick f4593f9 four
  8. exec make test

SPLITTING COMMITS

In interactive mode, you can mark commits with the action "edit". However,this does not necessarily mean that git rebase expects the result of thisedit to be exactly one commit. Indeed, you can undo the commit, or you canadd other commits. This can be used to split a commit into two:

  • Start an interactive rebase with git rebase -i <commit>^, where is the commit you want to split. In fact, any commit rangewill do, as long as it contains that commit.

  • Mark the commit you want to split with the action "edit".

  • When it comes to editing that commit, execute git reset HEAD^. Theeffect is that the HEAD is rewound by one, and the index follows suit.However, the working tree stays the same.

  • Now add the changes to the index that you want to have in the firstcommit. You can use git add (possibly interactively) orgit gui (or both) to do that.

  • Commit the now-current index with whatever commit message is appropriatenow.

  • Repeat the last two steps until your working tree is clean.

  • Continue the rebase with git rebase —continue.

If you are not absolutely sure that the intermediate revisions areconsistent (they compile, pass the testsuite, etc.) you should usegit stash to stash away the not-yet-committed changesafter each commit, test, and amend the commit if fixes are necessary.

RECOVERING FROM UPSTREAM REBASE

Rebasing (or any other form of rewriting) a branch that others havebased work on is a bad idea: anyone downstream of it is forced tomanually fix their history. This section explains how to do the fixfrom the downstream’s point of view. The real fix, however, would beto avoid rebasing the upstream in the first place.

To illustrate, suppose you are in a situation where someone develops asubsystem branch, and you are working on a topic that is dependenton this subsystem. You might end up with a history like thefollowing:

  1. o---o---o---o---o---o---o---o master
  2. \
  3. o---o---o---o---o subsystem
  4. \
  5. *---*---* topic

If subsystem is rebased against master, the following happens:

  1. o---o---o---o---o---o---o---o master
  2. \ \
  3. o---o---o---o---o o'--o'--o'--o'--o' subsystem
  4. \
  5. *---*---* topic

If you now continue development as usual, and eventually merge topic_to _subsystem, the commits from subsystem will remain duplicated forever:

  1. o---o---o---o---o---o---o---o master
  2. \ \
  3. o---o---o---o---o o'--o'--o'--o'--o'--M subsystem
  4. \ /
  5. *---*---*-..........-*--* topic

Such duplicates are generally frowned upon because they clutter uphistory, making it harder to follow. To clean things up, you need totransplant the commits on topic to the new subsystem tip, i.e.,rebase topic. This becomes a ripple effect: anyone downstream fromtopic is forced to rebase too, and so on!

There are two kinds of fixes, discussed in the following subsections:

  • Easy case: The changes are literally the same.
  • This happens if the subsystem rebase was a simple rebase andhad no conflicts.

  • Hard case: The changes are not the same.

  • This happens if the subsystem rebase had conflicts, or used—interactive to omit, edit, squash, or fixup commits; orif the upstream used one of commit —amend, reset, orfilter-branch.

The easy case

Only works if the changes (patch IDs based on the diff contents) onsubsystem are literally the same before and after the rebasesubsystem did.

In that case, the fix is easy because git rebase knows to skipchanges that are already present in the new upstream. So if you say(assuming you’re on topic)

  1. $ git rebase subsystem

you will end up with the fixed history

  1. o---o---o---o---o---o---o---o master
  2. \
  3. o'--o'--o'--o'--o' subsystem
  4. \
  5. *---*---* topic

The hard case

Things get more complicated if the subsystem changes do not exactlycorrespond to the ones before the rebase.

NoteWhile an "easy case recovery" sometimes appears to be successful even in the hard case, it may have unintended consequences. For example, a commit that was removed via git rebase —interactive will be resurrected!

The idea is to manually tell git rebase "where the old subsystem_ended and your _topic began", that is, what the old merge-basebetween them was. You will have to find a way to name the last commitof the old subsystem, for example:

  • With the subsystem reflog: after git fetch, the old tip ofsubsystem is at subsystem@{1}. Subsequent fetches willincrease the number. (See git-reflog[1].)

  • Relative to the tip of topic: knowing that your topic has threecommits, the old tip of subsystem must be topic~3.

You can then transplant the old subsystem..topic to the new tip bysaying (for the reflog case, and assuming you are on topic already):

  1. $ git rebase --onto subsystem subsystem@{1}

The ripple effect of a "hard case" recovery is especially bad:everyone downstream from topic will now have to perform a "hardcase" recovery too!

REBASING MERGES

The interactive rebase command was originally designed to handleindividual patch series. As such, it makes sense to exclude mergecommits from the todo list, as the developer may have merged thethen-current master while working on the branch, only to rebaseall the commits onto master eventually (skipping the mergecommits).

However, there are legitimate reasons why a developer may want torecreate merge commits: to keep the branch structure (or "committopology") when working on multiple, inter-related branches.

In the following example, the developer works on a topic branch thatrefactors the way buttons are defined, and on another topic branchthat uses that refactoring to implement a "Report a bug" button. Theoutput of git log —graph —format=%s -5 may look like this:

  1. * Merge branch 'report-a-bug'
  2. |\
  3. | * Add the feedback button
  4. * | Merge branch 'refactor-button'
  5. |\ \
  6. | |/
  7. | * Use the Button class for all buttons
  8. | * Extract a generic Button class from the DownloadButton one

The developer might want to rebase those commits to a newer masterwhile keeping the branch topology, for example when the first topicbranch is expected to be integrated into master much earlier than thesecond one, say, to resolve merge conflicts with changes to theDownloadButton class that made it into master.

This rebase can be performed using the —rebase-merges option.It will generate a todo list looking like this:

  1. label onto
  2.  
  3. # Branch: refactor-button
  4. reset onto
  5. pick 123456 Extract a generic Button class from the DownloadButton one
  6. pick 654321 Use the Button class for all buttons
  7. label refactor-button
  8.  
  9. # Branch: report-a-bug
  10. reset refactor-button # Use the Button class for all buttons
  11. pick abcdef Add the feedback button
  12. label report-a-bug
  13.  
  14. reset onto
  15. merge -C a1b2c3 refactor-button # Merge 'refactor-button'
  16. merge -C 6f5e4d report-a-bug # Merge 'report-a-bug'

In contrast to a regular interactive rebase, there are label, resetand merge commands in addition to pick ones.

The label command associates a label with the current HEAD when thatcommand is executed. These labels are created as worktree-local refs(refs/rewritten/<label>) that will be deleted when the rebasefinishes. That way, rebase operations in multiple worktrees linked tothe same repository do not interfere with one another. If the labelcommand fails, it is rescheduled immediately, with a helpful message howto proceed.

The reset command resets the HEAD, index and worktree to the specifiedrevision. It is similar to an exec git reset —hard <label>, butrefuses to overwrite untracked files. If the reset command fails, it isrescheduled immediately, with a helpful message how to edit the todo list(this typically happens when a reset command was inserted into the todolist manually and contains a typo).

The merge command will merge the specified revision(s) into whateveris HEAD at that time. With -C <original-commit>, the commit message ofthe specified merge commit will be used. When the -C is changed toa lower-case -c, the message will be opened in an editor after asuccessful merge so that the user can edit the message.

If a merge command fails for any reason other than merge conflicts (i.e.when the merge operation did not even start), it is rescheduled immediately.

At this time, the merge command will always use the recursivemerge strategy for regular merges, and octopus for octopus merges,with no way to choose a different one. To work aroundthis, an exec command can be used to call git merge explicitly,using the fact that the labels are worktree-local refs (the refrefs/rewritten/onto would correspond to the label onto, for example).

Note: the first command (label onto) labels the revision onto whichthe commits are rebased; The name onto is just a convention, as a nodto the —onto option.

It is also possible to introduce completely new merge commits from scratchby adding a command of the form merge <merge-head>. This form willgenerate a tentative commit message and always open an editor to let theuser edit it. This can be useful e.g. when a topic branch turns out toaddress more than a single concern and wants to be split into two oreven more topic branches. Consider this todo list:

  1. pick 192837 Switch from GNU Makefiles to CMake
  2. pick 5a6c7e Document the switch to CMake
  3. pick 918273 Fix detection of OpenSSL in CMake
  4. pick afbecd http: add support for TLS v1.3
  5. pick fdbaec Fix detection of cURL in CMake on Windows

The one commit in this list that is not related to CMake may very wellhave been motivated by working on fixing all those bugs introduced byswitching to CMake, but it addresses a different concern. To split thisbranch into two topic branches, the todo list could be edited like this:

  1. label onto
  2.  
  3. pick afbecd http: add support for TLS v1.3
  4. label tlsv1.3
  5.  
  6. reset onto
  7. pick 192837 Switch from GNU Makefiles to CMake
  8. pick 918273 Fix detection of OpenSSL in CMake
  9. pick fdbaec Fix detection of cURL in CMake on Windows
  10. pick 5a6c7e Document the switch to CMake
  11. label cmake
  12.  
  13. reset onto
  14. merge tlsv1.3
  15. merge cmake

BUGS

The todo list presented by the deprecated —preserve-merges —interactivedoes not represent the topology of the revision graph (use —rebase-mergesinstead). Editing commits and rewording their commit messages should workfine, but attempts to reorder commits tend to produce counterintuitive results.Use —rebase-merges in such scenarios instead.

For example, an attempt to rearrange

  1. 1 --- 2 --- 3 --- 4 --- 5

to

  1. 1 --- 2 --- 4 --- 3 --- 5

by moving the "pick 4" line will result in the following history:

  1. 3
  2. /
  3. 1 --- 2 --- 4 --- 5

GIT

Part of the git[1] suite