
# Menulis ke Repositori

Pitmaster menulis repositori git menggunakan format biner yang sama dengan biner `git` kanonis. Objek ditulis ke penyimpanan objek lepas (terkompresi zlib), index ditulis dalam format v2 (atau v3 jika entri memerlukan flag tambahan; index yang sudah ada mempertahankan versinya), dan ref ditulis sebagai file lepas. Data yang ditulis dapat diverifikasi dengan `git fsck --strict`.

## Menginisialisasi repositori

```php
use Pitmaster\Pitmaster;

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

Ini membuat:
- `.git/objects/` (penyimpanan objek kosong)
- `.git/refs/heads/` dan `.git/refs/tags/` (direktori ref kosong)
- `.git/HEAD` yang berisi `ref: refs/heads/main`
- `.git/hooks/`, `.git/info/exclude`, dan `.git/description`
- `.git/config` dengan pengaturan default

Berikan `'sha256'` sebagai argumen kedua untuk membuat repositori SHA-256: `Pitmaster::init($path, 'sha256')`.

Konfigurasi default tidak menetapkan `core.logallrefupdates`, sehingga Pitmaster tidak mencatat entri reflog di repositori yang dibuatnya sampai Anda menetapkan kunci tersebut ke `true` ([#8](https://github.com/inline0/pitmaster/issues/8)). Repositori yang dibuat oleh `git init` sudah menetapkannya.

## Memasukkan file ke staging

### add

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

Setiap argumen adalah path file yang relatif terhadap root working tree. Direktori dan pola glob tidak diperluas, dan file yang tidak ditemukan akan menimbulkan `RuntimeException`. Untuk setiap file, Pitmaster:
1. Membaca konten file dari working tree
2. Membuat objek `Blob` dan menghitung hash-nya
3. Menulis blob ke `.git/objects/` (terkompresi zlib)
4. Memperbarui entri index untuk path tersebut dengan hash dan metadata file yang baru

### remove

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

Menghapus path yang dilacak dari index dan working tree. Gunakan `--cached` untuk menghapus dari index saja, atau `-r` / `--recursive` untuk direktori yang dilacak.

### mv

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

Mengganti nama file di working tree, menghapus entri index lama, lalu menambahkan entri baru.

## Membuat commit

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

Proses commit:

1. **Bangun tree dari index**: daftar datar entri index disusun menjadi hierarki tree (direktori menjadi objek tree, file menjadi referensi blob). Setiap tree ditulis ke penyimpanan objek.
2. **Tentukan induk**: commit HEAD saat ini menjadi induknya. Commit root (commit pertama di repo) tidak memiliki induk.
3. **Tentukan author**: diambil dari variabel lingkungan `PITMASTER_AUTHOR_*` dan `GIT_AUTHOR_*`, lalu konstanta `PITMASTER_AUTHOR_NAME`/`PITMASTER_AUTHOR_EMAIL`, kemudian `.git/config` (`user.name`/`user.email`), lalu `Pitmaster <pitmaster@localhost>`. Identitas committer ditentukan dengan cara yang sama.
4. **Tulis objek commit**: konten commit (`tree`, `parent`, `author`, `committer`, pesan) diserialisasi, di-hash, lalu ditulis.
5. **Perbarui HEAD**: ref branch yang ditunjuk HEAD diperbarui ke hash commit baru.

Hook dijalankan di sekitar langkah-langkah ini (`pre-commit`, `prepare-commit-msg`, `commit-msg`, `post-commit`), kecuali jika handle dibuka dengan `['hooks' => false]`. `commit()` menimbulkan error jika pesan tidak ada, tree tidak berubah dari HEAD, atau index memiliki path yang belum digabung.

### Author khusus

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

## Memverifikasi dengan git fsck

Cakupan integrasi Pitmaster memeriksa objek yang ditulis dengan `git fsck --strict`, dan Anda dapat memverifikasi hal yang sama secara manual:

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

Jika Pitmaster menulis objek yang tidak valid, `git fsck` akan segera melaporkan kesalahan tersebut.

## Mengelola branch

### Membuat branch

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

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

Membuat file ref di `.git/refs/heads/<name>` yang berisi hash commit target.

### Menghapus branch

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

### Beralih branch

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

Memperbarui HEAD menjadi ref simbolis yang menunjuk ke branch, mengatur ulang index agar sesuai dengan tree ujung branch, lalu memperbarui working tree.

Perubahan lokal pada path yang berbeda di antara kedua tree akan menghentikan checkout dengan `RuntimeException`. Perubahan yang belum di-commit pada file terlacak lainnya akan diganti dengan konten branch jika git akan mempertahankannya ([#2](https://github.com/inline0/pitmaster/issues/2)). Commit atau stash perubahan tersebut sebelum beralih.

### Memeriksa apakah sudah digabung

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

## Mengelola tag

### Tag ringan

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

Tag ringan hanyalah ref yang langsung menunjuk ke commit.

### Tag beranotasi

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

Tag beranotasi membuat objek tag (dengan pembuat tag, pesan, dan target) serta ref yang menunjuk ke objek itu. Pembuat tag adalah identitas committer saat ini, yang ditentukan seperti author commit.

### Pembuat tag khusus

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

## Memperbarui ref secara langsung

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

## Menulis objek secara langsung

Untuk kasus penggunaan lanjutan, Anda dapat menulis objek tanpa melalui API tingkat tinggi.

### Menulis 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!"
```

Dalam repositori SHA-256, jalankan algoritmanya: `Blob::fromContent('Hello, world!', 'sha256')`.

### Menulis tree

```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()` menulis entri sesuai urutan yang diberikan dan string mode persis seperti yang diberikan. Berikan entri dalam urutan tree git (berdasarkan nama, dengan direktori dibandingkan seolah namanya diakhiri `/`) dan tulis mode direktori sebagai `40000`: git menyimpan mode tree tanpa nol di depan, dan `git fsck --strict` menolak `040000`.

### Menulis 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);
```

## Reset

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

## Restore

```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 dan revert

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

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

Keduanya menerima revisi apa pun yang diterima `resolve()`. Hash lengkap dapat digunakan; hash singkat tidak.
