I’ve given a talk (Git Legit) a few times that basically makes a case for atomic commits. I’ve created a cheatsheet to go with it, but then I realized there’s no point to the cheatsheet if you don’t use atomic commits. Then I realized you won’t start using atomic commits until you know the benefits. So, here’s basically a short recap of my talk if you haven’t seen it.
When I started using Git, I used it as a save point. Similarly to when you’re playing a video game, made a bunch of progress, and think “I should probably find a save point or I’ll lose all my progress”. This makes a lot of sense; we’re used to this linear, “checkpoint” mindset. But I shifted from the “checkpoint commit” mindset to atomic commits and haven’t looked back once.
Atomic commits, in short, are what they sound like: atomic. So small that they can't be broken up any further. I feel like there’s basically three features a commit needs to have to be atomic:
In short, all your tests need to be green on every commit and your application shouldn’t break. Every commit has a clear commit message and a description detailing what the purpose of these changes was. Lastly, the commit should only have changes pertaining to one fix or feature (or whatever you were working on). Don’t have commits where you “fixed that bug and also implemented the feature and then also refactored some class”.
Why, you ask? Well,
For me, one of the biggest advantages of atomic commits is spending a LOT less time solving merge conflicts. Because all your commits are concentrated to a certain part of the code, merging and rebasing become so, so much less painful. You’ll have a lot more context to the conflict and your conflicts will become smaller and much more rare. Any time you need to rebase your branch will go much more smoothly. Also, dropping and cherry-picking commits become very handy options.
Atomic commits also make code review much more pleasant. You’ll be able to review commit by commit, which will give your brain less information overload and offer a clear context of what you’re reviewing. After all, the commit is about one fix or feature only, and the message and description are clear and concise. Having this context and diminished confusion will not only make reviewing more pleasant; it will make you better at reviewing. This commit by commit review isn’t possible if you’re using git like a save point, creating checkpoint commits. That’s because they’re based on time, not on a portion of the code. Consequently, you might request changes or comment on one commit, when that particular change was already undone in a next commit. You just hadn’t gotten to that commit yet. Very inconvenient.
Lastly, your history becomes much more relevant. Without atomic commits, it becomes difficult to be descriptive in your commit messages, since you’re touching different areas of the code and not focusing on one central theme per commit. So, at a glance, people won’t be able to tell what happened in your history. Take this scenario:
You fix a bug. Commit. You realize you broke something else in the process. Another commit. You address comments on your PR. Commit. And so on, and so on. You end up with this:
Anyone looking through your history won’t know what commit actually fixed the bug. Without the context of your PR, the upper commit messages have no meaning. Every commit captures your application in a broken state, so you can’t revert comfortably. All you really wanted was to fix the bug. So amend your initial commit until you’ve actually fixed the bug.
How, you ask? Well, here’s
While on your task/feature/whatever branch, group all the changes to their relevant commits. This means you’ll need to change your commits if you make any changes to them afterwards. This is possible through interactive rebase and amending.
Please note: any time you change a commit, it changes the commit hash. This means that if you try to push it to your origin, it will be rejected (your origin thinks the commit doesn’t belong there), unless you
git push -f. You should probably protect master and any other important branches.
My cheatsheet lays out some handy commands Git offers to help you keep your commits atomic. It takes some learning and getting used to, but you’ll breathe a lot easier once you do.