Git-cherry-pick sadece belirli dosyalarda nasıl değişiklik yapılır?


587

Bir Git dalında birleştirmek istersem, yalnızca bazı dosyalarda yapılan değişiklikler, birden fazla dosyada değişiklik içeren belirli bir işlemde değiştirilirse, bu nasıl başarılabilir?

Git varsayalım denilen kesinleştirme stuffdosyalara değişiklikleri var A, B, Cve Dfakat sadece birleştirmek istediğiniz stuffdosyaları 'ın değişiklikleri Ave B. Bir iş gibi geliyor, git cherry-pickancak cherry-pickdosyaların bir alt kümesini değil, yalnızca tüm taahhütlerin nasıl birleştirileceğini biliyor.

Yanıtlar:


689

Taahhüt etmeden önce sonucu incelemenizi (ve değiştirmenizi) sağlayan cherry-pick -n( --no-commit) ile yapardım :

git cherry-pick -n <commit>

# unstage modifications you don't want to keep, and remove the
# modifications from the work tree as well.
# this does work recursively!
git checkout HEAD <path>

# commit; the message will have been stored for you by cherry-pick
git commit

Değişikliklerin büyük çoğunluğu, tek tek yolları (orta adım) kontrol etmek yerine, istemediğiniz şeylerse, her şeyi geri yükleyebilir, ardından ne istediğinizi ekleyebilirsiniz:

# unstage everything
git reset HEAD

# stage the modifications you do want
git add <path>

# make the work tree match the index
# (do this from the top level of the repo)
git checkout .

10
Buna ek olarak , kiraz toplama taahhüdü tarafından sunulan yeni ancak istenmeyen dosyaları kaldırmanızı git checkout .da tavsiye ederim git clean -f.
rlat

4
İkinci yöntem için ek not: Dosya başınagit add -p dizine hangi değişiklikleri eklemek istediğinizi etkileşimli olarak belirlemenize izin veren kullanıyorum
matthaeus

6
Bu kiraz aldı o kadar farklıdır çünkü geçerli çalışma kopyası için geçerli değildir taahhüt bu durumda o kadar büyük değil, ama bu bir dosya olacaktır temiz geçerlidir.
Sınırlı Kefaret

3
Ayrıca seçici olarak ile de değişmez git reset -p HEAD. Bu eşdeğerdir add -pama çok azı var olduğunu bilir.
Patrick Schlüter

1
Çok faydalı bir numara. Birisi hızlı bir komut dosyası olarak ihtiyaç duyması durumunda bir gist koydum gist.github.com/PiDayDev/68c39b305ab9d61ed8bb2a1195ee1afc
Damiano

146

Diğer yöntemler benim için işe yaramadı çünkü taahhüdün birçok başka dosyada çok fazla değişiklik ve çatışma vardı. Ortaya koyduğum sadece

git show SHA -- file1.txt file2.txt | git apply -

Aslında adddosyaları yapmaz veya sizin için bir taahhütte bulunmaz , bu yüzden onu takip etmeniz gerekebilir

git add file1.txt file2.txt
git commit -c SHA

Veya eklemeyi atlamak isterseniz, --cached için argümangit apply

git show SHA -- file1.txt file2.txt | git apply --cached -

Aynı şeyi tüm dizinler için de yapabilirsiniz

git show SHA -- dir1 dir2 | git apply -

2
İlginç bir yöntem, teşekkürler. Ama değil show SHA -- file | applytemelde aynı yapmak checkout SHA -- filegibi Mark Longair cevabı ?
Tobias Kienzler

4
Hayır, SHA'daki checkout SHA -- filesürümü tam olarak kontrol eder, ancak show SHA -- file | applysadece SHA'daki değişiklikleri uygular (tıpkı kiraz toplama gibi). (A) Kaynak şubede verilen dosyayı değiştiren birden fazla işlem varsa veya (b) mevcut hedef şubenizdeki dosyayı değiştiren bir işlem varsa önemlidir.
Michael Anderson

