cabang master dan 'asal / master' telah menyimpang, bagaimana cara 'undiverge' cabang '?

965

Entah bagaimana majikan saya dan cabang asal / master saya telah menyimpang.
Saya sebenarnya tidak ingin mereka menyimpang.

Bagaimana saya bisa melihat perbedaan ini dan menggabungkannya?

jujur
sumber
2
apa yang Anda maksud dengan menyimpang? apakah Anda rebase tuanmu setelah mendorongnya?
Hasen
13
Saya mendapat pesan yang mengatakan "Cabang dan 'asal / master' Anda telah berbeda, # dan masing-masing memiliki 1 dan 1 komit yang berbeda."
Frank
Saya telah memperbarui jawaban saya untuk mencerminkan pesan peringatan yang "berbeda".
VonC
Jawaban yang diterima untuk pertanyaan lain mungkin juga membantu dalam menyelesaikan kasus-kasus tertentu di mana ini mungkin ikut bermain (misalnya Anda mencoba untuk memindahkan master Anda, tetapi sudah didorong): stackoverflow.com/questions/2862590/…
lindes
8
Penjelasan di blog ini membantu saya jauh lebih banyak daripada jawaban di bawah ini: sebgoo.blogspot.com/2012/02/…

Jawaban:

1014

Anda dapat meninjau perbedaannya dengan:

git log HEAD..origin/master

sebelum menariknya (ambil + gabung) (lihat juga "Bagaimana Anda membuat git selalu menarik dari cabang tertentu?" )


Ketika Anda memiliki pesan seperti:

"Cabang dan 'asal / master' Anda masing-masing berbeda, # dan memiliki 1 dan 1 komitmen yang berbeda."

, periksa apakah Anda perlu memperbaruiorigin . Jika originup-to-date, maka beberapa komit telah didorong ke origindari repo lain saat Anda membuat komit Anda sendiri secara lokal.

... o ---- o ---- A ---- B  origin/master (upstream work)
                   \
                    C  master (your work)

Anda mendasarkan komit C pada komit A karena itu adalah pekerjaan terbaru yang Anda ambil dari hulu pada saat itu.

Namun, sebelum Anda mencoba untuk mendorong kembali ke asal, orang lain mendorong komit B.
Riwayat pengembangan telah menyimpang ke jalur yang terpisah.

Anda kemudian dapat menggabungkan atau rebase. Lihat Pro Git: Git Branching - Rebasing untuk detailnya.

Menggabungkan

Gunakan perintah git merge:

$ git merge origin/master

Ini memberitahu Git untuk mengintegrasikan perubahan dari origin/masterdalam pekerjaan Anda dan membuat komit gabungan.
Grafik sejarah sekarang terlihat seperti ini:

... o ---- o ---- A ---- B  origin/master (upstream work)
                   \      \
                    C ---- M  master (your work)

Penggabungan baru, komit M, memiliki dua orang tua, masing-masing mewakili satu jalur pengembangan yang mengarah ke konten yang tersimpan di komit itu.

Perhatikan bahwa sejarah di balik M sekarang non-linear.

Rebase

Gunakan perintah git rebase:

$ git rebase origin/master

Ini memberi tahu Git untuk mengulang komit C (pekerjaan Anda) seolah-olah Anda telah mendasarkannya pada komit B alih-alih A.
CVS dan pengguna Subversion secara rutin mengubah perubahan lokal mereka di atas pekerjaan hulu saat mereka memperbarui sebelum komit.
Git hanya menambahkan pemisahan eksplisit antara langkah komit dan rebase.

Grafik sejarah sekarang terlihat seperti ini:

... o ---- o ---- A ---- B  origin/master (upstream work)
                          \
                           C'  master (your work)

Komit C 'adalah komit baru yang dibuat oleh perintah git rebase.
Ini berbeda dari C dalam dua cara:

  1. Ini memiliki sejarah yang berbeda: B bukannya A.
  2. Kontennya menyumbang perubahan dalam B dan C; itu sama dengan M dari contoh gabungan.

Perhatikan bahwa sejarah di balik C 'masih linier.
Kami telah memilih (untuk saat ini) untuk hanya mengizinkan riwayat linear di cmake.org/cmake.git.
Pendekatan ini mempertahankan alur kerja berbasis CVS yang digunakan sebelumnya dan dapat memudahkan transisi.
Upaya memasukkan C 'ke dalam repositori kami akan berhasil (dengan asumsi Anda memiliki izin dan tidak ada yang mendorong saat Anda melakukan rebasing).

