Proper Git
Git is one of the most wonderful tools developers can use to make their lives better. As Linus himself put it best: “If you actually like using CVS, you shouldn’t be here. You should be in some mental institution.”
However, Git is often underused and abused with badly written messages, junk commits and weird branching. You should not treat Git, or any other SCM tool for that matter, as a way to “Save” your code, just like you would save your progress in a game. Git is about collaboration and delivering clear sets of changes. Every commit should be a small story for your team - it should be worth telling and told well. And it doesn’t matter whether you work on your repo alone or with a team, you should use it as if the whole world will be analyzing every change.
Why commit messages are important
Let’s say you are tracking down a difficult to reproduce bug. You look at your Git history trying to find what did other people change just before the bug was spotted, and you have to look at this:
It’s full of noise, unnecessary redundancy and you would actually have to inspect most of these commits to make sure what happened there. And probably keep a separate log with commit hashes that you already looked at, because there are so many fixes, that it would be pretty challenging to remember which of them you went through.
These are actual commits from our codebase (and a couple of bonus examples from whatthecommit.com). Thankfully, they are more than a couple of years old - our Git etiquette improved a lot.
And this is how a relatively good set of commits should look like:
This log is meaningful and has much more value per line. People cared about writing good commit
messages, and most probably took extra effort to rebase, amend and rewrite Git commits to get rid
of the noise, instead of doing it the easy way: git commit -am "Fix"
Whenever you write git commit -am "Fix"
, an octocat dies.
Anatomy of bad Git commits
You’re using master
as your main branch and have a “blessed” repository, maybe on GitHub? Great!
Now, how do you work with that?
Let’s say you want to add RSS feed to the website you’re building.
You get in the zone, and implement it in a couple of hours. While doing that, you also find a non-related bug, which you squash without mercy. And at the end of that great coding session, you commit:
and then
What’s wrong with that?
- This commit should have been split into two or even more commits. When it’s hard to add
different changes, i.e. they are in same files, you can do that pretty easily with
interactive patch, using
git add -ip
. - The commit message is too long. According to Git Commit Guidelines,
short summary should be 50 chars or less. Use
git commit -av
to write your commit messages instead of doing it with-m "inline message"
- you will see what you have changed, may notice that something is missing or broken, and you will almost always end up writing a much better commit message. - Instead of writing what you did, you should write what the code will do when applied. So, rather than writing “Adding RSS feed” or “Adds RSS feed”, write in imperative: “Add RSS feed”. Tim Pope has a great post about writing good git commit messages.
- You should not commit directly to
master
unless your code was reviewed and tested, especially if you use GitHub, which provides the absolutely amazing “Pull Requests” feature. If you don’t use GitHub, then you should put your code up for a review in some tool like Review Board. And you should have used a branch - two branches, one for RSS feeds, another for the Issue #124.
If you have a CI build server, you should make it build branches, or even better,
GitHub Pull Requests and get you to know if your changes would make the master
build fail after
you merge your changes before you merge your new code to master
.
We use Jenkins with customized version of GitHub Pull Request builder plugin to provide a nearly instant feedback loop about your upcoming changes.
Nothing is final
Even though that is true, we have a rule of the thumb - nothing is final until it is merged with
master
in the blessed repository. You can use git revert <SHA1>
, but that’s equal to failing
to ride a unicycle in front of a big audience, so it should be avoided at all cost.
One commit per change
Sometimes, when you are working on a feature, you get to randomly change this and that, or perhaps
throw in a small bugfix. Resist the urge to commit everything in one chunk. You may end up with a
lot of commits, but since you should make them in your own branch, you can rewrite them using
interactive rebase. So, if you made 3 commits, you can rebase them with git rebase -i HEAD~3
.
Here is a good post about squashing commits.
Don’t Fix, Amend!
Have you ever committed something that was slightly wrong? Perhaps you included a mistype, or
forgot to git add
a newly created file? Resist the urge to add an extra commit to fix that, do
git commit --amend
instead. It’s also great for fixing mistypes in your commit messages.
And the greatest thing is when other folks review your pull request, you can simply amend the changes, without adding that pesky “Fix code review comments” commit.
Rebase is better than merge
When you pull the code from a remote repo, use --rebase
option to rebase instead of adding a
merge commit. And when you have a pull request open and GitHub cannot merge it automatically, do
git rebase master
instead of git merge master
- your merge will be embedded in the last commit.
Remote branch is still your branch
These powerful history rewriting techniques come with a price. Your commits change, and you cannot
simply sync them with remote repository anymore. So, unless someone else is coding on your branch,
you will have to use git push --force
to update all your changes. It’s scary, but it’s safe while
you don’t get too confident with it and don’t try it on your blessed master
.
More on rewriting history
You can find a set of elaborate instructions about rewriting history in this great article.
Summary
- Respect Git conventions by writing commit messages properly
- Use pull requests if you’re on GitHub
- Don’t work on
master
directly - Redo your commits when needed
Learn to use Git well as it’s one of the best tools a developer can have.