If you've read The Clean Coder, you probably know that writing clean code is one of the marks of a professional developer. However a true professional doesn't stop with the code, but keeps everything around the code clean, including Git history.
Git is a wonderful tool for source control, but many people don't take advantage of some of the simplest features that can keep their source code repositories clean. Too many developers out there are only using the basic features: push, pull, commit.
Pull
Let's start with the standard `git pull`.
This command will pull in any commits that are on the remote branch, but not on your local branch yet. This sounds OK, but this type of pull will perform a merge, which mixes up the git history.
Take this series of commits:
With a standard pull, your changes get inserted in the history based on their timestamps, like so:
But this reflects the time that commits are made, not the time that the remote branch receives the commits. It can be a bit confusing when you make some commits, pull from the remote branch, then push your commits up, only to see them buried under several others.
So what does Git offer us to help with this confusion? Enter,
When pulling with the rebase option, all of your commits that aren't synced with the remote branch will be taken to the side, while your local branch gets up-to-date. Then your changes are put back on top, making the timeline reflect when changes are made to the remote branch.
So with our earlier example, the history would end up looking like this,
with the newest changes to the remote branch on top.
…And Squash
There may still be something that doesn't look quite right, though. Our commits are in a logical order, but the commits that were pushed include a work-in-progress commit.
We want master to only have commits that reflect a working system. That way, we can be assured that everything will work if we need to roll back to a certain commit. Since WIP commits are inherently incomplete, we need a way to keep them off of master. For this we use,
Another rebase? Yes, but this is slightly different. This command, called an interactive rebase, will allow you to squash all of your WIP commits into a single commit that holds all of the changes for your completed feature.
Once you pull in any changes from the remote branch (using the --rebase option of course), run this rebase command with the SHA of the commit previous to your first WIP commit, and follow the instructions.
If we look at the history from earlier (after the `git pull --rebase`), we would run
This will bring up a dialog that allows you to squash commits (along with some other options). We want "pick" the first commit in the list and "squash" the rest, like so
Once you do that, you will be able to make a new message for the combined commits by commenting out the other messages and typing a new one for your complete feature.
You should, however, be careful not to squash too many commits. Commits should be fairly small, so they can be easily understood and easily reverted if necessary. Squashing a whole week's worth of commits could lead to one giant commit with a never-ending diff.
…And Push
NO! Never, ever do this. Force pushing rewrites history, which can cause commits (and whole features) to be lost. Force push all you want to your own branches that don't matter, but never force push to master. Just stick to the standard `git push`.
…And That's All (For Now)
These are only a few useful Git commands that I use every day (with the exception of force push, which I never use). There are many more commands that you can use to make your git history clean and easy to follow. Take some time to investigate a few more commands offered by Git to be a considerate Git user.
I only ask that you fully read the documentation on a Git command before using it. Using a Git command that you don't understand can end up being painful for not just you, but everyone who works on the same project.