Start HereOrdered learning track

Rebase Mental Model

Learn Git In Action - Part 023

Memahami rebase sebagai replay patch di atas base baru, lengkap dengan graph semantics, safety boundary, conflict behavior, dan workflow operasional.

12 min read2342 words
PrevNext
Lesson 23126 lesson track01–23 Start Here
#git#rebase#branching#history-rewrite+1 more

Part 023 — Rebase Mental Model

Banyak engineer bisa menjalankan:

git rebase main

Tapi tidak semua engineer benar-benar memahami apa yang sedang terjadi.

Rebase bukan sekadar "membuat history linear". Rebase adalah operasi membangun ulang commit series dengan cara mengambil perubahan dari sekumpulan commit lama, lalu menerapkannya ulang di atas base baru.

Kalimat pentingnya:

Rebase tidak memindahkan commit lama. Rebase membuat commit baru yang membawa perubahan serupa pada posisi graph yang berbeda.

Itu sebabnya commit hash berubah. Itu sebabnya force push sering diperlukan. Itu sebabnya rebase aman untuk private branch tetapi berbahaya untuk public/shared history jika tidak ada protokol.

Part ini membangun mental model rebase secara dalam, operasional, dan defensif.


1. Problem yang Diselesaikan Rebase

Misalkan branch feature/payment-rule dibuat dari main beberapa hari lalu.

Graph-nya:

A---B---C---D  main
     \
      F1---F2  feature

Branch feature tertinggal dari main. Ada beberapa pilihan:

  1. merge main ke feature;
  2. rebase feature ke atas main;
  3. biarkan diverged lalu integrasi saat PR merge.

Rebase menghasilkan bentuk seperti ini:

A---B---C---D---F1'---F2'  feature
            ^
            main

F1' dan F2' bukan commit yang sama dengan F1 dan F2. Mereka commit baru. Patch-nya mirip, parent-nya berbeda, hash-nya berbeda.


2. Definisi Operasional

Secara mental, perintah:

git rebase main

pada branch feature berarti:

Ambil commit yang reachable dari feature tetapi belum reachable dari main.
Replay commit itu satu per satu di atas main.
Jika berhasil, pindahkan branch feature ke hasil replay terakhir.

Dalam bentuk konseptual:

# bukan implementasi literal, hanya mental model
commits=$(git rev-list --reverse main..feature)
git checkout main
for commit in $commits; do
  git cherry-pick $commit
 done
move feature to new HEAD

Rebase mirip cherry-pick berurutan, tetapi dengan manajemen branch, conflict continuation, todo list, dan opsi tambahan.


3. Rebase adalah Patch Replay, Bukan Commit Move

Commit identity di Git ditentukan oleh isi commit object. Commit object berisi antara lain:

  • tree hash;
  • parent hash;
  • author;
  • committer;
  • timestamp;
  • message.

Saat parent berubah, commit hash berubah. Jadi walaupun perubahan file sama, commit baru tetap punya identity baru.

Contoh:

F1 parent = B
F1' parent = D

Karena parent berbeda, F1 dan F1' adalah object berbeda.

Konsekuensi:

AreaDampak
commit hashberubah
branch pointerbergerak ke commit baru
review diffbisa berubah jika base berubah
CI cachemungkin invalidated
signed commitsignature lama tidak berlaku pada commit baru
shared branchcollaborator bisa punya commit lama
reflogbiasanya masih bisa recovery lokal

4. Before/After Graph

Sebelum rebase:

Setelah feature direbase ke main:

Yang hilang dari branch pointer bukan berarti object lama langsung hilang dari object database. Object lama biasanya masih reachable dari reflog untuk sementara.


5. Apa yang Dipilih Rebase untuk Direplay?

Default rebase memakai konsep upstream.

git rebase main

Saat berada di branch feature, commit yang dipilih kira-kira:

git log --oneline main..feature

Artinya:

commits reachable from feature, excluding commits reachable from main

Urutan replay adalah dari paling tua ke paling baru.

git rev-list --reverse main..feature

Kenapa urutan tua ke baru?

Karena setiap commit dalam series bisa bergantung pada perubahan commit sebelumnya.

F1: add database column
F2: use database column in service
F3: expose field in API

