GPG keys can be used to cryptographically sign your work when using Git for version control. This ties your identity to your work, whether that’s a commit, tag, or even a push. Gitea, GitHub, GitLab, and other popular Git hosting services support visualizing this feature when browsing changes, showing which commits are verified.

Tutorial

This tutorial describes how to use GPG to sign your work with Git, including command-line incantations and configuration options. It is assumed you already have a GPG key pair for your commit email. If not, I describe the exact steps in Generate a GPG Key. I also recommend you checkout my post Backup and Restore a GPG Key for ways to avoid the pain of losing your private key. The official Git Book contains a very useful section on this topic, Git Tools Signing Your Work. With that said, Ubuntu 20.04 is used as the reference system and familiarity with Git, GPG, and the command-line on Linux is assumed.

All Git configuration settings are done globally, i.e. for the user in these steps. They may just as easily be configured at the repository level.

  1. Install the git package, of course.

    sudo apt --yes install git
  2. Set your Git username.

    git config --global user.name "Ponder Stibbons"
  3. Set your Git commit email address.

    git config --global user.email "ponder.stibbons@unseen.edu"
  4. Identify the GPG secret key associated with your Git commit email address.

    gpg --list-secret-keys --keyid-format LONG
    /home/pstibbons/.gnupg/pubring.kbx
    -------------------------------
    sec   rsa4096/C8DE632E9A8A0BDD 2020-11-13 [SC] (1)
          F38915B041F5F1024AF95C30C8DE632E9A8A0BDD
    uid                 [ultimate] Ponder Stibbons <ponder.stibbons@unseen.edu>
    ssb   rsa4096/DBCD8B98F2F9188C 2020-11-13 [E]
    1 The secret key’s id is C8DE632E9A8A0BDD, the string of letters and numbers following rsa4096/. The sec word indicates this is the secret key.
  5. Configure this key as your default signing key for Git.

    git config --global user.signingKey C8DE632E9A8A0BDD
    The key can be provided when signing a tag, commit, or push, but it’s generally more convenient to configure your default key,
  6. Create a test directory for a test repository.

    mkdir git-signing-test
  7. Change into the directory.

    cd git-signing-test
  8. Initialize a new Git repository.

    git init
  9. Create an example file.

    touch test
  10. Stage this file in order to make a signed commit.

    git add test
  11. Pass the -S short option to git-commit(1) to manually sign a commit.

    git commit -S -m 'Signed commit'
  12. Enter the passphrase for your private key in the [Passphrase Prompt].

    GPG Key Passphrase Prompt
    Passphrase Prompt
    If you value your sanity, check the Save in password manager box to avoid unlocking your key for every signature.
  13. Optionally, configure Git to sign commits by default with git-config(1) thus allowing you to omit the -S flag.

    git config --global commit.gpgSign true
    This configuration setting applies to merge commits created when using git-merge(1) and git-pull(1).
  14. Check a signed commit by using the --show-signature option with git log.

    git log --show-signature -1
    commit 48fafff3d24a89ec9c6d5940317588f9b10b4f43 (HEAD -> master)
    gpg: Signature made Sat 14 Nov 2020 09:53:05 AM CST
    gpg:                using RSA key F38915B041F5F1024AF95C30C8DE632E9A8A0BDD
    gpg: Good signature from "Ponder Stibbons <ponder.stibbons@unseen.edu>" [ultimate]
    Author: Ponder Stibbons <ponder.stibbons@unseen.edu>
    Date:   Sat Nov 14 09:53:05 2020 -0600
    
        Signed commit
    When verifying GPG signatures, you will need to have the signer’s public key in your GPG keyring.
  15. Manually sign a tag with the -s short option.

    git tag -s v1.0.0 -m 'Signed tag'
  16. If desired, configure Git to sign all tags by default.

    git config --global tag.gpgSign true
  17. Verify a tag with the -v option.

    git tag -v v1.0.0
    object 48fafff3d24a89ec9c6d5940317588f9b10b4f43
    type commit
    tag v1.0.0
    tagger Ponder Stibbons <ponder.stibbons@unseen.edu> 1605369450 -0600
    
    Signed tag
    gpg: Signature made Sat 14 Nov 2020 09:57:30 AM CST
    gpg:                using RSA key F38915B041F5F1024AF95C30C8DE632E9A8A0BDD
    gpg: Good signature from "Ponder Stibbons <ponder.stibbons@unseen.edu>" [ultimate]
  18. Create and switch to a new branch.

    git switch -c unsigned
    Switched to a new branch 'unsigned'
  19. Create another file.

    touch test2
  20. Stage the new file.

    git add test2
  21. Now, commit the file without a signature.

    git commit --no-gpg-sign -m 'Unsigned commit'
  22. Switch back to the main branch.

    git switch master
    Switched to branch 'master'
  23. When using git-merge(1), sign a merge commit by passing the -S option and verify all signatures in the merge with the --verify-signatures option.

    git merge -S --verify-signatures unsigned
    fatal: Commit a3bf413 does not have a GPG signature.

    Oh no! The unsigned commit in the unsigned branch couldn’t be verified so the merge failed.

  24. To always verify signatures when merging, set the configuration option merge.verifySignatures to true.

    git config --global merge.verifySignatures true
    This configuration setting also applies to the git-pull(1) command.
  25. For git-pull(1), use the same options as with git-merge(1) to sign a merge commit and verify all signatures.

    git pull -S --verify-signatures
  26. Sign pushes by using the --signed long option with git-push(1).

    This option takes more than just a true or false value. The if-asked value used here signs pushes if the server supports this feature, but doesn’t otherwise. Signing pushed signifies intent, as Konstantin Ryabitsev describes in more detail in his article Signed git pushes.

    git push --signed=if-asked
  27. Configure Git to sign pushes by default if the server supports it.

    git config --global push.gpgSign if-asked

Conclusion

Now go setup your key for use with your Git servers and start rolling out your signed commits!