Skip to content

feat: move existing files to the target when creating a link - #39

Open
amd989 wants to merge 1 commit into
masterfrom
claude/move-files-on-link
Open

amd989 wants to merge 1 commit into
masterfrom
claude/move-files-on-link

Conversation

@amd989

@amd989 amd989 commented Nov 6, 2026

Copy link
Copy Markdown
Owner

Summary

Adds the SymMover-style workflow requested in #33. If a real folder or file already exists where the link should go, Symlinker can now move it to the target and leave a link in its place, so anything using the old path keeps working. This is mainly for moving large folders (games, AI models) to another drive.

Closes #33

How it works

Fill in the link location, a link name that matches the existing folder or file, and a new target path, then click Create link. When the target doesn't exist yet (or is an empty folder), a "Move to target?" prompt offers Move and link or Cancel.

  • Same drive: the item is renamed into place, which is instant and atomic.
  • Different drive:
    1. The original is renamed aside to <name>.symlinker-move. This fails right away if something has its files open, before any time is spent copying.
    2. It is copied to the target with a progress dialog ("1.0 GB of 3.0 GB copied") and a Cancel button.
    3. The link is created.
    4. The old copy is removed.
  • Rollback: any failure or cancel before the link exists removes the partial copy and puts the original back. If even that fails, the message says exactly where the files are.
  • If the target already has content, the existing replace prompt is unchanged.

Changes

  • Symlinker/LinkMover.cs (new): copy, move and delete helpers.
    • Nested symlinks and junctions are recreated as links and never followed.
    • Hidden, read-only and system attributes and timestamps are kept.
    • Delete clears read-only flags and removes nested links directly. Directory.Delete would otherwise try to unmount junctions with an API that needs admin rights, which fails when the app runs without admin.
  • Symlinker/MainWindow.xaml.cs:
    • Move offer in CreateLink(), plus the move/rollback flow and the progress dialog.
    • The window can't be closed while a move is running.
    • SendCommand() is split so RunMklinkAsync() returns success and message. mklink output is now shown as one dialog instead of one dialog per output line.
  • Properties/Resources*.resx + Resources.Designer.cs: 14 new strings, translated into all 9 languages.

Testing

Tested by hand in the running app (Debug build, not elevated, Developer Mode on):

Scenario Result
Folder, same drive (C: to C:) Moved, symlink created
Folder, C: to X:, with hidden, read-only and nested-junction items Copied correctly, junction kept as a link, the folder it points to untouched, old copy removed
3 GB folder, C: to X: Progress shown, checksum matches after the move
3 GB folder, Cancel mid-copy Original restored (same checksum), no partial copy left
File, cross-drive hard link (mklink refuses) File restored, mklink's reason shown
File, cross-drive symbolic link Moved, link created
Clicking the close button during a copy Window stays open until the move finishes

Release build: 0 warnings.

Notes for reviewers

  • Nested junctions inside a moved folder are recreated as directory symbolic links (same target). .NET has no API to create junctions.
  • File permissions (ACLs) and alternate data streams are not copied on cross-drive moves. Moved files get the destination folder's default permissions.
  • Another PR in progress (UI/UX review fixes) also adds strings at the end of every resx file and Resources.Designer.cs. Whichever merges second will need a small, easy merge fix.

🤖 Generated with Claude Code

When a real folder or file already sits where the link goes and the
target path is free (missing or an empty folder), offer to move it to
the target and leave a link in its place, so anything using the old
path keeps working (#33).

- Same drive: rename into place, instant and atomic
- Different drive: set the original aside first (fails fast if files
  are in use), copy with a cancelable progress dialog, create the link,
  then remove the old copy
- Any failure or cancel before the link exists puts everything back
- Nested links are recreated as links, never followed; hidden,
  read-only and timestamps are kept
- The window can't be closed while a move is running
- mklink output is now collected and shown as one dialog

Closes #33

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature: move all files on symlink creation

1 participant