kluster.aikluster.aiFeaturesEnterprisePricingDocsAboutSign InStart Free
kluster.aikluster.ai
Back to Blog

A Developer's Guide to Git Signed Commits

April 2, 2026
23 min read
kluster.ai Team
git signed commitsgpg signingssh signinggit securitycode integrity

Think of a Git signed commit as a cryptographic seal of approval on your code. It's a way to verify the author's identity and guarantee the code hasn't been touched since they committed it. This isn't just a nice-to-have; it's a critical security layer for any modern development workflow.

Why Git Signed Commits Are More Important Than Ever

A person's hand writing in a notebook next to a laptop displaying code, with text 'SECURE YOUR COMMITS'.

Let's be real—unsigned commits are a massive blind spot. By default, Git is dangerously flexible about identity. Anyone can configure their local machine with your exact user name and email, then push commits that look like they came directly from you. This is commit spoofing, and it's a tangible, exploitable security risk.

Imagine an attacker impersonating a trusted lead developer to sneak malicious code into a core project. Without signed commits, that toxic commit might blend in seamlessly, passing through code review without anyone batting an eye. This isn't some far-fetched, theoretical threat. It’s a practical vulnerability that happens in the wild, and signed commits are the tool designed specifically to shut it down.

The Problem of Trust in Distributed Teams

Today’s development is more distributed and complex than ever. We’re pulling in code from remote team members, open-source contributors, and now, AI coding assistants that generate entire functions in seconds. In this environment, blindly trusting the author field on a commit is just asking for trouble.

This is where signed commits change the game. By attaching a cryptographic signature to each commit, you get hard proof of:

  • Authenticity: The commit was created by the developer who owns the corresponding private key. No faking it.
  • Integrity: The code hasn't been altered one bit since it was signed.
  • Non-repudiation: A permanent, undeniable audit trail linking a developer to their code.

This creates a chain of trust that's incredibly difficult to forge but simple to verify. It’s why platforms like GitHub, GitLab, and Bitbucket show that coveted green "Verified" badge next to signed commits. It’s a clear signal: this code is legit.

To put it plainly, here’s how the two stack up.

Unsigned vs Signed Commits At a Glance

The difference between an unsigned and a signed commit isn't just a technical detail; it's the difference between a guess and a guarantee. This table breaks down what you gain by signing your work.

AttributeUnsigned CommitSigned Commit
IdentityBased on a local git config setting. Easily spoofed.Verified with a private cryptographic key (GPG/SSH). Hard to forge.
IntegrityNo guarantee. Code could be altered post-commit.Guaranteed. The signature breaks if the code is changed.
Trust SignalRelies on trusting the developer's machine and network.Relies on verifiable cryptography. Clear "Verified" badge on platforms.
Security RiskHigh. Vulnerable to impersonation and code injection attacks.Low. Establishes a clear, verifiable chain of custody.
Audit TrailWeak. An attacker can create a misleading history.Strong. Provides a non-repudiable link between code and author.

Ultimately, relying on unsigned commits is like accepting a package without checking the sender's ID. Signed commits give you the verification you need to secure your entire software supply chain.

A signed commit is your digital promise that you stand by the code you've written. It transforms an anonymous git config setting into a verifiable identity, providing the foundation for a secure software supply chain.

The Glaring Gap in Adoption

Despite the obvious benefits and native support in Git, adoption of signed commits is shockingly low. A comprehensive 2025 study that analyzed 60 popular open-source repositories found that only about 10% of all commits were cryptographically signed. You can dig into the full research on commit verification practices yourself, but the takeaway is clear: this is a huge gap in our collective security posture.

Even though Git has supported signing since 2011, the industry has been slow to catch on. This oversight leaves countless projects exposed.

Making signed commits a team standard is no longer a "nice-to-have" for the paranoid. It's a fundamental step toward building a trustworthy and resilient development process. As we move forward, securing your commit history isn't optional—it's essential for any team that's serious about code integrity.

When you decide to start signing your commits, you hit your first real decision: GPG or SSH keys? Both get the job done, creating cryptographically-verified commits. But they come from different worlds, with different workflows and, honestly, different levels of annoyance.

For years, GPG (GNU Privacy Guard) was the only game in town. It's powerful, battle-tested, and has a deep history in public-key cryptography. GPG gives you an incredible amount of control—subkeys for different tasks, expiration dates, a whole trust network. It was the original, default way to sign commits in Git for a reason.

