How to interactively use git rebase to fix unsigned GPG commits.
This is mainly a "post-it note to self" as I usually don't forget to sign my git commits, but occasionally I run into issues where I don't notice a commit has not been signed until 30 commits later. Here's the solution...
git rebase -i ID_OF_COMMIT_BEFORE_FIX
editfor the commit(s) needing a GPG signature and save.
git commit --amend --no-edit -S
git rebase --continue
In my usual workflow, especially when working with software that utilizes Git Flow like branches, I use GitKraken for visualization of the git structure. It has a nice UI and does a great job at presenting git repositories. But it does lack some "advanced" features to be truly useful to me. Such as handling complex GPG / SSH key setup's. (More on that in a future post)
This morning I was working on rushlow-development/atom, a PHP library to to generate fully compliant RFC4287 RSS/Atom feeds (Currently in initial development). I had mistakenly performed a cherry pick using GitKraken. I say mistakenly because as I mentioned previously, GitKraken doesn't recognize my GPG key setup, so while it did perform the cherry pick, GitKraken didn't actually sign the commit. I didn't notice this until after I had pushed a PR up to GitHub and seen one of my commits didn't have the green "verified" tag. Dang it!
Are my commits signed?
In the CLI,
git log --show-signature let's you know...
user@host:~/document$ git log --show-signature commit 4ff30a5... (HEAD -> master) gpg: Signature made Tue 28 Jan 2020 05:36:27 AM EST gpg: using RSA key ...741096D2ADE04CA gpg: Good signature from "Jesse Rushlow <firstname.lastname@example.org>" [ultimate] Author: Jesse Rushlow <email@example.com> Date: Tue Jan 28 05:36:01 2020 -0500 updated doc commit 95f8587... Author: Jesse Rushlow <firstname.lastname@example.org> Date: Tue Jan 28 05:21:15 2020 -0500 ignore ideas commit c772d80d... gpg: Signature made Tue 28 Jan 2020 05:19:33 AM EST gpg: using RSA key ...741096D2ADE04CA gpg: Good signature from "Jesse Rushlow <email@example.com>" [ultimate] Author: Jesse Rushlow <firstname.lastname@example.org> Date: Tue Jan 28 05:19:28 2020 -0500 Initial commit
I've modified the output above for readability but as you can see, the first (Initial commit) & the third (updated doc) commits were both signed using my GPG key. The second commit (ignore ideas) however was not signed.
As the above scenario doesn't happen often to me, I always seem to forget how to sign previous commits without getting into a git mess. After doing some googling, rebasing hit's me square in the forehead. Duh!
The key is that we want to begin the rebase at the commit just before the commit which needs to be modified. In this case it will be the initial commit
c772d80. We also want to
git rebase -i c772d80 will open a text editor that will allow use to tell git which commit we want to modify.
user@host:~/document$ git rebase -i c772d80 Editor: pick 95f8587 ignore ideas pick 4ff30a5 updated doc # Rebase c772d80..80753f3 onto c772d80 (3 commands) # # Commands: ..........
We are going to change
pick 95f8587 ignore ideas within the editor and save as shown below.
Editor: edit 95f8587 ignore ideas pick 4ff30a5 updated doc # Rebase c772d80..80753f3 onto c772d80 (3 commands) # # Commands: ..........
Git will then bring you back to the command prompt and halt at the commit that needs editing:
user@host:~/document$ git rebase -i c772d80 Stopped at 95f8587... ignore ideas You can amend the commit now, with git commit --amend '-S' Once you are satisfied with your changes, run git rebase --continue user@host:~/document$
Now we can sign the commit using
git commit --amend --no-edit -S
--amend Amend the previous commit.
--no-edit Use the existing commit message. No need to edit it.
-S GPG-sign the commit. If you need to specify the GPG key to use,
-S[<keyid>] is more appropriate.
After running the above command, git will output something similar to the following:
user@host:~/document$ git commit --amend --no-edit -S [detached HEAD c11cc9e] ignore ideas Author: Jesse Rushlow <email@example.com> Date: Tue Jan 28 05:21:15 2020 -0500 1 file changed, 1 insertion(+) create mode 100644 .gitignore user@host:~/document$
We can now go ahead and finish up the rebase with
git rebase --continue & if all goes well, git will tell you that the rebase was successful. To verify that we signed the commit,
git log --show-signature should now show the GPG signature:
user@host:~/document$ git rebase --continue Successfully rebased and updated refs/heads/master. user@host:~/document$ git log --show-signature ... commit c11cc9e... gpg: Signature made Tue 28 Jan 2020 05:58:38 AM EST gpg: using RSA key ...741096D2ADE04CA gpg: Good signature from "Jesse Rushlow <firstname.lastname@example.org>" [ultimate] Author: Jesse Rushlow <email@example.com> Date: Tue Jan 28 05:21:15 2020 -0500 ignore ideas ... user@host:~/document$
You're all set! Happy coding...
Word to the wise...
If you have already pushed the unsigned commit upstream to github as I did, care should be taken before using the above described method. As there could be negative consequences.
First, after performing the rebase, you will more than likely have to force push the branch upstream to github. If this is a feature branch that hasn't been merged / modified by others, forcing the push shouldn't cause any headaches.
If there have been modifications to the branch / PR upstream, you may want to revert the commit, and just create a new signed commit rather than doing a rebase. If all else fails, you can always let the unsigned commit ride solo as a reminder not to make that mistake again...
As always comments, rants, suggestions, and constructive criticism are always welcome.
- Jesse Rushlow