
# Scrierea în depozite

Pitmaster scrie depozite git folosind aceleași formate binare ca binarul canonic `git`. Obiectele sunt scrise în depozitul de obiecte libere (comprimate cu zlib), indexul este scris în format v2 (sau v3 când intrările necesită marcaje extinse; un index existent își păstrează versiunea), iar referințele sunt scrise ca fișiere libere. Datele scrise pot fi verificate cu `git fsck --strict`.

## Inițializarea unui depozit

```php
use Pitmaster\Pitmaster;

$repo = Pitmaster::init('/path/to/new-project');
```

Aceasta creează:
- `.git/objects/` (depozit de obiecte gol)
- `.git/refs/heads/` și `.git/refs/tags/` (directoare de referințe goale)
- `.git/HEAD` care conține `ref: refs/heads/main`
- `.git/hooks/`, `.git/info/exclude` și `.git/description`
- `.git/config` cu setările implicite

Transmiteți `'sha256'` ca al doilea argument pentru a crea un depozit SHA-256: `Pitmaster::init($path, 'sha256')`.

Configurația implicită nu setează `core.logallrefupdates`, așa că Pitmaster nu înregistrează nicio intrare reflog în depozitul pe care l-a creat până când nu setați acea cheie la `true` ([#8](https://github.com/inline0/pitmaster/issues/8)). Depozitele create de `git init` o au deja setată.

## Adăugarea fișierelor în zona de pregătire

### add

```php
$repo->add('src/Feature.php');
$repo->add('src/Feature.php', 'tests/FeatureTest.php', 'README.md');
```

Fiecare argument este o cale de fișier relativă la rădăcina arborelui de lucru. Directoarele și modelele glob nu sunt extinse, iar un fișier lipsă generează `RuntimeException`. Pentru fiecare fișier, Pitmaster:
1. Citește conținutul fișierului din arborele de lucru
2. Creează un obiect `Blob` și îi calculează hashul
3. Scrie blobul în `.git/objects/` (comprimat cu zlib)
4. Actualizează intrarea din index pentru calea respectivă cu noul hash și metadatele fișierului

### remove

```php
$repo->remove('old-file.txt');
$repo->remove('-r', 'src');
$repo->remove('--cached', 'old-file.txt');
```

Elimină căile urmărite din index și din arborele de lucru. Folosiți `--cached` pentru eliminarea doar din index sau `-r` / `--recursive` pentru directoare urmărite.

### mv

```php
$repo->mv('old-name.php', 'new-name.php');
```

Redenumește fișierul în arborele de lucru, elimină vechea intrare din index și o adaugă pe cea nouă.

## Crearea commiturilor

```php
$commitId = $repo->commit('Fix null pointer bug');
echo $commitId->hex;
```

Procesul de creare a commitului:

1. **Construirea arborelui din index**: lista plată de intrări din index este organizată într-o ierarhie de arbori (directoarele devin obiecte arbore, iar fișierele, referințe către bloburi). Fiecare arbore este scris în depozitul de obiecte.
2. **Determinarea părintelui**: commitul HEAD curent devine părintele. Commiturile rădăcină (primul commit dintr-un depozit) nu au părinți.
3. **Determinarea autorului**: preluat din variabilele de mediu `PITMASTER_AUTHOR_*` și `GIT_AUTHOR_*`, apoi din constantele `PITMASTER_AUTHOR_NAME`/`PITMASTER_AUTHOR_EMAIL`, apoi din `.git/config` (`user.name`/`user.email`), apoi din `Pitmaster <pitmaster@localhost>`. Identitatea autorului commitului este rezolvată în același mod.
4. **Scrierea obiectului commit**: conținutul commitului (`tree`, `parent`, `author`, `committer`, mesajul) este serializat, i se calculează hashul și este scris.
5. **Actualizarea HEAD**: referința ramurii către care indică HEAD este actualizată cu noul hash al commitului.

Hookurile rulează în jurul acestor pași (`pre-commit`, `prepare-commit-msg`, `commit-msg`, `post-commit`), cu excepția cazului în care handle-ul a fost deschis cu `['hooks' => false]`. `commit()` generează o eroare dacă lipsește mesajul, dacă arborele este neschimbat față de HEAD sau dacă indexul conține căi necomasate.

### Autor personalizat

```php
$author = 'Jane Smith <jane@example.com> 1700000000 +0000';
$commitId = $repo->commit('Add feature', $author);
```

## Verificarea cu fsck din git

Testele de integrare ale Pitmaster verifică obiectele scrise în raport cu `git fsck --strict`, iar dumneavoastră le puteți verifica și manual:

```bash
cd /path/to/repo
git fsck --strict
```

Dacă Pitmaster scrie un obiect invalid, `git fsck` va raporta imediat eroarea.

## Gestionarea ramurilor

### Crearea unei ramuri

```php
// Branch from HEAD
$repo->createBranch('feature/new-thing');

// Branch from a specific commit
$repo->createBranch('hotfix', $repo->resolve('v1.0.0'));
```

Creează un fișier de referință în `.git/refs/heads/<name>` care conține hashul commitului țintă.

### Ștergerea unei ramuri

```php
$repo->deleteBranch('feature/old');
```

### Schimbarea ramurilor

```php
$repo->checkout('feature/login');
```

Actualizează HEAD cu o referință simbolică către ramură, resetează indexul pentru a corespunde arborelui de la vârful ramurii și actualizează arborele de lucru.

Modificările locale din căile care diferă între cei doi arbori opresc operația de checkout cu o `RuntimeException`. Modificările necomise din toate celelalte fișiere urmărite sunt înlocuite cu conținutul ramurii, pe care git le-ar păstra ([#2](https://github.com/inline0/pitmaster/issues/2)). Comiteți-le sau puneți-le deoparte înainte de a schimba ramura.

### Verificarea dacă a fost comasată

```php
if ($repo->isBranchMerged('feature/done')) {
    $repo->deleteBranch('feature/done');
}
```

## Gestionarea etichetelor

### Etichetă simplă

```php
$repo->createLightweightTag('v1.0.0');                          // At HEAD
$repo->createLightweightTag('v0.9.0', $repo->resolve('HEAD~5'));
```

O etichetă simplă este doar o referință care indică direct către un commit.

### Etichetă adnotată

```php
$tagId = $repo->createTag('v2.0.0', 'Release version 2.0.0');
```

O etichetă adnotată creează un obiect etichetă (cu autorul etichetei, un mesaj și o țintă) și o referință către acesta. Identitatea autorului etichetei este cea a autorului commitului curent, rezolvată la fel ca autorul commitului.

### Autor personalizat al etichetei

```php
$tagId = $repo->createTag(
    'v2.0.0',
    'Release version 2.0.0',
    $repo->resolve('HEAD'),
    'Jane Smith <jane@example.com> 1700000000 +0000',
);
```

## Actualizarea directă a referințelor

```php
$repo->updateRef('refs/heads/main', $commitId);
$repo->updateRef('refs/tags/latest', $commitId);
```

## Scrierea directă a obiectelor

Pentru cazuri de utilizare avansate, puteți scrie obiecte fără a trece prin interfața de nivel înalt API.

### Scrierea unui blob

```php
use Pitmaster\Object\Blob;

$blob = Blob::fromContent('Hello, world!');
$id = $repo->writeObject($blob);
echo $id->hex;  // SHA-1 of "blob 13\0Hello, world!"
```

Într-un depozit SHA-256, transmiteți algoritmul: `Blob::fromContent('Hello, world!', 'sha256')`.

### Scrierea unui arbore

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

$entries = [
    new TreeEntry('100644', 'README.md', $blobId),
    new TreeEntry('40000', 'src', $subtreeId),
];

$tree = Tree::fromEntries($entries);
$id = $repo->writeObject($tree);
```

`Tree::fromEntries()` scrie intrările în ordinea furnizată și șirurile de mod în forma furnizată. Transmiteți-le în ordinea arborilor din git (după nume, comparând directoarele ca și cum numele lor s-ar termina în `/`) și scrieți modurile directoarelor ca `40000`: git stochează modurile arborilor fără zero-ul de la început, iar `git fsck --strict` respinge `040000`.

### Scrierea unui commit

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

$content = Commit::buildContent(
    tree: $treeId,
    parents: [$parentId],
    author: 'Name <email> 1700000000 +0000',
    committer: 'Name <email> 1700000000 +0000',
    message: "Commit message\n",
);

$commitId = ObjectId::compute(ObjectType::Commit, $content);
$commit = Commit::parse($content, $commitId);
$repo->writeObject($commit);
```

## Resetare

```php
// Soft: move HEAD only
$repo->reset('HEAD~1', 'soft');

// Mixed: move HEAD + reset index (default)
$repo->reset('HEAD~1');

// Hard: move HEAD + reset index + reset working tree
$repo->reset('HEAD~3', 'hard');
```

## Restaurare

```php
// Restore a file from the index into the worktree
$repo->restore('src/File.php');

// Restore the index version from HEAD (the worktree is left alone)
$repo->restore('src/File.php', staged: true);

// Restore from a specific commit
$repo->restore('src/File.php', 'HEAD~1');

// Restore both index and worktree from a specific commit
$repo->restore('src/File.php', 'HEAD~1', staged: true, worktree: true);
```

## Cherry-pick și revert

```php
// Apply a commit on the current branch
$newId = $repo->cherryPick('feature~1');

// Undo a commit
$revertId = $repo->revert('HEAD~2');
```

Ambele acceptă orice revizie acceptată de `resolve()`. Hashurile complete funcționează; cele abreviate nu.