But all that power comes with a price. Let's be honest: GPG has a reputation for being clunky. I've seen countless developers get tripped up just trying to generate and manage their keys. That complexity has always been the biggest hurdle to getting more people to actually sign their code.

The Rise of SSH Key Signing

Git and the big hosting platforms saw the friction. They realized developers were already using SSH keys every single day to talk to services like GitHub, GitLab, and Bitbucket. So why not use those same keys for signing? It was a natural, brilliant move.

If you're just getting started with repositories, our guide on how to clone a Git repository can walk you through the initial steps where SSH authentication comes into play.

This approach massively lowers the barrier to entry. Instead of wrestling with a whole new toolchain like GPG, you can just use the SSH key you already have.

Here's the deal: SSH signing is all about convenience. It uses a tool you're already familiar with, slashing the setup time and making it dead simple to get started. GPG offers more features, but at the cost of a much steeper learning curve.

This isn't just an academic debate; it has a real impact on repository security. The easier it is, the more likely people are to do it. A quick analysis using a script on GitHub shows the ugly truth: in many active projects, signed commits can be as low as 15-20% of the total. Most commits are flying blind, and the friction of GPG is a big reason why.

A Practical Comparison: GPG vs. SSH

So, which one should you pick? It really boils down to tradition versus convenience. Here’s how they stack up in the real world.

FeatureGPG (GNU Privacy Guard)SSH Keys
Setup ComplexityHigh. You're learning a new set of gpg commands and concepts.Low. You already know SSH. It's part of your daily workflow.
Key ManagementExtremely powerful, with subkeys, expirations, and trust levels.Simple. One key, often reused for authentication. Straightforward.
Developer WorkflowFeels like a separate, disconnected task from your Git/SSH work.Fits right into the authentication tools you already use every day.
Platform SupportUniversal. Works everywhere Git is used.Widely supported on GitHub and GitLab, and adoption is only growing.
Best ForSecurity purists, people in high-security environments, or anyone who already lives in the GPG ecosystem.Most developers. It's the fastest, easiest way to start signing commits right now.

Ultimately, there’s no wrong answer here. If you’re a security expert or you've already tamed the GPG beast, it's still a fantastic, powerful tool. You can't beat its granular control over key lifecycles.

But for the vast majority of developers and teams, SSH is the pragmatic choice. The ability to use a key you already have for authentication makes the process almost frictionless. That convenience is what will finally help us get closer to a world where every commit is verified.

Alright, you understand the why. Now it's time to get your hands dirty and set up your signing keys. Whether you go with the classic strength of GPG or the streamlined approach of SSH, this is where the rubber meets the road.

This whole process boils down to one initial choice, which this flowchart lays out nicely.

Flowchart illustrating the process of choosing GPG or SSH methods for code signing.

You either head down the GPG path or the SSH path. Both get you to the same end goal: git signed commits. Let’s break down how to set up each one.

Setting Up GPG for Commit Signing

For a long time, GPG was the only game in town for signing commits. It's a battle-tested system, and if you've installed Git for Windows, the gpg command-line tool is probably already on your machine.

To get started, pop open your terminal and run this command to generate a new key pair.

gpg --full-generate-key

This will launch an interactive wizard to walk you through the setup. You’ll be asked a few questions. Here are my personal recommendations for a rock-solid configuration:

  • Key Type: Stick with (1) RSA and RSA. It's the default for a reason—it’s universally supported.
  • Key Size: Don't skimp here. Go with 4096 bits for strong security that will stand the test of time.
  • Expiration: Set an expiration date. Something like 1y for one year is a smart security practice. It forces you to consciously rotate your keys and prevents a forgotten key from being a liability forever.
  • User Information: This part is absolutely critical. The email address you provide must be an exact match to a verified email on your GitHub, GitLab, or Bitbucket account.

After confirming your details, you'll create a passphrase. Think of it as a password for your key. You'll need it every time you sign a commit, which adds a crucial layer of security.

Pro Tip: Your new GPG key has a unique ID, and you'll need it for the next steps. Run gpg --list-secret-keys --keyid-format=long to find it. The ID is the long string of characters right after rsa4096/.

Once you have that key ID, you need to export the public key so you can upload it to your hosting provider.

gpg --armor --export YOUR_KEY_ID

