Web Artisan
Chapter 03 · Git

Branching, Merge
& Konflik

Kekuatan sejati Git muncul saat banyak orang bekerja sekaligus tanpa saling menimpa. Chapter ini adalah satu busur utuh: memisah jalur dengan branch, menyatukannya dengan merge, lalu menyelesaikan konflik yang muncul saat dua jalur bertabrakan.

Branch & mergeResolusi konflik~22 menit baca

Sampai Chapter 2, history kita satu garis lurus. Tapi tim nyata tidak bekerja berbaris satu per satu, mereka menggarap beberapa fitur sekaligus. Chapter ini menutup busur “bekerja paralel lalu menyatukan”: branch membuka jalur terpisah, merge menjahitnya kembali, dan ketika dua jalur menyentuh baris yang sama, konflik adalah momen Git memintamu memutuskan. Tiga section ini sengaja berurutan karena konflik hanya masuk akal setelah kamu paham bagaimana branch dan merge bekerja.

01

Branch: Ruang Kerja Paralel

Branch adalah pointer ringan ke commit

Branch bukan salinan kode, melainkan pointer ringan yang menunjuk ke satu commit, dan itulah kenapa Git memberanikan kita membuatnya sesering apa pun.

Di Chapter 2 kita melihat histori sebagai rantai commit yang saling menunjuk ke parent-nya. Branch adalah lapisan di atas rantai itu: sebuah nama yang menyimpan satu hash commit. Tidak ada folder baru, tidak ada penggandaan file. Saat kamu menjalankan git branch feature/login, Git hanya menulis satu baris berisi hash ke dalam .git/refs/heads/. Itu sebabnya membuat branch terasa instan, bahkan pada repo dengan ratusan ribu commit.

Yang membuat branch terasa “hidup” adalah HEAD. HEAD adalah pointer ke branch yang sedang aktif, bukan langsung ke commit. Saat kamu commit, Git menambah commit baru lalu menggeser pointer branch yang ditunjuk HEAD untuk menunjuk commit baru itu. Branch lain tidak bergerak. Inilah mekanisme isolasi: pekerjaan di feature/login tidak menyentuh main sampai kamu memutuskan menggabungkannya.

flowchart LR
  C1["c1"] --> C2["c2"]
  C2 --> C3["c3 main"]
  C2 --> C4["c4 feature"]
  HEAD(["HEAD"]) -.-> C4

Branch sebagai pointer. main menunjuk c3, feature menunjuk c4, dan HEAD menandai feature sebagai branch aktif.

Branch adalah pointer ringan ke commit main feature/login HEAD Tiap commit menunjuk parent-nya; branch hanya menyimpan satu hash, HEAD menandai branch aktif.
Branch sebagai pointer. main dan feature menunjuk commit berbeda; HEAD menandai branch aktif.

Di proyek skincare-backend, alur ini sangat praktis. Misalnya main memuat kode yang sudah jalan di produksi, sementara kamu sedang menggarap endpoint checkout. Kamu buat branch feature/checkout, bekerja bebas di sana, dan main tetap utuh siap dirilis kapan saja tanpa terkontaminasi kode setengah jadi.

🌿Analogi: cabang sungai

Branch seperti cabang sungai yang memisah dari aliran utama, mengalir sendiri sebentar, lalu nanti bisa bertemu kembali ke aliran induk lewat merge.

Karena branch sangat murah, tim mengandalkan konvensi penamaan agar jalur kerja tetap terbaca. Nama branch yang konsisten membuat git branch dan daftar PR di hosting langsung bercerita tentang jenis pekerjaannya.

feature/<ringkas>

Fitur baru, mis. feature/checkout. Lahir dari main, hidup pendek.

fix/<gejala>

Perbaikan bug, mis. fix/negative-price. Fokus satu masalah.

hotfix/<versi>

Patch darurat dari tag rilis, mis. hotfix/1.2.1. Dibahas di Chapter 6.

Untuk berpindah branch, perintah modern adalah git switch. Versi lama memakai git checkout yang memikul peran ganda (pindah branch sekaligus memulihkan file), sehingga Git memisahnya menjadi git switch dan git restore. git checkout masih ada dan berfungsi, tapi untuk berpindah branch sebaiknya pakai git switch agar niat kode lebih jelas.

Terminal
# membuat dan langsung pindah ke branch baru git switch -c feature/login # ... edit file, lalu: git add . git commit -m "feat: tambah handler login" # kembali ke main; commit feature/login tidak terbawa ke sini git switch main git log --oneline # commit login tidak terlihat di main

git switch -c feature/login setara dengan git branch feature/login lalu git switch feature/login dalam satu langkah, jadi kamu langsung berada di ruang kerja baru.

