git cherry-pick mengatakan "... 38c74d adalah penggabungan tetapi tidak ada opsi -m yang diberikan"

519

Saya membuat beberapa perubahan di cabang utama saya dan ingin membawanya ke hulu. ketika saya memilih tindakan berikut namun saya terjebak di fd9f578 di mana git mengatakan:

$ git cherry-pick fd9f578
fatal: Commit fd9f57850f6b94b7906e5bbe51a0d75bf638c74d is a merge but no -m option was given.

Apa yang git coba sampaikan kepada saya dan apakah cherry-pick adalah hal yang tepat untuk digunakan di sini? Cabang utama memang menyertakan perubahan ke file yang telah dimodifikasi di cabang hulu, jadi saya yakin akan ada beberapa konflik penggabungan tetapi itu tidak terlalu buruk untuk diluruskan. Saya tahu perubahan mana yang dibutuhkan di mana.

Ini adalah komitmen yang ingin saya bawa ke hulu.

e7d4cff added some comments...
23e6d2a moved static strings...
44cc65a incorporated test ...
40b83d5 whoops delete whitspace...
24f8a50 implemented global.c...
43651c3 cleaned up ...
068b2fe cleaned up version.c ...
fd9f578 Merge branch 'master' of ssh://extgit/git/sessions_common
4172caa cleaned up comments in sessions.c ...
wufoo
sumber

Jawaban:

607

Cara kerja cherry-pick adalah dengan mengambil diff yang diwakili oleh setset (perbedaan antara pohon yang bekerja pada saat itu dan pohon yang bekerja dari induknya), dan menerapkannya ke cabang Anda saat ini.

Jadi, jika komit memiliki dua atau lebih orang tua, itu juga mewakili dua atau lebih perbedaan - mana yang harus diterapkan?

Anda mencoba memilih cherry fd9f578, yang merupakan penggabungan dengan dua orang tua. Jadi, Anda perlu memberi tahu perintah cherry-pick yang mana yang harus dihitung bedanya dengan menggunakan -mopsi. Misalnya, git cherry-pick -m 1 fd9f578untuk menggunakan induk 1 sebagai basis.

Saya tidak bisa mengatakan dengan pasti untuk situasi khusus Anda, tetapi menggunakan git mergedaripada git cherry-pickumumnya disarankan. Saat Anda memilih komit gabungan, itu menciutkan semua perubahan yang dibuat di induk yang tidak Anda tetapkan -mmenjadi komit itu . Anda kehilangan semua sejarah mereka, dan menggabungkan semua diff mereka. Panggilanmu.

Borealid
sumber
3
@ wufoo Anda mungkin juga harus belajar tentang git rebase- ini seperti penggabungan, tetapi alih-alih mengintegrasikan dua cabang, transplantasi satu untuk duduk di atas yang lain.
Borealid
92
bagaimana Anda tahu nomor induknya?
Anentropic
66
@Anentropic 1 adalah "orang tua pertama", 2 adalah "orang tua kedua", dan seterusnya. Urutan adalah urutan di mana mereka terdaftar dalam komit (seperti yang dilihat oleh git showdan sejenisnya).
Borealid
2
@ lkraav Anda juga bisa melakukan saja git reset --hard HEAD@{1}untuk mendapatkan kembali komit Anda yang hilang. git resettidak dibatasi untuk bergerak "mundur" dalam sejarah. git checkout -b mybranch HEAD@{1}juga akan bekerja.
Borealid
4
PERINGATAN: git mergemungkin memiliki konsekuensi yang tidak diinginkan. Perintah itu akan menambahkan semua komit (lama) lain yang ada di cabang induk. Biasanya orang memilih untuk memetik ceri karena mereka tidak ingin yang lain melakukan. Pastikan Anda memeriksa ulang bahwa Anda hanya menerapkan perubahan yang Anda inginkan!
Kay V
52

-m berarti nomor induk.

Dari git doc:

Biasanya Anda tidak dapat memilih cherry-gabung karena Anda tidak tahu sisi mana dari penggabungan yang harus dianggap sebagai arus utama. Opsi ini menentukan nomor induk (mulai dari 1) dari jalur utama dan memungkinkan pengambilan ceri untuk memutar ulang perubahan relatif terhadap induk yang ditentukan.

Misalnya, jika pohon komit Anda seperti di bawah ini:

- A - D - E - F -   master
   \     /
    B - C           branch one

maka git cherry-pick Eakan menghasilkan masalah yang Anda hadapi.

git cherry-pick E -m 1berarti menggunakan D-E, sementara git cherry-pick E -m 2berarti menggunakan B-C-E.

gavincook
sumber
32

@ Borealid jawaban itu benar, tetapi anggaplah Anda tidak peduli tentang melestarikan sejarah penggabungan yang tepat dari sebuah cabang dan hanya ingin memilih versi yang sudah di-linearisasi. Berikut cara mudah dan aman untuk melakukannya:

Status awal: Anda berada di cabang X, dan Anda ingin memilih komit Y..Z.

  1. git checkout -b tempZ Z
  2. git rebase Y
  3. git checkout -b newX X
  4. git cherry-pick Y..tempZ
  5. (pilihan) git branch -D tempZ

