Skip to content

fix(core): report an unreadable repository instead of an empty diff - #57010

Open
feiiiiii5 wants to merge 2 commits into
anomalyco:devfrom
feiiiiii5:vcs-unreadable-repo
Open

feiiiiii5 wants to merge 2 commits into
anomalyco:devfrom
feiiiiii5:vcs-unreadable-repo

Conversation

@feiiiiii5

@feiiiiii5 feiiiiii5 commented Oct 19, 2026 •

Copy link
Copy Markdown

Issue for this PR

Closes #50934

Type of change

  • Bug fix

What does this PR do?

hasHead used git rev-parse --verify HEAD and treated every non-zero exit as "unborn branch". An unreadable repository (e.g. a linked worktree whose .git/worktrees/<name> admin directory was pruned) also exits 128 there, so both vcs.diff("working") and vcs.diff("committed") returned an empty diff — indistinguishable from a clean tree — before any listing ran.

This is the V2 (dev) port of #52544, which fixed the V1 tree and was closed when V1 PRs stopped being accepted; the V1 plugin path it changed no longer exists on dev.

The probe now runs git rev-parse --git-dir first. If that fails the repository cannot be read, so the diff routes fail with DiffError("Unable to read Git repository"). If it succeeds, the existing --verify HEAD probe still distinguishes an unborn branch (exit 128 after a readable git dir) from a commit, so diff("working") keeps diffing the first commit's working tree and diff("committed") stays empty. base() shares the probe, so it now reports unavailable instead of resolving to null for an unreadable repository.

Vcs.status has no error channel (Adapter.status and /api/vcs/status declare none), so it keeps its empty-list fallback for an unreadable repository; widening that contract is the separate decision discussed in the issue and is not bundled here.

How did you verify your code works?

Repro: a repo with one commit, a detached linked worktree containing an untracked file, then mv repo/.git/worktrees/wt elsewhere. In that worktree git rev-parse --git-dir exits 128 with empty stdout.

On ba06ff9e0 with only the new test, bun test test/vcs.test.ts -t unreadable fails at expect(Exit.isFailure(working)).toBeTrue() (received false). With the change it passes, and the same test also asserts diff("committed") fails and status() is still [].

$ cd packages/core && bun test test/vcs.test.ts
37 pass, 0 fail, 171 expect() calls

$ bun run typecheck
$ tsc -b tsconfig.json tsconfig.tests.json

AI-assisted development; the commands above were run locally against this branch.

Screenshots / recordings

Not a UI change.

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

hasHead treated every failed rev-parse as an unborn branch, so a pruned
worktree admin directory made both diff routes return no changes. Probe
the git directory first and fail with DiffError when it is unreadable;
status keeps its documented empty-list fallback.
@github-actions

Copy link
Copy Markdown
Contributor

The following comment was made by an LLM, it may be inaccurate:

@opencode-agent opencode-agent Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks sound. I reproduced the problem on the base and confirmed the fix with a live server: open a linked worktree, then remove its .git/worktrees/<name> admin directory. On the base, /api/vcs/diff (working and committed) returns 200 []. With this PR both return 503 Unable to read Git repository, and /api/vcs/status still returns [] as described. The new test fails on the base and passes here. The whole of test/vcs.test.ts passes and the packages/core typecheck is clean.

Public API: no endpoints, schemas or SDK types change. For an unreadable repository, /api/vcs/diff and /api/vcs/base now return their existing ServiceUnavailableError (503) where they used to return [] / null. Callers that already handle that error are unaffected.

Two things to be aware of:

  • base() also goes through hasHead, so /api/vcs/base now fails instead of returning null for an unreadable repository. The TUI diff viewer then shows an error instead of an empty view. That seems right, but the description doesn't mention it.
  • This only applies when the repository becomes unreadable after the location was resolved as Git. If you open a worktree that is already broken, project discovery finds no Git repository, so no VCS provider is chosen and the diff is still an empty 200. The workspace-delete check from #50934 also still treats the repository as clean, because status keeps its fallback (intended here, but #50934 isn't fully covered).

base() shares hasHead, so an unreadable repository now reports unavailable
instead of resolving to null; pin that in the same repro.
@feiiiiii5

Copy link
Copy Markdown
Author

Hi — #57010 and #52413 are green and mergeable: #57010 reports an unreadable repository instead of an empty diff (Closes #50934), and #52413 keeps an undecodable service config from being read as empty (Fixes #52412). Both are on dev and have regression coverage. Could someone take a look, or tell me if either direction is wrong?

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

vcs diff reports "no changes" when Git cannot read the repository

1 participant