Bagaimana cara memilih berbagai komitmen dan bergabung ke cabang lain?

640

Saya memiliki tata letak repositori berikut:

  • cabang utama (produksi)
  • integrasi
  • kerja

Apa yang ingin saya capai adalah memilih serangkaian komitmen dari cabang kerja dan menggabungkannya ke cabang integrasi. Saya cukup baru untuk git dan saya tidak tahu bagaimana tepatnya melakukan ini (memetik ceri rentang komit dalam satu operasi bukan penggabungan) tanpa mengacaukan repositori. Adakah petunjuk atau pemikiran tentang hal ini? Terima kasih!

orang gila
sumber
1
draconianoverlord.com/2013/09/07/no-cherry-picking.html (bukan blog saya)
Christophe Roussy

Jawaban:

808

Ketika datang ke berbagai komitmen, memetik ceri adalah adalah tidak praktis.

Seperti yang disebutkan di bawah oleh Keith Kim , Git 1.7.2+ memperkenalkan kemampuan untuk memilih cherry-pick sejumlah komitmen (tetapi Anda masih perlu menyadari konsekuensi memetik cherry untuk penggabungan di masa depan )

git cherry-pick "belajar memilih serangkaian commit
(mis." cherry-pick A..B"dan" cherry-pick --stdin"), begitu juga" git revert"; ini tidak mendukung kontrol sequencing yang lebih baik" rebase [-i]"miliki.

komentar damian dan memperingatkan kita:

Dalam bentuk " cherry-pick A..B", Aharus lebih tua dariB .
Jika urutannya salah maka perintah akan gagal .

Jika Anda ingin memilih kisaran Bmelalui D(inklusif) yang akan B^..D.
Lihat " Git membuat cabang dari rentang komit sebelumnya? " Sebagai ilustrasi.

Seperti Jubobs menyebutkan dalam komentar :

Ini mengasumsikan bahwa Bitu bukan komit root; Anda akan mendapat unknown revisionkesalahan " " sebaliknya.

Catatan: pada Git 2.9.x / 2.10 (Q3 2016), Anda dapat memilih berbagai komit langsung pada cabang yatim (kepala kosong): lihat " Bagaimana membuat cabang yang ada menjadi yatim di git ".


Jawaban asli (Januari 2010)

A rebase --ontoakan lebih baik, di mana Anda memutar ulang rentang komit yang diberikan di atas cabang integrasi Anda, seperti dijelaskan Charles Bailey di sini .
(juga, cari "Inilah cara Anda mentransplantasikan cabang topik berdasarkan satu cabang ke cabang lain" di halaman manual git rebase , untuk melihat contoh praktis dari git rebase --onto)

Jika cabang Anda saat ini adalah integrasi:

# Checkout a new temporary branch at the current location
git checkout -b tmp

# Move the integration branch to the head of the new patchset
git branch -f integration last_SHA-1_of_working_branch_range

# Rebase the patchset onto tmp, the old location of integration
git rebase --onto tmp first_SHA-1_of_working_branch_range~1 integration

Itu akan memutar ulang semuanya antara:

  • setelah induk dari first_SHA-1_of_working_branch_range(karenanya ~1): komit pertama yang ingin Anda putar ulang
  • hingga " integration" (yang menunjuk ke komit terakhir yang ingin Anda putar ulang, dari workingcabang)

ke " tmp" (yang menunjuk ke tempat integrationmenunjuk sebelumnya)

Jika ada konflik ketika salah satu dari komitmen itu diputar ulang:

  • pecahkan dan jalankan " git rebase --continue".
  • atau lewati tambalan ini, dan jalankan " git rebase --skip"
  • atau membatalkan semua hal dengan " git rebase --abort" (dan meletakkan kembali integrationcabang di tmpcabang)

