Saya memahami skenario yang disajikan dalam Pro Git tentang The Perils of Rebasing . Penulis pada dasarnya memberi tahu Anda bagaimana menghindari komit yang digandakan:
Jangan rebase komitmen yang telah Anda dorong ke repositori publik.
Saya akan memberi tahu Anda situasi khusus saya karena menurut saya itu tidak benar-benar sesuai dengan skenario Pro Git dan saya masih berakhir dengan komit duplikat.
Katakanlah saya memiliki dua cabang jarak jauh dengan rekan lokalnya:
origin/master origin/dev
| |
master dev
Keempat cabang berisi komit yang sama dan saya akan memulai pengembangan di dev
:
origin/master : C1 C2 C3 C4
master : C1 C2 C3 C4
origin/dev : C1 C2 C3 C4
dev : C1 C2 C3 C4
Setelah beberapa komit, saya mendorong perubahan ke origin/dev
:
origin/master : C1 C2 C3 C4
master : C1 C2 C3 C4
origin/dev : C1 C2 C3 C4 C5 C6 # (2) git push
dev : C1 C2 C3 C4 C5 C6 # (1) git checkout dev, git commit
Saya harus kembali ke master
untuk membuat perbaikan cepat:
origin/master : C1 C2 C3 C4 C7 # (2) git push
master : C1 C2 C3 C4 C7 # (1) git checkout master, git commit
origin/dev : C1 C2 C3 C4 C5 C6
dev : C1 C2 C3 C4 C5 C6
Dan kembali ke dev
saya rebase perubahan untuk memasukkan perbaikan cepat dalam pengembangan saya yang sebenarnya:
origin/master : C1 C2 C3 C4 C7
master : C1 C2 C3 C4 C7
origin/dev : C1 C2 C3 C4 C5 C6
dev : C1 C2 C3 C4 C7 C5' C6' # git checkout dev, git rebase master
Jika saya menampilkan riwayat komit dengan GitX / gitk, saya perhatikan bahwa origin/dev
sekarang berisi dua komit yang identik C5'
dan C6'
yang berbeda dengan Git. Sekarang jika saya mendorong perubahan origin/dev
ini adalah hasilnya:
origin/master : C1 C2 C3 C4 C7
master : C1 C2 C3 C4 C7
origin/dev : C1 C2 C3 C4 C5 C6 C7 C5' C6' # git push
dev : C1 C2 C3 C4 C7 C5' C6'
Mungkin saya kurang memahami penjelasan di Pro Git, jadi saya ingin mengetahui dua hal:
- Mengapa Git menduplikasi komit ini saat melakukan rebasing? Apakah ada alasan khusus untuk melakukan itu daripada hanya melamar
C5
danC6
setelahnyaC7
? - Bagaimana saya bisa menghindarinya? Apakah bijaksana untuk melakukannya?
origin/dev
. Ketika didev
-rebased, sejarahnya diubah (C5 / C6 sementara dihapus dan diterapkan kembali setelah C7). Mengubah riwayat repo yang didorong umumnya adalah Ide Benar-benar Buruk ™ kecuali Anda tahu apa yang Anda lakukan. Dalam kasus sederhana ini, masalah dapat diselesaikan dengan melakukan dorongan paksa daridev
keorigin/dev
setelah rebase dan memberi tahu orang lain yang bekerjaorigin/dev
bahwa mereka mungkin akan mengalami hari yang buruk. Jawaban yang lebih baik, sekali lagi, adalah "jangan lakukan itu ... gunakan penggabungan"Jawaban singkat
Anda menghilangkan fakta bahwa Anda berlari
git push
, mendapatkan kesalahan berikut, dan kemudian melanjutkan untuk menjalankangit pull
:Meskipun Git berusaha membantu, saran 'git pull' yang dimilikinya kemungkinan besar bukanlah yang ingin Anda lakukan .
Jika Anda:
git push --force
untuk memperbarui remote dengan komitmen pasca-rebase Anda ( sesuai jawaban pengguna4405677 ).git rebase
sejak awal. Untuk memperbaruidev
dengan perubahan darimaster
, Anda harus, alih-alih menjalankangit rebase master dev
, jalankangit merge master
saat didev
( sesuai jawaban Justin ).Penjelasan yang sedikit lebih panjang
Setiap hash komit di Git didasarkan pada sejumlah faktor, salah satunya adalah hash komit yang ada sebelumnya.
Jika Anda menyusun ulang komit, Anda akan mengubah hash komit; rebasing (ketika melakukan sesuatu) akan mengubah hash komit. Dengan itu, hasil dari menjalankan
git rebase master dev
, di manadev
tidak sinkron denganmaster
, akan membuat komit baru (dan dengan demikian hash) dengan konten yang sama seperti yang adadev
tetapi dengan komit pada yangmaster
disisipkan sebelumnya.Anda bisa berakhir dalam situasi seperti ini dengan berbagai cara. Dua cara yang bisa saya pikirkan:
master
yang Anda inginkan sebagai dasardev
pekerjaan Andadev
yang telah didorong ke remote, yang kemudian Anda ubah (reword komit pesan, susun ulang komit, komit squash, dll.)Mari kita lebih memahami apa yang terjadi — berikut ini contohnya:
Anda memiliki repositori:
Anda kemudian melanjutkan untuk mengubah komit.
(Di sinilah Anda harus mengambil kata-kata saya: ada sejumlah cara untuk mengubah komit di Git. Dalam contoh ini saya mengubah waktunya
C3
, tetapi Anda memasukkan komit baru, mengubah pesan komit, menyusun ulang komit, meremas komitmen bersama, dll.)Di sinilah penting untuk diperhatikan bahwa hash komit berbeda. Ini adalah perilaku yang diharapkan karena Anda telah mengubah sesuatu (apa pun) tentang mereka. Ini tidak masalah, TAPI:
Mencoba mendorong akan menunjukkan kesalahan (dan petunjuk bahwa Anda harus lari
git pull
).Jika kita jalankan
git pull
, kita melihat log ini:Atau, ditunjukkan dengan cara lain:
Dan sekarang kami memiliki komitmen ganda secara lokal. Jika kami menjalankannya,
git push
kami akan mengirimnya ke server.Untuk menghindari sampai ke tahap ini, kita bisa saja lari
git push --force
(di mana kita malah larigit pull
). Ini akan mengirimkan komit kami dengan hash baru ke server tanpa masalah. Untuk memperbaiki masalah pada tahap ini, kami dapat mengatur ulang kembali ke sebelum kami menjalankangit pull
:Lihatlah reflog (
git reflog
) untuk melihat apa hash komit itu sebelum kita berlarigit pull
.Di atas kami melihat bahwa
ba7688a
kami berkomitmen sebelum berlarigit pull
. Dengan hash komit di tangan kita dapat mengatur ulang kembali ke itu (git reset --hard ba7688a
) dan kemudian menjalankangit push --force
.Dan kami selesai.
Tapi tunggu, saya terus mendasarkan pekerjaan dari komit yang digandakan
Jika Anda entah bagaimana tidak menyadari bahwa komit diduplikasi dan terus bekerja di atas komit duplikat, Anda benar-benar telah membuat kekacauan untuk diri Anda sendiri. Ukuran kekacauan sebanding dengan jumlah komitmen yang Anda miliki di atas duplikat.
Seperti apa ini:
Atau, ditunjukkan dengan cara lain:
Dalam skenario ini kami ingin menghapus komit duplikat, tetapi tetap mempertahankan komitmen yang telah kami lakukan berdasarkan komitmen tersebut — kami ingin mempertahankan C6 hingga C10. Seperti kebanyakan hal, ada beberapa cara untuk melakukannya:
Antara:
cherry-pick
masing-masing komit (termasuk C6 hingga C10) ke cabang baru itu, dan perlakukan cabang baru itu sebagai kanonik.git rebase --interactive $commit
, di mana$commit
komit sebelum kedua komit yang digandakan 2 . Di sini kita bisa langsung menghapus garis untuk duplikat.1 Tidak masalah mana yang Anda pilih, salah satu
ba7688a
atau2a2e220
berfungsi dengan baik.2 Dalam contoh itu akan
85f59ab
.TL; DR
Setel
advice.pushNonFastForward
kefalse
:sumber
git push
s--force-with-lease
saat ini karena ini adalah default yang lebih baikSaya pikir Anda melewatkan detail penting saat menjelaskan langkah Anda. Lebih khusus lagi, langkah terakhir Anda,
git push
pada dev, sebenarnya akan memberi Anda kesalahan, karena Anda biasanya tidak dapat mendorong perubahan non-fastforward.Jadi yang Anda lakukan
git pull
sebelum dorongan terakhir, yang menghasilkan komit gabungan dengan C6 dan C6 'sebagai induk, itulah sebabnya keduanya akan tetap terdaftar di log. Format log yang lebih cantik mungkin membuatnya lebih jelas bahwa mereka adalah cabang gabungan dari komit yang digandakan.Atau Anda membuat
git pull --rebase
(atau tanpa eksplisit--rebase
jika itu tersirat oleh konfigurasi Anda) sebagai gantinya, yang menarik C5 dan C6 asli kembali ke dev lokal Anda (dan selanjutnya merombak yang berikut ini ke hash baru, C7 'C5' 'C6' ').Salah satu jalan keluarnya adalah
git push -f
memaksa push ketika memberikan kesalahan dan menghapus C5 C6 dari aslinya, tetapi jika orang lain juga menariknya sebelum Anda menghapusnya, Anda akan mendapat lebih banyak masalah .. Pada dasarnya setiap orang yang memiliki C5 C6 tentu perlu melakukan langkah-langkah khusus untuk menghilangkannya. Itulah mengapa mereka mengatakan Anda tidak boleh mendasarkan kembali apa pun yang sudah diterbitkan. Ini masih bisa dilakukan jika mengatakan "penerbitan" ada dalam tim kecil.sumber
git pull
sangat penting. Rekomendasi Andagit push -f
, meskipun berbahaya, mungkin itulah yang dicari pembaca.git push --force
, hanya untuk melihat apa yang akan dilakukan Git. Saya belajar banyak tentang Git sejak itu dan saat inirebase
adalah bagian dari alur kerja normal saya. Namun, saya melakukannyagit push --force-with-lease
untuk menghindari menimpa karya orang lain.--force-with-lease
adalah default yang baik, saya akan meninggalkan komentar di bawah jawaban saya jugaSaya menemukan bahwa dalam kasus saya, masalah ini adalah konsekuensi dari masalah konfigurasi Git. (Melibatkan tarik dan penggabungan)
Deskripsi masalah:
Gejala: Komit diduplikasi pada cabang anak setelah rebase, menyiratkan banyak penggabungan selama dan setelah rebase.
Alur Kerja: Berikut adalah langkah-langkah alur kerja yang saya lakukan:
Sebagai konsekuensi dari alur kerja ini, duplikasi dari semua komit "Cabang-fitur" sejak rebase sebelumnya ... :-(
Masalahnya adalah karena tarikan perubahan cabang anak sebelum rebase. Konfigurasi pull default Git adalah "merge". Ini mengubah indeks komit yang dilakukan di cabang anak.
Solusinya: di file konfigurasi Git, konfigurasikan pull untuk bekerja dalam mode rebase:
Semoga bisa membantu JN Grx
sumber
Anda mungkin telah menarik dari cabang jauh yang berbeda dari saat ini. Misalnya Anda mungkin telah menarik diri dari Master ketika cabang Anda mengembangkan pelacakan berkembang. Git akan dengan patuh menarik komit duplikat jika ditarik dari cabang yang tidak terlacak.
Jika ini terjadi, Anda dapat melakukan hal berikut:
dimana
n == <number of duplicate commits that shouldn't be there.>
Kemudian pastikan Anda menarik dari cabang yang benar dan kemudian jalankan:
Menarik dengan
--rebase
akan memastikan Anda tidak menambahkan komitmen asing yang dapat memperburuk riwayat komit.Ini sedikit pegangan tangan untuk git rebase.
sumber