worktree-sync-upstream
Sync a detached-HEAD worktree with its upstream branch in the main repo in both directions, rebasing only owned commits and fast-forwarding upstream, with recovery steps for tangled history and helpers for prose and secrets.toml conflicts. Use from inside a worktree.
- License 许可
- Apache-2.0
- 所属仓库
- mrchantey/beet
- 最近更新
- 2026-10-23
- Stars
- ★ 134
- Forks
- 8
01 前言 Front Matter
- name
- worktree-sync-upstream
- description
- Sync a detached-HEAD worktree with its upstream branch in the main repo in both directions, rebasing only owned commits and fast-forwarding upstream, with recovery steps for tangled history and helpers for prose and secrets.toml conflicts. Use from inside a worktree.
02 资源文件 2
- 脚本secrets_merge.py8.5 KB
- 脚本word_merge.py2.2 KB
03 SKILL.md 正文
Worktree Sync Upstream
Sync this worktree with its upstream branch in the main repo at ~/me/beet, in both directions. Do not delete this worktree and do not create a branch for it.
How these worktrees work
This is a Zed-style worktree: a separate cloned directory in detached HEAD (no branch), so parallel agents can each own a directory without competing for branch names. Keep it detached.
Think of the worktree as <upstream-branch> + your own commits on top. The upstream branch (eg beet_stack) is checked out in the main repo. It does not matter how long-lived the worktree is, as long as it stays a clean linear extension of upstream.
The duplication pitfall
Rebasing or replaying commits you do not own mints new hashes for identical changes. Git then sees two lineages with the same content, so every later sync conflicts or stacks up duplicate commits (same message, different hash). The rule that avoids this: only ever rebase YOUR commits, and only ever fast-forward the upstream branch. Never git merge the full divergent history in either direction.
Clean sync
Run from this worktree. Substitute the upstream branch name for <up>.
- Make sure the local upstream branch is current with its origin.
- Confirm the main repo working tree is clean (the fast-forward in step 4 touches it).
- Pull: replay only your commits onto the latest upstream, staying detached:
This brings in all upstream work and lands your commits on top. Resolve any conflicts carefully.git rebase <up> - Push: fast-forward the upstream branch to this worktree's HEAD:
git -C ~/me/beet merge --ff-only $(git rev-parse HEAD) - Confirm sync:
git rev-list --left-right --count HEAD...<up>reports0 0.
Never push to origin unless asked.
If history is already tangled
Symptom: many commits in <up>..HEAD share messages with upstream commits (duplicates from a past rebase). Recover by replaying only the genuinely unique commits:
- Back up first:
git tag backup/<name> HEAD. - Inspect
git log --oneline <up>..HEADand find the last duplicate commit (everything below your real work). - Replay only your work onto upstream:
git rebase --onto <up> <last-duplicate-commit>. - Verify no content changed:
git diff HEAD backup/<name>is empty. - Then fast-forward upstream as in step 4 above.
Resolving conflicts
Read a conflicted file against its base, git checkout --conflict=diff3 -- <file>: who changed what is only legible beside the base. After resolving, compile the touched crates before git rebase --continue, so a later commit never builds on a broken resolution.
- Prose. A markdown paragraph is one line, so two sides editing different sentences of it conflict on the whole paragraph.
python3 ~/me/beet/.agents/skills/worktree-sync-upstream/word_merge.py <file>...merges each hunk word by word and leaves the rest conflicted. A clean merge can still be wrong (both sides renaming one thing), so read every result. secrets.toml. Each group is one age blob, so two sides that each sealed something conflict on ciphertext, and taking a side silently drops the other's records.python3 ~/me/beet/.agents/skills/worktree-sync-upstream/secrets_merge.pydecrypts the three stages in memory and prints which side changed each record, never a value;--writewrites the merge, resealing only what needs it, and checks every blob against its index. It stops on a field the sides changed differently, ie a credential'sgrantsafter a converge on each: rerun with--take=ours|theirs, naming the side that sealed the credential the provider holds now. Grants it leaves behind the code only make the next plain deploy refuse with--elevated, which is the gate working.
Testing
If asked to test, run a sweep and fix any issues:
just test-core | tail
Leave the test fixes unstaged for review.
04 同仓库更多 Skills
worktree-sync-all
Sync the main branch with every worktree in both directions, applying the worktree-sync-upstream skill to each in turn, then levelling them. Use when asked to sync all worktrees.
test-the-works
Maximal project health pass. Run core tests, all tests, the example smoke set, the rsx_site verification and every downstream repo in sequence, fixing every failure and warning surfaced along the way. Use when asked to run 'the works' or fully verify the project.
test-run
Run the core test suite (just test-core) to a clean, warning-free pass, with recipes for hanging tests and mold linker flakes. Use when asked to run or fix the test suite.
test-examples
Run the workspace example smoke set to catch regressions unit tests miss, covering every feature gate combination with self-terminating examples and curl-probed servers. Use when asked to run or verify the examples.
test-dependency-audit
Audit every crate's Cargo.toml for unused, duplicate or misplaced dependencies, moving shared deps to the workspace and feature-gating where sensible. Use when asked to audit or slim dependencies.
inner-monologue
暂无简介