Setelah itu rebase --onto, integrationakan kembali pada komit terakhir dari cabang integrasi (yaitu tmpcabang + "semua komitmen yang diputar ulang)

Dengan memetik ceri atau rebase --onto, jangan lupa itu memiliki konsekuensi pada penggabungan berikutnya, seperti dijelaskan di sini .


cherry-pickSolusi " " murni dibahas di sini , dan akan melibatkan sesuatu seperti:

Jika Anda ingin menggunakan pendekatan tambalan maka "git format-patch | git am" dan "git cherry" adalah pilihan Anda.
Saat ini, git cherry-pickhanya menerima satu komit, tetapi jika Anda ingin memilih kisaran Bmelalui Dyang akan B^..Ddi git lingo, sehingga

git rev-list --reverse --topo-order B^..D | while read rev 
do 
  git cherry-pick $rev || break 
done 

Tapi bagaimanapun, ketika Anda perlu "memutar ulang" serangkaian commit, kata "replay" akan mendorong Anda untuk menggunakan fitur " rebase" Git.

VONC
sumber
1
Jika Anda memiliki komitmen yang memiliki orang tua yang memerlukan -mopsi, bagaimana Anda menangani komitmen tersebut? Atau adakah cara untuk menyaring komitmen ini?
Agustus
@ aug -mseharusnya menangani mereka untuk Anda, dengan memilih jalur utama yang dirujuk oleh -mparameter yang telah Anda pilih untuk memetik ceri ini.
VonC
Masalahnya adalah jika Anda ceri memilih serangkaian komit, itu akan ceri memilih induk melakukan dengan benar tetapi kemudian ketika itu memukul komit normal, itu gagal dan mengatakan komit bukan gabungan. Saya kira pertanyaan saya lebih baik diungkapkan bagaimana membuatnya lulus -mopsi hanya ketika hits komitmen orangtua ketika memilih berbagai komitmen melakukan ceri? Sekarang jika saya lulus -mseperti git cherry-pick a87afaaf..asfa789 -m 1itu berlaku untuk semua komit dalam jangkauan.
Agustus
@ aug Aneh, saya tidak mereproduksi masalah. Apa versi git Anda dan apa pesan kesalahan persis yang Anda lihat?
VonC
Ah saya menjalankan git versi 2.6.4 (Apple Git-63). Kesalahan yang saya lihat akan menjadi sesuatu seperti error: Commit 8fcaf3b61823c14674c841ea88c6067dfda3af48 is a merge but no -m option was given.saya benar-benar menyadari bahwa Anda bisa saja git cherry-pick --continuedan itu akan baik-baik saja (tapi itu tidak termasuk komitmen orang tua)
Agustus
136

Pada git v1.7.2 cherry pick dapat menerima serangkaian commit:

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].

Keith Kim
sumber
103
Catatan yang cherry-pick A..Btidak akan mendapatkan komit A (Anda akan perlu A~1..Buntuk itu), dan jika ada konflik git tidak akan secara otomatis melanjutkan seperti rebase tidak (setidaknya pada 1.7.3.1)
Gabe Moothart
3
Ini juga baik untuk dicatat bahwa git cherry-pick A..B Ctidak berfungsi seperti yang Anda harapkan, secara naif. Itu tidak akan memilih semuanya dalam jangkauan A..Bdan berkomitmen C! Untuk melakukan ini, Anda perlu dipecah menjadi dua baris, pertama git cherry-pick A..Bdan kemudian git cherry-pick C. Jadi, setiap kali Anda memiliki jangkauan, Anda harus menjalankannya secara terpisah.
MicroVirus
42

Asumsikan bahwa Anda memiliki 2 cabang,

"branchA": termasuk komit yang ingin Anda salin (dari "commitA" ke "commitB"

"branchB": cabang yang Anda inginkan komit ditransfer dari "branchA"

1)

 git checkout <branchA>

2) dapatkan ID dari "commitA" dan "commitB"

3)

git checkout <branchB>

4)

git cherry-pick <commitA>^..<commitB>