Jika F3 direplay sebelum F1, patch bisa gagal atau menghasilkan state tidak masuk akal.


6. Rebase dan Merge Base

Pada kasus sederhana, rebase menggunakan merge base antara upstream dan branch sebagai boundary.

A---B---C---D  main
     \
      F1---F2  feature

Merge base main dan feature adalah B.

Commit yang akan direplay:

B..feature = F1, F2

Base baru:

D

Hasil:

A---B---C---D---F1'---F2'

Mental model ini penting karena rebase bukan "ambil semua commit di branch". Rebase mengambil commit setelah fork point / merge base boundary.


7. ours dan theirs Saat Rebase

Ini salah satu bagian yang sering menjebak.

Saat merge biasa:

  • ours = branch saat ini;
  • theirs = branch yang sedang dimerge.

Saat rebase, Git sedang menaruh commit kita satu per satu di atas base baru. Dalam conflict rebase:

  • ours sering merepresentasikan hasil sementara rebased series, dimulai dari upstream/base baru;
  • theirs merepresentasikan commit dari branch kita yang sedang direplay.

Contoh:

git rebase main
# conflict

Jika memakai:

git checkout --ours path/to/file

itu bisa berarti mengambil versi dari base baru / hasil rebased sejauh ini, bukan "punyaku sebelum rebase" dalam arti sehari-hari.

Aturan aman:

Jangan memakai --ours / --theirs saat rebase berdasarkan intuisi bahasa. Baca conflict, baca base, baca patch intent.

Gunakan inspeksi:

git status
git diff
git diff --ours -- path/to/file
git diff --theirs -- path/to/file
git diff --base -- path/to/file
git ls-files -u -- path/to/file

8. Rebase State Machine

Rebase bukan single atomic command dari perspektif user. Ia punya state machine.

Command penting:

# lanjut setelah conflict resolved dan staged
git rebase --continue

# batalkan seluruh rebase dan kembali ke state awal
git rebase --abort

# skip commit yang sedang direplay
git rebase --skip

# edit todo list saat interactive rebase
git rebase --edit-todo

--skip harus sangat hati-hati. Skip berarti commit yang sedang replay tidak dimasukkan ke hasil baru. Itu bisa benar jika patch sudah ada di base baru, tetapi berbahaya jika hanya karena conflict terlihat sulit.


9. Rebase dan Working Tree Cleanliness

Sebelum rebase, working tree idealnya bersih.

Cek:

git status --short

Jika ada perubahan lokal:

# pilih salah satu
# 1. commit dulu
git add -p
git commit

# 2. stash dulu
git stash push -u -m "wip before rebase feature/payment-rule"

# 3. buang jika memang disposable
git restore --worktree --staged .
git clean -fd

Ada opsi:

git rebase --autostash main

Tapi jangan jadikan --autostash sebagai default tanpa memahami risikonya. Autostash membantu kenyamanan, tetapi jika rebase menghasilkan conflict lalu stash re-apply juga conflict, state mental jadi lebih kompleks.

Prinsip production-grade:

Rebase adalah operasi graph surgery. Mulailah dari working tree bersih agar failure mode mudah dibaca.


10. Rebase Biasa vs Pull Rebase

Dua command ini sering dicampur:

git rebase origin/main

versus:

git pull --rebase

pull adalah fetch plus integrate. Jadi:

git pull --rebase

kira-kira:

git fetch
git rebase @{upstream}

Untuk engineer senior, explicit form sering lebih aman:

git fetch origin
git log --oneline --graph --decorate --left-right HEAD...origin/main
git rebase origin/main

Kenapa?

Karena kamu membaca state sebelum mengubah graph.


11. Rebase pada Private Branch

Rebase paling aman untuk branch yang hanya kamu pakai sendiri.

Contoh daily update:

git switch feature/payment-rule
git fetch origin
git rebase origin/main

Jika sudah push branch itu ke remote tapi belum ada orang lain bergantung padanya:

git push --force-with-lease

Gunakan --force-with-lease, bukan --force, karena lease mengecek bahwa remote ref masih berada di posisi yang kamu kira. Jika ada orang lain push ke branch yang sama, push kamu akan ditolak.

Mental model:

--force:
  replace remote ref, regardless of what happened remotely

