开发者

Why should I care about lightweight vs. annotated tags?

开发者 https://www.devze.com 2023-02-10 12:39 出处:网络
I switched from Subversion to Git as my day-to-day VCS last year and am still trying to grasp the finer points of \"Git-think\".

I switched from Subversion to Git as my day-to-day VCS last year and am still trying to grasp the finer points of "Git-think".

The one which has been bothering me lately is "lightweight" vs. annotated vs. signed tags. It seems pretty universally accepted that annotated tags are superior to lightweight tags for all real uses, but the explanations I've found for why that's the case always seem to boil down to either "because best practices" or "because they're different". Unfortunately, those are very unsatisfying arguments without knowing why it's best practices or how those differences are relevant to my Git usage.

When I开发者_如何学Go first switched to Git, lightweight tags seemed to be the best thing since sliced bread; I could just point at a commit and say "that was 1.0". I'm having trouble grasping how a tag could ever need to be more than that, but I certainly can't believe that the Git experts of the world prefer annotated tags arbitrarily! So what's all the hubbub about?

(Bonus points: Why would I ever need to sign a tag?)

EDIT

I've been successfully convinced that annotated tags are a Good Thing — knowing who tagged and when is important! As a follow-up, any advice on good tag annotations? Both git tag -am "tagging 1.0" 1.0 and trying to summarize the commit log since the previous tag feel like losing strategies.


The big plus of an annotated tag is that you know who created it. Just like with commits, sometimes it's nice to know who did it. If you're a developer and you see that v1.7.4 has been tagged (declared ready) and you're not so sure, who do you talk to? The person whose name is in the annotated tag! (If you live in a distrustful world, this also keeps people from getting away with tagging things they shouldn't.) If you're a consumer, that name is a stamp of authority: that's Junio Hamano saying this version of git is hereby released.

The other metadata can be helpful too - sometimes it's nice to know when that version was released, not just when the final commit was made. And sometimes the message can even be useful. Maybe it helps explain the purpose of that particular tag. Maybe the tag for a release candidate contains a bit of a status/to-do list.

Signing tags is pretty much like signing anything else - it provides one more level of security for the paranoid. Most of us aren't ever going to use it, but if you really want to verify everything before you put that software on your computer, you might want it.

Edit:

As for what to write in a tag annotation, you're right - there's not always much useful to say. For a version number tag, it's implicitly understood that it marks that version, and if you're happy with your changelogs elsewhere, there's no need to put one there. In this case, it's really the tagger and date that are the most important. The only other thing I can think of is some sort of stamp of approval from a test suite. Have a look at git.git's tags: they all just say something like "Git 1.7.3 rc1"; all we really care about is Junio Hamano's name on them.

However, for less obviously named tags, the message could become much more important. I could envision tagging a specific special-purpose version for a single user/client, some important non-version milestone, or (as mentioned above) a release candidate with extra information. The message is then much more useful.


My personal, slightly different view on that topic:

  • Annotated tags are those tags meant to be published for other developers, most probably new versions (which should also be signed). Not only to see who tagged and when it was tagged, but also why (usually a changelog).
  • Lightweight are more appropriate for private use, that means tagging special commits to be able to find them again. May it be to review them, check them out to test something or whatever.


By default, Git only looks at annotated tags as a baseline for commands like git describe. Think of annotated tags as signposts that have enduring meaning to yourself and others, while lightweight tags are more like bookmarks for your later self to find. Hence, annotated tags are worth using as a reference, while lightweight tags shouldn't be.

Signing a tag is an assurance of the signer's identity. It lets users verify, for example, that the Linux kernel code they've picked up is the same code that Linus Torvalds actually released. The signature can also be an assertion that the signer is vouching for the software's quality and integrity at that commit.


Push annotated tags, keep lightweight local

Certain Git behaviors do differentiate between them in ways that this recommendation is useful e.g.:

  • annotated tags can contain a message, creator, and date different than the commit they point to. So you could use them to describe a release without making a release commit.

    Lightweight tags don't have that extra information, and don't need it, since you are only going to use it yourself to develop.

  • git push --follow-tags will only push annotated tags
  • git describe without command line options only sees annotated tags

