Git: how to separate out a feature branch after the fact

懵懂的女人 提交于 2019-12-07 16:23:27

If your develop branch has been published and you don't want to rewrite its history (which would be the easiest way), then you could indeed revert your changes in develop, start a new featureB branch off rebased B changes on top of develop. Something along the line:

 #given history (top - newest)
 #shaA3 <-- develop, HEAD
 #shaB2
 #shaA2
 #shaB1
 #shaA1

revert:

 git revert B2
 git revert B1

now the history contains:

 #revert of shaB1 <-- develop, HEAD
 #revert of shaB2
 #shaA3
 #shaB2
 #shaA2
 #shaB1
 #shaA1

create featureB and re-play reverted commits anew:

 git checkout -b featureB
 git rebase -i --onto develop shaB1~1 featureB

Comment out all the commits except for those belonging to feature B (shaB1 and shaB2 in our case) and complete the rebase. At this point you should have the history:

 #shaB2' <-- featureB, HEAD
 #shaB1'
 #revert of shaB1 <-- develop
 #revert of shaB2
 #shaA3
 #shaB2
 #shaA2
 #shaB1
 #shaA1

To double-check that everything went well, you can do git diff shaA3 - should be empty, git diff develop - should contain all of the desired B changes.

P.S. You can of course use cherry-pick or revert of reverts to replay the b changes into branchB, instead of the rebase interactive, e.g. when staying on develop:

 git checkout -b branchB
 git revert <revert of shaB1>
 git revert <revert of shaB2>

Will give you:

 #revert of revert of shaB2 = shaB2' <-- featureB, HEAD
 #revert of revert of shaB1 = shaB1'
 #revert of shaB1 <-- develop
 #revert of shaB2
 #shaA3
 #shaB2
 #shaA2
 #shaB1
 #shaA1
jthill

By far the cleanest method if you can manage it at all is to just write the correct histories and switch refs. Calling the base of the history you want to split X,

git checkout -b new-develop X
git cherry-pick [all the feature_A commits]
# repeat the cherry-pick as needed or convenient if there's too many 
git checkout -b new-featureB X
git cherry-pick [all the feature_B commits]
# ...

then swap names around with git branch -m, force-push, and have everyone refetch and rebase any unpublished work as necessary.

Every other option is going to leave you with a really messy history, and there's no reason to inflict that on all of posterity if it's at all reasonable to avoid it. Do avoid it if for any reason communication is a problem.

If you really can't do that, then see @MykolaGurov's nicely detailed answer.


( branchcheckout -b . . . )

Create branch featureB from current state of develop. Commit code to branch develop for any further development of featureA. Commit code to branch featureB for development of featureB. Regularly rebase branch featureB against branch develop so that it has the changes which are being added for development of featureA.

Hans-Peter Störr

If you want to avoid modifying the published history of branch develop, as in @thill's answer, and also want to avoid reverting the reverted commits, as in @Mykola Gorov's answer, you can also

  1. Create branch featureB from develop
  2. Revert the commits of feature B into branch develop. It might be sensible to do this in a single commit revert B, since this is one operation in the history.
  3. Merge branch develop into featureB with strategy ours. This does not change any files in branch featureB, but marks the reverted commit from 2. as already merged into featureB. Thus, if you later merge the feature branch featureB back into develop, the result will not contain commit revert B anymore.

When merging featureB back into develop you might want to have featureB as the first parent of the commit. (E.g. you merge develop into featureB and then set develop to featureB, not the other way around.) I suppose that way the reversion won't confuse blame etc. anymore. (?)

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!