link: cmux link seed pull brings a seeded Cloud folder's changes back as a diff
Leo's words:
Your project folder is copied up.
we'll want to deal with the edge cases for this too
This is piece 6 of the Cloud project seed, rule 6 of "Getting the project onto the machine" in the Cloud design spec: the folder's changes come back to the Mac as a diff, not as a copy of the folder. It is the Mac side of git.seed.export (#30633, merged) and reads its base from the seed record that cmux link seed push keeps (#30634, merged).
What it does
cmux link seed pull --socket PATH LOCAL REMOTE [--base-head SHA --base-index SHA --base-work SHA]
- Base. Read from this Mac's seed record for LOCAL and REMOTE. The three
--base-*flags, given all together or not at all, override it. It answersseed.no_previousin these cases:- there is no record;
- the record is for another machine;
- the last push was a copy, which
cmux link sync pullbrings back instead; - the record is incomplete.
- Export. Ask the machine for
git.seed.exportsince that base, wait for the job, and download the bundle overfs-v1. The machine must serve bothfs-v1andgit-seed-export-v1; otherwise the answer ismachine_outdated. - Fetch. Fetch the bundle into a quarantine repo. It borrows this repo's objects, checks every object with
transfer.fsckObjects, and checks that the bundle builds on the base. - Merge three ways against the base:
- The branch fast-forwards only when this Mac's HEAD is still the seed's. A switched branch, rewritten history, or both sides moving is a
.git/HEADconflict. - The machine's staged tree is staged here only when this Mac's staged tree is unchanged.
- Each working-tree file takes whichever side changed. If both sides changed it differently, it is a conflict.
- The branch fast-forwards only when this Mac's HEAD is still the seed's. A switched branch, rewritten history, or both sides moving is a
- On conflict nothing changes. The answer is
{"ok":false,"error_code":"sync_conflict","conflicts":[...]}. - Otherwise apply. Files move through
place.rs's transaction. The branch then moves by compare-and-swap and the index follows. Any failure puts back what changed. - Never brought back: a
.gitpath, a hook (includingcore.hooksPath), a nested repo, a symlink or submodule the machine changed, an ignored file, or an untracked secret. - The answer's
baseis saved as the new record, so the next pull and the nextcmux link seed push --since-lastbuild on it. A machine with no changes answersunchangedand nothing is downloaded.
The answer is {ok, unchanged, written, deleted, bytes, bundle_bytes, fast_forwarded, staged, head, base{head,index,work}, left_out}.
Layout
- The CLI lives in
link/seed_cli/pull.rs, next to push and clone, and handles the record. - The engine lives in
link/sync_cli/seed_pull.rs, because it uses the sync internals:Remote,git_back's quarantine, andplace. link/seed/plan.rsgainscurrent(), shared by the seed and the pull, and leaves out a secret by its raw (non-UTF-8) name.
Size
About 1,400 lines, of which about 360 are the journey test. The engine is one state machine (export, receive, the three-way plan, the transaction) with its tests. Splitting it would land a part that can't be run on its own.
Tests
tests/seed_pull_journey.rsruns real Mac and VM daemons on Linux, with the VM cloning fromfile://through theCMUX_TEST_GIT_SEED_GITHUBseam:- It seeds with
cmux link seed push, then pulls from the record. Covered: commits, edits, adds, deletes and staged changes come back; ignored files and secrets stay on the VM; a second pull isunchanged; the nextpush --since-lastbuilds on the pulled base. - Both sides edit different files and both edits are kept. A same-file conflict changes nothing, and a rewritten VM history is a
.git/HEADconflict. - Hooks, a hook-folder file and a nested repo never come back.
- It seeds with
- Unit tests cover parsing, picking the base from the record, and the plan's non-UTF-8 secret.
- On cmux-ci:
link::seed24 passed,seed_pull_journey3 passed,link_seed6 passed,cargo fmtclean. Clippy flags only files already flagged on base (start_chat_cli.rs,environments.rs).
Risk: new CLI only. Nothing calls seed pull yet, and the push side is unchanged.
Agent session: agent session
🤖 Generated with Claude Code
![View with [code]smith](https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v3-3x.png)
![Autofix with [code]smith](https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark-3x.png)
Need help on this PR? Tag @codesmith-bot with what you need.
Summary by cubic
Adds cmux link seed pull, the Mac side of the Cloud project seed: after a push copies a folder to the machine, pull brings its changes back as a diff merged against the seed, never as a folder copy. Nothing calls it yet and the push side is unchanged.
New Features
- Merges three ways against the seed's HEAD, INDEX, and WORK: the branch fast-forwards only while this Mac's HEAD is still the seed's, the machine's staged tree stages here when this Mac's staged tree is unchanged, and each working-tree file keeps whichever side changed; conflicts change nothing and answer
sync_conflict. - The base comes from this Mac's seed record, overridable by the three
--base-*flags given all together, and every pull keeps the machine's state as the next pull's andpush --since-last's base. - Hooks, nested repositories, symlinks or submodules the machine changed, ignored files, and untracked secrets never come back.
Bug Fixes
- A file saved during a pull (or one git assumes unchanged) is now a conflict, not an overwrite: the transaction checks bytes against the planned blob, and the index is replaced under git's own
index.lock. - Seed plans now leave untracked secrets out by their raw name, so a secret whose name is not UTF-8 no longer reaches the machine.