Skip to content

fix(watcher): realpath-resolve git dirs before subscribing#1126

Merged
Astro-Han merged 3 commits into
devfrom
claude/vcs-watcher-realpath
Jun 3, 2026
Merged

fix(watcher): realpath-resolve git dirs before subscribing#1126
Astro-Han merged 3 commits into
devfrom
claude/vcs-watcher-realpath

Conversation

@Astro-Han
Copy link
Copy Markdown
Owner

@Astro-Han Astro-Han commented Jun 3, 2026

Summary

realpath()-resolve each git metadata directory before the VCS file watcher subscribes to and filters its events.

Why

The VCS watcher resolves each git rev-parse --git-dir/--git-common-dir path with path.resolve only (file/watcher.ts). When .git is a symlink — a symlinked git dir, or a worktree reached through a symlinked path such as macOS /tmp/private/tmpgit rev-parse still reports .git, so we subscribe to the symlink path. parcel emits events at the canonical realpath, so shouldPublishVcsWatcherPath runs path.relative(symlinkPath, realPath), gets a .. traversal, and drops every HEAD/ref event. Branch detection then never fires for those repos.

Fix: realpath-resolve each git dir before subscribing/filtering, with a .catch fallback to the unresolved path when realpath fails (the dir may be absent). The watcher.ignore config check now matches either the resolved or the realpath'd dir, and a subscribedVcsDirs set preserves the existing single-subscription dedupe across the new realpath step.

Re-implements upstream anomalyco/opencode #27016 (1ac3f09468), adapted to PawWork's dual git-dir / git-common-dir subscription loop (which diverged from upstream's single-dir shape). Thanks to the upstream authors (Kagura, Simon Klee).

Related Issue

No tracked issue; surfaced via the upstream value-mining ledger. Related to previously shelved watcher work (#1016#1115).

Human Review Status

Pending

Review Focus

  • The realpath + .catch(() => resolvedVcsDir) fallback, and that the cfgIgnores check still honors a user-configured .git ignore by either path.
  • subscribedVcsDirs dedupe: two resolved paths can canonicalize to one realpath; we subscribe once.
  • Test harness ready() is now symlink-aware. A real .git dir resolves to itself, so all existing watcher cases are unchanged (verified: 20/20 pass).

Risk Notes

Low. Behavior changes only when a git dir is symlinked: previously dropped HEAD/ref events now publish. realpath failure falls back to prior behavior. No signature changes. The new regression test is skipped on Windows (symlink creation needs privileges), matching upstream.

How To Verify

bun test test/file/watcher.test.ts          → 20 pass, 0 fail (incl. new symlinked-.git case)
red→green proof: new test times out at 5s without the realpath fix, passes with it
bun run typecheck (tsgo --noEmit)            → clean, exit 0

Screenshots or Recordings

N/A — no UI change.

Checklist

  • Type label — this PR carries exactly one of bug, enhancement, task, documentation.
  • Routing labels — this PR carries at least one of app, ui, platform, harness, ci.
  • Priority label — this PR carries exactly one of P0, P1, P2, P3.
  • Human Review Status above is set to Pending, Approved by @<reviewer>, or Not required: <reason>.
  • I linked the related issue, or stated in Summary why there is no issue.

The VCS watcher resolved each `git rev-parse --git-dir/--git-common-dir`
path with `path.resolve` only. When `.git` is a symlink (a symlinked git
dir, or a worktree reached through a symlinked path such as macOS
`/tmp` -> `/private/tmp`), parcel emits events at the canonical realpath,
so `shouldPublishVcsWatcherPath` runs `path.relative(symlinkPath, realPath)`,
gets a `..` traversal, and drops every HEAD/ref event — branch detection
never fires.

realpath()-resolve each git dir before subscribing and filtering, with a
`.catch` fallback to the unresolved path when realpath fails (dir may be
absent). The cfg `watcher.ignore` check now matches either the resolved or
the realpath'd dir, and a `subscribedVcsDirs` set preserves the existing
single-subscription dedupe across the new realpath step.

The test harness `ready()` probe is now symlink-aware (a real `.git` dir
resolves to itself, so existing cases are unchanged), and a new
symlinked-`.git` regression test asserts HEAD events still publish.

Re-implements the fix from upstream anomalyco/opencode #27016 (1ac3f09468),
adapted to PawWork's dual git-dir/git-common-dir subscription loop. Thanks
to the upstream authors.
@Astro-Han Astro-Han added the bug Something isn't working label Jun 3, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jun 3, 2026

Warning

Review limit reached

@Astro-Han, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 16 minutes and 55 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 03c48599-bc19-4f16-be91-5d7f089a1e68

📥 Commits

Reviewing files that changed from the base of the PR and between 97e6e5f and 31b7047.

📒 Files selected for processing (2)
  • packages/opencode/src/file/watcher.ts
  • packages/opencode/test/file/watcher.test.ts
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/vcs-watcher-realpath

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request updates the file watcher to canonicalize potentially symlinked VCS directories (such as .git) using realpath. This ensures that file events emitted by Parcel at their real paths are correctly captured instead of being dropped. The test suite is updated to handle symlinked .git directories, and a new test is added to verify this behavior. Feedback on the test suggests using a second tmpdir() with await using to avoid polluting the parent directory and to simplify cleanup by removing the manual try-finally block.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread packages/opencode/test/file/watcher.test.ts
@github-actions github-actions Bot added harness Model harness, prompts, tool descriptions, and session mechanics P2 Medium priority labels Jun 3, 2026
Copy link
Copy Markdown

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

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

Suggested priority: P2 (includes non-doc, non-test paths outside the low-risk bucket).

P1/P0 are reserved for maintainer confirmation. Please relabel manually if this is a release blocker, security issue, data-loss risk, or updater/runtime failure.

Astro-Han added 2 commits June 3, 2026 16:53
Address Codex review: the native symlinked-.git integration test runs only
under the native watcher suite (CI skips it), so a regression of this fix
could merge unnoticed. Extract the git-dir resolution (resolve + realpath +
watcher.ignore + dedupe) into a pure, exported resolveVcsWatchDirs() and add
a CI-runnable unit test that exercises a real symlinked dir, the ignore
semantics, and the realpath-failure fallback — no native binding required.
Also shrinks the layer's inline subscription loop.
Address gemini-code-assist review: move the symlinked-.git target into a
second `await using tmpdir()` instead of writing it to the parent of the
repo tmpdir. Auto-cleans the target (no manual try/finally) and avoids
parent-directory writes that could fail in sandboxed environments.
@Astro-Han Astro-Han merged commit 242c212 into dev Jun 3, 2026
33 checks passed
@Astro-Han Astro-Han deleted the claude/vcs-watcher-realpath branch June 3, 2026 09:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working harness Model harness, prompts, tool descriptions, and session mechanics P2 Medium priority

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant