When you work on a new feature, or bug fix, and your code is approved; what do you do before merging it into master? Do you squash all commits, write the most beautiful commit message, and merge everything in? Or do you leave the history -- warts and all -- in place when merging?
I would hope you do the latter -- leaving every grisly commit for the world to see your process. A lot of people don't necessarily agree with this viewpoint; saying you must squash all commits, re-write your history to nice small chunks, and only merge the most beautiful git commits possible. I think that is kind of dumb for a few reasons:
- You should never be breaking your build, so why re-write history?
- When someone looks at a commit you made, they need granular detail
- Managing groomed commits is a horrible experience.
Lets talk about these points in more detail.
There was a talk I remember seeing a while ago about ID software and how they developed games so quickly in their heyday. One of the points that stood out to me the best was "Never break the build". Whenever ID commits code for other engineers to test, the build always works, and the game is always playable. This kept the development team focused on only creating working features. They never made a whole bunch of WIP (Work In Progress) commits and told themselves they could fix it later; no, the build must always pass and the game must always be playable.
I took this to heart within my own engineering practices. Whenever I commit something to a code-base, I make sure it always works, the tests pass, and the "build" never breaks. This is the easiest way to stay focused, keep commits light, and keep your features solid. When writing code this way, you should never feel the need to squash commits, re-write commit messages, or any of that crap. Just write solid commits that don't break the current build, and you're well on your way.
Stop me if you've experienced this scenario: You're debugging some very intricate piece of code, and its a very dense and hard-to-understand authentication logic. You check the commit history to get some context and it says something like:
Create shopping cart feature for users to store items before checking out
Instead of seeing a commit with some context around this small piece of code, you are hit with a squashed commit message that summarizes a whole👏entire👏 feature👏. I see this happen way too often at many organizations. There is too much emphasis on squashing your commits to keep the history neat, but I think that is dumb. Instead, imagine seeing this series of messages
Created new auth function to store information for shopping cart feature
Fixed small bug with auth function
Now we have some context! You can see the actual thought process behind each change in this function you're debugging, and now you have more information to move forward with. You can see that your colleague created a function in the auth module (or whatever) related to this big feature, then later you can see they already tried to fix one bug! Perhaps that created the bug you're currently debugging? It just adds so much to your thought process and you guess much less.
If your company or organization heavily practices re-writing history, you may have run into merge conflicts with your re-written commits. Lets walk through a scenario:
- you add a new feature and make a commit
- you add a test for the feature, and make a second commit
- someone reviews your code and finds a small issues with the feature
- you create a new commit to fix the bug, interactively rebase against master, and fixup commit #1 with this new work.
- you realize you forgot something, rebase and fixup commit #2 with more tests
- you rebase against master to get up to date and BOOM you hit a merge conflict.
It turns out that when you fixed up your commits and replayed the rewritten history of your branch, you have merge conflicts. This is a common problem I've seen many times. It's really hard to manager larger features and/or branches when you're continually fixing up commits with more work. Eventually you'll hit a merge conflict, and typically will create a whole new branch just to make everything better again.
I think in general, it should be best practice to leave your commit history in place when writing code. This will leave your colleagues with better documented commit messages, more context when dealing with bugs, and overall will be easier to work with.
Now if you'll excuse me, I have to commit this post without squashing any history!