Gå til indhold

Worktrees

Git worktrees gør det muligt for flere arbejdsmapper at dele ét repository. Hvert worktree har sit eget checkout (HEAD, indeks, arbejdsfiler), men deler objektlager, konfiguration og packed-refs med hovedrepositoriet. Pitmaster kan oprette, vise, låse og fjerne tilknyttede worktrees samt åbne dem som repositories. Beskæring, flytning og reparation af worktrees er ikke implementeret.

Begreber

Et almindeligt git-repository har ét arbejdstræ:

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

Med worktrees kan du have flere checkouts:

Kode
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

Det tilknyttede worktrees .git er en fil (ikke en mappe), der indeholder en sti relativ til worktree:

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

Oprettelse af et 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ølgende:

  1. Opretter branchen, hvis den ikke findes (fra HEAD eller den angivne $from-commit)
  2. Opretter metadatamappen i .git/worktrees/<name>/
  3. Skriver filen .git i worktree-mappen
  4. Skriver metadatafilerne HEAD, commondir og gitdir
  5. Checker branchens træ ud i worktree-mappen
  6. Skriver indekset for worktree

Hvis branchen allerede findes, ignoreres $from. I modsætning til git afviser addWorktree() ikke en branch, der allerede er checket ud i et andet worktree, og den kører ikke post-checkout-hooken.

Med et eksplicit metadatanavn

Brug det valgfrie argument name, når flere tilknyttede worktrees kan dele samme checkout-basismappenavn.

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

Dette metadatanavn bruges under .git/worktrees/<name>/ i stedet for basename($path).

Fra en bestemt commit

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

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

Listen indeholder altid hoved-worktree først, efterfulgt af tilknyttede worktrees. Kald worktrees() på et handle for hovedrepositoriet: på et handle til et tilknyttet worktree viser hovedposten i øjeblikket det tilknyttede worktrees egen sti (#14).

Fjernelse af et 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);

Fjernelse af et worktree:

  1. Kontrollerer, om worktree er låst (fejler, medmindre $force er true)
  2. Fjerner filen .git fra worktree-mappen
  3. Fjerner metadatamappen fra .git/worktrees/

Arbejdstræets filer slettes ikke automatisk. Branchen findes fortsat.

Åbning af et tilknyttet worktree

Du kan åbne et tilknyttet worktree ligesom ethvert repository:

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'

Repository-operationer løser stier sådan:

  • Objekter: læses fra og skrives til den fælles git-mappe (project/.git/objects/)
  • Konfiguration: læses fra den fælles git-mappe (project/.git/config)
  • Packed-refs: læses fra den fælles git-mappe
  • HEAD: pr. worktree (hvert worktree sporer sin egen branch), sammen med ORIG_HEAD, MERGE_HEAD og de øvrige operationsrefs
  • Indeks: pr. worktree (hvert worktree har sit eget staging-område)
  • Løse refs: branches og tags deles i den fælles mappe (project/.git/refs/)
  • Hooks: læses fra git-mappen pr. worktree, så de delte hooks ikke kører (#12)

Stash håndterer endnu ikke tilknyttede worktrees: den læser identiteten fra mappen pr. worktree, som ikke har nogen konfiguration, og skriver sin reflog dér (#14).

Worktree-værdiobjekt

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

Til worktree-administration på lavt niveau. WorktreeManager::add() skriver kun metadata og filen .git: den opretter ikke branchen, checker filer ud eller skriver et indeks. Brug $repo->addWorktree() til et brugbart checkout. Branchen skal allerede findes (eller være et fuldt ref-navn som refs/tags/v1.0.0, der frakobler 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åsning af 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åsen gemmes som en fil på .git/worktrees/<name>/locked. Filens indhold er låseårsagen (kan være tom).

Registrering af 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 for hoved-worktree ved tilknyttede worktrees

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

Det tilknyttede worktrees .git-fil

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

Ressourcedeling

Ressource Placering Delt?
Objekter $commonDir/objects/ Ja
Konfiguration $commonDir/config Ja
Packed-refs $commonDir/packed-refs Ja
Hooks $gitDir/hooks/ Nej: Pitmaster kører ikke de delte hooks i tilknyttede worktrees
HEAD $gitDir/HEAD Nej (pr. worktree)
Indeks $gitDir/index Nej (pr. worktree)
Løse refs $commonDir/refs/ Ja (HEAD og operationsrefs som MERGE_HEAD er pr. worktree)