Following the previous post there is something that need to be known to make all those commits a bit better.
Consider the following cases :
- you make a lot of commits and end up with two or three commits that all change the same line or lines
- you review your log and see a little typographic mistake in one of the comments (next post)
- you find out that one commit introduce some bad code (next post)
First, relax, it's far better to have dozens of short commits and the need to change some of them rather than having a big commit with some lines to edit. And it's ok to edit the history, as long as the history your are changing has not been pulled or merged by someone else. Remember : commit early, branch often.
In all those case you can use git rebase -i :
$ git rebase -i HEAD~X
The X is the number of commits you want to go back in history, be careful because all the commits in that list will be rewritten by git even if you don't touch them. The direct consequence of that is that it will break history for those commits hence trigger conflicts and problems at merge time for those who pulled or merged those commits already.
A good way to use that command is when you are wrapping up your topic branch before pushing it : reviewing one last time the commits it includes and making sure all is right. Use git cli or a GUI client to check the commits, then use rebase -i if one of the previously listed case has appeared.
You got three commits changing the same line, the last one is the good one.
Let's say they are among the 4 last commits :
$ git rebase -i HEAD~4
This will launch an editor with a list of commits in this fashion :
1 pick 9594048 adding google tracking 2 pick ff985cd changing google api key 3 pick 02a424d fixing api key 4 pick 5320563 something else
As the commentaries underneath those lines explains there is several things you can do. Let's focus on squash and fixup.
The commits are listed from oldest to newest (the last one, at the bottom, of the list is the newest commit in history) and your actions on one commit will affect the one above it (the previous one in history) or itself.
squash and fixup are pretty similar, they allow to merge two or more commits into one.
If you check the 4 lines you can see that the first 3 are similar, if you could check the diffs you could see that commits #2 and #3 are only making small changes to fix a typo and the syntax. They are not bringing any functional change but they allow the code to work by fixing bugs introduced in commit #1.
squash allow you to merge the commits but reuse the commit messages of the commits being squashed in the commit being created from them.
fixup just drop the commit messages of the squashed commits, only keeping the top commit's message.
In our case we don't want to keep the squashed commits' messages so we will use fixup :
1 pick 9594048 adding google tracking 2 f ff985cd changing google api key 3 f 02a424d fixing api key 4 pick 5320563 something else
Once you save and quit the editor git will merge the commits #2 and #3 into #1, then show you commit #1's message and allow you to edit it if you want. Once this one done it'll finish the work, edit history and return to the shell prompt.
If you check the logs, it should give you something like this :
$ git log --oneline 5320563 something else 12a544d adding google tracking ...
And voila ! Your three commits changing the same lines have been merged into one and history is cleaner.
This will make the pull request simpler to read to and that's important.
squash and fixup commands are useful to simplify your commits, in practice they allow you to do tons of commits while writing a piece of code without thinking too much about how history will look. Once you got something clean and working you can easily cleanup the bad commits leaving only clean, beautiful working code. Once again this shows how cheap the commits are in git.
The other use cases will be detailed in another tip, this one is already a bit long.