Working on many projects across multiple identities can be difficult to manage. This is a procedure that leverages git aliases to set an identity at the project level for any arbitrary project. This procedure also implements GPG-based commit signing, which establishes the following security features:

  • Authenticity: The name on the commit really is the author of the code.
  • Integrity: Neither the code nor the commit metadata have been tampered with post-commit. If there is a change to the code or the commit timestamp, author, or email address, to list some examples, the hash of the commit will be different than it was before and will no longer successfully validate against the author’s public key.
  • Nonrepudiation: The author cannot deny they wrote the code since it is signed using a secret key only they have.
  • Spoofing Protection: When paired with controls like GitHub’s Vigilant Mode, commits that claim to be authored by you will be flagged since they were not cryptographically signed by you.

First, remove any existing global identity Link to heading

git config --global --unset user.name
git config --global --unset user.email
git config --global --unset user.signingkey

Require local config to exist in order to make commits Link to heading

Without the global user name and user email, git would use the system’s hostname and username to make commits. Tell git to throw an error instead, requiring you to specify an identity for every new project.

git config --global user.useConfigOnly true

Ensure GPG is installed Link to heading

# Linux or Windows WSL
sudo apt-get update
sudo apt-get install gnupg

# macOS (recommend using homebrew)
brew update
brew install gnupg

For each identity, generate GPG keys Link to heading

Generate a GPG public/private key pair:

# Newer versions of GPG
gpg --full-gen-key

# Older versions of GPG (< 2.1.17)
gpg --gen-key
  • Choose (1) RSA and RSA key type.
  • Choose key size of 4096 bits, or whatever you think is appropriate for your use case.
  • Set the key to not expire (0) unless you want to repeat this step periodically.
  • Finally, set your name and email address.
  • Comment field can be left blank.
  • It is recommended to set a password. You will be prompted for it every time the key is used.

GPG key generation output

Once the key pair is generated we need to export the public key.

Export the public keys Link to heading

For each identity, export the public key:

# Replace with the email address you just used
EMAIL="user@example.com"
SERIAL=$(gpg --list-secret-keys --keyid-format LONG $EMAIL | awk '/sec/ {split($2, a, "/"); print a[2]}')

# Export the key
gpg --armor --export $SERIAL

GPG key export

Copy the public key block and add it to your Github or Gitlab settings. With the public key, Github and Gitlab can cryptographically verify your commits, placing a “Verified” label next to each. You may also want to enable Vigilant Mode, which will place an “Unverified” label next to commits that have not been signed (otherwise no label will be shown.)

Set global git config identities Link to heading

Now we need to create the identities in git’s global config. For example:

git config --global user.gitlab.name "Your Name"
git config --global user.gitlab.email "gitlab@example.com"
git config --global user.gitlab.signingkey 543166183AE7043A

git config --global user.github.name "Your Name"
git config --global user.github.email "github@example.com"
git config --global user.github.signingkey BCF8B7A8C138D16B

git config --global user.job.name "Your Name"
git config --global user.job.email "job@example.com"
git config --global user.job.signingkey 4F3FFC37B1A027BD

git config --global user.sidegig.name "Your Name"
git config --global user.sidegig.email "sidegig@example.com"
git config --global user.sidegig.signingkey D921F8BA473CF1FC

Create git alias Link to heading

Setting a git alias will give us a new git command to use to set the identity at a project level. This really is just a script that sets a particular global identity to the local config.

git config --global alias.identity '! git config user.name "$(git config user.$1.name)"; git config user.email "$(git config user.$1.email)"; git config user.signingkey "$(git config user.$1.signingkey)"; :'

Sign all commits by default Link to heading

To sign a commit, you need to pass the -S flag. It’s easy to forget to do this, so you may want to sign all commits by default.

git config --global commit.gpgsign true

Specify git identity Link to heading

For each project, specify the git identity to use:

$ cd /path/to/git/repo
$ git config user.email # should be no response
$ git config user.github.email
github@example.com
$ git identity github # here's where the magic happens
$ git config user.email
github@example.com

That’s it! Now whenever you start a new project or work on an existing project, you can be confident that the correct name, email address, and GPG signing key are being used.

Troubleshooting Link to heading

When signing your first commit, you may get the following error:

error: gpg failed to sign the data
fatal: failed to write commit object

This happens because GPG isn’t using the current terminal. To correct:

echo "export GPG_TTY=\$(tty)" >> ~/.bashrc # or ~/.zshrc if you're using zsh
source ~/.bashrc # or ~/.zshrc if you're using zsh

References Link to heading