Git Course 0%

Signed commits and verification

Expert ≈ 12 min

What you will learn

  • Why the author field is not identity
  • How to set up SSH signing in five minutes
  • How verification works locally and on the platforms

After this lesson you can

  • My commits are signed, and I know what a Verified badge does and does not prove

Why this matters

The author of a commit is whatever user.name and user.email say. Anyone can set them to anything, so on its own the author field is a label, not a claim. Signing turns it into one.

The problem, in one command

Terminal
$ git -c user.name="Ana Lopez" -c user.email="ana@northwind-trails.example" commit -m "docs: something"

That commit is now attributed to Ana, from anyone's machine. Push access is required to publish it, which is the real control, but within a repository the author field proves nothing by itself.

SSH signing

Signing used to mean GPG and was correspondingly rare. SSH signing, available since Git 2.34, uses the key you already have (lesson 8.2) and takes five minutes.

  1. Tell Git to sign with SSH and which key to use:

    Terminal
    $ git config --global gpg.format ssh
    $ git config --global user.signingkey ~/.ssh/id_ed25519.pub
    $ git config --global commit.gpgsign true
  2. Commit as usual. Nothing changes visibly.

  3. To verify locally, Git needs to know which keys to trust, in an allowed signers file:

    Terminal
    $ git config --global gpg.ssh.allowedSignersFile ~/.ssh/allowed_signers

    Each line is an email and a public key:

    ~/.ssh/allowed_signers
    ana@northwind-trails.example ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA…
  4. Upload the same public key to the platform as a signing key, which is a separate list from authentication keys.

Without step 3, verification fails with a precise message:

Output
error: gpg.ssh.allowedSignersFile needs to be configured and exist for ssh signature verification

With it:

Terminal
$ git log --show-signature -1
commit c19cb2a6a00205763ed6f713aa4ccb86568c5888
Good "git" signature for ana@northwind-trails.example with ED25519 key SHA256:oXkrCC4GFMEX55s1y3ga0TEiIENchQ3qo6OHN8SSsMo
Author: You <you@example.com>
Date:   Sat Sep 5 09:00:00 2026 +0200

    docs: a signed commit

A compact form for scripts and for scanning a log:

Terminal
$ git log -1 --format='%h %G? %GS'
c19cb2a G ana@northwind-trails.example

%G? is G for a good signature, B for bad, U for good but untrusted, N for none.

GPG, briefly

GPG signing still exists and some organizations require it. The shape is the same: generate a key, git config --global user.signingkey <key id>, gpg.format openpgp (the default), and upload the public key to the platform. The differences are practical: GPG keys need managing, expiring and distributing, and the agent setup is fiddlier. If you have a choice, choose SSH.

What Verified means on the platform

Both platforms show a Verified badge on a signed commit, and both mean the same narrow thing:

This commit carries a valid signature from a key registered to an account, and the commit's author email matches a verified email on that account.

What it does not mean:

  • That the change is correct, reviewed, or safe.
  • That the person intended to publish it, if their key was stolen.
  • Anything at all about the commits around it.

An unverified commit is not suspicious by default either: most people do not sign. The badge is useful precisely in projects where everyone signs, because then an unsigned commit stands out.

Both platforms can enforce it: GitLab through a push rule requiring signed commits, GitHub through a ruleset's Require signed commits. That is when signing stops being personal hygiene and becomes a project rule.

Signing tags

Terminal
$ git tag -s v1.3.0 -m "Release 1.3.0"
$ git tag -v v1.3.0

A signed tag is the stronger and older practice: it says "this exact state is the release", which matters more than any individual commit for anyone consuming your releases (lesson 13.6).

How to do it

Terminal
$ git config --global gpg.format ssh
$ git config --global user.signingkey ~/.ssh/id_ed25519.pub
$ git config --global commit.gpgsign true
$ git log --show-signature -1

With includeIf, work commits can be signed with a work key and personal ones with another (lesson 18.2).

Common mistakes

  • Expecting the author field to prove identity. It is configuration.
  • Forgetting the allowed signers file, then reporting that verification is broken.
  • Uploading the key as an authentication key only. Signing is a separate usage.
  • A signing email that is not verified on the account, which shows as unverified.
  • Treating Verified as a quality signal. It is an identity signal, and a narrow one.
  • Requiring signing without warning the team, which blocks everyone's push at once.

Try it yourself

Goal: sign a commit and verify it locally.

  1. Configure SSH signing with the three git config commands above.
  2. Make a commit and run git log --show-signature -1. Read the error about the allowed signers file.
  3. Create the allowed signers file with your email and public key, and set gpg.ssh.allowedSignersFile.
  4. Run the same command again and read the Good "git" signature line.
  5. Run git log -3 --format='%h %G? %GS' and note which of your recent commits are signed.

Expected result: one commit that verifies locally, and a compact way to see the signing status of a whole log.

Show solution

Step 2's error is worth meeting deliberately, because it is the most common confusion with SSH signing: the commit was signed, and the failure is that Git does not know which keys to trust. Signing and verifying are separate configurations, and only the first is needed for the platform's badge.

Check yourself

1. Why does the author field not prove who made a commit?
2. What does a Verified badge mean?
3. git log --show-signature reports that the allowed signers file is not configured. What does that mean?

Key terms

Commit SSH key Access token