Hoppa till innehåll

Arbetsträd

Git-arbetsträd gör det möjligt att dela ett arkiv mellan flera arbetskataloger. Varje arbetsträd har sin egen utcheckning (HEAD, index och arbetsfiler), men delar objektlagret, konfigurationen och packed-refs med huvudarkivet. Pitmaster kan skapa, lista, låsa och ta bort länkade arbetsträd samt öppna dem som arkiv. Rensning, flytt och reparation av arbetsträd är inte implementerat.

Begrepp

Ett vanligt git-arkiv har ett enda arbetsträd:

Kod
project/
  .git/          (directory)
  src/
  README.md

Med worktrees kan du ha ytterligare utcheckningar:

Kod
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

Den länkade worktreens .git är en fil (inte en katalog) som innehåller en sökväg relativ till worktreen:

Kod
gitdir: ../../path/to/project/.git/worktrees/review

Skapa en worktree

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() gör följande:

  1. Skapar grenen om den inte finns (från HEAD eller den angivna incheckningen $from)
  2. Skapar metadatakatalogen på .git/worktrees/<name>/
  3. Skriver filen .git i worktreens katalog
  4. Skriver metadatafilerna HEAD, commondir och gitdir
  5. Checkar ut grenens träd till worktreens katalog
  6. Skriver worktreens index

Om grenen redan finns ignoreras $from. Till skillnad från git vägrar addWorktree() inte att använda en gren som redan är utcheckad i en annan worktree, och kör inte hooken post-checkout.

Med ett uttryckligt metadatanamn

Använd det valfria argumentet name när flera länkade worktrees kan ha samma basnamn för utcheckningen.

PHP
$wt = $repo->addWorktree(
    '/tmp/sandboxes/app',
    'feature/review',
    name: 'sandbox-app',
);

Metadatanamnet används under .git/worktrees/<name>/ i stället för basename($path).

Från en specifik incheckning

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

Lista worktrees

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";
}

Listan innehåller alltid huvud-worktreen först, följd av länkade worktrees. Anropa worktrees() på ett handtag för huvudarkivet: på ett handtag för en länkad worktree rapporterar huvudentrén för närvarande den länkade worktreens egen sökväg (#14).

Ta bort en worktree

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);

När en worktree tas bort:

  1. Kontrolleras om worktreen är låst (misslyckas om inte $force är true)
  2. Tas filen .git bort från worktreens katalog
  3. Tas metadatakatalogen bort från .git/worktrees/

Filerna i arbetskatalogen tas inte bort automatiskt. Grenen finns kvar.

Öppna en länkad worktree

Du kan öppna en länkad worktree precis som vilket arkiv som helst:

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'

Sökvägar för arkivåtgärder hanteras så här:

  • Objekt: läses från och skrivs till den gemensamma katalogen git (project/.git/objects/)
  • Konfiguration: läses från den gemensamma katalogen git (project/.git/config)
  • Packed-refs: läses från den gemensamma katalogen git
  • HEAD: per worktree (varje worktree håller reda på sin egen gren), tillsammans med ORIG_HEAD, MERGE_HEAD och övriga åtgärdsreferenser
  • Index: per worktree (varje worktree har sitt eget stagingområde)
  • Lösa referenser: grenar och taggar delas i den gemensamma katalogen (project/.git/refs/)
  • Hooks: läses från worktreens egen katalog git, så de delade hookarna körs inte (#12)

Stash hanterar ännu inte länkade worktrees: det läser identiteten från worktreens egen katalog, som saknar konfiguration, och skriver refloggen där (#14).

Worktree-värdeobjekt

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

För hantering av worktrees på låg nivå. WorktreeManager::add() skriver bara metadata och filen .git: den skapar inte grenen, checkar inte ut filer och skriver inget index. Använd $repo->addWorktree() för en användbar utcheckning. Grenen måste redan finnas (eller vara ett fullständigt referensnamn, till exempel refs/tags/v1.0.0, vilket kopplar loss 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');

Låsa worktrees

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');

Låset lagras som en fil på .git/worktrees/<name>/locked. Filinnehållet är låsorsaken (kan vara tomt).

Identifiera worktrees

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'

Filstruktur

Metadata för huvud-worktreen vid länkade worktrees

Kod
.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

Den länkade worktreens .git-fil

Kod
gitdir: <relative path to>/project/.git/worktrees/<name>

Delade resurser

Resurs Plats Delad?
Objekt $commonDir/objects/ Ja
Konfiguration $commonDir/config Ja
Packed-refs $commonDir/packed-refs Ja
Hooks $gitDir/hooks/ Nej: Pitmaster kör inte de delade hookarna i länkade worktrees
HEAD $gitDir/HEAD Nej (per worktree)
Index $gitDir/index Nej (per worktree)
Lösa referenser $commonDir/refs/ Ja (HEAD och åtgärdsreferenser som MERGE_HEAD är per worktree)