Workflows
Signing Commits Workflow
Use GPG or SSH to sign commits, establishing verifiable commit identity and a trusted collaboration chain.
- Teams turning commands into repeatable routines
- Readers who need sequencing, branch, and sync discipline
- Basic understanding of fetch, pull, push, and branches
- A sense of how and why branches diverge
- Copying a workflow without checking branch state
- Choosing the wrong integration path on shared branches
The short version
Signed commits cryptographically sign each commit using a GPG or SSH key, letting collaborators verify that the commit truly came from the claimed author and preventing identity forgery and supply-chain attacks.
GPG key pairConfigure git configUpload public key to platform
Commit has signature badgePlatform shows VerifiedRepository integrity checkable
Signing is not optional; it is trust infrastructure. Configuring from day one is easier than retrofitting later.
Why signed commits are needed
In open-source projects and large teams, anyone can set arbitrary user.name and user.email. A malicious attacker can forge an identity to commit harmful code. Signed commits make identity verifiable and history trustworthy.
Unsigned commit:
commit abc123
Author: Alice <alice@example.com>
← No proof this was actually Alice
Signed commit:
commit def456
Author: Alice <alice@example.com>
gpgsig: -----BEGIN PGP SIGNATURE-----
← Can be verified with Alice's public key
GPG signing workflow
1. Generate a GPG key
# Generate a GPG key (RSA 4096 or Ed25519 recommended)
gpg --full-generate-key
# Select key type and size
# Enter name and email (must match Git configuration)
2. Configure Git to use GPG
# List keys to get the Key ID
gpg --list-secret-keys --keyid-format=long
# Configure Git to use that key
git config --global user.signingkey YOUR_KEY_ID
# Sign all commits by default
git config --global commit.gpgsign true
# Sign tags
git config --global tag.gpgsign true
3. Commit and push
# If commit.gpgsign is not enabled, manually sign a commit
git commit -S -m "feat: add authentication"
# Create a signed tag
git tag -s v1.0.0 -m "Release version 1.0.0"
# Verify commit signatures
git log --show-signature
# Verify tag signatures
git tag -v v1.0.0
SSH signing workflow (Git 2.34+)
Compared to GPG, SSH signing is lighter and can reuse your existing SSH keys.
# Generate an SSH signing key (if you don't have one)
ssh-keygen -t ed25519 -C "signing@example.com" -f ~/.ssh/git_signing_key
# Configure Git to use SSH signing
git config --global user.signingkey ~/.ssh/git_signing_key.pub
git config --global gpg.format ssh
# Tell Git which public keys are trusted
git config --global gpg.ssh.allowedSignersFile ~/.ssh/allowed_signers
# Add a trusted signer
echo "$(git config user.email) $(cat ~/.ssh/git_signing_key.pub)" >> ~/.ssh/allowed_signers
# Sign a commit
git commit -S -m "feat: use ssh signing"
Platform verification setup
GitHub
# Export GPG public key
gpg --armor --export YOUR_KEY_ID
# Or export SSH public key
cat ~/.ssh/git_signing_key.pub
# Copy to GitHub Settings -> SSH and GPG keys -> New GPG key / Signing Key
GitLab
# Similarly export the public key and add it to GitLab Profile -> GPG Keys / SSH Keys
# Signed commits will display a "Verified" badge in GitLab
Team-enforced signing policy
Through GitHub branch protection
Settings -> Branches -> Branch protection rules
-> Require signed commits
Through Git hook validation
# .git/hooks/commit-msg
#!/bin/sh
# Require all commits to be signed
git verify-commit HEAD 2>/dev/null || {
echo "Error: Commit must be signed"
exit 1
}
Handling mixed scenarios
Some commits not signed
# Temporarily skip signing for a quick fix
git commit --no-gpg-sign -m "chore: quick fix"
# Only sign important commits
git commit -S -m "feat: security-critical change"
Multi-device usage
# Use the same GPG key on different devices
# Export the key pair
gpg --export-secret-keys --armor YOUR_KEY_ID > private.key
# Import on another device
gpg --import private.key
# Securely delete the export file
shred -u private.key
Troubleshooting verification failures
| Issue | Cause | Fix |
|---|---|---|
| gpg failed to sign | GPG agent not running | gpgconf --launch gpg-agent |
| key not found | Git-configured key ID is wrong | Check user.signingkey |
| bad signature | Public key not uploaded to platform | Export the public key to GitHub/GitLab |
| unable to verify | Public key not in allowed_signers | Check the allowedSignersFile for SSH signing |
Best practices summary
- Enable signing by default: Set
commit.gpgsign trueso signing becomes the default behavior - Protect private keys: GPG/SSH private keys are identity credentials; protect them with strong passphrases
- Set key expiration: Even if the primary key does not expire, subkeys should have reasonable expiration dates
- Back up keys: Export and securely back up key pairs to avoid losing the ability to verify historical commits
- Platform cross-recognition: Ensure the public key is uploaded to all Git hosting platforms you use
- Team-wide policy: Consider requiring signed commits on core branches
Key takeaways
- Signed commits add a small amount of metadata, slightly increasing repository size
- The initial verification workflow may be tedious, but it is a one-time investment
- When enabling signing on an existing project, old unsigned commits do not automatically become verified
- If a key is compromised, revoke it immediately and generate a new one
- CI/CD automated commits typically do not need signing, but critical release tags should be signed