Seluruh merge sejauh ini berjalan mulus karena kita sengaja menjaga kedua sisi bekerja pada berkas yang berbeda. Proyek nyata tidak seberuntung itu: dua orang mengubah baris yang sama pada berkas yang sama, dan mesin penggabung tiga arah mendadak tidak punya jawaban. Git tidak menebak, tidak memilih juara — ia menghentikan merge di tengah jalan dan menyerahkan keputusan kepada kita. Kejadian itu bernama merge conflict. Chapter ini mereproduksi konflik sungguhan dari nol, lalu membedah keluarannya baris demi baris: pesan kegagalan, keadaan git status, dan isi berkas yang penuh penanda <<<<<<< sampai >>>>>>>.

Membuat Dua Sisi Bersenggol #

Konflik butuh satu bahan: baris yang sama disunting berbeda di kedua sisi. Repository latihan menyiapkan persis itu pada README.md, sementara satu berkas lain sengaja dibuat aman:

bash
mkdir latihan-konflik && cd latihan-konflik
git init -b main
git config user.name "Budi Santoso"
git config user.email "[email protected]"
printf '# Toko Budi\n\nKontak: [email protected]\n' > README.md
git add README.md
git commit -m "Buat README toko"
git switch -c fitur-kontak
printf '# Toko Budi\n\nKontak: [email protected]\n' > README.md
git add README.md
git commit -m "Ganti kontak ke layanan pelanggan"
printf '# Panduan\n1. pilih produk\n' > panduan.md
git add panduan.md
git commit -m "Tambah panduan belanja"
git switch main
printf '# Toko Budi\n\nKontak: [email protected]\n' > README.md
git add README.md
git commit -m "Ganti kontak ke tim penjualan"
git merge fitur-kontak

Output yang Dihasilkan (perintah git merge fitur-kontak):

text
Auto-merging README.md
CONFLICT (content): Merge conflict in README.md
Automatic merge failed; fix conflicts and then commit the result.

Tiga baris itu menceritakan kegagalan yang rapi. Mari bedah satu per satu:

  1. Baris 1 (Auto-merging README.md) — Git tetap mencoba menggabungkan README.md secara otomatis. “Auto-merging” berarti mesin tiga arah menyisir berkas ini, berhasil pada baris-baris yang tidak bersenggol, lalu menyerah pada satu titik tabrakan.
  2. Baris 2 (CONFLICT (content): ...) — penanda jenis tabrakan: konflik isi (content) pada README.md. Kata CONFLICT adalah alarm-nya; teks dalam kurung akan berbeda untuk jenis konflik lain, seperti kita lihat nanti di chapter Menyelesaikan Merge Conflicts .
  3. Baris 3 (Automatic merge failed; ...) — vonis akhirnya: penggabungan otomatis gagal, perbaiki konfliknya, lalu jalankan commit hasilnya. Perintah ini keluar dengan kode gagal (bukan nol) — shell kita diam-diam mencatatnya.

Keadaan Repository Saat Konflik #

Merge yang gagal meninggalkan repository di keadaan khusus: setengah tergabung. git status membaca kondisi itu dengan jujur:

bash
git status

Output yang Dihasilkan:

text
On branch main
You have unmerged paths.
  (fix conflicts and run "git commit")
  (use "git merge --abort" to abort the merge)

Changes to be committed:
	new file:   panduan.md

Unmerged paths:
  (use "git add <file>..." to mark resolution)
	both modified:   README.md

Empat bagian keluaran ini wajib dibaca berurutan:

  1. On branch main + You have unmerged paths. — kita masih di main, dan merge belum selesai: ada jalur berkas yang belum punya hasil akhir. Dua petunjuk di bawahnya adalah pintu darurat: git commit untuk menuntaskan, atau git merge --abort untuk membatalkan seluruhnya.
  2. Changes to be committed: dengan new file: panduan.md — bukti penting yang sering terlewat: berkas yang tidak konflik sudah selesai digabung dan tertinggal di staging area. Panduan belanja dari sisi fitur sudah masuk; hanya README.md yang macet.
  3. Unmerged paths: dengan both modified: README.md — status khas konflik isi: kedua sisi (both) mengubah (modified) berkas yang sama. Di sinilah keputusan menunggu.
  4. Tab tunggal sebelum new file: dan both modified: adalah format asli git status — bagian tabrakan ditandai lewat status berkas, bukan teks khusus.

Isi Berkas dengan Penanda Konflik #

Tabrakan itu sendiri terpahat langsung di dalam berkas. Git menulis kedua versi yang bersenggol berdampingan, dipisah tiga penanda:

bash
cat README.md

Output yang Dihasilkan:

text
# Toko Budi

<<<<<<< HEAD
Kontak: [email protected]
=======
Kontak: [email protected]
>>>>>>> fitur-kontak

Blok penanda itu punya anatomi tetap:

  • <<<<<<< HEAD — pembuka sisi kita: versi pada branch aktif saat merge dijalankan (main), identik dengan alias --ours yang kita pakai nanti.
  • Baris di bawahnya sampai ======= — isi versi kita: Kontak: [email protected].
  • ======= — garis pembatas kedua sisi, ketujuh tanda sama dengan.
  • Baris dari ======= sampai >>>>>>> — isi versi seberang: Kontak: [email protected].
  • >>>>>>> fitur-kontak — penutup sisi seberang: versi dari branch yang digabungkan, alias --theirs.

Baris pertama berkas (# Toko Budi) sama sekali tidak terkena penanda — hanya baris yang benar-benar bersenggol yang dikepung. Penanda ini bukan kode program: ia catatan sementara yang wajib kita hapus saat menyelesaikan konflik.

Mengapa Git Berhenti, Bukan Menebak #

Kembali ke meja tiga arah dari chapter Three-Way Merge : Git menghadapi tiga versi README.md — versi dasar (titik percabangan, [email protected]), versi main (penjualan@), dan versi fitur-kontak (cs@). Karena ketiganya berbeda pada baris yang sama, tidak ada satu pun sisi yang bisa dinyatakan “tetap tidak berubah” — pola yang biasa dipakai Git untuk memilih pemenang secara aman. Menebak berarti risiko membuang pekerjaan orang; berhenti berarti pekerjaan tidak ada yang hilang.

Pertanyaan itu dijawab lewat empat langkah tenang: baca status, sunting berkas, git add, lalu commit. Alurnya lengkap dengan keluaran nyata ada di chapter berikutnya: Menyelesaikan Merge Conflicts . Konflik yang dijelaskan Pro Git secara resmi bisa dibaca di git-scm.com/book/en/v2/Git-Branching-Basic-Branching-and-Merging .