> 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?
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.
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`.
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.
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.
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.
> 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
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.
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.
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.
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.
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).
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.
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.)
Right, but what if that was the second breaking change you made? To figure out whether that's the case, i.e. whether you need to bump the major version or not, you'll have to check out the last released version.
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.
The downside is that you need a commit which serves no purpose other than to increment the number. The only content which is different is is the version number; you're effectively tagging the same software with an incremented version. Why tag the same thing with two different versions? You're only inviting merge conflicts with changes like this.
Unreleased software doesn't require a version number at all.
What you can do is incorporate some build identifier into local unreleased builds. It could be from the git sha, plus some indication of whether it was a dirty state or exactly that git sha.
The command does it:
git describe --tags --dirty
It will produce a string consisting of the most recent tag, the number of commits since that tag, a portion of the hash and the word "dirty", all separated from each other by a dash. If there are no uncommited changes, then -dirty is omitted, and if there are no commits since the tag (we are at the tagged commit), then the count of commits and hash are omitted.
You can incorporate this sort of identifying string at build time; nothing is checked in. If you build the tagged release in a clean repo, the string will just be the tag. Only if you make new commits and/or have uncommited changes do you get a different string without having to commit anything.
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.
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.
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).
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.
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.
I’m not sure what you mean by “bumping the version in source after a release”. If it’s after a release then you released it with the old version? Or do you mean prep the repo for the next version by incrementing it optimistically, in which case it probably depends on your branding model. But I don’t understand the “immediately” part of your question.
Where I work we don’t increment until we know what branch/version we are working on, and determine the semantic version, and then we still use tagging because we tag the version + build id which is computed at build time
My understanding is that initially they have version 1.4.2 and release 1.4.2. Then in the next commit, immediately after release, they bump the version to 1.4.3.alpha0 or something of that nature.
I personally think that's messy because it requires two changes to the version instead of one and there's no way of knowing that the next version will be 1.4.3. If a developer makes a breaking change, will they update the version in the code to 2.0.0.alpha0 or do they fix the version when they release?
I suppose if you have nightly releases then it could make sense but then I think using the date or the commit hash as the version number would make more sense.
> We update the version in source to the next logical version
That’s some impressive prognostication! I recently released a semver major for a minor corner case bug fix. I couldn’t have predicted this would be version 4 after over a year maintaining version 3, but it was a significant breaking change to fix a minor bug.
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.
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).
> 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.
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.
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.
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 ?
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.
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).
In practice that's a tiny bit more complicated to do it really well, with the version as a dependency in the build process. When developing, you don't want to trigger a full rebuild if the version number changes, but you do have rebuilds to do when it does.
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.
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.
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.
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).
It seems like what you describe is what golang supports: the folder structure in the tag name must match the folder structure in the repository. That's pretty cool.
Do people use a mono repo approach with different parts of the repo having different version numbers? The way I thought of it, the whole point of mono repo is not that.
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.
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.
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 module-compatible tagged version, 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.
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.
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.
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.
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.
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).
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.
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)
> 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.
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.
Annotated tags affect their own sha (because they actually have one, non-annotated tags do not), 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.
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.
I think it's at least partly for providing a storage location for after-commit release signatures. You can't modify the commit to add the release-signer signature, and many things will want direct access to it, so you need/want a publicly-identifiable commit-pointer of some kind: tags.
Off at the less-structured side of things though, have you seen git notes? You can add notes to any commit: https://git-scm.com/docs/git-notes . Tags are kinda just notes with a display name.
At times I've wished that code review feedback were just stored in notes, so it'd survive changing hosting systems... but they're not quite reasonable for that :| What I think I really want is a notes tree under any commit, which kinda exists since you can add notes to notes, but there isn't really enough structure or tooling to support that kind of use out-of-the-box.
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.
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.
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?
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?
Because the "downstream systems" you mention are just all local git commands. It is necessary to branch from these points, so creating branches and checking state requires them. So e.g. `git diff lsb/master` becomes `git diff lsb/master/built-YYYYMMDD-HHMMSS` or at least `git diff $(git merge-base master)`. That doesn't seem better than the occasional --force.
It's better because you might be looking at the output of that, and then your co-worker tries to do the same, except you're looking at different because you force-pushed it.
And you furthermore have no immutable record of what happened anymore.
You can always create one tag that you force-push to point to the "latest tag" to make it convenient for one-liners, but not force-push any of the "real" ones.
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`.
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.)
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 :)
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.
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
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.
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.
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.
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.
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.
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.
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.
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.
> 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.
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.
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.
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?