W3docs

Signing commits

Learn how to cryptographically sign Git commits and tags with GPG or SSH keys to prove authorship and earn the Verified badge. Setup included.

Signing a commit attaches a cryptographic signature that proves who created it and that its contents have not been altered. This chapter explains why signatures matter, how to generate a key, how to sign commits and tags with GPG or SSH, how to verify signatures, and how to fix the errors you'll likely hit the first time.

What signing actually proves

By default, the author name and email on a commit are just text. Git never checks them — anyone can set user.name and user.email to any value (see git config) and produce a commit that claims to be from someone else. A signature changes that: it is made with a private key only you hold, and anyone with your matching public key can confirm two things:

  • Authorship — the commit really came from the holder of that private key.
  • Integrity — if a single byte of the commit (message, tree, parent, author) changes, verification fails.

For projects where provenance matters — security-sensitive code, open-source releases, regulated environments — signed history lets reviewers trust who wrote what. On GitHub and GitLab, a verified signature earns a green Verified badge next to the commit.

Note
A signature does not encrypt anything. The commit and its contents stay fully readable; the signature only attests to origin and integrity.

Choosing GPG or SSH

Git supports two signing formats. Pick one:

  • GPG (OpenPGP) — the traditional choice. Mature, widely supported, but key management (keyrings, expiry, the gpg agent) has a learning curve.
  • SSH — available since Git 2.34. If you already use an SSH key to push, you can reuse it for signing with almost no extra setup. Simpler to manage, now the recommended starting point for most people.

Check your version first:

git --version

Signing with GPG

Generate or find a key

If you don't already have a GPG key, create one (accept the defaults, choosing a 4096-bit RSA or an ECC key):

gpg --full-generate-key

List your keys and copy the long key ID — the value after the algorithm on the sec line:

gpg --list-secret-keys --keyid-format=long
sec   ed25519/3AA5C34371567BD2 2024-01-08 [SC]
      AB1C2D3E...
uid   Jane Dev <[email protected]>

Here the key ID is 3AA5C34371567BD2.

Configure Git

git config --global user.signingkey 3AA5C34371567BD2
git config --global commit.gpgsign true

Sign a commit or tag

With commit.gpgsign on, every commit is signed automatically. To sign a single commit explicitly, use the uppercase -S flag:

git commit -S -m "Add audited payment handler"

To sign an annotated tag, use the lowercase -s:

git tag -s v2.0.0 -m "Signed release 2.0.0"
Warning
Case matters: -S (uppercase) signs a commit, while -s (lowercase) signs a tag. On git commit, lowercase -s instead adds a Signed-off-by line — a plain-text Developer Certificate of Origin, not a cryptographic signature.

Signing with SSH

If you have an id_ed25519 key (or any SSH key), point Git at the public key and switch the signing format:

git config --global gpg.format ssh
git config --global user.signingkey ~/.ssh/id_ed25519.pub
git config --global commit.gpgsign true

Commits and tags now sign with the same -S / -s flags as before — no other change needed.

Enable local verification for SSH

Unlike GPG, Git has no built-in trust store for SSH keys, so git log --show-signature reports No principal matched until you create an allowed signers file. Map each email to its public key:

mkdir -p ~/.config/git
echo "[email protected] $(cat ~/.ssh/id_ed25519.pub)" >> ~/.config/git/allowed_signers
git config --global gpg.ssh.allowedSignersFile ~/.config/git/allowed_signers

Uploading your public key to the host

The Verified badge only appears after the host has your public key on file.

  • GPG: copy the armored public key from gpg --armor --export <key-id> and paste it under Settings → SSH and GPG keys → New GPG key.
  • SSH: add the contents of ~/.ssh/id_ed25519.pub as a key of type Signing key (separate from an authentication key) in the same settings area.

The email on your key must match the commit's author email, or the host shows Unverified.

Verifying signatures

Check signatures locally with any of these:

git log --show-signature          # show signature status in the log
git verify-commit HEAD            # verify one commit
git verify-tag v2.0.0             # verify a tag

For a compact view, the %G? placeholder in git log prints a single status code per commit:

git log --pretty="%h %G? %s"
a1b2c3d G  Add audited payment handler
d4e5f6g N  Quick typo fix

G means a good (valid) signature, B bad, U good with unknown validity, and N no signature.

Common errors and fixes

  • error: gpg failed to sign the data — usually the GPG agent can't prompt for your passphrase. Export the terminal it should use: export GPG_TTY=$(tty) (add it to ~/.bashrc or ~/.zshrc). On macOS, install pinentry-mac so the agent can pop a password dialog.
  • gpg: signing failed: No secret keyuser.signingkey points at the wrong ID. Re-check with gpg --list-secret-keys --keyid-format=long.
  • GitHub shows Unverified — the commit's author email doesn't match an email attached to your uploaded key, or the key wasn't added to the host.
  • No principal matched (SSH) — you haven't set up gpg.ssh.allowedSignersFile (see above).

Common options

CommandDescription
git commit -SSigns a single commit.
git tag -s <name>Creates a signed annotated tag.
git config commit.gpgsign trueSigns every commit automatically.
git config gpg.format sshSign with an SSH key instead of GPG.
git log --show-signatureDisplays signature status in the log.
git log --pretty="%G?"Prints a per-commit signature status code.
git verify-commit <commit>Verifies a commit's signature.
git verify-tag <tag>Verifies a tag's signature.

Practice

Practice
Which statements about signing commits are correct?
Which statements about signing commits are correct?
Was this page helpful?