Sepanjang section ini kita mengumpulkan peralatan lengkap penulis ulang riwayat: git rebase , interactive rebase , reset , hingga amend . Semua demo terasa aman karena satu syarat yang selalu terpenuhi diam-diam: riwayatnya masih milik kita. Chapter penutup ini membuat syarat itu eksplisit — aturan emasnya, konsekuensi nyata pelanggarannya, pengecualiannya, dan protokol bila terpaksa.
Aturan Emas: Riwayat Shared Tidak Ditulis Ulang #
Aturannya satu kalimat: riwayat yang sudah di-push ke branch yang dipakai bersama tidak ditulis ulang. Alasannya melekat pada identitas commit: hash dihitung dari isi, pesan, penulis, dan parent — ganti satu saja, hash berubah total (bedahnya di Commit Hash dan References ). Menulis ulang berarti melahirkan hash baru, sementara rekan memegang hash lama; dua salinan riwayat yang tadinya sama mendadak bercerai.
Empat perintah ini semua penulis ulang riwayat: git rebase, git reset, git commit --amend, dan pembersih massal seperti git-filter-repo
. Sebaliknya git revert dan git cherry-pick hanya menambah commit tanpa menyentuh yang lama — aman untuk branch mana pun; itulah mengapa alur di Revert vs Reset
bermuara ke revert untuk branch shared.
Konsekuensi Nyata: Push Ditolak #
Skenarionya: Budi bekerja di latihan-aturan dengan remote shared toko-e.git, dan Siti memakai clone toko-e-rekan. Bangun sisi Budi:
mkdir latihan-aturan && cd latihan-aturan
git init -b main
git config user.name "Budi Santoso"
git config user.email "[email protected]"
printf 'beranda\n' > situs.md
git add situs.md
git commit -m "Buat halaman beranda"
git init --bare ../toko-e.git
git remote add origin ../toko-e.git
git push -u origin mainSiti meng-clone, menambah satu halaman, lalu mendorongnya:
git clone ../toko-e.git ../toko-e-rekan
cd ../toko-e-rekan
git config user.name "Budi Santoso"
git config user.email "[email protected]"
printf 'kontak\n' > kontak.md
git add kontak.md
git commit -m "Tambah halaman kontak"
git pushBudi menulis galeri.md lalu menyinkronkan diri — fetch, rebase di atas pembaruan Siti, baru push (pola sehat dari git rebase
):
cd ../latihan-aturan
printf 'galeri\n' > galeri.md
git add galeri.md
git commit -m "Tambah galeri"
git fetch origin
git rebase origin/main
git push origin main
git log --oneline --graph --allOutput yang Dihasilkan:
* 1a869b2 Tambah galeri
* 87c152e Tambah halaman kontak
* 19cdf59 Buat halaman berandaRiwayat shared sehat — lalu Budi meng-amend pesan Tambah galeri yang dirasanya terlalu singkat (pola Amend Commit
); sebelum itu Budi sempat membuat cadangan cadangan.bundle, kebiasaan yang akan menyelamatkan:
git bundle create cadangan.bundle main
git commit --amend -m "Tambah halaman galeri"Output yang Dihasilkan (git commit –amend):
[main 899847b] Tambah halaman galeri
Date: Sat Oct 24 09:10:00 2026 +0700
1 file changed, 1 insertion(+)
create mode 100644 galeri.mdAmend melahirkan commit baru — bandingkan identitasnya dengan yang tercatat di remote:
git rev-parse origin/mainOutput yang Dihasilkan:
1a869b2aa9698df334a3d38b1ff76fffc4ea4413git rev-parse HEADOutput yang Dihasilkan:
899847b11113d794f7bcf3a28ab8fd84bce3d58dgit log --oneline --graph --all --decorateOutput yang Dihasilkan:
* 899847b (HEAD -> main) Tambah halaman galeri
| * 1a869b2 (origin/main) Tambah galeri
|/
* 87c152e Tambah halaman kontak
* 19cdf59 Buat halaman berandaDua garis bercerai di atas Tambah halaman kontak: HEAD -> main membawa versi amend, origin/main memegang versi lama. Isinya nyaris sama, tetapi bagi Git itu dua commit berbeda. Budi mencoba mendorongnya:
git push origin mainOutput yang Dihasilkan:
To ../toko-e.git
! [rejected] main -> main (non-fast-forward)
error: failed to push some refs to '../toko-e.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. If you want to integrate the remote changes,
hint: use 'git pull' before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.Push ditolak: ! [rejected] main -> main (non-fast-forward). HEAD Budi bukan turunan origin/main, jadi push tidak bisa maju cepat — penolakan ini penyelamat pertama dari penimpaan tanpa sengaja.
Skenario Terburuk: Force Push Menimpa Kerjaan Rekan #
Sementara itu Siti terus bekerja: menarik galeri Budi, menambah satu halaman, lalu push:
cd ../toko-e-rekan
git fetch origin
git rebase origin/main
printf 'tentang\n' > tentang.md
git add tentang.md
git commit -m "Tambah halaman tentang"
git push
git log --oneline origin/mainOutput yang Dihasilkan (git log –oneline origin/main):
d366e1b Tambah halaman tentang
1a869b2 Tambah galeri
87c152e Tambah halaman kontak
19cdf59 Buat halaman berandaRemote kini memuat empat commit — kerjaan Siti tercatat resmi. Budi mencoba pintu yang lebih sopan dulu:
cd ../latihan-aturan
git push --force-with-lease origin mainOutput yang Dihasilkan:
To ../toko-e.git
! [rejected] main -> main (stale info)
error: failed to push some refs to '../toko-e.git'Penolakan (stale info) ini justru penyelamat kedua: salinan remote-tracking Budi masih mengira origin/main di hash lama, padahal remote sudah berisi kerjaan Siti yang belum Budi ketahui — --force-with-lease menolak menimpa commit yang tak dikenalnya. Perbedaan perilakunya dengan --force polos dibedah di Non-Fast-Forward Push
. Budi mengabaikan sinyal itu dan memakai tenaga penuh:
git push --force origin mainOutput yang Dihasilkan:
To ../toko-e.git
+ d366e1b...899847b main -> main (forced update)Tanda + dan (forced update) mengumumkan remote digeser paksa: d366e1b milik Siti digulingkan 899847b milik Budi. Dampaknya terasa saat Siti menarik ulang:
cd ../toko-e-rekan
git fetch originOutput yang Dihasilkan:
From ../toko-e
+ d366e1b...899847b main -> origin/main (forced update)git statusOutput yang Dihasilkan:
On branch main
Your branch and 'origin/main' have diverged,
and have 2 and 1 different commits each, respectively.
(use "git pull" if you want to integrate the remote branch with yours)
nothing to commit, working tree cleangit log --oneline --graph --all --decorateOutput yang Dihasilkan:
* d366e1b (HEAD -> main) Tambah halaman tentang
* 1a869b2 Tambah galeri
| * 899847b (origin/main, origin/HEAD) Tambah halaman galeri
|/
* 87c152e Tambah halaman kontak
* 19cdf59 Buat halaman berandaTiga keluaran itu satu kisah: fetch melaporkan origin/main berpindah paksa ke 899847b. Status menyebut angkanya: diverged, and have 2 and 1 different commits each — main Siti membawa dua commit yang tak lagi ada di remote (Tambah halaman tentang plus Tambah galeri versi lama), origin/main membawa satu commit tak dikenalnya (versi amend Budi). Graf terakhir memperlihatkan d366e1b tergantung di jalur yang tak lagi dirujuk remote.
Kerjaan Siti tidak terhapus dari laptopnya — tapi lenyap dari riwayat bersama. Rekan ketiga yang meng-clone toko-e.git kini hanya mendapati tiga commit tanpa tentang.md; satu-satunya salinan d366e1b hidup di mesin Siti. Bila laptop itu rusak sebelum Siti sempat mendorong ulang, commit itu mati.
Pengecualian: Branch Pribadi Sebelum Pull Request #
Aturan emas punya batas yang sehat. Branch fitur yang belum dibuka pull request-nya adalah wilayah pribadi — belum ada orang lain yang menariknya, jadi silakan rebase, amend, bahkan reset untuk merapikannya sebelum mendorongnya. Pola umumnya: bekerja kotor di lokal, merapikan lewat interactive rebase , baru push dan membuka pull request .
Setelah pull request terbuka dan direview, perubahan baru sebaiknya masuk sebagai commit biasa agar diskusi review tetap terbaca; sebagian tim memang menganjurkan rebase ringkas sebelum merge final — tetap wajar di branch pribadi selama push-nya memakai --force-with-lease. Yang wajib dilindungi justru branch integrasi seperti main dan develop; banyak platform menyediakan protected branches
yang menolak force push sama sekali.
Tabel Praktik Aman dan Tidak Aman #
| Praktik | Status | Alasan |
|---|---|---|
git revert di branch shared | aman | hanya menambah commit; rekan tinggal menariknya |
git reset di riwayat lokal yang belum di-push | aman | belum ada orang lain yang memegang hash-nya |
| rebase, amend, reset di branch pribadi pra-PR | aman | riwayat belum dibagikan |
| rebase, amend, reset di branch shared yang ter-push | dilarang | hash acuan rekan rusak; sinkronisasi berikutnya menuntut penimpaan |
git push --force ke branch shared | dilarang | menimpa kerjaan rekan yang belum ditarik |
git push --force-with-lease bila terpaksa | mitigasi | menolak bila remote berisi commit yang belum kita ketahui |
Checklist Sebelum Menulis Ulang #
Empat langkah berikut meminimalkan kerusakan bila penulisan ulang benar-benar diperlukan:
- Ukur jaraknya:
git statusdangit log origin/main..HEADmemperlihatkan seberapa jauh riwayat lokal bercerai dengan remote. - Tanyakan kepemilikan: adakah orang lain yang mungkin sudah menarik branch ini? Bila ragu, perlakukan sebagai shared dan pilih revert.
- Buat cadangan:
git bundle create cadangan.bundle mainmengemas seluruh riwayat branch jadi satu berkas yang bisa di-clone kembali. Periksa isinya:
git bundle verify cadangan.bundleOutput yang Dihasilkan:
cadangan.bundle is okay
The bundle contains this ref:
1a869b2aa9698df334a3d38b1ff76fffc4ea4413 refs/heads/main
The bundle records a complete history.
The bundle uses this hash algorithm: sha1Keluaran The bundle records a complete history. menegaskan cadangan itu berdiri sendiri. Di sandbox, cadangan.bundle dibuat sebelum amend — riwayat lama tetap tersimpan utuh meski force push terlanjur terjadi; tekniknya dibahas di Backup Repository
.
- Baru tulis ulang dan dorong dengan
git push --force-with-lease— bila ditolakstale info, berhenti dan tinjau dulu apa yang berubah di remote.
d366e1b milik Siti sesungguhnya masih bisa ditolong: objeknya hidup di object database selama belum dibuang, dan cadangan.bundle Budi menyimpan jalur riwayat lama. Git punya jaring pengaman lebih banyak daripada yang terlihat — reflog, objek dangling, cadangan bundle. Cara menggali semuanya kembali saat kehilangan terjadi adalah pokok bahasan section berikutnya: Pemulihan dan Recovery
.