--force-with-lease:
  replace remote ref only if remote still matches my last known value

12. Rebase pada Shared Branch

Jangan sembarang rebase branch yang dipakai banyak orang.

Shared branch contoh:

  • main;
  • develop;
  • release/2026.07;
  • branch feature yang dipakai banyak engineer;
  • branch vendor/customer delivery;
  • branch compliance evidence.

Jika branch sudah menjadi basis kerja orang lain, rebase menyebabkan mereka memiliki history lama sementara remote pindah ke history baru.

Efeknya:

remote/shared: A---B---C'---D'
local teammate: A---B---C---D---E

Teammate harus reconcile dua dunia:

  • commit lama C-D;
  • commit baru C'-D';
  • commit lokal mereka E yang mungkin based on old D.

Itu masih bisa diselesaikan, tetapi biaya koordinasinya tinggi.

Rule of thumb:

Rebase private history. Merge or revert public history.

Bukan hukum absolut, tapi default aman.


13. Rebase vs Merge: Beda Tujuan

Merge mempertahankan fakta integrasi.

A---B---C---D  main
     \       \
      F1---F2 M  feature after merge

Rebase membentuk ulang series agar terlihat seolah-olah dikembangkan dari base baru.

A---B---C---D---F1'---F2'

Trade-off:

DimensionMergeRebase
preserves topologykuatdefault tidak
linear historytidak selaluya
public safetylebih amanperlu disiplin
review patch seriesbisa noisysering lebih bersih
integration recordeksplisithilang kecuali manual
bisect readabilitytergantung merge policysering lebih linear
conflict timingsekali saat mergebisa per commit saat replay

Rebase bukan "lebih benar". Merge bukan "lebih jujur" untuk semua kasus. Pilihan bergantung pada invariant tim.


14. Conflict Saat Rebase: Kenapa Bisa Lebih Banyak?

Merge menyelesaikan integrasi antara dua endpoint.

Rebase menerapkan commit satu per satu.

Jika commit series panjang:

F1, F2, F3, F4, F5

maka conflict bisa muncul di beberapa commit. Itu melelahkan, tetapi ada manfaat: kamu tahu commit mana yang membawa intent yang sedang conflict.

Pada merge biasa, conflict muncul pada hasil gabungan endpoint. Kadang lebih sedikit, tapi root cause lebih kabur.

Pilihan:

  • rebase bagus untuk memperbaiki patch series sebelum review;
  • merge bagus untuk mencatat integration point dan menghindari rewrite public branch;
  • rerere membantu jika conflict rebase berulang.

15. Empty Commit Saat Rebase

Saat rebase, sebuah commit bisa menjadi empty jika patch-nya sudah ada di upstream/base baru.

Contoh:

main already has equivalent fix
feature contains old fix commit

Saat replay, Git mendeteksi tidak ada perubahan baru.

Kemungkinan tindakan:

# skip commit jika memang perubahan sudah ada
git rebase --skip

# atau pertahankan empty commit jika punya makna metadata tertentu
git commit --allow-empty

Jangan otomatis skip tanpa membaca:

git show <commit-being-replayed>
git log --cherry-pick --right-only --oneline upstream...HEAD

Empty commit bisa berarti:

  • patch sudah upstream;
  • commit hanya mengubah context yang sudah berubah;
  • conflict resolution menghapus semua perubahan;
  • commit memang marker metadata.

16. Rebase dengan --onto

--onto adalah bentuk rebase yang lebih eksplisit dan powerful.

Format umum:

git rebase --onto <newbase> <upstream> <branch>

Artinya:

Ambil commit dari <branch> yang ada setelah <upstream>, lalu replay di atas <newbase>.

Contoh memindahkan sub-series:

A---B---C---D  main
     \
      X---Y---Z  feature

Misal X adalah eksperimen buruk, tapi Y-Z ingin dipertahankan dan dipindahkan ke D.

git rebase --onto main X feature

Hasil:

A---B---C---D---Y'---Z'  feature

X tidak ikut karena range yang diambil adalah X..feature, yaitu Y dan Z.

Use case --onto:

