Git Style Guide

Git is a powerful and flexible version control system (VCS) for your code. There are several strategies you can employ to add, maintain, and modify your code. This doc explains my currently preferred git style and justifications for it.
Branch style
…or “how to name a branch”
The main branch is labeled either main
or the release version.
Feature branches are labeled <type>/<name>
where <type>
is one of the following and describes the type of change being made
- feature The addition of new behavior, consumer affecting
- bug Fix for an issue, or something not working as intended
- dev Regular maintenance-like submissions. Including updates to the developer-facing environment and dependency updates
and <name>
is a short name for what is changing.
This is similar to the git flow style which prefixes the type of change to the name of the branch “type/change”.
Pros:
- Compatible with automation for version bumping and changelogs. You can set up a github action to automatically label PRs with the type and then use that information to catalog changes in a release draft. This creates pretty changelog notes with relatively little additional work on your development flow.
- Keeps changes focused. If your branch has a purpose/type, it makes it clear what should be included in a particular branch.
Cons:
- Your changes may be harder to discover in a large org. In this case, you could adopt a
username/type/name
style, which would allow you to find all of your changes easily (especially with tab-complete).
Commit style
…or “when and how should I commit my changes”
I’ve employed two different styles of committing in my experience. I currently prefer the “google style” of “one commit, many amends” but it requires you to be a power user of git (so it is not beginner friendly).
On teams with less experience or if you anticipate needing to share your development branches across many computers, you should prefer the “many commits, one on merge” strategy.
One commit, many amends
AKA the “single commit” strategy.
This strategy employs a single commit for a single change, but you override the commit as you iterate. You can still get back history, but it’s through the less well-known git reflog
and you will need to be careful when you push since you have to use -f
.
When to commit: Make a commit for every change and overwrite your commit when you update it.
What’s in the message: A description of the change you are making. If you find you need to make more than one change, make a new commit. You will put that commit on a new branch and create a separate PR for it.
How to sync: When you sync you’ll want to use git fetch; git rebase origin/main
so that your commit is on top.
When you merge: You can employ any style when you merge since it’s a single commit. I still prefer “squash merge”.
Pros:
- Easier to cherry-pick, move, and rebase
- Easier to see what you are working on from the log since it’s always at the top.
- Dev branches look more like
main
Cons:
- Rewrites history (locally)
- Harder to share with other people since it rewrites history
This method is most like the method employed at Alphabet (aka Google). Since there isn’t a need to track “history” outside of the web-based IDE, the utility of “many commits” is diminished. It’s also not common for developers to share their work with others until after it’s been merged into main
. Nor is it common for a developer to want to share their code between multiple computers. If you find yourself in either situation, you should employ the many commits strategy.
Other approaches
Many commits, one on merge
AKA the “squash merge” strategy.
When do you commit: All of the time, for every change. You’ll be using “git commit” as a “save” function, with the added benefit that you’ll be able to undo your saves across computers and people.
What’s in the message: It doesn’t matter. Put whatever you think is relevant if you think you might undo it later. You’ll be overwriting these commits when you merge into main
so it doesn’t matter.
How to sync: Just use the standard git fetch; git merge origin/main
to update your dev branch.
When you merge: Always merge in a PR, and always use the “squash merge” strategy. This will squash all of your “work in progress” commits into one, using the title and description of your PR.
Pros:
- Clean
main
branch - each PR is squashed to one commit - No need to
-f
or force anything during development - Easier sharing between computers because the history of your branch is never overwritten
- Easier to undo changes because history is never overwritten
- Easier for newbies to git to understand and employ ->squash merge is just a button press in GitHub
- If you need to sync with
main
, use ‘merge’ instead of ‘rebase’
Cons:
- Harder to rebase on top of
main
if you’re trying to keep up to date - Merging with
main
may make it harder to see what you’ve changed in the log since your commits could be buried.
This has historically been my go-to strategy for changes due to the ability to undo and share between machines due to guaranteed history alignment. It is the most friendly to newbies to git. This strategy allows you to make all sorts of changes without ever having to use -f
which can be very dangerous (re-writing history) if you don’t know what you’re doing.
The great part about this strategy is that it allows you to have your “single commit” in main
(through squash-merge) without risking any loss of history on your development branch.
PR Style
Subject line
The subject of a PR should be a short summary of what is being done by the pull request. This is what will appear in your git history, so it should be informative but concise.
Try to keep your first line short, focused, and to the point.
Write in an imperative tone. Example
Add a versioning feature to the FooBar application
Instead of
Adding a versioning feature to the FooBar application
Body
This is a longer description of your pull request. It should cover details on what problem is being solved, why this is a good solution, and adds more context about the implementation.
Example:
Fixes #24
Documentation and version mismatch is now handled using versioning. Documentation will be published with a version number matching the package allowing users to find the applicable documentation for their version.
Order of operations:
1. Developer makes a change
2. Version bump
3. Package released
4. New docs uploaded to the version folder
5. Pointer to latest documentation updated
PR merge strategy
Prefer the squash merge
strategy.
This will create a single commit for your change and merge it on top of HEAD.
Chained commits
One feature that you may need is a chained commit - or a commit that needs to merge multiple commits at the same time.
This is possible on GitHub but requires you to do manual configuration.