Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When git rebasing two branches with some shared history, is there an easy way to have the common history remain common?

Tags:

Suppose we have the following revision graph:

A-X-Z--B
     \
      \-C

with A preceding both B and C. Further suppose I rebase A from upstream, creating a new commit A*, and then rebase both B and C onto A*. The resulting revision graph is the following:

A*-X'-Z'-B
 \
  \-X"-Z"-C

Note that the shared history is no longer shared. Is there a simple way to fix this, other than, say, rebasing B and then rebasing C onto Z' explicitly. In other words is there a better way to automatically rebase multiple branches at the same time in order to preserve shared history? It just seems a little bit awkward to have to either artificially place a tag at the split point, or manually inspect the graph to find out sha1 of the commit on which to rebase C to keep the shared history, not to mention opening up the possibility of mistakes, especially since I have to do this every time I rebase until I check the changes into the upstream branch.

like image 307
jonderry Avatar asked Apr 11 '11 22:04

jonderry


2 Answers

git rebase --committer-date-is-author-date --preserve-merges --onto A* A C
git rebase --committer-date-is-author-date --preserve-merges --onto A* A B

This should keep the common commits having the same sha1 and any merges preserved. Preserve merges is not required in this case, but will become an issue with a less trivial history.

To do this for all branches that contain A in their history do:

git branch --contains A | xargs -n 1 git rebase --committer-date-is-author-date --preserve-merges --onto A* A 

Hope this helps.

UPDATE:

This may be cleaner syntax:

for branch in $(git branch --contains A); do git rebase --committer-date-is-author-date --preserve-merges --onto A* A $branch; done
like image 131
Adam Dymitruk Avatar answered Oct 21 '22 14:10

Adam Dymitruk


The problem in the general case

I was concerned with a similar problem: rebasing a whole subhistory -- several branches, with some links between them resulting from merge:

A--B-B2-B3 <--topicB
\   /
 \-C-C2-C3 <--topicC

If I run several git rebase sequentially (for topicB and topicC), then I doubt the merges between the branches can be preserved correctly. So I would need to rebase all the branches at once, hoping that would reconstruct the merges between them correctly.

A "solution" that worked in a specific subcase

In my case, I had luck that topicC was actually merged into topicB:

A-B-----------B2-B3 <--topicB
   \         /
    \-C-C2-C3 <--topicC

so to rebase the whole subhistory, I could just run

git rebase -p A topicB --onto A*

(where A* is the new base, instead of A, as in your question; topicB is the branch name that would initially point to the old commit B3 and to the rewritten commit B3' afterwards; -p is a short name for --preserve-merges option), obtaining a history like:

A-B-----------B2-B3
   \         /
    \-C-C2-C3 <--topicC

A*-B'-------------B2'-B3' <--topicB
    \            /
     \-C'-C2'-C3'

and then reset all remaining branch refs (and tags) to the new corresponding commits (in the new subhistory), e.g.

git branch -f topicC C3'

It worked:

A*-B'-------------B2'-B3' <--topicB
    \            /
     \-C'-C2'-C3' <--topicC

(Moving the branch refs and tags could perhaps be done with a script.)

A "solution" for the general case inspired by that specific one

If topicC was not merged into topicB, I could create a fake top commit to merge all the branches I want to rebase, e.g.:

git checkout -b fake topicB
git merge -s ours topicC

and then rebase it that way:

git rebase -p A fake --onto A*

and reset the topic branches to the new commits, delete the fake branch.

Other answers

I believe that the other answer with --committer-date-is-author-date is also good and sensible, but in my experience with Git, I hadn't had that idea and solved the problem of keeping the shared history really shared after a rebase the way I have described in my additional answer here.

like image 31
imz -- Ivan Zakharyaschev Avatar answered Oct 21 '22 15:10

imz -- Ivan Zakharyaschev