Cara menggabungkan komit tertentu di Git

1035

Saya telah bercabang cabang dari repositori di GitHub dan melakukan sesuatu yang spesifik untuk saya. Sekarang saya menemukan repositori asli memiliki fitur bagus yang ada di HEAD.

Saya ingin menggabungkannya hanya tanpa komitmen sebelumnya. Apa yang harus saya lakukan? Saya tahu cara menggabungkan semua komitmen:

git branch -b a-good-feature
git pull repository master
git checkout master
git merge a-good-feature
git commit -a
git push
netawater
sumber
Jika Anda mencoba melakukan ini sehubungan dengan github, artikel ini akan membantu Anda. markosullivan.ca/how-to-handle-a-pull-request-from-github
johndpope

Jawaban:

1176

' git cherry-pick' harus menjadi jawaban Anda di sini.

Terapkan perubahan yang diperkenalkan oleh komit yang ada.

Jangan lupa membaca jawaban bdonlan tentang konsekuensi memetik ceri di pos ini:
"Tarik semua komit dari cabang, tekan komit yang ditentukan ke yang lain" , di mana:

A-----B------C
 \
  \
   D

menjadi:

A-----B------C
 \
  \
   D-----C'

Masalah dengan komit ini adalah bahwa git menganggap berkomitmen untuk memasukkan semua riwayat sebelum mereka

Di mana C 'memiliki SHA-1ID yang berbeda .
Demikian juga, cherry memilih komit dari satu cabang ke cabang lainnya pada dasarnya melibatkan menghasilkan tambalan, kemudian menerapkannya, sehingga kehilangan sejarah seperti itu juga.

Perubahan ID komit ini memecah fungsionalitas git antara lain (meskipun jika digunakan dengan hemat ada heuristik yang akan menjelaskan hal ini).
Lebih penting lagi, ia mengabaikan dependensi fungsional - jika C benar-benar menggunakan fungsi yang didefinisikan dalam B, Anda tidak akan pernah tahu .

VONC
sumber
1
@ openid000: "cabang berbutir lebih halus": yang memang persis seperti yang disarankan bdonlan dalam jawabannya.
VonC
8
Catatan: "git rebase" juga mengubah SHA-1. Lihat juga "git rebase vs. git merge ( stackoverflow.com/questions/804115/git-rebase-vs-git-merge ) dan" git workflow "( stackoverflow.com/questions/457927/… ) untuk kasus di mana" git rebase "Sah.
VonC
1
Antara "cabang berbutir halus", "cherry-pick" dan "rebase", Anda kemudian akan memiliki semua kemungkinan untuk mengelola kode di cabang dengan git.
VonC
@VonC "cabang berbutir halus" ya. Saya memiliki dua cabang dan telah melakukan upgrade ke modul visualisasi tertentu di satu cabang. Modul ini tidak tergantung pada semua kode lain, jadi cherry-pick sangat nyaman untuk menerapkan perubahan ini ke cabang lain juga
Cheeku
1
Tetapi perhatikan bahwa ketika Anda bergabung, kedua komit C 'dan C akan menang dalam sejarah komit.
Rahul Shah
739

Anda dapat menggunakan git cherry-pick untuk menerapkan satu komit dengan sendirinya ke cabang Anda saat ini.

Contoh: git cherry-pick d42c389f

omong kosong
sumber
68
+1 untuk pos lama Anda pada pemungutan ceri ( stackoverflow.com/questions/880957/… ) Saya mengambil kebebasan untuk menyalin ekstraknya di jawaban saya sendiri di atas.
VonC
4
Mungkin git cherry-pick d42catau git cherry-pick d42c3 akan berhasil. Git pintar. ;)
guneysus
2
fatal: revisi buruk
Ievgen Naida
2
Saya baru saja melakukan perintah ini dan tampaknya berhasil, tetapi ketika saya melakukan status git, ia mengatakan "tidak ada yang harus dilakukan, bekerja membersihkan pohon". Ketika saya menyegarkan halaman Komit di halaman web bitbucket saya, itu tidak muncul. Tapi itu muncul ketika saya menjalankan git log. Dan saya melihat kode yang dimodifikasi. Adakah yang bisa menjelaskan jika saya harus melakukan langkah tambahan?
Ray
1
Sayangnya ini tidak menjawab pertanyaan: Itu sebenarnya tidak membuat penggabungan. Tidak ada keturunan yang menunjuk d42c389f. Mungkin OP tidak peduli tentang membuat penggabungan per se, tetapi perbedaannya terkadang penting.
LarsH
29

Mari kita coba mengambil contoh dan memahami:

Saya memiliki cabang, katakan master , menunjuk ke X <commit-id>, dan saya memiliki cabang baru yang menunjuk ke Y <sha1>.

Di mana Y <commit-id> = <master> cabang melakukan - sedikit komitmen

Sekarang katakanlah untuk cabang Y saya harus menutup-menutup komit antara cabang master dan cabang baru. Di bawah ini adalah prosedur yang bisa kita ikuti:

Langkah 1:

git checkout -b local origin/new

di mana lokal adalah nama cabang. Nama apa pun bisa diberikan.

