
# كائنات Git

يُخزّن كل شيء في مستودع git ككائن. وهناك أربعة أنواع: الكتل (محتوى الملفات)، والأشجار (قوائم المجلدات)، والالتزامات (لقطات مع بيانات وصفية)، والوسوم (مراجع مشروحة). ويُعرّف كل كائن بتجزئة SHA-1 لمحتواه.

## أنواع الكائنات

### Blob

تحتوي الكتلة على محتوى الملف الخام دون بيانات وصفية (لا اسم ملف ولا أذونات).

```php
use Pitmaster\Object\Blob;

// Create from content
$blob = Blob::fromContent('Hello, world!');
echo $blob->content;    // 'Hello, world!'
echo $blob->size();     // 13
echo $blob->id->hex;    // SHA-1 hash
echo $blob->type->value; // 'blob' (the property is ObjectType::Blob)
```

### شجرة

الشجرة هي قائمة دليل. لكل إدخال وضع واسم وتجزئة تشير إلى كتلة أو شجرة فرعية.

```php
use Pitmaster\Object\Tree;
use Pitmaster\Object\TreeEntry;

// Read a tree
$tree = $repo->readObject($treeHash);

foreach ($tree->entries as $entry) {
    echo "{$entry->mode} {$entry->name} {$entry->hash->hex}\n";
}

// Look up a specific entry
$entry = $tree->entry('README.md');

// Create a tree (entries already in git's sort order)
$entries = [
    new TreeEntry('100644', 'README.md', $blobId),
    new TreeEntry('100755', 'deploy.sh', $execBlobId),
    new TreeEntry('40000', 'src', $subtreeId),
];

$tree = Tree::fromEntries($entries);
```

يكتب `Tree::fromEntries()` الإدخالات بالترتيب المعطى وسلاسل الأوضاع كما أُعطيت تمامًا، لذا رتّبها أولًا واكتب أوضاع المجلدات كـ`40000`. تنتج الشجرة المكتوبة باستخدام `040000` أو بترتيب آخر تجزئة تختلف عن تجزئة git، ويعرض `git fsck --strict` الوضع بأصفار بادئة.

#### TreeEntry

`TreeEntry` كائن قيمة للقراءة فقط:

```php
use Pitmaster\Object\TreeEntry;

$entry->mode;   // '100644', '100755', '40000', '120000', '160000'
$entry->name;   // Filename
$entry->hash;   // ObjectId

// Type checks
$entry->isBlob();        // regular or executable file (mode 10xxxx)
$entry->isExecutable();  // mode 100755
$entry->isTree();        // mode 40000 (also accepts 040000)
$entry->isSymlink();     // mode 120000
$entry->isGitlink();     // mode 160000 (submodule)
```

قيم الوضع:
| الوضع | المعنى |
|------|---------|
| `100644` | ملف عادي |
| `100755` | ملف قابل للتنفيذ |
| `40000` | مجلد (شجرة)؛ يعرضه `git ls-tree` بالشكل `040000` |
| `120000` | رابط رمزي |
| `160000` | رابط Git (وحدة فرعية) |

تُرتب إدخالات الشجرة حسب الاسم، مع التعامل مع أسماء المجلدات كما لو كانت تنتهي بـ`/` لأغراض المقارنة.

### إيداع

يربط الالتزام شجرةً بسجلها التاريخي وبياناتها الوصفية.

```php
use Pitmaster\Object\Commit;

$commit = $repo->head();

$commit->id;         // ObjectId
$commit->tree;       // ObjectId of the tree
$commit->parents;    // array of ObjectId (zero for root, one for normal, two+ for merge)
$commit->author;     // 'Name <email> timestamp timezone'
$commit->committer;  // 'Name <email> timestamp timezone'
$commit->message;    // Full commit message

$commit->isMerge();  // true if two or more parents
```

#### إنشاء محتوى الالتزام

```php
$content = Commit::buildContent(
    tree: $treeId,
    parents: [$parentId],
    author: 'Jane <jane@example.com> 1700000000 +0000',
    committer: 'Jane <jane@example.com> 1700000000 +0000',
    message: "Add feature\n",
);
```

تنسيق الالتزام الخام:

```
tree <hex hash>\n
parent <hex hash>\n       (zero or more)
author <name> <<email>> <timestamp> <tz>\n
committer <name> <<email>> <timestamp> <tz>\n
\n
<message>
```

### وسم

الوسوم المشروحة كائنات تتضمن بيانات وصفية.

```php
use Pitmaster\Object\Tag;

$tag = $repo->readObject($tagHash);

$tag->object;        // ObjectId of the target
$tag->objectType;    // ObjectType enum (ObjectType::Commit, ...)
$tag->name;          // Tag name
$tag->tagger;        // 'Name <email> timestamp timezone'
$tag->message;       // Tag message
```

تنسيق الوسم الخام:

```
object <hex hash>\n
type commit\n
tag <name>\n
tagger <name> <<email>> <timestamp> <tz>\n
\n
<message>
```

## ObjectId

`ObjectId` كائن قيمة للقراءة فقط يمثل تجزئة SHA-1 (أو SHA-256).

```php
use Pitmaster\Object\ObjectId;
use Pitmaster\Object\ObjectType;

// From hex string
$id = ObjectId::fromHex('a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2');

// From binary (20 bytes for SHA-1, 32 for SHA-256)
$id = ObjectId::fromBinary($rawBytes);

// Compute from content
$id = ObjectId::compute(ObjectType::Blob, 'Hello, world!');
$id = ObjectId::compute(ObjectType::Blob, 'Hello, world!', 'sha256');

// Zero hash
$id = ObjectId::zero();
$id = ObjectId::zero('sha256');

// Properties
$id->hex;      // 40-character hex string (64 for SHA-256)
$id->binary;   // 20-byte raw binary (32 for SHA-256)
$id->algo;     // 'sha1' or 'sha256'
$id->isSha256();
$id->isZero();

// Comparison
$id->equals($otherId);
```

