Pracovní stromy
Git Pracovní stromy umožňují více pracovním adresářům sdílet jeden repozitář. Každý pracovní strom má vlastní kontrolní kopii (HEAD, index, pracovní soubory), ale sdílí úložiště objektů, konfiguraci a packed-refs s hlavním repozitářem. Pitmaster umožňuje vytvářet, vypisovat, zamykat a odstraňovat propojené pracovní stromy a otevírat je jako repozitáře. Ořezávání, přesouvání a opravy pracovních stromů nejsou implementovány.
Základní pojmy
Běžný git repozitář má jeden pracovní strom:
project/
.git/ (directory)
src/
README.md
S pracovními stromy můžete mít další kontrolní kopie:
project/ (main worktree)
.git/ (the actual git directory)
worktrees/
review/ (metadata for linked worktree)
HEAD
commondir
gitdir
/tmp/review/ (linked worktree)
.git (file, not directory)
src/
README.md
.git propojeného pracovního stromu je soubor (nikoli adresář) obsahující cestu relativní vůči pracovnímu stromu:
gitdir: ../../path/to/project/.git/worktrees/review
Vytvoření pracovního stromu
$repo = Pitmaster::open('/path/to/project');
// Create a linked worktree on a new branch
$wt = $repo->addWorktree('/tmp/review', 'feature/review');
echo $wt->path; // '/tmp/review'
echo $wt->branch; // 'feature/review'
echo $wt->gitDir; // '/path/to/project/.git/worktrees/review'
var_dump($wt->isMain); // bool(false)
var_dump($wt->isDetached); // bool(false)
addWorktree() provede následující:
- Vytvoří větev, pokud neexistuje (z HEAD nebo zadaného commitu
$from) - Vytvoří adresář metadat v
.git/worktrees/<name>/ - Zapíše soubor
.gitdo adresáře pracovního stromu - Zapíše soubory metadat
HEAD,commondiragitdir - Zkontroluje strom větve do adresáře pracovního stromu
- Zapíše index pracovního stromu
Pokud větev již existuje, $from se ignoruje. Na rozdíl od git addWorktree() neodmítne větev, která je již zkontrolována v jiném pracovním stromu, a nespustí hook post-checkout.
S explicitním názvem metadat
Použijte volitelný argument name, pokud může více propojených pracovních stromů sdílet stejný základní název kontrolní kopie.
$wt = $repo->addWorktree(
'/tmp/sandboxes/app',
'feature/review',
name: 'sandbox-app',
);
Tento název metadat se použije v .git/worktrees/<name>/ místo basename($path).
Z konkrétního commitu
$wt = $repo->addWorktree('/tmp/hotfix', 'hotfix/urgent', $repo->resolve('v1.0.0'));
Výpis pracovních stromů
$worktrees = $repo->worktrees();
foreach ($worktrees as $wt) {
$type = $wt->isMain ? 'main' : 'linked';
$branch = $wt->branch ?? 'detached';
$locked = $wt->isLocked ? ' [locked]' : '';
echo "{$wt->path} ({$type}) [{$branch}]{$locked}\n";
}
Seznam vždy nejprve obsahuje hlavní pracovní strom a za ním propojené pracovní stromy. Na objektu hlavního repozitáře zavolejte worktrees(): u objektu propojeného pracovního stromu aktuálně hlavní položka uvádí cestu vlastního propojeného pracovního stromu (#14).
Odebrání pracovního stromu
$repo->removeWorktree('review'); // by metadata name
$repo->removeWorktree('/tmp/review'); // or by worktree path
// Force remove (even if locked)
$repo->removeWorktree('review', force: true);
Odebrání pracovního stromu:
- Zkontroluje, zda je pracovní strom zamčený (selže, pokud
$forcenení true) - Odebere soubor
.gitz adresáře pracovního stromu - Odebere adresář metadat z
.git/worktrees/
Soubory pracovního stromu se automaticky neodstraňují. Větev zůstane zachována.
Otevření propojeného pracovního stromu
Propojený pracovní strom můžete otevřít stejně jako jakýkoli repozitář:
$repo = Pitmaster::open('/tmp/review');
// Pitmaster detects the .git file and follows the gitdir reference
echo $repo->isLinkedWorktree(); // true
echo $repo->gitDir(); // '/path/to/project/.git/worktrees/review'
echo $repo->commonGitDir(); // '/path/to/project/.git'
echo $repo->workDir(); // '/tmp/review'
Operace s repozitářem řeší cesty takto:
- Objekty: čtou se ze společného adresáře git a zapisují se do něj (
project/.git/objects/) - Konfigurace: čte se ze společného adresáře git (
project/.git/config) - Packed-refs: čtou se ze společného adresáře git
- HEAD: pro každý pracovní strom zvlášť (každý pracovní strom sleduje vlastní větev), společně s
ORIG_HEAD,MERGE_HEADa dalšími referencemi operací - Index: pro každý pracovní strom zvlášť (každý má vlastní přípravnou oblast)
- Volné reference: větve a štítky se sdílejí ve společném adresáři (
project/.git/refs/) - Hooky: čtou se z adresáře git konkrétního pracovního stromu, takže sdílené hooky se nespustí (#12)
Stash zatím nepodporuje propojené pracovní stromy: načítá identitu z adresáře konkrétního pracovního stromu, který nemá konfiguraci, a zapisuje tam jeho reflog (#14).
Objekt hodnoty pracovního stromu
use Pitmaster\Worktree\Worktree;
$wt->name; // Metadata name (null for the main worktree)
$wt->path; // Working directory path
$wt->gitDir; // Per-worktree git dir
$wt->branch; // Branch name (null if detached)
$wt->head; // ObjectId of HEAD commit
$wt->isMain; // true for the main worktree
$wt->isDetached; // true if HEAD is detached
$wt->isLocked; // true if worktree is locked
$wt->lockReason; // Lock reason, or null when there is none (an empty reason is also null)
WorktreeManager
Pro nízkoúrovňovou správu pracovních stromů. WorktreeManager::add() zapisuje pouze metadata a soubor .git: nevytváří větev, nekontroluje soubory ani nezapisuje index. Pro funkční kontrolní kopii použijte $repo->addWorktree(). Větev již musí existovat (nebo jít o úplný název reference, například refs/tags/v1.0.0, který odpojí HEAD).
use Pitmaster\Worktree\WorktreeManager;
$manager = new WorktreeManager($repo->commonGitDir(), $mainWorkDir);
// List
$worktrees = $manager->list();
// Add
$wt = $manager->add('/tmp/review', 'feature/review');
$wt = $manager->add('/tmp/review', 'feature/review', 'review-app');
// Remove
$manager->remove('review');
$manager->remove('review', force: true);
// Lock/unlock (metadata name only)
$manager->lock('review', 'Do not delete, code review in progress');
$manager->unlock('review');
Zamykání pracovních stromů
// Lock (prevents removal)
$manager->lock('review', 'Code review in progress');
// Attempt to remove locked worktree
try {
$manager->remove('review');
} catch (\RuntimeException $e) {
// "Worktree is locked: review (Code review in progress)"
}
// Force remove ignores the lock
$manager->remove('review', force: true);
// Unlock
$manager->unlock('review');
Zámek se ukládá do souboru v .git/worktrees/<name>/locked. Obsah souboru je důvod zámku (může být prázdný).
Rozpoznávání pracovních stromů
// Check if a path is a linked worktree
if (Pitmaster::isWorktree('/tmp/review')) {
echo "This is a linked worktree\n";
}
// Resolve the common git dir from any path
$common = Pitmaster::commonGitDir('/tmp/review');
// '/path/to/project/.git'
// Resolve a .git file to the actual git directory
$gitDir = WorktreeManager::resolveGitFile('/tmp/review');
// '/path/to/project/.git/worktrees/review'
Rozložení souborů
Metadata hlavního pracovního stromu pro propojené pracovní stromy
.git/worktrees/<name>/
HEAD Per-worktree HEAD (symbolic ref or detached hash)
commondir Relative path to the shared git dir ("../..")
gitdir Path back to the worktree's .git file, as passed to addWorktree()
locked Present if locked (content = reason)
index Per-worktree index/staging area
Soubor .git propojeného pracovního stromu
gitdir: <relative path to>/project/.git/worktrees/<name>
Sdílení prostředků
| Prostředek | Umístění | Sdílený? |
|---|---|---|
| Objekty | $commonDir/objects/ |
Ano |
| Konfigurace | $commonDir/config |
Ano |
| Packed-refs | $commonDir/packed-refs |
Ano |
| Hooky | $gitDir/hooks/ |
Ne: Pitmaster nespouští sdílené hooky v propojených pracovních stromech |
| HEAD | $gitDir/HEAD |
Ne (pro každý pracovní strom zvlášť) |
| Index | $gitDir/index |
Ne (pro každý pracovní strom zvlášť) |
| Volné reference | $commonDir/refs/ |
Ano (HEAD a reference operací, jako MERGE_HEAD, jsou pro každý pracovní strom zvlášť) |