This spits out a big block of text that starts with -----BEGIN PGP PUBLIC KEY BLOCK-----. Copy that whole thing, from beginning to end. Now, head over to your GitHub or GitLab settings, find the "SSH and GPG keys" area, and paste it in to add a new GPG key.

Using SSH Keys for a Simpler Workflow

If that GPG process felt a little heavy, you're not wrong. Using SSH keys for signing is a much more direct, modern approach. You're almost certainly using SSH keys for authentication already, so why not for signing?

First, generate a new SSH key that's dedicated to signing. You could reuse your auth key, but from a security standpoint, it’s much cleaner to keep them separate.

ssh-keygen -t ed25519 -C "your_email@example.com"

I always recommend the Ed25519 algorithm—it's fast, modern, and very secure. When prompted, make sure to save the key to a new file, something obvious like ~/.ssh/id_ed25519_signing.

Next, add this new key to your ssh-agent so it's ready to be used.

ssh-add ~/.ssh/id_ed25519_signing

Just like with GPG, you'll copy the public part of your key and add it to your hosting provider's settings. The key difference here is that when you add it, you must explicitly choose its purpose as a "Signing Key". This is a great security feature—it means the key can only be used to sign commits, not to push code or grant repository access.

Configuring Git to Sign Your Commits

The last piece of the puzzle is telling Git to actually use your shiny new key. The setup is slightly different depending on which path you chose.

For GPG:

  1. First, tell Git which key to use for signing. git config --global user.signingkey YOUR_GPG_KEY_ID
  2. Then, tell Git to sign every commit by default. git config --global commit.gpgsign true

For SSH:

  1. First, set the signing format to ssh. git config --global gpg.format ssh
  2. Next, point Git to the public key file you created. git config --global user.signingkey ~/.ssh/id_ed25519_signing.pub
  3. Finally, enable signing by default. git config --global commit.gpgsign true

By setting commit.gpgsign to true, you're making git signed commits your new default. No more forgetting the -S flag. Security just became a seamless part of your daily workflow.

Creating and Verifying Signed Commits in Your Workflow

A person types on a computer keyboard, working on code displayed on a blue monitor in an office setting.

Alright, you’ve got your keys generated and configured. Now for the fun part: actually using them in your day-to-day workflow to create git signed commits.

Signing a one-off commit is simple. Just tack the -S flag onto your usual commit command.

git commit -S -m "feat: Add user authentication"

Git will then create the commit and sign it with your configured GPG or SSH key. If you set a passphrase on your key—which you absolutely should—you'll be prompted for it. This is a good thing. It's an extra layer of defense ensuring that even if someone gets access to your machine, they can't start impersonating you in the repo.

But let's be real, remembering to add -S every single time is a recipe for failure. It’s tedious and you will forget. That's why we set commit.gpgsign to true in the previous section. This "set it and forget it" approach makes security a default part of your workflow, not an afterthought.

Verifying Signatures Locally

Making a signed commit is one thing, but knowing how to check them is where the real trust comes from. Your main tool for this is git log. Just add the --show-signature flag, and you’ll get a detailed report on the signature status of your recent commits right in the terminal.

git log --show-signature

This command peels back the curtain and shows you the cryptographic proof tied to each commit. GPG will give you one of a few statuses, and each one tells a very different story.

  • good signature: This is the gold standard. It means the commit was signed by a key your system trusts, and the code hasn't been touched since.
  • bad signature: A massive red flag. This means the commit data was tampered with after it was signed. The cryptographic seal is broken. Investigate immediately.
  • no signature: The commit wasn't signed at all. Its origin and integrity are a mystery.
  • unknown public key: The commit is signed, but you don't have the public key needed to verify it. This is common when a new developer joins the team. You just need to import their public key into your keychain.

The idea of checking signatures isn't new. Git has supported this for a long time, with a critical evolution happening around June 4, 2014, when Scott Chacon first showed how to sign a commit with a 2048-bit RSA key. Despite this long history, a 2025 analysis revealed signed commits were still used by less than 12% of top open-source contributors. You can learn more about Git's powerful commit history tools and see how these features have developed over the years.

To make this a practical part of your daily routine, you'll want to get comfortable with a few key commands for managing and verifying signatures.

Here's a quick reference table for the commands you'll use most often when dealing with signed commits.

Common Git Signature Verification Commands

A reference table of essential Git commands for creating, checking, and troubleshooting signed commits in your daily workflow.

