Přejít k obsahu

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:

Kód
project/
  .git/          (directory)
  src/
  README.md

S pracovními stromy můžete mít další kontrolní kopie:

Kód
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:

Kód
gitdir: ../../path/to/project/.git/worktrees/review

Vytvoření pracovního stromu

PHP
$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í:

  1. Vytvoří větev, pokud neexistuje (z HEAD nebo zadaného commitu $from)
  2. Vytvoří adresář metadat v .git/worktrees/<name>/
  3. Zapíše soubor .git do adresáře pracovního stromu
  4. Zapíše soubory metadat HEAD, commondir a gitdir
  5. Zkontroluje strom větve do adresáře pracovního stromu
  6. 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.

PHP
$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

PHP
$wt = $repo->addWorktree('/tmp/hotfix', 'hotfix/urgent', $repo->resolve('v1.0.0'));

Výpis pracovních stromů

PHP
$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

PHP
$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:

  1. Zkontroluje, zda je pracovní strom zamčený (selže, pokud $force není true)
  2. Odebere soubor .git z adresáře pracovního stromu
  3. 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ář:

PHP
$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_HEAD a 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

PHP
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).

PHP
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ů

PHP
// 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ů

PHP
// 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

Kód
.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

Kód
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ášť)