Git-cherry-pick केवल कुछ फ़ाइलों में कैसे बदलता है?


586

अगर मैं एक Git शाखा में विलय करना चाहता हूं, तो केवल कुछ फ़ाइलों में किए गए परिवर्तनों को एक विशेष प्रतिबद्ध में बदला गया है, जिसमें कई फ़ाइलों में परिवर्तन शामिल हैं, यह कैसे प्राप्त किया जा सकता है?

Git मान लीजिए बुलाया प्रतिबद्ध stuffफ़ाइलों में परिवर्तन है A, B, C, और D, लेकिन मैं केवल मर्ज करना चाहते हैं stuffफ़ाइलों के लिए के परिवर्तन Aऔर B। यह एक नौकरी की तरह लगता है git cherry-pickलेकिन cherry-pickकेवल यह जानता है कि संपूर्ण कमेट को कैसे मर्ज किया जाए, फाइलों का सबसेट नहीं।

जवाबों:


688

मैं इसे cherry-pick -n( --no-commit) के साथ करूँगा जो आपको करने से पहले परिणाम का निरीक्षण (और संशोधित) करने देता है:

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

यदि संशोधनों के विशाल बहुमत ऐसी चीजें हैं जो आप नहीं चाहते हैं, तो अलग-अलग रास्तों (मध्य चरण) की जांच करने के बजाय, आप सब कुछ वापस रीसेट कर सकते हैं, फिर आप जो चाहें उसमें जोड़ सकते हैं:

# 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
इसके अलावा, git checkout .मैं git clean -fचेरी-कमिट की गई किसी भी नई लेकिन अवांछित फाइल को हटाने की भी सिफारिश करूंगा ।
05

4
उत्तरार्द्ध विधि के लिए अतिरिक्त नोट: मैं उपयोग git add -pकरता हूं जो आपको अंतःक्रियात्मक रूप से तय करने देता है कि आप प्रति फ़ाइल
मैथ्यूस

6
यह इस मामले में बहुत अच्छा नहीं है कि चेरी-पिकेड कमिट वर्तमान वर्किंग कॉपी पर लागू नहीं होती है क्योंकि यह बहुत अलग है, लेकिन यह कि एक फाइल सफाई से लागू होगी
सीमित प्रायश्चित

3
आप चुनिंदा रूप से भी अस्थिर कर सकते हैं git reset -p HEAD। यह बराबर है, add -pलेकिन बहुत कम लोग जानते हैं कि यह मौजूद है।
पैट्रिक शाल्टर

1
बहुत उपयोगी ट्रिक। मैं एक सार मामले में किसी को एक त्वरित स्क्रिप्ट के रूप में यह जरूरत में डाला है gist.github.com/PiDayDev/68c39b305ab9d61ed8bb2a1195ee1afc
डेमियानो

146

दूसरे तरीकों ने मेरे लिए काम नहीं किया क्योंकि कमिट में बहुत सारी अन्य फाइलों के लिए बहुत सारे बदलाव और संघर्ष थे। मैं जो लेकर आया था वह बस था

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

यह वास्तव addमें फाइल नहीं करता है या आपके लिए कमिट करता है, इसलिए आपको इसे फॉलो करने की आवश्यकता हो सकती है

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

या यदि आप ऐड को छोड़ना चाहते हैं तो आप --cachedतर्क का उपयोग कर सकते हैंgit apply

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

आप पूरी निर्देशिका के लिए भी यही काम कर सकते हैं

git show SHA -- dir1 dir2 | git apply -

2
दिलचस्प विधि, धन्यवाद। लेकिन show SHA -- file | applyमूल checkout SHA -- fileरूप से मार्क लोंगेयर के जवाब में जैसा है वैसा नहीं है ?
टोबियास किंजलर

4
नहींं, checkout SHA -- fileSHA में बिल्कुल संस्करण की जाँच show SHA -- file | applyकरेगा , जबकि SHA में केवल परिवर्तन लागू करेगा (जैसे चेरी-पिक करता है)। यह (a) स्रोत शाखा में दी गई फ़ाइल को बदलने के लिए एक से अधिक प्रतिबद्ध होने पर, या (b) आपकी वर्तमान लक्ष्य शाखा में फ़ाइल को बदलने के लिए एक प्रतिबद्ध है तो यह मायने रखता है।
माइकल एंडरसन

