Stacked MR Tanpa Graphite, karena di jj Stack Cuma Bentuk Graph
Di jj, stack bukan metadata yang harus dijaga alat lain, tapi bentuk graph itu sendiri, jadi revisi di lapisan bawah tidak butuh upacara restack.
Hari pertama pakai jj di repo internal core kami, jam 15:01, main maju. Saya punya beberapa bookmark yang belum di-merge, sebagian saling bertumpuk. Saya rebase sekali, pindahkan enam bookmark, dan semua turunannya ikut turun sendiri. Tidak ada yang saya checkout satu per satu.
Di git, momen itu biasanya saya tunda sampai besok. Bukan karena susah, tapi karena melelahkan. Rebase lapisan pertama, lalu rebase lapisan kedua ke hasil yang pertama, lalu yang ketiga, sambil berharap tidak ada konflik di tengah jalan yang memaksa saya berhenti.
Artikel terakhir seri ini membahas stack: kerja bertumpuk yang dipecah jadi beberapa MR kecil.

Stack itu apa
Stack adalah deret commit dari titik cabang di trunk sampai posisimu sekarang. Lapisan bawah bikin tabel, lapisan tengah bikin endpoint, lapisan atas bikin UI. Masing-masing jadi MR sendiri supaya reviewer bisa baca satu hal dalam satu duduk.
Git tidak dirancang untuk ini. Branch di git adalah pointer yang ikut maju saat kamu commit, jadi setiap lapisan butuh branch sendiri, dan setiap perubahan di lapisan bawah berarti rebase manual untuk semua lapisan di atasnya.
Graphite lahir untuk menambal itu. Ia menyimpan metadata “branch ini duduk di atas branch itu”, lalu menyediakan gt restack untuk merapikan ulang. Alatnya bagus, tapi ia tetap metadata yang ditempel di atas git. Keluar sedikit dari pola satu-commit-satu-branch, metadatanya bisa tidak sinkron. Dan Graphite cuma untuk GitHub. Kami di GitLab, jadi pilihan itu memang tidak pernah ada di meja.
Di jj, stack tidak disimpan di mana-mana selain di graph itu sendiri. Commit B anaknya A, C anaknya B. Itu saja. Kalau A diubah, jj me-rebase B dan C otomatis, karena memang begitu jj memperlakukan setiap turunan. Tidak ada perintah “restack” karena tidak ada yang perlu dipulihkan.
Graphite mengingat bentuk stack-mu. jj tidak perlu mengingat, karena bentuk itu adalah datanya.
Alurnya
Mulai dari trunk, lalu tiap lapisan satu jj new:
jj new 'trunk()' -m "feat(api): tabel lead_notes"
# edit file
jj new -m "feat(api): endpoint lead notes"
# edit file
jj new -m "feat(learner): panel catatan lead"
Satu catatan untuk yang menyuruh agen: selalu pakai -m. jj describe, jj split, atau jj squash tanpa -m membuka editor, dan agen akan diam menunggu selamanya. Kami sudah mengalaminya.
Lalu review masuk untuk lapisan paling bawah. Lompat ke sana, ubah, balik ke atas:
jj edit <change-id-lapisan-bawah>
# perbaiki sesuai review
jj next --edit # naik satu lapisan; jj prev --edit untuk turun
Dua lapisan di atasnya sudah ikut di-rebase begitu kamu selesai mengedit. Kalau perubahanmu bentrok dengan lapisan atas, konfliknya ditandai di commit yang bersangkutan dan kerja kamu tidak berhenti. Selesaikan nanti, saat kamu memang sampai di lapisan itu.
Untuk sinkron dengan trunk, dari commit mana pun di stack:
jj git fetch && jj rebase -d 'trunk()'
Rebase itu membawa seluruh cabang beserta turunannya. Kalau MR bawah sudah di-merge dengan squash, commit lamanya bisa tersisa kosong; buang dengan jj abandon.
Dari stack ke MR
Push butuh bookmark. Kalau kamu tidak peduli nama branch-nya, jj bisa membuatkan dari change ID:
jj git push -c <change-id>
Kalau peduli, pasang sendiri dengan jj bookmark set <nama> -r <change-id>, lalu jj git push. Bookmark ikut pindah saat commit-nya ditulis ulang, jadi setelah revisi di lapisan bawah, jj git push dari puncak stack sudah cukup untuk mendorong ulang semuanya.
Di GitLab, stacked MR artinya MR atas menarget branch MR bawah, bukan main. Reviewer lapisan kedua cuma melihat diff lapisan kedua. Ini disiplin di sisi GitLab, bukan di sisi jj, jadi tetap harus diingat waktu membuka MR.
Kamus buat yang datang dari Graphite
| Graphite | jj polos |
|---|---|
gt create | jj new -m "..." |
gt modify / gt restack | tidak perlu, turunan ikut sendiri |
gt sync | jj git fetch && jj rebase -d 'trunk()' |
gt up / gt down / gt checkout | jj next --edit / jj prev --edit / jj edit |
gt fold / gt split | jj squash / jj split |
gt submit | bookmark per commit, lalu jj git push |
Kalau kamu membaca panduan LazyJJ, kamu akan ketemu nama seperti stack-sync, stack-submit, stack-top, atau restack. Itu alias buatan LazyJJ, bukan perintah bawaan jj. Padanannya ada di tabel di atas. Pakai aliasnya kalau cocok, tapi pahami dulu perintah polosnya, karena agenmu tidak akan punya alias itu kecuali kamu pasangkan.
Perlu jujur juga: perbandingan di LazyJJ ditulis oleh orang yang sudah memilih jj, jadi tidak netral. Graphite masih masuk akal kalau kamu di GitHub dan butuh merge queue, dashboard stack untuk tim, atau sudah terlanjur investasi di alurnya. Buat kami yang di GitLab, dengan beberapa agen yang ngetik di repo yang sama, alat yang menyimpan stack sebagai graph biasa berarti satu layanan lebih sedikit yang bisa tidak sinkron.
Alat stacking terbaik adalah yang tidak perlu kamu rawat.
Satu hal yang perlu diingat
Di jj, merevisi lapisan bawah stack sama murahnya dengan merevisi lapisan atas, jadi pecah MR sekecil yang enak direview.
Ini artikel terakhir seri “jj pelan-pelan”. Kalau kamu masuk dari sini, balik ke awal: kenapa tim yang separuhnya agen mempertimbangkan jj, lalu model mental bahwa working copy adalah commit. Semua yang ada di artikel ini berdiri di atas dua ide itu.
Bahan dasar: panduan From Graphite dan referensi Stack di lazyjj.dev oleh Ernesto Jiménez.