Работни дървета
Git Работните дървета позволяват на няколко работни директории да споделят едно хранилище. Всяко работно дърво има собствен checkout (HEAD, индекс, работни файлове), но споделя хранилището на обектите, конфигурацията и packed-refs с основното хранилище. Pitmaster може да създава, изброява, заключва и премахва свързани работни дървета, както и да ги отваря като хранилища. Почистването, преместването и поправянето на работни дървета не са реализирани.
Основни понятия
Обикновено git хранилище има едно работно дърво:
project/
.git/ (directory)
src/
README.md
С работни дървета можете да имате допълнителни checkout-и:
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 на свързаното работно дърво е файл (а не директория), който съдържа относителен път към работното дърво:
gitdir: ../../path/to/project/.git/worktrees/review
Създаване на работно дърво
$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в директорията на работното дърво - Записва файловете с метаданни
HEAD,commondirиgitdir - Извлича дървото на клона в директорията на работното дърво
- Записва индекса за работното дърво
Ако клонът вече съществува, $from се игнорира. За разлика от git, addWorktree() не отказва клон, който вече е checkout-нат в друго работно дърво, и не изпълнява hook-а post-checkout.
С изрично име на метаданните
Използвайте незадължителния аргумент name, когато няколко свързани работни дървета могат да имат еднакво базово име за 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 = $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";
}
Списъкът винаги включва първо основното работно дърво, следвано от свързаните работни дървета. Извикайте worktrees() върху обект за основното хранилище: при обект за свързано работно дърво основният запис в момента показва собствения път на свързаното работно дърво (#14).
Премахване на работно дърво
$repo->removeWorktree('review'); // by metadata name
$repo->removeWorktree('/tmp/review'); // or by worktree path
// Force remove (even if locked)
$repo->removeWorktree('review', force: true);
При премахване на работно дърво:
- Проверява дали работното дърво е заключено (неуспешно, освен ако
$forceе true) - Премахва файла
.gitот директорията на работното дърво - Премахва директорията с метаданни от
.git/worktrees/
Файловете в работното дърво не се изтриват автоматично. Клонът продължава да съществува.
Отваряне на свързано работно дърво
Можете да отворите свързано работно дърво точно като всяко друго хранилище:
$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: специфичен за работното дърво (всяко работно дърво следи собствения си клон), заедно с
ORIG_HEAD,MERGE_HEADи другите refs за операции - Индекс: специфичен за работното дърво (всяко работно дърво има собствена зона за добавяне)
- Loose refs: клоновете и етикетите се споделят в общата директория (
project/.git/refs/) - Hooks: четат се от git директорията на работното дърво, затова споделените hooks не се изпълняват (#12)
Stash все още не обработва свързани работни дървета: чете идентичността от директорията на работното дърво, в която няма конфигурация, и записва своя reflog там (#14).
Обект със стойност на работно дърво
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
За управление на работни дървета на ниско ниво. WorktreeManager::add() записва само метаданните и файла .git: не създава клон, не извлича файлове и не записва индекс. Използвайте $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');
Заключване на работни дървета
// 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. Съдържанието на файла е причината за заключването (може да е празно).
Откриване на работни дървета
// 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'
Структура на файловете
Метаданни на основното работно дърво за свързани работни дървета
.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 на свързаното работно дърво
gitdir: <relative path to>/project/.git/worktrees/<name>
Споделяне на ресурси
| Ресурс | Местоположение | Споделен? |
|---|---|---|
| Обекти | $commonDir/objects/ |
Да |
| Конфигурация | $commonDir/config |
Да |
| Packed-refs | $commonDir/packed-refs |
Да |
| Hooks | $gitDir/hooks/ |
Не: Pitmaster не изпълнява споделените hooks в свързаните работни дървета |
| HEAD | $gitDir/HEAD |
Не (специфичен за работното дърво) |
| Индекс | $gitDir/index |
Не (специфичен за работното дърво) |
| Loose refs | $commonDir/refs/ |
Да (HEAD и refs за операции, например MERGE_HEAD, са специфични за работното дърво) |