Help git help you

Maureen Holland

My early commits with the git version control system were monstrous, haphazard things. Hours of work bundled into a vague message, never looked at again. I began to notice pitfalls with this approach when I learned I could use git reset –-hard to restore my working tree to the last (good) commit. I did this when I got frustrated with a particular bug, only to become more frustrated by the disappearance of a lot of unrelated, uncommitted working code.

This ushered in my stream-of-consciousness commits phase, where I would commit everything, all the time:

  • Missed a typo? Add a “fix typo” commit.
  • Not finished a task but have to stop working? Add a “WIP” commit.
  • Forgot to save file? Save it. Then add an “actually save file” commit.

I was the only one who had to deal with the consequences of these commits in freelance days. I was the developer, reviewer, and debugger. Quite frankly, I only thought of myself as the developer. I appreciate the roles of reviewer and debugger more when part of a team.

Seeing a descriptive commit message history on a pull request or finding a well-defined set of changes in a main branch commit makes those jobs easier.

My unfocused, unfiltered commits would be useless to folks trying to understand the logic and context of my changes. In addition, they would be a tangled mess to roll back if a problem occurred after merging to the main branch.

Recognizing a good commit is one thing. Getting in the habit of writing them yourself is a whole other thing. I’m still learning here. If you’re just getting started, feel free to skip the years I spent writing terrible commits without knowing it and help git help you.

What is a good commit

A good commit is a logical chunk of work with a concise and consistent explanatory message. A good commit message follows your team’s style standards (i.e. line length limit, active voice) and adds context via the message body when needed.

How to write good commits

The what of a good git commit is a team decision, but the how is an individual one.

Here’s the thing: I still miss typos, finish my workday in the middle of a task, and forget to save files. I still get excited about a feature and code for hours without a thought of version control.

The difference now is that I have a bit more git knowledge, and I benefit from the example of more experienced teammates. With many thanks to those who shared their tips at a silverorange developer meeting on this topic, here are a few commands you can use to clean up your commits.

If you have too much uncommitted code:

git add -p or git reset -p

The -p flag allows you to review the difference of your modified code and select the chunks you want added or reset. This allows you to organize your commits into smaller, logical bundles of work. It is particularly useful if you want to select parts of a file to commit, but keep working on other parts.

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)

If you need to change already committed code:

git commit --amend (optional --no-edit)

The --amend flag replaces the incorrect or incomplete commit with a new commit that includes your latest changes. By default, you will be asked to enter a commit message for the amended commit. If you don’t need to change the commit message, include the --no-edit flag. This one’s a personal favourite for all those missed typos.

A message I want to change

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# Date:      Fri Oct 14 16:24:19 2022 +0100
# On branch main
# Changes to be committed:
#       modified:

git rebase -i HEAD~3

Git rebase is kind of like the swiss army knife of git commit fixing. You can remove commits, change commit messages, edit commit content… for multiple commits at once! HEAD~3 is an example of how you can select the commits you want to alter.

pick b14b68e Complete commit
reword 669afb8 A message I want to change
drop a6d958c Testing, do not commit for real

# Rebase dbff38c..a6d958c onto dbff38c (3 commands)
# Commands:
# p, pick <commit> = use commit
# r, reword <commit> = use commit, but edit the commit message
# e, edit <commit> = use commit, but stop for amending
# s, squash <commit> = use commit, but meld into previous commit
# f, fixup [-C | -c] <commit> = like "squash" but keep only the previous
#                    commit's log message, unless -C is used, in which case
#                    keep only this commit's message; -c is same as -C but
#                    opens the editor
# x, exec <command> = run command (the rest of the line) using shell
# b, break = stop here (continue rebase later with 'git rebase --continue')
# d, drop <commit> = remove commit
# l, label <label> = label current HEAD with a name
# t, reset <label> = reset HEAD to a label
# m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
# .       create a merge commit using the original merge commit's
# .       message (or the oneline, if no original merge commit was
# .       specified); use -c <commit> to reword the commit message
# These lines can be re-ordered; they are executed from top to bottom.
# If you remove a line here THAT COMMIT WILL BE LOST.

Important: --amend and rebase commands alter git history and require force pushes to update remote repos. They should not be used on shared branches (like your main branch). They work best for pull request branches before review begins.

Bonus: git stash is a great helper for things you’re happy with but not ready to commit. Stashes are bundled away to leave your working tree clean (allowing you to switch branches and do something else if needed).git stash apply will restore the code to your working tree.