5) Jika Anda memiliki konflik, pecahkan dan ketik

git cherry-pick --continue

untuk melanjutkan proses memetik ceri.

KostasA
sumber
Bekerja seperti pesona! Terima kasih banyak!
snukone
28

Anda yakin tidak ingin benar-benar menggabungkan cabang? Jika cabang kerja memiliki beberapa komitmen terbaru yang tidak Anda inginkan, Anda bisa membuat cabang baru dengan HEAD pada titik yang Anda inginkan.

Sekarang, jika Anda benar-benar ingin memilih berbagai komitmen, untuk alasan apa pun, cara yang elegan untuk melakukan ini adalah dengan hanya menarik tambalan dan menerapkannya ke cabang integrasi baru Anda:

git format-patch A..B
git checkout integration
git am *.patch

Ini pada dasarnya adalah apa yang dilakukan git-rebase, tetapi tanpa perlu bermain game. Anda dapat menambahkan --3wayuntukgit-am jika Anda perlu bergabung. Pastikan tidak ada file * .patch lain yang sudah ada di direktori tempat Anda melakukan ini, jika Anda mengikuti petunjuk secara verbatim ...

djs
sumber
1
Perhatikan bahwa, sama dengan rentang revisi lainnya, harus A^disertakan A.
Gino Mempin
9

Saya membungkus kode VonC menjadi skrip bash pendek git-multi-cherry-pick,, agar mudah dijalankan:

#!/bin/bash

if [ -z $1 ]; then
    echo "Equivalent to running git-cherry-pick on each of the commits in the range specified.";
    echo "";
    echo "Usage:  $0 start^..end";
    echo "";
    exit 1;
fi

git rev-list --reverse --topo-order $1 | while read rev 
do 
  git cherry-pick $rev || break 
done 

Saya saat ini menggunakan ini ketika saya membangun kembali sejarah sebuah proyek yang memiliki kode pihak ketiga dan kustomisasi dicampur bersama dalam batang svn yang sama. Saya sekarang memisahkan kode inti pihak ke-3, modul-modul pihak ke-3, dan penyesuaian ke cabang git mereka sendiri untuk pemahaman yang lebih baik tentang penyesuaian yang akan dilakukan. git-cherry-pickmembantu dalam situasi ini karena saya memiliki dua pohon di repositori yang sama, tetapi tanpa leluhur bersama.

Adam Franco
sumber
3

Semua opsi di atas akan meminta Anda untuk menyelesaikan konflik penggabungan. Jika Anda menggabungkan perubahan yang dilakukan untuk suatu tim, sulit untuk menyelesaikan konflik gabungan dari pengembang dan melanjutkan. Namun, "git merge" akan melakukan penggabungan dalam satu kesempatan tetapi Anda tidak dapat melewati berbagai revisi sebagai argumen. kita harus menggunakan perintah "git diff" dan "git apply" untuk melakukan penggabungan berbagai revs. Saya telah mengamati bahwa "git apply" akan gagal jika file patch memiliki diff untuk terlalu banyak file, jadi kita harus membuat patch per file dan kemudian menerapkan. Perhatikan bahwa skrip tidak akan dapat menghapus file yang dihapus di cabang sumber. Ini adalah kasus yang jarang terjadi, Anda dapat menghapus file seperti itu secara manual dari cabang target. Status keluar dari "git apply" bukan nol jika tidak dapat menerapkan tambalan,

Di bawah ini adalah skripnya.

enter code here



  #!/bin/bash

    # This script will merge the diff between two git revisions to checked out branch
    # Make sure to cd to git source area and checkout the target branch
    # Make sure that checked out branch is clean run "git reset --hard HEAD"


    START=$1
    END=$2

    echo Start version: $START
    echo End version: $END

    mkdir -p ~/temp
    echo > /tmp/status
    #get files
    git --no-pager  diff  --name-only ${START}..${END} > ~/temp/files
    echo > ~/temp/error.log
    # merge every file
    for file in `cat  ~/temp/files`
    do
      git --no-pager diff --binary ${START}..${END} $file > ~/temp/git-diff
      if [ $? -ne 0 ]
      then
