Panduan

git rebase: cara kerjanya, mode interaktif, dan squash commit

Diperbarui: · Skillaria.Top

git rebase memindahkan commit branch aktif ke basis yang baru: Git menyimpan sementara commit Anda, menggeser branch ke ujung basis tujuan, lalu menerapkan ulang commit tersebut sebagai commit baru. Hasilnya riwayat linier tanpa commit merge. Dengan git rebase -i, commit bisa digabung (squash), diberi nama ulang, atau dihapus; penulisan ulang seperti ini hanya aman untuk commit yang belum dipublikasikan ke orang lain.

Apa yang dilakukan git rebase

git rebase memindahkan «tempat duduk» branch Anda ke titik lain dalam riwayat. Misalnya, Anda membuat feature dari main seminggu yang lalu, sementara main sudah maju sejak itu. Setelah rebase, branch Anda terlihat seolah-olah baru dimulai dari commit terbaru di main.

git switch feature
git fetch origin
git rebase origin/main

Di balik layar ada tiga langkah. Git mencari leluhur bersama kedua branch, menyimpan perubahan dari setiap commit Anda setelah leluhur itu, memajukan branch ke origin/main, lalu menerapkan kembali perubahan tersebut satu per satu. Hasilnya adalah commit baru dengan isi dan pesan yang sama, tetapi hash-nya berbeda, karena induknya juga berbeda.

Sebelum:  A---B---C---F---G   main
                  \
                   D---E     feature

Sesudah:  A---B---C---F---G   main
                          \
                           D'---E'   feature

Tanda kutip pada D' dan E' bukan hiasan. Itu objek lain dengan hash lain dan induk yang berbeda. Dari sini muncul aturan terpenting dalam rebase, yang dibahas di bawah.

Aturan emas: jangan rebase yang sudah dipublikasikan

Rebase menulis ulang riwayat. Selama commit hanya ada di komputer Anda, itu aman. Namun jika rekan sudah mengunduh branch tersebut, setelah rebase mereka masih memiliki D dan E lama, sedangkan Anda memiliki D' dan E' baru. Saat pull berikutnya, riwayat akan terduplikasi dan konflik harus diselesaikan ulang.

Aturan praktisnya: rebase diterapkan pada branch lokal dan branch fitur pribadi yang hanya dikerjakan satu orang. Branch bersama seperti main, develop, dan branch rilis tidak pernah di-rebase.

Cara mengirim branch setelah rebase

Jika branch sudah ada di server, git push biasa akan ditolak, karena riwayat lokal tidak lagi melanjutkan riwayat remote. Yang dibutuhkan adalah force push dalam versi yang aman:

git push --force-with-lease

Berbeda dengan --force, flag --force-with-lease menolak menimpa branch jika di server ada commit yang belum pernah Anda lihat. Dengan begitu, pekerjaan orang lain tidak terhapus secara tidak sengaja.

Konflik saat rebase

Karena commit diterapkan satu per satu, konflik bisa muncul pada commit mana pun, bahkan beberapa kali berturut-turut. Git berhenti dan memberi tahu commit mana yang gagal diterapkan.

$ git rebase origin/main
Auto-merging src/api.js
CONFLICT (content): Merge conflict in src/api.js
error: could not apply 4e1f9a2... Tambahkan cache permintaan
hint: Resolve all conflicts manually, mark them as resolved with
hint: "git add/rm <conflicted_files>", then run "git rebase --continue".

Langkah yang dilakukan:

  1. Buka berkas yang konflik, pilih versi yang benar, dan hapus penanda <<<<<<<, =======, dan >>>>>>>.
  2. Tandai berkas sebagai sudah diselesaikan: git add src/api.js. Tidak perlu membuat commit.
  3. Lanjutkan dengan git rebase --continue. Git akan menerapkan commit berikutnya.

Perintah lain untuk mengendalikan proses:

PerintahFungsi
git rebase --continueMelanjutkan setelah konflik diselesaikan
git rebase --skipMelewati commit saat ini sepenuhnya (perubahannya hilang)
git rebase --abortMembatalkan rebase dan mengembalikan branch ke keadaan semula

Perhatikan kebingungan soal sisi «kita» dan «mereka». Selama rebase, HEAD adalah basis tempat Anda memindahkan branch, sedangkan perubahan «yang masuk» adalah commit Anda sendiri. Karena itu git checkout --ours saat rebase mengambil versi dari main, dan --theirs mengambil versi Anda. Saat merge, kebalikannya berlaku.

