Verify Git Commit Signatures Without Guessing at the Result
You will finish with a repeatable check for whether one or more Git commits carry a valid GPG signature. The examples distinguish a verified signature from an unsigned commit, show how to capture the exit status, and keep raw GPG records separate from text intended for people. This guide uses Git 2.43.0 from the installed git-man package version 1:2.43.0-1ubuntu7.3.
The route
Jump straight to the step you need, or tick off Done means at the end.
Allow about fifteen minutes. You need Git, a repository containing the commit you want to inspect, and access to the public key needed to verify its signature. The check is read-only. It does not sign, amend, rewrite or push a commit, and it does not import keys for you.
1. Confirm the installed command
Start by checking the binary and the package version. These are ordinary, unprivileged commands:
$ git --version
git version 2.43.0
$ dpkg-query -W -f='${Package} ${Version}\n' git-man
git-man 1:2.43.0-1ubuntu7.3
The command is a subcommand of git, so use git verify-commit, not a separate executable that you need to find on PATH. Check the syntax before adapting a script:
$ git verify-commit -h
usage: git verify-commit [-v | --verbose] [--raw] <commit>...
Checkpoint: the installed synopsis accepts one or more commit arguments, plus --verbose and --raw. It does not provide an option that makes an unsigned commit valid.
2. Verify one commit for a person
Run the check from the repository containing the target commit. HEAD means the current commit; a full object name, tag or other revision that resolves to a commit can also be used where your Git revision setup permits it.
$ git verify-commit HEAD
gpg: Signature made Thu Sep 24 06:09:54 2026 BST
gpg: using RSA key C4072639F52CDA37C447867E467512876453914A
gpg: Good signature from "Guide Test <[email protected]>" [ultimate]
A successful check returns status 0. The exact signer, key, timestamp and trust text come from GPG, so do not compare the whole display as a stable string. The useful human check is the successful exit status together with a signer you recognise.
Capture the result immediately if you need to report it:
$ git verify-commit HEAD
$ status=$?
$ printf 'verification status: %s\n' "$status"
verification status: 0
The status belongs to the command immediately before the assignment. Running another command first would overwrite the evidence you are trying to record.
3. Treat an unsigned commit as a failed check
Git validates signatures created by commands such as git commit -S. A normal commit without a GPG signature is not silently accepted:
$ git verify-commit HEAD
$ printf 'verification status: %s\n' "$?"
verification status: 1
On this installed Git, the unsigned test produced no normal output and returned 1. That is still a definite result. Do not write a script that treats empty output as success, and do not interpret every failure as a broken GPG installation. First establish whether the target commit is unsigned, then investigate key or GPG errors when there is diagnostic output.
This command does not add a signature after the fact. If you own the commit and need to create a new signed commit, that is a separate history-changing operation. Do not amend or rewrite a shared branch merely to make this check pass without first agreeing the repository's signing and review process.
4. Inspect the commit object before checking it
Add --verbose when you need the commit contents printed before signature validation:
$ git verify-commit --verbose HEAD
tree 2fc512acf5c9f35cfd3d1e9c168de64973947022
author Guide Test <[email protected]> 1790226594 +0100
committer Guide Test <[email protected]> 1790226594 +0100
signed test commit
gpg: Good signature from "Guide Test <[email protected]>" [ultimate]
The object details are printed before the GPG result. Object IDs, identity fields and message text are repository-specific, while GPG's diagnostic wording is version-specific. Use verbose mode for an operator looking at one result, not as a machine parser.
Checkpoint: if the object details are not the commit you expected, stop and check the revision you supplied. A valid signature on the wrong commit is not a useful release check.
5. Use raw status for automation
Use --raw when another program needs GPG's status records. They are written to standard error rather than the normal human-readable display:
$ git verify-commit --raw HEAD >/tmp/verify.out 2>/tmp/verify-gpg.status
$ status=$?
$ printf 'verification status: %s\n' "$status"
verification status: 0
$ sed -n '1,5p' /tmp/verify-gpg.status
[GNUPG:] NEWSIG
[GNUPG:] GOODSIG 467512876453914A Guide Test <[email protected]>
[GNUPG:] VALIDSIG C4072639F52CDA37C447867E467512876453914A 2026-09-24 ...
The key ID and records vary. A script should use the exit status as its primary pass or fail signal and parse raw records only when it has a specific need, such as recording the validated fingerprint. Keep the status stream separate from ordinary command output so a log consumer does not mistake diagnostics for application data.
The temporary paths above are examples only. If the signature data is sensitive in your environment, use a protected directory and remove the files after review. The command itself does not change the repository, but saved logs can disclose signer identities and key fingerprints.
6. Check several commits without losing the first failure
The ellipsis in the synopsis means that several commit arguments are accepted:
$ git verify-commit HEAD HEAD~1 HEAD~2
$ status=$?
$ printf 'verification status: %s\n' "$status"
verification status: 0
Use explicit revisions while investigating. In a shell loop, preserve each result and fail the overall check if any commit is not verified:
failed=0
for commit in HEAD HEAD~1 HEAD~2; do
if git verify-commit --raw "$commit" >/dev/null; then
printf '%s: signed\n' "$commit"
else
printf '%s: signature check failed\n' "$commit" >&2
failed=1
fi
done
exit "$failed"
Quote revision variables even though ordinary revision names usually contain no spaces. Do not pass unchecked user input as a collection of options. If a caller can supply revisions, validate the allowed form and keep the revision arguments separate from fixed options.
7. Diagnose the common traps
A non-zero status can mean an unsigned commit, a bad signature, an unavailable public key, or another GPG verification problem. Read the diagnostic stream and check that the key belongs to the expected person through your project's established trust process. A displayed signer name alone is not proof that the key is authorised for your repository.
Do not use sudo as a default repair. Signature verification normally needs no elevated privilege, and running Git as root can create root-owned files in a working tree. If a repository or keyring is unreadable, fix ownership or access deliberately with the system owner; changing permissions is outside this read-only check.
If the command reports that the revision cannot be found, verify the repository and revision first:
$ git rev-parse --show-toplevel
$ git rev-parse --verify 'HEAD^{commit}'
<commit object name printed here>
A successful git rev-parse lookup proves that the name resolves to a commit. It does not prove that the commit is signed, so still run git verify-commit afterwards.
Done means
- The installed Git and
git-manversions are known. - The target revision was checked in the intended repository.
- Status 0 is required for a valid signature; an unsigned commit is not treated as success.
--verboseis reserved for human inspection and--rawfor deliberate automation.- Signer identity and key trust were reviewed rather than inferred from a name alone.
- No commit, branch, keyring or repository state was changed.