#      Diff usually fail if the file got deleted 
        echo Skipping the merge: git diff command failed for $file >> ~/temp/error.log
        echo Skipping the merge: git diff command failed for $file
        echo "STATUS: FAILED $file" >>  /tmp/status
        echo "STATUS: FAILED $file"
    # skip the merge for this file and continue the merge for others
        rm -f ~/temp/git-diff
        continue
      fi

      git apply  --ignore-space-change --ignore-whitespace  --3way --allow-binary-replacement ~/temp/git-diff

      if [ $? -ne 0 ]
       then
#  apply failed, but it will fall back to 3-way merge, you can ignore this failure
         echo "git apply command filed for $file"
       fi
       echo
       STATUS=`git status -s $file`


       if [ ! "$STATUS" ]
       then
#   status is null if the merged diffs are already present in the target file
         echo "STATUS:NOT_MERGED $file"
         echo "STATUS: NOT_MERGED $file$"  >>  /tmp/status
       else
#     3 way merge is successful
         echo STATUS: $STATUS
         echo "STATUS: $STATUS"  >>  /tmp/status
       fi
    done

    echo GIT merge failed for below listed files

    cat ~/temp/error.log

    echo "Git merge status per file is available in /tmp/status"
Yoganand Bijapur
sumber
2

Saya telah mengujinya beberapa hari yang lalu, setelah membaca penjelasan yang sangat jelas tentang Vonc.

Langkah saya

Mulailah

  • Cabang dev : ABCDEFGHIJ
  • Cabang target : ABCD
  • Saya tidak ingin EatauH

Langkah-langkah untuk menyalin fitur tanpa langkah E dan H di cabang dev_feature_wo_E_H

  • git checkout dev
  • git checkout -b dev_feature_wo_E_H
  • git rebase --interactive --rebase-merges --no-ff Dtempat saya meletakkan dropdepan EdanH di editor rebase
  • menyelesaikan konflik, lanjutkan dan commit

Langkah-langkah untuk menyalin cabang dev_feature_wo_E_Hpada target.

  • git checkout target
  • git merge --no-ff --no-commit dev_feature_wo_E_H
  • menyelesaikan konflik, lanjutkan dan commit

Beberapa komentar

  • Saya sudah melakukan itu karena terlalu banyak cherry-pickpada hari-hari sebelumnya
  • git cherry-pick kuat tetapi sederhana

    • itu membuat duplikat komit
    • dan ketika saya ingin mergesaya harus menyelesaikan konflik komit awal dan duplikat komit, jadi untuk satu atau dua cherry-pick, tidak apa-apa untuk "memetik ceri" tetapi untuk lebih banyak itu terlalu bertele-tele dan cabang akan menjadi terlalu kompleks
  • Dalam pikiran saya langkah-langkah yang saya lakukan lebih jelas daripada git rebase --onto
phili_b
sumber
1
Pos yang bagus. Terpilih. Ini mengingatkan saya pada stackoverflow.com/a/38418941/6309 . Saya mencatat kembali kekurangan memetik buah ceri pada tahun 2012: stackoverflow.com/a/13524494/6309 .
VonC
1

Pilihan lain mungkin untuk menggabungkan dengan strategi kita untuk komit sebelum rentang dan kemudian gabungan 'normal' dengan komit terakhir dari rentang itu (atau cabang ketika itu adalah yang terakhir). Jadi anggaplah hanya 2345 dan 3456 komit master yang akan digabung menjadi cabang fitur:

menguasai:
1234
2345
3456
4567

di cabang fitur:

git menggabungkan -s milik kita 4567
git menggabungkan 2345
Koos Vriezen
sumber