🌉Jembatan: dari menyalin folder ke branch

Kebiasaan menyalin folder project jadi project-eksperimen lalu bingung mana yang terbaru, digantikan branch resmi: satu repo, banyak garis kerja, dan Git yang menjaga mana yang aktif.

📝Branch itu sangat murah

Sebuah ref branch hanya menyimpan satu hash (sekitar 41 byte di disk), jadi jangan ragu membuat branch untuk tiap fitur, percobaan, atau perbaikan kecil. Sebagai imbalannya, jaga umurnya pendek, makin lama branch hidup, makin besar jurang dengan main dan makin sering konflik.

02

Merge: Menggabungkan Branch

Fast-forward versus three-way merge

Merge adalah cara Git menyatukan pekerjaan dari satu branch ke branch lain, dan hasilnya bergantung pada apakah base sudah bergerak sejak branch dibuat.

Setelah pekerjaan di feature/login selesai dan teruji, kamu ingin perubahannya masuk ke main. Caranya: pindah ke branch tujuan, lalu jalankan git merge. Git punya dua strategi yang dipilih otomatis tergantung bentuk histori, dan memahami keduanya membuat histori proyekmu lebih mudah dibaca.

Kasus pertama adalah fast-forward. Ini terjadi bila main tidak menerima commit baru sejak feature/login dipisah, sehingga commit main masih merupakan leluhur (ancestor) dari ujung feature. Karena tidak ada yang perlu didamaikan, Git cukup menggeser pointer main maju ke commit terakhir feature. Tidak ada commit baru yang dibuat; histori tetap satu garis lurus.

Kasus kedua adalah three-way merge. Ini terjadi bila kedua branch sama-sama maju: ada commit baru di main dan ada commit baru di feature. Git tidak bisa sekadar menggeser pointer karena keduanya menyimpang. Git mengambil tiga titik (ujung kedua branch dan commit leluhur bersama), menggabungkannya, lalu membuat merge commit yang istimewa karena punya dua parent.

gitGraph
  commit id: "c1"
  commit id: "c2"
  branch feature/login
  checkout feature/login
  commit id: "f1"
  commit id: "f2"
  checkout main
  commit id: "m1"
  merge feature/login id: "merge"

Three-way merge. Karena main maju dengan m1 dan feature dengan f1, f2, Git membuat merge commit berisi dua parent.

📄Analogi: menggabungkan dua revisi dokumen

Bayangkan dua orang mengedit salinan dokumen yang sama dari titik awal yang identik. Three-way merge adalah proses menyatukan kedua revisi dengan melihat naskah asli sebagai acuan, bukan menimpa salah satunya.

Terminal
# pindah ke branch tujuan dulu git switch main # fast-forward bila main belum bergerak sejak feature dibuat git merge feature/login # Updating a1b2c3d..e4f5g6h # Fast-forward # memaksa merge commit walau sebenarnya bisa fast-forward git merge --no-ff feature/login

Kapan memilih --no-ff? Flag ini memaksa Git membuat merge commit meski fast-forward sebenarnya mungkin. Banyak tim memakai ini agar serangkaian commit satu fitur tetap tampak sebagai satu gugus di histori.

Fast-forward (default)
  • Tanpa merge commit, histori tetap lurus.
  • Asal feature tidak menonjol sebagai grup.
  • Cocok untuk perbaikan kecil satu commit.
--no-ff (merge commit dipaksa)
  • Selalu membuat merge commit penanda fitur.
  • git log —graph menunjukkan gugus commit per fitur.
  • Banyak tim memakainya saat merge feature ke main.
💡--no-ff menjaga jejak feature

Pakai git merge —no-ff untuk feature branch agar grup commit-nya tetap tampak utuh di histori; git log —oneline —graph akan menunjukkan struktur cabangnya dengan jelas.

📝Merge bisa memicu konflik

Saat kedua sisi mengubah baris yang sama, three-way merge tidak bisa otomatis dan Git menandainya sebagai conflict. Cara membaca dan menyelesaikannya dibahas tuntas di section berikut.

03

Merge Conflict

Saat Git tidak bisa menggabungkan otomatis

Merge conflict bukan tanda ada yang rusak, melainkan momen Git jujur bahwa ia tidak punya cukup informasi untuk memilih gabungan yang benar, dan menyerahkan keputusan itu kepadamu.

Konflik muncul ketika dua branch mengubah baris yang sama pada file yang sama, atau satu sisi mengubah file sementara sisi lain menghapusnya. Untuk perubahan yang menyentuh baris berbeda, Git menggabungkan otomatis tanpa kamu sadari. Hanya saat dua sisi bertabrakan di baris yang persis sama, Git berhenti, menandai file, dan meminta penyelesaian manual.