9
Sadece bunun için başka bir harika kullanım bulundu: seçici geri alma, yalnızca bir dosyayı geri almak istediğinizde ( git reverttüm taahhüdü geri aldığından ). Bu durumda sadece kullanıngit show -R SHA -- file1.txt file2.txt | git apply -
Michael Anderson

2
@RoeiBahumi çok farklı bir anlamı var. git diff SHA -- file1.txt file2.txt | git apply -dosyanın geçerli sürümü ile SHA'daki sürüm arasındaki tüm farklılıkları geçerli sürüme uygulamak anlamına gelir. Aslında aynıdır git checkout SHA -- file1.txt file2.txt. Bu git showsürümün neden farklı olduğundan önceki yorumuma bakın .
Michael Anderson

5
Çatışmaları çözmeniz gerekiyorsa git apply -3 -, sadece yerine kullanın git apply -, bir çakışma olursa, kullanmak da dahil olmak üzere standart çakışma çözümleme tekniğinizi kullanabilirsiniz git mergetool.
qwertzguy

87

Ben genellikle -p , diğer dallardan karşılaştığım diğer yöntemlerden daha kolay ve daha ayrıntılı bulduğum bir daldan çıkış işaretiyle bayrağı .

Prensip olarak:

git checkout <other_branch_name> <files/to/grab in/list/separated/by/spaces> -p

misal:

git checkout mybranch config/important.yml app/models/important.rb -p

Daha sonra, bu hemen hemen ardından işaret edebilir sürekli kod değişikliği her yığın dışarı çalışır siz "lekeler" istediğiniz hangi değişikliklerin soran bir iletişim kutusu görürler y(Evet)n kodunun her parça için vb (Hayır).

-pVeya patchopsiyon dahil GIT'de komutları çeşitli için çalışıyorgit stash save -p bunu mevcut işten saklamak istediğinizi seçmenize olanak verir

Bazen bu tekniği çok fazla iş yaptığımda ve ayırmak ve git add -pher taahhüt için ne istediğimi kullanarak ve seçerek daha fazla konu bazlı taahhütte bulunmak istediğimde kullanıyorum :)


3
Düzenli olarak kullandığım git-add -pama ben bilmiyordum git-checkoutayrıca sahiptir -pbayrak - yaptığı düzeltme birleştirme problemleri olmayan -pcevap vardır?
Tobias Kienzler

1
en azından -pböyle bir çelişkili bölüm için elle düzenlemeye izin verirdi, bu cherry-pickda yine de yine de verim verecektir. Bunu bir daha ihtiyacım olduğunda test edeceğim, kesinlikle ilginç bir yaklaşım
Tobias Kienzler

2
Dallarda eşzamanlı değişiklikleri öldürmeyen en iyi iki cevaptan biri.
akostadinov

1
Hangi parçaların uygulanacağını seçmek için bu cevaba bakınız: stackoverflow.com/a/10605465/4816250 Özellikle 's' seçeneği çok yardımcı oldu.
jvd10

1
git reset -p HEADAyrıca, -pyalnızca bazı yamaları dizinden kaldırmak istediğinizde kullanışlı olabilmesini sağlar .
Patrick Schlüter

42

Belki üzerinde Bu yöntemin avantajı Jefromi cevabı size davranışı hangi hatırlamak zorunda kalmamasıdır git sıfırlama doğru olanıdır :)

 # Create a branch to throw away, on which we'll do the cherry-pick:
 git checkout -b to-discard

 # Do the cherry-pick:
 git cherry-pick stuff

 # Switch back to the branch you were previously on:
 git checkout -

 # Update the working tree and the index with the versions of A and B
 # from the to-discard branch:
 git checkout to-discard -- A B

 # Commit those changes:
 git commit -m "Cherry-picked changes to A and B from [stuff]"

 # Delete the temporary branch:
 git branch -D to-discard