Langkah 2:

  git merge origin/master --no-ff --stat -v --log=300

Gabungkan komit dari cabang master ke cabang baru dan juga buat komit gabung dari pesan log dengan deskripsi satu-baris dari paling banyak <n> komit aktual yang sedang digabung.

Untuk informasi lebih lanjut dan parameter tentang penggabungan Git, silakan merujuk ke:

git merge --help

Juga jika Anda perlu menggabungkan komit tertentu, maka Anda dapat menggunakan:

git cherry-pick <commit-id>
laba laba
sumber
apakah Anda mengubah definisi Ydalam kalimat 3d Anda? "Saya punya cabang baru yang menunjuk ke Y" vs "Sekarang katakan untuk cabang Y", sepertinya Y dulu komit dan kemudian menjadi cabang
Purefan
bagaimana saya bisa melakukan itu dari titik komit tertentu dari cabang yang ingin saya gabungkan
Kulbhushan Singh
3

Dalam kasus penggunaan saya, kami memiliki kebutuhan yang sama untuk CD CI. Kami menggunakan aliran git dengan mengembangkan dan menguasai cabang. Pengembang bebas untuk menggabungkan perubahan di sana secara langsung untuk mengembangkan atau melalui permintaan tarik dari cabang fitur. Namun untuk menguasai, kami hanya menggabungkan komit stabil dari cabang pengembangan dengan cara otomatis melalui Jenkins.

Dalam hal ini melakukan cherry-pick bukanlah pilihan yang baik. Namun kami membuat cabang lokal dari commit-id kemudian menggabungkan cabang lokal tersebut untuk dikuasai dan melakukan verifikasi mvn clean (kami menggunakan maven). Jika berhasil maka lepaskan artefak versi produksi ke nexus menggunakan plugin rilis pakar dengan localCheckout = opsi benar dan pushChanges = false. Akhirnya ketika semuanya berhasil maka dorong perubahan dan tag ke asal.

Cuplikan kode contoh:

Anggap saja Anda master jika dilakukan secara manual. Namun pada jenkins, ketika Anda checkout repo Anda akan berada di cabang default (master jika dikonfigurasi).

git pull  // Just to pull any changes.
git branch local-<commitd-id> <commit-id>  // Create a branch from the given commit-id
git merge local-<commit-id>  // Merge that local branch to master.
mvn clean verify   // Verify if the code is build able
mvn <any args> release:clean release:prepare release:perform // Release artifacts
git push origin/master  // Push the local changes performed above to origin.
git push origin <tag>  // Push the tag to origin

Ini akan memberi Anda kontrol penuh dengan merger atau konflik neraka tanpa rasa takut.

Jangan sungkan untuk memberi saran jika ada opsi yang lebih baik.

nrkkalyan
sumber
3

Jawaban utama menjelaskan bagaimana menerapkan perubahan dari komit tertentu ke cabang saat ini. Jika itu yang Anda maksud dengan "cara menggabungkan", maka gunakan saja cherry-pick seperti yang disarankan.

Tetapi jika Anda benar-benar menginginkan penggabungan , yaitu Anda ingin komit baru dengan dua orang tua - komit yang ada di cabang saat ini, dan komit yang ingin Anda terapkan perubahannya - maka pemilihan ceri tidak akan mencapai itu.

Memiliki riwayat gabungan yang sebenarnya mungkin diinginkan, misalnya, jika proses build Anda memanfaatkan git keturunan untuk secara otomatis mengatur string versi berdasarkan tag terbaru (menggunakan git describe).

Alih-alih memilih ceri, Anda dapat melakukan yang sebenarnya git merge --no-commit, dan kemudian secara manual menyesuaikan indeks untuk menghapus perubahan yang tidak Anda inginkan.

Misalkan Anda berada di cabang Adan Anda ingin menggabungkan komit di ujung cabang B:

git checkout A
git merge --no-commit B

Sekarang Anda siap membuat komit dengan dua orang tua, tip saat ini adalah dari Adan B. Namun Anda mungkin memiliki lebih banyak perubahan yang diterapkan daripada yang Anda inginkan, termasuk perubahan dari komit sebelumnya pada cabang B. Anda harus membatalkan perubahan yang tidak diinginkan ini, lalu komit.

(Mungkin ada cara mudah untuk mengatur status direktori kerja dan indeks kembali ke keadaan sebelum penggabungan, sehingga Anda memiliki papan tulis yang bersih untuk memilih komit yang Anda inginkan di tempat pertama. Tapi Saya tidak tahu cara mencapai yang bersih, git checkout HEADdan git reset HEADkeduanya akan menghapus status gabungan, mengalahkan tujuan metode ini.)

Jadi secara manual batalkan perubahan yang tidak diinginkan. Misalnya, Anda bisa

git revert --no-commit 012ea56

untuk setiap komit yang tidak diinginkan 012ea56.

Setelah selesai menyesuaikan hal-hal, buat komit Anda:

git commit -m "Merge in commit 823749a from B which tweaked the timeout code"

Sekarang Anda hanya memiliki perubahan yang Anda inginkan, dan pohon keturunan menunjukkan bahwa Anda secara teknis bergabung dari B.

Lars
sumber