🌉Jembatan: dua orang mengedit paragraf yang sama

Sama seperti dua rekan yang menulis ulang paragraf yang identik di dokumen bersama lalu harus menentukan versi final, dua branch yang mengubah baris yang sama memaksamu memilih atau memadukan keduanya secara sadar.

Saat konflik terjadi, Git menyisipkan conflict marker ke dalam file. Ada tiga penanda: kepala <<<<<<< menandai awal versi branch saat ini (current), pemisah ======= membatasi kedua versi, dan ekor >>>>>>> menutup versi yang masuk (incoming). Tugasmu mengganti seluruh blok bertanda ini dengan versi final yang benar.

internal/order/service.go (saat konflik)
func calcTotal(items []Item) int64 { <<<<<<< HEAD var total PriceRupiah for _, it := range items { total += it.Price * int64(it.Qty) } ======= var total int64 for _, it := range items { total += it.Subtotal } >>>>>>> feature/discount return int64(total) }

Setelah membaca kedua sisi, kamu menulis ulang blok itu menjadi satu versi yang menggabungkan maksud keduanya, lalu menghapus semua marker. Hasil resolusi bisa berupa gabungan ide dari kedua branch, bukan sekadar memilih salah satu mentah-mentah.

internal/order/service.go (setelah resolve)
func calcTotal(items []Item) PriceRupiah { var total PriceRupiah for _, it := range items { total += it.Price*int64(it.Qty) - it.Discount } return total }

Begitu file beres, kamu menandainya selesai dengan git add <file>, lalu menuntaskan merge dengan git commit. Bila ternyata situasinya terlalu rumit dan kamu ingin mundur, git merge --abort mengembalikan working tree ke kondisi sebelum merge dimulai, seolah merge tak pernah terjadi.

Buat konflik sengaja

Di main ubah satu baris fungsi, commit. Buat branch dari commit sebelumnya, ubah baris yang sama secara berbeda, commit juga.

Coba merge

Kembali ke main lalu jalankan git merge nama-branch; Git melaporkan CONFLICT dan menyisipkan marker ke file.

Baca kedua sisi

Buka file, pahami current (HEAD) dan incoming, lalu tulis ulang blok menjadi versi final tanpa menyisakan marker.

Tandai selesai

Jalankan git add file untuk menyatakan konflik teratasi, lalu git commit untuk membuat merge commit.

Verifikasi

Jalankan test dan build (go test ./…) untuk memastikan hasil gabungan benar-benar berjalan, bukan hanya bebas marker.

⚠️Jangan asal pilih satu sisi

Memilih satu sisi tanpa membaca konteks bisa membuang logika penting dari branch lain. Selalu baca kedua versi, dan setelah resolve jalankan test, karena file yang lolos kompilasi belum tentu benar secara logika.

💡Kurangi konflik dengan integrasi sering

Cara paling ampuh menghindari konflik besar bukan jago resolve, melainkan jarang menumpuknya: tarik perubahan main ke branch-mu secara rutin dan jaga branch tetap pendek. Konflik kecil yang sering jauh lebih murah daripada satu konflik raksasa di akhir.

04

Ringkasan

Dari satu garis lurus ke kerja paralel yang aman

Branch, merge, dan konflik adalah satu busur: memisah jalur, menyatukannya, dan menengahi saat keduanya bertabrakan.

Kamu kini bisa bekerja paralel tanpa takut. Branch adalah pointer murah yang mengisolasi pekerjaan, jaga umurnya pendek dan namai dengan konvensi. Merge menyatukannya, fast-forward saat linear, merge commit saat menyimpang, dengan --no-ff untuk menjaga jejak fitur. Dan konflik bukan kegagalan, melainkan undangan untuk memutuskan dengan membaca kedua sisi lalu memverifikasi dengan test. Sejauh ini semua masih lokal di mesinmu. Di Chapter 4 kita buka pintu ke tim: remote, Pull Request, dan branch protection.

Yang Wajib Menempel

  • Branch adalah pointer ringan ke satu commit; HEAD menandai branch aktif, isolasi terjadi otomatis.
  • Pakai git switch untuk berpindah; namai branch dengan konvensi (feature/, fix/) dan jaga umurnya pendek.
  • Fast-forward saat main belum bergerak; three-way merge membuat merge commit dua parent saat menyimpang.
  • —no-ff menjaga gugus commit fitur tetap terlihat di histori.
  • Konflik diselesaikan dengan membaca kedua sisi, menghapus marker, lalu git add + test; git merge —abort untuk mundur.