Bagaimana cara memilih banyak komitmen

875

Saya punya dua cabang. Komit aadalah kepala satu, sementara yang lain memiliki b, c, d, edan fdi atas a. Saya ingin pindah c, d, edan funtuk cabang pertama tanpa komit b. Mudah menggunakan cherry pick: checkout cabang pertama cherry-pick satu per satu cke fdan rebase cabang kedua menjadi yang pertama. Tapi apakah ada cara untuk memilih semua c- fdalam satu perintah?

Berikut ini adalah deskripsi visual dari skenario (terima kasih JJD ):

masukkan deskripsi gambar di sini

cekcok
sumber
3
rebase yang Anda sebutkan sebenarnya tidak relevan untuk pertanyaan, bukan? (Saya bf
paham

Jawaban:

1282

Git 1.7.2 memperkenalkan kemampuan untuk memilih berbagai komitmen. Dari catatan rilis :

git cherry-pickbelajar memilih serangkaian komitmen (misalnya cherry-pick A..Bdan cherry-pick --stdin), begitu juga git revert; ini tidak mendukung kontrol urutan yang lebih baik rebase [-i].

Untuk memilih semua komitmen dari komit Ake komit B(di mana Alebih tua dari B), jalankan:

git cherry-pick A^..B

Jika Anda ingin mengabaikan A itu sendiri, jalankan:

git cherry-pick A..B

(Kredit jatuh ke damian, JB Rainsberger dan sschaef di komentar)

Eric Darchis
sumber
249
Dalam bentuk "ceri-pilih A..B", A harus lebih tua dari B. Jika urutannya salah maka perintah akan gagal.
Damian
294
Juga, ini tidak akan memilih A, melainkan segalanya setelah A hingga dan termasuk B.
JB Rainsberger
455
Untuk memasukkan tipe Agit cherry-pick A^..B
kiritsuku
17
Jika Anda memiliki git 1.7.1 atau yang lebih lama dan tidak dapat memperbarui, Anda dapat dengan cepat memilihnya secara berurutan dengan menjalankan git cherry-pick f~3lalu git cherry-pick f~2dll. Hingga git cherry-pick f(menekan panah ke atas mendapatkan perintah sebelumnya sehingga saya dapat dengan cepat mengubah nomor dan menjalankan itu, harus serupa di sebagian besar konsol).
David Mason
19
Mungkin baik untuk mengetahui bahwa sintaks ini juga berfungsi dengan nama cabang. git cherry-pick master..somebranchakan memilih semua commit di somebranch sejak master (dengan asumsi sudah diubah ke master), dan menerapkannya ke cabang Anda saat ini.
Tor Klingberg
103

Cara paling sederhana untuk melakukan ini adalah dengan ontoopsi untuk rebase. Misalkan cabang yang selesai sekarang adisebut mybranch dan ini adalah cabang yang ingin Anda pindahkan c- fke.

# checkout mybranch
git checkout mybranch

# reset it to f (currently includes a)
git reset --hard f

# rebase every commit after b and transplant it onto a
git rebase --onto a b
CB Bailey
sumber
1
Terima kasih! Bisakah Anda juga menambahkan git checkout secondbranch && git rebase mybranchjawaban lengkapnya
tig
1
Jawaban ini banyak membantu saya untuk mendapatkan sekitar yang komit dalam skenario ini. Dan: Anda juga dapat menggunakan rebasemode interaktif. Terima kasih, @ Charles!
Oliver
1
Keindahan dari pendekatan ini adalah bahwa Anda dapat menggunakan --interactiveuntuk menghapus beberapa commit dari urutan atau menyusun ulang sebelum "cherry pick". +1
Michael Merickel
ini adalah perintah yang genious, agak sulit untuk membungkus kepala Anda, tetapi itu bekerja dengan baik.
Valerio
Liar bahwa Anda harus memberikan komit yang tidak ingin Anda rebase sebagai salah satu argumen ( bdalam contoh ini) tapi ya ini berhasil bagi saya.
asontu
85

Atau satu baris yang diminta:

git rebase --onto a b f
serigala
sumber
5
Jika hanya untuk singkatnya, ini adalah jawaban terbaik.
Nate Chandler
9
Diputakhirkan tetapi akan membuat Anda dalam keadaan HEAD terlepas jika f adalah komit (berlawanan dengan cabang) - Anda harus mengedit untuk menambahkan bahwa seseorang harus checkout cabang seperti dalam jawaban
Mr_and_Mrs_D
68

Anda dapat menggunakan kombinasi serial git rebasedan git branchuntuk menerapkan sekelompok commit ke cabang lain. Seperti yang sudah diposting oleh wolfc , perintah pertama sebenarnya menyalin komit. Namun, perubahan tidak terlihat sampai Anda menambahkan nama cabang ke komit paling atas grup.

Silakan buka gambar di tab baru ...

Alur kerja

Untuk meringkas perintah dalam bentuk teks:

  1. Terbuka gitk sebagai proses independen dengan menggunakan perintah: gitk --all &.
  2. Lari git rebase --onto a b f.
  3. Tekan F5di gitk . Tidak ada yang berubah. Tapi tidakHEAD yang ditandai.
  4. Lari git branch selection
  5. Tekan F5di gitk . Cabang baru dengan komitnya muncul.

Ini harus menjelaskan hal-hal:

  • Komit aadalah tujuan utama baru grup.
  • Komit badalah komit sebelum komit pertama grup (eksklusif).
  • Komit fadalah komitmen terakhir dari grup (inklusif).

Setelah itu, Anda bisa menggunakan git checkout feature && git reset --hard buntuk menghapus komit csampai fdari featurecabang.

Selain jawaban ini, saya menulis posting blog yang menggambarkan perintah dalam skenario lain yang seharusnya membantu untuk menggunakannya secara umum.

JJD
sumber
2
Jika mybranch (a..f commit) tidak diperlukan lagi, ini dapat disederhanakan menjadi: git rebase --onto a b mybranchdan btw - program mana yang menampilkan gambar git yang bagus itu?
Mr_and_Mrs_D
2
@Mr_and_Mrs_D Terima kasih atas komentar Anda. Saya pikir saya menggunakan cacoo.com untuk menggambar.
JJD
48

Untuk menerapkan komentar JB Rainsberger dan sschaef untuk secara spesifik menjawab pertanyaan ... Untuk menggunakan rentang pick-ceri pada contoh ini:

git checkout a
git cherry-pick b..f

atau

git checkout a
git cherry-pick c^..f
Andy
sumber
3
Saya menggunakan git 2.7.0.windows.1dan memperhatikan bahwa ketika saya mencoba untuk melakukan berbagai tindakan, semuanya baik-baik saja tetapi git tidak memberi tahu Anda di mana pun yang harus Anda lakukan git cherry-pick --continue | --abort | --quitsebelum Anda mencoba untuk melakukan / memilih lagi. Jadi, jika Anda memilih berbagai komitmen, Anda harus menjalankan git cherry-pick --continuesetiap kali Anda siap (menyelesaikan konflik atau semacamnya) dengan komit dari kisaran yang diberikan.
kerabat
Saya melakukan hal yang persis sama, tetapi fatal: Tidak dapat menemukan 'a..b'
Amit Karnik
Saya tidak tahu di mana saya salah tetapi ketika saya melakukan 'git cherry-pick c ^ .. f' di pihak saya, ini termasuk komit f tetapi bukan komit c. Tetapi ketika saya membaca di mana-mana, itu seharusnya mendefinisikan c dan f sebagai inklusif. Atau saya salah?
Samuel
@Amuel ya, itu benar. The ^setelah c sebenarnya berarti "komit sebelum c" yang merupakan b dalam kasus ini. Inilah sebabnya mengapa c^..fidentik dengan b..f. Coba lakukan git log c^..fdan Anda akan melihat komit c hingga f, persis sama seperti jika Anda melakukannyagit log b..f
Andy
41

Jika Anda memiliki revisi selektif untuk bergabung, katakan A, C, F, J dari A, B, C, D, E, F, G, H, I, J berkomitmen, cukup gunakan perintah di bawah ini:

git cherry-pick ACFJ

Shrikant Wandhare
sumber
1
bagus dan sederhana
spinup
21
git rev-list --reverse b..f | xargs -n 1 git cherry-pick
Dustin
sumber
2
Berfungsi sempurna jika tidak ada konflik, jika tidak, "rebase ke" mungkin lebih mudah karena Anda tidak perlu mencari tahu di mana ia telah berhenti dan menerapkan kembali sisa tambalan.
Ruslan Kabalin
6
Silakan tambahkan komentar yang menjelaskan apa yang dilakukannya
Mr_and_Mrs_D
7
karena tidak ada yang menjelaskan ... git rev-list mencetak semua revisi dari cabang b ke f (terbalik) sehingga ketika setiap baris (hash komit) dilewati secara berurutan, ia akan memilih masing-masing ke KEPALA git saat ini. yaitugit cherry-pick {hash of c}; git cherry-pick {hash of d}; ...
coderatchet
8

Untuk memilih cherry dari id komit sampai ke ujung cabang, Anda dapat menggunakan:

git cherry-pick commit_id^..branch_name

ut9081
sumber
Ini sudah menjadi bagian dari jawaban stackoverflow.com/a/31640427/96823
tig
1
Jawaban ini sebenarnya berbeda dan sangat membantu saya. Ini menentukan nama cabang, bukan SHA komit akhir.
Subtletree
7

Varian lain yang layak disebutkan adalah bahwa jika Anda ingin nkomit terakhir dari cabang, ~sintaksnya dapat berguna:

git cherry-pick some-branch~4..some-branch

Dalam kasus ini, perintah di atas akan memilih 4 commit terakhir dari cabang bernama some-branch(meskipun Anda juga bisa menggunakan hash komit sebagai pengganti nama cabang)

Nick F
sumber
1
SANGAT jawaban yang berguna, terima kasih! :)
Jacek Dziurdzikowski
3

