fix(sync): handle detached HEAD by skipping pull and ingesting local working tree#635
Closed
tmchow wants to merge 1 commit into
Closed
fix(sync): handle detached HEAD by skipping pull and ingesting local working tree#635tmchow wants to merge 1 commit into
tmchow wants to merge 1 commit into
Conversation
c86f2e1 to
2b0369c
Compare
5 tasks
Owner
|
Closing — your fix landed in master via the v0.30.3 fix-wave PR #776 (merged at Thank you for the contribution — credit is preserved in the v0.30.3 CHANGELOG entry. 🙏 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
gbrain syncon a detached-HEAD repo no longer silently no-ops. It now detects the detached state up front, skipsgit pull --ff-only, and falls back to ingesting the working tree (tracked diff against HEAD plus untracked files) so local-only edits get picked up.Why this matters
Issue #574 describes the exact failure mode: in a detached-HEAD worktree,
git pull --ff-onlyerrors with "You are not currently on a branch", the existingtry/catchswallows it asWarning: git pull failed:, and the rest of sync seeslastCommit === HEAD, short-circuits to "Already up to date.", and never reads the working tree. Reporter cited the gstackgstack-brain-syncfederation flow where two worktrees can't sharemain, so the brain worktree gets stuck in detached HEAD and silently no-ops every subsequent sync.Approach
Picked option (b) from the issue body (skip pull, ingest working tree) over (a) hard-fail and (c) skip + non-zero exit. (b) preserves the federation use case the reporter described while still surfacing what happened in the user-facing message.
Changes in
src/commands/sync.ts:isDetachedHead(repoPath)usinggit symbolic-ref --quiet HEAD(exit code 1 -> detached). Wrapped in try/catch so the existinggit()helper's exception path doesn't surface to the user.if (!opts.noPull). This waygbrain sync --no-pullon a detached worktree also gets the working-tree fallback, not just the default-pull path.git pullis now gated on!opts.noPull && !detachedHead. Existing pull semantics for the attached-HEAD path are unchanged.buildDetachedWorkingTreeManifest(repoPath)that combinesgit diff --name-status HEAD(tracked changes) andgit ls-files --others --exclude-standard(untracked files) into the sameSyncManifestshape the rest of the pipeline uses. The downstream sync flow processes added/modified/deleted/renamed without caring whether the source was a commit diff or a working-tree diff.The user-facing message is the literal string from the issue's option (b):
Detached HEAD on <repoPath>; skipping git pull. Syncing from local working tree.Exit code stays 0 because we did sync, just from a different source.If you'd rather have option (a) hard-fail or (c) explicit skip + non-zero exit, the delta is small. Happy to switch.
Testing
bun run typecheckclean.bun test test/sync.test.tsdetached-HEAD tests both pass:detached HEAD skips git pull and ingests local working-tree files— default-pull path. Asserts nogit pull failedwarning, the local-only file gets imported, andresult.added === 1.detached HEAD with --no-pull also ingests local working-tree files—--no-pullpath. Asserts the working-tree fallback fires regardless of pull mode, since detection is outside the pull gate.Both tests use a real
git worktree-style detached checkout (git checkout --detach HEAD) and write a markdown file that doesn't exist anywhere in git history, then verify the page is imported with the expected title.Fixes #574
Need help on this PR? Tag
@codesmithwith what you need.