Git is a very powerful tool, that can be used for more than just archiving code and give you the possibility to revert and track changes.
If you use it wisely, even if you don’t have the git-foo powers, your project can benefit from it.
Sometimes, while browsing around some code on GitHub or GitLab I struggle to find some reasoning for changes,
why those files we’re changed or why something was implemented that way.
The climax is when you found a commit message with tons of changes, spread across several files and the commit message:
misc fixes
or another vague title, that says absolute nothing about the change.
Transposing this to a company project, I believe it’s even more important, because people come and go to the projects, and we sometimes struggle to find the reasoning for changing or why the developer choose that approach instead of another one. If there’s no place with that information, we’re left in the dark and may start to do some changes and will wither be rejected at review time (in the best case) or released in production and lead to unexpected behaviour (in case the change was made due to some restrictions).
So what can we do to avoid this and save precious developer hours or, for someone like me, who’s very found of cheese and tend to forget the reasoning’s after sometime? Use, what you already need to write: the commit message!
Unleash the power of the commit messages
In order to make the best use of the of Git utilities to work with commits history, we must follow some rules. If you’re not used to them, it may look too overwhelming at the beginning, but after a fer commits you’ll start to see the light and benefit from them. Also, a good place to start enforcing is at the code review process, including the format and content of commit messages as part of the review process.
What to include in the commit message?
Every commit message should answer three simple questions:
- Why?
- How?
- What?
Why this change is needed?
This change may fix a bug, add a feature, improve performance, reliability, stability, readability, etc. This information will help the reviewer or person looking through the project history to identify the reason that lead to the code change.
How this change will fulfil his purpose?
High level description of what have been done and what approach was taken to fulfil the requirements. If there are alternative approaches that we’re tried and failed, I like to enumerate them along with their downsides to better justify the approach taken.
What will this change affect the system?
What are the side effects of this change.
The format of the commit messages
In this section, I will give an example of what is considered a good commit message. Note that I’m not the creator of this format, it’s just a mere observation of other projects best practices with some reasoning around.
Short summary with less than 50 chars
Body of the commit message, with the explanation of why we need this change, what changed and what are
the side effects. Use at most 72 chars per line and in the imperative, like "Fix bug" and not "Fixed bug".
You can split your message by paragraphs, but make sure you add an empty line between them.
- Use bullet points whenever you need to enumerate things
Issue references or commands
you may wonder that the last line means, but if you use GitHub or GitLab you can use Issue #1
to mention the issue that this commit is related or Closes #1
to state that this commit will fix and close the issue, Make sure you search for the available commands from your provider.
Reasoning about some conventions here
50 chars summary
This will be converted as the Pull/Merge request title, also allow to quickly identify the changes
Empty line after summary
That’s the obvious separation between the title and the body
72 char lines
git log
does not do any special wrapping of the commit messages. With an 80 char terminal, we’ll have to have room for indentation.- GitHub/GitLab don’t break lines
- An exception to this rule can be applied when quoting something or other text with a special formatting.
Use of the imperative
Because it matches with commit messages generated by commands like git merge
and git revert
and consistency is an odd thing.
Useful tips for a sane commit history
Never user git commit -m
This way, you will ever have a proper editor window
Never mix two changes in the same commit
If your commit addresses one issue, solve that issue in only one commit, if you need other changes, add as much commit messages as you need. For example, if an issue needs an updated on your dependencies, add one commit to update it and then another to address the issue. Remember that in Git history, like the real world, order is important.
Make sure every commit is green
Every commit should pass you CI,
Use squash and fix up
By the sum of the previous two, sometimes we need to have intermediary commits with smaller changes or experiments. It’s OK to have them, but remember to squash or fix up them, before the Pull/Merge Request.
Bonus content
Use the force… but with care
When you need to do a push
with --force
to override some changes, make sure you use --force-with-lease
to update the branch with possible changes from others, otherwise you will
override your teammate’s work with your command.
How to squash commits
Imagine that you have three temporary commits and want to merge all the changes in one commit with a single message. You start with:
git rebase -i HEAD~3
meaning you want to interactively (-i
) change the last three commits. At this point, you’ll get:
pick abfc1db01f Fix test case
pick 70e3d1ea9d Fix typo
pick b54e5706e7 Improve documentation
# Rebase 4b010b0815..3e46cf7533 onto 4b010b0815 (3 commands)
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
# d, drop = remove commit
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out
So you’ll change the command from pick
to squash
(s
) or fixup
(f
) if you want to discard the commit messages.
After saving, you’ll have an editor to input your commit message and voilá.
If you have pushed the commits to your remote (server), you’ll need to do a --force-with-lease
to override the old commits.
How to configure Vim as your git editor and enforce this rules
Add the editor to the core section of your .gitconfig
file:
[core]
editor = vim
Personally, I would recommend to use vim-fugitive plugin, but adding this line to your .vimrc
also work as a minimalist solution:
autocmd Filetype gitcommit setlocal spell textwidth=72
To know more about vim-fugitive check it’s page