CommandDescriptionExample Usage
git commit -S -m "..."Creates a single, manually signed commit.git commit -S -m "fix: Correct API endpoint"
git log --show-signatureDisplays commits with their signature verification status.git log --show-signature -n 5
git show --show-signatureShows the full details of a specific commit, including its signature.git show --show-signature <commit-hash>
git verify-commit <hash>Verifies a single commit without showing its full content.git verify-commit HEAD~2
git config --global commit.gpgsign trueConfigures Git to sign all commits by default.git config --global commit.gpgsign true

These commands are your bread and butter for maintaining a secure and verifiable commit history. Make them part of your muscle memory.

The Verified Badge on GitHub and GitLab

While local verification is essential, the most visible payoff happens on your Git hosting platform. When you push a signed commit to GitHub, GitLab, or Bitbucket, the platform runs its own verification.

If the signature is valid and it's from a key you’ve uploaded to your account, you get the coveted "Verified" badge next to your commit.

That little green badge is more than just a cosmetic touch. It’s a powerful public signal. It instantly tells everyone on your team—from other developers to your project manager—that the code is authentic and untampered. In a pull request, seeing a whole list of "Verified" commits provides immediate confidence, making code reviews faster and reinforcing a culture of accountability.

Enforcing Signed Commits for Your Team and CI CD

A woman interacting with a large digital screen showing a workflow with 'ENFORCE Signed Commits'.

Getting individual developers to sign their work is a great start. But the real security payoff comes from making it a non-negotiable rule for the entire team. This is where git signed commits shift from a personal best practice to a serious organizational security control. You're guaranteeing that every single line of code hitting your main branch is authenticated and its integrity is verified.

Let's be real: relying on developers to remember to sign every commit just isn't going to work. People forget. True enforcement means moving the responsibility from the person to the system itself. You do this by configuring rules directly in your Git hosting provider, making it flat-out impossible to merge unverified code into your protected branches.

Activating Branch Protection Rules

Modern Git platforms like GitHub and GitLab have powerful branch protection rules that act as gatekeepers for your most critical branches, like main or develop. By flipping on the signed commit requirement, you're effectively blocking any pull request that contains unsigned or unverified commits.

On GitHub, it's pretty straightforward:

  • Head over to your repository’s Settings tab.
  • Find the Branches section and add or edit a protection rule for your main branch.
  • Just check the box for "Require signed commits".

Once that's enabled, GitHub will automatically check every commit in a pull request aimed at that branch. If even one commit lacks a valid signature, the merge button is disabled. It's a simple checkbox with a huge impact.

GitLab has a similar feature under its "Protected branches" settings. You can find it by going to Settings > Repository and expanding the Protected branches section. Look for the option to "Reject unsigned commits"—it enforces the same high standard of verification before anything gets merged.

Enforcing signed commits at the branch level is the most effective way to ensure 100% compliance. It removes the element of human error and creates a hard, auditable rule that every single commit must be cryptographically verified to proceed.

This small configuration change completely reframes the conversation. It's no longer "Did you remember to sign your commits?" but "Your commits must be signed to be merged." The system is now accountable, not just a developer's memory.

Integrating Verification into Your CI CD Pipeline

Branch protection is your first line of defense, but a true defense-in-depth strategy takes it a step further. Integrating signature checks directly into your CI/CD pipeline ensures that unverified code never even gets built, tested, or deployed. Think of it as the ultimate backstop for your software supply chain security.

You can configure your CI pipeline to run a script that verifies every single commit in a push or pull request. If it finds a commit that is unsigned, has a bad signature, or was signed by an unknown key, the pipeline fails immediately. This provides instant feedback and saves you from wasting compute resources on untrusted code.

For instance, a GitHub Actions workflow can easily include a step to check commit signatures. You can add a step to your job that runs a simple shell script to loop through the commits and check their status. If you're new to creating these kinds of automated checks, our guide on building a GitHub Action tutorial can help you get the hang of the basics.

Here’s a basic script concept you could adapt for your pipeline:

git log --show-signature --no-patch
origin/main..HEAD | grep "gpg: " | grep -v "Good signature"

If the command above finds any output (like "No signature" or "Bad signature"),

it means there's a problem. We exit with an error code.

if [ $? -eq 0 ]; then echo "Found commits with invalid or missing signatures!" exit 1 fi

echo "All commits are properly signed. Proceeding with build." exit 0

