Přeskočit na obsah

Tato stránka byla automaticky přeložena z angličtiny, jediného jazyka, ve kterém se tento web píše. Číst originál.

Worktrees

topologie větev–worktree: `<repo>-wt/<branch>` odvozené z identity gitu a nikdy neregistrované, stavy a diagnostické kódy, příkazy, životní cyklus, vrstvené vynucování, migrace ověřená otiskem, režimy selhání a obnova

Vykresleno z docs/WORKTREES.md — téhož Markdownu, který ukazuje GitHub.

Kam patří každý linked git worktree tohoto repozitáře, jak se to rozhoduje a co nástroj dělá s tím, který je jinde. Chování tak, jak je implementováno a testováno; kde se tento dokument a spustitelný soubor rozcházejí, je chybný dokument a mění se ve stejném commitu. Rozhodnutí je ADR 21; pravidlo je project.worktree-topology.

~/dev/prismatic-majordomus                    the primary checkout: hosts the trunk, never moves
~/dev/prismatic-majordomus-wt/                the container: the checkout's sibling, name + "-wt"
~/dev/prismatic-majordomus-wt/feature/x       the one worktree of branch feature/x
~/dev/prismatic-majordomus-wt/fix/a/b         the one worktree of branch fix/a/b — hierarchy kept

Název větve je relativní cesta. Nic se neplošťuje (feature-x), nehashuje (feature-x-a93f) ani nečísluje (worktree-1) a nic se nikde neregistruje: pro daný repozitář a větev existuje právě jedna cesta a každý povrch ji odvozuje stejně.

Proč

Na jednom repozitáři pracuje současně několik relací — lidí i agentů — a každá potřebuje vlastní checkout. Mechanismem je linked worktree gitu a git bere cíl jako argument, takže o jeho umístění rozhoduje každý volající. Třiapadesát z nich se zde rozhodlo jinak: ploché sourozenecké adresáře pojmenované po ničem, scratch adresáře relací, jeden uvnitř primárního checkoutu, jeden zabírající vlastní cestu kontejneru. Cenou nebyla neuklizenost. Bylo to, že „kde je větev X“ a „kam patří nový“ nemělo odpověď, která by nezahrnovala hledání, hádání nebo vymýšlení, a relace, která musí cestu vymyslet, nechá svou práci tam, kde ji další relace hledat nebude.

Topologie dává odpověď jednou: izolace funkcí bez rozhodování, paralelní agenti, kteří nemohou kolidovat na cestě, předvídatelné mapování relace → větev → worktree, které předávka umí pojmenovat a obnovená relace se podle něj umí vrátit, úklid, který je odvozený seznam, ne archeologie, a topologie, kterou může ukázat každý povrch, protože ji každý počítá stejně.

Jak se cesta odvozuje

git rev-parse --git-common-dir             which repository this is, from any directory of any worktree
git worktree list --porcelain              its worktrees; the first record is the primary checkout
parent(primary) / name(primary) + "-wt"    the container
container / branch name                    the worktree, one directory per component

Druhý řádek je ten, který se snadno pokazí. git rev-parse --show-toplevel odpovídá na aktuální worktree, takže odvození kontejneru z něj uvnitř linked worktree by vytvořilo …-wt/feature/x-wt, kontejner pro každý worktree, zanořující se donekonečna. Primární checkout se proto čte ze sdílených metadat gitu, takže odpověď je stejná z primárního checkoutu, z jeho podadresáře, z linked worktree i ze čtyř adresářů hluboko uvnitř něj.

