Kenapa ada file penuh tanda kurung ini?
Kamu baru selesai push kode, lalu jalankan git pull atau git merge. Tiba-tiba terminal menampilkan:
CONFLICT (content): Merge conflict in config.php
Automatic merge failed; fix conflicts and then commit the result.
Kamu buka config.php dan mendapati isinya dipenuhi tanda aneh yang tidak pernah kamu lihat sebelumnya:
<<<<<<< HEAD
'debug' => true,
=======
'debug' => env('APP_DEBUG', false),
>>>>>>> fitur/teman
Perasaan pertama kali melihat ini hampir selalu sama: panik. Apa yang terjadi? Apa kamu sudah merusak repository? Apakah rekan tim akan marah?
Tenang. Konflik bukan error dan bukan kegagalan. Ini adalah cara Git memberitahumu: "Dua perubahan tumpang tindih di sini, aku tidak bisa memilih sendiri mana yang benar — keputusan ada di tanganmu." Terjadi di tim mana pun, di proyek mana pun, dan selalu bisa diselesaikan.
Artikel ini akan membekalimu alur tetap yang berlaku di semua skenario: membaca penanda, memilih versi, menyelesaikan dengan git add dan git commit, mundur dengan --abort saat butuh jalan keluar, plus cara memanfaatkan VS Code, mergetool, dan diff3 untuk konflik yang lebih rumit. Satu janji penting: setiap konflik selalu bisa di-undo. Kamu tidak bisa merusak apa pun secara permanen selama tahu --abort.
Apa Itu Merge Conflict dan Kenapa Terjadi
Merge conflict terjadi saat dua branch mengubah baris yang sama di satu file, atau satu branch mengedit file yang dihapus branch lain. Git tidak bisa memutuskan sendiri versi mana yang "benar", sehingga ia berhenti dan meminta keputusanmu.
Yang perlu dipahami: sebagian besar waktu Git menggabungkan perubahan secara otomatis tanpa masalah. Kalau kamu mengubah baris 10 dan temanmu mengubah baris 50 di file yang sama, Git bisa menggabungkan keduanya — tidak ada tumpang tindih. Konflik hanya muncul di area yang benar-benar tumpang tindih.
Contoh paling klasik: dua developer membuka config.php. Kamu mengubah baris 'debug' dari false menjadi true di branch main. Temanmu mengubah baris yang sama menjadi 'debug' => env('APP_DEBUG', false) di branch fitur/teman. Keduanya mengedit baris yang sama — Git tidak tahu mana yang harus dipertahankan.
Satu hal yang perlu diingat sejak awal: git merge --abort selalu membatalkan merge dan mengembalikan ke kondisi sebelumnya. Jadi tidak perlu takut untuk mencoba menyelesaikan. Satu catatan kecil: kalau ada perubahan yang belum di-commit saat merge dimulai, --abort bisa gagal mengembalikannya dengan sempurna. Pastikan working directory bersih atau sudah commit/stash dulu sebelum merge.
Membaca Conflict Markers: <<<<<<< dan Kawan-kawannya
Saat konflik terjadi, Git menyisipkan conflict markers ke dalam file. Inilah contohnya dari config.php:
<<<<<<< HEAD
'debug' => true,
=======
'debug' => env('APP_DEBUG', false),
>>>>>>> fitur/teman
Cara bacanya:
<<<<<<< HEAD— tanda pembuka. LabelHEADmenunjukkan versi dari branch yang sedang kamu aktif (ours/kita).- Antara
<<<<<<<dan=======— isi versi "ours": perubahan yang sudah ada di branch aktifmu. =======— pemisah antara dua versi.- Antara
=======dan>>>>>>>— isi versi "theirs": perubahan dari branch yang sedang di-merge. >>>>>>> fitur/teman— tanda penutup. Labelfitur/temanmenunjukkan asal versi lawan.
Label di baris <<<<<<< dan >>>>>>> menunjukkan ref atau commit asal, sehingga kamu langsung tahu versi mana yang berasal dari mana.
Sambil membuka file, jalankan git status untuk melihat daftar file berkonflik:
$ git status
On branch main
You have unmerged paths.
(fix conflicts and run "git commit")
Unmerged paths:
both modified: config.php
Status both modified adalah yang paling umum ditemui pemula. Artinya: kedua branch mengedit file yang sama.
Kalau ingin lebih detail, git ls-files -u menampilkan tiga versi index untuk file berkonflik: stage 1 (common ancestor/base), stage 2 (ours/HEAD), dan stage 3 (theirs/MERGE_HEAD). Berguna saat konflik kompleks dan kamu perlu melihat versi asal sebelum perubahan apapun.
Alur Inti: Selesaikan, Stage, Lanjutkan
Setelah memahami penandanya, penyelesaiannya mengikuti pola yang sama di semua skenario:
Langkah 1 — Kenali file berkonflik:
git status
Cari seksi "Unmerged paths" untuk tahu file mana yang perlu dibereskan.
Langkah 2 — Buka file, putuskan isi akhir:
Buka file tersebut di editor. Cari setiap blok <<<<<<< sampai >>>>>>>. Hapus semua penanda, dan pastikan isi yang tersisa adalah yang kamu inginkan — bisa salah satu versi, atau gabungan dari keduanya. Pastikan tidak ada satu pun <<<<<<<, =======, atau >>>>>>> yang tertinggal.
Langkah 3 — Stage file yang sudah resolved:
git add config.php
Perintah ini menandai file sebagai "resolved" di index Git — bukan sekadar menambah ke staging area seperti saat menyiapkan commit biasa. Ini langkah kunci yang sering terlewat. Kalau kamu lupa git add, Git akan menolak melanjutkan dengan pesan: "all conflicts fixed but you are still 'merging'."
Langkah 4 — Lanjutkan operasi:
Perintah lanjutan tergantung skenario:
| Skenario | Perintah lanjut |
|---|---|
git merge | git commit atau git merge --continue |
git pull (default merge) | git commit (sama seperti merge) |
git pull --rebase | git rebase --continue |
git rebase | git rebase --continue |
git cherry-pick | git cherry-pick --continue |
git revert | git revert --continue |
--continue hanya berfungsi setelah semua file berkonflik sudah di-git add. Kalau masih ada satu file yang belum di-stage, perintahnya tidak akan jalan.
Kenapa git commit dan --continue bisa digunakan bergantian di merge? Karena git merge --continue pada dasarnya memanggil git commit di balik layar — perbedaannya --continue memastikan ada operasi merge yang sedang berjalan sebelum melanjutkan.
Kalau kamu merasa salah atau bingung, hentikan segera:
git merge --abort
Semua file kembali seperti sebelum merge dimulai. Tidak ada yang hilang.
Memutuskan: Ours atau Theirs?
Di sinilah kebingungan terbesar pemula muncul. Apa sebenarnya "ours" dan "theirs"?
Saat git merge: orientasi normal
Ketika kamu menjalankan git merge fitur/teman dari branch main:
ours= branch yang sedang kamu aktif, yaitumain(HEAD).theirs= branch yang di-merge, yaitufitur/teman.
Penanda di file akan menampilkan <<<<<<< HEAD untuk versi ours dan >>>>>>> fitur/teman untuk versi theirs. Intuisinya sederhana: "ours = punyaku, theirs = punya orang lain."
Saat git rebase / git pull --rebase: orientasi TERBALIK
Ini poin nomor satu yang sering membingungkan dan menjadi sumber bug karena salah pilih versi.
Saat rebase, orientasi ours/theirs terbalik. Dokumentasi resmi Git menyatakan:
During
git rebaseandgit pull --rebase,oursandtheirsmay appear swapped;--oursgives the version from the branch the changes are rebased onto, while--theirsgives the version from the branch that holds your work.
Artinya secara praktis:
ours= branch tujuan rebase (misalnyamain), bukan kode yang sedang kamu kerjakan.theirs= commit pekerjaanmu yang sedang diputar ulang.
Jadi kalau kamu menjalankan git rebase main di branch fitur/login dan ingin tetap memakai kode kerjamu sendiri, kamu harus memilih --theirs, bukan --ours. Kebalikannya dari intuisi awal.
Cara aman: jangan pernah menebak. Selalu baca label di conflict markers atau cek git status sebelum memutuskan.
Perintah cepat memilih satu versi
Kalau kamu sudah yakin ingin mengambil salah satu versi secara utuh (tanpa edit manual), ada perintah singkat:
git checkout --ours config.php
git restore --ours config.php
Kedua perintah melakukan hal sama — git restore --ours adalah padanan modern yang diperkenalkan sejak Git 2.23 (2019), sementara git checkout --ours tetap berfungsi sebagai cara klasik.
Atau untuk versi branch lain:
git checkout --theirs config.php
git restore --theirs config.php
Poin kritis: perintah ini hanya berfungsi untuk file yang sedang berkonflik (unmerged paths). Kamu tidak bisa memakainya di luar kondisi konflik untuk "memilih versi tertentu dari suatu file." Setelah menjalankan perintah ini, tetap harus git add file-nya.
Jangan tertukar dengan -X ours / -X theirs
Ada perintah lain yang namanya serupa tapi beda fungsi:
git merge -X theirs fitur/teman
-X theirs (atau -X ours) adalah opsi strategi yang diberikan saat memulai merge. Ini memerintahkan Git untuk otomatis memihak salah satu sisi di semua area tumpang tindih — tanpa menampilkan konflik sama sekali.
Sedangkan git checkout --ours / git restore --theirs bekerja setelah konflik muncul, memilih satu versi per file dari yang sudah berkonflik. Keduanya berbeda momen dan beda mekanisme — jangan dicampuradukkan.
Menghadapi Konflik di Berbagai Skenario
Semua skenario konflik memakai mesin merge yang sama, sehingga penanda dan cara membacanya identik. Yang berbeda adalah perintah lanjutannya. Semua contoh di bawah memakai file config.php yang sama supaya kamu bisa fokus ke perintah, bukan konteks baru.
git merge <branch>
Ini skenario paling murni. Kamu menjalankan merge, konflik muncul, kamu resolve, lalu commit:
$ git switch main
$ git merge fitur/teman
Auto-merging config.php
CONFLICT (content): Merge conflict in config.php
Automatic merge failed; fix conflicts and then commit the result.
Buka config.php, hapus semua penanda, putuskan isi akhir, lalu:
$ git add config.php
$ git commit
Atau kalau mau membatalkan:
$ git merge --abort
git merge --continue juga bisa dipakai sebagai pengganti git commit — hasilnya sama.
git pull dan git pull --rebase
Secara default, git pull menjalankan fetch lalu merge. Jadi konfliknya sama persis dengan skenario merge di atas — setelah resolve, cukup git add dan git commit.
Yang perlu diperhatikan adalah git pull --rebase. Banyak tim yang mengaktifkan opsi ini (atau mengaturnya lewat git config --global pull.rebase true) supaya riwayat commit lebih bersih. Saat pull --rebase berkonflik, orientasi ours/theirs terbalik seperti yang sudah dijelaskan di bagian sebelumnya.
$ git pull --rebase origin main
Auto-merging config.php
CONFLICT (content): Merge conflict in config.php
error: could not apply abc1234... feat: ubah config
Resolve all conflicts manually, mark them as resolved with
"git add/rm <conflicted_files>", then run "git rebase --continue".
Resolve filenya, lalu:
$ git add config.php
$ git rebase --continue
Kalau rebase melibatkan beberapa commit, konflik bisa muncul lebih dari sekali — setiap kali kamu resolve dan --continue, rebase berpindah ke commit berikutnya. Kalau commit tertentu sudah tidak relevan, bisa dilewat dengan git rebase --skip.
git rebase main
Pola dasarnya sama dengan pull --rebase, tapi kamu yang menginisiasi:
$ git switch fitur/login
$ git rebase main
Konflik bisa muncul di beberapa commit secara berurutan. Resolve satu per satu, git rebase --continue setelah tiap resolusi. Kalau kamu sudah frustrasi dan ingin membatalkan seluruh rebase:
$ git rebase --abort
Semua commit kembali ke posisi semula sebelum rebase dimulai. Perlu diingat: --abort membatalkan seluruh rebase, bukan hanya commit yang sedang berkonflik.
git cherry-pick dan git revert
Skenario ini lebih ringkas karena biasanya hanya melibatkan satu commit.
Cherry-pick:
$ git cherry-pick abc1234
# konflik muncul
$ git add config.php
$ git cherry-pick --continue
Mundur: git cherry-pick --abort. Lewati commit ini: git cherry-pick --skip.
Revert:
$ git revert abc1234
# konflik muncul
$ git add config.php
$ git revert --continue
Mundur: git revert --abort.
git stash pop dan git stash apply
Skenario ini punya jebakan khas yang perlu diketahui. Ketika kamu git stash pop dan working tree sudah berubah sejak stash dibuat, konflik bisa muncul. Penandanya akan berbeda labelnya:
<<<<<<< Updated upstream
'debug' => true,
=======
'debug' => false,
>>>>>>> Stashed changes
Di sini, --theirs mengacu ke isi stash (Stashed changes), sementara --ours adalah versi yang sedang ada di working tree (Updated upstream).
Yang kritis: git stash pop tidak menghapus stash saat konflik terjadi. Normalnya pop = apply + drop (stash langsung terbuang). Tapi kalau apply menghasilkan konflik, drop tidak dilakukan — stash tetap tersimpan di daftar. Ini keamanan dari Git supaya pekerjaan lama tidak hilang begitu saja.
Setelah resolve dan git add, kamu harus menghapus stash secara manual:
$ git stash drop
Atau kalau ingin lebih hati-hati, gunakan git stash apply dulu (yang memang tidak pernah drop otomatis), cek hasilnya, baru git stash drop jika sudah yakin.
Konflik di Pull Request GitHub / Merge Request GitLab
Di GitHub, ketika branch PR kamu berkonflik dengan base branch (misalnya main), tombol "Merge pull request" dinonaktifkan dan muncul pesan: "This branch has conflicts that must be resolved."
Ada dua jalur penyelesaian.
Jalur 1 — Web editor (untuk konflik sederhana):
Klik "Resolve conflicts" di halaman PR. GitHub membuka editor web yang menampilkan file berkonflik dengan conflict markers. Edit langsung di sini, hapus penanda, pilih isi yang benar, lalu klik "Mark as resolved" dan "Commit merge." Tombol merge akan aktif kembali.
Jalur 2 — CLI lokal (untuk konflik rumit atau banyak file):
$ git fetch origin
$ git switch <branch-pr>
$ git merge origin/main
# resolve konflik di semua file
$ git add .
$ git commit
$ git push
Setelah push, tombol "Merge pull request" di GitHub akan aktif kembali.
GitLab punya pola yang sama dengan istilah "Merge request" alih-alih "Pull request." Konflik bisa diselesaikan di lokal lewat CLI, lalu push — atau lewat fitur rebase di GitLab yang bisa menjalankan rebase dari web.
Yang perlu diingat: tidak semua konflik bisa diselesaikan di web. Konflik yang melibatkan banyak file atau perubahan kompleks lebih baik ditangani di lokal di mana kamu punya akses penuh ke editor dan terminal.
Alat Bantu: VS Code, Mergetool, dan diff3
Menyelesaikan konflik secara manual dengan mengedit teks memang selalu bisa dilakukan. Tapi ada alat yang membuat prosesnya lebih cepat dan lebih aman.
VS Code — rekomendasi untuk pemula
VS Code mendeteksi conflict markers secara otomatis dan menampilkan CodeLens tepat di atas setiap blok konflik:
- Accept Current Change — ambil versi branch aktif (ours).
- Accept Incoming Change — ambil versi branch yang di-merge (theirs).
- Accept Both Changes — gabungkan keduanya secara berurutan.
- Compare Changes — tampilkan diff berdampingan.
Untuk konflik yang lebih rumit, VS Code punya 3-way merge editor yang bisa diakses lewat tombol "Resolve in Merge Editor." Panel ini menampilkan versi Incoming (kiri), Current (kanan), dan Result (bawah). Kamu bisa memilih perubahan lewat checkbox, dan Result ter-update secara real-time. Setelah yakin, klik "Complete Merge."
Merge editor ini aktif secara default di versi modern VS Code dan bekerja untuk konflik dari merge maupun rebase. Satu hal yang tidak boleh dilupakan: setelah selesai di VS Code, kamu tetap harus menjalankan git add dan git commit / --continue di terminal untuk menyelesaikan prosesnya.
git mergetool
git mergetool meluncurkan program GUI eksternal untuk menyelesaikan konflik. Beberapa tool populer: KDiff3, Meld, Beyond Compare, dan vimdiff.
Konfigurasi awal:
git config --global merge.tool meld
Kemudian setelah konflik muncul, jalankan:
git mergetool
Tool yang dipilih akan terbuka dengan empat panel: versi lokal (ours), versi remote (theirs), versi base (common ancestor), dan versi hasil merge. Git men-supply variabel lingkungan $LOCAL, $REMOTE, $BASE, dan $MERGED ke tool yang dijalankan.
Setelah selesai menyimpan di mergetool, tetap jalankan git add dan git commit atau --continue — sama seperti resolusi manual.
diff3 dan zdiff3: melihat versi base
Secara default, conflict markers hanya menampilkan dua versi: ours dan theirs. Ada kalanya kamu perlu melihat versi asal (common ancestor) untuk memahami dari mana masing-masing versi bercabang. Di sinilah diff3 berguna.
git config --global merge.conflictstyle diff3
Setelah diaktifkan, conflict markers akan menambahkan blok dengan penanda ||||||| yang berisi versi base sebelum =======:
<<<<<<< HEAD
'debug' => true,
||||||| merged common ancestor
'debug' => false,
=======
'debug' => env('APP_DEBUG', false),
>>>>>>> fitur/teman
Sekarang kamu bisa melihat bahwa versi asalnya false, kamu mengubah ke true, dan temanmu mengubah ke env('APP_DEBUG', false). Dengan konteks tambahan ini, keputusan jadi lebih mudah.
Ada juga zdiff3 (tersedia sejak Git 2.35) yang mirip diff3 tetapi memangkas baris identik di awal/akhir area konflik sehingga tampilannya lebih ringkas:
git config --global merge.conflictstyle zdiff3
Poin penting: merge.conflictStyle hanya mengubah cara konflik ditampilkan, bukan hasil merge. Git tetap menghitung merge dengan cara yang sama — yang berubah hanyalah tampilan penandanya di dalam file. Jangan khawatir, mengaktifkan diff3 atau zdiff3 tidak akan mengubah apapun dari hasil merge-mu.
Kesalahan Umum dan Solusinya
Berikut beberapa kesalahan yang paling sering menimpa developer pemula saat menghadapi konflik:
| Kesalahan | Penyebab | Solusi |
|---|---|---|
Commit berisi <<<<<<< | Lupa menghapus conflict markers | Cari <<<<<<< di file sebelum commit; jalankan build/test |
git status menolak lanjut | Lupa git add setelah mengedit file | Jalankan git add <file> dulu |
| Salah ambil versi saat rebase | Orientasi ours/theirs terbalik | Periksa label di conflict markers; ingat rebase membalik orientasi |
git push --force ke branch bersama | Panik, ingin "menghindari" konflik | Jangan; pakai git pull + resolve, bukan force-push |
Stash tetap ada setelah pop + konflik | pop tidak auto-drop saat konflik | Jalankan git stash drop manual setelah resolve |
| File tiba-tiba hilang atau dikembalikan | Konflik delete/modify | Pahami deleted by us/thems di git status; git add untuk kembalikan, git rm untuk hapus |
| Perubahan rekan tiba-tiba hilang | Paksa satu sisi (-X ours) tanpa cek | Hanya pakai -X kalau memang yakin; cek dulu |
| Kode error setelah resolve | Isi resolve secara logik salah | Jalankan build/test setelah resolve, bukan hanya cek syntax |
Praktik Terbaik + Penutup
Beberapa kebiasaan yang akan mengurangi frekuensi dan dampak konflik:
- Pull rutin dan sering. Branch yang tidak pernah di-pull selama seminggu akan menghasilkan konflik yang lebih besar. Kalau tim menggunakan
pull --rebase, aktifkanpull.rebase truedi config global. - Branch kecil dan pendek. Semakin kecil branch, semakin kecil kemungkinan tumpang tindih. Idealnya satu branch = satu perubahan logis.
- PR/MR kecil. Review lebih mudah, konflik lebih jarang, dan resolve lebih cepat.
- Komunikasi tim. Kalau tahu ada file yang sedang dikerjakan orang lain, koordinasi dulu. Hindari touch file yang sama secara bersamaan tanpa alasan.
- Commit/stash perubahan lokal sebelum merge. Ini memastikan
--abortbisa mengembalikan working directory dengan sempurna. - Aktifkan
diff3atauzdiff3. Melihat versi base sangat membantu saat konflik kompleks. - Pakai VS Code atau mergetool untuk konflik yang melibatkan banyak perubahan — jangan hanya mengandalkan teks editor polos.
- Berlatih di playground. Buat repo percobaan, sengaja bikin konflik, coba resolve dengan berbagai cara, lalu
--abortuntuk latihan mundur.
Alur penyelesaian konflik bisa diringkas begini: baca penanda → putuskan isi → git add → lanjutkan. Kalau bingung atau salah pilih, mundur dengan --abort — kamu selalu bisa mulai lagi dari awal.
Untuk mendalami lebih jauh, Pro Git tersedia gratis dengan versi bahasa Indonesia, dokumentasi resmi ada di git-scm.com/docs, dan GitHub Docs tentang merge conflicts memberikan panduan langkah demi langkah yang berguna untuk referensi. Selamat berlatih — konflik tidak lagi harus menakutkan.
Fitur komentar belum diaktifkan oleh administrator.