Konflik yang berulang: rerere

Jika Anda sering me-rebase branch yang panjang dan menyelesaikan konflik yang sama berulang kali, aktifkan git config --global rerere.enabled true. Git akan mengingat cara Anda menyelesaikan konflik dan menerapkannya sendiri pada kejadian berikutnya.

Rebase interaktif: merapikan riwayat sebelum dipublikasikan

Bagian paling berharga dari rebase adalah mode interaktifnya. Di sini Anda bisa merapikan commit sendiri sebelum menampilkannya untuk review: menggabungkan «fix», «fix typo», dan «fix lagi» menjadi satu commit yang bermakna, menulis ulang pesan, mengubah urutan, atau membuang yang tidak perlu.

git rebase -i HEAD~4        # 4 commit terakhir
git rebase -i origin/main   # semua commit branch yang tidak ada di main

Git membuka editor dengan daftar commit dari yang paling lama ke yang paling baru (berlawanan dengan git log):

pick 1a2b3c4 Tambahkan form login
pick 5d6e7f8 fix
pick 9a0b1c2 Validasi e-mail
pick 3d4e5f6 salah ketik di validasi

# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash" but keep only the previous commit's log message
# d, drop = remove commit

Anda cukup mengubah kata di awal baris, menyimpan, lalu menutup editor. Git akan menjalankan rencana itu dari atas ke bawah.

PerintahEfek
pickBiarkan commit apa adanya
rewordBiarkan perubahannya, tetapi tulis ulang pesannya
editBerhenti di commit ini untuk mengubah isinya (git commit --amend) atau memecahnya menjadi beberapa commit
squashGabungkan ke commit sebelumnya; pesan keduanya digabung dan bisa diedit
fixupGabungkan ke commit sebelumnya; pesan commit ini dibuang
dropHapus commit beserta perubahannya

Menukar posisi baris mengubah urutan commit, dan menghapus baris sama artinya dengan drop.

Squash commit: menggabungkan commit menjadi satu

Di Git tidak ada perintah git squash tersendiri. Ada tiga cara untuk «memampatkan» commit, dan rebase interaktif adalah yang paling fleksibel.

Cara 1. Rebase interaktif

Untuk contoh di atas, kita ubah empat commit menjadi dua commit yang rapi:

pick 1a2b3c4 Tambahkan form login
fixup 5d6e7f8 fix
pick 9a0b1c2 Validasi e-mail
fixup 3d4e5f6 salah ketik di validasi

Commit pertama dalam sebuah grup harus tetap pick. Perintah squash dan fixup menggabungkan commit ke commit sebelumnya, dan commit paling pertama tidak punya tempat untuk digabungkan.

Cara 2. Commit fixup dan autosquash

Jika Anda sudah tahu commit mana yang ingin diperbaiki, buat commit perbaikan yang langsung ditandai:

git commit --fixup=1a2b3c4
git rebase -i --autosquash origin/main

Git akan menempatkan commit fixup tepat di bawah commit yang dituju dan menandainya sebagai fixup; Anda tinggal menyimpan rencananya. Untuk mengaktifkannya secara default, gunakan git config --global rebase.autoSquash true.

Cara 3. Memampatkan seluruh branch menjadi satu commit

git reset --soft $(git merge-base HEAD origin/main)
git commit -m "Login dengan e-mail dan validasi"

reset --soft memindahkan penunjuk branch kembali ke titik percabangan, tetapi semua perubahan tetap berada di index, sehingga Anda hanya perlu membuat satu commit. Pilihan lain adalah git merge --squash saat menggabungkan ke branch utama, atau tombol «Squash and merge» pada pull request.

Varian rebase yang berguna

git pull --rebase

Pull biasa menggabungkan branch remote ke branch Anda dan membuat commit merge jika Anda commit secara paralel dengan rekan. Sebagai gantinya, git pull --rebase memindahkan commit lokal Anda ke atas versi terbaru dari remote. Untuk menjadikannya perilaku default: git config --global pull.rebase true. Inilah area aman untuk rebase, karena commit lokal Anda belum dilihat siapa pun.

--onto: memindahkan sebagian branch

Misalnya Anda membuat feature-b dari feature-a, dan sekarang ingin feature-b langsung berasal dari main, tanpa commit feature-a:

git rebase --onto main feature-a feature-b

Maknanya: ambil commit di feature-b yang tidak ada di feature-a, lalu pindahkan ke main.

