Lewati ke konten

Worktree

Git worktree memungkinkan beberapa direktori kerja berbagi satu repositori. Setiap worktree memiliki checkout sendiri (HEAD, indeks, file kerja), tetapi berbagi penyimpanan objek, konfigurasi, dan packed-refs dengan repositori utama. Pitmaster dapat membuat, mencantumkan, mengunci, dan menghapus worktree tertaut, serta membukanya sebagai repositori. Pemangkasan, pemindahan, dan perbaikan worktree belum diimplementasikan.

Konsep

Repositori git biasa memiliki satu tree kerja:

Kode
project/
  .git/          (directory)
  src/
  README.md

Dengan worktree, Anda dapat memiliki checkout tambahan:

Kode
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

File .git worktree tertaut berisi jalur relatif terhadap worktree (bukan direktori):

Kode
gitdir: ../../path/to/project/.git/worktrees/review

Membuat worktree

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() melakukan hal berikut:

  1. Membuat cabang jika belum ada (dari HEAD atau commit $from yang ditentukan)
  2. Membuat direktori metadata di .git/worktrees/<name>/
  3. Menulis file .git di direktori worktree
  4. Menulis file metadata HEAD, commondir, dan gitdir
  5. Melakukan checkout tree cabang ke direktori worktree
  6. Menulis indeks untuk worktree

Jika cabang sudah ada, $from diabaikan. Tidak seperti git, addWorktree() tidak menolak cabang yang sudah di-checkout di worktree lain, dan tidak menjalankan hook post-checkout.

Dengan nama metadata eksplisit

Gunakan argumen opsional name jika beberapa worktree tertaut dapat memiliki nama dasar checkout yang sama.

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

Nama metadata tersebut digunakan di bawah .git/worktrees/<name>/, bukan basename($path).

Dari commit tertentu

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

Mencantumkan worktree

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

Daftar selalu menyertakan worktree utama terlebih dahulu, diikuti worktree tertaut. Panggil worktrees() pada handle repositori utama: pada handle worktree tertaut, entri utama saat ini melaporkan jalur worktree tertaut itu sendiri (#14).

Menghapus worktree

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

Menghapus worktree:

  1. Memeriksa apakah worktree terkunci (gagal kecuali $force bernilai true)
  2. Menghapus file .git dari direktori worktree
  3. Menghapus direktori metadata dari .git/worktrees/

File tree kerja tidak otomatis dihapus. Cabang tetap ada.

Membuka worktree tertaut

Anda dapat membuka worktree tertaut seperti repositori lainnya:

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'

Operasi repositori menyelesaikan jalur sebagai berikut:

  • Objek: dibaca dari dan ditulis ke direktori git bersama (project/.git/objects/)
  • Konfigurasi: dibaca dari direktori git bersama (project/.git/config)
  • Packed-refs: dibaca dari direktori git bersama
  • HEAD: per-worktree (setiap worktree melacak cabangnya sendiri), bersama dengan ORIG_HEAD, MERGE_HEAD, dan ref operasi lainnya
  • Indeks: per-worktree (setiap worktree memiliki area staging sendiri)
  • Ref longgar: cabang dan tag dibagikan di direktori bersama (project/.git/refs/)
  • Hook: dibaca dari direktori git per-worktree, sehingga hook bersama tidak dijalankan (#12)

Stash belum menangani worktree tertaut: identitas dibaca dari direktori per-worktree, yang tidak memiliki konfigurasi, dan reflog ditulis di sana (#14).

Objek nilai worktree

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

Untuk pengelolaan worktree tingkat rendah. WorktreeManager::add() hanya menulis metadata dan file .git: tidak membuat cabang, melakukan checkout file, atau menulis indeks. Gunakan $repo->addWorktree() untuk checkout yang dapat digunakan. Cabang harus sudah ada (atau berupa nama ref lengkap seperti refs/tags/v1.0.0, yang melepaskan 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');

Mengunci worktree

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

Kunci disimpan sebagai file di .git/worktrees/<name>/locked. Isi file adalah alasan penguncian (boleh kosong).

Mendeteksi worktree

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'

Tata letak file

Metadata worktree utama untuk worktree tertaut

Kode
.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

File .git worktree tertaut

Kode
gitdir: <relative path to>/project/.git/worktrees/<name>

Berbagi sumber daya

Sumber daya Lokasi Dibagikan?
Objek $commonDir/objects/ Ya
Konfigurasi $commonDir/config Ya
Packed-refs $commonDir/packed-refs Ya
Hook $gitDir/hooks/ Tidak: Pitmaster tidak menjalankan hook bersama di worktree tertaut
HEAD $gitDir/HEAD Tidak (per-worktree)
Indeks $gitDir/index Tidak (per-worktree)
Ref longgar $commonDir/refs/ Ya (HEAD dan ref operasi seperti MERGE_HEAD bersifat per-worktree)