23

I created a tag and forgot to sign it, then pushed the tag to GitHub. Is it possible to just sign that tag retroactively, or do I have to create a new tag?

I have read the man page for git tag and googled a bit, but have come up with no clues that adding a signature to an already existing tag is possible.

3 Answers 3

19

No you would need to::

  • replace it with a tag using the same name:

      git tag <tag name> <tag name> -f -s
    
  • but first set the committer date, in order to not change the date

      set GIT_COMMITTER_DATE="$(git log -1 --format=%aD <tag_name>)"
    

As mrts adds in the comments, since the tag was already pushed:

You also need to force push the updated tag with git push origin <tag_name> -f


Starting with Git 2.42 (Q3 2023), you can sign only the tags which need it, using:

git for-each-ref --format='%(refname) %(signature)' refs/tags | python re-sign-tags.py
                                      ^^^^^^^^^^^^
                                   (new with Git 2.42)

And re-sign-tags.py

import sys
import subprocess

def verify_tag(refname, signature):
    grade = signature.split(":")[1]
    if grade == "good":
        print("Tag '%s' is signed with a good signature." % refname)
    else:
        print("Tag '%s' is signed with a bad signature." % refname)

def re_sign_tag(refname, signature):
    signer = signature.split(":")[1]
    print("Re-signing tag '%s' with key '%s'." % (refname, signer))
    subprocess.run(["git", "tag", "-f", refname, refname, "-s"], check=True)

if __name__ == "__main__":
    for refname, signature in sys.stdin:
        if refname.startswith("refs/tags/"):
            if verify_tag(refname, signature):
                re_sign_tag(refname, signature)

With Git 2.42 (Q3 2023), the "git for-each-ref"(man) family of commands learned placeholders related to GPG signature verification.

See commit 26c9c03, commit 2f36339 (04 Jun 2023) by Kousik Sanagavarapu (five-sh).
(Merged by Junio C Hamano -- gitster -- in commit 81ebc54, 14 Jul 2023)

ref-filter: add new "signature" atom

Co-authored-by: Hariom Verma
Co-authored-by: Jaydeep Das
Co-authored-by: Nsengiyumva Wilberforce
Mentored-by: Christian Couder
Mentored-by: Hariom Verma
Signed-off-by: Kousik Sanagavarapu

Duplicate the code for outputting the signature and its other parameters for commits and tags in ref-filter from pretty.
In the future, this will help in getting rid of the current duplicate implementations of such logic everywhere, when ref-filter can do everything that pretty is doing.

The new atom "signature" and its friends are equivalent to the existing pretty formats as follows:

%(signature) = %GG
%(signature:grade) = %G?
%(siganture:signer) = %GS
%(signature:key) = %GK
%(signature:fingerprint) = %GF
%(signature:primarykeyfingerprint) = %GP
%(signature:trustlevel) = %GT

git for-each-ref now includes in its man page:

signature

The GPG signature of a commit.

signature:grade

Show "G" for a good (valid) signature, "B" for a bad signature, "U" for a good signature with unknown validity, "X" for a good signature that has expired, "Y" for a good signature made by an expired key, "R" for a good signature made by a revoked key, "E" if the signature cannot be checked (e.g. missing key) and "N" for no signature.

signature:signer

The signer of the GPG signature of a commit.

signature:key

The key of the GPG signature of a commit.

signature:fingerprint

The fingerprint of the GPG signature of a commit.

signature:primarykeyfingerprint

The primary key fingerprint of the GPG signature of a commit.

signature:trustlevel

The trust level of the GPG signature of a commit. Possible outputs are ultimate, fully, marginal, never and undefined.

3
  • See also, on a similar topic (including the push): stackoverflow.com/a/21741848/6309
    – VonC
    Commented Aug 17, 2014 at 8:40
  • 1
    You also need to force push the updated tag with git push origin <tag_name> -f.
    – mrts
    Commented Jul 5, 2021 at 19:31
  • 2
    @mrts Good point. I have included your comment in the answer for more visibility.
    – VonC
    Commented Jul 5, 2021 at 19:54
4

I know this is an old question but going off of what VonC said. I created a one-line bash script that can go through all of your tags in your repository and sign them automatically. All you have to do is either accept or change the message set in the old tag. Here is the command I came up with

git for-each-ref refs/tags | awk '{print $3}' | cut -c11- | xargs -I % sh -c 'git tag % % -f -s'
0
1

This one-liner will sign all your local tags and force push them. At the same time, it will set the date of the tag to the commit it is attached to:

git tag -l | while read -r tag ; do COMMIT_HASH=$(git rev-list -1 $tag) && GIT_COMMITTER_DATE="$(git show $COMMIT_HASH --format=%aD | head -1)" git tag -a -s -f $tag -m"$tag" $COMMIT_HASH ; done && git push --tags --force

Source: This is a modified version of this other answer by vmrob adjusted to the current question.

Not the answer you're looking for? Browse other questions tagged or ask your own question.