Skip to content

[finding] Any local pnpm run version rehearsal hangs forever in a shallow clone — getCommitsThatAddFiles deepens in a loop that never terminates without a reachable remote #9555

Description

@os-project-manager

Found while rehearsing #9518's fix. Cost roughly two and a half hours of wall clock across two attempts before it was diagnosed. Recording so the next rehearsal — #9500 is the scheduled one — does not pay it again.

What happens

changeset version resolves a commit for every consumed changeset to build changelog
links. @changesets/git's getCommitsThatAddFiles does this per path:

git log --diff-filter=A --max-count=1 --pretty=format:%H:%p .changeset/NAME.md

and then branches on whether the returned commit has a parent. A parentless commit is
treated as possibly being the boundary of a shallow clone rather than the real add, so:

  • if git rev-parse --is-shallow-repository is true, it calls deepenCloneBy({ by: 50 })
    (git fetch --deepen=50) and retries those paths;
  • only if the repo is NOT shallow does it accept them and break.

Why it never terminates here

The agent container's /home/user/objectstack is itself a shallow clone. Any clone
taken from it inherits .git/shallow, and every .changeset/*.md resolves to the shallow
boundary commit, which by definition has no parent. The rehearsal recipe also says to use a
throwaway clone with no push remote — so git fetch --deepen=50 has no remote to fetch
from, remaining never shrinks, and the do ... while (remaining.size) loop spins forever.

Observable symptom: changeset version never finishes, git status stays clean, and the
same changeset filenames scroll past repeatedly in ps. It looks like slow progress, not a
hang, which is what makes it expensive — the first attempt was allowed to run 70 minutes on
the assumption that O(packages x changesets) was simply large.

Two dead ends worth naming: deleting .git/shallow makes git fail outright
(cannot simplify commit ... because of ... on the missing parent), and re-adding a remote
to the shallow source cannot help either, because deepening from a shallow source still
lands on a parentless boundary.

What does work

Give every changeset an add-commit that has a parent, inside the throwaway clone, before
running the version pass. The net tree is unchanged (verified: identical tree hash):

git rm -r --cached --quiet .changeset
git commit -q -m "replay scaffold: untrack .changeset"
git add .changeset
git commit -q -m "replay scaffold: re-add .changeset"

A full resolution pass then takes 0.57s instead of never completing, and
pnpm run version runs end to end.

Not a production defect

cut-rc.yml checks out with actions/checkout@v7 and fetch-depth: 0, so the real cut has
full history and never enters this branch. This is a local rehearsal trap only — but
rehearsing in a throwaway clone is the prescribed verification route for the release
machinery (#9497, #9500 and #9518 all call for it), so it is on the path of every card that
touches this area.

Adjacent, different mechanism: #9408 covers a shallow objectui clone defeating
bump-objectui.sh's walkability guard.

Shape of a fix

Options, roughly in increasing cost: document the scaffold above wherever the throwaway-clone
rehearsal is prescribed; or provide it as a small script next to the other scripts/pm/
helpers; or have the rehearsal recipe detect git rev-parse --is-shallow-repository and
refuse with this explanation rather than hanging.


Generated by Claude Code

Metadata

Metadata

Assignees

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions