Avoid this in the future
First, consider the following to avoid running into this situation again:
- Make ongoing changes in a separate branch until you have some tested and working changes ready to merge into
master
. Then you can merge or rebase a clean history into master
.
- Don't push to remote that often while doing small changes that are in progress. Just commit locally, then you can do a merge to remote with a single commit after you have everything tested and working.
This allows you to rewrite local history when you run into issues like this, if you want to, and then when you push the history will be clean.
The best answer is...
git revert
will work to add a new commit that will undo the changes in the commits that you need to undo, per Andy's answer.
And that's really the best answer most of the time - there's really no need to have a completely clean history - when you create a defect, just fix it and commit the fix.
But if you really want to know how to clean the history up, read on...
Clean up the History of master
If you are the only one using this remote, there is no problem with just rewriting history. There are lots of different ways to do this - rebase, cherry-pick, etc. For example, you can:
- checkout 4854bf7
- create a new branch (maybe call it
fix
?)
- cherry-pick 0f2d03c
- fix any merge conflicts if making a new commit here
- if you want to keep the commit, you can commit at this point, keeping the original commit message or creating a new one
- cherry-pick fba0f87
- fix any merge conflicts
- Make final commit (again, just reuse the original commit message, add to it, or make a new one)
a - b - c - d - e <-- master currently here
\
c' - e' <-- fix currently here
- At this point, if you want to move
master
branch over to this new branch and effectively delete the original master
:
- checkout
master
- reset master branch to
fix
- delete
fix
branch
If you follow #5, you end up with:
a - c' - e' <-- master
\
[b] - [c] - [d] - [e] <-- will be garbage collected eventually
This might require a force push to push these new changes up to the remote, which, of course, should only be done if no one else has these changes already or if you have coordinated this fix with everyone that could have it.
Note you could also use the above strategy with fix
to get everything the way you want it, and then just do a merge into master
from fix
. This is the same as revert, but if the changes in these commits are large and/or complex, you may want to handle the reintegration in a separate branch which is easier to handle then trying to keep track of in-process reversions.