
# Penggabungan

Pitmaster melakukan penggabungan branch dengan penggabungan tiga arah, seperti `git merge`. Kelas ini mendeteksi peluang fast-forward, menghitung basis penggabungan, melakukan penggabungan pada tingkat tree dan konten, serta menghasilkan penanda konflik standar saat file mengalami konflik.

## Penggabungan dasar

```php
$result = $repo->merge('feature/login');

if ($result->clean) {
    echo "Merged successfully: {$result->commitId->hex}\n";
} else {
    echo "Conflicts:\n";
    foreach ($result->conflictPaths as $path) {
        echo "  {$path}\n";
    }
}
```

## MergeResult

Kelas `MergeResult` adalah objek nilai readonly:

```php
use Pitmaster\Merge\MergeResult;

$result->clean;           // bool: true if merge completed without conflicts
$result->commitId;        // ?ObjectId: the merge commit (null if conflicts)
$result->conflictPaths;   // array: list of file paths with conflicts
$result->mergedContents;  // array: path => content with conflict markers (if conflicts)
```

Saat penggabungan berhenti karena konflik, status konflik ditulis ke index dan working tree. Selesaikan file, `add()` file tersebut, lalu panggil `$repo->mergeContinue()`, atau panggil `$repo->mergeAbort()` untuk memulihkan keadaan sebelum penggabungan.