2
Cevabınız için teşekkürler. Bu bana düşünmem için ilham verdi, neden atlayıp cherry-pickdoğrudan kullanmıyorsun git checkout stuff -- A B? Ve git commit -C stufftaahhüt mesajı da aynı kalacaktı
Tobias Kienzler

8
@Tobias: Bu değişiklik dosyalar yalnızca çalışacak stuffgeçerli dal üzerinde veya herhangi bir yerde ortak atası arasındaki modifiye edilmemiş HEADve stuffve ucu stuff. Eğer öyleyse, o zaman cherry-pickdoğru sonucu yaratır (esas olarak bir birleşme sonucu), yönteminiz mevcut daldaki değişiklikleri atar ve ortak atadaki tüm değişiklikleri stuffsadece budakilere değil tek taahhüt.
Cascabel

2
@Tobias Kienzler: Başlangıç ​​noktanızın, stuffvişne seçiminin sonucunun ayrılacağından Ave Btaahhütte içeriklerinden farklı içeriğe sahip olduğundan yeterince farklı olduğunu varsayıyordum stuff. Ancak, sadece aynı olurdu, haklısın - sadece dediğin gibi yapabilirsin.
Mark Longair

@Jeromi, @Mark: Geri bildiriminiz için teşekkürler, benim durumumda şubeleri tamamen öneri dosyaları ile tedavi ediyorum ve bu da beni önerime götürdü. Ama er ya da geç onunla başım belada olurdu, bu yüzden bunu getirdiğin için teşekkür ederim
Tobias Kienzler

Sanırım bu diğer konudaki cevabım senin peşinde olduğun şey olabilir.
Ian

30

Kiraz seçim belirli bir "taahhüt" değişiklikleri seçmek için. En basit çözüm, belirli dosyaların tüm değişikliklerini seçmektir.

 git checkout source_branch <paths>...

Örnek olarak:

$ git branch
* master
  twitter_integration
$ git checkout twitter_integration app/models/avatar.rb db/migrate/20090223104419_create_avatars.rb test/unit/models/avatar_test.rb test/functional/models/avatar_test.rb
$ git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#   new file:   app/models/avatar.rb
#   new file:   db/migrate/20090223104419_create_avatars.rb
#   new file:   test/functional/models/avatar_test.rb
#   new file:   test/unit/models/avatar_test.rb
#
$ git commit -m "'Merge' avatar code from 'twitter_integration' branch"
[master]: created 4d3e37b: "'Merge' avatar code from 'twitter_integration' branch"
4 files changed, 72 insertions(+), 0 deletions(-)
create mode 100644 app/models/avatar.rb
create mode 100644 db/migrate/20090223104419_create_avatars.rb
create mode 100644 test/functional/models/avatar_test.rb
create mode 100644 test/unit/models/avatar_test.rb

Kaynaklar ve tam açıklama http://jasonrudolph.com/blog/2009/02/25/git-tip-how-to-merge-specific-files-from-another-branch/

GÜNCELLEME:

Bu yöntemle git dosyayı BİRLEŞTİRMEZ, hedef dalda yapılan diğer değişiklikleri geçersiz kılar. Değişiklikleri manuel olarak birleştirmeniz gerekir:

$ git diff HEAD dosya adı


5
Ben de öyle düşündüm , ancak mevcut şubenizin değişikliklerini attığı için dosyalar her iki dalda da değiştiyse bu korkunç bir şekilde başarısız oluyor
Tobias Kienzler 4:13

Haklısın, git'in bu şekilde birleşmediğini, sadece geçersiz kıldığını açıklığa kavuşturmak şart. Daha sonra nelerin değiştiğini görmek ve birleştirmeyi elle yapmak için "git diff HEAD dosya adı" yapabilirsiniz.
cminatti

18

Durum:

Diyelim ki dalınızdasın masterve diyelim ki başka bir şubeye bağlılığınız var. Söz konusu işlemden yalnızca bir dosya seçmelisiniz.

