Skip to content

prerelease-link.sh: link/unlink repins manifests inside nested git worktrees (not round-trip safe) #323

Description

@dmealing

Found while evaluating 0.24.0-rc.3 in an adopter project.

Summary

repin_npm() walks the consumer tree with:

find "$PROJECT" -name package.json -not -path '*/node_modules/*' -not -path '*/.git/*'

-not -path '*/.git/*' prunes paths inside a .git directory. But a linked git worktree's .git is a file (containing gitdir: …), so nothing under a nested worktree is pruned. link therefore repins tracked manifests belonging to a different checkout on a different branch.

Observed

The adopter keeps agent worktrees under .claude/worktrees/<name>/. Running:

prerelease-link.sh link --version 0.24.0-rc.3

reported:

repinned 7 @metaobjectsdev/* dependencies to 0.24.0-rc.3 across 4 manifest(s)

Only 2 of those manifests belonged to the project being linked. The other 2 were inside a nested worktree checked out on an unrelated branch that had its own uncommitted work in progress.

Why this is worse than a stray edit

That nested worktree was deliberately pinned to an older MetaObjects (0.19.0). unlink_all() calls the same walk (repin_npm "$TO"), and --to defaults to the current public latest. So the documented recovery:

prerelease-link.sh unlink --to 0.23.2

would have rewritten those manifests to 0.23.2a version they were never on. linkunlink is not round-trip safe across a nested worktree: it silently relocates an unrelated branch's dependency pin, and nothing records that it happened.

The .git/info/exclude protection does not help here: these are tracked files, and they belong to a different worktree.

Suggested fix

Constrain the walk to the worktree being linked. Roughly in order of robustness:

  1. Prune any directory other than $PROJECT that is itself a worktree root — i.e. contains a .git entry of either kind, file or directory.
  2. Ask git directly: git -C "$PROJECT" worktree list --porcelain, prune every returned path except $PROJECT.
  3. Enumerate candidates with git -C "$PROJECT" ls-files -- '*package.json', so only files tracked by this worktree are considered.

Option 1 additionally covers nested submodules and vendored unrelated repos, which the current find has the same blind spot for.

The nuget / python / maven paths do their own walking and globbing, so they are likely to share this and are worth checking together.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions