Git push ditolak setelah cabang fitur rebase

919

OK, saya pikir ini adalah skenario git sederhana, apa yang saya lewatkan?

Saya memiliki mastercabang dan featurecabang. Saya melakukan beberapa pekerjaan master, beberapa feature, dan kemudian beberapa lagi master. Saya berakhir dengan sesuatu seperti ini (urutan leksikografis menyiratkan urutan komitmen):

A--B--C------F--G  (master)
       \    
        D--E  (feature)

Saya tidak punya masalah untuk git push origin mastermenjaga remote masterdiperbarui, atau dengan git push origin feature(saat aktif feature) untuk menjaga cadangan jarak jauh untuk featurepekerjaan saya . Sampai sekarang, kami baik-baik saja.

Tetapi sekarang saya ingin rebase featuredi atas F--Gkomit pada tuan, jadi saya git checkout featuredan git rebase master. Masih bagus. Sekarang kita punya:

A--B--C------F--G  (master)
                 \
                  D'--E'  (feature)

Masalah: saat saya ingin mem-backup featurecabang rebased baru dengan git push origin feature, dorongan ditolak karena pohon telah berubah karena rebasing. Ini hanya dapat diselesaikan dengan git push --force origin feature.

Saya benci menggunakan --forcetanpa yakin saya membutuhkannya. Jadi, apakah saya membutuhkannya? Apakah rebasing harus menyiratkan bahwa berikutnya pushharus --forcememuaskan?

Cabang fitur ini tidak dibagi dengan pengembang lain, jadi saya tidak punya masalah secara de facto dengan dorongan paksa, saya tidak akan kehilangan data apa pun, pertanyaannya lebih konseptual.

Yuval Adam
sumber

Jawaban:

682

Masalahnya adalah git pushmengasumsikan bahwa cabang jarak jauh dapat diteruskan dengan cepat ke cabang lokal Anda, yaitu bahwa semua perbedaan antara cabang lokal dan cabang jarak jauh adalah di daerah memiliki beberapa komitmen baru pada akhirnya seperti itu:

Z--X--R         <- origin/some-branch (can be fast-forwarded to Y commit)
       \        
        T--Y    <- some-branch

Ketika Anda melakukan git rebasekomit D dan E diterapkan ke basis baru dan komit baru dibuat. Itu berarti setelah rebase Anda memiliki sesuatu seperti itu:

A--B--C------F--G--D'--E'   <- feature-branch
       \  
        D--E                <- origin/feature-branch

Dalam situasi itu cabang terpencil tidak dapat diteruskan ke lokal. Meskipun, secara teoritis cabang lokal dapat digabungkan menjadi remote (jelas Anda tidak membutuhkannya dalam kasus itu), tetapi sebagaigit push melakukan hanya gabungan fast-forward yang dilemparkan dan kesalahannya.

Dan --forceopsi apa yang dilakukan adalah mengabaikan keadaan cabang jarak jauh dan mengaturnya ke komit yang Anda dorong. Jadi git push --force origin feature-branchcukup menimpa origin/feature-branchdengan lokal feature-branch.

Menurut pendapat saya, menghidupkan kembali fitur cabang masterdan mendorong mereka kembali ke repositori jarak jauh adalah OK selama Anda adalah satu-satunya yang bekerja pada cabang itu.

KL-7
sumber
68
Sejujurnya, menarik dan menggabungkan versi asli dari fitur cabang ke yang rebased agak menghilangkan seluruh gagasan rebasing.
KL-7
24
Mungkin saya tidak mengerti Anda dengan benar, tetapi jika Anda menarik cabang fitur, rebase ke cabang master baru, Anda tidak dapat mendorongnya kembali tanpa paksa, karena versi jauh dari cabang fitur tidak dapat diteruskan dengan cepat ke yang baru (rebased) versi cabang fitur. Itulah yang dijelaskan OP dalam pertanyaannya. Jika setelah rebasing, tetapi sebelum mendorong, Anda melakukannya git pull feature-branch, tarikan ini akan menghasilkan komit gabungan baru (dengan menggabungkan versi lokal dan jauh dari cabang fitur). Jadi, Anda mendapatkan gabungan yang tidak perlu setelah rebasing, atau Anda mendorong dengan --force.
KL-7
6
Ah, saya pikir saya mengerti. Anda menggambarkan pendekatan yang sama seperti dalam jawaban Mark Longair. Tapi itu menghasilkan komit gabungan. Ini mungkin berguna dalam beberapa kasus, tetapi saya menggunakan rebase sebagian besar di cabang fitur saya sendiri (karenanya push --forcebukan masalah) untuk menjaga linear komit tetap tanpa penggabungan komit sama sekali.
KL-7
11
Masalah dengan "force-push" adalah, bahwa Anda memang bisa "kehilangan barang" (melakukan sebelumnya), sesuatu yang biasanya TIDAK PERNAH mungkin terjadi dalam Sistem Kontrol Versi apa pun ➪ Untuk alasan itu setidaknya satu cabang "master-ish" harus memiliki pengaturan untuk tidak menerima paksa , untuk membatasi potensi kerusakan. (Sebutkan salah satu dari yang berikut: karyawan yang pemarah / dipecat, kebodohan sendiri, 'keputusan' yang lelah & bekerja terlalu keras ...).
Frank Nocke 6-15
13
--force-with-leaseseperti yang disarankan @hardev adalah pilihan yang bagus
augustorsouza
466

Alih-alih menggunakan -f atau - memaksa pengembang harus menggunakan

--force-with-lease

Mengapa? Karena memeriksa cabang jarak jauh untuk perubahan yang benar-benar ide yang bagus. Mari kita bayangkan bahwa James dan Lisa sedang bekerja di cabang fitur yang sama dan Lisa telah mendorong sebuah komit. James sekarang mengubah cabang lokalnya dan ditolak ketika mencoba mendorong. Tentu saja James berpikir ini karena rebase dan menggunakan --force dan akan menulis ulang semua perubahan Lisa. Jika James menggunakan - force-with-leasing, ia akan menerima peringatan bahwa ada komitmen yang dilakukan oleh orang lain. Saya tidak melihat mengapa ada orang yang menggunakan --force bukannya --force-with-leasing saat mendorong setelah rebase.

Hardev
sumber
33
Penjelasan yang bagus. git push --force-with-leasetelah menyelamatkan saya banyak.
ckib16
5
Ini adalah komentar yang bermanfaat, tetapi sebenarnya bukan jawaban untuk pertanyaan itu.
Dallin
4
Ini adalah jawabannya, rebasing untuk menguasai / mengembangkan menciptakan masalah, inilah sebabnya - memaksa-dengan-sewa ada.
Tamir Daniely
3
Ini harus menjadi jawaban yang diterima. Memecahkan persis masalah yang dideskripsikan - kekuatan mendorong tanpa memaksa jika orang lain berkomitmen untuk sementara waktu.
Luzian
3
Saya pikir jawaban yang diterima dan yang satu ini menjawab pertanyaan. Jawaban yang diterima menjelaskan mengapa Anda harus memaksa. Dan yang ini menjelaskan mengapa --force-with-leasemembahas masalah menggunakan--force
Jeff Appareti
48

Saya akan menggunakan "checkout -b" dan lebih mudah dimengerti.

git checkout myFeature
git rebase master
git push origin --delete myFeature
git push origin myFeature

ketika Anda menghapus, Anda mencegah untuk mendorong cabang keluar yang berisi ID SHA berbeda. Saya hanya menghapus cabang jarak jauh dalam kasus ini.

Eddy Hernandez
sumber
6
Ini berfungsi dengan baik, terutama jika tim Anda memiliki git hook yang menolak semua perintah push - force git.
Ryan Thames
1
terima kasih untuk itu berhasil. Berikut adalah detail lebih lanjut tentang yang saya baca untuk memahami lebih baik. Ini sangat berguna ketika Anda tidak ingin atau tidak dapat melakukan push force. Menghapus Cabang dan Rebasing Terpencil
RajKon
5
Ini memiliki hasil yang sama dengan push --force, jadi hanya cara untuk menghindari repro git --force. Dengan demikian, saya tidak berpikir ini adalah ide yang bagus - baik repo mengizinkan push --force, atau karena alasan yang baik itu menonaktifkannya. Jawaban Nabi lebih tepat jika --forcedinonaktifkan pada repo jarak jauh, karena tidak memiliki risiko kehilangan komit dari pengembang lain atau jika tidak menyebabkan masalah.
Logan Pickup
19

Salah satu solusi untuk ini adalah melakukan apa yang dilakukan skrip penggabungan msysGit - setelah rebase, gabungkan di kepala lama featuredengan -s ours. Anda berakhir dengan grafik komit:

A--B--C------F--G (master)
       \         \
        \         D'--E' (feature)
         \           /
          \       --
           \    /
            D--E (old-feature)

... dan dorongan Anda feature akan menjadi maju cepat.

Dengan kata lain, Anda dapat melakukan:

git checkout feature
git branch old-feature
git rebase master
git merge -s ours old-feature
git push origin feature

(Tidak diuji, tapi saya pikir itu benar ...)

Mark Longair
sumber
26
Saya percaya alasan paling umum untuk menggunakan git rebase(alih-alih menggabungkan masterkembali ke cabang fitur Anda) adalah membuat histori linear yang bersih. Dengan pendekatan Anda, sejarah menjadi semakin buruk. Dan karena rebasing membuat komit baru tanpa referensi ke versi sebelumnya, saya bahkan tidak yakin bahwa hasil penggabungan ini akan memadai.
KL-7
6
@ KL-7: Intinya merge -s oursadalah bahwa itu secara buatan menambahkan referensi induk ke versi sebelumnya. Tentu, sejarahnya tidak kelihatan bersih, tetapi si penanya tampaknya sangat terganggu karena harus memaksakan dorongan featurecabang, dan ini berhasil. Jika Anda ingin rebase, ini lebih atau kurang satu atau yang lain. :) Lebih umum, saya pikir itu menarik bahwa proyek msysgit melakukan ini ....
Mark Longair
@ KL-7: Kebetulan, saya memberi +1 jawaban Anda, yang jelas jawaban yang tepat - Saya hanya berpikir ini mungkin menarik juga.
Mark Longair
Itu pasti menarik, setidaknya bagi saya. Terima kasih. Saya telah melihat oursstrategi sebelumnya, tetapi saya pikir itu hanya berlaku untuk situasi konflik dengan secara otomatis menyelesaikannya menggunakan perubahan di cabang kami. Ternyata kerjanya berbeda. Dan bekerja dengan cara itu sangat berguna jika Anda memerlukan versi rebased (misalnya, untuk repo maintainer menerapkannya dengan bersih master) tetapi ingin menghindari memaksakan dorongan (jika banyak ppl lain karena alasan tertentu menggunakan cabang fitur Anda).
KL-7
15

Mungkin atau mungkin tidak ada kasus bahwa hanya ada satu pengembang di cabang ini, yaitu sekarang (setelah rebase) tidak sejalan dengan asal / fitur.

Karena itu saya menyarankan untuk menggunakan urutan berikut:

git rebase master
git checkout -b feature_branch_2
git push origin feature_branch_2

Ya, cabang baru, ini harus menyelesaikan ini tanpa --force, yang saya pikir umumnya adalah kelemahan utama git.

JAR.JAR.beans
sumber
3
Maaf untuk mengatakan, tetapi: "Terus hasilkan cabang" untuk menghindari penimpaan paksa yang ada tidak membantu "pengembang fitur kesepian" (yang dapat mengesampingkan) atau beberapa orang yang bekerja pada cabang fitur (perlu mengkomunikasikan "kenaikan" cabang itu dan memberi tahu untuk pindah, kawan). - Ini lebih seperti versi manual ("thesis_00.doc, thesis_01.doc, ..."), dalam sistem versi ...
Frank Nocke
2
Plus, ini tidak membantu ketika Anda memiliki PR github dibuka pada satu nama cabang, Anda harus membuat PR baru untuk nama cabang baru yang Anda dorong.
gprasant
1
@ Frankee Setengah benar dari pengalaman saya. untuk pengembang yang kesepian, ya, memaksa saja sudah cukup mudah, tapi itu kebiasaan yang mungkin menggigitmu nanti. + seorang dev baru saja bergabung? atau mungkin beberapa sistem CI yang tidak menggunakan --hard reset? untuk tim yang berkolaborasi, saya pikir mengkomunikasikan nama cabang yang baru cukup mudah, ini dapat dengan mudah dituliskan juga + untuk tim, saya akan menyarankan untuk rebase secara lokal atau ketika cabang siap untuk bergabung, bukan pada hari ke hari kerja , komit tambahan tidak lebih merepotkan daripada berurusan dengan rebase / gabungkan konflik sebagai hasilnya.
JAR.JAR.beans
@ gpasant untuk PR, sekali lagi, saya pikir ini akan salah untuk rebase, saya sebenarnya ingin melihat komit tunggal dengan perbaikan PR. Rebase (squash) harus terjadi hanya kemudian sebagai bagian dari penggabungan untuk menguasai dan ketika PR selesai dan siap (jadi tidak perlu PR baru dibuka).
JAR.JAR.beans
13

Cara saya menghindari dorongan gaya adalah dengan membuat cabang baru dan melanjutkan pekerjaan pada cabang baru itu dan setelah beberapa stabilitas, hapus cabang lama yang telah direstrukturisasi:

  • Rebasing cabang yang dicek secara lokal
  • Bercabang dari cabang rebased ke cabang baru
  • Mendorong cabang itu sebagai cabang baru ke jarak jauh. dan menghapus cabang lama di remote