man git-tag says:

Annotated tags are meant for release while lightweight tags are meant for private or temporary object labels.

Internals differences

  • both lightweight and annotated tags are a file under .git/refs/tags that contains a SHA-1

  • for lightweight tags, the SHA-1 points directly to a commit:

    git tag light
    cat .git/refs/tags/light
    

    prints the same as the HEAD's SHA-1.

    So no wonder they cannot contain any other metadata.

  • annotated tags point to a tag object in the object database.

    git tag -as -m msg annot
    cat .git/refs/tags/annot
    

    contains the SHA of the annotated tag object:

    c1d7720e99f9dd1d1c8aee625fd6ce09b3a81fef
    

    and then we can get its content with:

    git cat-file -p c1d7720e99f9dd1d1c8aee625fd6ce09b3a81fef
    

    sample output:

    object 4284c41353e51a07e4ed4192ad2e9eaada9c059f
    type commit
    tag annot
    tagger Ciro Santilli <your@mail.com> 1411478848 +0200
    
    msg
    -----BEGIN PGP SIGNATURE-----
    Version: GnuPG v1.4.11 (GNU/Linux)
    
    <YOUR PGP SIGNATURE>
    -----END PGP SIGNAT
    

    And this is how it contains extra metadata. As we can see from the output, the metadata fields are:

    • the object it points to
    • the type of object it points to. Yes, tag objects can point to any other type of object like blobs, not just commits.
    • the name of the tag
    • tagger identity and timestamp
    • message. Note how the PGP signature is just appended to the message

    A more detailed analysis of the format is present at: What is the format of a git tag object and how to calculate its SHA?

Bonuses

  • Determine if a tag is annotated:

    git cat-file -t tag
    

    Outputs commit for lightweight, tag for annotated.

  • List only lightweight tags: How can I list all lightweight tags?


Signing a tag is an easy way to assert the authenticity of a release.

This is particularly useful in a DVCS because anyone can clone the repository and modify history (e.g. via git-filter-branch). If a tag is signed, the signature will not survive a git-filter-branch operation, so if you have a policy that every release is tagged and signed by a committer, it's possible to detect a bogus release tag in the repository.

If it weren't for signing, I wouldn't see much point in annotated tags either.


I've found the one good use for lightweight tags - creating a release at GitHub in retrospective.

We did release our software and we had the necessary commits, we just didn't bother to maintain the 'Release' section on the GitHub. And when we gave that a little attention, we've realised that we would want to add some previous releases too, with correct old release dates for them.

If we would just create an annotated tag on an old commit, GitHub would take the date for the release from the tag object. In contrast, when we've created a lightweight tag for this old commit, the release started showing the correct (old) date. Source @ GitHub help, 'About releases'

It seems it's also possible to specify your desired date for an annotated commit, but it doesn't look that simple to me: https://www.kernel.org/pub/software/scm/git/docs/git-tag.html#_on_backdating_tags


For me important difference is lightweight tag doesn't have timestamp. Let's say you added several lightweight tags:

git tag v1
git tag v2
git tag v3

and then, maybe later, you want to get last added lightweight tag. There is no way to do it. Neither "git describe" nor "git tag" will not give you chronologically last lightweight tag. "git tag -l" can return all of them or sort them in lex order, but not by date/time. "git describe --tags" will return "v1" which is definitely not last added tag.

From the other hand, if you add annotated tags:

git tag v1 -m v1
git tag v2 -m v1
git tag v3 -m v1

you always can get timestamp of every tag and "git describe" will sure return "v3" which is really last added tag.


Annotated tags store extra metadata such as author name, release notes, tag-message, and date as full objects in the Git database. All this data is important for a public release of your project.

git tag -a v1.0.0

Lightweight tags are the simplest way to add a tag to your git repository because they store only the hash of the commit they refer to. They can act like "bookmarks" to a commit, as such, they are great for private use.

git tag v1.0.0

You can sort, list, delete, show and edit old tags. All these functions will help you to identify specific release versions of your code. I found this article that could help you to get a better idea what tags can do.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号