You cannot directly squash earlier commits whilst retaining later ones.
Instead you need to create a picking branch, do some reset operations, re-commit the intermediary changes,
and cherry-pick over the relevant commits. You must start from a clean state - no modifications, no staged
items, no untracked files. Working tree clean
must appear in your git status
.
Below, my-branch-name
is the name of the branch you are on.
- Make a branch copy to perform the pick from - this also serves as a "backup" in case you need to abort squashing altogether/start again.
git branch pick/my-branch-name
- Hard-reset the changes you DO want to keep - say you want to preserve the last 2 commits:
git reset --hard HEAD~2
- Confirm that the commits to preserve still exist, and that your latest commits are the ones you want to squash
git log --all
- (at this point, if you overshot, you can pull the changes back in and try again)
git pull . pick/my-branch-name
- Then, soft-reset the commits you want to squash, say the last 3 commits, and make a new single commit
git reset HEAD~3 ; git log --all
- if all is ok, proceed:
git add .
git commit -m "new commit message"
- Now cherry-pick over the old commits from the pick branch
- We use a couple of notations to specify the range from which to pick
- relative commits notation with
~
to pick the second to last (my-branch-name~1
) commit from the picking branch - We use the
^
to specify not to pick items from before the specified commit - we pick all the way up until the tip commit (
my-branch-name
itself)
- relative commits notation with
git cherry-pick ^pick/my-branch-name~1 pick/my-branch-name
- if you did it wrong, you can do
git cherry-pick --abort
here to bring your repository back to a clean state git log --graph --all
- We use a couple of notations to specify the range from which to pick
- Picking is now done, your intermediaries are squashed and your extra commits are restored. You can now delete the picking branch if you are happy
git branch -D pick/my-branch-name
- If you need to scrap and try again, switch to the picking branch to use it as backup, delete your original branch, and then re-create it:
git checkout pick/my-branch-name
git branch -D my-branch-name
git branch my-branch-name
git checkout my-branch-name
git log
- Then, return to step 2