Nabi
sumber
1
Mengapa tidak ada cinta untuk opsi ini? Ini jelas yang paling bersih, paling sederhana, paling aman.
cdmo
Karena saya memiliki sekitar 200 sistem yang melacak nama cabang, dan itu harus menjadi nama spesifik untuk tugas tersebut, dan jika saya mulai melakukan perubahan nama cabang setiap dorongan, saya akan kehilangan akal.
Tamir Daniely
@TamirDaniely saya belum mencoba, tetapi apakah menghapus cabang lama (dari jarak jauh) sebelum mendorong dan mendorong cabang baru dengan nama lama yang sama menyelesaikan masalah Anda?
Nabi
2
@Nabi Itulah yang dilakukan --force-with-leasing, kecuali itu juga memverifikasi bahwa tidak ada komitmen baru yang bukan milik Anda.
Tamir Daniely
12

Yang lain telah menjawab pertanyaan Anda. Jika Anda rebase cabang, Anda harus memaksa untuk mendorong cabang itu.

Rebase dan repositori bersama umumnya tidak cocok. Ini adalah penulisan ulang sejarah. Jika orang lain menggunakan cabang itu atau telah bercabang dari cabang itu maka rebase akan sangat tidak menyenangkan.

Secara umum, rebase bekerja dengan baik untuk manajemen cabang lokal. Manajemen cabang jarak jauh bekerja paling baik dengan penggabungan eksplisit (--no-ff).

Kami juga menghindari penggabungan master ke cabang fitur. Alih-alih, kami rebase menjadi master tetapi dengan nama cabang baru (mis. Menambahkan akhiran versi). Ini menghindari masalah rebasing di repositori bersama.

Bill Door
sumber
5
Bisakah Anda menambahkan contoh?
Thermech
8

Apa yang salah dengan git merge masterdi featurecabang? Ini akan mempertahankan pekerjaan yang Anda miliki, sambil tetap memisahkannya dari cabang jalur utama.

A--B--C------F--G
       \         \
        D--E------H

Sunting: Ah maaf tidak membaca pernyataan masalah Anda. Anda akan membutuhkan kekuatan saat Anda melakukan a rebase. Semua perintah yang mengubah riwayat akan membutuhkan --forceargumen. Ini adalah cara yang gagal untuk mencegah Anda kehilangan pekerjaan (yang lama Ddan Eakan hilang).

Jadi, Anda melakukan apa git rebaseyang membuat pohon itu terlihat (meskipun sebagian disembunyikan Ddan Etidak lagi dalam cabang bernama):

A--B--C------F--G
       \         \
        D--E      D'--E'

Jadi, ketika mencoba untuk mendorong featurecabang baru Anda (dengan D'dan E'di dalamnya), Anda akan kehilangan Ddan E.

Bouke
sumber
3
Tidak ada yang salah dengan itu, dan saya tahu itu akan berhasil. Itu bukan apa yang saya butuhkan. Seperti saya katakan, pertanyaannya lebih konseptual daripada praktis.
Yuval Adam
4

Bagi saya langkah-langkah mudah berikut berfungsi:

1. git checkout myFeature
2. git rebase master
3. git push --force-with-lease
4. git branch -f master HEAD
5. git checkout master
6. git pull

Setelah melakukan semua hal di atas, kita dapat menghapus cabang myFeature juga dengan mengikuti perintah:

git push origin --delete myFeature
Neeraj Khede
sumber
3

Berikut ini berfungsi untuk saya:

git push -f origin branch_name

dan itu tidak menghapus kode saya.

Tetapi, jika Anda ingin menghindari ini maka Anda dapat melakukan hal berikut:

git checkout master
git pull --rebase
git checkout -b new_branch_name

maka Anda dapat memilih semua komitmen Anda ke cabang baru. git cherry-pick COMMIT ID dan kemudian dorong cabang baru Anda.

Sohair Ahmad
sumber
5
-fadalah alias untuk --force, yang merupakan pertanyaan yang coba dihindari jika mungkin.
epochengine
1

Karena OP memang memahami masalahnya, hanya mencari solusi yang lebih baik ...

Bagaimana ini sebagai praktik?

  • Miliki cabang pengembangan fitur yang sebenarnya (di mana Anda tidak pernah rebase dan memaksa, jadi sesama pengembang fitur Anda tidak membenci Anda). Di sini, secara teratur ambil perubahan itu dari main dengan penggabungan. Sejarah Messier , ya, tapi hidup itu mudah dan tidak ada yang terganggu dalam karyanya.

  • Memiliki cabang pengembangan-fitur yang kedua, di mana satu anggota tim pengawas fitur mendorong semua fitur berkomitmen untuk, memang diubah, memang dipaksa. Jadi hampir bersih berdasarkan komit master yang cukup baru. Setelah fitur lengkap, dorong cabang itu di atas master.

Mungkin sudah ada nama pola untuk metode ini.

Frank Nocke
sumber