This logic is easy enough to adapt for any CI system, whether it's GitLab CI, Jenkins, or CircleCI. By embedding this check early in your pipeline, you create an automated audit that runs on every single code change. It becomes a silent guardian, ensuring your commitment to using git signed commits is enforced long before the code gets anywhere near a production environment.

Common Questions About Git Signed Commits

Once you get signed commits set up, the real fun begins. It's one thing to follow a guide, but it's another thing entirely to handle the weird edge cases that pop up in a real workflow.

Let's walk through some of the most common questions I hear from teams adopting signed commits. Think of this as your field guide for navigating the "what ifs" of a secure commit history.

What if I Lose My Signing Key or It Gets Compromised?

This is the big one, and it's a matter of when, not if. A laptop gets lost, a machine is compromised—it happens. When it does, your immediate response should be muscle memory: revoke the old key and create a new one.

If you’re using GPG, you should have generated a revocation certificate when you first made your key. Now's the time to use it. Just run gpg --import with that certificate file, and the old key is toast. For SSH keys, it's even easier: just delete the public key from your account settings on GitHub or GitLab.

Next, generate a brand new key pair just like you did the first time. Add the new public key to your hosting provider and point your local Git config to the new key ID. Done.

That moment of panic when you realize a key is gone is real. But having a plan turns it from a crisis into a simple security drill. Revoking the old key immediately is the most critical step—it cuts off its ability to sign anything new in your name.

Can I Sign Old Commits?

This comes up all the time, usually right after a team decides to enforce signing on a project that's been running for a year. The short answer is yes, but it means rewriting history.

You can't just slap a signature onto an old commit. The signature is baked into the commit object itself. To sign a project's history, you have to use an interactive rebase (git rebase -i --rebase-merges) and effectively re-create every single commit.

This process generates brand new commits with new SHA-1 hashes. If that history is already on a shared remote, you'll have to force-push the changes, which is a major disruption for everyone else on the team. My advice? Only do this with careful coordination, and preferably only on projects that haven't been cloned by dozens of people yet.

How Do Rebasing and Merging Affect Signatures?

This is where a lot of people get tripped up. The interaction between Git operations and signatures is crucial.

When you do a standard merge (git merge), everything is fine. The signatures on the original commits are untouched, and you can even sign the new merge commit itself to keep the chain of trust complete.

Rebasing, however, is a different beast. A rebase works by rewriting commits—it replays them on top of a new base commit. This creates entirely new commit objects, and the original signatures are lost in the process.

Here's what you need to remember:

  • If you rebase a branch, you absolutely must re-sign each commit as it's replayed.
  • The easy way to handle this is to enable automatic signing (commit.gpgsign = true). Git will then prompt you for your passphrase for each new commit it creates.
  • If you don't have auto-signing on, the rebased commits will all be unsigned.

Do I Need a New Key for Every Machine?

No, you don't have to, but you probably should. You really have two options here, each with a different security profile.

  1. One Key, Multiple Machines: You can securely transfer your private key to every machine you work on. It's convenient, but it also multiplies your risk. If one of those machines is compromised, that single key is compromised everywhere.
  2. One Key Per Machine: The much safer approach is to generate a unique key for each machine (e.g., "Work Laptop," "Home Desktop"). You then upload all of those public keys to your GitHub or GitLab account. This isolates your risk—a breach on one machine doesn't invalidate your identity on the others.

I strongly recommend the one-key-per-machine model. It takes a few extra minutes to set up, but it's far better security hygiene. A single point of failure shouldn't be able to compromise your entire developer identity.


At kluster.ai, we believe security and compliance shouldn't slow you down. Our real-time AI code review platform runs in your IDE to catch security issues, logic errors, and policy violations before they become commits. By enforcing standards automatically, we help teams ship trusted, production-ready code faster. Learn more and start your free trial at https://kluster.ai.

kluster.ai

Real-time code reviews for AI generated and human written code that understand your intent and prevent bugs before they ship.

Developers

  • Documentation
  • Cursor Extension
  • VS Code Extension
  • Claude Code Agent
  • Codex Agent

Resources

  • About Us
  • Contact
  • Blog
  • CodeRabbit vs kluster.ai
  • Greptile vs kluster.ai
  • Qodo vs kluster.ai

All copyrights reserved kluster.ai © 2026

  • Privacy Policy
  • Terms of Use