Skip to content

Rewrite and sign past Git commits with handy one-liners

  • Git
  • GPG
  • SSH

Recently I encountered a situation where I needed to rewrite the email from all past commits I made on a particular git repository. I used the following script:

#!/bin/sh

git filter-branch --env-filter 'if [ "$GIT_AUTHOR_EMAIL" = "incorrect@email" ]; then
GIT_AUTHOR_EMAIL=correct@email;
GIT_AUTHOR_NAME="Correct Name"
GIT_COMMITTER_EMAIL=$GIT_AUTHOR_EMAIL;
GIT_COMMITTER_NAME="$GIT_AUTHOR_NAME"; fi' -- --all'

It worked flawlessy, but I noticed that all changed commits were now unsigned. Fortunately, there's a simple one-liner for that, which also uses git-filter-branch under the hood:

#!/bin/sh

git filter-branch --commit-filter '
    if [ "$GIT_COMMIT" != "" ];
    then
        git commit-tree -S "$@";
    else
        git commit-tree "$@";
    fi' HEAD

You can run it directly, or save into a shell script and run it from there. It will sign all commits in the current branch. If you want to sign all commits in all branches, you can use --all instead of HEAD.

Since you've now rewritten the history, you'll need to force push the changes to the remote repository:

git push --force

Before you do that though, it may be worth checking if the commits have actually been signed and that a GPG/SSH signature is correct. You can run git log command with the following options:

git log --pretty=format:"%h %G? %aN <%aE> %s" -n 10

You may get a following message next to each commit:

error: gpg.ssh.allowedSignersFile needs to be configured and exist for ssh signature verification

It means that git doesn't know what signatures are valid yet. Find you signature (e.g. from ~/.gitconfig), create a new file, like ~/allowed_signers and add your copied signature there. Then run the following to tell git where to find it:

git config --global gpg.ssh.allowedSignersFile ~/allowed_signers

Let's now run the git log --pretty=format:"%h %G? %aN <%aE> %s" -n 10 command again. For reference, here's an explanation of each placeholder used in the format string:

%h: Abbreviated commit hash (usually the first 7 characters of the full commit hash)
%G?: Signature status of the commit (see below for a detailed explanation of the possible values)
%aN: Author name of the commit
<%aE>: Author email of the commit, enclosed within angle brackets <>
%s: Commit message (subject line)

Regarding the %G? placeholder, it shows the GPG/SSH signature status of each commit with the following possible values:

G: Good signature (signed and valid)
B: Bad signature (signed but invalid)
U: Untrusted signature (signed but not enough trust)
X: Expired signature (signed but signature has expired)
Y: Expiring signature (signed but signature will expire soon)
R: Revoked signature (signed but signature has been revoked)
E: Couldn't be checked (signature exists but an error occurred)
N: No signature

If everything looks good, force push to the remote repository. (Just remember that force-pushing can cause problems for collaborators who have already fetched or cloned the repository. Make sure to inform them of the changes and have them re-clone or rebase their local copies.)


PS. If you liked this article, please share to spread the word.

Share

Looking for a handy server monitoring tool?

Check out StackScout, a project I've been working on for the past few years. It's a user-friendly web app that keeps an eye on your servers across different clouds and data centers. With StackScout, you can set custom alerts, access informative reports, and use some neat analytics tools. Feel free to give it a spin and let me know what you think!

Learn more about StackScout

StackScout server monitoring tool screenshot