Windows Junction Migration
Overview
Move a Windows app's data directory to another drive without deleting the source and without
breaking the app. The app keeps reading/writing the original path; an NTFS directory junction
(mklink /J) transparently redirects that path to the new location. The original directory is
only renamed to .bak (never deleted), so rollback is always one rename away.
The critical insight bundled here: Windows administrator cmd.exe cannot reliably parse a
.bat file that contains any Chinese (or other non-ASCII) characters — not in UTF-8, not in
GBK, not even with chcp. The symptom is the script "echoing" every line as a command
('local' is not recognized...). The fix is to keep the .bat 100% pure ASCII and pass any
Chinese path in through an external GBK-encoded helper file that cmd reads natively.
When to use this skill
- User wants to relocate an app's data folder (e.g.
C:\Users\...\AppData→D:\or a Chinese
path likeE:\软件\workbuudy\.workbuddy). - User tried a
.bat/.ps1with the Chinese path inline and it produced garbled output or
"not recognized as an internal or external command" errors. - User wants to free C: space by moving a large data dir off the system drive.
- Any task that needs a transparent, app-agnostic path redirect on Windows with zero config
changes in the target application.
Decision: junction vs copy+delete vs symlink
| Approach | Safe? | App-aware change needed? | Notes |
|---|---|---|---|
| Copy + delete original | ❌ Risky | No | If app is running, files are locked → script失控/partial delete. Hard to roll back. |
Directory junction (mklink /J) |
✅ Safe | No | Transparent redirect. Rename-only backup. Local-fixed-drive only (C:↔E: OK). Use this. |
Symlink (mklink) |
⚠️ | No | Needs SeCreateSymbolicLinkPrivilege; junctions are simpler and sufficient for local drives. |
| Change app config / env var | ⚠️ | Yes | Only if app exposes a dataDir setting; many don't. |
Workflow
Step 0 — Pre-checks (non-destructive, run while app is open)
- Confirm both drives are NTFS (junction requirement). Local fixed drives on Windows are
almost always NTFS; a cross-volume junction between two local NTFS drives works fine. - Identify
SRC(the live path the app uses) andDST(the new location, must already contain
the synced data or be empty for first sync). - Mirror the data first while the app is still running (safe, read-only copy):
robocopy "<SRC>" "<DST>" /E /R:2 /W:2 /MT:8 /NFL /NDL /NJH /XJ
This populatesDSTwith everything fromSRC. Never use/PURGEyet.
Step 1 — Generate the migration script (the encoding-safe way)
Do NOT hand-write a .bat with the Chinese path inside it. Use the bundled generator:
python scripts/gen_migration_bat.py --src "C:\Users\Administrator\.workbuddy" --dst "E:\软件\workbuudy\.workbuddy" --out "C:\Users\Administrator\migrate.bat"
This produces TWO files:
- migrate.bat — 100% pure ASCII, contains NO Chinese. It reads the destination path from
an external file at runtime.
- migrate_target.txt — the destination path written in GBK encoding, which admin cmd
reads natively without mojibake.
The generated .bat performs, safely and in order:
1. Admin check (net session); aborts with a clear message if not elevated.
2. Optional WorkBuddy/App running check; warns, asks for YES to force.
3. Final sync robocopy SRC DST /E /PURGE (closes the gap from files written since Step 0).
4. Integrity check — DST file count must be ≥ 80% of SRC; otherwise abort (prevents
data loss).
5. Backup — rename SRC → SRC.bak (rename only, no deletion).
6. Junction — mklink /J SRC DST.
7. Probe — write+delete a temp file through the junction to prove read/write works.
8. Auto-rollback on any failure in steps 5–7: delete junction, rename .bak back.
Step 2 — Execute (app MUST be fully closed first)
> The agent itself runs inside the app; once the app is closed the agent cannot act. Therefore
> the user must run the generated script themselves after closing the app.
- Fully exit the app (including its system-tray icon — lingering processes keep log/session
files locked and will make the rename in step 5 fail). - Right-click
migrate.bat→ Run as administrator (Admin is required to rename a user dir
and create a junction underC:\Users). - Type
YES(case-insensitive) when prompted. - Script prints
[OK] ...for each step and ends with============ SUCCESS ============.
Step 3 — Verify
After the script reports success, confirm before deleting the .bak:
- C:\Users\Administrator\.workbuddy now shows as a reparse point (<JUNCTION> / lrwxrwxrwx)
pointing at DST.
- Reading a known file through SRC (e.g. settings.json) succeeds.
- Write test: create a temp file under SRC; confirm it physically appears under DST.
- The app's .bak backup directory still exists at SRC.bak.
- Start the app and confirm it runs normally, history/memory intact.
Only after a few days of clean operation should the .bak be deleted.
The encoding rule (read this before writing ANY Windows batch code)
- Never put Chinese/non-ASCII text in a
.bat/.cmdfile that will run as Admin. Not even
in comments.cmdmis-parses the whole file (the@echo offline itself fails, so every
subsequent line is echoed as a command). - Proof by failure (all tried and all failed): PowerShell
.ps1(UTF-8 no-BOM → garbled
syntax);.batwith Chinese comments (admin cmd parses comment lines as commands);.batwith
auto-elevation viapowershell Start-Process -Verb RunAs(Chinese path breaks in the elevated
child process);.batwritten in GBK (stray non-ASCII bytes still creep in);.batthat
scansE:\*for a Chinese folder viafor /d(the Chinese dir name is not enumerable). - The only reliable pattern: keep the
.batpure ASCII (verify with
python -c "open(f,'rb').read()and assert zero bytes > 127), and feed Chinese paths in from
an external GBK file viaset /p DST=<target.txt. Windows reads that file in the system
codepage (GBK) correctly. Seereferences/encoding-pitfalls.mdfor the full war story.
Resources
scripts/gen_migration_bat.py
Generates the encoding-safe pure-ASCII migration .bat plus its GBK target file. Run it, then
hand the .bat to the user to run as Admin after closing the app.
references/encoding-pitfalls.md
Detailed account of every failed approach and exactly why it failed — useful when debugging a
stubborn Windows batch task with non-ASCII paths.