--exec: memeriksa setiap commit

git rebase -i --exec "npm test" origin/main

Setelah setiap commit diterapkan, tes akan dijalankan. Jika ada yang gagal, rebase berhenti di commit bermasalah. Ini berguna ketika setiap commit dalam riwayat harus bisa di-build.

--update-refs: tumpukan branch yang saling bergantung

Jika Anda memelihara rantai branch yang saling bergantung, flag --update-refs (Git 2.38+) juga memajukan branch di tengah saat branch paling atas di-rebase.

Cara membatalkan rebase

Selama proses masih berjalan, gunakan git rebase --abort. Jika rebase sudah selesai dan hasilnya tidak Anda inginkan, commit lama tidak hilang: mereka tercatat di log referensi.

$ git reflog
e5f6a7b HEAD@{0}: rebase (finish): returning to refs/heads/feature
e5f6a7b HEAD@{1}: rebase (pick): Validasi e-mail
...
c3d4e5f HEAD@{5}: commit: salah ketik di validasi

$ git reset --hard HEAD@{5}

Cari di git reflog entri sebelum rebase dimulai, lalu atur ulang branch ke entri itu. Tepat setelah rebase, jalan pintas git reset --hard ORIG_HEAD juga bisa dipakai. Reflog disimpan secara lokal dan umumnya menyimpan entri sekitar 90 hari, jadi tidak perlu panik setelah rebase yang gagal.

Kesalahan yang sering terjadi

  • Branch bersama di-rebase lalu force push, dan riwayat rekan kerja menjadi kacau. Perbaikannya: sepakati aturan, pulihkan branch dari reflog orang yang memiliki versi yang benar, dan larang force push ke branch terlindungi di server.
  • Memakai --force alih-alih --force-with-lease, sehingga commit yang baru dikirim rekan di branch yang sama ikut tertimpa.
  • Squash di baris pertama rencana interaktif akan menghasilkan error «cannot 'squash' without a previous commit».
  • Memulai rebase dengan direktori kerja yang kotor. Git akan menolak. Commit perubahan Anda, simpan dengan git stash, atau gunakan git rebase --autostash.
  • Setelah menyelesaikan konflik, menjalankan git commit alih-alih git rebase --continue. Hasilnya commit tambahan yang tidak perlu; setelah --continue, commit itu bisa dibersihkan dengan rebase interaktif berikutnya.

Ringkasan

git rebase memindahkan commit Anda ke basis baru dan membuatnya kembali, sehingga riwayat menjadi lurus dan bersih. Mode interaktif mengubah commit coretan menjadi riwayat yang rapi: squash dan fixup menggabungkan perubahan, reword memperbaiki pesan, drop membuang yang tidak diperlukan. Aturan utamanya: rebase hanya yang belum dipublikasikan untuk orang lain, dan setelah rebase pada branch remote milik sendiri, kirim dengan --force-with-lease. Rebase dan pembersihan riwayat commit dibahas dengan praktik dalam kursus Git.

Pertanyaan umum

Apakah git rebase mengubah isi kode?

Jika tidak ada konflik, isi file akhirnya sama seperti hasil merge. Namun commit dibuat ulang dengan hash dan induk baru. Jika saat konflik Anda memilih versi yang berbeda, commit perantara juga bisa berbeda dari aslinya.

Bagaimana cara me-rebase branch ke commit paling awal repositori?

Gunakan git rebase -i --root; semua commit, termasuk commit pertama, masuk ke rencana. Perintah ini jarang diperlukan, misalnya untuk mengubah pesan commit pertama.

Apakah rebase menyimpan penulis dan tanggal commit?

Penulis dan tanggal penulisan dipertahankan, sedangkan committer dan tanggal commit menjadi milik Anda dengan waktu saat ini. Flag --committer-date-is-author-date membuat tanggal commit sama dengan tanggal penulisan.

Bisakah commit merge ikut di-rebase?

Secara default rebase meluruskan riwayat dan membuang commit merge. Untuk mempertahankan struktur penggabungan, gunakan git rebase --rebase-merges.

Bagaimana cara mengetahui bahwa rebase sedang berjalan?

git status akan menampilkan baris interactive rebase in progress atau You are currently rebasing beserta perintah yang tersedia. Banyak shell dan IDE juga menampilkannya di prompt.

Pelajari topik ini lewat video

Topik ini dibahas mendalam dengan praktik di kursus “Git dari Nol hingga Pengembang Percaya Diri”.

Ke kursus