Apa yang dilakukan adalah membuat cabang tempZberdasarkan Z, tetapi dengan sejarah dari Ylinier selanjutnya, dan kemudian memetiknya ke salinan yang Xdisebut newX. (Lebih aman melakukan ini pada cabang baru daripada bermutasi X.) Tentu saja mungkin ada konflik di langkah 4, yang harus Anda selesaikan dengan cara biasa ( cherry-pickbekerja sangat mirip rebasedalam hal itu). Akhirnya menghapus tempZcabang sementara .

Jika langkah 2 memberikan pesan "TempZ cabang saat ini adalah yang terbaru", maka Y..Zsudah linear, jadi abaikan saja pesan itu dan lanjutkan dengan langkah 3 dan seterusnya.

Kemudian tinjau newXdan lihat apakah itu sesuai dengan yang Anda inginkan.

(Catatan: ini tidak sama dengan yang sederhana git rebase Xketika di cabang Z, karena itu tidak tergantung dengan cara apa pun pada hubungan antara Xdan Y; mungkin ada komit antara leluhur yang sama dan Yyang tidak Anda inginkan.)

Daira Hopwood
sumber
1
git rebase YmengatakanCurrent branch tempZ is up to date
Basilev
Saya pikir itu berarti Y..Zsudah linear. Jadi, Anda dapat mengabaikan pesan itu dan melanjutkan dengan langkah 3 dan 4.
Daira Hopwood
1
Ide yang menarik, saya harus menggambarnya di atas kertas untuk sepenuhnya menghargai apa yang sedang terjadi = D
Chris
2
Cemerlang. git cherry-pick untuk seluruh jajaran mengeluh bahwa opsi -m hilang atau disediakan. Solusi Anda adalah emas. (Satu saran: hapus cabang tempZ setelah)
Otheus
1
ini luar biasa! Saya telah berjuang dengan ceri memilih dan hanya ini masuk akal. Saya memiliki sedikit masalah dengan memilih huruf (beberapa cabang dan komit adalah huruf besar dan beberapa cabang adalah huruf kecil)
pcarvalho
19

Menyederhanakan. Cherry memilih komit. Jangan memilih cherry-merger.

Inilah penulisan ulang jawaban yang diterima yang secara ideal mengklarifikasi keuntungan / risiko dari pendekatan yang mungkin:

Anda mencoba memilih cherry fd9f578, yang merupakan gabungan dari dua orang tua.

Alih-alih memilih cherry memilih merger, hal yang paling sederhana adalah memilih cherry memilih commit yang sebenarnya Anda inginkan dari setiap cabang di merger.

Karena Anda sudah bergabung, kemungkinan semua komitmen yang Anda inginkan ada di daftar Anda. Pilih Cherry secara langsung dan Anda tidak perlu mengacaukan komit gabungan.

penjelasan

Cara kerja cherry-pick adalah dengan mengambil diff yang diwakili oleh changeset (perbedaan antara pohon yang bekerja pada saat itu dan pohon yang bekerja dari induknya), dan menerapkan perubahan tersebut ke cabang Anda saat ini.

Jika komit memiliki dua atau lebih orang tua, seperti halnya dengan gabungan, komit juga mewakili dua atau lebih perbedaan. Kesalahan terjadi karena ketidakpastian di mana diff harus berlaku.

alternatif

Jika Anda menentukan bahwa Anda perlu menyertakan gabungan vs memetik komitmen terkait, Anda memiliki dua opsi:

  1. (Lebih rumit dan tidak jelas; juga membuang riwayat) Anda dapat menunjukkan orang tua mana yang harus diterapkan.

    • Gunakan -mopsi untuk melakukannya. Misalnya, git cherry-pick -m 1 fd9f578akan menggunakan induk pertama yang tercantum dalam gabungan sebagai basis.

    • Juga pertimbangkan bahwa ketika Anda memilih komit gabungan, itu menciutkan semua perubahan yang dibuat di induk yang Anda tidak tentukan -mmenjadi komit yang satu itu . Anda kehilangan semua sejarah mereka, dan menggabungkan semua diff mereka. Panggilanmu.

  2. (Lebih sederhana dan lebih akrab; menyimpan sejarah) yang dapat Anda gunakan git mergesebagai gantinya git cherry-pick.

    • Seperti biasa git merge, ia akan mencoba untuk menerapkan semua komit yang ada pada cabang yang Anda gabungkan, dan mendaftar secara individual di log git Anda.
Kay V
sumber
2

Penyederhanaan metode @Daira Hopwood bagus untuk memilih satu komit tunggal. Tidak perlu cabang sementara.

Dalam kasus penulis:

  • Z ingin komit (fd9f578)
  • Y berkomitmen sebelum itu
  • X cabang kerja saat ini

lalu lakukan:

git checkout Z   # move HEAD to wanted commit
git reset Y      # have Z as changes in working tree
git stash        # save Z in stash
git checkout X   # return to working branch
git stash pop    # apply Z to current branch
git commit -a    # do commit
efhemerr
sumber
2
Ini tentu saja kehilangan metadata yang terkait dengan komit asli. Saya kira ini masalah pendapat apakah itu lebih sederhana. Saya menggunakannya kadang-kadang ketika saya ingin kehilangan metadata dan hanya menyimpan perubahan kode secara keseluruhan. Perhatikan bahwa ia berfungsi bahkan jika Y bukan merupakan induk langsung dari Z (dalam hal ini perubahan akan tergencet).
Daira Hopwood