Jedno repo, dvě klávesnice: synchronizace práce mezi Windows a Macem
Přes den sedím u Windows desktopu, večer u MacBooku. Stejný projekt, dvě klávesnice. Přenést mezi nimi kód není problém — to git umí od svého prvního dne. Problém je přenést rozdělanou hlavu: co bylo včera rozhodnuté, co si pamatuje agent, se kterým na projektu pracuji, jaké hooky a skripty má session k dispozici. Tenhle článek popisuje setup, který na to používám: jedno privátní repo jako jediný zdroj pravdy — pro kód i pro kontext — a automatizace, díky které na synchronizaci nemusím myslet.
(Každodenní práci s gitem přes Claude Code — commity, PR, záchranné situace — pokrývá tahák Git a GitHub s Claude Code. Tady jde o vrstvu nad tím: víc strojů, jeden projekt.)
Základ: remote jako jediný zdroj pravdy
Nejdřív to, co nedělat: nekopírovat složky přes USB a nedávat repo do složky synchronizované iCloudem, Dropboxem ani OneDrivem. Sync klient nevidí do .git atomicky — přenáší soubory po jednom, uprostřed zápisu, a lock soubory a poloviční objekty umí repozitář rozbít způsobem, který se projeví až za týden. O synchronizaci se má starat git — sync klient mu do toho jen hrabe.
Zbytek je disciplína o dvou příkazech:
git pull --ff-only # než začnu pracovat
# ... práce, commity ...
git push # než vstanu od stroje--ff-only znamená, že pull nikdy potichu nevyrobí merge commit. Když se stroje skutečně rozjedou — na obou vznikly commity — příkaz selže a já to řeším vědomě, místo abych to zjistil z divné historie.
Z toho plyne jediné tvrdé pravidlo: nevstávat od stroje s nepushnutými commity. Commit klidně může být rozpracovaný — na to jsou větve a wip: prefixy. Nepushnutý commit je ale commit, který na druhé stanici neexistuje.
Druhá vrstva: synchronizovat kontext, ne jen kód
Kdyby šlo jen o kód, tenhle článek by skončil o odstavec výš. Jenže s Claude Code žije v repu víc než kód:
CLAUDE.md— instrukce pro agenta: konvence, strukturu projektu, pravidla workflowmemory/— persistentní paměť agenta, obyčejné markdown soubory: rozhodnutí, specifikace sekcí, zpětná vazba, rozdělaná práce.claude/settings.json— sdílené hooky (o těch za chvíli)tools/— skripty, které hooky spouštějí
Když se tohle všechno verzuje spolu s kódem, druhá stanice není čistá instalace. Sednu k MacBooku a agent ví, co jsme ve středu rozhodli u Windows desktopu — protože si to přečte ze stejných souborů, které tam napsal.
Jedna podmínka: privátní repo. Paměť agenta obsahuje interní poznámky o projektu, rozhodnutí a jejich zdůvodnění — nic z toho nepatří do veřejného repozitáře.
A jedna výjimka: cesty se synchronizovat nedají. C:\Users\... na Windows není /Users/... na Macu. Per-machine nastavení proto žije v gitignorovaném lokálním configu (.claude/settings.local.json), který generuje init skript — spouští se jednou po clone:
./claude-init.sh # zapíše lokální cesty, doinstaluje per-machine drobnostiDělení je pak jednoduché: verzuje se všechno, co je pro stanice společné (instrukce, paměť, hooky, skripty), gitignoruje se všechno, co je per stroj (lokální cesty, buildy, node_modules).
Automatizace rituálu
Rituál „pull před prací, push po ní" má jednu slabinu: spoléhá na mě. A já si po víkendu nevzpomenu. Claude Code umí spouštět příkazy při událostech session — a dvě události přesně odpovídají oběma koncům rituálu:
{
"hooks": {
"SessionStart": [
{
"matcher": "startup|resume",
"hooks": [{ "type": "command", "command": "bash tools/sync-pull.sh" }]
}
],
"Stop": [
{ "hooks": [{ "type": "command", "command": "bash tools/unpushed-check.sh" }] }
]
}
}SessionStart pullne za mě. Jádro skriptu:
git fetch --prune origin
git merge --ff-only '@{u}' # tiché, když není co; křičí jen při reálném rozjetíStop — konec odpovědi agenta — spočítá nepushnuté commity a připomene se:
n=$(git log --oneline @{u}.. 2>/dev/null | wc -l | tr -d ' ')
[ "$n" != "0" ] && echo "📌 Máš $n nepushnutých commitů — git push, ať je vidí druhý stroj."Efekt v praxi: otevřu session na MacBooku a repo je aktuální dřív, než napíšu první prompt. A protože settings.json s hooky je sám součástí repa, chová se tak každá stanice automaticky — automatizace se synchronizuje stejnou cestou jako všechno ostatní.
Pasti Mac ↔ Windows
Věci, na které se nepřijde čtením dokumentace, ale až když kousnou.
Konce řádků. Windows git s core.autocrlf=true checkoutuje textové soubory s CRLF — včetně bash skriptů. Jednoduchý skript to přežije; skript s podmínkami a funkcemi spadne na neviditelném \r na konci řádku. A protože skript commitnutý z Windows poběží přes hook i na Macu, je to past s časovaným spuštěním. Oprava jednou provždy, v repu:
# .gitattributes
*.sh text eol=lf
Git identity na novém stroji. První commit na čerstvém MacBooku mi skončil hláškou „Author identity unknown". Nic dramatického, ale patří to do checklistu nové stanice, ne do debugování v půlce práce:
git config --global user.name "Jmeno Prijmeni"
git config --global user.email "mail@example.com"Smazaná větev na remotu. Feature větev mergnutá a smazaná z druhého stroje: na prvním stroji na ní pořád stojím a git pull skončí chybou „no such ref was fetched". Vypadá to jako konflikt, ale je to jen mrtvá reference:
git fetch --prune # ukliď reference na smazané remote větve
git checkout main && git pull --ff-onlySync skript výše proto rozlišuje tři stavy: živý upstream (pullni), větev celá obsažená v origin/main (řekni „přepni na main"), a skutečné rozjetí (jediný případ, kdy má křičet).
Nástroje, které na druhé platformě nejsou. lsof v git-bash na Windows chybí, netstat se zase jinak chová na Macu. Skripty sdílené mezi platformami potřebují guard — command -v lsof >/dev/null || exit 0 — aby chybějící nástroj znamenal tichou degradaci, ne rozbitý hook.
Checklist: nová stanice za deset minut
gh repo clone <user>/<repo> # 1. mimo iCloud/OneDrive/Dropbox složky!
cd <repo>
git config --global user.name "..." # 2. identita, pokud je stroj čerstvý
git config --global user.email "..."
./claude-init.sh # 3. lokální config, per-machine instalace
npm install # 4. závislosti projektuA pátý krok, který se vyplatí nepřeskočit: ověřovací round-trip. Commit z nové stanice, push, pull na staré — a totéž obráceně. Až projdou oba směry včetně hooků, je stanice plnohodnotná. Já si při něm odchytil jak chybějící identitu, tak CRLF ve skriptech.
Závěr
Nesynchronizuji soubory, synchronizuji pracoviště. Měřítko úspěchu je jednoduché — sednu k druhému stroji, otevřu session a první prompt může znít „pokračujeme". Bez vysvětlování, kde jsme skončili. Git nese kód, repo nese kontext a hooky nesou disciplínu, na kterou bych se sám spolehnout nemohl.