Skip to content

fix(core): preserve external files during snapshot restore - #46947

Open
kitlangton wants to merge 1 commit into
v2from
restore-symlinks
Open

fix(core): preserve external files during snapshot restore#46947
kitlangton wants to merge 1 commit into
v2from
restore-symlinks

Conversation

@kitlangton

@kitlangton kitlangton commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Why

Undoing a change from a symlink to a directory can delete a file outside the project. Restore currently follows the input map's order: it can restore the parent symlink, then follow that link while removing an old child path.

What Changes

Operation Result
Undo a symlink-to-directory replacement Restore the link without deleting its external target's files
Redo the change Restore the project directory and its contents without manual filesystem repair
Repeat either restore or reverse the input path order Preserve the same result and the external file
Delete a child beneath an existing external symlink Reject the deletion with a typed restore error

The user's Git index remains unchanged.

Restore Planning

Reuse the entry type already returned by git ls-tree, then process ancestors before descendants. An absent child beneath a restored file, symlink, or removed ancestor has no project-owned file to delete. Other deletions check the canonical parent against the canonical worktree before touching the filesystem. Present entries continue to use Git's native checkout behavior.

The real Session Undo/Redo regression also exposes Git's rejection of historical paths beneath a restored symlink during ignore checking. Snapshot comparison retries those paths using the first symlink ancestor's ignore status. Normal checks keep their existing path, and genuine Git errors still propagate.

Scope

Local snapshot restore safety and the corresponding Session Undo/Redo round trip only. This does not restore the deferred session-movement feature, change snapshot IDs, add cross-store retention, or change a public API. It does not introduce a general filesystem sandbox or new coordination with external filesystem writers.

Verification

cd packages/core
bun run test test/git.test.ts test/snapshot.test.ts test/session-revert.test.ts --rerun-each 3
bun run test test/session-owned.test.ts test/session-runner.test.ts test/config/snapshot.test.ts test/instruction-state.test.ts
bun typecheck
cd ../..
bun run lint:effect-simplifications
bunx prettier --check packages/core/src/git.ts packages/core/src/snapshot.ts packages/core/test/snapshot.test.ts packages/core/test/session-revert.test.ts
git diff --check
  • Original deletion reproduced on base 1e4e9c5d85 with an external-file sentinel in an isolated temporary directory.
  • The expanded regression set, loaded with the unmodified base Git/Snapshot modules, has six failures and one passing existing-safe case. The temporary preload was removed afterward.
  • Fixed Git/Snapshot/Session revert suites: 23 tests, 69 passes across three repetitions.
  • Broader session/configuration suites: 217 passes.
  • Coverage includes the closest ancestor in a mixed-tree plan, exact containment errors, repeated Session Undo, complete Redo, unchanged project-index bytes, ignored symlink ancestors, and propagation of malformed Git configuration errors.
  • Local verification is on macOS. All checks pass on head ffa2db8dcf, including Linux and Windows unit tests and typechecking.

Plan ancestor restoration before descendant deletion and keep missing paths beneath restored non-directory entries away from filesystem removal. Validate canonical parents for remaining deletions while leaving present entries to native Git checkout.

Handle historical ignore queries beneath restored symlinks so Session Undo and Redo finish without manual repair. Cover external-file preservation, repeated and reversed restoration, mixed trees, typed failures, and unchanged user index state.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant