Bagaimana cara menggabungkan atau mengambil perubahan secara selektif dari cabang lain di Git?


1450

Saya menggunakan git pada proyek baru yang memiliki dua cabang pengembangan paralel - namun saat ini eksperimental:

  • master: impor basis kode yang ada ditambah beberapa mod yang secara umum saya yakini
  • exp1: cabang eksperimental # 1
  • exp2: cabang eksperimental # 2

exp1dan exp2mewakili dua pendekatan arsitektur yang sangat berbeda. Sampai saya melanjutkan, saya tidak memiliki cara untuk mengetahui yang mana (jika salah satu) akan bekerja. Ketika saya membuat kemajuan di satu cabang, saya kadang-kadang memiliki pengeditan yang berguna di cabang lain dan ingin menggabungkan hanya itu.

Apa cara terbaik untuk menggabungkan perubahan selektif dari satu cabang pengembangan ke yang lain sambil meninggalkan yang lainnya?

Pendekatan yang saya pertimbangkan:

  1. git merge --no-commit diikuti oleh penghapusan manual sejumlah besar pengeditan yang tidak ingin saya lakukan di antara cabang-cabang.

  2. Menyalin manual file umum ke direktori temp diikuti oleh git checkoutuntuk pindah ke cabang lain dan kemudian lebih banyak menyalin manual dari direktori temp ke pohon kerja.

  3. Variasi di atas. Tinggalkan expcabang untuk saat ini dan gunakan dua repositori lokal tambahan untuk eksperimen. Ini membuat penyalinan file secara manual jauh lebih mudah.

Ketiga pendekatan ini tampak membosankan dan rentan kesalahan. Saya berharap ada pendekatan yang lebih baik; sesuatu yang mirip dengan parameter jalur filter yang akan membuat git-mergelebih selektif.


5
Jika perubahan di cabang eksperimental Anda diatur dengan baik dalam komit terpisah, lebih baik untuk berpikir dalam hal menggabungkan komit selektif daripada file selektif. Sebagian besar jawaban di bawah menganggap ini adalah masalahnya.
akaihola

2
Bukankah kombinasi git merge -s ours --no-commitdiikuti oleh beberapa git read-treemenjadi solusi yang baik untuk ini? Lihat stackoverflow.com/questions/1214906/…
VonC

34
Pertanyaan yang lebih baru memiliki jawaban satu baris, yang ditulis dengan baik: stackoverflow.com/questions/10784523/…
brahn

Periksa blog ini hanya untuk menggabungkan file tertentu jasonrudolph.com/blog/2009/02/25/…
Ashutosh Chamoli

Jawaban:


475

Anda menggunakan perintah cherry-pick untuk mendapatkan komitmen individu dari satu cabang.

Jika perubahan yang Anda inginkan tidak berada dalam komit individual, maka gunakan metode yang ditampilkan di sini untuk membagi komit menjadi komit individual . Secara kasar, Anda menggunakan git rebase -iuntuk mendapatkan komit asli untuk diedit, lalu git reset HEAD^untuk secara selektif mengembalikan perubahan, lalu git commituntuk mengkomit bit itu sebagai komit baru dalam sejarah.

Ada metode bagus lain di sini di Red Hat Magazine, di mana mereka menggunakan git add --patchatau mungkin git add --interactiveyang memungkinkan Anda untuk menambahkan hanya bagian dari sebongkah, jika Anda ingin membagi perubahan yang berbeda ke file individual (cari di halaman itu untuk "split").

Setelah memecah perubahan, Anda sekarang dapat memilih apa yang Anda inginkan.


14
Dari pemahaman saya, ini lebih berbelit-belit daripada jawaban yang lebih tinggi.
Alexander Bird