9
बस इसके लिए एक और महान उपयोग पाया गया: चयनात्मक रिवर्ट, जब आप केवल एक फाइल को रिवर्ट करना चाहते हैं (क्योंकि git revertपूरे कमिट को अनडू करता है)। उस मामले में बस का उपयोग करेंgit show -R SHA -- file1.txt file2.txt | git apply -
माइकल एंडरसन

2
@ रोइबाहुमी जिसका काफी अलग अर्थ है। git diff SHA -- file1.txt file2.txt | git apply -साधन फ़ाइल के वर्तमान संस्करण और SHA में वर्तमान संस्करण के बीच के सभी अंतरों को लागू करें। संक्षेप में यह वैसा ही है git checkout SHA -- file1.txt file2.txt। इस git showसंस्करण के लिए अलग क्यों है के लिए मेरी पिछली टिप्पणी देखें ।
माइकल एंडरसन

5
यदि आपको संघर्षों को हल करना है , तो git apply -3 -इसके बजाय का उपयोग करें git apply -, यदि कोई संघर्ष होता है, तो आप अपने मानक संघर्ष समाधान तकनीक का उपयोग कर सकते हैं, जिसमें उपयोग करना भी शामिल है git mergetool
qwertzguy 16

87

मैं आमतौर पर -pदूसरी शाखा से एक गिट चेकआउट के साथ ध्वज का उपयोग करता हूं जो मुझे लगता है कि मैं भर में आया अन्य तरीकों की तुलना में आसान और अधिक दानेदार हूं।

सिद्धांत में:

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

उदाहरण:

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

फिर आपको एक संवाद मिलता है जिसमें आपसे पूछा जाता है कि आप "ब्लब्स" में कौन सा परिवर्तन चाहते हैं। यह निरंतर कोड परिवर्तन के हर भाग के लिए काम करता है जिसे आप कोड के प्रत्येक भाग के लिए तब y(हां) n(नहीं) आदि संकेत दे सकते हैं ।

-pया patchविकल्प सहित Git में आदेशों की एक किस्म के लिए काम करता है git stash save -pजो कि तुम क्या आप अपने वर्तमान काम से छिपाया जा चयन कर सकते

मैं कभी-कभी इस तकनीक का उपयोग करता हूं जब मैंने बहुत काम किया है और मैं इसे अलग git add -pकरना चाहता हूं और प्रत्येक विषय के लिए जो मैं चाहता हूं उसे चुनना और चुनना अधिक विषय आधारित प्रतिबद्ध है


3
मैं नियमित रूप से उपयोग करता हूं git-add -p, लेकिन मुझे नहीं पता था कि git-checkoutइसके पास एक -pझंडा भी है - जो विलय की समस्याओं को-p ठीक करता है गैर जवाब है?
टोबियास किंजलर

1
कम से कम -pइस तरह के एक परस्पर विरोधी अनुभाग के लिए एक मैनुअल एडिट की अनुमति होगी, जो cherry-pickशायद वैसे भी उपज देगा। मैं अगली बार इसका परीक्षण करूंगा, मुझे इसकी आवश्यकता है, निश्चित रूप से एक दिलचस्प दृष्टिकोण
टोबीस किन्ज़लर

2
दो सर्वश्रेष्ठ उत्तरों में से एक जो शाखाओं में समवर्ती परिवर्तनों को नहीं मारता है।
एकोस्टाडिनोव

1
यह चयन करने के लिए कि कौन से हॉक को लागू करने का चयन करें: stackoverflow.com/a/10605465/4816250 विशेष रूप से 's' विकल्प बहुत मददगार था।
jvd10

1
git reset -p HEADयह भी अनुमति देता है -pजिसे तब छोड़ा जा सकता है जब आप केवल सूचकांक से कुछ पैच निकालना चाहते हैं।
पैट्रिक श्लुटर

42