Perintah git pull menyediakan cara cepat untuk mengambil dari asal dan melakukan rebase pada pekerjaan lokal di dalamnya:

$ git pull --rebase

Ini menggabungkan langkah-langkah ambil dan rebase di atas menjadi satu perintah.

VONC
sumber
5
Saya menemukan ini sambil mencari masalah yang sama, dapatkah Anda menjelaskan mengapa 'git reset --hard HEAD' tidak memperbaiki masalah?
Neth
12
@Neth: karena ini bukan tentang modifikasi bertahap (yaitu modifikasi hadir dalam indeks tetapi belum dilakukan), tetapi tentang komit lokal (yang berbeda dari komit yang ada pada remote). git reset --hard HEADhanya akan menghapus modifikasi tidak berkomitmen yang diindeks lokal, dan tidak akan melakukan apa pun untuk mendamaikan perbedaan antara komitmen lokal dan jarak jauh . Hanya gabungan atau rebase yang akan menyatukan dua set komit (yang lokal dan yang jauh).
VonC
4
Wow, terima kasih atas tanggapan yang luar biasa ini. Kami tidak sengaja melakukan "git pull" tanpa "--rebase", dan "git rebase origin / master" hanyalah perbaikan!
mrooney
3
Bagaimana - saya hanya ingin mengabaikan / membuang perubahan lokal saya dan bersama dengan cabang lokal saya di mana remote berada? Dengan kata lain, saya ingin mastermenunjukkan Bcontoh Anda.
CygnusX1
23
@ CygnusX1 yang akan menjadi git reset --hard origin/masterseperti yang disebutkan dalam jawaban di bawah ini: stackoverflow.com/a/8476004/6309
VonC
726

Saya memiliki ini dan saya bingung tentang apa yang menyebabkannya, bahkan setelah membaca tanggapan di atas. Solusi saya adalah melakukan

git reset --hard origin/master

Kemudian itu hanya me-reset salinan master (lokal) saya (yang saya anggap kacau) ke titik yang benar, sebagaimana diwakili oleh (remote) asal / master.

PERINGATAN : Anda akan kehilangan semua perubahan yang belum didorong ke origin/master.

skiphoppy
sumber
21
ya, rasanya agak seperti opsi boneka, tetapi jika tidak ada bahaya nyata dan Anda di sini untuk perbaikan cepat - ini bekerja (bagi saya tetap)
PandaWood
7
Ini harus berada di cabang master sebelumnya ("git checkout master").
blueyed
Ini terjadi pada saya sekali ketika saya mendapat berita terbaru dari asal, maka orang lain melakukan push force to origin yang menyebabkan komit sebelumnya asal dikembalikan. Jadi cabang lokal saya memiliki komit yang dikembalikan, dan ketika saya mencoba untuk menarik terbaru dari asal dikatakan bahwa saya harus bergabung. Dalam kasus ini, masuk akal untuk mereset --hard origin / (branch) karena masalah telah diperbaiki pada awalnya.
Landon Poch
96
Anda mungkin harus memperingatkan pengguna bahwa ini akan membuat mereka kehilangan semua perubahan yang belum didorong ke asal
Pedro Loureiro
6
@PedroLoureiro Commits diketahui benar-benar hilang, Anda masih dapat menemukan komit dengan git reflogatau melihatnya gitk --all. Namun, tentu saja hard reset adalah hal lain selain rebase.
sebkraemer
52
git pull --rebase origin/master 

adalah satu perintah yang dapat membantu Anda sebagian besar waktu.

Sunting: Menarik komit dari sumber / master dan menerapkan perubahan Anda pada riwayat cabang yang baru ditarik.

asitmoharna
sumber
89
tolong sebutkan apa yang dilakukan perintah itu, kalau tidak orang akan menjalankannya dan akhirnya mengacaukannya
Baz1nga
1
Jika tidak ada masalah, Anda harus berakhir dengan master Anda yang berisi semua perubahan asal / master ditambah semua komit lokal Anda akan diputar ulang di atasnya. Sepertinya baik untuk saya.
Philipp Claßen
7
Kecuali ketika ada perbedaan nyata dan itu membuat Anda dalam rebase dibatalkan.
meraba
Ini menghasilkan kesalahan: kesalahan: Gagal menggabungkan perubahan. Patch gagal pada 0024 Model Request and Response
IgorGanapolsky
32

Saya menemukan diri saya dalam situasi ini ketika saya mencoba untuk rebase cabang yang melacak cabang terpencil, dan saya mencoba untuk rebase pada master. Dalam skenario ini jika Anda mencoba melakukan rebase, kemungkinan besar cabang Anda berbeda dan bisa membuat kekacauan yang bukan untuk git nube!

