Squash Commits
To squash commits together, we're going to use the extremely powerfulgit rebase
command. This is one of my_favorite_commands, but it did take me_quite_a while to become comfortable with it. At first, it was somewhat challenging for me to get a handle on how it works, and then (after reading countless warnings online) I was scared to actually use it for fear of irreparably damaging my project's Git history.
But I'm here to tell you thatgit rebase
isn't really all that difficult, and that you can bravely make changes to your repository without fear of doing any damage! (<-- quite the claim, isn't it!?!)
Let's first get a big picture idea of how squashing works, and then we'll actually do some squashing with thegit rebase
command.
The command I used is:
$ git rebase -i HEAD~3
The Rebase Command
Thegit rebase
command will move commits to have anew base. In the commandgit rebase -i HEAD~3
, we're telling Git to useHEAD~3
as the base where all of the other commits (HEAD~2
,HEAD~1
, andHEAD
) will connect to.
The-i
in the command stands for "interactive". You_can_perform a rebase in a non-interactive mode. While you're learning how to rebase, though, I definitely recommend that you do_interactive_rebasing.
Ancestry References
As a brief refresher,HEAD
indicates your current location (it could point to several things, but typically it'll either point to a branch name or directly to a commit's SHA). The~3
part means "three before", soHEAD~3
will be the commit that's three before the one you're currently on. We're using this relative reference to a commit in thegit rebase
command.
Let me demonstrate how to use this command to combine the three destination commits into one.
QUIZ QUESTION
In the command:
$ git rebase -i HEAD~3
...theHEAD~3
is an ancestry reference to a commit that will act as the new base for the commits that are being rebased.
Which of the following could be used as a reference to a base?
a SHA
a branch name
a tag name
SUBMIT: All.
That's correct! You can use any of these as a reference to the location where the newly-rebased commits will branch off from.
Force Pushing
In the video, I had to force push the branch. I had to do this because GitHub was trying to prevent me from accidentally deleting commits. Because I used thegit rebase
command, I effectively_erased_the three separate commits that recorded my addition of Florida, Paris, and Scotland. I usedgit rebase
to combine or_squash_all of these commits into one, single commit.
Usinggit rebase
creates a new commit with a new SHA. When I tried usinggit push
to send this commit up to GitHub, GitHub knew that accepting the push would erase the three separate commits, so it rejected it. So I had to_force push_the commits through usinggit push -f
.
⚠️ Force Pushing ⚠️
In this instance, force pushing my commits was necessary. But if you try to push commits and GitHub rejects them, it's trying to help you, so make sure to review what commits you're pushing_and_the commits that are on GitHub to verify you're not about to overwrite content on your remote repository accidentally!
Rebase Commands
Let's take another look at the different commands that you can do withgit rebase
:
- use
p
orpick
– to keep the commit as is - use
r
orreword
– to keep the commit's content but alter the commit message - use
e
oredit
– to keep the commit's content but stop before committing so that you can:- add new content or files
- remove content or files
- alter the content that was going to be committed
- use
s
orsquash
– to combine this commit's changes into the previous commit (the commit above it in the list) - use
f
orfixup
– to combine this commit's change into the previous one but drop the commit message - use
x
orexec
– to run a shell command - use
d
ordrop
– to delete the commit
When to rebase
As you've seen, thegit rebase
command is incredibly powerful. It can help you edit commit messages, reorder commits, combine commits, etc. So it truly is a powerhouse of a tool. Now the question becomes "_When_should you rebase?".
Whenever you rebase commits, Git will create a new SHAfor each commit! This has drastic implications. To Git, the SHA is the identifier for a commit, so a different identifier means it's a different commit,regardless if the content has changed at all.
So you should not rebase if you have already pushed the commits you want to rebase. If you're collaborating with other developers, then they might already be working with the commits you've pushed. If you then usegit rebase
to change things around and then force push the commits, then the other developers will now be out of sync with the remote repository. They will have to do some complicated surgery to their Git repository to get their repo back in a working state...and it might not even be possible for them to do that; they might just have to scrap all of their work and start over with your newly-rebased, force-pushed commits.
Recap
Thegit rebase
command is used to do a great many things.
# interactive rebase
$ git rebase -i
<
base
>
# interactively rebase the commits to the one that's 3 before the one we're on
$ git rebase -i HEAD~
3
Inside the interactive list of commits, all commits start out aspick
, but you can swap that out with one of the other commands (reword
,edit
,squash
,fixup
,exec
, anddrop
).
I recommend that you create abackup
branch_before_rebasing, so that it's easy to return to your previous state. If you're happy with the rebase, then you can just delete thebackup
branch!
Further Research
- Git Branching - Rebasing from the Git Book
- git-rebase from the Git Docs
- https://www.atlassian.com/git/tutorials/rewriting-history#git-rebase from the Atlassian blog