54
Secara teknis ini adalah jawaban yang benar, jawaban yang benar memang tampak "berbelit-belit". --- Jawaban yang lebih tinggi hanyalah jawaban cepat dan kotor "melakukan trik", yang bagi kebanyakan orang adalah semua yang mereka miliki (:
Jacob

3
@akaihola: KEPALA ^ benar. Lihat man git-rev-parse: Sufiks ^ ke parameter revisi berarti induk pertama dari objek komit tersebut. Notasi awalan digunakan untuk mengecualikan komit yang dapat dijangkau dari komit.
Tyler Rick

13
Saya hanya ingin berbagi pendekatan lain yang tampaknya paling bersih dan tidak berbelit-belit dari semuanya: jasonrudolph.com/blog/2009/02/25/... total kesederhanaan dan mengagumkan
superuseroi

14
Bingung oleh perdebatan tentang pendekatan mana yang 'benar'? Pertimbangkan perbedaan antara file & komit (lihat Keterangan di bawah) . OP ingin menggabungkan FILES & tidak menyebutkan KOMIT. Jawaban dengan suara lebih tinggi khusus untuk file; jawaban yang diterima menggunakan ceri-pilih, yang khusus untuk berkomitmen. Cherry-pick mungkin merupakan kunci untuk menggabungkan komit secara selektif, tetapi bisa sangat menyakitkan untuk memindahkan file dari satu cabang ke cabang lainnya. Meskipun komit adalah jantung dari kekuatan git, jangan lupa file masih memiliki peran!
Kay V

970

Saya memiliki masalah yang sama persis seperti yang disebutkan oleh Anda di atas. Tetapi saya menemukan ini lebih jelas dalam menjelaskan jawabannya.

Ringkasan:

  • Periksa jalur dari cabang yang ingin Anda gabungkan,

    $ git checkout source_branch -- <paths>...
    

    Petunjuk: Ini juga berfungsi tanpa --seperti yang terlihat di pos tertaut.

  • atau untuk secara selektif menggabungkan bakhil

    $ git checkout -p source_branch -- <paths>...
    

    Atau, gunakan reset dan kemudian tambahkan dengan opsi -p,

    $ git reset <paths>...
    $ git add -p <paths>...
    
  • Akhirnya komit

    $ git commit -m "'Merge' these changes"
    

9
Artikel yang ditautkan oleh Bart J adalah pendekatan terbaik. Jelas, sederhana, satu perintah. Ini yang akan saya gunakan. :)
Pistos

256
Ini bukan penggabungan nyata. Anda memilih perubahan berdasarkan file alih-alih dengan komit, dan Anda akan kehilangan informasi komit yang ada (penulis, pesan). Memang, ini bagus jika Anda ingin menggabungkan semua perubahan dalam beberapa file dan tidak apa-apa jika Anda harus melakukan kembali semua komitmen. Tetapi jika file berisi kedua perubahan untuk digabungkan dan yang lainnya untuk dibuang, salah satu metode yang disediakan dalam jawaban lain akan lebih membantu Anda.
akaihola

10
@ mykhal dan lain-lain: ini secara bertahap file dalam indeks, sehingga Anda jika Anda check out foo.clakukan git reset HEAD foo.cuntuk unstage file itu dan Anda kemudian dapat diff itu. Saya menemukan ini setelah mencobanya dan kembali ke sini untuk mencari jawaban atas hal ini
michiakig

12
untuk melihat perubahannya, Anda juga dapat menggunakan:git diff --cached
OderWat

9
Menurut jawaban ini git checkout -p <revision> -- <path>akan sama dengan mengeluarkan tiga perintah pertama yang Anda jelaskan :)
7hi4g0

338

Untuk menggabungkan file secara selektif dari satu cabang ke cabang lain, jalankan

git merge --no-ff --no-commit branchX

di mana branchXcabang yang ingin Anda gabungkan menjadi cabang saat ini.

The --no-commitpilihan akan menggelar file yang telah digabungkan oleh Git tanpa benar-benar melakukan mereka. Ini akan memberi Anda kesempatan untuk memodifikasi file yang digabungkan namun Anda ingin dan kemudian melakukannya sendiri.

Bergantung pada bagaimana Anda ingin menggabungkan file, ada empat kasus:

1) Anda ingin penggabungan yang benar.

Dalam hal ini, Anda menerima file yang digabungkan dengan cara Git menggabungkannya secara otomatis dan kemudian mengkomitnya.

2) Ada beberapa file yang tidak ingin Anda gabungkan.

Misalnya, Anda ingin mempertahankan versi di cabang saat ini dan mengabaikan versi di cabang yang Anda gabungkan.

Untuk memilih versi di cabang saat ini, jalankan:

git checkout HEAD file1

Ini akan mengambil versi file1di cabang saat ini dan menimpa yang file1diautomatisasi oleh Git.

3) Jika Anda menginginkan versi di branchX (dan bukan gabungan sebenarnya).

Lari:

git checkout branchX file1

Ini akan mengambil versi file1in branchXdan overwrite yang file1digabungkan secara otomatis oleh Git.

4) Kasus terakhir adalah jika Anda hanya ingin memilih gabungan tertentu file1.

Dalam hal ini, Anda dapat mengedit yang dimodifikasi file1secara langsung, memperbarui ke versi apa pun yang Anda inginkan file1, dan kemudian komit.

Jika Git tidak dapat menggabungkan file secara otomatis, itu akan melaporkan file sebagai " unmerged " dan menghasilkan salinan di mana Anda harus menyelesaikan konflik secara manual.



Untuk menjelaskan lebih lanjut dengan contoh, katakanlah Anda ingin bergabung branchXke cabang saat ini:

git merge --no-ff --no-commit branchX

Anda kemudian menjalankan git statusperintah untuk melihat status file yang dimodifikasi.

Sebagai contoh:

git status

# On branch master
# Changes to be committed:
#
#       modified:   file1
#       modified:   file2
#       modified:   file3
# Unmerged paths:
#   (use "git add/rm <file>..." as appropriate to mark resolution)
#
#       both modified:      file4
#

Di mana file1,, file2dan file3file-file git telah berhasil digabungkan secara otomatis.

Ini artinya bahwa perubahan pada masterdan branchXuntuk ketiga file tersebut telah digabungkan bersama tanpa konflik.

Anda dapat memeriksa bagaimana penggabungan dilakukan dengan menjalankan git diff --cached;

git diff --cached file1
git diff --cached file2
git diff --cached file3

Jika Anda menemukan beberapa penggabungan yang tidak diinginkan maka Anda bisa

  1. edit file secara langsung
  2. menyimpan
  3. git commit

Jika Anda tidak ingin menggabungkan file1dan ingin mempertahankan versi di cabang saat ini

Lari

git checkout HEAD file1

Jika Anda tidak ingin bergabung file2dan hanya ingin versi masukbranchX

Lari

git checkout branchX file2

Jika Anda ingin file3digabungkan secara otomatis, jangan lakukan apa pun.

Git sudah menggabungkannya pada saat ini.


file4di atas adalah penggabungan gagal oleh Git. Ini berarti ada perubahan di kedua cabang yang terjadi pada baris yang sama. Di sinilah Anda harus menyelesaikan konflik secara manual. Anda bisa membuang gabungan yang dilakukan dengan mengedit file secara langsung atau menjalankan perintah checkout untuk versi di cabang yang Anda inginkan file4.


Akhirnya, jangan lupa git commit.


10
Hati-hati: Jika git merge --no-commit branchXhanya maju cepat, penunjuk akan diperbarui, dan --no-komit karenanya diabaikan secara diam-diam
cfi

16
@ cfi Bagaimana dengan menambahkan --no-ffuntuk mencegah perilaku itu?
Eduardo Costa

5
Saya sarankan memperbarui jawaban ini dengan opsi "--no-ff" Eduardo. Saya membaca semuanya (yang sebenarnya hebat) hanya untuk membuat saya cepat diteruskan.
Funktr0n

7
Solusi ini memberikan hasil dan fleksibilitas terbaik.
Thiago Macedo

20
Tidak seperti jawaban dengan suara terbanyak, solusi ini mempertahankan sejarah gabungan saya, yang penting bagi saya karena saya menenun sebagian melakukan bolak-balik di seluruh cabang. Saya tidak mencoba semua solusi yang disarankan lainnya, jadi mungkin beberapa dari mereka melakukan ini juga.
ws_e_c421

107

Saya tidak suka pendekatan di atas. Menggunakan cherry-pick sangat bagus untuk memilih satu perubahan, tetapi itu menyebalkan jika Anda ingin membawa semua perubahan kecuali untuk beberapa perubahan buruk. Ini pendekatan saya.

Tidak ada --interactiveargumen yang bisa Anda sampaikan ke git merge.

Berikut ini alternatifnya:

Anda memiliki beberapa perubahan dalam 'fitur' cabang dan Anda ingin membawa beberapa tetapi tidak semuanya ke 'master' dengan cara yang tidak ceroboh (yaitu Anda tidak ingin memilih dan melakukan masing-masing)

git checkout feature
git checkout -b temp
git rebase -i master

# Above will drop you in an editor and pick the changes you want ala:
pick 7266df7 First change
pick 1b3f7df Another change
pick 5bbf56f Last change

# Rebase b44c147..5bbf56f onto b44c147
#
# Commands:
# pick = use commit
# edit = use commit, but stop for amending
# squash = use commit, but meld into previous commit
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.
#

git checkout master
git pull . temp
git branch -d temp

Jadi bungkus saja dalam skrip shell, ubah master menjadi $ to dan ubah fitur menjadi $ from dan Anda siap melakukannya:

#!/bin/bash
# git-interactive-merge
from=$1
to=$2
git checkout $from
git checkout -b ${from}_tmp
git rebase -i $to
# Above will drop you in an editor and pick the changes you want
git checkout $to
git pull . ${from}_tmp
git branch -d ${from}_tmp

Saya memperbaiki pemformatan - ini adalah metode yang cukup bagus, jika Anda ingin melakukan pilihan komit
1800 INFORMASI

Saya menggunakan teknik ini sekarang dan tampaknya telah bekerja dengan sangat baik.
dylanfm

4
Anda mungkin ingin mengubah git rebase -i $toke git rebase -i $to || $SHELL, sehingga pengguna dapat memanggil git --skipdll, jika perlu jika rebase gagal. Juga layak untuk merantai baris bersama dengan &&bukannya baris baru.
sircolinton