Název větve je vstup odvozený ze souborového systému a zachází se s ním tak: název se validuje podle vlastních pravidel gitu pro reference (žádné .., žádná komponenta začínající ., žádná končící .lock, žádné řídicí znaky, žádný z ~ ^ : ? * [ \, žádné @{, žádné počáteční -), každá komponenta platného názvu je běžný název adresáře a odvozená cesta se při každém volání dokazuje jako striktně pod kontejnerem. Komponenty v Unicode jsou běžné názvy adresářů. Dva názvy lišící se jen velikostí písmen se ohlásí (worktree.case_collision), protože na souborovém systému nerozlišujícím velikost je obě obsahuje jeden adresář.

Trunk se objevuje, nikdy není napevno zadán: HEAD vzdáleného repozitáře (refs/remotes/origin/HEAD), pak init.defaultBranch, když ta větev existuje, pak kterákoli z main a master existuje sama, pak vlastní větev primárního checkoutu. Hlásí se, která rozhodla.

-wt se píše jednou, jako CONTAINER_SUFFIX v apps/majordomus-cli/src/worktree/path.rs. Žádná konfigurace neexistuje: kontejner, který lze konfigurovat, je kontejner, o kterém by se dva checkouty jednoho repozitáře mohly rozejít.

Architektura

flowchart TD
  git["git<br>common dir · worktree list · for-each-ref · status"]
  identity["RepositoryIdentity<br>primary, current, container, trunk —<br>from anywhere inside"]
  path["path::expected_path()<br>container / branch, proved to stay<br>inside the container"]
  service["WorktreeService<br>topology · status · guard · inspect<br>create · migrate · repair · remove"]
  cli["CLI"]
  registry["capability registry"]
  hook["git hook"]
  mcp["MCP tools + majordomus://worktrees<br>/api/v1/worktrees* · OpenAPI · Swagger UI"]
  cockpit["Cockpit /cockpit/worktrees · docs/generated"]
  git --> identity
  identity --> path
  path --> service
  service --> cli
  service --> registry
  service --> hook
  registry --- mcp
  registry --- cockpit

Business logika existuje jednou, v apps/majordomus-cli/src/worktree/. Každý povrch je adaptér, který vykresluje nebo promítá, co služba odpověděla; žádný neodvozuje cestu, nerozhoduje o postavení ani neposuzuje, zda je tah bezpečný. Pět otázek jen pro čtení jsou schopnosti registru (worktree.topology, worktree.status, worktree.inspect, worktree.migration_plan, worktree.uncommitted), což je staví na MCP, HTTP, OpenAPI, Swagger UI, do Cockpitu a vygenerované reference, aniž by kterýkoli z nich nesl vlastní trasu nebo schéma. Vytváření, migrace, oprava a odebírání jsou operace téže služby na příkazové řádce. Schopnost, která zapisuje do repozitáře, musí deklarovat efekt repository_mutation, být příkazem a být dosažena přes POST — unit test jejich množinu připíná, takže přidání další je viditelné rozhodnutí — a přesouvání worktrees na disku není něco, co sdílený server MCP přebírá; Cockpit jmenuje přesný příkaz pro každý z nich.

Postavení a diagnostika

Každý registrovaný worktree má jeden stav:

postavenívýznam
primaryhlavní worktree; vyňato z pravidla cest, vázáno pravidlem trunku
canonicallinked worktree přesně na cestě své větve
misplacedlinked worktree jinde; migrace jej přivede domů
detachedžádná větev, tedy žádná kanonická cesta; nikdy se nepřesouvá
ephemeralpracovní checkout relace pod deklarovaným pracovním kořenem — dočasným adresářem, <primary>/.claude/worktrees/ pro Claude Code, adresářem vláken bb pod jeho datovým adresářem — jak je deklaruje share/providers.yaml; hlásí se, odmítá se pro commity, přesouvá se jen na požádání
missingregistrace, jejíž adresář zmizel; repair ji odstraní

a každá podmínka má stabilní kód, na každém povrchu stejný, každá s nápravou:

kódzávažnostco to znamená
worktree.path_mismatchchybalinked worktree není na kanonické cestě své větve
worktree.container_occupiedchybaworktree zabírá samotnou cestu kontejneru
worktree.nestedvarováníleží uvnitř primárního checkoutu nebo jiného worktree
worktree.destination_conflictchybakanonická cesta je obsazena něčím jiným — registrovaným worktree jiné větve, cizím checkoutem, adresářem, souborem, symbolickým odkazem
worktree.missing / worktree.stale_registrationvarováníadresář je pryč
worktree.branch_already_checked_outchybavětev je checkoutnutá jinde než na své kanonické cestě
worktree.detachedinfožádná větev
worktree.ephemeralvarováníodkládací checkout relace držící větev
worktree.primary_on_non_trunkchybaprimární checkout drží větev, která není trunk
worktree.trunk_in_linked_worktreevarovánítrunk je checkoutnutý v propojeném worktree
worktree.path_escape / worktree.invalid_branch_namechybaz názvu nelze odvodit cestu
worktree.lockedvarovánígit ho nepřesune, dokud se neodemkne
worktree.migration_verification_failedchybaotisk po přesunu se liší od otisku před ním
worktree.trunk_unknownvarovánínic neřeklo, která větev je trunk
worktree.case_collisionvarovánídva názvy větví odvozují jeden adresář na souborovém systému nerozlišujícím velikost písmen
worktree.cross_deviceinfopřesun byl proveden kopií přes souborové systémy

Topologie je platná, pokud nestojí žádná diagnostika na úrovni chyby. Odpojené a efemérní worktree ji neznehodnocují; guard přesto odmítne commit z efemérního.

Které adresáře činí checkout efemérním, jsou data, ne seznam ve spustitelném souboru: share/providers.yaml deklaruje vlastní scratch kořeny nástroje (dočasný adresář) a kořeny každého providera — <primary>/.claude/worktrees pro Claude Code a pro bb, orchestrátor, který drží worktree na vlákno, ${BB_DATA_DIR:-~/.bb}/plugins/environment-git-worktree/host-data/worktrees. Kořen se rozbalí vůči primárnímu checkoutu a prostředí; kořen, pod nímž žije sám primární checkout, se přeskočí, takže fixture testu v dočasném adresáři má běžné sourozence. Diagnostika jmenuje providera, jehož kořen odpovídal. Cizí checkout se nikdy nemigruje bez vyžádání a nástroj jej nikdy neuklízí: náprava, kterou guard nabízí, je pokračovat v kanonickém worktree (ADR 0024).

Příkazy

majordomus worktree                            # where am I, and is that where I belong (exit 10 if not)
majordomus worktree create feature/improve-cli # start: the path is derived, never given
cd "$(majordomus worktree path feature/improve-cli)"
majordomus worktree list                       # every worktree with its standing
majordomus worktree topology --format json     # the whole document the API and the Cockpit render
majordomus worktree doctor                     # every diagnostic with its code and remedy
majordomus worktree migrate --plan             # what would move; changes nothing
majordomus worktree migrate                    # move, verify, report
majordomus worktree cleanup                    # what is merged and clean; deletes nothing
majordomus worktree uncommitted                # uncommitted work in linked worktrees, and what holds it
majordomus worktree cleanup --remove           # removes those worktrees, refusing what it cannot prove
commandco děláexit 10, když
worktree / worktree statustento worktree: větev, postavení, kanonická cesta, nezacommitovaná práce, upstream, úkoltento worktree je na špatném místě
worktree listkaždý worktree, jeden řádek na worktreetopologie má chybu
worktree topologyrepozitář, kontejner, trunk, worktrees, větve bez worktree, diagnostika, součtytopologie má chybu
worktree rootkontejner, jedna cesta
worktree path <branch>kanonická cesta větve, jedna cesta; větev nemusí existovatnázev je neplatný
worktree inspect <branch>kanonická cesta, zda větev existuje, co cestu zabírá, co brání
worktree create <branch> [--base REF] [--issue ID]kanonický worktree; větev, kterou má vzdálený repozitář trunku, se převezme a sleduje ji, nová začíná na právě stáhnutém trunku vzdáleného repozitáře a řádek jmenuje přesný commitobsazeno, checkoutnuto jinde, neplatné
worktree ensure <branch>totéž, ale místo odmítnutí vrátí existující kanonický worktreecheckoutnuto jinde
worktree migrate [--plan] [--only B]… [--allow-copy] [--include-ephemeral]přivést zbloudilé worktrees domů, ověřeno otiskemkrok byl zablokován nebo selhal
worktree validate / worktree doctorchyby, nebo každá diagnostikachyba trvá
worktree guard [--quiet]smí odsud commit pokračovatne
worktree repair [--dry-run]zahodí zastaralé registrace, opraví odkazy gitu; nemaže žádný adresář
worktree remove <branch|path> [--force]odstranit jeden linked worktree; nikdy primární, nikdy větev, nikdy špinavou práci bez vynuceníodmítnuto
worktree cleanupvětve sloučené do trunku nebo do sledované vzdálené větve, kterou trunk následuje, jejichž worktree je čisté nebo chybí, s příkazy, které by je odstranily. Primární checkout, který nikdo nepullnul, neskrývá to, co už přistálo.
worktree cleanup --removeodstraní ty worktrees, každý předtím znovu změří; odmítne větev předbíhající svůj remote, worktree, ve kterém se pracuje, necommitnutou práci nebo měření, které nešlo provést
worktree uncommitted [--older-than D]každý linked worktree s necommitnutou prací: cesty, stáří nejstarší změny, co je drží, abandoned či ne, a příkaz, který by je dostal na vzdálenou ref; nic nezapisuje
worktree trunkjak daleko jsou tento checkout a lokální trunk od vzdáleného trunku, podle posledního fetche, posuzováno vůči checkout.trunk_distance, s příkazy, které jej zaktualizují; nic nestahujecheckout je zastaralý
worktree branches [--without-worktree]každá lokální větev, jedna na řádek

wt je alias pro worktree. --format json je dostupný všude a je to tatáž typovaná odpověď, kterou vykresluje lidská forma. Selektor pro remove je přesný — název větve nebo cesta — a nic se neshoduje podle prefixu ani podobnosti. Neexistuje worktree cd: podřízený proces nemůže změnit adresář nadřazeného shellu, takže path jeden vypíše a zbytek udělá shell.

worktree create --issue I0042 jmenuje větev feature/I0042-<slug> z vlastního záznamu úkolu a topologie čte úkol zpět z jakékoli větve, která nese id úkolu jako složku cesty (feature/I0042-live-page → I0042). Nic se neodvozuje z podobnosti: feature/I00420-x nejmenuje žádný úkol.

Shellové doplňování argumentů větví čte živou množinu, ne seznam:

# zsh
_mj_wt_branches() { compadd -- ${(f)"$(majordomus worktree branches 2>/dev/null)"} }
compdef '_arguments "1:sub:(status list topology root path inspect create ensure migrate validate doctor guard repair remove cleanup uncommitted trunk branches)" "2:branch:_mj_wt_branches"' majordomus-worktree

Životní cyklus

flowchart TD
  issue["issue / task<br>majordomus plan next"]
  branch["branch<br>feature/&lt;ID&gt;-&lt;slug&gt;, or any name git accepts"]
  worktree["canonical worktree<br>majordomus worktree create &lt;branch&gt;<br>→ &lt;repo&gt;-wt/&lt;branch&gt;"]
  session["session / agent<br>majordomus context, start, checkpoint, handover<br>— in that worktree"]
  commit["commit / PR<br>the pre-commit hook asks the guard;<br>push and open the PR from there"]
  cleanup["cleanup<br>majordomus worktree cleanup → worktree remove,<br>git branch -d, by a person"]
  issue --> branch
  branch --> worktree
  worktree --> session
  session --> commit
  commit --> cleanup

Předávka zaznamenává větev a worktree; relace obnovená jinde odvodí worktree z větve, místo aby věřila zaznamenané cestě, protože cesta je pomíjivá a větev ne.

Kde nová větev začíná

Primární checkout hostí trunk a nic jej neposouvá: 2026-10-16 byl jeho lokální master o 779 commitů za origin/master. Větev z něj odříznutá nenesla nic z té práce, report říkal (new, from master), jako by to bylo aktuální, a každý pracovník resetoval nový worktree na origin/master ručně. Větev, která existovala jen na remote, byla horší: začala se znovu z toho zastaralého master místo aby se převzala.

Proto create a ensure, pro větev, která zde neexistuje, se ptají vzdáleného úložiště trunku — vzdáleného, jehož HEAD pojmenovává trunk, jinak toho, které pojmenovává branch.<trunk>.remote — dvěma tichými, omezenými fetchi (--no-tags, bez promptu, zabité po 60 sekundách) trunku a větve do jejich remote-tracking refů. Lokální trunk se nikdy nepřesouvá.

remotevětev začínáreport
má větevkde stojí větev vzdáleného repozitáře, sledující jibranch fix/x (from origin/fix/x at <sha>, tracking it)
odpovídá, takovou větev nemána trunku vzdáleného serveru tak, jak byl právě stažen, bez sledování čehokoli do prvního pushebranch feature/x (new, from origin/master at <sha>)
nelze přečístna lokálním trunku, nebo na větvi remote, jak byla naposledy stažena, pokud je známastejný řádek z master a řádek warning jmenující remote, důvod gitu, o kolik commitů je lokální trunk za trunkem remote podle posledního fetche a reset, který spustit, až odpoví
žádný nenína lokálním trunku; nic se nestahuje a nic se neříkábranch feature/x (new, from master at <sha>)

--base REF se bere tak, jak je zadáno, a nikdy se nestahuje. JSON nese rozhodnutí jako start (from: remote_trunk, remote_branch, local_trunk, given, head, se vzdáleným repozitářem, důvodem a vzdáleností) a commit přečtený zpět z nového worktree jako start_commit. Je to jediné místo, kde příkaz worktree sahá do sítě; každé jiné čtení a rozhodnutí funguje offline.

.envrc nové cesty

direnv schvaluje .envrc podle cesty a obsahu, takže vlastní .envrc repozitáře na cestě, která před chvílí neexistovala, je pro direnv soubor, který nikdy neviděl, a první cd do nového worktree byl zodpovězen direnv: error .envrc is blocked. Naměřeno 2026-09-09: z 37 worktree, které nesly .envrc, jich bylo 36 zablokováno a jediný schválený byl primární checkout. create, ensure a každý přesunutý krok migrate proto přenášejí schválení primárního checkoutu na novou cestu — direnv allow na .envrc worktree v okamžiku, kdy cesta existuje — a hlásí, co se stalo, na řádku envrc a v poli envrc JSON.

Přenášeno, ne uděleno: .envrc worktree musí být bajt po bajtu shodné s primárním checkoutem a primární checkout musí být sám schválen, jinak není schváleno nic a report říká které (differs, not_approved_in_primary). Větev s vlastním .envrc je soubor, který člověk nečetl, a schvalovat ho za něj je jediná věc, které má direnv allow zabránit. Bez direnv v PATH je výsledek direnv_absent a nic není blokováno, protože by soubor nic nenačetlo.

Strojově lokální .envrc.local primárního checkoutu se sdílí ve stejném okamžiku. .envrc repozitáře načítá .envrc.local z adresáře, do kterého se vstoupí, a pravidlo adaptéru drží tento soubor bez programu, který by vyhledání primárního checkoutu potřebovalo. Nový worktree proto dostane symlink na .envrc.local primárního checkoutu a vlastní exporty člověka, včetně tajemství uložených v keychainu, jako je OPENAI_API_KEY, se tam načtou také, z jednoho souboru. Odkaz se vytvoří jen tam, kde git název ignoruje (jinak not_ignored), a worktree s vlastním .envrc.local si ho ponechá (own_kept).

Hooky jeho vlastního stromu

Repozitář, který sleduje své háčky (.githooks/, jmenované v core.hooksPath), chce, aby se každý commit posuzoval háčky stromu, ve kterém vzniká. core.hooksPath žije v konfiguraci, kterou sdílí každý worktree, takže jakmile obsahuje absolutní cestu — <primary>/.githooks, jako tady —, každý propojený worktree spouští háčky primárního checkoutu. Dne 2026-10-14 byl primární checkout o tři dny pozadu a doctor, které jeho háčky spouštěly, odmítlo každý commit aktuálního masteru v každém worktree (FAIL wiring ssh-permissions-on-commit … not invoked by <primary>/.githooks/pre-commit); I2718.

Každý propojený worktree má proto vlastní cestu k hookům. create, ensure, každý přesunutý krok migrate a repair čtou politiku repozitáře z efektivního core.hooksPath primárního checkoutu, najdou adresář, který uvádí, uvnitř worktree, a když by git spustil hooky jiného checkoutu, svážou worktree s jeho vlastními přes konfiguraci gitu pro jednotlivé worktree:

git config core.repositoryformatversion 1          # only when it was 0
git config extensions.worktreeConfig true          # only when it was off
git -C <worktree> config --worktree core.hooksPath <worktree>/.githooks

Hodnota je absolutní, takže přesunuté worktree se znovu naváže na své nové umístění. Zpráva říká, co se stalo, na řádku hooks a v poli hooks JSON (set, own, not_configured, shared, untracked, failed). Nic se nevymýšlí: repozitář bez core.hooksPath ponechává vlastní adresář hooků gitu, adresář hooků mimo primární checkout je sdílen záměrně a ponechán beze změny a strom, který adresář nenese, si ponechá primární místo žádného. Relativní .githooks git už řeší proti vrcholu každého worktree a hlásí se jako own. Primární checkout se nikdy nemění.

doctor, spuštěné v linked worktree, které git posílá do hooků jiného checkoutu, selže s wiring hooks-path a přesným příkazem, který to opraví, a posuzuje zapojení vlastního adresáře worktree — co bude spouštět po opravě — ne to zastaralé.

Poslední krok je ten, za kterým žádný mechanismus nestojí. Vytvořit worktree je jeden příkaz; odstranit ho je rozhodnutí, k němuž nikoho nic nenutí, a cleanup záměrně nic nemaže — jmenuje, co je sloučeno a čisté, a akt nechává na člověku, protože nástroj, který by odstranil worktree na základě sloučené větve, by nakonec odstranil práci, kterou někdo nedokončil. Cena je skutečná a zaplatila se tu 2026-09-08, osmnáct hodin poté, co topologie přistála: patnáct worktree mezi sebou drželo 104 GB adresářů Rustu target/, devět z nich s větvemi už sloučenými do trunku, a svazek klesl na 124 MiB volného místa. Nic v topologii nic neuvolňuje a pro kontejner není deklarován žádný rozpočet, takže „stůj“ říká disk — a říká to slovníkem, který zrovna použije další selhávající subsystém. Tři řekly, že jejich obsah je špatně. Proč je mechanismus nepřítomný, a ne chybějící, viz ADR 0021.

Pokud držíte worktree, jehož větev přistála, worktree cleanup vám to řekne; git worktree remove <path> a git branch -d <branch> jsou ty dva příkazy a adresář target/ uvnitř je obvykle většinou toho, co stojí.

Vynucování

Vrstvené a poctivé k tomu, co každá vrstva dokáže:

vrstvaco dělá
doktrínaADR 21 a project.worktree-topology říkají, jaké je pravidlo
agent se bootstrapujeAGENTS.md, CLAUDE.md a další soubory poskytovatelů, generované ze šablon, říkají každému pracovníkovi, aby před implementací ustavil topologii a nesoulad opravil nástrojem
workflow.ai/repo/workflows/task-lifecycle.md začíná na worktree status
příkazová řádkaworktree create je způsob, jakým worktree větve vzniká; cesta nikdy není argumentem
git hook.githooks/pre-commit se ptá majordomus worktree guard; feature branch se commituje jen z jejího kanonického worktree, primární checkout jen na trunku
kontrola zapojeníseznam enforcement politiky deklaruje stráž, takže majordomus doctor dokazuje, že se jí hook ptá
záznam repozitáře.envrc vykresluje prostředí repozitáře při vstupu přes bin/majordomus-env, které nikdy nesestavuje (ENVIRONMENT.md); stav worktree v tom banneru je cíl
povrchytopologie je na příkazové řádce, v MCP, HTTP, Swagger UI i v Cockpitu, takže špatná je vidět všude
testysada cratu dokazuje odvození, bezpečnost a migraci proti skutečnému gitu; shell případ dokazuje zapojení
kontrola CIscripts/ci/worktree-check drží konstantu, hook, politiku, dokumenty a případ pohromadě

Git hook se obchází pomocí --no-verify a CI runner nevidí lokální souborový systém přispěvatele; kontrola proto měří strojové vybavení pravidla, ne adresáře tohoto stroje. V rámci workflow řízených Majordomem je špatný worktree odmítnut; mimo ně je viditelný na každém povrchu.

Migrace

worktree migrate --plan uvádí každý špatně umístěný worktree s větví: kde je, kam patří, jak by se přesunul, nepotvrzenou práci, která se přesune s ním, a co mu brání. Obyvatel kontejneru, pokud nějaký je, je první — každý jiný cíl leží uvnitř kontejneru, a dokud worktree kontejnerem je, vznikly by tyto cíle uvnitř tohoto worktree. Výjimky — odpojené worktree, zastaralé registrace, dočasné pracovní checkouty, primární checkout mimo trunk — jsou uvedeny s tím, co s každou z nich udělat.

worktree migrate přepočítá plán pod zámkem repozitáře a pro každý pohyblivý krok:

  1. pořídí otisk worktree: větev, HEAD, index (každá sledovaná cesta s režimem, blobem a stagem), staged diff, unstaged diff, každý nesledovaný soubor s velikostí a hashem obsahu, každá ignorovaná položka s cestou a velikostí;
  2. požádá git o přesun — git worktree move, jedno přejmenování, včetně špinavého stavu; obsah kontejneru se vyvede do pracovní cesty a pak dovnitř; přesun mezi souborovými systémy se odmítne, pokud není --allow-copy, a pak se provede kopií, git worktree repair, ověřením proti manifestu každé položky stromu a teprve potom odstraněním originálu;
  3. znovu vezme otisk v cíli, ověří, že jej tam git registruje, a krok hlásí jako moved jen když se oba otisky rovnají — jinak failed, s tím, co se liší, a nic dalšího se nedotkne;
  4. pojmenovává relativní symbolické odkazy, které se z nové cesty už nerozřeší (node_modules -> ../sibling/node_modules), které přesun nezměnil a nepřepisuje.

Nic se neresetuje, nestashuje, nečistí, nečekautuje ani nemaže. Zamčený worktree je blokován, dokud není odemčen. Cíl, který existuje — registrovaný worktree jiné větve, cizí git checkout, adresář prázdný či nikoli, soubor, odkaz — je konflikt pojmenovaný druhem a nikdy se nepřepíše. Pokud byl příkaz spuštěn z worktree, který se přesunul, report říká, kam cd.

Způsoby selhání a obnova

situaceco vidíteco dělat
špinavý worktree na špatném místěworktree.path_mismatch, počty špinavých u kroku plánumajordomus worktree migrate; práce se přesouvá s ním
použije se kanonická cestaworktree.destination_conflict jmenující, co tam jeodsuňte to ručně stranou; nic to nepřepíše
odpojený worktreeworktree.detached, postavení detachedgit switch -c <branch> tam, pak migrovat; nebo to nechat
chybějící adresářworktree.missing / worktree.stale_registrationmajordomus worktree repair
zamčený worktreeworktree.locked, krok blokovángit worktree unlock <path>, poté migrovat
špatná větev ve worktreeworktree.branch_already_checked_out při vytvořenímigrujte worktree, který jej drží, nebo pracujte tam
direnv: error .envrc is blocked ve worktreeenvrc differs … nebo envrc the primary checkout's is not approved … ve zprávě o vytvoření či migracipřečtěte soubor, poté tam direnv allow; nejprve schvalte primární checkout, když report pojmenovává právě ten. Worktree starší než report: schválení nyní nese majordomus worktree ensure <branch>
commit ve worktree posuzovaném zastaralými hookyFAIL wiring hooks-path … runs <primary>/.githooks, not its own z doctorpříkaz, který vypisuje doctor, nebo majordomus worktree repair, který váže každý propojený worktree na jeho vlastní .githooks
primární checkout na feature větviworktree.primary_on_non_trunk; guard odmítnekdyž je čistý, git switch <trunk>; pak worktree create <branch>
nový worktree založený pozadu za vzdáleným serveremwarning could not fetch <trunk> from <remote> (…): started from the local <trunk>, N commits behind … ve zprávě o vytvořeníjakmile vzdálený server odpoví, git fetch a git reset --hard, které varování vypíše, v novém worktree před jeho prvním commitem
pracovní checkout relace na větviworktree.ephemeral; guard odmítnegit switch --detach tam a pokračujte v kanonickém worktree, nebo migrate --include-ephemeral --only <branch>
úloha v novém worktree odvozeném z meshWARN mesh … not claimed on it z start, poté FAIL mesh … never claimed on it z pre-push finish --checkstart před převzetím nároku zajistí vlastní server worktree, takže se toto objeví jen tehdy, když to nešlo (MAJORDOMUS_RUNTIME=off, zastaralý spustitelný soubor, server, který nekonvergoval): majordomus serve ensure, pak majordomus check, který nárokuje rozsah běžící úlohy. Žádný částečný finish a druhý start (docs/MESH.md)
přesun, který překročil zařízeníworktree.cross_device na kroku, nebo odmítnutímigrate --allow-copy
nesoulad otiskuworktree.migration_verification_failed, rozdíly vypsánynež na cokoli sáhnete, prohlédněte worktree tam, kde jej git registruje; přejmenování nemůže ztratit obsah, takže do něj během přesunu něco zapsalo
sestavený spustitelný soubor, který tu byl před minutouMAJORDOMUS_BIN is not an executable nebo test selhávající na větvi, která byla zelenáudálost prostředí, ne vada testované větve: jiný worker si zabral adresář buildu. Sestavte znovu ve svém vlastním worktree a změřte znovu. project.reclaim-only-what-you-own říká, čí byl
vypůjčený spustitelný soubor z jiného worktreeartefakty hlášené jako zastaralé na stromě, který je aktuální; derivace, která odstraní tisíce řádkůověřte před použitím, že odpovídá — majordomus generate --check musí říkat in sync — a před commitem si přečtěte git diff --stat -- docs/generated site/data. project.a-tool-that-cannot-match-the-tree-refuses

Výkon

worktree status a worktree guard čtou registrace, větve a jeden git status aktuálního worktree: hrstku podprocesů, žádné sestavení indexu, žádnou síť, žádné skenování. topology a list přidávají jeden git status na existující worktree a nic jiného. Migrace hashuje obsah každého změněného a nesledovaného souboru worktrees, která přesouvá, což je jediné místo, kde ta cena sedí. Nic se mezi voláními necachuje: topologie se mění mimo proces.

Kde co je

konstanta a odvozeníapps/majordomus-cli/src/worktree/path.rs
identita a trunkapps/majordomus-cli/src/worktree/identity.rs
typovaná topologieapps/majordomus-cli/src/worktree/model.rs
službaapps/majordomus-cli/src/worktree/service.rs
migrace a otiskyapps/majordomus-cli/src/worktree/migrate.rs, fingerprint.rs
schopnostiapps/majordomus-cli/src/capability/builtin/worktree.rs
příkazová řádkaapps/majordomus-cli/src/commands/worktree.rs, deklarováno v src/cli.rs
stránka Cockpitu/cockpit/worktrees, apps/majordomus-cli/src/cockpit/pages.rs
launcher, který hook používábin/majordomus-cli; .envrc prochází přes bin/majordomus-env, launcher, který nikdy nebuilduje, a oba najdou spustitelný soubor přes lib/rust_bin.sh
hook.githooks/pre-commit; položka v .ai/repo/policy.yaml enforcement
kontrolascripts/ci/worktree-check, kontrola worktree-topology v .ai/repo/ci/gates.yaml
testyapps/majordomus-cli/tests/worktree.rs, test/cases/96_worktree_topology.sh
rozhodnutí a pravidloADR 21, .ai/repo/rules/project/worktree-topology.v1.md

Vracení

majordomus worktree cleanup vypíše, co je nadbytečné: každou větev slitou do trunku, jejíž worktree je čistý nebo chybí. --remove odstraní tyto worktrees a nic jiného — větve se nikdy nemažou, protože dochází disk a větev je jediné trvalé jméno, které práce má.

Výpis a odstranění jsou dva okamžiky a vše, co činí worktree bezpečným k odstranění, se mezi nimi může změnit, takže výpis jen nominuje: --remove čte postavení větve vůči jejímu remote, zda uvnitř něco běží a zda je strom opět čistý, u každého kandidáta bezprostředně před jeho odstraněním a jmenuje, co odmítá:

odmítnutíproč to nepokrývá „sloučeno a čisto“
před svým remote, bez upstreamu, nebo upstream zmizelsloučená historie větve je na vzdáleném serveru; commit nad ní je na jednom disku a to, že je větev sloučena, o tom nic neříká
proces má uvnitř svůj pracovní adresářmtime lže: dva worktrees uklizené 2026-09-15 vypadaly od 12. nedotčené a měly v sobě živé procesy
necommitnutá prácepřečteno znovu v okamžiku odstranění, nevzato z výpisu
lsof chybí, nebo git nemohl porovnat s upstreamemnevědět není totéž co nic tam není

test/cases/611_the_reclaim_refuses_what_it_cannot_prove.sh nasází fixture s vlastním remote: volný worktree, který odejde, a větev před svým remote, jednu nikdy nepushnutou, jednu, jejíž vzdálená větev byla smazána, jednu s procesem uvnitř a jednu špinavou před výpisem, které zůstanou. Zahrnuje kombinaci, kvůli které zamítnutí existují: větev mergnutá do trunku a nesoucí commit, který její remote nikdy neviděl. Co se změní po výpisu — commit, smazaná vzdálená větev, zapsaný soubor — nasázejí unit testy reclaimu v apps/majordomus-cli/src/commands/worktree.rs. Chybějící lsof se nenasazuje nikde.

Checkout a jeho vzdálenost od trunku

Dne 2026-10-16 byl primární checkout o 779 commitů za origin/master, s necommitnutými změnami, které nikdo nedokázal přiřadit. Jeho sdílený server odpovídal na plánovací otázky každé relace z toho stromu a jeho místní master byl základem, z něhož se dělaly nové větve. Každé čtení říkalo, že server je připraven; žádné neříkalo, že strom, který servíroval, není trunk. project.a-checkout-serves-the-current-trunk z té vzdálenosti dělá čtení.

worktree::trunk::measure počítá pro HEAD checkoutu i pro lokální větev trunku commity vzdáleně sledovaného trunku, které každému chybí (behind), a commity, které každý má a trunku chybí (ahead). Vzdálený trunk je upstream trunku, jinak remote, jehož HEAD trunk jmenuje, jinak první remote s větví tohoto jména. Nic se nestahuje: odpověď platí k poslednímu fetchi a fetched_at (čas FETCH_HEAD) říká, kdy to bylo. Za probíhajícího merge se jeho příchozí commity počítají jako přítomné, takže merge, který větev aktualizuje, se nikdy neposuzuje podle stromu, který nahrazuje.

postaveníkdydoctor
currentpozadu o méně než warn_behind, nebo ne pozaduOK clone trunk
laggingna checkout.trunk_distance.warn_behind nebo za nímWARN, s nápravou
stalena checkout.trunk_distance.fail_behind nebo za nímFAIL, takže commit hook odmítne
unjudgedpozadu a politika nedeklaruje žádný práhINFO
unmeasuredžádný trunk, nebo ho nenese žádná vzdálená sledovaná větevINFO

Prahy jsou prahy politiky (.ai/repo/policy.yaml, checkout.trunk_distance) a nic nečte výchozí hodnotu, když chybějí. Stav checkoutu je to, co servíruje jeho server; stav lokálního trunku (local_trunk_standing) je to, z čeho začíná větev z něj vytvořená. V propojeném worktree je lokální trunk věcí posunu jiného checkoutu, takže doctor ho hlásí jako WARN clone local-trunk a nikdy kvůli němu neselže commity tohoto worktree.

Totéž čtení se objeví všude, kde se strom poskytuje:

  • majordomus worktree trunk (worktree.trunk, majordomus_worktree_trunk, GET /api/v1/worktrees/trunk) na to odpovídá sám a končí kódem 10, když je checkout zastaralý;
  • majordomus worktree status vytiskne řádek currency a vrátí trunk_distance;
  • server.status (majordomus serve status, majordomus_server, GET /api/v1/server) jej nese jako trunk pro checkout, který server obsluhuje;
  • instrukce MCP initialize přidají jednu větu — „Tento server servíruje strom o N commitů za origin/master“ — když je vzdálenost zaostávající, zastaralá nebo neposouzená;
  • majordomus doctor to vykresluje pod clone.

Náprava, kterou každé čtení jmenuje, uvede checkout do aktuálního stavu: fast-forward checkoutu trunku, merge vzdáleného trunku do feature větve (pushnutá větev se nikdy nepřepisuje) a fast-forward lokálního trunku tam, kde je checkoutnut. Necommitnutá práce se nejprve zachrání — git stash create, git update-ref refs/rescue/<checkout>-<timestamp> s tím commitem, nesledované soubory zkopírované vedle. Typovaná majordomus worktree update, která dělá přesně toto a odmítá, dokud v checkoutu stojí proces, je dalším řezem pravidla. test/cases/3176_a_checkout_serves_the_current_trunk.sh drží vzdálenosti, postavení, verdikty doctoru a případ rozpracovaného mergu proti fixture remote.

Necommitnutá práce, kterou nikdo nedrží

Větev, jejíž commity nedosáhnou žádného remote, jmenuje doctor (clone unpushed). Práce, která nedosáhla žádného commitu, jmenována nebyla: 2026-10-07 polovina oprav bezpečnosti integrace o důvěře v nahrazení ležela jako 45 nezacommitovaných souborů ve worktree skončené relace a 2026-10-08 doktrína dokončení ležela jako 21 připravených souborů v jiném. Obojí se našlo, protože někdo náhodou četl ty adresáře, a pushlo se jako větve rescue/...; téhož večera mělo datové úložiště volných 125 MiB (I1995).

majordomus worktree uncommitted (worktree.uncommitted, majordomus_worktree_uncommitted, GET /api/v1/worktrees/uncommitted) vypíše každý propojený worktree, jehož index nebo pracovní strom obsahuje změny — připravené, nepřipravené, nesledované i konfliktní cesty dohromady — spolu s:

poleco to je
paths, workkolik cest a součty, které uvádí worktree status
oldest_change, agenejdřívější čas změny mezi změněnými cestami; pro cestu, která už neexistuje (smazání), nahrazuje čas souboru indexu
tasktam zaznamenaná aktivní úloha: .ai/local/state/current.yaml s výsledkem active, záznam, který start zapisuje a finish uzavírá
episodestamní otevřené epizody (.ai/local/state/sessions-open/), každá s posledním znamením života a informací, zda uvízla
process_insideproces má uvnitř svůj pracovní adresář, podle čtení lsof, na kterém cleanup --remove odmítá
standingheld, když ji drží úloha, epizoda, která není opuštěná, nebo proces; jinak abandoned, když je nejstarší změna starší než práh (nebo nelze přečíst její stáří), jinak recent
remedypříkaz, který by tam práci commitnul a pushnul do rescue/<branch>-<YYYYMMDD>; jen navržený, nikdy spuštěný

Práh je session.stranded_after z politiky, ticho, po jehož uplynutí se klient epizody považuje za odešlého. Odpovídá na obě zde položené otázky — drží otevřená epizoda stále práci a čekala práce, kterou nic nedrží, dost dlouho na to, aby byla opuštěná — takže neexistuje druhé číslo, které by se od něj rozcházelo; --older-than jmenuje jiné pro jedno čtení. Politika, která žádné nedeklaruje, nepovažuje nic za opuštěné a říká to.

majordomus doctor vykreslí odečet: jedno WARN clone <path> na opuštěný worktree s jeho počtem a stářím a příkaz rescue jako nápravu; držená a nedávná práce se počítá v jednom OK. Odečet vyžaduje jedno git status na propojený worktree, asi 15 s na tři sta z nich, proto .githooks/pre-commit spouští doctor jako MAJORDOMUS_DOCTOR_CONTEXT=commit bin/majordomus doctor: v tomto kontextu, jediném, který doctor přijímá, se odečet vynechá a řádek INFO clone uncommitted to řekne a jmenuje majordomus worktree uncommitted. Ani odečet, ani doctor nic nezapisuje: každé git status běží pod GIT_OPTIONAL_LOCKS=0, takže se neobnovuje žádný index a .ai/local/ jiného worktree se čte, nikdy nezapisuje. test/cases/3025_abandoned_work_in_a_worktree_is_named.sh zasadí opuštěný worktree, držený a nedávný a drží git status každého worktree bajt po bajtu shodné napříč odečtem, nástrojem MCP a doctorem.

Build za zámkem jiného worktree

Worktrees sdílejí jeden adresář sestavení (ADR 0139) a cargo zamyká adresář sestavení po celou dobu, kdy jej sestavení používá, takže dvě sestavení najednou jsou jedno sestavení a jedno čekání. Každé sestavení, které nástroje tohoto repozitáře spouštějí, jde přes mj_cargo_bounded (lib/rust_bin.sh):

co se dějeco worker vidí
nikdo jiný nebuildujebuild běží ve sdíleném adresáři jako dříve
zámek drží jiný worktreepo MAJORDOMUS_BUILD_LOCK_WAIT sekundách (výchozí 60) jeden řádek řekne, jak dlouho čekal a které procesy cargo existují, a sestavení se znovu spustí ve vlastním adresáři tohoto stromu pod ~/.cargo/build-private/
zámek se uvolnil před limitemnic: build pokračoval ve sdíleném adresáři
sestavení už je ve vlastním adresáři a stále je blokovánovýstupní kód 75 s důvodem místo čekání
limit disku odmítne soukromé sestaveníkód 12 s vlastní zprávou hranice

Soukromý adresář zkompiluje každou závislost jednou, takže jeho první sestavení je pomalé. MAJORDOMUS_BUILD_LOCK_WAIT=0 nikdy nečeká; exportovaný CARGO_BUILD_BUILD_DIR se bere jako vlastní adresář stromu a nikdy se nenahrazuje. Pravidlo je project.a-build-never-waits-in-silence-on-anothers-lock; scripts/liveness-check odmítne sledovaný skript, který volá cargo jakýmkoli jiným způsobem, a test/cases/3002_a_build_never_waits_in_silence.sh dokládá každý řádek výše.

Nečinné inkrementální relace ve sdíleném build adresáři

rustc drží inkrementální stav cratu jako adresáře relací pod <build-dir>/<profile>/incremental/<crate>-<hash>/ a dokončenou odstraní jen pozdější kompilace téhož cratu. Crate, který se přestane kompilovat, zanechá poslední relaci a v build adresáři sdíleném každým worktree to znamená zbytky každého worktree. Dne 2026-10-16 držel sdílený adresář 79 GB takových zbytků ve 454 relacích, z nichž pět bylo živých, a na disku zbývalo volných 6,9 GiB.

Proto před zahájením buildu mj_cargo_bounded a kontrola limitu disku (lib/rust_bin.sh) odstraní nečinné relace adresáře buildu, který tento build používá:

coco se děje
dokončená relace (s-*, ne -working) nezapsaná déle než MAJORDOMUS_INCREMENTAL_IDLE_MIN minut (výchozí 30)odstraněno poté, co se vypíše jeho stáří a velikost; pak jeden řádek říká, kolik jich bylo odstraněno, co v nich napočítal du, a volné místo svazku před a po
relace -working, mladší relace, soubory zámků vedle nichnikdy se nedotklo
jiný adresář sestavení, adresář target, adresář profilu, který je symlinkem ven z adresáře sestavenínikdy se nedotklo
cargo neříká, kde je adresář buildu, nenese žádnou značku cargo, nebo výpis nelze přečíst celýnic se neodstraňuje a jeden řádek říká proč
volné místo je i potom stále pod limitembuild selže a odmítnutí jmenuje pět největších build adresářů s jejich velikostmi

Adresář sestavení je ten, který hlásí cargo metadata, zjištěný zevnitř cratu, aby odpověděl připnutý toolchain. MAJORDOMUS_INCREMENTAL_IDLE_MIN=0 nic neodstraňuje. test/cases/3080_idle_incremental_sessions_are_pruned.sh dokazuje každý řádek proti fixture adresáři sestavení.

Zastaralé artefakty crate a žádný úklid pod běžícím sestavením

Každý build majordomus-cli zapíše svou knihovnu, spustitelný soubor, každou testovací a bench binárku a na macOS jeden objektový soubor na codegen unit do <build-dir>/<profile>/deps pod názvem <target>-<hash>. Hash je metadata jednotky, takže generace, která se přestane stavět — povýšená závislost, změněné nastavení profilu, přejmenovaný test — tam zůstane navždy, a ve sdíleném build adresáři jsou to zbytky každého worktree. Dne 2026-10-16 měl sdílený deps 204 GB; příštího rána svazek dosáhl nuly a každá relace na stroji se zastavila.

Takže stejný úklid, který odstraňuje nečinné inkrementální relace (mj_rust_prune_build_dir v lib/rust_bin.sh, na který se ptá mj_cargo_bounded a kontrola prahu disku před každým buildem), odstraňuje i zastaralé generace tohoto crate, v každém profilu adresáře buildu — debug, release, každého cílového tripletu a běhu pokrytí llvm-cov-target/debug:

coco se děje
generace vlastních jednotek tohoto crate (knihovna, spustitelné soubory, testy, benchmarky), z nichž žádný soubor nebyl zapsán déle než MAJORDOMUS_DEPS_IDLE_MIN minut (výchozí 120), žádná není otevřenáodstraněno celé; jeden řádek říká, kolik generací a souborů a co napočítalo du, vypíše se dvanáct největších podle počtu souborů a jeden řádek uvádí volné místo svazku před a po
generace s jedním nedávným souborem nebo jedním souborem, který některý proces drží otevřený (lsof)nikdy se nedotklo
artefakt jiného crate, název bez hashe jednotky, zámek, adresář samotného cargonikdy se nedotklo
profil, na kterém build drží zámek cargoneprozkoumáno a jeden řádek to říká
profil, který nějaký proces pojmenovává ve své příkazové řádce — testovací binárka, rustdoc doctestu, kompilátornezameteno a jeden řádek jmenuje proces
chybí flock(1) nebo lsofnic se neodstraňuje a jeden řádek říká proč

Zámek je ten, který cargo samo drží po celé sestavení, <profile>/.cargo-build-lock (flock(2); .cargo-lock v adresáři target staršího cargo), brán bez čekání. Sám o sobě nestačí a druhá podmínka existuje kvůli tomu, co mine: cargo test uvolní zámek, když kompilace skončí, dřív než se spustí binárky a doctesty, které sestavilo, a doctest čte rlib crate z deps. 2026-10-16 zmizely rlib pod běžícím sestavením, zatímco úklid soudil jen podle stáří. Každá podmínka se zjišťuje znovu v okamžiku před odstraněním čehokoli. Příliš brzy odstraněná generace stojí jednu kompilaci: cargo přestaví jednotku, jejíž výstup chybí. test/cases/3196_stale_crate_artifacts_are_pruned_under_cargos_lock.sh prokazuje každý řádek i to, že odstranění zámku nebo kontroly procesu způsobí selhání.

Soukromé adresáře sestavení

Build, který čeká MAJORDOMUS_BUILD_LOCK_WAIT sekund za zámkem jiného worktree, se znovu buildí v ~/.cargo/build-private/<cksum of the tree's root> (mj_cargo_private_dir; nic v ~/.cargo/config.toml je nevytváří — dělá to mj_cargo_bounded exportem CARGO_BUILD_BUILD_DIR). Každý je studený build všech závislostí, několik GiB, a na stroji, kde buildí mnoho relací, většina buildů čeká tak dlouho, takže skoro každý worktree, který kdy buildil, jeden zanechal. Dne 2026-10-17 držely 178 GiB v 35 adresářích, z nichž 25 bylo nečinných déle než dvě hodiny, a svazek dosáhl 3,9 GiB.

Kontrola limitu disku proto také spouští mj_rust_reclaim_private, nejvýše jednou za deset minut na stroj:

coco se děje
privátní adresář nesoucí značku cargo, nic v něm nezapsáno déle než MAJORDOMUS_PRIVATE_BUILD_IDLE_MIN minut (výchozí 120), žádný držený zámek profilu, žádný proces, který jej jmenujepřejmenováno mimo dosah cargo, pak odstraněno celé; jeden řádek jmenuje, co bylo odstraněno, a volné místo před a po
ten s čerstvým souborem, drženým zámkem nebo procesem, který jej jmenuje; ten bez značky cargo; symlink; ten, ve kterém tento shell stavízachováno
výpis, který nelze přečíst celýzachováno

test/cases/3197_idle_private_build_dirs_are_reclaimed.sh dokazuje každý řádek.

Všechny tři zametací průchody jsou v testovací sadě vypnuté (test/run.sh nastavuje MAJORDOMUS_INCREMENTAL_IDLE_MIN, MAJORDOMUS_DEPS_IDLE_MIN a MAJORDOMUS_PRIVATE_BUILD_IDLE_MIN na 0): případ nesmí jednat na stroji, který měří. scripts/ci/backlog-check odmítne strom, v němž build už nezametá před startem, zametání už nebere zámek cargo ani se neptá tabulky procesů, nebo runner nechává zametání zapnuté (pravidlo project.accumulation-is-measured).

Path crate, který sestavil jiný worktree

Sdílení adresáře buildu sdílí každou jednotku, kterou cargo pojmenuje cestou relativní k workspace, takže tentýž path crate zkompilovaný ze dvou worktrees je jeden slot. Build skript majordomus-cli se znovu spouští při MAJORDOMUS_BUILD_TREE (.cargo/config.toml), čímž jeho slot zastarává v každém stromu kromě toho, který ho zapsal. Path crate bez toho build skriptu to nedostane: 2026-10-17 build dávky 71b znovu použil majordomus-core, který jiný worktree zkompiloval proti jiným verzím závislostí, a selhal s „multiple different versions of crate serde_json“. Dotek src/lib.rs toho crate vynutil přestavbu. Každý path crate workspace potřebuje totéž cargo:rerun-if-env-changed=MAJORDOMUS_BUILD_TREE ve vlastním build skriptu.

Tvrzení, která tento dokument definuje

Každý odkazuje na vlastní stránku s implementací, testem a místy použití.

Prohledává názvy a popisy všech stránek, které tento web publikuje.