Sebenarnya, cara paling sederhana untuk melakukannya adalah dengan:

  1. catat basis gabungan antara dua cabang: MERGE_BASE=$(git merge-base branch-a branch-b)
  2. maju cepat atau rebase cabang yang lebih lama ke cabang yang lebih baru
  3. rebase cabang yang dihasilkan ke dirinya sendiri, mulai dari pangkalan gabungan dari langkah 1, dan secara manual menghapus komit yang tidak diinginkan:

    git rebase ${SAVED_MERGE_BASE} -i
    

    Atau, jika hanya ada beberapa komit baru, lewati langkah 1, dan gunakan saja

    git rebase HEAD^^^^^^^ -i
    

    pada langkah pertama, cukup gunakan ^ untuk melewati merge-base.

Anda akan melihat sesuatu seperti ini di rebase interaktif:

pick 3139276 commit a
pick c1b421d commit b
pick 7204ee5 commit c
pick 6ae9419 commit d
pick 0152077 commit e
pick 2656623 commit f

Kemudian hapus baris b (dan lainnya yang Anda inginkan)

ealfonso
sumber
1
git format-patch --full-index --binary --stdout range... | git am -3
Roger Wang
sumber
42
Silakan tambahkan komentar yang menjelaskan apa yang dilakukannya
Mr_and_Mrs_D
0

Berikut ini adalah skrip yang akan memungkinkan Anda untuk memilih-ceri beberapa komit secara berturut-turut hanya dengan memberi tahu skrip sumber dan cabang target untuk ceri pilihan dan jumlah komit:

https://gist.github.com/nickboldt/99ac1dc4eb4c9ff003a1effef2eb2d81

Untuk memilih-ceri dari cabang Anda untuk dikuasai (gunakan cabang saat ini sebagai sumber):

./gcpl.sh -m

Untuk memilih 5 komitmen terbaru dari cabang 6.19.x Anda untuk dikuasai:

./gcpl.sh -c 5 -s 6.19.x -t master
nickboldt
sumber
Mengapa skrip ini diperlukan saat Anda dapat melakukannya dengan Git secara langsung? ...
code_dredd
Kurang mengetik, dan skrip saya secara otomatis memilih komit untuk Anda, jadi Anda tidak perlu memilih masing-masing dengan SHA-nya. Bagaimanapun, YMMV.
Nickboldt