Katakanlah Anda berada di cabang my_remote_tracking_branch, yang bercabang dari master

$ git status

# Di cabang my_remote_tracking_branch

tidak ada yang berkomitmen (direktori kerja bersih)

Dan sekarang Anda mencoba untuk rebase dari master sebagai:

git rebase master

BERHENTI SEKARANG dan selamatkan diri Anda dari masalah! Alih-alih, gunakan gabungan sebagai:

git merge master

Ya, Anda akan berakhir dengan komitmen ekstra di cabang Anda. Tetapi kecuali jika Anda siap untuk "un-diverging" branch, ini akan menjadi alur kerja yang jauh lebih lancar daripada rebasing. Lihat blog ini untuk penjelasan yang lebih rinci.

Di sisi lain, jika cabang Anda hanya cabang lokal (yaitu belum didorong ke remote) Anda pasti harus melakukan rebase (dan cabang Anda tidak akan menyimpang dalam hal ini).

Sekarang jika Anda membaca ini karena Anda sudah berada dalam skenario "menyimpang" karena rebase tersebut, Anda dapat kembali ke komit terakhir dari asal (yaitu dalam keadaan tidak-menyimpang) dengan menggunakan:

git reset --barang asli / my_remote_tracking_branch

paneer_tikka
sumber
5
Aturan praktis adalah untuk digunakan rebasejika cabang yang Anda rebiling belum diterbitkan (dan digunakan oleh orang lain). Kalau tidak, gunakan merge. Jika Anda rebase cabang yang sudah diterbitkan (dan digunakan), Anda harus mengoordinasikan konspirasi untuk menulis ulang sejarah di setiap pengembang yang telah menggunakan cabang Anda.
Mikko Rantalainen
1
Sayangnya saya tidak membaca pesan ini sebelum melakukan git rebase master...
Vitaly Isaev
Jika saya melakukan git rebase master saat di cabang 'foobar' maka secara teknis foobar berbeda dari asal / foobar sampai saya melakukan git push -f, kan?
Relipse
1
git reset --hard origin/my_remote_tracking_branchadalah apa yang benar-benar bekerja
roothahn
24

Dalam kasus saya di sini adalah apa yang saya lakukan untuk menyebabkan pesan yang berbeda : Saya lakukan git pushtetapi kemudian git commit --amendmenambahkan sesuatu ke pesan komit. Kemudian saya juga melakukan komitmen lain.

Jadi dalam kasus saya itu hanya berarti asal / master sudah ketinggalan zaman. Karena saya tahu tidak ada orang lain yang menyentuh asal / master, perbaikannya sepele: git push -f (di mana -fberarti kekuatan)

Darren Cook
sumber
8
+1 untuk git push -fmenimpa perubahan yang sebelumnya dilakukan dan didorong ke asal. Saya juga yakin tidak ada orang lain yang menyentuh repositori.
zacharydl
4
Perintah yang sangat berisiko. Silakan tulis informasi singkat mengenai faktor risiko dari perintah.
J4cK
1
@ Trik: Saya sudah menggambarkan risiko: "karena saya tahu tidak ada orang lain yang menyentuh asal / master". Saya percaya, dalam hal ini, ini bukan perintah yang berisiko.
Darren Cook
1
Jika seseorang berkomitmen pada master dan kemudian satu orang menjalankan perintah git push -f maka itu adalah perintah berisiko tinggi
J4cK
11

