Git Style Guide

Git Style Guide
Photo by Yancy Min on Unsplash

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.