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.