Nový notebook je hezká věc, dokud si nespočítáte, kolik hodin dá dostat ho do stavu, kdy jde skutečně pracovat. Napsal jsem si na to sadu skriptů – a cestou narazil na tři věci, které mi vzaly víc času než všechno ostatní dohromady.
Proč ne obraz disku
Nabízí se klonovat disk a mít hotovo. Jenže cílem migrace pro mě není mít stejný stroj – je to jediná příležitost za pět let vyhodit balast. Deset let starých instalaček, cache po nástrojích, které už nepoužívám, tři verze Pythonu z toho jednu ruční. Obraz disku všechno tohle věrně přenese.
Druhý důvod je praktičtější: obraz je černá skříňka. Když za rok potřebuju vědět, kde se vlastně bere junction C:\work, chci to najít v očíslovaném skriptu, ne rekonstruovat z paměti. Ta sada je tím pádem zároveň dokumentace stroje.
Architektura: dvě poloviny a jeden USB disk
Skripty jsou rozdělené na dvě sady. OLD-PC\ se pouští na starém stroji a jenom čte – zálohuje a exportuje. NEW-PC\ se pouští na novém a zapisuje – instaluje a obnovuje. Obě strany mají společný parametr -Dest (u mě D:\Migrace), což je externí disk, který mezi stroji putuje fyzicky.
Číslování je pořadí spuštění. Zní to triviálně, ale u migrace to je zásadní – pár kroků na sobě tvrdě závisí a špatné pořadí znamená tichý polovičatý výsledek, kterého si všimnete až za týden.
| Vrstva | Export (OLD-PC) | Import (NEW-PC) |
|---|---|---|
| Data a konfigurace | ZIP zálohy 02–06 | rozbalení do cílů |
| Aplikace | winget export |
winget install po skupinách |
| Junctiony | junctions.csv |
New-Item -ItemType Junction |
| IIS | appcmd list → XML |
appcmd add + netsh http |
| Systém | PATH, hosts, Wi-Fi, WSL | sloučení / import |
Past první: ZIP a reparse pointy
První verze zálohy wwwroot běžela přes Compress-Archive. Nechal jsem to běžet přes oběd, vrátil se – a archiv měl 180 GB a pořád rostl.
Důvod: wwwroot je prošpikovaný junctiony a symlinky. Adresář active\ obsahuje spojky, které routují domény na skutečné složky webů, wwwroot\etc míří rovnou do C:\Windows\System32\drivers\etc, a v každém node_modules po pnpm sedí stovky symlinků. Compress-Archive je všechny poslušně následuje. Výsledkem je masivní duplicita a v horším případě cyklus.
Řešení je nezávidět si a napsat si vlastní průchod stromem, který reparse pointy rozpozná a přeskočí – včetně jejich obsahu, na všech úrovních:
foreach ($item in Get-ChildItem -LiteralPath $dir -Force) {
# ReparsePoint = junction i symlink; přeskoč i s obsahem
if ($item.Attributes -band [IO.FileAttributes]::ReparsePoint) {
$stats.SkippedLinks++
continue
}
if ($item.PSIsContainer) { Add-Directory $item.FullName; continue }
[System.IO.Compression.ZipFileExtensions]::CreateEntryFromFile(
$zip, $item.FullName, $entry, $Level) | Out-Null
}
Když už jsem si ten walker psal, přešel jsem rovnou na System.IO.Compression.ZipFile místo Compress-Archive. Dva vedlejší efekty stojí za zmínku: podpora ZIP64 (žádný strop 2 GB / 65 535 položek) a možnost volit kompresi. U desítek gigabajtů zdrojáků a instalaček je Fastest podstatně rozumnější než Optimal – rozdíl ve velikosti je pár procent, rozdíl v čase násobky. NoCompression pak dělá z archivu prostý kontejner, což je nejrychlejší varianta, když házíte data na externí disk s dost místem.




