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
$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:
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).
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)
$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).
Penggabungan tiga arah
Saat branch telah menyimpang, Pitmaster melakukan penggabungan tiga arah:
- Temukan basis penggabungan (leluhur bersama terdekat)
- Bandingkan basis dengan milik kita dan milik mereka pada tingkat tree
- Untuk setiap file yang berubah di kedua branch, lakukan penggabungan pada tingkat konten
- 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.
$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:
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
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:
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):
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.
$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.
$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:
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).
Alur penggabungan pada tingkat tree
Proses penggabungan lengkap:
MergeBase::findAll()mencari leluhur bersama- Tree basis, milik kita, dan milik mereka dibandingkan path demi path
- Untuk file yang hanya diubah di satu branch: gunakan perubahan tersebut
- 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
- Jika semua penggabungan bersih: bangun tree hasil penggabungan dan buat commit penggabungan
- Jika ada konflik: kembalikan
MergeResultdenganclean = falsedan path yang berkonflik
Penggunaan CLI
./vendor/bin/pitmaster merge feature/login
Output jika berhasil:
Merge successful.
Output saat terjadi konflik:
CONFLICT in: src/File.php, src/Other.php