Pola stacked pull requests (stacked PR) mempercepat alur kerja rekayasa perangkat lunak dengan memecah fitur besar menjadi serangkaian PR kecil yang saling bergantung. Masalah muncul ketika PR dasar (base PR) di-merge ke branch utama (main) menggunakan strategi Squash and Merge atau Rebase and Merge. Hash commit pada branch utama berubah total, menyebabkan branch turunannya membawa commit lama yang kini berstatus orphan atau duplikat. Jika branch turunan langsung di-rebase secara reguler (git rebase main), konflik merge berulang dan broken builds di CI/CD tidak dapat dihindari.

Solusi presisi untuk persoalan ini adalah perintah git rebase --onto. Perintah ini memungkinkan pemotongan rentang commit tertentu dari branch turunan dan mencangkokkannya langsung ke commit tujuan baru tanpa menyertakan riwayat commit yang telah usang.

Anatomi Masalah: Mengapa Rebase Biasa Gagal pada Stacked PR

Bayangkan struktur cabang berikut:

      B1---B2 (feature-base)
     /
---A---C (main)
        \
         B1---B2---F1---F2 (feature-stacked)

Ketika feature-base digabungkan ke main menggunakan metode Squash and Merge, GitHub/GitLab membuat satu commit baru S yang berisi perubahan dari B1 dan B2:

---A---C---S (main, commit S berisi diff B1 + B2)

Branch feature-stacked awalnya bercabang dari feature-base (ujung B2). Jika Anda menjalankan git checkout feature-stacked && git rebase main, Git akan memeriksa riwayat commit dari titik temu terakhir (commit A). Git akan mencoba menerapkan ulang commit B1, B2, F1, dan F2 di atas commit S. Karena S sudah memuat modifikasi yang sama dengan B1 dan B2 namun memiliki hash berbeda, Git sering kali memicu konflik file massal atau memasukkan commit duplikat yang merusak riwayat CI/CD.

Sintaks dan Mekanisme git rebase --onto

Perintah git rebase --onto memisahkan antara target cabang baru dengan titik awal commit yang akan dipotong. Sintaks dasarnya:

git rebase --onto <new-parent> <old-parent> <branch-name>
  • <new-parent>: Commit atau branch tujuan tempat commit akan ditautkan (misalnya: origin/main).
  • <old-parent>: Batas awal commit lama yang tidak ingin disertakan (eksklusif). Git akan mengambil commit setelah titik ini sampai ujung branch kerja.
  • <branch-name>: Branch target yang sedang dipindahkan (inklusif hingga HEAD).

Workflow Implementasi: Menyelamatkan feature-stacked

Berikut adalah langkah-langkah konkret menyelesaikan stacked PR setelah base PR di-merge ke main:

1. Sinkronisasi Remote Repository

Ambil metadata terbaru dari origin untuk memastikan posisi main sudah memuat commit squash.

git fetch origin

2. Eksekusi Pemotongan Commit Orphan

Pindahkan rentang commit F1..F2 dari branch feature-stacked langsung ke atas origin/main, melewati commit feature-base yang sudah usang:

# Format: git rebase --onto <tujuan_baru> <titik_potong_eksklusif> <branch>
git rebase --onto origin/main feature-base feature-stacked

Jika branch lokal feature-base sudah terhapus, gunakan hash commit terakhir dari base PR sebelum di-merge:

git rebase --onto origin/main <hash-B2> feature-stacked

Git hanya akan memetik commit F1 dan F2, lalu menerapkannya tepat di atas origin/main. Riwayat cabang kini bersih tanpa commit duplikat dari feature-base.

3. Perbarui Remote Branch

Karena riwayat commit lokal telah ditulis ulang, perbarui branch di remote menggunakan flag proteksi:

git push --force-with-lease origin feature-stacked
Catatan Keamanan: Selalu gunakan --force-with-lease alih-alih --force murni untuk mencegah penimpaan commit rekan kerja yang mungkin telah dikirim ke remote branch tersebut secara bersamaan.

Mencegah Broken Builds: Health Check Otomatis Tiap Commit

Salah satu penyebab utama kegagalan CI/CD pada stacked PR adalah rusaknya integritas build pada commit individual di tengah branch (bukan hanya commit terakhir). Jika commit F1 bergantung pada perubahan yang diubah saat squash, PR mungkin lolos build di commit F2, tetapi git bisect di masa depan akan rusak karena F1 berstatus broken build.

Validasi integritas setiap commit selama rebase menggunakan flag -x (singkatan dari --exec):

git rebase --onto origin/main feature-base feature-stacked -x "npm test"

Untuk project berbasis Go atau language tooling lain:

git rebase --onto origin/main feature-base feature-stacked -x "go test ./... && golangci-lint run"

Jika salah satu commit memicu kegagalan test atau linter, proses rebase berhenti tepat di commit yang bermasalah. Anda dapat langsung memperbaiki bug tersebut, lalu melanjutkan:

# Perbaiki kode sumber
git add .
git commit --amend --no-edit
git rebase --continue

Kesalahan Umum dan Cara Mitigasi

  • Salah menentukan boundary <old-parent>: Menyertakan argumen yang salah dapat memotong commit yang sebenarnya dibutuhkan atau menyisakan commit lama. Jika rebase berantakan, kembalikan posisi branch ke titik sebelum rebase menggunakan git reset --hard ORIG_HEAD.
  • Merge bertahap tanpa sinkronisasi: Hindari menumpuk lebih dari 3 tingkat stacked PR secara manual tanpa tooling (seperti gh-stack atau Graphite). Semakin tinggi tumpukan PR, semakin tinggi beban kognitif dalam melacak hash commit dasar yang tepat untuk setiap pemanggilan --onto.