2

How do I view the history of a tag in git? For instance, let's suppose that it was decided that tag 't1' was pointing at the wrong commit after it has been pushed to other repositories. One can change the tag - git tag -a -f t1 [commitid]. git show t1 will show the information for the tag (annotation & what commit it currently points to). But how does one show the commits it may have pointed to in the past?

Note: This is a more general version of See who deleted git tag. There is an incantation in an answer for that question that uses git fsck and git show to allow one to get at "unreachable" commits assuming garbage collection has not reaped them.

Schwern
  • 153,029
  • 25
  • 195
  • 336
Juan
  • 1,204
  • 1
  • 11
  • 25
  • 3
    Short version: one doesn't. Longer version: if you had the tag last week, inspect the value it had last week. Update it every second, and note when it changes. (But don't do that, it's antisocial at best.) Tags are never *supposed* to change, as their purpose is to be a fixed name for a fixed commit. And, if a tag has annotations, you can read the annotations, which are free-form text that someone can use; perhaps if someone is misusing tags as moving pointers, they'll keep a log there. – torek May 08 '21 at 16:28
  • This was a weakness with cvs - inability to track changes on "metadata" like tags. It's shocking to me that git has [nearly] the same problem. It happens all the time in the real world that developers move tags before an official release is generated. – Juan May 08 '21 at 16:38
  • And if some slides a tag maliciously or accidentally, there's no [permanent] record of that? Color me stunned. – Juan May 08 '21 at 17:05
  • 2
    "*It happens all the time in the real world that developers move tags before an official release…*" People use Git in all possible wrong ways. Moving tags is one such a wrong way. "Moving tags" are branches and Git tracks their movement; see `git reflog`. – phd May 08 '21 at 17:27
  • 1
    It is not a moving tag in the sense that it is continually updated like a branch. It is [typically] a mistaken or premature tag that is intended to be a pointer to a particular commit, but needs to be moved. It's not wrong to do so despite your claim. It is most certainly wrong that git allows one to erase any signs of a tag from the repository history. By comparison, it's also 'wrong' to commit code with a bug. The proper way to address that is to commit a change (that is tracked, of course) that fixes the mistake. – Juan May 08 '21 at 18:14
  • @phd That response (and someone's upvote) is what allows tools like git to get away with allowing any record of a tag deletion to be removed from history. – Juan May 08 '21 at 18:17
  • Git doesn't track tag deletion so there is no history to remove records from. – phd May 08 '21 at 20:17
  • @phd. Git does track tag deletions, just as it does a tag rename. But unfortuately, that history is subject to garbage collection and thus may disappear. Everyone commenting here seems to think it's an acceptable design for a version control tool. Asserting that (a) tags are permanent and should not be moved and (b) then turning around to suggest that it is fine for them to be renamed and deleted with no history (or history that will disappear from the repo) is contradictory, and I am arguing, a large design flaw. – Juan May 09 '21 at 15:24
  • @Juan Think of it this way. Commits are a record of changes to the content. That is **The History**. Git is a content tracker, tracking content is its purpose. The History is what is useful to a project. Tags are not part of The History, they are sticky notes on The History. They help people find important moments. You can "move" tags ***if you force it***, that's what `-f` means *stop and think*. You're expecting Git to keep a history of how you're using tags on The History just in case someone uses tags in a way it has told you is abnormal. – Schwern May 09 '21 at 22:00
  • 2
    Tell all the users of github projects that a tag is not part of the project history. When that very important sticky note _can_ be moved, and there's no record of where it used to point, that's a flaw. Git already has some support for history for a tag (`git show tagX`). It should just (clearly this is the IMO part) support first class history tracking rather than something as weak as what it has now. VonC in a different answer mentioned hg: `hg tag` also requires -f for tag modification, but it still tracks that in history. Git could (and should) track that history as well. – Juan May 09 '21 at 22:57
  • @Juan It's a design choice. References with history would be implemented and treated quite different than the lightweight, ephemeral things they are now. (`git show tagX` displays the commit or tag object currently named tagX, no history). As the `hg` docs say "*The fact that tags identify changesets and are also parts of changesets has some potentially confusing implications*". That `hg` made a different design choice doesn't change how one uses tags *in Git* and that's what you've asked. If your process requires tag history, and you're using Git, you're gonna have a bad time. – Schwern May 10 '21 at 00:24
  • 2
    Just one real world example (and ensuing fallout) of moving the tag: https://github.com/getmail6/getmail6/issues/77. If there were visible history of the change, there would be no confusion, and everything would be documented. This comments discussion has deviated from the original question (the answer to which is: "You cannot"), of course, but one might be inclined to modify the last sentence of the previous comment to just: "If you're using Git, you're gonna have a bad time" (so far in my use of Git, that is certainly true). Putting it another way, it's a befuddling design choice. – Juan May 26 '21 at 23:28

3 Answers3

4

First, moving a tag around is first a local operation (that you can track, for up to 90 days) in git reflog.

(As noted by Guildenstern in the comments, setting core.logAllRefUpdates to always would force a missing reflog to be automatically created for any ref under refs/)

As long as the tag was not pushed, you can move it as many times as you need.

Once pushed, though, it should be considered immutable.

Which means if "It is [typically] a mistaken or premature tag that is intended to be a pointer to a particular commit, but needs to be moved", then, if it was pushed, you need to create a new tag.

For example, for a mistakenly applied 1.1.0 tag: create a 1.1.1 tag instead of the initial 1.1.0.
And publish a 1.1.0_deprecated on the commit currently referenced by 1.1.0 to better communicate that the 1.1.0 tag should not be considered.

VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
  • "considered immutable" - maybe one should _consider_ it immutable, and usually people conventionally treat tags a such, but it is _not_ immutable. The tool allows deleting / renaming tags - and pushing such operations without reporting any sort of conflict or consitency problem. Thus, it _should_ support history tracking of such operations. That is certainly my opinion. I am surprised that (so far) the answers appear to be adamant that losing tag change history is not a flaw. – Juan May 09 '21 at 16:18
  • "moving a tag around is a first a local operation". Nitpicking that language a little - all git operations (except inter-repo operations like push/pull) are first a local operation. In that respect, `tag` is not unique. It can be differentiated other ways, of course. – Juan May 09 '21 at 16:22
  • @Juan Once it is pushed, it is, in effect, immutable. We are taking about *distributed* history here. The tag can be then fetched/pulled around. Changing it won't change the same tag in other cloned updated repositories. – VonC May 09 '21 at 16:49
  • You can delete/move a tag, and push/pull the tag change. The tool allows it. And, perhaps worse, if you don't push/pull it (or only push to say, a repo treated as conventionally authoritative), it is extremely hard to see that there is any difference between the repos that have not received this change. – Juan May 09 '21 at 17:18
  • I agree that it is "bad form" to modify a tag. But, again, the tool allows it. Because of how git handles tag changes, it is worse than "bad form" really. I feel git should just accept that it has been given the option to the user and get an update to fully support history tracking of such a change. OR truly make it immutable if it won't support history tracking of a tag change. – Juan May 09 '21 at 17:18
  • @Juan I understand. I discussed that kind of issue for months back in 2010-2013, with Mercurial users (who do have tag history https://www.mercurial-scm.org/wiki/TagDesign, as well as global-like states, like "closing a branch": https://stackoverflow.com/a/3228023/6309) – VonC May 09 '21 at 18:21
  • @Juan I stopped though, and accepted the constraints/contradictions which come with a file-based local tool like Git vs. distributed history. Also because (almost) nobody else uses Mercurial anymore. (https://dev.to/syntaxseed/migrating-mercurial-repos-to-git-23ee) – VonC May 09 '21 at 18:24
  • Not getting into the hg v git debate too much, but hg has it right here (re: tag history). – Juan May 09 '21 at 22:37
  • “that you can track, for up to 90 days” If you have set `core.logallrefupdates` to `always`, which is not the default (unfortunately). – Guildenstern May 07 '23 at 18:48
  • 1
    @Guildenstern Thank you for your feedback, good point. I have included your comment in the answer for more visibility. – VonC May 07 '23 at 19:02
2

How do I view the history of a tag in git?

You can't, it does not exist.

Lightweight tags and branches are labels which point at a commit. Each is a single file on disk or line in a packfile with a name and the commit ID it points to. They have no history. When a tag is moved or a branch updated the file or line is overwritten.

Annotated tags work the same way, but they have a name and the ID of a tag object. A tag object holds the tag name, annotation and the commit it points at. git tag -a -f makes a new tag object and changes the file/line to point at the new tag object. There is no history.

Here's what is inside an unreachable tag object.

$ git fsck --unreachable | grep tag
Checking object directories: 100% (256/256), done.
Checking objects: 100% (3/3), done.
unreachable tag f25f9e059dc07f741aae47ee422f675b9bdd0f5f

$ openssl zlib -d < .git/objects/f2/5f9e059dc07f741aae47ee422f675b9bdd0f5f
 
tag 153object 91e645c1a8a2ff1edc788066ef02f349f959da20
type commit
tag annotated
tagger Michael G. Schwern <schwern@pobox.com> 1620501711 -0700

test annotated

It points at commit 91e645c. It was tagged by me today. The annotation is "test annotated".

As you noted it is possible, on the repository which moved the tag, to find unreferenced tag objects assuming they have not yet been garbage collected. git fsck --unreachable | grep tag This is only an emergency operation to recover after a mistake and should not be part of normal procedure. Backups are a better option.

It happens all the time in the real world that developers move tags before an official release is generated.

Then they are using tags wrong. The sole purpose of tags is to not move. Tags which move are branches. Use a branch instead.

For example, perhaps you have a release tag which you move every time you release a new version. Replace this with a branch called release. They are functionally equivalent, but a release branch is supposed to move.

A release branch still doesn't have a history. If you want a release history that is what tags are for. Tag each release like v1.0.0, v1.0.1, v1.1.0 and so on. If the tag is wrong and has already been pushed don't change the tag, declare a new release.

Schwern
  • 153,029
  • 25
  • 195
  • 336
  • "You can't, it doesn't exist". That is not accurate. You can view history (`fsck --unreachable`) after a tag rename / deletion. However, that history is subject to garbage collection. I am nitpicking your statement that it does not exist. In spirit, since it will disapper due to [eventual] GC (unless you perform no operation that triggers GC), effectively users could treat that "it does not exist" as more or less how git (currently) works. – Juan May 09 '21 at 15:30
  • "Then they are using tags wrong". You are saying that tags should not be changed - and at the same time saying that tags should not have history. Yet the tool allows you to delete and rename tags (and allow the history of such operations to disappear from the repo history via GC). There's a problem there, in my opinion. And it's a similar problem that existed/exists in cvs from 20+ years ago. If tags are "permanent" and tags can be renamed/deleted, the history of such an operation should be tracked and not disappear due to GC. I'm amazed people seem to be fine with that. – Juan May 09 '21 at 16:01
  • The rest of the answer is good information about this issue using the [today's version of] git. Thank you for that. The opinion parts could be modified to emphasize that it is opinion... or even the common workflow. But this limitation of the tool could also be noted - and would (IMO) improve the answer. The tool _should_ protect against some things. Absent that protection, the comments about _why_ one should treat tags with extra care (_because of_ the tool's limitations) will be valuable to the unwary. – Juan May 09 '21 at 16:09
  • @Juan Git is very different from other version control systems. It is fundamentally very simple, everything it does is just a handful of tricks recombined. It gives you power, but fundamental restrictions, and fewer guard rails. Tags are not supposed to move, but there reasons you might have to so you can *with a `--force` telling you this is not normal*. Git does not consider history to be immutable, nor all events to be worth retaining. Rebase and moving tags are not history *they reshape history* to better serve the project. This all takes some getting used to, and its worth it. – Schwern May 09 '21 at 21:28
  • 1
    I'm fully familiar with the dvcs model and many of the variant implementations thereof. Just because it's 'not normal' doesn't mean that something like tag changes should not have full first class history tracking. Mutable history is fine as long as one can push/pull safely between repositories without losing history. I stand by my comments and critique of the "opinion" parts of your answer (intended to be honest, not antagontistic, feedback). – Juan May 09 '21 at 22:49
1

If you want an audit log for all tag changes, you can tell git to keep one:

git config --global core.logallrefupdates always

Afterwards, git will start keeping track of all changes to all tags in the reflog. To find out why a tag was moved or deleted and where it previously pointed, you can then simply query the reflog:

git reflog mytagname

Note that - at least by default - this history will not be kept around forever, as it is subject to the usual reflog expiration rules. You can prevent that by changing the following options:

So, contrary to the other answers, what you want is definitely possible. (With a few limitations, such as that the reflog is local to each clone, which means that you will not have a consistent, shared history of your tags.)

I also believe that the philosophical debate on whether you should do this is unnecessary. While git tags certainly weren't designed to be moved around, I've also run into scenarios where I needed exactly that, and having an audit log for each tag for debugging purposes makes sense even if you never intend to move them.

mb-lang
  • 73
  • 5
  • Is `always` a valid value for `core.logallrefupdates`? Seems that `git config` only mentions `true` and `false`. – Guildenstern May 04 '23 at 13:54
  • I was wrong: `always` *is* a valid value, and (confusingly) seems to log more than `true` (I would have thought that `true` would be enough). Thanks! – Guildenstern May 04 '23 at 21:45