#1938·changesets

`changeset publish --no-git-tag` still creates git tags for private packages

Author: MaximusHaximusCreated Apr 22, 2026Updated Aug 20, 2026

Hello folks!! Thanks for the awesome toolset -- this is by far my favorite path for managing releases . I think I found a bug that I'd love to see fixed; I've worked around it with some hacky manual scripting but I think the functionality is just broken in its current form so thought I'd raise an issue here for feedback and hopefully resolution :).

Summary

The --no-git-tag flag on changeset publish is advertised as suppressing git tag creation during publish, but it only suppresses tags for packages that were published to npm. When config.privatePackages.tag: true, publish continues to create annotated git tags for untagged private packages even when --no-git-tag is passed. The flag is non-functional for any repo that uses the built-in private-package tagging feature.

Reproduction

Monorepo config:

jsonc
// .changeset/config.json
{
  "privatePackages": { "version": true, "tag": true }
  // ...
}

A single private package at 0.1.0, add a minor changeset, changeset version, then:

$ changeset publish --no-git-tag
  No unpublished projects to publish
  success found untagged projects:
  [email protected]
$ git tag -l
[email protected]

Expected: no tags created. Actual: [email protected] is created locally as an annotated tag.

Root cause

In packages/cli/src/commands/publish/index.ts at v2.31.0 (unchanged on main):

typescript
export default async function publish(
  cwd,
  { otp, tag, gitTag = true }: { ...; gitTag?: boolean },
  config
) {
  // ...
  const tagPrivatePackages = config.privatePackages && config.privatePackages.tag;
  // ...
  if (gitTag) {
    await tagPublish(tool, successfulNpmPublishes, cwd);   // public: guarded
  }
  // ...
  if (untaggedPrivatePackageReleases.length > 0) {
    await tagPublish(tool, untaggedPrivatePackageReleases, cwd);   // private: NOT guarded
  }
}

The gitTag parameter (set to false by --no-git-tag) guards the public-package call but not the private-package call. Git-blame shows the private branch landed in #662 (Oct 2022), after --no-git-tag was added in #701 (Dec 2021); the new code path simply didn't honor the existing flag.

Proposed fix

diff
 if (untaggedPrivatePackageReleases.length > 0) {
   success("found untagged projects:");
   logReleases(untaggedPrivatePackageReleases);

-  await tagPublish(tool, untaggedPrivatePackageReleases, cwd);
+  if (gitTag) {
+    await tagPublish(tool, untaggedPrivatePackageReleases, cwd);
+  }
 }

Honors the CLI flag uniformly across both publish paths. Does not change anything about the privatePackages.tag config — that still controls whether private packages are considered for tagging at all; --no-git-tag becomes the per-invocation override it's already documented to be.

Breaking change analysis

This is a bug, not an intentional behavior AFAICT. Consider who is affected:

  • Users not passing --no-git-tag — no change. Both branches still tag; identical behavior.
  • Users passing --no-git-tag with only public packages — no change. The guarded branch was already respecting the flag.
  • Users passing --no-git-tag with privatePackages.tag: true — fewer tags created than today. This group is the only one affected, and they are already in an inconsistent state: they asked for no tags but got some anyway. No reasonable use case requires selectively tagging private packages while suppressing public ones via --no-git-tag; anyone who wants private tags created can either omit the flag or run changeset tag separately, which tags both public and private packages deterministically.
  • Users passing --no-git-tag with privatePackages.tag: false — no change. Private packages weren't eligible for tagging to begin with.

The asymmetry has no documented rationale, and the current behavior means --no-git-tag cannot be used as a reliable "never create tags in this step" primitive — which is exactly what users need when they drive tagging from a separate step (e.g. CD pipelines that tag only after a post-publish lockfile commit). Treat as a straightforward bug fix, not a breaking change.

Why this matters

Downstream CI that needs to stage publish and tag as separate commits (for reasons like post-publish lockfile updates, tag-triggered Docker builds, or signed-tag requirements) currently has to sweep local tags between publish and tag to avoid tags pointing at the wrong commit. A one-line fix removes that class of workaround entirely :)