Pular para o conteúdo

Árvores de trabalho

Git árvores de trabalho permitem que vários diretórios de trabalho compartilhem um único repositório. Cada árvore de trabalho tem sua própria cópia (HEAD, índice, arquivos de trabalho), mas compartilha o armazenamento de objetos, a configuração e packed-refs com o repositório principal. Pitmaster pode criar, listar, bloquear e remover árvores de trabalho vinculadas, além de abri-las como repositórios. A limpeza, a movimentação e o reparo de árvores de trabalho não estão implementados.

Conceitos

Um repositório git comum tem uma única árvore de trabalho:

Código
project/
  .git/          (directory)
  src/
  README.md

Com árvores de trabalho, você pode ter cópias adicionais:

Código
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

O .git da árvore de trabalho vinculada é um arquivo (não um diretório) que contém um caminho relativo à árvore de trabalho:

Código
gitdir: ../../path/to/project/.git/worktrees/review

Criar uma árvore de trabalho

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() faz o seguinte:

  1. Cria a ramificação, caso ela não exista (a partir de HEAD ou do commit $from especificado)
  2. Cria o diretório de metadados em .git/worktrees/<name>/
  3. Grava o arquivo .git no diretório da árvore de trabalho
  4. Grava os arquivos de metadados HEAD, commondir e gitdir
  5. Verifica a árvore da ramificação no diretório da árvore de trabalho
  6. Grava o índice da árvore de trabalho

Se a ramificação já existir, $from será ignorado. Ao contrário de git, addWorktree() não recusa uma ramificação que já esteja verificada em outra árvore de trabalho e não executa o hook post-checkout.

Com um nome de metadados explícito

Use o argumento opcional name quando várias árvores de trabalho vinculadas puderem compartilhar o mesmo nome base de cópia.

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

Esse nome de metadados é usado em .git/worktrees/<name>/ em vez de basename($path).

A partir de um commit específico

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

Listar árvores de trabalho

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

A lista sempre inclui primeiro a árvore de trabalho principal, seguida pelas árvores de trabalho vinculadas. Chame worktrees() em um handle do repositório principal: em um handle de árvore de trabalho vinculada, a entrada principal informa atualmente o próprio caminho da árvore de trabalho vinculada (#14).

Remover uma árvore de trabalho

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

Remover uma árvore de trabalho:

  1. Verifica se ela está bloqueada (falha, a menos que $force seja true)
  2. Remove o arquivo .git do diretório da árvore de trabalho
  3. Remove o diretório de metadados de .git/worktrees/

Os arquivos da árvore de trabalho não são excluídos automaticamente. A ramificação continua existindo.

Abrir uma árvore de trabalho vinculada

Você pode abrir uma árvore de trabalho vinculada como qualquer outro repositório:

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'

As operações do repositório resolvem os caminhos desta forma:

  • Objetos: lidos de e gravados no diretório git comum (project/.git/objects/)
  • Configuração: lida do diretório git comum (project/.git/config)
  • Packed-refs: lidos do diretório git comum
  • HEAD: por árvore de trabalho (cada árvore acompanha sua própria ramificação), junto com ORIG_HEAD, MERGE_HEAD e as outras referências de operação
  • Índice: por árvore de trabalho (cada árvore tem sua própria área de preparação)
  • Referências soltas: ramificações e tags são compartilhadas no diretório comum (project/.git/refs/)
  • Hooks: lidos do diretório git de cada árvore de trabalho, então os hooks compartilhados não são executados (#12)

Stash ainda não lida com árvores de trabalho vinculadas: lê a identidade do diretório por árvore de trabalho, que não tem configuração, e grava seu reflog nesse local (#14).

Objeto de valor da árvore de trabalho

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

Para gerenciamento de árvores de trabalho de baixo nível. WorktreeManager::add() grava apenas os metadados e o arquivo .git: não cria a ramificação, não verifica arquivos nem grava um índice. Use $repo->addWorktree() para obter uma cópia funcional. A ramificação precisa já existir (ou ser um nome de referência completo, como refs/tags/v1.0.0, que desanexa 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');

Bloquear árvores de trabalho

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

O bloqueio é armazenado como um arquivo em .git/worktrees/<name>/locked. O conteúdo do arquivo é o motivo do bloqueio (pode estar vazio).

Detectar árvores de trabalho

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'

Estrutura de arquivos

Metadados da árvore de trabalho principal para árvores vinculadas

Código
.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

Arquivo .git da árvore de trabalho vinculada

Código
gitdir: <relative path to>/project/.git/worktrees/<name>

Compartilhamento de recursos

Recurso Localização Compartilhado?
Objetos $commonDir/objects/ Sim
Configuração $commonDir/config Sim
Packed-refs $commonDir/packed-refs Sim
Hooks $gitDir/hooks/ Não: Pitmaster não executa os hooks compartilhados em árvores de trabalho vinculadas
HEAD $gitDir/HEAD Não (por árvore de trabalho)
Índice $gitDir/index Não (por árvore de trabalho)
Referências soltas $commonDir/refs/ Sim (HEAD e referências de operação como MERGE_HEAD são por árvore de trabalho)