शायद जेफ्रोमी के जवाब पर इस पद्धति का लाभ यह है कि आपको यह याद रखने की ज़रूरत नहीं है कि गीट रीसेट का कौन सा व्यवहार सही है :)

 # 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
आपके उत्तर के लिए धन्यवाद। अब इसने मुझे सोचने के लिए प्रेरित किया, क्यों न cherry-pickसीधे और सीधे इस्तेमाल किया जाए git checkout stuff -- A B? और git commit -C stuffकमिट मैसेज के साथ भी वही रहेगा
टॉबीस किंजलर

8
@Tobias: यह काम करेगा ही अगर पर संशोधित फ़ाइलों stuffअपने वर्तमान शाखा पर या के आम पूर्वज के बीच कहीं भी संशोधित नहीं किया गया है HEADऔर stuffऔर की नोक stuff। यदि उनके पास है, तो cherry-pickसही परिणाम (अनिवार्य रूप से एक मर्ज का परिणाम) बनाता है, जबकि आपकी विधि वर्तमान शाखा में बदलावों को दूर कर देगी, और सामान्य पूर्वजों से सभी परिवर्तनों को रखने के लिए stuff- केवल उनमें से नहीं सिंगल कमिट।
Cascabel

2
@ टोबियास किंजलर: मैं यह मान रहा था कि आपका शुरुआती बिंदु माता-पिता से पर्याप्त रूप से अलग था stuff, चेरी पिक का नतीजा निकल जाएगा Aऔर Bकमिट में उनकी सामग्री से अलग सामग्री के साथ stuff। हालांकि, अगर यह सिर्फ एक ही होगा, तो आप सही हैं - जैसा आप कहते हैं वैसा ही कर सकते हैं।
मार्क लॉन्गेयर

@ जेरोमी, @ मर्क: आपकी प्रतिक्रिया के लिए धन्यवाद, मेरे मामले में मैं पूरी तरह से अव्यवस्थित फ़ाइलों के साथ शाखाओं का इलाज कर रहा हूं, जिससे मुझे मेरे सुझाव का सामना करना पड़ा। लेकिन वास्तव में मैं इसके साथ या बाद में परेशानी में चला गया था, इसलिए इसे लाने के लिए धन्यवाद
टोबीस किंजलर

मुझे लगता है कि इस दूसरे सूत्र में मेरा जवाब हो सकता है कि आप क्या कर रहे हैं।
इयान

30

चेरी पिक विशिष्ट "कमिट" से परिवर्तन लेने के लिए है। सबसे सरल समाधान कुछ फ़ाइलों के सभी परिवर्तनों का उपयोग करना है

 git checkout source_branch <paths>...

उदाहरण में:

$ 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

स्रोत और पूर्ण विवरण http://jasonrudolph.com/blog/2009/02/25/git-tip-how-to-merge-specific-files-from-another-branch/

अपडेट करें:

इस पद्धति के साथ, git फ़ाइल को MERGE नहीं करेगा, यह गंतव्य शाखा पर किए गए किसी भी अन्य परिवर्तन को ओवरराइड करेगा। आपको मैन्युअल रूप से परिवर्तनों को मर्ज करना होगा:

$ git का नाम HEAD फ़ाइल नाम है


5
मैंने भी ऐसा सोचा था , लेकिन यह बुरी तरह से विफल हो जाता है अगर फाइल दोनों शाखाओं पर बदल गई है क्योंकि यह आपकी वर्तमान शाखा के बदलावों को
खारिज कर देती है

आप सही हैं, यह स्पष्ट करना चाहिए कि इस तरह से यह जरूरी नहीं है, यह सिर्फ ओवरराइड करता है। फिर आप क्या बदल सकते हैं और क्या मर्ज मैन्युअल रूप से करते हैं यह देखने के लिए "git diff HEAD फ़ाइल नाम" कर सकते हैं।
cminatti 14:18

18

स्थिति:

आप अपनी शाखा पर हैं, मान लीजिए कि masterआप किसी अन्य शाखा पर अपनी प्रतिबद्धता रखते हैं। आपको उस विशेष कमिट से केवल एक फ़ाइल चुननी होगी।

पहुंच:

चरण 1: आवश्यक शाखा पर चेकआउट करें।

