Never thought that GIT squashing would be that important for the open source contribution. I was contributing to an open source project. I finished the feature and created a pull request. Surprise!!! the community didn’t accept my pull request. Okay. … It was not rejected because of my wrong implementation or bad code. It was rejected just because of my commit history.
So, in the open source project, this is kinda common practice to use one commit message per feature/branch per push. It actually makes sense when you look at the commit history of an open source project. Nobody wants to see your code noises in the commit history. Basically, you will have to squash multiple commits into one commit before creating pull request.
What is GIT Squashing?
Normally, we do commit as we progress. But, we don’t want to push all of them into the repository. GIT squashing compress down multiple commits into one single meaningful commit. Sometime, we do some unnecessary commits or we find some commits that should be belong to the same logical change-set. GIT squashing enables rewrite the history by keeping one commit message, and squashing those subsequent commits into the previous one.
In this tutorial we will use GIT interactive rebase command for squashing. We will use squash
and fixup
.
squash
(s
for short), which melds the commit into the previous one (the one in the line before)fixup
(f
for short), which acts like “squash”, but discards this commits message
Let’s go through an example. We have five commits in log history.
bbf19a9 my 1st commit 9added2 my 2nd commit 1a8482a my 3rd commit 68782bd my 4th commit 3c9956e my 5th commit |
We want squash the last three commits (3c9956e to 1a8482a) into the first commit (bbf19a9 )and discard the forth commit from the last (9added2). You need to run the command: interactive (-i for short ) rabsae
git rebase -i HEAD~5 |
It will show a list of each commit as below in your default git editor (either nano or vim)
pick bbf19a9 my 1st commit pick 9added2 my 2nd commit pick 1a8482a my 3rd commit pick 68782bd my 4th commit pick 3c9956e my 5th commit # Rebase 187a597..3c9956e onto 187a597 (5 commands) # # Commands: # p, pick = use commit # r, reword = use commit, but edit the commit message # e, edit = use commit, but stop for amending # s, squash = use commit, but meld into previous commit # f, fixup = like "squash", but discard this commit's log message # x, exec = run command (the rest of the line) using shell # d, drop = remove commit # # These lines can be re-ordered; they are executed from top to bottom. # # If you remove a line here THAT COMMIT WILL BE LOST. # # However, if you remove everything, the rebase will be aborted. # # Note that empty commits are commented out |
The above screen already describes with all the options and what exactly you have to use based on our requirement. In our case, we use squash and fixup.
pick bbf19a9 my 1st commit fixup 9added2 my 2nd commit squash 1a8482a my 3rd commit squash 68782bd my 4th commit squash 3c9956e my 5th commit |
You can also use s
as short for squash and f
as short for fixup.
Save and exit the editor. It will show another screen
# This is a combination of 5 commits. # This is the 1st commit message: my 1st commit # The commit message #2 will be skipped: # third commit # This is the commit message #3: my 3rd commit # This is the commit message #4: my 4th commit # This is the commit message #5: my 5th commit # Please enter the commit message for your changes. Lines starting # with '#' will be ignored, and an empty message aborts the commit. # # Date: Sat Nov 11 18:29:04 2017 -0500 # # interactive rebase in progress; onto 187a597 # Last commands done (5 commands done): # s 68782bd fifth commit # s 3c9956e sixth commit # No commands remaining. # You are currently rebasing branch 'master' on '187a597'. # # Changes to be committed: # modified: indext.html # |
The above screen describes what would be your new modified and composite commit message. You can leave as it is or you can write your new commit message. If you leave as it, all commit messages will appear except the commit: my 2nd commit
.
If you check git log, you will get one commit with all of your changes
git log |
When you shouldn’t do this?
Don’t sqaush or rewrite the history if they have been already push to public repository.
Some developers have different thought, they don’t like to squash because the commits are well-structured a series of commits which represents a logical series of meaningful changes. The main trick is not squashing everything into one gigantic commits, rather clean up or remove the unnecessary commits from git change history.
Tips:
1. To halt the squashing middle of rebase, simple run this command: git rebase --abort
2. To see git log in a cleaner way: git log --pretty=oneline
3. To rewrite the commit message, use reword rather than squash or fixup.
4. Just to change the last commit message: git commit --amend
Helpful links:
https://git-scm.com/book/en/v2/Git-Tools-Rewriting-History
http://stackoverflow.com/questions/2563632/how-can-i-merge-two-commits-into-one