Use caseCommand shape
memindahkan branch dependency ke base barugit rebase --onto newbase oldbase topic
membuang commit awal dari seriesgit rebase --onto main badcommit feature
memindahkan stacked branchgit rebase --onto new-parent old-parent child-branch
memisahkan branch dari wrong basegit rebase --onto correct-base wrong-base feature

17. Rebase Stacked Branch

Stacked branch:

main --- A---B  feature/base
              \
               C---D  feature/child

Jika feature/base direbase:

main --- A'---B'  feature/base

feature/child masih menunjuk ke commit lama B.

Kamu perlu rebase child dari old base ke new base:

git switch feature/child
git rebase --onto feature/base <old-feature-base-tip> feature/child

Karena <old-feature-base-tip> sering sulit diingat, simpan anchor sebelum rewrite:

git branch backup/feature-base-before-rebase feature/base

git switch feature/base
git rebase origin/main

# lalu child
git switch feature/child
git rebase --onto feature/base backup/feature-base-before-rebase feature/child

Ini akan dibahas lebih jauh di stacked branches, tapi mental model --onto wajib dikuasai dari sekarang.


18. Rebase dan Reflog Recovery

Sebelum rebase besar, buat safety anchor:

git branch backup/feature-before-rebase

Atau gunakan tag lokal sementara:

git tag backup/feature-before-rebase-2026-07-07

Jika lupa, reflog biasanya menyimpan posisi lama:

git reflog

Contoh:

abc1234 HEAD@{0}: rebase (finish): returning to refs/heads/feature
fed5678 HEAD@{1}: rebase (pick): add validation
111aaaa HEAD@{2}: rebase (start): checkout origin/main
999bbbb HEAD@{3}: commit: add rule engine

Recover:

git branch recovery/feature-before-rebase HEAD@{3}

Atau reset kembali jika yakin:

git reset --hard HEAD@{3}

Gunakan branch recovery lebih dulu. Reset hard adalah operasi destructive terhadap working tree.


19. Rebase dan Signed Commits

Karena rebase membuat commit baru, signature lama tidak ikut secara otomatis sebagai signature yang sama. Signed commit adalah signature terhadap commit object tertentu. Saat object berubah, signature harus dibuat ulang jika ingin hasil tetap signed.

Konsekuensi untuk tim:

  • branch dengan policy signed commits perlu re-sign hasil rebase;
  • rebase massal bisa mengubah banyak signature;
  • audit harus memahami bahwa commit lama dan commit baru adalah object berbeda;
  • release tag signed di atas commit lama tidak otomatis mewakili commit baru.

Praktik:

git rebase --exec 'git commit --amend --no-edit -S' origin/main

Tetapi command semacam ini harus hati-hati karena amend setiap commit juga dapat mengubah metadata committer. Untuk workflow regulated, lebih baik tentukan policy eksplisit: kapan rewrite boleh, siapa yang boleh, dan evidence apa yang disimpan.


20. Rebase dan CI

Rebase mengubah commit SHA, sehingga CI yang sebelumnya hijau pada commit lama tidak otomatis membuktikan commit baru hijau.

Contoh:

F2 passed CI on base B
F2' must be tested again on base D

Kenapa?

Karena environment kode berubah:

  • dependency code dari main berubah;
  • test suite berubah;
  • config berubah;
  • migration berubah;
  • conflict resolution bisa salah;
  • hidden semantic interaction muncul.

Team policy yang sehat:

After rebase, rerun CI on the new tip.

Untuk PR:

  • jangan hanya percaya CI lama sebelum force push;
  • pastikan status check terasosiasi dengan commit SHA terbaru;
  • merge queue idealnya mengetes hasil final integrasi, bukan hanya branch tip lama.

21. Rebase dan Review

Rebase bisa memperbaiki reviewability:

  • commit series lebih linear;
  • fixup commit bisa disquash;
  • unrelated change bisa dipisah;
  • base terbaru mengurangi conflict saat merge PR;
  • reviewer bisa membaca commit-by-commit.

Tapi rebase juga bisa merusak review:

  • force push besar tanpa explanation;
  • reviewer kehilangan anchor lama;
  • komentar PR menjadi outdated;
  • diff terlihat berubah total akibat base drift;
  • commit message berubah tanpa catatan.

Protocol setelah rebase PR:

1. Push with --force-with-lease.
2. Comment summary of what changed.
3. Provide range-diff if patch series changed meaningfully.
4. Do not hide semantic changes inside rebase cleanup.
5. Ask reviewer to review changed commits, not necessarily everything again.

Command:

git range-diff origin/main...backup/feature-before-rebase origin/main...feature

22. Rebase Public Branch: Kapan Boleh?

Default: jangan.

Tetapi ada situasi enterprise tertentu di mana rewrite public branch dilakukan dengan protokol:

  • repository migration;
  • secret purge setelah credential rotation;
  • removing massive binary history;
  • legal/compliance takedown;
  • pre-agreed branch maintenance sebelum branch dikonsumsi luas;
  • internal patch stack branch yang memang policy-nya rewriteable.

Jika harus:

1. Freeze branch.
2. Announce exact ref affected.
3. Create immutable backup ref.
4. Perform rewrite.
5. Push with force-with-lease or admin-controlled update.
6. Tell consumers exact recovery/rebase instructions.
7. Monitor CI and downstream breakage.
8. Record audit note.

Jangan menyebut operasi ini "cleanup kecil". Bagi downstream, ini bisa menjadi incident.


23. Operational Playbook: Safe Private Rebase

Gunakan ini untuk daily feature branch update.

# 1. Pastikan branch benar
git branch --show-current

# 2. Pastikan working tree bersih
git status --short

# 3. Ambil remote state terbaru
git fetch origin

# 4. Baca divergence
git log --oneline --graph --decorate --left-right HEAD...origin/main

# 5. Buat safety anchor
git branch backup/$(git branch --show-current)-before-rebase

# 6. Rebase
git rebase origin/main

# 7. Jika conflict: resolve, test, continue
# edit files
git add <resolved-files>
git rebase --continue

# 8. Test hasil final
./gradlew test
# atau command test repo-mu

# 9. Push aman jika branch remote perlu update
git push --force-with-lease

24. Operational Playbook: Abort and Recover

Jika rebase kacau:

git status

Jika masih dalam rebase:

git rebase --abort

Jika rebase sudah selesai tapi hasil salah:

git reflog

git branch recovery/before-bad-rebase HEAD@{n}

git switch recovery/before-bad-rebase

Jika ingin mengembalikan branch:

git switch feature/payment-rule
git reset --hard recovery/before-bad-rebase

Lebih aman:

git switch -c feature/payment-rule-recovered recovery/before-bad-rebase

Jangan panik. Selama object belum dipruning dan reflog masih ada, sebagian besar bad rebase bisa dipulihkan.


25. Failure Modes

Failure modePenyebabPencegahan
force push menimpa commit teammatepakai --force pada shared branchgunakan --force-with-lease, ownership branch jelas
conflict resolved salahfokus pada marker teks, bukan domain invariantbaca patch intent, test, review
commit hilang setelah rebasesalah range atau --skip sembaranganbuat backup branch, inspect todo/range
PR diff berubah totalbase drift besar atau rewrite kacaurange-diff, split rewrite, jelaskan ke reviewer
signed commit jadi unsignedrebase membuat commit barure-sign atau jangan rewrite signed history
CI lama dianggap validSHA berubahrerun CI on new tip
rebase stacked branch rusakchild branch masih pointing ke old basegunakan --onto dan backup anchors
autostash conflict membingungkanWIP ikut bercampurmulai dari clean working tree

26. Decision Framework

Gunakan rebase ketika:

  • branch private atau rewriteable by policy;
  • ingin memperbarui feature branch ke base terbaru;
  • ingin membersihkan patch series sebelum review;
  • ingin replay fix ke base berbeda;
  • stacked branch perlu dipindahkan;
  • commit series perlu dibuat lebih mudah dibaca.

Hindari rebase ketika:

  • branch sudah shared dan banyak orang bergantung padanya;
  • branch adalah release/protected branch;
  • audit membutuhkan history apa adanya;
  • commit sudah signed/released/tagged sebagai evidence;
  • kamu tidak siap melakukan force-push protocol;
  • conflict semantic terlalu besar sehingga merge commit lebih jujur sebagai integration record.

27. Mental Checklist Sebelum git rebase

Tanyakan:

