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

前端 未结 2 859
情深已故
情深已故 2020-11-29 05:31

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 up

相关标签:
2条回答
  • 2020-11-29 06:06

    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.

    0 讨论(0)
  • 2020-11-29 06:12
    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
    
    0 讨论(0)
提交回复
热议问题