TL;DR: I try to pragmatically craft relevant changes into single context commits. I use the power of Git's interactive mode
git add -iand
git add --interactivewhen I have made multiple changes in my working directory and want to extract my work into atomic commits.
What is an atomic commit?
I don't know what the official definition is but to me an atomic commit is a commit that is focused on one context and one context only. Sometimes this is tricky. I do this to the best of my abilities but I don't always get it right.
Disclaimer: I don't mean this in the sense of committing a single line or function. By context I mean a single topic: a feature, bug fix, refactor, task...
This is in contrast to a monolithic commit which doesn't necessarily need to be large. I see it as a commit that is tightly coupled with other changes. Like a tangled or spaghetti commit.
What do I gain from it?
I choose to make my commits atomic because they are easier to:
track: I know where they are in the history.
git log --onelineshows me all commits.
git log --grep <pattern>lets me find a commit based on a partial message.
git log <commit> -pwill jump to that commit and show previous commits.
understand: I document each change with a commit message and elaborate with an explanation if I need to.
read: it's a change focused on a single context which makes it smaller, simpler and easier to read the patch
git show <commit>or
git log <commit> -p
review: as it is a small, focused, documented change, a reviewer should easily be able to follow the code changes and keep their sanity.
revert - reverting
git revert <commit>an atomic commit will not revert unrelated changes like a monolithic commit would.
If you are keen to know more about why I choose to write atomic commits then checkout my article.
I wrote another article explaining how I think you can craft changes into small atomic commits using Git.
The essence of the article is to show you how to use interactive mode through
git add -i or
git add --interactive. This mode lets you stage (update), unstage (revert), apply specific lines or hunks (patch) and add untracked files. There are many GUI interfaces that offer this feature if you choose not to use the CLI.
When I enter this mode I start updating the files that I want staged for commit. Perhaps I add a file that is part of another change by accident. That is when I will revert the file so it goes back to an unstaged state. If I have a file that has changes relating to different contexts then I use patch to commit the file in hunks. When I quit the interactive mode, my staging area is ready for me to commit my changes. Then it is rinse and repeat until I have covered all my changes.
The more I did this, the faster I learned to make more manageable changes that can be committed easily. Making multiple changes that touch a lot of the same files will cause for some tedious work which will result in a monolithic commit because it has become such a mess to untangle.
The goal is not to create a lot of commits but pragmatically craft relevant changes for a better history, cognitive load and an easier means to rollback changes.