When I discover the repository git-blame-someone-else, which you can change the author of the commit, I realized that someone can alter the commit metadata to record anybody name on it. And I was thinking: what if someone use it to alter some commit to assign my name on it, and blame me for the bad code? It could possibly happen, right?

So, signed your commit to prevent commit's metadata changed by anyone but you.

Introduction to git commit signature

You can add a cryptographic signature to the commits that you make with a GPG, SSH, or S/MIME key. With this extra step, you provide the assurance that a commit is originated from you, rather than an impersonator.

Other people can not modify a signed commit if they don't have the private key that is used to sign. And remote repository storage provider like Github, Gitlab can display the status "Verify" on your commit listing on their UI.

My commits are marked Verified in the Github UI
Gitlab UI marked commit is Verified in their UI

Steps to configure commit signing

To sign commits, you will need to configure both on your local machine and the remote git storage provider. Also remember to back up your key so you will not lose it.

In this post, I will guide you to sign commit using GPG key.

Configure with git

I. Create a GPG key

  1. Please install GPG for your operating system, then generate the key pair with this command
$ gpg --full-gen-key

2. Select the algorithm your key should use, or press Enter to select the default option, RSA and RSA (default).

3. Select the key length, in bits. It must be at least 4096 bits, press Enter to accept the default.

4. Enter the length of time the key should be valid. Press Enter to specify the default selection, indicating that the key doesn't expire.

5. Verify that your answers are correct, press y

6. Enter your name and email address. Note that email address should match that email in your remote repository storage account (ex: github, gitlab) inorder to display commit as Verified in their UI.

7. Verify you information once again and confirm to continue

8. Enter a strong password and then confirm it.

9. It is done, now display created key with this command:

# to display a specific key, replace EMAIL with your email
$ gpg --list-secret-keys --keyid-format LONG <EMAIL>
# to display all the keys for which you have both public and private key
$ gpg --list-secret-keys --keyid-format=long
Display the GPG keys

10. In the output, identify the sec line, and copy the GPG key ID. It begins after the / character. In this example, the key ID is E93131EB80810CC7:

sec   rsa2048/E93131EB80810CC7 2019-05-02 [SC]
      3CB2CF2EB3CEB0C721BF3A00E93131EB80810CC7
uid                 [ultimate] hoangddt (MacPro key) <reacted email>
ssb   rsa2048/4E177431166EC61C 2019-05-02 [E]

11. To show the associated public key, run this command, replacing <ID> with the GPG key ID from the previous step:

$ gpg --armor --export <ID>

Copy the public key to use in the next step, beginning with -----BEGIN PGP PUBLIC KEY BLOCK----- and ending with -----END PGP PUBLIC KEY BLOCK-----.

II. Associate your GPG key with git

After you create your GPG key, you must configure git to use this key

  1. Run this command to list the private key you want to use, replace <EMAIL> with the email address you use for your key:
$ gpg --list-secret-keys --keyid-format LONG <EMAIL>

2. Copy the GPG private key ID that starts with sec.

3. Run this command to configure git to sign your commit with your key, replace <KEY ID> with your GPG key ID:

$ git config --global user.signingkey <KEY ID>

III. Sign your git commits

1.Sign individual Git commits manually

$ git commit -S -m "My commit message"

2. Enter the passphrase of your GPG key when asked

3. Sign all Git commits by default by running this command:

$ git config --global commit.gpgsign true

4. View and update global config:

$ git config --global --edit

Set up on the online repository storage like Github

When you add your public key to the online repository storage account. Commits signed by any of the corresponding private keys will show as verified. If you haven't add any GPG key to the provider, it won't show the marked verified even though commit is signed.

I. Add a GPG key to your account

This will be the steps on github and gitlab

For Github:

  • In upper right corner, click on you profile page, then click Settings
  • In the Access section of the sidebar, click SSH and GPG keys
  • Click New GPG Key
  • In the "Key" field, paste the public GPG key. Confirm action by enter you Github password.

For Gitlab:

  • In the upper right corner, click on your avatar
  • Select Edit profile.
  • On the left sidebar, select GPG Keys
  • In Key, paste your public GPG key and select Add key

II. Push your commit

Push your commit as normal, you should see the marked "Verified" in the commit listing

github display mark Verified

III. Troubleshooting

1. Failed to sign commit

If you got error error: gpg failed to sign the data. fatal: failed to write commit object.

git commit -m "init"
error: gpg failed to sign the data
fatal: failed to write commit object

Prepend GIT_TRACE=1 then run the commit again to see what is the actual problem. Example:

GIT_TRACE=1 git commit -m "init"
12:05:55.591085 exec-cmd.c:139          trace: resolved executable path from Darwin stack: /Library/Developer/CommandLineTools/usr/bin/git
12:05:55.591488 exec-cmd.c:238          trace: resolved executable dir: /Library/Developer/CommandLineTools/usr/bin
12:05:55.591935 git.c:460               trace: built-in: git commit -m init
12:05:55.593151 run-command.c:654       trace: run_command: gpg --status-fd=2 -bsau 6A3B2EFE956B5B36
error: gpg failed to sign the data
fatal: failed to write commit object

This show us that there is something wrong with the gpg command, so let run the standalone gpg command to see what is the problem.

echo "dummy" | gpg -bsau 6A3B2EFE956B5B36
gpg: signing failed: Inappropriate ioctl for device
gpg: signing failed: Inappropriate ioctl for device

Hmm, something about ioctl, after some google search, the fix for this is to append this to your start-up bash file, in my case is .zshrc:

export GPG_TTY=$(tty)

The above did the trick and the commit signing now work properly.

If you got error message: Unusable secret key, it means your key has expired, you will have to generate new key and use new key.

2. Show the signature on commit

To make sure the commit is signed, you can show it signature by using this command, it will show signing information by prefix gpg:

$ git log --show-signature
commit c180cc5a0e1ba42d74a0dd5541fb940f08717f97
gpg: Signature made Sat Apr  8 10:24:49 2023 +07
gpg:                using RSA key 9EA3..redacted..56B5B36
gpg: Good signature from "hoangddt <[email protected]>" [ultimate]
Author: hoangddt <q..[email protected]>
Date:   Sat Apr 8 10:24:49 2023 +0700

    add: test file

3. Verified mark on remote git storage system

If the git storage doesn't show "Verified" mark, make sure the email address of the GPG key match with the email address of your account, and email in git commit message.

Conclusion

This extra step of configuration will make sure your commit are protected, if anyone want to change the commit's metadata, they are required to have the private key.

Remember to configure the signing public key with the git storage provider so your commit can be mark "Verified", indicate that is really come from you.

Next step, you might want to configure GPG passphrase so you don't have to type it every time in MacOS.