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:

bash
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 main

Siti meng-clone, menambah satu halaman, lalu mendorongnya:

bash
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 push

Budi menulis galeri.md lalu menyinkronkan diri — fetch, rebase di atas pembaruan Siti, baru push (pola sehat dari git rebase ):

bash
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 --all

Output yang Dihasilkan:

text
* 1a869b2 Tambah galeri
* 87c152e Tambah halaman kontak
* 19cdf59 Buat halaman beranda

Riwayat 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:

bash
git bundle create cadangan.bundle main
git commit --amend -m "Tambah halaman galeri"

Output yang Dihasilkan (git commit –amend):

text
[main 899847b] Tambah halaman galeri
 Date: Sat Oct 24 09:10:00 2026 +0700
 1 file changed, 1 insertion(+)
 create mode 100644 galeri.md

Amend melahirkan commit baru — bandingkan identitasnya dengan yang tercatat di remote:

bash
git rev-parse origin/main

Output yang Dihasilkan:

text
1a869b2aa9698df334a3d38b1ff76fffc4ea4413
bash
git rev-parse HEAD

Output yang Dihasilkan:

text
899847b11113d794f7bcf3a28ab8fd84bce3d58d
bash
git log --oneline --graph --all --decorate

Output yang Dihasilkan:

text
* 899847b (HEAD -> main) Tambah halaman galeri
| * 1a869b2 (origin/main) Tambah galeri
|/  
* 87c152e Tambah halaman kontak
* 19cdf59 Buat halaman beranda

Dua 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:

bash
git push origin main

Output yang Dihasilkan:

text
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:

bash
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/main

Output yang Dihasilkan (git log –oneline origin/main):

text
d366e1b Tambah halaman tentang
1a869b2 Tambah galeri
87c152e Tambah halaman kontak
19cdf59 Buat halaman beranda

Remote kini memuat empat commit — kerjaan Siti tercatat resmi. Budi mencoba pintu yang lebih sopan dulu:

bash
cd ../latihan-aturan
git push --force-with-lease origin main

Output yang Dihasilkan:

text
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:

bash
git push --force origin main

Output yang Dihasilkan:

text
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:

bash
cd ../toko-e-rekan
git fetch origin

Output yang Dihasilkan:

text
From ../toko-e
 + d366e1b...899847b main       -> origin/main  (forced update)
bash
git status

Output yang Dihasilkan:

text
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 clean
bash
git log --oneline --graph --all --decorate

Output yang Dihasilkan:

text
* d366e1b (HEAD -> main) Tambah halaman tentang
* 1a869b2 Tambah galeri
| * 899847b (origin/main, origin/HEAD) Tambah halaman galeri
|/  
* 87c152e Tambah halaman kontak
* 19cdf59 Buat halaman beranda

Tiga 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 #

PraktikStatusAlasan
git revert di branch sharedamanhanya menambah commit; rekan tinggal menariknya
git reset di riwayat lokal yang belum di-pushamanbelum ada orang lain yang memegang hash-nya
rebase, amend, reset di branch pribadi pra-PRamanriwayat belum dibagikan
rebase, amend, reset di branch shared yang ter-pushdilaranghash acuan rekan rusak; sinkronisasi berikutnya menuntut penimpaan
git push --force ke branch shareddilarangmenimpa kerjaan rekan yang belum ditarik
git push --force-with-lease bila terpaksamitigasimenolak bila remote berisi commit yang belum kita ketahui

Checklist Sebelum Menulis Ulang #

Empat langkah berikut meminimalkan kerusakan bila penulisan ulang benar-benar diperlukan:

  1. Ukur jaraknya: git status dan git log origin/main..HEAD memperlihatkan seberapa jauh riwayat lokal bercerai dengan remote.
  2. Tanyakan kepemilikan: adakah orang lain yang mungkin sudah menarik branch ini? Bila ragu, perlakukan sebagai shared dan pilih revert.
  3. Buat cadangan: git bundle create cadangan.bundle main mengemas seluruh riwayat branch jadi satu berkas yang bisa di-clone kembali. Periksa isinya:
bash
git bundle verify cadangan.bundle

Output yang Dihasilkan:

text
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: sha1

Keluaran 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 .

  1. Baru tulis ulang dan dorong dengan git push --force-with-lease — bila ditolak stale 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 .