2
Sayangnya, tampaknya tautan dalam jawabannya sudah mati.
ThomasW

Tidak hanya tautannya mati, tetapi memiliki peringatan reputasi buruk WOT. Karena itu saya menghapusnya.
Jean-François Corbett

93

Ada cara lain:

git checkout -p

Ini adalah campuran antara git checkoutdan git add -pdan mungkin persis apa yang Anda cari:

   -p, --patch
       Interactively select hunks in the difference between the <tree-ish>
       (or the index, if unspecified) and the working tree. The chosen
       hunks are then applied in reverse to the working tree (and if a
       <tree-ish> was specified, the index).

       This means that you can use git checkout -p to selectively discard
       edits from your current working tree. See the “Interactive Mode”
       section of git-add(1) to learn how to operate the --patch mode.

10
Sejauh ini, ini adalah metode termudah dan paling sederhana, selama Anda hanya memiliki sejumlah perubahan yang dapat dikelola untuk digabungkan. Saya harap lebih banyak orang akan memperhatikan jawaban ini dan meningkatkannya. Contoh: checkout git --patch exp1 file_to_merge
Tyler Rick

1
Jawaban serupa diposting pada pertanyaan ini: stackoverflow.com/a/11593308/47185
Tyler Rick

Oh, saya tidak tahu checkout punya tambalan! Saya melakukan checkout / reset / add -p sebagai gantinya.
Daniel C. Sobral

2
Sungguh metode yang paling sederhana. git checkout -p featuresebranch nama file. Dan yang terbaik adalah ketika perintah dijalankan, ia memberi Anda ay / n / e /? / ... dll. opsi untuk memutuskan cara menggabungkan file. Saya mencoba dengan e dan saya bahkan dapat mengedit tambalan sebelum menerapkan ... Seberapa keren itu. Satu liner sejati untuk menggabungkan file selektif dari cabang lain.
infoclogged

55

Sementara beberapa jawaban ini cukup bagus, saya merasa tidak ada yang benar-benar menjawab kendala asli OP: memilih file tertentu dari cabang tertentu. Solusi ini dapat melakukannya, tetapi mungkin membosankan jika ada banyak file.

Katakanlah Anda memiliki master, exp1, dan exp2cabang. Anda ingin menggabungkan satu file dari masing-masing cabang eksperimental menjadi master. Saya akan melakukan sesuatu seperti ini:

git checkout master
git checkout exp1 path/to/file_a
git checkout exp2 path/to/file_b

# save these files as a stash
git stash
# merge stash with master
git merge stash

Ini akan memberi Anda in-file diff untuk setiap file yang Anda inginkan. Tidak ada lagi. Tidak kurang. Ini berguna Anda memiliki perubahan file yang sangat berbeda antara versi - dalam kasus saya, mengubah aplikasi dari Rails 2 ke Rails 3.

EDIT : ini akan menggabungkan file, tetapi menggabungkan cerdas. Saya tidak dapat menemukan cara menggunakan metode ini untuk mendapatkan informasi di-file in-file (mungkin masih akan untuk perbedaan ekstrem. Mengganggu hal-hal kecil seperti spasi putih digabungkan kembali kecuali Anda menggunakan -s recursive -X ignore-all-spaceopsi)


4
Juga perhatikan: Anda dapat melakukan banyak file dari cabang yang diberikan semua inline, misalnyagit checkout exp1 path/to/file_a path/to/file_x
EMiller

