A how-to guide for a clean, organized, beautiful git commit history

I’ve been taught several times about best practices for committing code changes grouped into logical chunks, and I start every new feature branch with the intention of writing organized commits based on these principles. In practice, however, my commit history always looks like more of a hot mess than I had originally intended.

While it’s tempting to write off commit hygiene as more effort than it’s worth, authoring clean and organized commits that logically group code changes has substantial benefits:

  • It cuts out the noise of the development process and brings clarity to the development outcome.
  • The act of organizing commits gives the author an opportunity to reflect and ensure that their changes make sense and are within scope.
  • Clean commits offer reviewers the option to go through the changes commit-by-commit, as well as or instead of viewing the whole diff, which can be a really useful tool for cueing the reader into which changes are important.
  • Organized commits lend themselves to writing good commit messages, because if the change is isolated, intentional, and has obvious purpose, the description practically writes itself.
  • Finally, your choice to better organize your commits can enable other engineers to use tools like cherry-pick to better organize their commits. In other words, organizing commits is like washing your hands – it’s something everybody should be doing to improve the whole group’s [code] hygiene.

For some tasks, writing code in a way that lends itself to organized commits comes naturally, but for more complex features this can be anywhere from difficult to impossible. If I want to consistently have a useful commit history at the end of the development process, like a Hollywood celebrity I sometimes have to fake it in post-processing.

Thankfully, git provides several tools to make this easy. Here is a list of some of the tools I find most useful, including when and how to use them.

Amend your most recent commit

What it does

Augments your most recent commit message, contents, or both. It will lump any staged changes in with your most recent commit contents, and will give you an opportunity to make inline edits to that commit’s message.

When to use it

You just thought of a better way to phrase your commit message. You noticed some weird extra whitespace after a change you just made. Amending is useful any time you want to correct a small error or make a small improvement.

How to use it

git commit --amend will add any staged changes to your most recent commit and launch an editor to edit the message

Further reading

Changing Last Commit Using –amend – PawanGaria

Interactive rebase

What it does

Launches an interactive editor that allows you to re-order, combine, and remove commits as well as edit commit messages.

When to use it

You’ve created a new commit for every single trivial change in your feature branch. You have several commits that introduce a change only to later change it again or just revert it. Reviewers are faced with a formidable 18 commits with no guidance on where the bulk of the changes live. Interactive rebasing allows you to logically group those commits by combining multiple commits that have all made related changes or deleting ones that are no longer wanted.

How to use it

git rebase -i HEAD~{n}, where {n} is the number of previous commits you would like to rewind. This will launch an interactive editor with detailed instructions, but I’ll include only a few highlights:

  • Change the order of commits simply by cutting and pasting
  • Delete or comment out (#) a commit to remove it entirely from your history
  • Replace pick with f or fixup to combine a commit with the one immediately before it (remember, you can change the order to combine it with any commit). Replace it with s or squash if you want to do combine commits while also updating the commit message.
  • Include the flag --preserve-merges (or -p) when you run your rebase if you’ve been using a merge-based workflow. This will help you avoid resolving rebase conflicts on merge commits (otherwise, you will be very, very sad).

Further reading

Git Interactive Rebase, Squash, Amend – ThoughtBot

Cherry-pick

What it does

Creates a new commit on your current branch that applies the contents of an existing commit from a different branch.

When to use it

You are branching from a coworker’s work because they have done an important refactor in part of the code base that you are working with. However, they’ve also introduced several other changes in the same branch that are irrelevant to your work. Since you only care about a subset of the changes on their branch, you would cherry-pick only the commit(s) containing the refactor.

How to use it

git cherry-pick {sha} where {sha} is the unique ID for the commit you would like to apply to your branch. Repeat for every single commit you would like to cherry-pick.

Further reading

Understanding Git Cherry-pick – CodeMentor

sb-tech-site-technology

Add patch

What it does

Launches an interactive editor that allows you to stage your local changes one chunk at a time.

When to use it

You’ve introduced several local changes (a small refactor, a new feature, and tests for each) that don’t logically belong in the same commit, but may live in the same file(s). Add patch will allow you to stage and commit your changes in logically grouped chunks.

How to use it

git add --patch (or -p) will launch an editor that will group your unstaged changes and display one at a time. Changes are grouped into hunks based on git heuristics, but you can split a hunk into smaller pieces, provided that the changes are not on adjacent lines. If you have made two unrelated changes on adjacent lines, you can manually edit the hunk within the interactive editor. After you’ve staged a logical grouping of your changes, commit and then repeat.

Further reading

Git add –patch – Nuclear squid

Reset Origin

What it does

Rewinds all commit history compared to the origin and surfaces the changes in your working tree.

When to use it

You want to split changes in a single commit into multiple commits, or keep only some changes from a single commit. This is particularly powerful when combined with add patch to turn one or more bloated commits into new commits with logically grouped changes. When your commit history is so messy that it feels beyond rescuing, resetting your branch to origin/master is a wonderful git-grenade. It will allow you to keep all of your changes but re-write your commit history from scratch.

How to use it

git reset {sha} to rewind to a particular commit, or git reset origin/{parent branch} to rewind all the way back to a parent branch.

Further reading

Reset – Git documentation

Some final notes

Assuming you have at some point pushed your changes to the remote origin branch, using any of these tools will require a force push (-f or --force). This can be scary at first, and the internet is full of strongly-worded warnings against force pushing.

However, provided you are safely sandboxed on a feature branch, the damage you can do with a force push is minimal. If I am the only person working on a feature branch, I won’t hesitate to force push as often as I’d like. If I am working with other engineer(s) on a single branch, it can be a bit easier to permanently overwrite changes with a force push. Some helpful ways to prevent lost changes are:

  • communicate that you plan to use a rebase workflow that involves force pushing
  • pull from the remote origin frequently
  • check that somebody else has not recently pushed to the remote branch before doing a force push
  • ask a colleague to check out an up-to-date copy of the branch before a force push or make a second local copy yourself before a rebase. This way there is a local copy to revert to in the case that things go terribly wrong
  • give your most important branches (such as master) a ‘protected’ status, which will create automatic guardrails to prevent force pushing to those branches

Just before I am ready to open a pull request up for review, I will use these tools to re-organize my commits. I find that they are incredibly helpful for turning my change history into useful documentation, and I hope you find value in them too!

Ready to start your career at Simply Business?

Want to know more about what it’s like to work in tech at Simply Business? Read about our approach to tech, then check out our current vacancies.

Nicole Rifkin

This block is configured using JavaScript. A preview is not available in the editor.