أشجار العمل
تتيح أشجار عمل 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
إنشاء شجرة عمل
$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 أو من الالتزام
$fromالمحدد) - ينشئ دليل البيانات الوصفية في
.git/worktrees/<name>/ - يكتب ملف
.gitفي دليل شجرة العمل - يكتب ملفات البيانات الوصفية
HEADوcommondirوgitdir - يسحب شجرة الفرع إلى دليل شجرة العمل
- يكتب فهرس شجرة العمل
إذا كان الفرع موجودًا بالفعل، يتم تجاهل $from. وعلى خلاف git، لا يرفض addWorktree() فرعًا تم سحبه بالفعل في شجرة عمل أخرى، ولا يشغّل الخطاف post-checkout.
باستخدام اسم بيانات وصفية صريح
استخدم الوسيط الاختياري name عندما يمكن لأشجار العمل المرتبطة المتعددة مشاركة اسم أساس السحب نفسه.
$wt = $repo->addWorktree(
'/tmp/sandboxes/app',
'feature/review',
name: 'sandbox-app',
);
يُستخدم اسم البيانات الوصفية هذا في .git/worktrees/<name>/ بدلًا من basename($path).
من التزام محدد
$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);
تتضمن إزالة شجرة عمل ما يلي:
- التحقق مما إذا كانت شجرة العمل مقفلة (تفشل العملية ما لم تكن
$forcetrue) - إزالة الملف
.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ومراجع العمليات الأخرى - الفهرس: خاص بكل شجرة عمل (لكل منها منطقة تجهيز خاصة بها)
- المراجع المنفصلة: الفروع والوسوم مشتركة في الدليل المشترك (
project/.git/refs/) - الخطافات: تُقرأ من دليل git الخاص بشجرة العمل، لذا لا تعمل الخطافات المشتركة (#12)
لا يتعامل التخزين المؤقت مع أشجار العمل المرتبطة حتى الآن: فهو يقرأ الهوية من دليل شجرة العمل، الذي لا يحتوي على إعدادات، ويكتب سجل 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() للحصول على عملية سحب قابلة للاستخدام. يجب أن يكون الفرع موجودًا بالفعل (أو أن يكون اسم مرجع كاملًا مثل 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 |
نعم |
| الخطافات | $gitDir/hooks/ |
لا: لا يشغّل Pitmaster الخطافات المشتركة في أشجار العمل المرتبطة |
| HEAD | $gitDir/HEAD |
لا (خاص بكل شجرة عمل) |
| الفهرس | $gitDir/index |
لا (خاص بكل شجرة عمل) |
| المراجع المنفصلة | $commonDir/refs/ |
نعم (HEAD ومراجع العمليات مثل MERGE_HEAD خاصة بكل شجرة عمل) |