At work we use Vincent Driessen's Git Flow branching model. Recently we found ourselves in the following situation:
- feature A had been finished and merged into
1.2.3, containing feature A, had been started
- feature B had been started after the start of the release
- there were some adjustment commits for feature A in the release
- feature B had been finished and merged into
- feature B didn't depend on any code added by feature A.
Visually, that's what we had (each column represents a branch; and each point represents a commit, with a single letter instead of its hash):
Now we just had to test and finish the release
1.2.3 (merge it into
master, create a tag
1.2.3 at the merge, and then merge the tag into
develop in order to integrate commits
J) and deploy the newly created tag into production. Then we would create another release for feature B (probably
1.2.4), test, finish and deploy it.
For reasons that are beside the point of this post, it was decided that feature B should make it into production before feature A. In order to not mess up our version numbers:
1.2.3now should contain feature B, but
- without the code of feature A commits (B, C, D and E)
- without the code of the adjustment commits F, G and J
- the "new" release
1.2.3should be tested, finished and deployed
- feature A and its adjustment commits should be in a new release (
- commit history of
developshould not be rewritten, for it's a shared branch and other features may have been started based on it.
Note: for simplicity, in the Git commands below I'll use the letter that was used to represent each commit in the images instead of the commits' hashes.
- We took note of the hashes of the feature A adjustment commits (F, G and J)
develop, we reverted the commit that merged
develop(the merge commit E), so that code related to feature A was "removed" from
<master> $ git checkout develop
<develop> $ git revert -m 1 E # creates commit M
reset --hardto reset the branch
develop, so that
release/1.2.3pointed to commit M
<develop> $ git checkout release/1.2.3
<release/1.2.3> $ git reset --hard develop
release/1.2.3, we made a commit to bump the version number (a change required by Git Flow)
<release/1.2.3> $ echo '1.2.3' > version.txt
<release/1.2.3> $ git add version.txt && git commit -m "Bump version number" # creates commit NNote that, since the
reset --hard, the commits F, G and J have been "lost". That's why we took note of their hashes in the beggining - we'll bring them back later.
After testing release
1.2.3, which now only contained code related to feature B, we finished (creating its tag and merging the tag into
develop) and deployed it
<release/1.2.3> $ git checkout master
<master> $ git merge --no-ff release/1.2.3 # creates commit O
<master> $ git tag -a 1.2.3
<master> $ git checkout develop
<develop> $ git merge --no-ff 1.2.3 # creates commit PAt this point, we have released only feature B, but now we must bring back the code of feature A, create a new release with it, and also bring back its adjustments commits.
On develop, we reverted the commit M, which was a revert commit itself. Yes, we "reverted a revert".
<develop> $ git revert M # creates commit QIn practice, that brought the code of feature A (inserted in commits B, C, D and E) back to develop.
Finally, we created the new release (
1.2.4) and brought the adjustments previously made to feature A into it
<develop> $ git checkout -b release/1.2.4
<release/1.2.4> $ git cherry-pick F # creates commit R
<release/1.2.4> $ git cherry-pick G # creates commit S
<release/1.2.4> $ git cherry-pick J # creates commit T
With a few basic Git commands (
cherry-pick, etc.) we were able to solve our problem:
- only code related to feature B has been deployed
- feature A and its adjustment commits have not been lost
- we haven't messed up our versioning numbers
- the history of the branch
develophas not been rewritten.