2
Ini indah. Saya lakukan git checkout feature <path>/*untuk mendapatkan kelompok file.
isherwood

Ini berfungsi baik, tetapi menambahkan dua objek komit tambahan. Bukan masalah besar tapi agak berantakan
MightyPork

@MightyPork Anda benar. Sayangnya, sejak saya menulis ini sejak lama, saya tidak lagi yakin mengapa langkah "git simpanan" dan "git penggabungan simpanan" ada di sana alih-alih "git commit".
Eric Hu

2
Oh, itu jelas, saya pikir. Dengan cara ini menggabungkan satu file, tidak perlu menimpa perubahan sebelumnya pada cabang target.
MightyPork

48

1800 INFORMASI jawaban sepenuhnya benar. Namun, sebagai git noob, "gunakan git cherry-pick" tidak cukup bagi saya untuk mencari tahu tanpa menggali lebih dalam di internet jadi saya pikir saya akan memposting panduan yang lebih rinci jika ada orang lain di perahu serupa.

Kasus penggunaan saya ingin secara selektif menarik perubahan dari cabang github orang lain ke saya sendiri. Jika Anda sudah memiliki cabang lokal dengan perubahan, Anda hanya perlu melakukan langkah 2 dan 5-7.

  1. Buat (jika tidak dibuat) cabang lokal dengan perubahan yang ingin Anda bawa.

    $ git branch mybranch <base branch>

  2. Beralihlah ke dalamnya.

    $ git checkout mybranch

  3. Tarik ke bawah perubahan yang Anda inginkan dari akun orang lain. Jika Anda belum melakukannya, Anda ingin menambahkannya sebagai remote.

    $ git remote add repos-w-changes <git url>

  4. Tarik semuanya dari cabang mereka.

    $ git pull repos-w-changes branch-i-want

  5. Lihat log komit untuk melihat perubahan mana yang Anda inginkan:

    $ git log

  6. Beralih kembali ke cabang tempat Anda ingin menarik perubahan.

    $ git checkout originalbranch

  7. Cherry memilih komitmen Anda, satu per satu, dengan hash.

    $ git cherry-pick -x hash-of-commit

Kiat topi: http://www.sourcemage.org/Git_Guide


3
Kiat: pertama-tama gunakan git cherryperintah (lihat manual terlebih dahulu) untuk mengidentifikasi komit yang belum Anda gabungkan.
akaihola

Ini berfungsi .. 1. membuat cabang baru 2. membuat beberapa file / membuat beberapa perubahan 3. melakukan 4. checkout cabang master 5. Menjalankan git cherry-pick -x hash-of-commit dan menyelesaikan konflik gabungan apakah Anda baik untuk pergi.
RamPrasadBismil

Tautan Anda tidak berfungsi lagi. Bisakah Anda memperbaruinya?
creep3007

42

Berikut adalah cara Anda dapat mengganti Myclass.javafile di mastercabang dengan Myclass.javadi feature1cabang. Ini akan berfungsi bahkan jika Myclass.javatidak ada master.

git checkout master
git checkout feature1 Myclass.java

Perhatikan bahwa ini akan menimpa - bukan menggabungkan - dan mengabaikan perubahan lokal di cabang master.


6
Ini tidak akan bergabung. Itu hanya akan menimpa perubahan pada master dengan perubahan dari cabang feature1.
Skunkwaffle

3
Sempurna, saya sedang mencari penggabungan semacam ini di mana theirsmenimpa ours=> +1 Cheers;)
olibre

1
Beberapa kali yang ingin Anda lakukan adalah mengganti seluruh file, jadi ini yang saya inginkan, tetapi Anda harus memastikan bahwa Anda ingin kehilangan semua perubahan yang Anda buat pada file ini.
MagicLAMP

1
Solusi terbersih, mengingat bahwa OP secara khusus ingin mengganti seluruh file dengan yang setara pada cabang lain:2. Manual copying of common files into a temp directory followed by ...copying out of the temp directory into the working tree.
Brent Faust

29

Cara sederhana, untuk benar-benar menggabungkan file tertentu dari dua cabang, bukan hanya mengganti file tertentu dengan yang dari cabang lain.

Langkah satu: Diff cabang-cabangnya

git diff branch_b > my_patch_file.patch

Membuat file tambalan dari perbedaan antara cabang saat ini dan branch_b

Langkah dua: Terapkan tambalan pada file yang cocok dengan suatu pola

git apply -p1 --include=pattern/matching/the/path/to/file/or/folder my_patch_file.patch

catatan berguna pada opsi

Anda dapat menggunakan *sebagai wildcard dalam pola include.

Garis miring tidak perlu lepas.

Selain itu, Anda dapat menggunakan --exclude sebagai gantinya dan menerapkannya pada semua hal kecuali file yang sesuai dengan pola, atau membalikkan tambalan dengan -R

Opsi -p1 adalah peninggalan dari perintah * unix patch dan fakta bahwa isi file patch mendahului setiap nama file dengan a/ataub/ (atau lebih tergantung pada bagaimana file patch dihasilkan) yang perlu Anda strip sehingga Anda dapat mencari tahu file asli ke jalur ke file patch perlu diterapkan.

Lihatlah halaman manual untuk git-apply untuk lebih banyak opsi.

Langkah tiga: tidak ada langkah ketiga

Jelas Anda ingin melakukan perubahan, tetapi siapa bilang Anda tidak memiliki beberapa tweak terkait lainnya yang ingin Anda lakukan sebelum membuat komit.


1
Ini sangat berguna di mana current_branch memiliki banyak perubahan "tambahan" yang perlu dipertahankan. Mendapat perbedaan hanya perubahan yang dibawa oleh branch_b sebagai: git diff HEAD ... branch_b (ya - tiga periode melakukan trik sulap).
Saad Malik

@masukomi, Pada langkah 2, bukankah seharusnya Anda menambahkan file tambalan yang dibuat pada langkah 1 sebagai argumen?
Spiralis

Bagi saya, semua perubahan ditolak. Ada yang tahu kenapa?
LinusGeffarth

Pikiran awal @LinusGeffarth adalah mungkin Anda mendapatkan cabang ke belakang saat membuat tambalan? akan menindaklanjuti di luar SO untuk melihat apakah kami dapat mengetahuinya.
masukomi

24

Berikut adalah cara Anda mendapatkan riwayat untuk mengikuti hanya beberapa file dari cabang lain dengan minimal keributan, bahkan jika gabungan yang lebih "sederhana" akan menghasilkan lebih banyak perubahan yang tidak Anda inginkan.

Pertama, Anda akan mengambil langkah yang tidak biasa menyatakan sebelumnya bahwa apa yang akan Anda lakukan adalah penggabungan, tanpa git melakukan apa pun pada file di direktori kerja Anda:

git merge --no-ff --no-commit -s ours branchname1

. . . di mana "branchname" adalah apa pun yang Anda klaim untuk gabungkan. Jika Anda berkomitmen segera, itu tidak akan membuat perubahan tetapi itu akan tetap menunjukkan keturunan dari cabang lain. Anda dapat menambahkan lebih banyak cabang / tag / dll. ke baris perintah jika Anda perlu, juga. Pada titik ini, tidak ada perubahan untuk dikomit, jadi dapatkan file dari revisi lain, selanjutnya.

git checkout branchname1 -- file1 file2 etc

Jika Anda menggabungkan dari lebih dari satu cabang lain, ulangi sesuai kebutuhan.

git checkout branchname2 -- file3 file4 etc

Sekarang file dari cabang lain berada di indeks, siap untuk dilakukan, dengan sejarah.

git commit

dan Anda akan memiliki banyak penjelasan yang harus dilakukan dalam pesan komit itu.

Harap dicatat, kalau-kalau tidak jelas, ini harus dikacaukan. Bukan dalam semangat untuk apa "cabang" itu, dan memilih ceri adalah cara yang lebih jujur ​​untuk melakukan apa yang akan Anda lakukan, di sini. Jika Anda ingin melakukan "penggabungan" lain untuk file lain di cabang yang sama yang tidak Anda bawa terakhir kali, itu akan menghentikan Anda dengan pesan "sudah terbaru". Ini adalah gejala tidak bercabang ketika kita seharusnya, dalam cabang "dari" harus lebih dari satu cabang yang berbeda.


3
Perintah pertama Anda ( git merge --no-ff --no-commit -s outs branchname1) persis seperti yang saya cari! Terima kasih!
RobM

1
Dengan banyak cabang, diperlukan riwayat, perlu menggabungkan file tunggal dan harus mengubah konten file sebelum mendorong, ini tampaknya menjadi alternatif yang layak. Misalnya dev => master, tetapi Anda ingin mengubah definisi host atau yang serupa sebelum mendorong ke master.
timss

15

Saya tahu saya sedikit terlambat tetapi ini adalah alur kerja saya untuk menggabungkan file selektif.

#make a new branch ( this will be temporary)
git checkout -b newbranch
# grab the changes 
git merge --no-commit  featurebranch
# unstage those changes
git reset HEAD
(you can now see the files from the merge are unstaged)
# now you can chose which files are to be merged.
git add -p
# remember to "git add" any new files you wish to keep
git commit

Saya menggunakan sedikit variasi dalam hal ini. Alih-alih bergabung saya memilih cherry. Itu berhasil. Satu-satunya downside ke pendekatan ini adalah Anda kehilangan referensi ke hash komit asli.
Matt Florence

15

Cara termudah adalah dengan mengatur repo Anda ke cabang yang ingin Anda gabungkan lalu jalankan,

git checkout [branch with file] [path to file you would like to merge]

Jika Anda berlari

git status

Anda akan melihat file sudah dipentaskan ...

Lalu lari

git commit -m "Merge changes on '[branch]' to [file]"

Sederhana.


3
Ini hampir jawaban terbaik yang saya temukan. silakan lihat jasonrudolph.com/blog/2009/02/25/... Sangat jelas, ringkas dan hanya berfungsi!
superuseroi

1
yang sepenuhnya akan menggantikan konten file dari cabang sumber daripada penggabungan
Amare

Saya baru saja akan menjawab seperti ini, saya pikir saya menemukan hal-hal baru yang belum dijawab! Tetapi ini adalah cara paling sederhana untuk melakukannya. Ini harus di atas!
Irfandy Jip

15

Saya menemukan posting ini mengandung jawaban paling sederhana. Cukup lakukan:

$ #git checkout <branch from which you want files> <file paths>

Contoh:

$ #pulling .gitignore file from branchB into current branch
$ git checkout branchB .gitignore

Lihat posting untuk info lebih lanjut.


3
Ini tidak benar-benar bergabung, itu menimpa file pada cabang saat ini.
Igor Ralic

1
@igrali Itu komentar yang berguna, tetapi dibandingkan dengan kesulitan cara yang "tepat" untuk melakukan ini, ini adalah solusi yang baik. Seseorang harus sangat berhati-hati.
owensmartin

12

Sungguh aneh bahwa git masih tidak memiliki alat yang nyaman "out of the box". Saya sangat menggunakannya ketika memperbarui beberapa cabang versi lama (yang masih memiliki banyak pengguna perangkat lunak) dengan hanya beberapa perbaikan bug dari cabang versi saat ini. Dalam hal ini, sering kali diperlukan untuk mendapatkan hanya beberapa baris kode dari file di trunk, mengabaikan banyak perubahan lain (yang tidak seharusnya masuk ke versi lama) ... Dan tentu saja interaktif tiga cara menggabungkan diperlukan dalam kasus ini, git checkout --patch <branch> <file path>tidak dapat digunakan untuk tujuan penggabungan selektif ini.

Anda dapat melakukannya dengan mudah:

Cukup tambahkan baris ini ke [alias]bagian di file global .gitconfigatau lokal Anda .git/config:

[alias]
    mergetool-file = "!sh -c 'git show $1:$2 > $2.theirs; git show $(git merge-base $1 $(git rev-parse HEAD)):$2 > $2.base; /C/BCompare3/BCompare.exe $2.theirs $2 $2.base $2; rm -f $2.theirs; rm -f $2.base;' -"

Ini berarti Anda menggunakan Beyond Compare. Ubah saja ke perangkat lunak pilihan Anda jika perlu. Atau Anda dapat mengubahnya menjadi penggabungan otomatis tiga arah jika Anda tidak memerlukan penggabungan selektif interaktif:

[alias]
    mergetool-file = "!sh -c 'git show $1:$2 > $2.theirs; git show $(git merge-base $1 $(git rev-parse HEAD)):$2 > $2.base; git merge-file $2 $2.base $2.theirs; rm -f $2.theirs; rm -f $2.base;' -"

Kemudian gunakan seperti ini:

git mergetool-file <source branch> <file path>

Ini akan memberi Anda peluang penggabungan jalan selektif sejati dari sembarang file di cabang lain.


10

Bukan apa yang Anda cari, tapi itu berguna bagi saya:

git checkout -p <branch> -- <paths> ...

Ini adalah campuran dari beberapa jawaban.


2
Ini memang berguna dan dapat ditambahkan ke jawaban terbaik bagi saya, jawaban @ alvinabad. Ketika melakukan: git checkout HEAD file1untuk menjaga versi saat ini dan memisahkan file file1, seseorang dapat menggunakan -popsi untuk memilih bagian dari file yang akan digabung. terima kasih untuk triknya!
Simon C.

Ini adalah jawaban favorit saya. Sederhana, to the point dan berhasil
Jesse Reza Khorasanee

8

Saya akan melakukan

git diff commit1..commit2 filepattern | git-apply --index && git commit

Dengan cara ini Anda dapat membatasi rentang komit untuk filepattern dari cabang.

Dicuri dari: http://www.gelato.unsw.edu.au/archives/git/0701/37964.html


Dalam beberapa situasi, ini bisa sangat berguna. Namun, jika perubahan ada di cabang yang berbeda, Anda bisa checkout dari ujung cabang itu, seperti pada jawaban Bart J di atas.
cdunn2001

Siapakah Bart Js?
Hitam

8

Saya memiliki masalah yang sama persis seperti yang disebutkan oleh Anda di atas. Tapi saya menemukan blog git ini lebih jelas dalam menjelaskan jawabannya.

Perintah dari tautan di atas:

#You are in the branch you want to merge to
git checkout <branch_you_want_to_merge_from> <file_paths...>

apakah kamu menguji ini? saya yakin file akan diganti dari <branch_you_want_to_merge_from> daripada digabung
Amare

7

Saya suka jawaban 'git-interactive-merge', di atas, tetapi ada satu yang lebih mudah. Biarkan git melakukan ini untuk Anda menggunakan kombinasi rebase interaktif dan ke:

      A---C1---o---C2---o---o feature
     /
----o---o---o---o master

Jadi masalahnya adalah Anda ingin C1 dan C2 dari cabang 'fitur' (titik cabang 'A'), tetapi tidak ada yang lain untuk saat ini.

# git branch temp feature
# git checkout master
# git rebase -i --onto HEAD A temp

Yang, seperti di atas, menjatuhkan Anda ke editor interaktif tempat Anda memilih baris 'pilih' untuk C1 dan C2 (seperti di atas). Simpan dan keluar, dan kemudian akan melanjutkan dengan rebase dan memberi Anda cabang 'temp' dan juga KEPALA di master + C1 + C2:

      A---C1---o---C2---o---o feature
     /
----o---o---o---o-master--C1---C2 [HEAD, temp]

Kemudian Anda bisa memperbarui master ke HEAD dan menghapus cabang temp dan Anda bisa melanjutkan:

# git branch -f master HEAD
# git branch -d temp

7

Bagaimana dengan git reset --soft branch? Saya terkejut bahwa belum ada yang menyebutkannya.

Bagi saya, ini adalah cara termudah untuk secara selektif memilih perubahan dari cabang lain, karena, perintah ini menempatkan di pohon kerja saya, semua perubahan berbeda dan saya dapat dengan mudah memilih atau mengembalikan yang mana yang saya butuhkan. Dengan cara ini, saya memiliki kendali penuh atas file yang dikomit.


6

Saya tahu pertanyaan ini sudah lama dan ada banyak jawaban lain, tetapi saya menulis skrip saya sendiri yang disebut 'pmerge' untuk menggabungkan sebagian direktori. Ini adalah pekerjaan yang sedang berjalan dan saya masih belajar skrip git dan bash.

Perintah ini menggunakan git merge --no-commitdan kemudian menghapus perubahan yang tidak cocok dengan jalur yang disediakan.

Penggunaan: git pmerge branch path
Contoh:git merge develop src/

Saya belum mengujinya secara ekstensif. Direktori yang berfungsi harus bebas dari perubahan yang tidak dikomit dan file yang tidak terlacak.

#!/bin/bash

E_BADARGS=65

if [ $# -ne 2 ]
then
    echo "Usage: `basename $0` branch path"
    exit $E_BADARGS
fi

git merge $1 --no-commit
IFS=$'\n'
# list of changes due to merge | replace nulls w newlines | strip lines to just filenames | ensure lines are unique
for f in $(git status --porcelain -z -uno | tr '\000' '\n' | sed -e 's/^[[:graph:]][[:space:]]\{1,\}//' | uniq); do
    [[ $f == $2* ]] && continue
    if git reset $f >/dev/null 2>&1; then
        # reset failed... file was previously unversioned
        echo Deleting $f
        rm $f
    else
        echo Reverting $f
        git checkout -- $f >/dev/null 2>&1
    fi
done
unset IFS

4

Anda dapat menggunakan read-treeuntuk membaca atau menggabungkan pohon jarak jauh yang diberikan ke indeks saat ini, misalnya:

git remote add foo git@example.com/foo.git
git fetch foo
git read-tree --prefix=my-folder/ -u foo/master:trunk/their-folder

Untuk melakukan penggabungan, gunakan -msaja.

Lihat juga: Bagaimana cara saya menggabungkan sub direktori di git?


3

Pendekatan sederhana untuk menggabungkan / melakukan selektif berdasarkan file:

git checkout dstBranch git merge srcBranch // make changes, including resolving conflicts to single files git add singleFile1 singleFile2 git commit -m "message specific to a few files" git reset --hard # blow away uncommitted changes


3

Jika Anda tidak memiliki terlalu banyak file yang telah berubah, ini akan membuat Anda tanpa komitmen tambahan.

1. Cabang duplikat sementara
$ git checkout -b temp_branch

2. Setel ulang ke komit yang terakhir diinginkan
$ git reset --hard HEAD~n , di mana njumlah komit yang Anda perlukan untuk kembali

3. Periksa setiap file dari cabang asli
$ git checkout origin/original_branch filename.ext

Sekarang Anda dapat melakukan dan memaksa push (untuk menimpa jarak jauh), jika perlu.


3

Jika Anda hanya perlu menggabungkan direktori tertentu dan membiarkan yang lainnya tetap utuh dan belum menyimpan sejarah, Anda dapat mencoba ini ... buat yang baru target-branchdari mastersebelum Anda bereksperimen.

Langkah-langkah di bawah ini menganggap Anda memiliki dua cabang target-branchdan source-branch, dan direktori dir-to-mergeyang ingin Anda gabungkan ada di source-branch. Juga asumsikan Anda memiliki direktori lain seperti dir-to-retaindi target yang Anda tidak ingin mengubah dan mempertahankan sejarah. Juga, anggap ada konflik gabungan di dir-to-merge.

git checkout target-branch
git merge --no-ff --no-commit -X theirs source-branch
# the option "-X theirs", will pick theirs when there is a conflict. 
# the options "--no--ff --no-commit" prevent a commit after a merge, and give you an opportunity to fix other directories you want to retain, before you commit this merge.

# the above, would have messed up the other directories that you want to retain.
# so you need to reset them for every directory that you want to retain.
git reset HEAD dir-to-retain
# verify everything and commit.

2

Ketika hanya beberapa file telah berubah di antara commit saat ini dari dua cabang, saya secara manual menggabungkan perubahan dengan menelusuri file yang berbeda.

git difftoll <branch-1>..<branch-2>

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.