Yaklaşım:

Adım 1: Gerekli şubede ödeme yapın.

git checkout master

2. Adım: Gerekli işleme karmasını kopyaladığınızdan emin olun.

git checkout commit_hash path\to\file

Adım 3: İstediğiniz dalda istediğiniz dosya değişikliğine sahipsiniz. Bunları eklemeniz ve taahhüt etmeniz yeterlidir.

git add path\to\file
git commit -m "Your commit message"

1
Müthiş! Ayrıca benim için \ path \ to \ directory \ olan bir dizindeki tüm değişiklikler için çalıştı
zaggi

13

Her şeyi sadece kiraz alırdım, sonra şunu yapardım:

git reset --soft HEAD^

Sonra istemediğim değişiklikleri geri alırdım, sonra yeni bir taahhütte bulunacağım.


11

git merge --squash branch_nameBunu kullanın , diğer daldaki tüm değişiklikleri alacak ve sizin için bir taahhüt hazırlayacaktır. Şimdi tüm gereksiz değişiklikleri kaldırın ve istediğiniz değişiklikleri bırakın. Ve git bir birleşme olduğunu bilmeyecek.


Teşekkürler, bu birleştirme seçeneğini bilmiyordum. Tüm bir dalın çoğunu kiraz toplamak istiyorsanız uygun bir alternatiftir (ancak kiraz
toplamanın aksine

4

IMO'nun hatırlanması ve anlaşılması kolay olan kiraz toplamada herhangi bir çelişkili birleşmeyi önleyen başka bir yol buldum. Aslında bir vişne seçmediğiniz için, ama bunun bir parçası olarak, önce onu bölmeniz ve ardından ihtiyaçlarınıza uyacak ve kiraz seçimini yapacak bir taahhüt oluşturmanız gerekir.

Öncelikle bölmek istediğiniz taahhütten bir şube oluşturun ve ödünç alın:

$ git checkout COMMIT-TO-SPLIT-SHA -b temp

Ardından önceki taahhüdü geri alın:

$ git reset HEAD~1

Ardından, kiraz seçimini yapmak istediğiniz dosyaları / değişiklikleri ekleyin:

$ git add FILE

ve taahhüt edin:

$ git commit -m "pick me"

taahhüt karmasını not edin, ona PICK-SHA diyelim ve ana şubenize geri dönelim, örneğin kasayı zorlamak için usta:

$ git checkout -f master

ve kiraz seçimini yap:

$ git cherry-pick PICK-SHA

şimdi geçici dalı silebilirsiniz:

$ git branch -d temp -f

2

Bir dalı yenisiyle (squash) birleştirin ve gerekli olmayan dosyaları kaldırın:

git checkout master
git checkout -b <branch>
git merge --squash <source-branch-with-many-commits>
git reset HEAD <not-needed-file-1>
git checkout -- <not-needed-file-1>
git reset HEAD <not-needed-file-2>
git checkout -- <not-needed-file-2>
git commit

2

Tamlık uğruna, benim için en iyi olan şey:

git show YOURHASH --no-color -- file1.txt file2.txt dir3 dir4 | git apply -3 --index -

git status

OP'nin tam olarak istediği şeyi yapar. İhtiyaç duyulduğunda uyuşmazlık çözümlemesi yapar, benzer şekilde nasıl mergeyapar. Yeni değişikliklerinizi yapmaz addama yapmaz commit.


1

Kullanabilirsiniz:

git diff <commit>^ <commit> -- <path> | git apply

Gösterim <commit>^, öğesinin (ilk) üst öğesini belirtir <commit>. Dolayısıyla, bu fark komutu yapılan değişiklikleri alır <path>taahhüt içinde<commit> .

Bunun henüz hiçbir şey işlemeyeceğini unutmayın (olduğu git cherry-pickgibi). Yani bunu istiyorsanız, yapmanız gerekenler:

git add <path>
git commit
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.