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æ:
project/
.git/ (directory)
src/
README.md
Med worktrees kan du have flere checkouts:
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:
gitdir: ../../path/to/project/.git/worktrees/review
Oprettelse af et worktree
$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:
- Opretter branchen, hvis den ikke findes (fra HEAD eller den angivne
$from-commit) - Opretter metadatamappen i
.git/worktrees/<name>/ - Skriver filen
.giti worktree-mappen - Skriver metadatafilerne
HEAD,commondiroggitdir - Checker branchens træ ud i worktree-mappen
- 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.
$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
$wt = $repo->addWorktree('/tmp/hotfix', 'hotfix/urgent', $repo->resolve('v1.0.0'));
Vis worktrees
$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
$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:
- Kontrollerer, om worktree er låst (fejler, medmindre
$forceer true) - Fjerner filen
.gitfra worktree-mappen - 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:
$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_HEADog 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
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).
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
// 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
// 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
.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
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) |