انتقل إلى المحتوى

أشجار العمل

تتيح أشجار عمل Git مشاركة مستودع واحد بين أدلة عمل متعددة. ولكل شجرة عمل عملية سحب خاصة بها (HEAD والفهرس وملفات العمل)، بينما تشارك مخزن الكائنات والإعدادات وpacked-refs مع المستودع الرئيسي. يستطيع Pitmaster إنشاء أشجار العمل المرتبطة وسردها وقفلها وإزالتها، وفتحها كمستودعات. لم تُنفذ عمليات التنقية والنقل والإصلاح لأشجار العمل.

المفاهيم

يحتوي مستودع git عادي على شجرة عمل واحدة:

الكود
project/
  .git/          (directory)
  src/
  README.md

باستخدام أشجار العمل، يمكنك إجراء عمليات سحب إضافية:

الكود
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 أو من الالتزام $from المحدد)
  2. ينشئ دليل البيانات الوصفية في .git/worktrees/<name>/
  3. يكتب ملف .git في دليل شجرة العمل
  4. يكتب ملفات البيانات الوصفية HEAD وcommondir وgitdir
  5. يسحب شجرة الفرع إلى دليل شجرة العمل
  6. يكتب فهرس شجرة العمل

إذا كان الفرع موجودًا بالفعل، يتم تجاهل $from. وعلى خلاف git، لا يرفض addWorktree() فرعًا تم سحبه بالفعل في شجرة عمل أخرى، ولا يشغّل الخطاف post-checkout.

باستخدام اسم بيانات وصفية صريح

استخدم الوسيط الاختياري name عندما يمكن لأشجار العمل المرتبطة المتعددة مشاركة اسم أساس السحب نفسه.

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

يُستخدم اسم البيانات الوصفية هذا في .git/worktrees/<name>/ بدلًا من basename($path).

من التزام محدد

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 ومراجع العمليات الأخرى
  • الفهرس: خاص بكل شجرة عمل (لكل منها منطقة تجهيز خاصة بها)
  • المراجع المنفصلة: الفروع والوسوم مشتركة في الدليل المشترك (project/.git/refs/)
  • الخطافات: تُقرأ من دليل git الخاص بشجرة العمل، لذا لا تعمل الخطافات المشتركة (#12)

لا يتعامل التخزين المؤقت مع أشجار العمل المرتبطة حتى الآن: فهو يقرأ الهوية من دليل شجرة العمل، الذي لا يحتوي على إعدادات، ويكتب سجل 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() للحصول على عملية سحب قابلة للاستخدام. يجب أن يكون الفرع موجودًا بالفعل (أو أن يكون اسم مرجع كاملًا مثل 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 نعم
الخطافات $gitDir/hooks/ لا: لا يشغّل Pitmaster الخطافات المشتركة في أشجار العمل المرتبطة
HEAD $gitDir/HEAD لا (خاص بكل شجرة عمل)
الفهرس $gitDir/index لا (خاص بكل شجرة عمل)
المراجع المنفصلة $commonDir/refs/ نعم (HEAD ومراجع العمليات مثل MERGE_HEAD خاصة بكل شجرة عمل)