Sebelum menggabungkan, commit atau stash perubahan lokal: proses penggabungan membangun ulang working tree dari hasilnya dan menimpa perubahan yang belum di-commit pada file yang dilacak ([#2](https://github.com/inline0/pitmaster/issues/2)).

## Penggabungan fast-forward

Jika HEAD saat ini merupakan leluhur branch target, Pitmaster melakukan penggabungan fast-forward: HEAD cukup dipindahkan ke commit target tanpa membuat commit penggabungan.

```
Before:         After fast-forward:

A---B (main)    A---B---C---D (main)
     \
      C---D (feature)
```

```php
$result = $repo->merge('feature');
// $result->clean === true
// $result->commitId points to D
// No merge commit created
```

Kasus sebaliknya belum terdeteksi: menggabungkan branch yang sudah tercakup dalam HEAD akan membuat commit penggabungan baru dengan tree yang tidak berubah, alih-alih melaporkan "Already up to date" ([#6](https://github.com/inline0/pitmaster/issues/6)).

## Penggabungan tiga arah

Saat branch telah menyimpang, Pitmaster melakukan penggabungan tiga arah:

1. Temukan basis penggabungan (leluhur bersama terdekat)
2. Bandingkan basis dengan milik kita dan milik mereka pada tingkat tree
3. Untuk setiap file yang berubah di kedua branch, lakukan penggabungan pada tingkat konten
4. Buat commit penggabungan dengan dua induk

```
Before:              After merge:

      C---D (feature)         C---D
     /                       /     \
A---B---E---F (main)   A---B---E---F---G (main)
```

## Basis penggabungan

Basis penggabungan adalah leluhur bersama paling baru dari dua commit. Pitmaster menemukannya dengan menelusuri graf commit. Jika ada beberapa basis penggabungan (riwayat criss-cross), `merge()` menggabungkannya terlebih dahulu dan menggunakan hasilnya sebagai basis.

```php
$base = $repo->mergeBase(
    $repo->resolve('main'),
    $repo->resolve('feature/login'),
);

if ($base !== null) {
    echo "Common ancestor: {$base->hex}\n";
}
```

Untuk mengakses algoritme `MergeBase` secara langsung:

```php
use Pitmaster\Merge\MergeBase;

$finder = new MergeBase($repo->objectDatabase());
$base = $finder->find($commitA, $commitB);

// All merge bases (criss-cross histories can have more than one)
$bases = $finder->findAll($commitA, $commitB);

// Check ancestry
$isAncestor = $finder->isAncestor($oldCommit, $newCommit);
```

## Pemeriksaan penggabungan branch

```php
if ($repo->isBranchMerged('feature/done')) {
    // All commits in feature/done are reachable from default branch
    $repo->deleteBranch('feature/done');
}

// Check against a specific target
if ($repo->isBranchMerged('feature/done', 'develop')) {
    // Merged into develop
}
```

## Penanda konflik

Jika sebuah file diubah di kedua branch dan perubahan saling bertumpang tindih, Pitmaster menghasilkan penanda konflik standar. `merge()` memberi label pada sisi-sisinya sebagai `HEAD` dan nama branch hasil penggabungan:

```
<<<<<<< HEAD
    return $this->value + 1;
=======
    return $this->value * 2;
>>>>>>> feature
```

Kelas `ConflictMarker` membungkus dua versi dengan penanda:

```php
use Pitmaster\Merge\ConflictMarker;

$marked = ConflictMarker::mark($oursContent, $theirsContent, 'HEAD', 'feature');

// diff3 style, with the base section
$marked = ConflictMarker::mark($oursContent, $theirsContent, 'HEAD', 'feature', $baseContent, 'base');
```

## Penggabungan tiga arah pada tingkat konten

Kelas `ThreeWayMerge` menggabungkan konten blob (bukan file):

```php
use Pitmaster\Merge\ThreeWayMerge;

$result = ThreeWayMerge::merge($baseContent, $oursContent, $theirsContent, 'HEAD', 'feature');

$result['content'];   // merged text, with conflict markers if needed
$result['clean'];     // true when nothing conflicted
$result['conflicts']; // number of conflict regions
```

Kelas ini menerima tiga string (versi basis, versi kita, dan versi mereka) beserta label opsional, serta gaya konflik (`'merge'` sebagai default, atau `'diff3'` untuk menyertakan bagian basis).

## Strategi penggabungan

### Penggabungan rekursif

Strategi default (`$repo->merge($branch)`) menangani riwayat penggabungan criss-cross dengan beberapa basis penggabungan. CLI juga menerima `-s ort` dan `-s recursive` untuk strategi ini.

### Penggabungan Ours

Mencatat commit penggabungan yang mempertahankan tree HEAD tanpa perubahan. Berguna untuk menjaga riwayat branch tanpa menyertakan perubahan.

```php
$result = $repo->merge('feature/old', 'ours');
```

### Penggabungan Octopus

Menggabungkan dua branch atau lebih ke dalam satu commit penggabungan. Jika ada branch yang mengalami konflik, hasilnya dinyatakan tidak bersih tanpa menulis status penggabungan atau membuat commit.

```php
$result = $repo->mergeOctopus(['feature/a', 'feature/b', 'feature/c']);
```

Kelas `RecursiveMerge`, `OursMerge`, dan `OctopusMerge` di `Pitmaster\Merge` adalah helper tingkat konten; `Repository` mengimplementasikan sendiri strategi di atas.

## Rerere (gunakan kembali resolusi yang tercatat)

Pitmaster menyertakan kelas mandiri `Rerere` yang menyimpan resolusi dalam tata letak `rr-cache` milik git:

```php
use Pitmaster\Merge\Rerere;

$rerere = new Rerere($repo->gitDir());

if ($rerere->isEnabled()) {                     // rerere.enabled = true in .git/config
    $rerere->record($conflicted, $resolved);    // remember a resolution
    $resolved = $rerere->resolve($conflicted);  // replay it, or null
}
```

`merge()` tidak memanggilnya: resolusi dicatat dan diterapkan kembali hanya jika kode Anda melakukannya. `isEnabled()` mencari teks literal `rerere.enabled = true` di `.git/config` dan tidak mengenali bagian `[rerere]` yang ditulis oleh `git config rerere.enabled true` ([#20](https://github.com/inline0/pitmaster/issues/20)).

## Alur penggabungan pada tingkat tree

Proses penggabungan lengkap:

1. `MergeBase::findAll()` mencari leluhur bersama
2. Tree basis, milik kita, dan milik mereka dibandingkan path demi path
3. Untuk file yang hanya diubah di satu branch: gunakan perubahan tersebut
4. Untuk file yang diubah di kedua branch:
   - Jika keduanya berubah dengan cara yang sama: gunakan salah satunya (hasilnya sama)
   - Jika perubahannya berbeda: jalankan `ThreeWayMerge::merge()` pada konten
5. Jika semua penggabungan bersih: bangun tree hasil penggabungan dan buat commit penggabungan
6. Jika ada konflik: kembalikan `MergeResult` dengan `clean = false` dan path yang berkonflik

## Penggunaan CLI

```bash
./vendor/bin/pitmaster merge feature/login
```

Output jika berhasil:
```
Merge successful.
```

Output saat terjadi konflik:
```
CONFLICT in: src/File.php, src/Other.php
```