git checkout master

चरण 2: सुनिश्चित करें कि आपने आवश्यक प्रतिबद्ध हैश की प्रतिलिपि बनाई है।

git checkout commit_hash path\to\file

चरण 3: अब आपके पास अपनी इच्छित शाखा में आवश्यक फ़ाइल के परिवर्तन हैं। आपको बस उन्हें जोड़ने और प्रतिबद्ध करने की आवश्यकता है।

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

1
बहुत बढ़िया! इसके अलावा एक निर्देशिका में सभी परिवर्तनों के लिए मेरे लिए \ path \ to \ directory \ के साथ काम किया
zaggi

13

मैं बस सब कुछ चेरी-चुनूंगी, फिर यह करें:

git reset --soft HEAD^

तब मैं उन परिवर्तनों को वापस कर दूंगा जो मैं नहीं चाहता, फिर एक नई प्रतिबद्धता बनाएं।


11

इसका उपयोग git merge --squash branch_nameकरें अन्य शाखा से सभी परिवर्तन मिलेंगे और आपके लिए एक कमिट तैयार करेंगे। अब सभी अनावश्यक परिवर्तनों को हटा दें और जिसे आप चाहते हैं उसे छोड़ दें। और गिट पता नहीं चलेगा कि मर्ज था।


धन्यवाद, मुझे उस मर्ज विकल्प के बारे में नहीं पता था। यह एक व्यवहार्य विकल्प है यदि आप एक पूरी शाखा का अधिकांश हिस्सा चेरी-पिक करना चाहते हैं (लेकिन चेरी-पिक के विपरीत यह काम नहीं करेगा यदि कोई सामान्य पूर्वज नहीं है)
तोबियस किंज़्लर

4

मुझे एक और तरीका मिला जो चेरी-पिकिंग पर किसी भी परस्पर विरोधी मर्ज को रोकता है जिसे आईएमओ को याद रखना और समझना आसान है। चूँकि आप वास्तव में चेरी-कमिट नहीं कर रहे हैं, लेकिन इसका एक हिस्सा है, तो आपको इसे पहले विभाजित करने की आवश्यकता है और फिर एक कमिट तैयार करना है जो आपकी आवश्यकताओं और चेरी-पिक के अनुरूप होगा।

पहले उस शाखा से एक शाखा बनाएँ जिसे आप विभाजित करना चाहते हैं और उसकी जाँच करें:

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

फिर पिछली प्रतिबद्ध वापसी करें:

$ git reset HEAD~1

फिर चेरी / पिक में उन फ़ाइलों / परिवर्तनों को जोड़ें:

$ git add FILE

और इसे प्रतिबद्ध करें:

$ git commit -m "pick me"

प्रतिबद्ध हैश पर ध्यान दें, इसे PICK-SHA कहें और अपनी मुख्य शाखा में वापस जाएं, उदाहरण के लिए मास्टर चेकआउट के लिए मजबूर करें:

$ git checkout -f master

और चेरी-कमिट चुनें:

$ git cherry-pick PICK-SHA

अब आप अस्थायी शाखा को हटा सकते हैं:

$ git branch -d temp -f

2

एक शाखा को नए (स्क्वैश) में मिलाएं और उन फ़ाइलों को हटा दें जिनकी आवश्यकता नहीं है:

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

पूर्णता के लिए, मेरे लिए सबसे अच्छा काम क्या है:

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

git status

यह वही करता है जो ओपी चाहता है। यह जरूरत पड़ने पर संघर्ष का समाधान करता है, इसी तरह mergeयह कैसे करता है। यह करता है addलेकिन commitआपके नए परिवर्तन नहीं।


1

आप उपयोग कर सकते हैं:

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

संकेतन <commit>^निर्दिष्ट करता है (प्रथम) का जनक <commit>। इसलिए, यह अलग कमांड <path>कमिट में किए गए बदलावों को चुनता है <commit>

ध्यान दें कि यह अभी तक कुछ भी नहीं करेगा (जैसा कि git cherry-pickकरता है)। इसलिए यदि आप ऐसा चाहते हैं, तो आपको करना होगा:

git add <path>
git commit
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.