Home Explore Blog CI



git

14th chunk of `Documentation/git-rebase.adoc`
57def832a91e770395d7bc35d72307d1cb47c6bfed0d4db10000000100000fa6
 *---*---*-..........-*--*  topic
------------

Such duplicates are generally frowned upon because they clutter up
history, making it harder to follow.  To clean things up, you need to
transplant the commits on 'topic' to the new 'subsystem' tip, i.e.,
rebase 'topic'.  This becomes a ripple effect: anyone downstream from
'topic' 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 and
	had 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; or
	if the upstream used one of `commit --amend`, `reset`, or
	a full history rewriting command like
	https://github.com/newren/git-filter-repo[`filter-repo`].


The easy case
~~~~~~~~~~~~~

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

In that case, the fix is easy because 'git rebase' knows to skip
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
------------
you will end up with the fixed history

------------
    o---o---o---o---o---o---o---o  master
				 \
				  o'--o'--o'--o'--o'  subsystem
						   \
						    *---*---*  topic
------------


The hard case
~~~~~~~~~~~~~

Things get more complicated if the 'subsystem' changes do not exactly
correspond to the ones before the rebase.

NOTE: While 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 base
between them was.  You will have to find a way to name the last commit
of the old 'subsystem', for example:

* With the 'subsystem' reflog: after `git fetch`, the old tip of
  'subsystem' is at `subsystem@{1}`.  Subsequent fetches will
  increase the number.  (See linkgit:git-reflog[1].)

* Relative to the tip of 'topic': knowing that your 'topic' has three
  commits, the old tip of 'subsystem' must be `topic~3`.

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

------------
    $ 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 "hard
case" recovery too!

REBASING MERGES
---------------

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

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

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

------------
*   Merge branch 'report-a-bug'
|\
| * Add the feedback button
* | Merge branch 'refactor-button'
|\ \
| |/
| * Use the Button class for all buttons
| * Extract a generic Button class from the DownloadButton one
------------

The developer might want to rebase those commits to a newer `master`
while keeping the branch topology,

Title: Recovering from Upstream Rebase and Rebasinng Merges
Summary
This section discusses how to recover from an upstream rebase, including easy and hard cases, where the changes are the same or different, and how to rebase 'topic' to the new 'subsystem' tip, and also covers rebasing merges, including how to recreate merge commits to keep the branch structure when working on multiple, inter-related branches, and how to use `git rebase --onto` to transplant commits to a new base.