Rebasing merged trees in Git

I have something akin to the following history (but more complicated with several merges):

        F - G - H - K - L - …
       /           /
      /       I - J - M - …
     /       /
A - B - C - D - E - …

A′- B′- C′- D′- E′

A and A′ are identical trees with different histories.

I want to rebase all branch commits to equivalent commits on the prime branch, preserving merges (presumably by manually specifying the hashes of where the equivalent commits are on the rebased tree). How do I tell K′ that it needs to merge from H′ and J′? Or do I have to recreate these merge commits manually?

If I do git rebase -p --onto B′ B L it fails to apply cleanly. I could rebase H onto B′ and M onto D′, and then recreate merge K myself, then rebase L onto K′ (and so on for other branches/merges not shown) but it would be a fair bit of work.

I have looked at several other questions but none of them featured merges.

  • How do I `git rebase -i` and prevent “You asked to amend the most recent commit, but doing so would make it empty.”?
  • How and why does “git checkout HEAD” result in different results for “--theirs” and “--ours”?
  • git rebase ate my commits ! Translate 'git reflog' output for me?
  • Revert merge without rebase
  • Rebasing a branch that has been branched from
  • Git rebase leaves current branch behind remote, want a cleaner commit history
  • How to avoid rebase hell when merge commits are in the way?
  • Rebasing after a Pull request
  • 2 Solutions collect form web for “Rebasing merged trees in Git”

    Assuming this is either not a public repository, or any other users of the repository are ok with significant rewriting, the most efficient way to accomplish this is with git filter-branch. One of the possible filters is --parent-filter, which allows you to change the parentage of specific commits, somewhat similarly to grafting or rebasing a portion of the tree, but since you can also pass the --all option, you can accomplish the effect on multiple branches in one invocation. This could also be accomplished with a --commit-filter; but that’s a more general solution intended for changing other aspects of individual commits – not just the parents. You’d probably also want to use a --tag-name-filter cat to move any tags in the portions of the tree being rewritten.

    So the final command would look something like:

    git filter-branch --parent-filter <somescript> --tag-name-filter cat -- --all

    where <somescript> is either properly quoted/escaped bash code to replace A with A' for the B commit (details on exactly how the information is provided to the script and what the results of the script should be can be found in git help filter-branch), or the name of an actual shell script that accomplishes the same.

    There is also some cleanup to be done afterwards – filter-branch leaves your original branches in place, but with new names (refs/original/...) so you can recover if something doesn’t look right. There’s plenty of information out there on how to remove dead branches once you’re satisfied that filter-branch did what you wanted, and re-packing your repository to recover the storage space, so I won’t replicate that here…

    Have you tried using interactive rebasing? Basically, do git rebase -i A and git will fire up your text editor with the commits after A listed. You can then move commits around and squash commits into each other to your heart’s content.

    Git Baby is a git and github fan, let's start git clone.