تُحسب التجزئة على البيانات غير المضغوطة: `sha1("{type} {size}\0{content}")`، أو SHA-256 للبايتات نفسها. ويختار `fromHex()` و`fromBinary()` الخوارزمية بحسب الطول.

## تعداد ObjectType

```php
use Pitmaster\Object\ObjectType;

ObjectType::Blob;    // 'blob'
ObjectType::Tree;    // 'tree'
ObjectType::Commit;  // 'commit'
ObjectType::Tag;     // 'tag'
```

## تنسيق الكائنات المنفصلة

تُخزّن الكائنات المنفصلة كملفات مستقلة في `.git/objects/`، وتُنظّم وفق أول محرفين سداسييْن عشريين من تجزئتها.

```
.git/objects/a1/b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2
```

يُضغط محتوى الملف باستخدام zlib. ويكون المحتوى بعد فك الضغط:

```
<type> <decimal size>\0<raw content>
```

على سبيل المثال، تُخزّن كتلة تحتوي على "Hello" بالشكل التالي:

```
zlib_encode("blob 5\0Hello")
```

### قراءة الكائنات المنفصلة

```php
use Pitmaster\Storage\LooseObjectStore;

$store = new LooseObjectStore('/path/to/.git/objects');
$object = $store->read($objectId);
```

يقوم `LooseObjectStore` بما يلي:
1. يحسب مسار الملف انطلاقًا من التجزئة
2. يقرأ الملف ويفك ضغطه باستخدام zlib
3. يحلل الترويسة لاستخراج النوع والحجم
4. يفوّض المهمة إلى المحلل المناسب (`Blob` و`Tree` و`Commit` و`Tag`)

### كتابة الكائنات المنفصلة

```php
$store->write($object);
```

خطوات الكتابة:
1. التسلسل: `"{type} {size}\0{content}"`
2. استخدام `id` الخاص بالكائن، المحسوب عند إنشائه (ويتحقق من التجزئة عند القراءة)
3. الضغط: `zlib_encode(serialized)`
4. الكتابة إلى `.git/objects/{hash[0..1]}/{rest of hash}`
5. كتابة ذرية (الكتابة إلى ملف مؤقت ثم إعادة تسميته)

## ObjectSerializer

يتولى `ObjectSerializer` ترميز تنسيق الكائنات على السلك وفك ترميزه.

```php
use Pitmaster\Storage\ObjectSerializer;

// Encode
$raw = ObjectSerializer::encodeRaw($object);
// "blob 5\0Hello"
$compressed = ObjectSerializer::encode($object);
// zlib-compressed form, as stored in a loose object file

// Decode
$object = ObjectSerializer::decodeRaw($raw);
$object = ObjectSerializer::decode($compressed, $expectedHex); // throws CorruptObjectException on a hash mismatch
```

## ObjectDatabase

`ObjectDatabase` مخزن مركّب يبحث أولًا عن الكائنات المنفصلة، ثم عن ملفات الحزم.

```php
use Pitmaster\Storage\ObjectDatabase;

$db = new ObjectDatabase('/path/to/.git/objects');

$object = $db->read($objectId);    // Search loose, then packs
$db->write($object);               // Always writes to loose
$exists = $db->exists($objectId);  // Check loose, then packs
$all = $db->listAll();             // All hashes from loose + packs
```

الكتابة مباشرةً إلى التخزين المنفصل مقصودة. اترك `git gc` يعيد تجميع الكائنات في ملفات الحزم.

## دعم SHA-256

يجرّد Pitmaster خوارزمية التجزئة عبر `ObjectId`. وSHA-1 هو الافتراضي. ويُختار SHA-256 لكل مستودع عند إنشائه:

```php
$repo = Pitmaster::init('/path/to/project', 'sha256');
```

```bash
./vendor/bin/pitmaster init --object-format=sha256
```

يكتب هذا `extensions.objectformat = sha256` في الإعدادات. وتقرأ المستودعات التي تُفتح لاحقًا التنسيق من هذا المدخل، ثم تستخدم كل عمليات الكائنات والأشجار والفهرس والمراجع سلاسل سداسية عشرية بطول 64 محرفًا وتجزئات ثنائية بطول 32 بايت. لا يوجد إعداد عام أو ثابت للخوارزمية.

تتلقى فئات الكائنات الخوارزمية كوسيط عند إنشائك الكائنات بنفسك: `Blob::fromContent($content, 'sha256')` و`Tree::fromEntries($entries, 'sha256')` و`ObjectId::compute($type, $content, 'sha256')`. وينشئ `Pitmaster::clone()` مستودعات SHA-1.

## التنسيق الثنائي لإدخال الشجرة

يُرمّز كل إدخال داخل محتوى كائن الشجرة كما يلي:

```
<octal mode as ASCII> <space> <filename> \0 <binary hash: 20 bytes SHA-1, 32 bytes SHA-256>
```

ملاحظة: التجزئة ثنائية خام وليست سداسية عشرية، بخلاف التجزئات السداسية العشرية في كائنات الالتزام والوسم.

تُرتب الإدخالات بمقارنة الأسماء كما لو كان للمجلدات لاحقة `/`. ويجب الالتزام بترتيب الفرز هذا بدقة كي تطابق تجزئة الشجرة ما يحسبه git.