1. Branch ini private atau shared?
2. Apa upstream/base yang benar?
3. Commit mana yang akan direplay?
4. Apakah working tree bersih?
5. Apakah sudah fetch remote terbaru?
6. Apakah perlu backup branch?
7. Apakah ada stacked branch yang bergantung pada branch ini?
8. Apakah hasil perlu force push?
9. Apakah CI harus rerun?
10. Apakah reviewer perlu range-diff?

Jika kamu tidak bisa menjawab nomor 2 dan 3, jangan rebase dulu.

Inspect:

git merge-base HEAD origin/main
git log --oneline origin/main..HEAD
git log --oneline --graph --decorate --boundary origin/main...HEAD

28. Lab: Observe Rebase Object Identity

Buat repo kecil:

mkdir /tmp/git-rebase-lab
cd /tmp/git-rebase-lab
git init

echo A > file.txt
git add file.txt
git commit -m "A"

echo B >> file.txt
git commit -am "B"

git switch -c feature
echo F1 >> feature.txt
git add feature.txt
git commit -m "F1"

echo F2 >> feature.txt
git commit -am "F2"

old_tip=$(git rev-parse HEAD)
old_f1=$(git rev-parse HEAD~1)

git switch main
echo C >> file.txt
git commit -am "C"

git switch feature
git rebase main

new_tip=$(git rev-parse HEAD)
new_f1=$(git rev-parse HEAD~1)

echo "old tip: $old_tip"
echo "new tip: $new_tip"
echo "old F1:  $old_f1"
echo "new F1:  $new_f1"

Bandingkan patch:

git show --stat $old_f1
git show --stat $new_f1

git patch-id < <(git show $old_f1)
git patch-id < <(git show $new_f1)

Perhatikan:

  • commit hash berubah;
  • patch bisa sama atau sangat mirip;
  • parent berubah;
  • branch pointer sekarang menunjuk ke commit baru.

29. Lab: Rebase Conflict and Inspect Stages

mkdir /tmp/git-rebase-conflict-lab
cd /tmp/git-rebase-conflict-lab
git init

echo "mode=old" > config.txt
git add config.txt
git commit -m "initial config"

git switch -c feature
echo "mode=feature" > config.txt
git commit -am "feature changes mode"

git switch main
echo "mode=main" > config.txt
git commit -am "main changes mode"

git switch feature
git rebase main

Saat conflict:

git status
git ls-files -u
git diff --base -- config.txt
git diff --ours -- config.txt
git diff --theirs -- config.txt

Resolve:

echo "mode=feature-on-main" > config.txt
git add config.txt
git rebase --continue

Catatan: jika editor commit message muncul, simpan dan keluar.


30. Engineering Invariant

Rebase yang sehat menjaga invariant ini:

The rewritten series should preserve the intended semantic changes,
be based on the correct upstream,
and be safe for every consumer of the rewritten branch.

Jika hanya linear tapi kehilangan intent, itu bukan rebase yang baik. Itu history kosmetik yang merusak correctness.


31. Ringkasan

Rebase adalah operasi kuat karena ia menggabungkan tiga hal:

  1. graph rewriting;
  2. patch replay;
  3. branch pointer movement.

Mental model yang benar:

old commits are not moved;
new commits are created;
branch ref is updated;
old commits are usually recoverable via reflog;
shared consumers must be protected.

Gunakan rebase untuk membentuk private history yang bersih dan reviewable. Jangan gunakan rebase untuk memutihkan fakta integrasi yang seharusnya tercatat, atau untuk rewrite shared branch tanpa protokol.

Di part berikutnya, kita akan masuk ke interactive rebase: bukan hanya replay branch di atas base baru, tapi mengedit patch series sebagai artefak desain.


Referensi

  • Git documentation: git rebase
  • Pro Git: Git Branching — Rebasing
  • Pro Git: Git Tools — Rewriting History
  • Git documentation: git rev-list, git merge-base, git reflog
  • Git documentation: git push --force-with-lease
Lesson Recap

You just completed lesson 23 in start here. Use the series map if you want to review the broader track, or continue directly into the next lesson while the context is still warm.

Continue The Track

Keep the momentum while the lesson is still fresh. Move backward for review or continue forward into the next concept.