Sebagian besar jawaban sebelumnya salah!
Jangan lakukan ini:
git branch -t newbranch
git reset --hard HEAD~3
git checkout newbranch
Saat lain kali Anda menjalankan git rebase
(atau git pull --rebase
) 3 komit itu akan dibuang secara diam-diam newbranch
! (lihat penjelasan di bawah)
Alih-alih lakukan ini:
git reset --keep HEAD~3
git checkout -t -b newbranch
git cherry-pick ..HEAD@{2}
- Pertama, membuang 3 komit terbaru (
--keep
seperti --hard
, tapi lebih aman, karena gagal daripada membuang perubahan yang tidak dikomit).
- Lalu itu memotong
newbranch
.
- Kemudian dia memilih 3 komit itu kembali
newbranch
. Karena mereka tidak lagi direferensikan oleh cabang, ia melakukannya dengan menggunakan reflog git : HEAD@{2}
adalah komit yang HEAD
digunakan untuk merujuk ke 2 operasi yang lalu, yaitu sebelum kita 1. memeriksa newbranch
dan 2. digunakangit reset
untuk membuang 3 commit.
Peringatan: reflog diaktifkan secara default, tetapi jika Anda telah menonaktifkannya secara manual (misalnya dengan menggunakan repositori git "bare"), Anda tidak akan bisa mendapatkan 3 komit kembali setelah menjalankan git reset --keep HEAD~3
.
Alternatif yang tidak bergantung pada reflog adalah:
# newbranch will omit the 3 most recent commits.
git checkout -b newbranch HEAD~3
git branch --set-upstream-to=oldbranch
# Cherry-picks the extra commits from oldbranch.
git cherry-pick ..oldbranch
# Discards the 3 most recent commits from oldbranch.
git branch --force oldbranch oldbranch~3
(jika Anda mau, Anda bisa menulis @{-1}
- cabang yang sebelumnya diperiksa - bukan oldbranch
).
Penjelasan teknis
Mengapa git rebase
membuang 3 komit setelah contoh pertama? Ini karenagit rebase
tanpa argumen memungkinkan --fork-point
opsi secara default, yang menggunakan reflog lokal untuk mencoba menjadi kuat terhadap cabang hulu yang didorong paksa.
Misalkan Anda bercabang asal / master ketika berisi komit M1, M2, M3, lalu buat tiga komit sendiri:
M1--M2--M3 <-- origin/master
\
T1--T2--T3 <-- topic
tapi kemudian seseorang menulis ulang sejarah dengan memaksa asal / master untuk menghapus M2:
M1--M3' <-- origin/master
\
M2--M3--T1--T2--T3 <-- topic
Dengan menggunakan reflog lokal Anda, git rebase
dapat melihat bahwa Anda melakukan forked dari inkarnasi awal dari cabang asal / master, dan karenanya M2 dan M3 commit tidak benar-benar bagian dari cabang topik Anda. Oleh karena itu cukup beralasan bahwa sejak M2 telah dihapus dari cabang hulu, Anda tidak lagi menginginkannya di cabang topik Anda begitu cabang cabang dimundurkan:
M1--M3' <-- origin/master
\
T1'--T2'--T3' <-- topic (rebased)
Perilaku ini masuk akal, dan umumnya hal yang benar untuk dilakukan ketika rebasing.
Jadi alasan mengapa perintah berikut gagal:
git branch -t newbranch
git reset --hard HEAD~3
git checkout newbranch
karena mereka meninggalkan reflog dalam kondisi yang salah. Git melihat newbranch
telah memotong cabang upstream pada revisi yang menyertakan 3 commit, lalu reset --hard
menulis ulang sejarah upstream untuk menghapus commit, dan lain kali Anda menjalankannya git rebase
membuangnya seperti halnya komit lain yang telah dihapus dari upstream.
Tetapi dalam kasus khusus ini kami ingin 3 komitmen tersebut dianggap sebagai bagian dari cabang topik. Untuk mencapai itu, kita perlu memotong upstream pada revisi sebelumnya yang tidak termasuk 3 komitmen. Itulah yang dilakukan solusi yang saya sarankan, karena itu keduanya meninggalkan reflog dalam kondisi yang benar.
Untuk lebih jelasnya, lihat definisi --fork-point
di git rebase dan git merge-base docs.