Fixing non-atomic commits in git
Posted on 15/9/09 by Felix Geisendörfer
Let's say somebody else made a commit that mixes a bug fix and a new feature together. This sucks if you only want to take the bugfix to merge it into your stable branch (using git cherry-pick).
If you were using SVN you'd be screwed now. However, if you are using git you can actually clean this mess in an elegant and transparent fashion:
git revert --no-edit
git revert --no-edit HEAD git reset HEAD^ git add -p # or other commands to partially stage stuff from the current tree git commit -m "Commit A" git add -p git commit -m "Commit B"
Let's look at this step by step:
git revert --no-edit
This command creates a new commit that reverts the bad commit.
git revert --no-edit HEAD
The second 'revert' creates a new commit that basically redoes the bad commit. Why? Because now you have a "clone" of the bad commit sitting cleanly on top of your HEAD, waiting for you to be messed with. This is much better than using git reset to go back to it, because you are not deleting history (which is very likely to give you merge conflicts).
git reset HEAD^
By using git revert you can now go back to the moment before the "clone" commit was created, but still have all of its changes in your tree (=local file system). This essentially takes you back in time to the moment before your evil teammate hit the commit button and gives you the ability to yell "Nooooooo! Let me tell you about atomic commits buddy".
Now you can use 'git add' or 'git add -p' to stage all your changes for the first atomic change and commit it. Repeat the process until you've split the bad commit into nice atomic pieces.
All that is left to do now is to explain to your teammate that next time he accidentally makes a bad commit, he should use 'git reset HEAD^' right away. This way the problem can be fixed before git push distributes the bad commit to your team.
Let me know if this makes sense or if you have any question. I also accept "Git Challenges". That is if you have a problem you'd like to know how it could be solved in Git, just let me know in the comments and I'll blog about it : ).
-- Felix Geisendörfer aka the_undefined
You can skip to the end and add a comment.
Dardo: Because that will rewrite the history. There is no track record of the bad commit and it's correction and other people who are already working on top of the bad history are likely to get merge conflicts from that. Or am I missing something?
Felix: No, you are not rewriting published history. You let the bad commit alone in his branch, and people should not get merge conflicts (if you only split the patch).
And by the way, rewriting published history is not that bad in small teams when doing private projects.
Also, your trick is applicable in svn.
Dardo: Maybe I'm missing something, but git-cherry pick will do exactly nothing on a branch that already has the commit in. And about svn, that is correct, except SVN has no way for staging chunks. Nor does it have a merge mechanism I'd want to use. So no, SVN is not suitable. Unless I want to spend more time than doing the job by hand that is.
Anyway, I'm still curious to hear your git cherry-pick idea. Can you elaborate further?
Felix, my bad because I didn't explained myself very well. While you are talking about fixing the bad commit in the branch it is committed (because someone wants to cherry-pick a part of it) I'm talking about the procedure the cherry-picking guy should do to get his half-commit.
And about SVN, yes there is not chunk staging so you should do it by hand (editing patches). Anyway I'm not advocating the use of SVN but the contrary. I really love git.
@Dardo: Ok, makes much more sense now. Your solution is good in the scenario you describe ; )
Thanx for the example, sometimes you want to revert your git repository but there are some changes in some files that you want to keep (sometimes you might added good files), how can I revert my repository and keep the files and don't want to revert??
This post is too old. We do not allow comments here anymore in order to fight spam. If you have real feedback or questions for the post, please contact us.
Why don't just:
git cherry-pick the_commit
git reset HEAD^
git add -p and stuff
Also you can use git format-patch and git apply.