Two scripts can disagree about the same “latest” release

A deployment script that sorts tags alphabetically can choose v2.9 over v2.10, even though v2.10 is the higher version. A script that chooses the most recent tag date can make a different mistake: selecting a backfilled tag that is newer by date but older by version.

That disagreement is not a Git bug. It comes from using one word for two different questions: “Which tag names the greatest version?” and “Which tag has the newest associated date?” A third question—“Which release did we publish most recently on GitHub?”—may have a different answer again.

The practical choice depends on the job. A deployment that promises users the highest stable version usually needs version ordering. A report about recent tagging activity may need a date. A release dashboard may need the hosting platform’s publication data, not either Git sort.

Make that choice before adding a command to CI. Otherwise, a script can run successfully, return a plausible tag, and still deploy the wrong build.

Version order and date order measure different things

Git can sort tag names using version-aware ordering. For names such as v2.9 and v2.10, this treats the numeric components as numbers rather than comparing the strings character by character. The result is useful for version-like names, but it does not decide which tags your project considers valid releases.

Date ordering instead ranks tags by a date field. Annotated tags contain tagger information, including a tagger date. Lightweight tags have no separate tag object or tagger date; their associated creator date comes from the commit they point to. So a date-based sort of mixed tag types is not a consistent record of when each tag was created.

And neither date necessarily means “published on our release page.” A tag identifies a point in Git history; a hosting platform’s release record has its own publication information. If the task asks when an announcement went live, use that platform’s release data.

Choose the date field that matches the question

For a quick view of tags by creator date, use:

git tag --list --sort=-creatordate

The minus sign sorts in descending order, so the first line is the greatest creator date Git can use for each tag. For an annotated tag, that is the tagger date; for a lightweight tag, it is the target commit’s date. Read the result as “newest available creator date,” not as a guaranteed timestamp for when somebody created or published a tag.

Annotated tags make the distinction visible: git show v1.2 can show the tagger date and, separately, the date of the commit the tag refers to. Those dates can differ. A tagger date is useful metadata for activity reports, but it can be set independently of the commit date and should not silently define which release is newest.

If the actual question is “Which commit has the newest committer date?”, say that explicitly and sort by commit date instead. If it is “Which release did GitHub publish last?”, query the GitHub release records. Matching the field to the question is more important than choosing a clever sort flag.

Sort version-like tag names with Git

For a local repository whose tags follow a version-like naming scheme, list them in descending version order with:

git tag --list --sort=-version:refname

To print just the first candidate:

git tag --list --sort=-version:refname | sed -n '1p'

For example, with tags v2.9 and v2.10, version-aware ordering puts v2.10 first. Plain alphabetical sorting does not give the same guarantee. Git’s version sort is handy for naturally numbered names, but it is not a complete release policy: the project still has to define prefixes, prereleases, product lines, and which tags are eligible.

That last point matters for names outside a simple numeric pattern. A project may use date-based names, custom suffixes, or multiple release tracks. Don’t assume the sort output means “latest stable product release” until you have checked that the names and eligibility rules match your convention.

Filter stable candidates before choosing a deployment target

Suppose a deployment should use the greatest stable tag in the format vMAJOR.MINOR or vMAJOR.MINOR.PATCH. The following sample policy accepts those names and excludes prerelease suffixes such as -rc1:

latest=$(
  git tag --list --sort=-version:refname |
  grep -E '^v[0-9]+\.[0-9]+(\.[0-9]+)?$' |
  sed -n '1p'
)
printf '%s\n' "$latest"

Given these tags:

  • v2.9 matches the stable-name filter.
  • v2.10 matches the stable-name filter.
  • v2.11-rc1 does not match because it has a prerelease suffix.

After sorting and filtering, the eligible list starts with v2.10, so that is the deployment target. This is a sample policy, not a universal definition of stable: if your project uses a v2.10.0 format, a different prefix, or another prerelease convention, change the expression to match the names you actually release.

For remote tags, fetch before sorting locally

git tag --list works on tags available in the local repository. If a script needs current tags from origin, fetch them first:

git fetch --tags origin
git tag --list --sort=-version:refname

Then apply the stable-tag filter used by your deployment policy. This gives the sort access to local tag objects and avoids treating a remote listing as though it contained all the date information in the repository.

git ls-remote --tags is useful when you need to inspect which tag refs a remote advertises, but it is not a substitute for fetching objects when you need local date fields. Annotated tags can also appear with a peeled commit line ending in ^{}; naïve text processing can mistake that extra line for another tag. For release selection, fetch and use Git’s tag-listing and sorting commands rather than selecting a line from raw remote output.

Use this decision guide for the task at hand

Pick the command based on the outcome you need, not on which interpretation sounds most like “latest.” The table separates version ranking, Git dates, and hosting-platform publication time.

Task Meaning of “latest” Use Main caveat
Deploy the highest stable release Greatest eligible version Version sort, then a project-specific stable-tag filter Git cannot infer your prerelease or product-line policy.
Review recent tagging activity Newest available tag creator date git tag --sort=-creatordate Lightweight tags use the target commit’s date, not a tagger date.
Find the newest commit among tagged releases Newest commit date Sort using the commit-date field The newest commit is not necessarily the greatest release version.
Show the most recently published hosted release Latest platform publication Query the hosting platform’s release records A Git tag alone does not establish a platform publication time.

A useful rule is to include the noun in the requirement: “highest stable version,” “newest tagger date,” or “most recently published release.” That small change makes reviews easier because the implementation can be checked against a specific result.

Test the policy against awkward tag names

Before using the result in a release job, test it with tags that could expose a policy mistake. Include a two-digit minor version such as v2.10, a prerelease such as v2.11-rc1, and any patch-level or alternate-prefix form your repository actually uses. Confirm the selected output, not merely that the command exits successfully.

Also decide what to do when the filter returns no match. An empty variable should stop a deployment rather than flow into a later command as though it were a valid tag. For a release script, check that latest is non-empty and fail with a clear message if no eligible tag exists.

Finally, keep the policy close to the script that uses it. If one job includes release candidates while another excludes them, they should not both call a helper named latest_tag and imply identical behavior. A name such as latest_stable_tag records the rule and reduces surprises when someone changes the filter later.

Make the choice explicit in your next release script

This week, find the command in CI, a deployment script, or a release dashboard that currently chooses a “latest” tag. Write down whether it needs the highest stable version, the newest Git date, or the most recently published hosted release. Then use the matching method from the guide and test it against your repository’s real tag patterns.

For a stable deployment, the worked example gives you a starting point: sort version-like names, filter to the exact stable naming convention, and verify that v2.10 wins over v2.9 while v2.11-rc1 is excluded. For a date-based report, use creator-date ordering with the lightweight-tag caveat in mind. The important fix is not a more elaborate command; it is making “latest” a rule your team can state and test.