Question

There are a load of commits for two bugfixes X and Y pushed to a release branch in a remote repository. The last stable release from the branch was done after commit a.

enter image description here Bugfix X is done but bugfix Y is still ongoing. The x commits are mixed up with the y commits.

The original intention was to release bugfixes X and Y together, but things have changed and it's decided we need to release the fix for X right now.

Speaking conceptually this means I have to remove all the y commits and keep all the x commits since commit a - then release bugfix X from the branch - and then reapply all the y commits again so work can resume on bugfix Y.

One solution I thought about is to just branch from commit a and cherry pick x1, x2 and x3 to the new branch, then just release from there. However, unfortunately this isn't possible as I have to release from this branch and continue working on it afterwards (don't ask me why, I just do :))

This isn't a problem I've encountered before with GIT so I'm unsure what potential patterns there are for solving it. So, what is the most elegant way to achieve my goal?

I want a solution that uses minimal steps, preserves the true history of the branch before it was applied (i.e. I strictly cannot rebase!), and does not requiring manual file editing, with the possible exception of merges.

Was it helpful?

Solution

The most elegant way is to rebase --interactive

Update Since you clarified that this isn't even an option, clearly you have revert and cherry-pick left (like an accountant: no deletions, but 'counter-bookings').

This can get tedious. However, you can use UI's to your advantage. Or you can automate things:

UPDATE I've made a complete self-contained demo Live on Coliru

If you can 'grep' the commits for issue-tracker IDs, you could automate things. Here's a demo assuming you used ticket number #724 in the commit messages. (Note how it limits the range of commits to grep by doing a..HEAD (replace a by the relevant commit-id or tag):

$ culprits=$(git rev-list --grep='#724' a..HEAD)
$ do git revert --no-edit $culprits

now do your release!

$ git tag RELEASE_20130913   # or, maybe, do the release immediately
    $ git cherry-pick $culprits 

if you mess up, just git reset --hard 'commit' (with 'commit' referring to the last commit before the first revert).


Original answer:

The most elegant way is to rebase --interactive

git rebase -i a

Many git gui tools make it exceptionally easy to do this (TortoiseGit on windows, very nice). However, I have no trouble following the instructions on screen in the command-line version (which let's you 'edit rebase-plan' in a text editor).

So your rebase will come up with e.g.

pick a comment1
pick x1 comment2
pick y1 comment3
pick x2 comment4
pick x3 comment5
pick y2 comment6

Just reorder:

pick a comment1
pick x1 comment2
pick x2 comment4
pick x3 comment5
pick y1 comment3
pick y2 comment6

Note This does rewrite history since after the x1 commit (but that's not released yet, and you can probably decide whether it's a problem with your co-workers who might have to rebase their changes onto the newer HEAD now). Just fair warning.

And done!


After the rebase, just tag the new revision x3 (with "comment5"), e.g.

git tag release_20130912 HEAD~2

and release that. It will still be from the current branch, which already has the two y1,y2 commits as well.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top