Δέντρα εργασίας
Τα worktree Git επιτρέπουν σε πολλούς καταλόγους εργασίας να μοιράζονται ένα αποθετήριο. Κάθε worktree έχει το δικό του checkout (HEAD, ευρετήριο, αρχεία εργασίας), αλλά μοιράζεται με το κύριο αποθετήριο το χώρο αποθήκευσης αντικειμένων, τις ρυθμίσεις και τα packed-refs. Το Pitmaster μπορεί να δημιουργεί, να παραθέτει, να κλειδώνει και να αφαιρεί συνδεδεμένα worktree, καθώς και να τα ανοίγει ως αποθετήρια. Η εκκαθάριση, η μετακίνηση και η επιδιόρθωση worktree δεν υλοποιούνται.
Βασικές έννοιες
Ένα κανονικό αποθετήριο git έχει ένα μόνο δέντρο εργασίας:
project/
.git/ (directory)
src/
README.md
Με τα worktrees, μπορείτε να έχετε επιπλέον 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
Το .git του συνδεδεμένου worktree είναι αρχείο (όχι κατάλογος) που περιέχει μια διαδρομή σχετική με το worktree:
gitdir: ../../path/to/project/.git/worktrees/review
Δημιουργία 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() εκτελεί τα εξής:
- Δημιουργεί τον κλάδο, αν δεν υπάρχει (από το HEAD ή το καθορισμένο commit
$from) - Δημιουργεί τον κατάλογο μεταδεδομένων στη θέση
.git/worktrees/<name>/ - Γράφει το αρχείο
.gitστον κατάλογο του worktree - Γράφει τα αρχεία μεταδεδομένων
HEAD,commondirκαιgitdir - Κάνει checkout το δέντρο του κλάδου στον κατάλογο του worktree
- Γράφει το index για το worktree
Αν ο κλάδος υπάρχει ήδη, το $from αγνοείται. Σε αντίθεση με το git, το addWorktree() δεν αρνείται να χρησιμοποιήσει κλάδο που έχει ήδη γίνει checkout σε άλλο worktree και δεν εκτελεί το hook post-checkout.
Με ρητό όνομα μεταδεδομένων
Χρησιμοποιήστε το προαιρετικό όρισμα name όταν πολλά συνδεδεμένα worktrees μπορούν να έχουν το ίδιο όνομα βάσης checkout.
$wt = $repo->addWorktree(
'/tmp/sandboxes/app',
'feature/review',
name: 'sandbox-app',
);
Αυτό το όνομα μεταδεδομένων χρησιμοποιείται στη θέση .git/worktrees/<name>/ αντί για basename($path).
Από συγκεκριμένο commit
$wt = $repo->addWorktree('/tmp/hotfix', 'hotfix/urgent', $repo->resolve('v1.0.0'));
Καταχώριση 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";
}
Η λίστα περιλαμβάνει πάντα πρώτο το κύριο worktree και έπειτα τα συνδεδεμένα worktrees. Καλέστε το worktrees() σε ένα handle του κύριου αποθετηρίου: σε ένα handle συνδεδεμένου worktree, η κύρια εγγραφή αναφέρει προς το παρόν τη διαδρομή του ίδιου του συνδεδεμένου worktree (#14).
Αφαίρεση 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);
Η αφαίρεση ενός worktree:
- Ελέγχει αν το worktree είναι κλειδωμένο (αποτυγχάνει εκτός αν το
$forceείναι true) - Αφαιρεί το αρχείο
.gitαπό τον κατάλογο του worktree - Αφαιρεί τον κατάλογο μεταδεδομένων από τη θέση
.git/worktrees/
Τα αρχεία του working tree δεν διαγράφονται αυτόματα. Ο κλάδος συνεχίζει να υπάρχει.
Άνοιγμα συνδεδεμένου worktree
Μπορείτε να ανοίξετε ένα συνδεδεμένο worktree όπως οποιοδήποτε αποθετήριο:
$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'
Οι λειτουργίες του αποθετηρίου επιλύουν τις διαδρομές ως εξής:
- Αντικείμενα: διαβάζονται από και γράφονται στον κοινό κατάλογο git (
project/.git/objects/) - Ρυθμίσεις: διαβάζονται από τον κοινό κατάλογο git (
project/.git/config) - Packed-refs: διαβάζονται από τον κοινό κατάλογο git
- HEAD: ανά worktree (κάθε worktree παρακολουθεί τον δικό του κλάδο), μαζί με τα
ORIG_HEAD,MERGE_HEADκαι τις άλλες αναφορές λειτουργιών - Index: ανά worktree (κάθε worktree έχει τη δική του περιοχή σταδιοποίησης)
- Loose refs: οι κλάδοι και οι ετικέτες είναι κοινόχρηστα στον κοινό κατάλογο (
project/.git/refs/) - Hooks: διαβάζονται από τον κατάλογο git ανά worktree, επομένως τα κοινόχρηστα hooks δεν εκτελούνται (#12)
Το Stash δεν υποστηρίζει ακόμη συνδεδεμένα worktrees: διαβάζει την ταυτότητα από τον κατάλογο ανά worktree, ο οποίος δεν έχει ρυθμίσεις, και γράφει εκεί το reflog του (#14).
Αντικείμενο τιμής Worktree
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
Για διαχείριση worktree χαμηλού επιπέδου. Το WorktreeManager::add() γράφει μόνο τα μεταδεδομένα και το αρχείο .git: δεν δημιουργεί τον κλάδο, δεν κάνει checkout αρχεία και δεν γράφει index. Χρησιμοποιήστε το $repo->addWorktree() για ένα λειτουργικό checkout. Ο κλάδος πρέπει να υπάρχει ήδη (ή να είναι πλήρες όνομα ref, όπως το refs/tags/v1.0.0, που αποσυνδέει το 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');
Κλείδωμα 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');
Το κλείδωμα αποθηκεύεται ως αρχείο στη θέση .git/worktrees/<name>/locked. Το περιεχόμενο του αρχείου είναι ο λόγος κλειδώματος (μπορεί να είναι κενό).
Εντοπισμός 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'
Διάταξη αρχείων
Μεταδεδομένα κύριου worktree για συνδεδεμένα 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
Αρχείο .git συνδεδεμένου worktree
gitdir: <relative path to>/project/.git/worktrees/<name>
Κοινή χρήση πόρων
| Πόρος | Τοποθεσία | Κοινόχρηστος; |
|---|---|---|
| Αντικείμενα | $commonDir/objects/ |
Ναι |
| Ρυθμίσεις | $commonDir/config |
Ναι |
| Packed-refs | $commonDir/packed-refs |
Ναι |
| Hooks | $gitDir/hooks/ |
Όχι: το Pitmaster δεν εκτελεί τα κοινόχρηστα hooks σε συνδεδεμένα worktrees |
| HEAD | $gitDir/HEAD |
Όχι (ανά worktree) |
| Index | $gitDir/index |
Όχι (ανά worktree) |
| Loose refs | $commonDir/refs/ |
Ναι (το HEAD και οι αναφορές λειτουργιών, όπως το MERGE_HEAD, είναι ανά worktree) |