You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
deepnote sync directory moves can land on another project's directory #537
When deepnote sync renames a project directory after a cloud rename, it calls fs.rename without checking that the destination is free or that nothing under the source belongs to another project. Four scenarios, each present on main:
1. High. Renaming onto an empty project directory cross-links two projects.fs.rename succeeds when the destination is an existing empty directory. Project A (has notebooks) is renamed to B's path while B's directory has no notebooks. A's directory lands on B's, and the manifest records both projects at one directory. On the next run B's local hash equals A's content, so sync classifies B as a push and imports A's notebooks into project B in the cloud.
2. Medium. A failed move does not block moves into its source. X Foo → Zed fails because Zed is occupied. Y moves into Foo/Y, inside X's still-present directory. On the next run X's move carries Foo/Y to Zed/Y, Y is pulled fresh into Foo/Y, and Y's unpushed edits stay hidden in Zed/Y. They are not reported as untracked because Zed is tracked.
3. Medium-low. The untracked-destination check can target another project's live directory. When a record's source directory is missing and its planned directory exists, sync treats that directory as untracked. If it is another project's current directory whose own move failed, the prompt says the directory is not linked, and --on-conflict override overwrites the other project's files.
4. Medium. A new project can be synced into a tracked project's directory before that project moves out. A new project Y is planned at A while tracked project X moves from A to B. Projects sync in path order, so Y can run first. Y has no manifest record, so A looks like an untracked directory, and --on-conflict override writes Y's notebooks over X's local files. X then moves A to B and pulls its cloud copy, so X's unpushed local edits are lost.
Expected
Before any rename, refuse when the destination exists (lstat, allowing case-only renames), matches another record's current directory, or when the source contains another pending move's destination. When a move fails, fail every pending move whose destination is under the failed move's source. Run every directory move before any project writes into its planned directory. Report each refused move as an error outcome and leave disk and manifest untouched.
Found while reviewing #535. All three exist on main, so they are not part of that change.
Activity