TL;DR: Matching dates + re-creating GPG signatures
(Comment/edit if you know a workaround for the stripping to preserve the original signature.)
I'll bump this old thread because a feature of signing commits has been introduced and all of these git filter-branch and likes basically strip the signatures as specified in the docs:
... If the tag has a signature attached, the signature will be stripped. It is by definition impossible to preserve signatures. ... (source: --tag-name-filter )
But it'll also break the pretty Verified badge on a GitHub commit (and in other Git hosting places if implemented in the same way), so this will fix that as well. Partially.
Afaik it's not possible to mangle a (GPG) signature through git command in a way that it also contains the date of a commit instead of the date of signing in a simple way and therefore even if the dates for authoring and commit are moved, it'll still be the present date, example:
commit <hash>
gpg: Signature made Sun 25 Jul 2021 00:00:00 PM TZ
gpg: using TYPE key KEY
gpg: Good signature from "Signer <email@domain.tld>"
Author: Author <email@domain.tld>
AuthorDate: Sat Jan 1 00:00:00 2000 +0000
Commit: Author <email@domain.tld>
CommitDate: Sat Jan 1 00:00:00 2000 +0000
So imagine you have a repo you want to sign from a certain commit (I'll go for the root commit; not recommended if somebody else works on the repo). The documentation for git commit says it pulls data from env vars too, if present, thus we have a place to put the input to.
To retrieve the data (can be set with git commit --date=...) we can take a look at git show --format=%ad, so for a raw date string that would be:
git show --format=%ad --no-patch
# Sat Jan 1 00:00:00 2000 +0000
So we have:
- point of start
- raw date string for each commit
GIT_COMMITTER_DATE to match the dates (author -> committer)
For rebasing let's do this:
git rebase --root <branch-name> --keep-empty --interactive
which will go for the root commit of a branch <branch-name>, preserve any empty commits created with git commit -m "empty" --allow-empty and ask you which commits to modify. There you change the desired commits from pick to edit (for my case that'd be marking all of them as edit), then you'll be dropped into a detached HEAD commit and from here the fun begins.
# or "while :"
while true
do
GIT_COMMITTER_DATE=$(git show --format=%ad --no-patch) \
git commit --amend --gpg-sign --no-edit --allow-empty
git rebase --continue
done
(if you do not have user.signingkey specified, use --gpg-sign=<fingerprint>)
This will go through each of the edit-marked commit, set the committer's date to match the author's date, keep any empty commit, won't touch the overall patch body and will add a signature with a date of when the command was executed.
Once you see fatal: No rebase in progress? press Ctrl-C to stop the loop and check the logs to confirm that the dates match and the signatures are present everywhere with:
git log --pretty=fuller --show-signature
If everything is okay in the logs, simply issue git push --force and you're done. Now you should see that Verified badge for each commit.
Example with a real history tree. GitHub doesn't seem to care about the signature's date (no reference anywhere), but it'll still be present in the git log.