I've used another way for a few times. In fact, it is a manual git rebase -i
and it is useful when you want to rearrange several commits including squashing or splitting some of them. The main advantage is that you don't have to decide about every commit's destiny at a single moment. You'll also have all Git features available during the process unlike during a rebase. For example, you can display the log of both original and rewritten history at any time, or even do another rebase!
I'll refer to the commits in the following way, so it's readable easily:
C # good commit after a bad one
B # bad commit
A # good commit before a bad one
Your history in the beginning looks like this:
x - A - B - C
| |
| master
|
origin/master
We'll recreate it to this way:
x - A - B*- C'
| |
| master
|
origin/master
Procedure
git checkout B # get working-tree to the state of commit B
git reset --soft A # tell Git that we are working before commit B
git checkout -b rewrite-history # switch to a new branch for alternative history
Improve your old commit using git add
(git add -i
, git stash
etc.) now. You can even split your old commit into two or more.
git commit # recreate commit B (result = B*)
git cherry-pick C # copy C to our new branch (result = C')
Intermediate result:
x - A - B - C
| \ |
| \ master
| \
| B*- C'
| |
| rewrite-history
|
origin/master
Let's finish:
git checkout master
git reset --hard rewrite-history # make this branch master
Or using just one command:
git branch -f master # make this place the new tip of the master branch
That's it, you can push
your progress now.
The last task is to delete the temporary branch:
git branch -d rewrite-history