You don't have to look through too many commit histories on GitHub to see people are pretty terrible about making commits.
Now I'm not going to talk about writing commit messages. Here's the post on that. I want to talk about the equally important topic of when to make commits.
I get asked this a lot at conferences. Enough to where I made two rules I've continually put to the test.
I make a commit when:
- I complete a unit of work.
- I have changes I may want to undo.
Anytime I satisfy one of these rules, I commit that set of changes. To be clear, I commit only that set of changes. For example, if I had changes I may want to undo and changes that completed a unit of work, I'd make two commits - one containing the changes I may want to undo and one containing the changes that complete the work.
I think the second rule is pretty straightforward. So let's tackle it first. Over the course of time, you'll make some changes you know will be undone. Be it a promotional feature, patch, or other temporary change someday soon you'll want to undo that work.
If that's the case, I'll make these changes in their own commit. This way it's easy to find the changes and use git revert. This practice has proven itself time and again, I'll even commit changes I'm simply uncertain about in their own commit.
So back to the first rule.
I think generally most of us follow this rule. However, you don't have to scroll through too many repositories on GitHub to see that we're pretty bad with commits.
The discrepancies come from how we define a unit of work.
Let's start with how not to define a unit of work.
A unit of work is absolutely not based on time. Making commits every X number of minutes, hours, or days is ridiculous and would never result in a version history that provides any value outside of a chronicling system.
Yes, WIP commits are fine. But if they appear in the history of your master branch I'm coming for you!
A unit of work is not based on the type of change. Making commits for new files separate from modified files rarely makes sense. Neither does any other type abstraction: code (e.g. JavaScript vs HTML), layer (e.g. Client vs API), or location (e.g. file system).
So if a unit of work is not based on time or type, then what?
I think it's based by feature. A feature provides more context. Therein making it a far better measurement for a unit of work. Often, implicit in this context are things like time and type, as well as the nature of the change. Said another way, by basing a unit of work by feature will guide you to make commits that tell a story.
So, why not just make the first rule: I make a commit when I complete a feature?
Well, I think this a case where the journey matters. A feature can mean different things, even within the context of the same repository. A feature can also vary in size. With unit of work, you keep the flexibility to control the size of the unit. You just need to know how to measure. I've found by feature gives you the best commit.
Enjoy this post? Check out my comprehensive video series Getting Git.

Oldest comments (32)
Where you leave commits is very subjective, and best left to the programmer to decide where the logical breaks should be. I follow these rules:
1) Start a new commit with a message describing your planned change immediately before doing any real work. Use an arbitrary file with a date or version number or whatever.
2) Commit frequently, --amend is your friend.
3) Don't be afraid to amend your commit message when you have to change your mind about what you need to do, but...
4) Stick to simple incremental changes that focus on the direction in which your program is evolving.
5) A commit (or its final amendment) must be a complete change, compile and run without obvious errors for all targets, and pass all tests.
Learn to love $git rebase -i
An interactive rebase is brilliant if you haven't pushed your work to a remote yet, because it allows you to slap together those twelve commits where you were testing out stuff and making typos that you removed later, and just keep the two or three where you made actual progress and logical additions to your code.
You even get to edit all your commit messages in one handy list!
This is a decent tutorial: blog.ona.io/general/2016/02/02/squ...
Except for the part where they do a --force push. Don't do that unless you're working alone, or you hate your colleagues.
Indeed!
I've even have an alias for interactive rebase:
git rbi:)+1 for this.
git rebase -i HEAD~...Personally I like to commit often and also push to remote often to backup the work. I use personal working branch in the remote so I can safely rebase and force push without interfering with others. Once the feature is ready, commits tidied up and merged to master (meant rebased with master and integrated), the personal branch can be deleted.
I recommend to use git flow. Feature branches reintegrate into develop using rebase. Its better than the personal branch way, because you can easy switch between features and work on multiple features or keep them back for later.
Push as many times as you want to feature branches, but rewrite history on rebase into develop.
This is exactly how I'm doing it. Personal branch == feature branch, and there can be many of them. It seems that I wrote "merged to master" when in fact I'm using rebase. Wrong choice of words, I will edit it :) Thanks.
But make sure to be careful enough when you use interactive rebase on a branch where you and your teammates work on at the same time. Because your force push may affect others code base.
Our convention is that if the branch has your initials in the name (e.g. 'rl-new-nifty-feature'), then it's yours, and you're the only one allowed to rebase it. Other branches must not be rebased.
A third context I seem to hit a lot is debugging something external. Eg if CI or the server is failing for some reason, I can't fix it locally, I might commit a series of changes trying to get it to do what I want. Or if I work on it on my computer, I might push so that I can pull on my Windows machine and run the tests there. It needs to pass tests on both environments, but it needs to be committed to travel between them.
Also, I sometimes leave WIP commits in b/c they include useful experiments / places I got to that I wasn't sure if they were useful or not. The final commit might be cleaned up and elegant, but seeing that intermediate state can be useful when you return to that code or API.
Once I learned
git add -pandgit rebase -imy commits instantly improved.I also generally like to separate commits that do extensive refactorings or changes in code style etc.
But what about dealing with unnecessary large conflicts? Why give yourself another annoying thing to deal with? My team prefers to commit early and often to avoid conflicts upstream – this way we merge features instead epics.
Few weeks ago, I wrote a similar article about why and how creating small and useful commits: adopteungit.fr/en/methodology/2017...
Just do
git rebase -iand squash them into one!I don't trust the local storage, I need to push from time to time. So I do WIP commits.
How do you handle this?
git merge --squash?Based on experience I don't use and recommend rebase, it's too powerful and ppl brake things.
I
rebasebefore merging my feature branch. This allows me to clean up any previous commits accordingly.git commit --amend:)))))))