How to make your git history beautiful

Oleksandra Holovina
Vivid Seats Tech Blog
5 min readNov 13, 2019

I’m sure you’ve already seen this picture before (I hope not in your git history, and if so then definitely read this article):

Unfortunately, it takes more than just one command to save you from trying to untangle your thought process a year later (or even worse, someone else’s). Some of the steps may seem obvious but, all together they make a huge difference.

For simplicity sake, let’s assume that you’re working with a git feature branch workflow, where there is a master branch. Every new task you do is created in a separate feature branch, which eventually will be combined with master.

Step 1 — Always make sure you pull latest master

When I started using git, I kept forgetting this step and ended up fixing a lot of merge conflicts. Since I wasn’t the one who did all those changes that I just pulled in, I couldn’t be sure that I resolved the conflicts correctly. This may easily lead to accidental overriding someone’s work and/or new bugs.

Apply these commands:

1) git checkout master (in case you are at a different branch)

2) git pull origin master

Step 2 — Create feature branch first

Have you ever been in a situation where you came up with a brilliant idea to quickly refactor a small portion of code but the timing was wrong?

I always think: “Oh, I’ll just make a few changes before I forget, and then create a user story (I need it to create a branch name, which I will talk about later in the article). My changes will be moved automatically, no harm, right?”.

However, then lunch time comes, friends are waiting, and you don’t end up creating a user story. Not a problem, you make a local commit to master and move it later. When you come back, you obviously forget about it, pull remote master and a merge commit appears. We’ll discuss merge commits in a bit.

So, always create a feature branch before you start coding:

1) git checkout master (in case you are at a different branch)

2) git checkout -b new-branch-name

Step 3 — Never push to master

This rule is probably set in your repository (if not, do it). This will prevent changes from being pushed without a review.

Step 4 — Name your branches well

At my company, we use Jira as tool to create stories. You may be using a different one, but all of them assign an id to every story. I work on a team called DOT. So, all stories are named like DOT-1, DOT-2, etc.

I highly recommend starting your branch name with this id. That way, you can configure a git hook that will append it to every commit message. The next step will tell you more about why git hooks are useful.

Step 5 — Use meaningful commit messages

Imagine that, in a year, you’ll need to implement a feature similar to what someone has already done. You can easily find it in Jira (or another tool), but you still need some details in the code.

Assume that all commit messages look like this: “added file”, “refactored”, “added tests”, “made changes per dev review”. Even if you remember the approximate date when the changes were made, you will still have to look through a bunch of commits to understand what exactly was done there.

Instead, if every commit started with a story id, it really helps save time searching. That’s why creating a git hook that will apply a story id to a commit message is so useful.

Step 6 — Limit number of commits in a feature branch

Let’s say you followed the rule at a step 5 and found the commits related to the story you’re interested in, but instead of having everything in one or several meaningful commits, your history looks like this:

ID-222 Create banner.html

ID-222 Add title to banner.html

ID-222 Merge commit

ID-222 Fix typo in banner.html title

ID-222 Merge commit

ID-222 Move banner.html to a home directory

Yes, it is very descriptive but do you really want to know all these details? You especially don’t need to know when someone pulled master (merge commits).

It doesn’t mean that you shouldn’t commit anything before you’re ready. The idea of this approach is to squash these six commits into one with an appropriate commit message before creating a pull request.

E.g. ID-222 Create a banner fragment

Sometimes it does make sense to have several commits. For example,

ID-333 Refactor <short description of what was refactored>

ID-333 Implement <short description of functionality>

This may help during code review because neither people like to see hundreds of changed files (especially if the functional change is just in a few lines) nor can they look through them attentively.

To do squash your commits successfully:

1) git rebase -i origin/master

2) You will see a screen with options. Choose “squash” on every commit except first one (or “fixup” to preserve the commit message of the first one)

3) git push origin <your branch name> — force-with-lease (force-with-lease saves you from accidentally overriding commits if someone is working on the same branch)

Step 7 — Don’t pull master into your branch

In order to combine your changes with the ones in master, you need to have all of master’s changes as well. However, no one likes reviewing a pull request with dozens of extra commits. Sometimes, when you had some conflicts, a merge commit appears, which does nothing but clutters your feature branch history. This happens when you pull master, instead use rebase:

1) git fetch — all (to get changes from all remote branches)

2) git rebase origin/master (this puts your commits on top of master’s)

I recommend squashing your commits before rebasing in order to resolve conflicts (if any) just once instead of for every commit.

Summary

These were seven easy steps to save you and your colleagues from spaghetti in your git history. Even if you don’t really care now, trust me, in a while you will be glad that you decided to keep your git commits in order!

--

--