Dalam kasus saya, saya telah mendorong perubahan origin/masterdan kemudian menyadari saya seharusnya tidak melakukannya :-( Ini rumit oleh fakta bahwa perubahan lokal dalam subtree. Jadi saya kembali ke komitmen baik terakhir sebelum lokal "buruk" perubahan (menggunakan SourceTree) dan kemudian saya mendapat "pesan divergensi".

Setelah memperbaiki kekacauan saya secara lokal (detailnya tidak penting di sini) saya ingin "mundur dalam waktu" origin/mastercabang jarak jauh sehingga akan disinkronkan dengan lokal masterlagi. Solusi dalam kasus saya adalah:

git push origin master -f

Perhatikan -fsakelar (paksa). Ini menghapus "perubahan buruk" yang didorong origin/masteroleh kesalahan dan sekarang cabang lokal dan jarak jauh disinkronkan.

Harap diingat bahwa ini adalah operasi yang berpotensi merusak jadi lakukan hanya jika Anda yakin 100% bahwa "mundur" master jarak jauh dalam waktu adalah OK.

Laryx Decidua
sumber
Selalu bermanfaat tetapi tentu saja tidak menjawab pertanyaan.
Thibault D.
1
@ThibaultD. bahkan jika tidak, ini persis apa yang saya cari.
Neil Chowdhury
Saya mengerti You are not allowed to force push code to a protected branch on this project.. Saya mencoba mendorong ke garpu.
BanAnanas
Saya harus menghapus perlindungan pada gitlab repo stackoverflow.com/questions/32246503/…
BanAnanas
5

Saya tahu ada banyak jawaban di sini, tetapi saya pikir git reset --soft HEAD~1patut mendapat perhatian, karena itu memungkinkan Anda menjaga perubahan di komit lokal terakhir (tidak didorong) sambil menyelesaikan keadaan yang berbeda. Saya pikir ini adalah solusi yang lebih fleksibel daripada menarikrebase , karena komit lokal dapat ditinjau dan bahkan dipindahkan ke cabang lain.

Kuncinya adalah menggunakan --soft, bukannya keras --hard. Jika ada lebih dari 1 komit, variasi HEAD~xharus bekerja. Jadi, inilah semua langkah yang memecahkan situasi saya (saya punya 1 komit lokal dan 8 komit di remote):

1) git reset --soft HEAD~1 untuk membatalkan komit lokal. Untuk langkah selanjutnya, saya telah menggunakan antarmuka di SourceTree, tapi saya pikir perintah berikut juga harus berfungsi:

2) git stash untuk menyembunyikan perubahan dari 1). Sekarang semua perubahan aman dan tidak ada perbedaan lagi.

3) git pull untuk mendapatkan perubahan jarak jauh.

4) git stash pop atau git stash applyuntuk menerapkan perubahan simpanan terakhir, diikuti oleh komit baru, jika diinginkan. Langkah ini opsional, bersama dengan 2) , ketika ingin membuang perubahan di komit lokal. Juga, ketika ingin komit ke cabang lain, langkah ini harus dilakukan setelah beralih ke yang diinginkan.

Bianca Daniciuc
sumber
Sebenarnya, akhir-akhir ini, bagaimanapun juga pull --rebaseakan tersimpan secara otomatis. stackoverflow.com/a/30209750/6309
VonC
4

Untuk melihat perbedaan:

git difftool --dir-diff master origin/master

Ini akan menampilkan perubahan atau perbedaan antara dua cabang. Dalam araxis (Favorit saya) ini menampilkannya dalam gaya folder berbeda. Menampilkan masing-masing file yang diubah. Saya kemudian dapat mengklik file untuk melihat detail dari perubahan dalam file.

C Johnson
sumber
1
Penggunaan git-dir yang menarik: +1
VonC
3

Dalam kasus saya ini disebabkan oleh tidak melakukan resolusi konflik saya.

Masalahnya disebabkan oleh menjalankan git pullperintah. Perubahan asal menyebabkan konflik dengan repo lokal saya, yang saya selesaikan. Namun, saya tidak melakukan itu. Solusi pada titik ini adalah melakukan perubahan (git commit file yang diselesaikan)

Jika Anda juga telah memodifikasi beberapa file sejak menyelesaikan konflik, git statusperintah akan menampilkan modifikasi lokal sebagai modifikasi lokal yang tidak dipentaskan dan menggabungkan resolusi sebagai modifikasi lokal yang dipentaskan. Ini dapat diselesaikan dengan benar dengan melakukan perubahan dari penggabungan terlebih dahulu git commit, kemudian menambahkan dan melakukan perubahan yang tidak dipentaskan seperti biasa (misalnya dengan git commit -a).

Mohammad Reza Esmaeilzadeh
sumber
0

Saya memiliki pesan yang sama ketika saya mencoba untuk mengedit pesan komit terakhir, dari sudah mendorong komit, menggunakan: git commit --amend -m "New message" Ketika saya mendorong perubahan menggunakan git push --force-with-lease repo_name branch_name tidak ada masalah.

LoveForDroid
sumber
0

Temui masalah ini ketika saya membuat cabang berdasarkan cabang A oleh

git checkout -b a

dan kemudian saya mengatur aliran cabang a ke asal cabang B oleh

git branch -u origin/B

Lalu saya mendapat pesan kesalahan di atas.

Salah satu cara untuk mengatasi masalah ini bagi saya adalah,

  • Hapus cabang a
  • Buat cabang baru b oleh
git checkout -b b origin/B
YanQing
sumber