Overview

Version control is an essential part of maintaining any project, open-source or internal. It can be easy to dismiss the importance of commit logs, squashing commits and revision history but a small effort will go a long way in ensuring a codebase's success.

For some, the thought of amending or squashing commits can also be intimidating. Most developers will use version control on a daily basis, meaning it is more important than ever to spend some time learning how to make it work for us, rather than work against it in frustration.

This web page aims to provide tips, tricks and commands for Git that will empower you to use Git with confidence.

Writing commits

Commit message guidelines

Writing a great commit message consists of seven rules ("How to Write a Git Commit Message" cbea.ms):

  1. separate the subject from the body with a blank line;
  2. limit the subject line to 50 characters;
  3. captialize the subject line;
  4. do not end the subject line with a period;
  5. use the imperative mood in the subject line;
  6. wrap the body at 72 characters; and
  7. use the body to explain what and why versus how.

Personally, using imperative mood is one of the fastest ways to improve a Git commit message.

A properly formed Git commit subject line should always be able to complete the following sentence.

"How to Write a Git Commit Message" cbea.ms

An example of this would be:

When applied, this commit will refactor the CSS to remove a bug with padding.

If a Git commit's subject were "Bug fixes for CSS padding", this would read as:

When applied, this commit will bug fixes for CSS padding.

The latter example does not read well; the purpose of this writing style is to ensure all commits have a similar voice and increase the clarity of changes.

Setting up your commit editor

If you use an external application to commit, you do not need to set up a commit editor as you write your commits within the application itself.

By default, Git will use the default text editor defined via a shell environment variable. If none exist, it will fall back to vi.

Visual Studio Code

To set Visual Studio Code as the default Git editor, run the following command:

git config --global core.editor code

Vim

Even though Git will fall back to vi you should still set the core editor to vim or nvim to ensure you load your preferred environment and configuration:

git config --global core.editor vim

You can also set up helpful Git character limit guides.

Graphical User Interface vs Command Line Interface

There are various graphical user interface (GUI) implementations of Git, such as SourceTree, GitKraken and Tower Git Client.

These tools can help make complicated Git operations less daunting and can make a great asset to a developer's toolset. Many offer things the Git command line interface (CLI) cannot, such as branch visualisations and easier diff navigation.

Why use a GUI?

GUIs excel at making a Git history more accessible. They can visualise a repository's history, implement drag-and-drop functionality, introduce right-click contextual menus to perform specific actions and more.

Why use the CLI?

The strongest advantage of using the CLI is having a transferable knowledge of Git commands.

The CLI is also perfect for simple tasks like pushing and pulling.

Which should I use?

Whichever you are more comfortable with.

Git configuration

The global configuration file

Git's global configuation can be found in the .gitconfig file in your home directory. An example of this file will look like:

[core]
  excludesfile = ~/.gitignore
[user]
  name = Name
  email = name@email.com

.gitconfig can be read on the CLI using the git config command with the --global flag, e.g.:

$ git config --global -l
> core.excludesfile=~/.gitignore
\ user.name=Name
\ user.email=name@email.com

Specify a config key to show only that value in .gitconfig:

$ git config --global user.name
> Name

Supply a value after specifying the config key to update that value in .gitconfig:

$ git config --global user.name "Name"

You can omit the --global flag if you prefer to configure Git projects locally.

Aliases

Git aliases are a great tool for anyone who uses the CLI regularly. They can be used to create shorthand versions of common commands or entirely new commands for complex or lengthy operations.

Examples of shorthand commands include:

[alias]
  a = add
  b = branch
  c = commit
  ch = checkout
  s = status

An examples of a new command would be creating a command to quickly visualise the history:

[alias]
  tree = log --oneline --graph

Commit history

Commit history is an incredibly important part of software development. Good commit history can serve as:

A personal three-step approach is to only commit if:

  1. the commit is an individual piece of work;
  2. the commit does not fail tests, linting or typechecks; and
  3. the commit does not retroactively fix bugs when it can be squashed instead.

A commit is an individual piece of work

Each commit should be an individual change. That is, the smallest possible change that makes sense.

Consider there are two files:

  1. one that fetches data from an external API; and
  2. one that renders a component using the fetched data.

If a piece of work requires the fetch to return additional data, the commit log could look like this:

feat: return a `message` prop from the external API
feat: render the `message` prop in the component

A commit never has broken tests, linting or typechecks

Every commit should be able to be deployed. That means every commit should pass all tests, linting and typechecking.

This reinforces the idea of every commit being an individual piece of work, with its own tests and checks put in place as necessary.

This allows pull request reviewers to see the full extent of a single change in a single commit diff. This is incredibly useful when a pull request contains several commits for larger pieces of work where many files are changed.

A commit should not retroactively fix bugs when it can be squashed.

Commits should build on one another but they should not retroactively fix bugs when they can be squashed. To illustrate this, below is an example commit history:

feat: return a `message` prop from the external API
feat: render the `message` prop in the component
fix: use the `text` field instead of `message` in the external API
fix: render the `text` field instead of the `message` prop in the component

This commit history can be confusing when reviewing by commit as it requires the reviewer to context switch from the API changes to the component and then back to the API and component again.

A better approach is to use Git's rebase operation to merge the revised changes into two commits:

feat: return a `text` prop from the external API
feat: render the `text` prop in the component

Works cited

"How to Write a Git Commit Message" cbeams, cbeams, https://cbea.ms/git-commit/. Accessed 15 November 2022.

"8.1 Customizing Git - Git Configuration", Git, Git, https://www.git-scm.com/book/en/v2/Customizing-Git-Git-Configuration Accessed 20 November 2022.

"Visual Studio Code on macOS" Visual Studio Code, Microsoft, https://code.visualstudio.com/docs/setup/mac. Accessed 20 November 2022.

"Can changelogs be bad", Keep a Changelog, Olivier Lacan https://keepachangelog.com/en/1.0.0/#bad-practices Accessed 20 November 2022.