[HN Gopher] Proper Use of Git Tags
       ___________________________________________________________________
        
       Proper Use of Git Tags
        
       Author : da-x
       Score  : 189 points
       Date   : 2022-05-23 15:30 UTC (7 hours ago)
        
 (HTM) web link (blog.aloni.org)
 (TXT) w3m dump (blog.aloni.org)
        
       | bmon wrote:
       | I can't agree with having a source file include the tag. It is an
       | eternal source of merge conflicts and pain, unless you take steps
       | to automate it. And in that case, does the file add much value
       | anymore?
       | 
       | My personal experience with go modules and versioning has been
       | really positive. In that ecosystem - you only define your major
       | version in the mod file and rely on the VCS for everything else.
        
         | da-x wrote:
         | I don't think that it poses an issue with merge conflicts. It
         | is likely not an issue as long as the tagging is only made on
         | the main branch by the release managers.
         | 
         | I know it is working well for Linux kernel hackers. Linus does
         | all the tagging, and I don't recall issues with merge conflicts
         | on the top Makefile with the part that holds the version.
        
           | zbuf wrote:
           | I think you're assuming that all software results in a single
           | "release manager".
           | 
           | In many environments, multiple branches of software are
           | deployed and maintained at the same time.
           | 
           | Even your example with Linux; I don't think that is correct
           | -- Linus doesn't tag the stable branches, and others. There
           | is a centralised agreement of how the version numbers are
           | maintained though.
        
             | da-x wrote:
             | My example is relevant to tagging in branches that may
             | merge back into the main one. The stable branches in Linux
             | are cherry-pick branches that don't get merged back and
             | therefore creating tags there is harmless.
        
         | WorldMaker wrote:
         | Yeah, I often prefer, when possible, to automate in CI dumping
         | the git describe output to a .gitignored source file and
         | letting tags themselves be the "source of truth".
         | 
         | Related to that "tags as source of truth" is using tags as a
         | deployment trigger. A release manager applying a tag can be a
         | signal or gate for a version to go to later environments. (For
         | instance: CI builds from main branches stop at Dev
         | environments, CI builds triggered from new tags automatically
         | move on to UAT and Staging environments.)
         | 
         | Also, another tip I've found useful for people with more
         | "monorepos": tag name restrictions match branch name
         | restrictions and you can use a "folder structure" of tags. You
         | can name tags things like subcomponent/v1.0.2. Some git UIs
         | even present such tags as a folder structure. Doing that can
         | confuse git describe, of course, so finding an arrangement that
         | works for your project may be a balancing act. I've used
         | lightweight tags for subcomponents so that git describe doesn't
         | "see them" by default and then you can use the git describe
         | --tags that also takes lightweight tags into account if you
         | need a "subcomponent version" for subcomponent tag triggered
         | deployments (and then you just need to remove to remove the
         | folder prefix).
        
           | aarchi wrote:
           | > you can use a "folder structure" of tags. You can name tags
           | things like subcomponent/v1.0.2. [...] Doing that can confuse
           | git describe
           | 
           | Using the --match option, `git describe
           | --match='subcomponent/*'` fixes this problem. It filters the
           | tags that are considered to only those matching the pattern,
           | so that a later tag for another subcomponent will not be
           | used.
        
         | zbuf wrote:
         | Yes, having a version number centralised in source code is
         | exactly the thing Git helped us many times to avoid.
         | 
         | Also ties in with the author's recommendation to begin tags
         | with "v". My experience is that excluding it is better. Then a
         | simple "git describe" readily gives the version number in
         | scripts with no sed or reprocessing.
         | 
         | I've seen many conventions with Git. It's interesting to hear
         | some rationale, but a stretch to describe these suggestions as
         | the "proper" way.
        
           | jonnycomputer wrote:
           | How is the version information included in the software if
           | you don't include it in the source? Do you have a deploy
           | script that modifies source based on the git tag, or ?
        
             | WorldMaker wrote:
             | I sometimes dump git describe to a JSON file rather than
             | modifying a source file and let the build bundle it as an
             | embedded resource. You can .gitignore the JSON file to keep
             | it from accidentally getting checked in (and causing merge
             | conflicts).
             | 
             | As also pointed out, many build tools that want or need
             | version numbers often also have command line flags or can
             | take environment variables instead of using source files.
        
             | t0astbread wrote:
             | Some languages and build systems also allow you to set
             | constants from compiler flags (Go for example). Other
             | systems make the entire build configuration an executable
             | program (Gradle).
        
           | CameronNemo wrote:
           | The v prefix really helps us only run ci on version tags
           | instead of random tags as well. If you are strict and always
           | use semantic versions for tags, you can just keep doing what
           | you are doing. But if you ever want to create some random tag
           | later and don't want your automation to try to use it as a
           | version number, the v prefix helps.
        
         | howinteresting wrote:
         | How does it work if a single repository contains more than one
         | independently usable library?
        
           | wnoise wrote:
           | Well, if they're actually independent, clearly those
           | shouldn't be in the same repository...
        
             | howinteresting wrote:
             | That doesn't follow. Why should they not be in the same
             | repository? They may be related but independently usable.
        
             | tricky777 wrote:
             | mono repo approach (or something to that direction)
        
             | t0astbread wrote:
             | "Independently usable" does not have to mean "totally
             | independent". The libraries could be related to the same
             | product using the same infrastructure.
        
           | WorldMaker wrote:
           | Tags in git allow just about the same naming options as
           | branches, and "v" prefix is just a convention not a
           | requirement. So rather than applying a tag v1.3.2 you could
           | use a "folder structure" such as library1/v1.3.2 and
           | library2/v3.1.2. Today I learned that you can use `git
           | describe --match="library1/v*"` to get the version relative
           | to just "library1" if you are versioning this way (in a
           | monorepo, for instance).
        
       | TekMol wrote:
       | What are the pros and cons of branches+merge-commits vs tags?
       | 
       | So instead of a "v4.11-rc7-87" tag you would have a
       | "v4.11-rc7-87" branch and a merge commit that holds the meta info
       | about that branch.
        
         | da-x wrote:
         | These are not mutually exclusive. Tags relate to commits
         | irrespective of the containing branch or the merge history.
        
         | Hackbraten wrote:
         | Branches are mutable, tags aren't. You can add a commit to
         | `v4.11-rc7-87` anytime. Now your branch has grown past the
         | merge commit. Confusion ensues.
         | 
         | Compared to a tag, you'd have to deliberately go out of your
         | way to move a tag (`git tag -f`). Generally, you should be able
         | to trust your team members not to move a tag.
        
         | ww520 wrote:
         | They are for different purposes. Branch is for working on the
         | code. Once done, use tag to snapshot a release. The branch can
         | move forward as needed.
        
         | WorldMaker wrote:
         | Annotated Tags hold meta info for the tag. (git tag -a) That's
         | why a lot of advice is to always use annotated tags. They look
         | like a commit with an author, a message, and can be signed,
         | etc.
         | 
         | Branches can change over time, whereas tags are largely
         | immutable. A change to a tag always requires a force push and
         | if you've got branch protection tools in your repository host's
         | arsenal you can often entirely prevent tag changes.
         | 
         | I'd also like to point out that often if you are using merge
         | commits as your "version management tool" there's a lot of
         | people that use "branch per environment" strategies and need to
         | merge between environments. There's a lot more risk in that
         | approach than a tag-based approach: if a tag doesn't change
         | after it is applied, then you don't need to rebuild binaries to
         | deploy it again. If you don't rebuild binaries between
         | environments you have to make sure the same binaries work in
         | all environments, which is good practice. I've seen too many
         | times "branch-per-environment" strategies wind up with per-
         | environment code that is difficult to untangle and makes
         | testing and debugging difficult and merge conflicts more likely
         | and more complicated and increases the risk of per-environment
         | bugs.
        
         | da-x wrote:
         | The tag in the repo is `v4.11-rc7` not `v4.11-rc7-87`. The
         | `-87-g28cf22d0ba28` suffix is added by `git describe`.
        
       | Robin_Message wrote:
       | Since the OP is here, I am confused.
       | 
       | In the first section, are you saying that:
       | 
       | a) tags can and should be named to include the sha at the end;
       | and
       | 
       | b) git commands silently discard everything before "-g" if what
       | follows is a sha?
       | 
       | I feel like I've missed something as I find b) very surprising.
       | (Consider giving someone the string
       | $LATEST_VERSION_NUMBER-g$MALICIOUS_VERSION_SHA; I can't work out
       | an exact exploit but it seems wrong to only process the SHA.)
        
         | da-x wrote:
         | I'll try to clarify. The `<tag>-g<hash>` string is _not_ the
         | name of the tag, it's a string emitted by `git describe` based
         | on the existence of `<tag>` in the history.
         | 
         | Of course I'm not suggesting that tag names should include the
         | hash. The existence of the tag `v4.11-rc7` allows other commits
         | to have nicer derived names.
         | 
         | EDIT:
         | 
         | Also to your last inquiry, it may be indeed a surprise that Git
         | resolves `<anystring>-g<githash>` to `<githash>`, but some may
         | argue it's a feature, not a bug :)
        
           | WorldMaker wrote:
           | Also, git describe's output is not just <tag>-g<hash>, it is
           | <tag>-<commitcount>-g<hash>. That number of commits since the
           | tag counter can be handy in its own way (such as it is
           | sometimes handy when trying to figure out which branch the
           | <hash> is most likely from).
           | 
           | I'm curious if the git resolution algorithm that accepts
           | describe strings also verifies/checks the commit count
           | matches. Glancing at the documentation [1] it says that it
           | specifically matches describe output strings (the docs call
           | it <describeOutput>) and not _just_ "<anything>-g<hash>", so
           | it may actually check the tag existence and verify the commit
           | count.
           | 
           | [1] https://git-scm.com/docs/gitrevisions
        
       | captn3m0 wrote:
       | Another important reason to use annotated tags: Tags without
       | annotation are just a reference to a commit and cannot contain
       | any metadata, such as the release date or the release authorship
       | information.
        
         | globular-toast wrote:
         | Lightweight tags are not supposed to be pushed. They are meant
         | to be used to help with scripts and stuff. It's a great example
         | of weird git UI that lightweight tags are the default when 99%
         | of of users would never want to create one.
        
       | unholiness wrote:
       | Our company uses a tag, quite improperly, to indicate our "last
       | successful build" on each branch, so our local builds can pull
       | e.g. compiled .o files from that build and speed up the local
       | build process. A tag is improper since it needs to move with
       | every successful build. All relevant commands need --force to
       | update it and it can rarely create headaches when machines
       | disagree.
       | 
       | I believe the right thing functionally is a child branch, updated
       | automatically with either a merge or a force-push on new
       | successful builds. But it feels not quite right conceptually, and
       | it's harder to explain to new developers (who can already get
       | overwhelmed with handling multiple branches). Is there a non-
       | branch solution for having a moving unique label?
        
         | avar wrote:
         | Don't clobber the branch or tag, instead create dated tags with
         | a name like built-YYYYMMDD-HHMMSS. Then have downstream systems
         | pick whatever the latest tag is.
         | 
         | You _can_ force push it, but why not keep a trail of what your
         | past state was?
        
       | azundo wrote:
       | > The commit that changed the version in source is the one to be
       | tagged
       | 
       | I've never understood this practice of not immediately bumping
       | the version in source after a release. We update the version in
       | source to the next logical version (usually a patch bump with an
       | alpha0 pre-release tag) immediately after tagging and publishing
       | a release. This way you just need to look at the version in
       | source to understand where you're at in a release process, and
       | only a single commit has the full release version in source,
       | which is also tagged as such. This doesn't seem to be a common
       | pattern, so shat are the downsides of this approach? Am I missing
       | something?
        
         | epage wrote:
         | This is what I was used to at a prior day job (another one just
         | didn't keep a version in source). When I took over cargo-
         | release [0], this was also the default though not anymore.
         | 
         | Benefits of keeping the last version
         | 
         | - Easier to detect when a change occurred since the last
         | release
         | 
         | - (Rust specific) It makes it harder to patch a registry
         | dependency with a git dependency because the versions will
         | never align [1]. This is why the default changed in cargo-
         | release.
         | 
         | - The "next version" is just speculation. Will the next release
         | bump the pre-release version, patch, minor, or major?
         | 
         | A downside to keeping either last version or next version is if
         | someone builds from master, the bug report could be confusing
         | unless you include the git hash (and whether the repo was
         | dirty).
         | 
         | I've seen some advocate for not keeping a version in source at
         | all [2]. This article advocates against it but doesn't give the
         | reason. I guess one is it requires you to have all tags locally
         | which is a silent failure mode when you don't and disallows
         | shallow clones.
         | 
         | [0] https://github.com/crate-ci/cargo-release
         | 
         | [1] https://github.com/crate-ci/cargo-release/issues/82
         | 
         | [2] https://github.com/rust-lang/cargo/issues/6583
        
         | js2 wrote:
         | My repos have a `make_release {bump | tag}` script for this
         | purpose. The repos have both a develop and a main branch. The
         | develop branch always has the next version with a `-dev`
         | suffix. e.g. `4.6.1-dev`.
         | 
         | Then, running `make_release tag`:
         | 
         | 1. Sets the version to `4.6.1`, commits it, tags it.
         | 
         | 2. Sets the version to `4.6.2-dev`, commits it.
         | 
         | I then merge the 4.6.1 tag to main and push main, develop, and
         | the 4.6.1 tag to the repo.
         | 
         | If development calls for a minor or major release, that's what
         | the `bump` option is for. It prompts for which part of the
         | version string should be incremented and creates a commit doing
         | just that.
         | 
         | Setting the version usually involves touching a couple of
         | files. e.g. a source code constant and a podspec or some other
         | metadata file. The script really helps preventing any mistakes.
        
         | Symbiote wrote:
         | This is more-or-less the default pattern with Java, completely
         | automated by Maven.
        
         | blibble wrote:
         | maven's release plugin used to do this (and I think still does)
         | 
         | it was a never ending source of merge conflicts
         | 
         | branching also causes problems (and mistakes)
         | 
         | vs. the build process deciding (possibly with some human input)
         | what the tree's version is, then tagging it
        
         | globular-toast wrote:
         | Why have the version in the source code at all? A
         | build/packaging process should query git at build/packaging
         | time to get the version (using git describe) and produce
         | versioned artifacts (which can be source code or binaries).
        
           | grumbel wrote:
           | Sometimes people don't use git to get the source. The
           | "Download ZIP" function on Github for example includes no
           | version number at all as far as I can tell, only the branch
           | name in the filename.
           | 
           | For GitLab there there exist a workaround by using
           | .gitattributes to create a VERSION file with
           | "$Format:%(describe:tags)$" which will get expanded to the
           | git describe string on archive creation, so the version
           | number even survives in a .zip. GitHub however ignores
           | .gitattributes and so far I haven't seen another way to get a
           | version number into the .zip other than just including it in
           | the source.
        
             | glacials wrote:
             | I'm fine with this sacrifice -- if someone downloads HEAD,
             | they are not running a "version", so it would be misleading
             | to have the source think it's at 1.0 or 1.1 if really it's
             | at a commit between those two versions. I have my build
             | scripts call the version "develop" or similar if this is
             | the case.
        
             | DANK_YACHT wrote:
             | Not related to your broader point, but you can download
             | source from a release via
             | https://github.com/<org>/<repo>/archive/refs/tags/<tag>.zip
        
               | grumbel wrote:
               | This works too:
               | https://github.com/<org>/<repo>/archive/HEAD.zip
               | 
               | Gives a HEAD.zip that contains a "<repo>-<sha1>/" folder
               | and the latest source.
        
         | da-x wrote:
         | OP here. I agree with this as well, you are not missing
         | anything. I was meaning to write another post about a similar
         | approach.
         | 
         | To illustrate to readers, let's say we are developing over a
         | tag `v1.2-pre` in `main`, and now the code has stabilized. The
         | idea is to release a tagged `v1.2`, then in the `main` branch
         | immediately release a tagged `v1.3-pre` that follows it.
         | 
         | We obtain the following:
         | 
         | 1) `main` branch commits look like `v1.3-pre-<number>-g<hash>`
         | in `git-describe`.
         | 
         | 2) A maintenance branch may have been forked from the commit
         | that was tagged with `v1.2`, so for the commits in that
         | `release/1.2` branch you automatically get
         | `v1.2-<number>-g<hash>` as output of `git describe`.
        
         | Hackbraten wrote:
         | I feel that both variants have their merits.
         | 
         | One possible downside (of version bumping immediately after
         | release) is that if you use the major/minor/patch scheme, you
         | can never be sure that you're actually bumping the correct
         | number. The upcoming release may be a major, a minor, or a
         | patch release, and it might take you a while until you know
         | enough about completed work items so you can make up your mind
         | which one it's going to be.
        
           | tjoff wrote:
           | Not sure why that is a downside? Once you realize you need to
           | update minor or whatever then update it, done. As a bonus you
           | have great visibility into why and when the minor needed to
           | update.
        
             | OJFord wrote:
             | One complexity with that that comes to mind is how do you
             | then track whether you've already bumped minor/major since
             | last release and so don't need to again? Maybe it's rare
             | enough and quick enough to manually check that that's not a
             | problem, depends on the project I suppose.
        
               | [deleted]
        
               | CameronNemo wrote:
               | As opposed to having to track later when you bump the
               | version whether you need a patch/minor/major bump?
               | 
               | Seems like any way you do it you have to keep track. IMO
               | version numbers are slightly easier to keep track of than
               | breaking changes. Changelogs are not always structured.
        
               | Vinnl wrote:
               | If you do it at the time of bumping, you can just look at
               | the then-current version number to decide what new
               | version a major, minor or patch release will result in.
               | If you do it the moment you introduce e.g. a breaking
               | change, you'll have to check whether this is the first
               | breaking change since the last release (i.e. keep the set
               | version number as-is), or if the one that it's currently
               | set to is what it should be.
               | 
               | Minor annoyance, but still.
        
               | CameronNemo wrote:
               | Yes, but what I am saying it is way easier to go back and
               | look at what the most recent release was (most SCMs have
               | a page for all the tags, and there is git tag), but it is
               | harder to go back and figure out if you had breaking
               | changes (would need to use conventional commits or
               | similar, then parse the gitlog or structured changelog).
        
               | Vinnl wrote:
               | You'll have to figure out if you had breaking changes
               | regardless of when you change the version number, no? My
               | strategy there is to add to the 'unreleased' section of
               | the changelog as they are introduced.
        
               | OJFord wrote:
               | GP's point (I think I agree now, I was only saying it was
               | a potential issue) being that that's a lot easier to do
               | at the time - you know you've just made a breaking change
               | (or you should do; as much/more than you ever will) so
               | that's the easiest time to bump the version
               | appropriately.
               | 
               | An alternative model I suppose would immediately have
               | major bump, minor bump, and patch bump branches; then you
               | just commit to the appropriate one, and I suppose keep
               | major rebased on minor rebased on patch. (And master =
               | major I suppose.)
        
               | azundo wrote:
               | We do this by looking at the patch version. For example,
               | current version in source is 2.2.1-alpha0. This means the
               | last bump was a patch version from 2.2.0 to 2.2.1, so if
               | you want a minor bump, then you need to bump to
               | 2.3.0-alpha0. Now that the patch version is 0, it's that
               | someone has already bumped the minor version since the
               | last release, so no need to do so again. This would break
               | down if someone bumps to 2.3.1-alpha0 unnecessarily but
               | otherwise it's immediately obvious looking at the current
               | version in source whether someone has already bumped the
               | minor version.
        
               | anonymous_sorry wrote:
               | > One complexity with that that comes to mind is how do
               | you then track whether you've already bumped minor/major
               | since last release and so don't need to again?
               | 
               | I wonder if it really matters that release version
               | numbers only increment by one. If not, just bump anyway
               | when appropriate change is made - no need to check.
               | 
               | In practice I think the problems would be a) having to be
               | very disciplined about this on every commit, rather than
               | having a reminder to consider it as part of a release
               | process, and b) ordering version numbers from different
               | branches when merging to mainline
        
               | OJFord wrote:
               | True, that'd be a decent way to approach it. Though at
               | that point perhaps you may as well actually release all
               | the versions too.
               | 
               | Or you could bump every time as you describe, but on
               | every major/minor bump make sure the parent commit is
               | released first (which would be >=1 commits since the last
               | one and have at least a patch bump). And I suppose you'd
               | never need to bump patch if that was just a lone thing
               | that happened post-release in prep for the next.
        
         | moron4hire wrote:
         | I thought about doing that, but I realized that I have no way
         | of knowing how pervasive my next set of changes will be,
         | whether it will warrant a patch or a minor version bump. The
         | major ones are much easier to predict; I don't go into them
         | without prior planning. But sometimes, what I think are going
         | to be bug fixes turn out to be API changes. So is 2.19.3 going
         | to go to 2.19.4 or 2.20.0 next week? No clue.
        
           | azundo wrote:
           | Bump it in source to 2.19.4 immediately, and then to 2.20.0
           | as soon as there is code that requires a minor version bump,
           | then to 3.0.0 as soon as a major version bump is required.
           | Then your version in source is always semver compatible vs by
           | definition being off until you cut the next release as soon
           | as you introduce a breaking change.
        
       | Groxx wrote:
       | > _Tag push permissions_
       | 
       | Agreed entirely on restricting tags (they tend to have meaning
       | and expected semantics outside the repo, which makes them risky),
       | but also! Teach people more about git, or unix CLI patterns in
       | general. `git log test` is ambiguous about it being a tag (or
       | branch) or file/folder, but `git log -- test` is not. `--` as an
       | ambiguity-preventer is a very common pattern, it works in many,
       | many CLI tools.
       | 
       | ---
       | 
       | One that hasn't been included here: don't rely on tags for any
       | kind of business logic, if you have literally any way to avoid
       | it.
       | 
       | Tags are _mutable_ , and do not have a history of when you
       | changed them. They're plenty handy for "human, enter a thing to
       | use" purposes, but you should _immediately_ turn that into a
       | commit sha and then only use that commit sha.
        
         | WorldMaker wrote:
         | Annotated tags can be signed and you can check tag signatures
         | in addition to commit hashes. Admittedly, if you don't trust
         | the remote repo you don't trust the remote repo even with
         | signatures.
         | 
         | Similarly, many repository hosts can help you setup tag
         | protection as a part of branch protection tools, but while that
         | helps with your own repos it doesn't generally help with remote
         | repos.
        
           | Groxx wrote:
           | Checking signatures will tell you that X created tag Y, but
           | tells you nothing about "Y has not changed", regardless of
           | how Y has changed. But yeah, I wish more setups would sign
           | things.
           | 
           | And agreed, tag protection rules do exist and are _fairly_
           | common. Though by far the majority I run across do not
           | protect tags or branches _by default_. And even if they do,
           | external systems may or may not honor changes - that 's why
           | dependency management lock-files exist, to detect changes
           | like this where the "name" (i.e. tagged version) stayed the
           | same but the content changed.
           | 
           | Or in a different flavor, you have Go modules, where you
           | cannot ever remove or mutate a tagged version in the main
           | proxy... but you can change it in github, and now your web-
           | UI-visible code differs from what people download. Which may
           | be worse, because while go.sum will store the _go module
           | checksums_ and can complain if you pull the wrong _contents_
           | , that checksum doesn't match the sha it pulled. If you have
           | a semver-compatible tag, the git sha isn't stored anywhere,
           | you just have `require thing v1.2.3` and the go module
           | content hashes. Trying to "recover" the sha from this can be
           | rather painful, as you essentially have to check the module
           | checksum for all shas in a repo... assuming it even still
           | exists.
        
             | WorldMaker wrote:
             | Yeah, at the end of the day it is all about trust. If you
             | trust that X creates tags that don't change, that signature
             | is a trust document.
             | 
             | Right, as with many things in computing often you want
             | "trust, but verify". Trust a good tag by a good author not
             | to change, but also go ahead and store a hash in a lockfile
             | and verify it, just in case.
        
         | avgcorrection wrote:
         | Wait. Aren't tags immutable?
        
           | colonwqbang wrote:
           | Nope. A tag is just a file in the .git directory. Delete the
           | file, delete the tag.
           | 
           | $ rm .git/refs/tags/v1.0
           | 
           | or in git syntax
           | 
           | $ git tag -d v1.0
           | 
           | For this reason one should never use tags when pulling code
           | from untrusted repos, instead use the SHA1 hash which is much
           | harder to forge.
        
             | avgcorrection wrote:
             | Um okay.
             | 
             | That is not what anyone means when they talk about
             | immutable objects or refs in Git. I can delete a commit
             | from `.git` but they are still considered immutable.
             | 
             | A branch is mutable since you can push it forward without
             | `--force` as long as the current commit becomes an ancestor
             | of the next tip. Can you change a remote tag without
             | `--force`? I don't know off the top of my head. But I doubt
             | it.
        
               | TobTobXX wrote:
               | Checked it for you:                   To /tmp/foo1/
               | ! [rejected]        ddd -> ddd (already exists)
               | error: failed to push some refs to '/tmp/foo1/'
               | hint: Updates were rejected because the tag already
               | exists in the remote.
               | 
               | I think any ref is 'mutable' by these standards, and any
               | hash is immutable (that's the entire point of a hash).
        
               | Groxx wrote:
               | You can delete a random commit, but if anyone references
               | it transitively, it'll immediately detect that breakage.
               | E.g. you can't modify or remove commits "in the past" on
               | your main branch. And "moving" a commit creates _a new
               | commit_ , with a different sha.
               | 
               | Tags do not do that. Moving or removing a tag retains no
               | history about the move, nor is the old value left hanging
               | around somewhere (after pruning), nor will anyone who had
               | not yet pulled the tag notice the removal, as with
               | branches. _Most_ configs will complain about the tag
               | disappearing or moving, as with branches, but that
               | depends on your config and the command you ran.
               | 
               | (annotated tags _do_ have their own sha and creation date
               | and whatnot, which is great, but next to nothing
               | references them. and removing them from a commit leaves
               | no evidence that it ever was on that commit, as the
               | commit is unmodified)
        
               | avgcorrection wrote:
               | > Most configs will complain about the tag disappearing
               | or moving, as with branches, but that depends on your
               | config and the command you ran.
               | 
               | Needing `--force` with a default setup is literally all I
               | meant by "immutable", apparently a cursed word in this
               | context (the tip of a branch is supposed to be able to
               | move in that common-history sense of movement). Gawd.
        
               | colonwqbang wrote:
               | If you don't know, maybe you could give me the benefit of
               | the doubt?
               | 
               | In any case, the syntax to delete a remote tag is just
               | e.g.
               | 
               | $ git push origin :mytag
               | 
               | After that you are free to push a new tag to replace the
               | old, with no warnings or --force flags.
               | 
               | When we use a tag to specify the version of a dependency,
               | we trust the maintainer not to do this. If we don't have
               | that level of trust we can use SHA1's instead. We should
               | not pretend that there is anything in git that tries to
               | prevent a tag from being rewritten by someone who wants
               | to.
        
               | [deleted]
        
           | lamontcg wrote:
           | annotated tags are immutable, they're part of the history
           | that makes up the sha.
           | 
           | normal tags are just labels that can be changed or deleted at
           | will. removing a normal/lightweight tag from history doesn't
           | change any commits and doesn't require a force push.
           | 
           | that is why the article recommends annotated tags. you have
           | to force push to rewrite those. then the git describe tags
           | based on those will be immutable unless someone goes out of
           | their way to rewrite history.
        
             | [deleted]
        
             | Groxx wrote:
             | Annotated tags affect _their own_ sha (because they
             | actually have one), but they don 't affect the commit you
             | tag in any way. Otherwise you wouldn't be able to add them
             | at a later date - it'd change that commit's sha.
             | 
             | If you move a tag, annotated or no, the .git/refs/tags/x
             | file will contain the new sha it points to... but the
             | _history_ of that `x` is not stored anywhere. The fact that
             | it used to point to [old sha] is gone for good, and the old
             | sha will eventually get garbage collected (via pruning),
             | just like if you remove a branch.
        
               | lamontcg wrote:
               | Oh that's weird, I always thought the next commit would
               | have its gitsha affected by the annotated tag and you
               | couldn't just delete it, but I just tried that and it
               | deleted fine...
               | 
               | Now I'm not sure what the point is of annotated tags
               | other than the default git describe behavior.
               | 
               | Everyone else can enjoy Cunningham's Law working like a
               | charm.
        
         | tome wrote:
         | Yeah, the "tag called test" example is strange, given that
         | "branch called test" has exactly the same problem.
        
           | da-x wrote:
           | Yes, but a pushed tag `test` pollutes a local `test` name for
           | everybody, whereas `test` as a local branch is just a local
           | branch. A remote `test` branch is only referred via `<remote-
           | name>/test` from a local POV.
        
             | avgcorrection wrote:
             | It only pollutes the local repo if you pull it explicitly
             | (or with `--all`) from the remote.
        
               | da-x wrote:
               | IIRC, in `git fetch` if a tag is contained in one of the
               | updated branches, you get it automatically without
               | specifying an additional flag. What you say is true for
               | 'orphaned tags' which no remote branch contains.
        
       | CamelRocketFish wrote:
       | The author suggest prepending v to a tag name for the sake of
       | shell completion. This assumes that everyone will be interacting
       | with git tags using a shell. I disagree that a v should be
       | prepended for that reason.
        
         | Tyr42 wrote:
         | GitHub let's you do a partial filter. So it helps there too.
        
         | silverwind wrote:
         | Yes, this practice of adding a v is common, yet pointless. I
         | prefer my git tags to be valid semvers.
        
           | wnoise wrote:
           | This works iff you never want to tag anything but semvers.
           | This is an unusual and strict policy. As soon as you want the
           | ability to move outside this you need something like
           | namespacing. Prepending v is a pretty lightweight way to do
           | this.
        
         | inetknght wrote:
         | > _The author suggest prepending v to a tag name for the sake
         | of shell completion. This assumes that everyone will be
         | interacting with git tags using a shell._
         | 
         | I would argue that any tool (such as a GUI app) which doesn't
         | provide the depth of usability that a shell offers is, in fact,
         | a deficiency in the GUI or tool. If your GUI doesn't offer
         | completions similar to a shell then your GUI tool is inferior
         | to a shell.
        
           | V-2 wrote:
           | This is usually the case. GUIs are superior in some aspects
           | (eg. visualization of complex branch structures), inferior in
           | others (eg. add more friction - many operations can be done
           | faster from the console). As far as Git goes, I see GUI
           | clients as complementary rather than full-scale substitute
           | for the terminal.
        
       | ed25519FUUU wrote:
       | I don't like semantic versioning because it's difficult to sort.
       | E.g. 1.2 is more than 1.100, and that doesn't even include fix
       | versions.
       | 
       | Has there been a better proposal to semantic tags which makes
       | organizing and sorting them easier?
        
         | paol wrote:
         | Semantic versions can't be sorted trivially, it's true. For
         | projects that benefit from the "semantic" part of semantic
         | versioning the benefits outweigh that minor inconvenience. But
         | not everything needs semantic versioning.
        
         | ssully wrote:
         | It's not semantic versioning, but a project I am on started
         | using calendar versioning[1]. I was hesitant to try it at
         | first, but it's kind of growing on me. It's really easy to
         | organize and sort. We have a pretty regular release cadence so
         | it was easy to implement, but I could see it being more
         | complicated for certain projects.
         | 
         | [1]: https://calver.org/
        
           | klysm wrote:
           | I think calver is good for things that don't really have an
           | API per se. Stuff like GUI applications.
        
         | jlokier wrote:
         | In fact there are standard-ish sorting methods that work fine
         | with mixed text and version numbers such that 1.100 is more
         | than 1.2, and v1.100 is more than v1.2.
         | 
         | Git has built in version sort, though it's not the default.
         | GitHub shows tags in version sort order in the drop-down tag
         | selection.
         | 
         | Here are some commands that use that sort order:
         | ls -v                               # Linux only.            ls
         | | sort -V                        # Most modern OSes.
         | git tag | sort -V                   # Git tags in version
         | order.            git tag --sort=v:refname            # Same as
         | above, it's a git builtin.            git config tag.sort
         | v:refname       # Set default sort order for `git tag`.
         | git config versionsort.suffix -pre  # Make 1.2-pre1 sort before
         | 1.2.            git config versionsort.suffix -rc   # Now
         | 1.2-pre1 < 1.2-rc1 < 1.2 < 1.2-patch1
        
           | teddyh wrote:
           | There's even a GNU C Library function: strverscmp():
           | 
           | https://www.gnu.org/software/libc/manual/html_node/String_00.
           | ..
        
             | blibble wrote:
             | git directly uses that function :)
             | 
             | (... well, it's copied and pasted into the tree but close
             | enough)
        
         | pizza234 wrote:
         | Sort in which context? Unix has `sort -V`. Of the other
         | languages I use, they all have either built-in functionalities,
         | or small libraries to do it.
        
         | __david__ wrote:
         | Ancient Perl started off encoding their version numbers as a
         | float (you can see it in their old version numbers before
         | 5.6.0, 5.005 for instance[1]). Integers were the major version,
         | thousandths were minor, and millionths were patch. So 1.003004
         | was 1.3.4. This makes them extremely easy to sort. At some
         | point they decided that was too obscure and came up with
         | "version strings". But the old way is (crazily) still supported
         | and can cause pain if you forget since 1.2.0 is unambiguous,
         | but 1.2 means 1.200000, ie 1.200.0.
         | 
         | [1] https://en.wikipedia.org/wiki/Perl_5_version_history
         | 
         | Just to be clear, I don't actually think this is a great
         | solution, but is _is_ easy to sort.
        
           | avar wrote:
           | Perl still represents versions like that internally, the
           | "pretty" v-prefixed variant is just some overload/OO magic.
        
         | infogulch wrote:
         | That's a good point. With the omnipresence of semver, you'd
         | think that more interfaces would support sorting tags with a
         | semver comparison instead of lexically.
         | 
         | I searched for a bit and found that github does this ( e.g.
         | https://github.com/TryGhost/Ghost/tags ). Also there is a git
         | config that sets this globally for the git cli: `git config
         | --global tag.sort version:refname` ( found here:
         | https://gist.github.com/loisaidasam/b1e6879f3deb495c22cc ).
         | That's another git config going onto every machine I use...
        
       | CalChris wrote:
       | How do I pull something like _LLVM 14.0.0_ by tag?
        
         | abdusco wrote:
         | git clone -b $tag $repo_url
        
           | junon wrote:
           | Or the longer way, if you're on a version of git without the
           | -b option:                   git init ./repo         cd
           | ./repo         git remote add origin $uri         git fetch
           | origin $tag         git checkout FETCH_HEAD
        
             | carlhjerpe wrote:
             | Or what you would probably do using libgit(2?) :)
        
         | AceJohnny2 wrote:
         | Couple ways, for example on the github page [1] , click the
         | "tags" link [2] above the file list, find the tag for release
         | 14.0.0, which is _llvmorg-14.0.0_ , and now you can do any
         | commit operation with that name instead of a commit.
         | 
         | If you have a checkout, you can find the tag name by doing a
         | _git tag -l *14*_ and looking through the output; I 'm
         | intentionally providing a very general glob because one may not
         | know beforehand their exact tag name conventions.
         | 
         | [1] https://github.com/llvm/llvm-project
         | 
         | [2] https://github.com/llvm/llvm-project/tags
        
       | junon wrote:
       | Please don't prefix version tags with "v"...
        
         | recursive wrote:
         | I'm not stopping until you give me a reason. So far it hasn't
         | caused any problems.
        
           | foobarbaz33 wrote:
           | I guess it contributes to global warming. Not in a
           | significant way. But it literally does take more energy to
           | write an extra character to a disk.
        
         | patmorgan23 wrote:
         | Why?
        
       | nickjj wrote:
       | Annotated tags are a good idea, they also happen to be a
       | requirement when signing tags. You can create empty message
       | signed annotated tags with `git tag -sm "" v123-my-signed-tag`.
       | 
       | I once made a blog post / video around signing and verifying your
       | commits and tags at: https://nickjanetakis.com/blog/signing-and-
       | verifying-git-com...
        
       ___________________________________________________________________
       (page generated 2022-05-23 23:00 UTC)