Към съдържанието

Работни дървета

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

Създаване на работно дърво

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() извършва следното:

  1. Създава клона, ако той не съществува (от HEAD или от зададения commit $from)
  2. Създава директорията с метаданни в .git/worktrees/<name>/
  3. Записва файла .git в директорията на работното дърво
  4. Записва файловете с метаданни HEAD, commondir и gitdir
  5. Извлича дървото на клона в директорията на работното дърво
  6. Записва индекса за работното дърво

Ако клонът вече съществува, $from се игнорира. За разлика от git, addWorktree() не отказва клон, който вече е checkout-нат в друго работно дърво, и не изпълнява hook-а post-checkout.

С изрично име на метаданните

Използвайте незадължителния аргумент name, когато няколко свързани работни дървета могат да имат еднакво базово име за checkout.

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

Това име на метаданните се използва в .git/worktrees/<name>/ вместо basename($path).

От конкретен commit

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

Изброяване на работните дървета

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

Списъкът винаги включва първо основното работно дърво, следвано от свързаните работни дървета. Извикайте worktrees() върху обект за основното хранилище: при обект за свързано работно дърво основният запис в момента показва собствения път на свързаното работно дърво (#14).

Премахване на работно дърво

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

При премахване на работно дърво:

  1. Проверява дали работното дърво е заключено (неуспешно, освен ако $force е true)
  2. Премахва файла .git от директорията на работното дърво
  3. Премахва директорията с метаданни от .git/worktrees/

Файловете в работното дърво не се изтриват автоматично. Клонът продължава да съществува.

Отваряне на свързано работно дърво

Можете да отворите свързано работно дърво точно като всяко друго хранилище:

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'

Операциите с хранилището разрешават пътищата по следния начин:

  • Обекти: четат се от и се записват в общата 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).

Обект със стойност на работно дърво

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

За управление на работни дървета на ниско ниво. WorktreeManager::add() записва само метаданните и файла .git: не създава клон, не извлича файлове и не записва индекс. Използвайте $repo->addWorktree() за работещ checkout. Клонът трябва вече да съществува (или да е пълно име на ref, като refs/tags/v1.0.0, което отделя 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');

Заключване на работни дървета

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

Заключването се съхранява във файл в .git/worktrees/<name>/locked. Съдържанието на файла е причината за заключването (може да е празно).

Откриване на работни дървета

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'

Структура на файловете

Метаданни на основното работно дърво за свързани работни дървета

Код
.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, са специфични за работното дърво)