git मर्ज: कोड में परिवर्तन लागू करें जो एक अलग फ़ाइल में चले गए


132

मैं अभी एक बहुत बीफ़ जीआईटी मर्ज पैंतरेबाज़ी का प्रयास कर रहा हूं। एक समस्या जो मुझे सामने आ रही है वह यह है कि मैंने अपनी शाखा में कुछ कोड में कुछ बदलाव किए, लेकिन मेरे सहयोगी ने उस कोड को अपनी शाखा में एक नई फ़ाइल में स्थानांतरित कर दिया। इसलिए जब मैंने किया git merge my_branch his_branch, git ने ध्यान नहीं दिया कि नई फ़ाइल में कोड पुराने जैसा ही था, और इसलिए मेरा कोई भी बदलाव नहीं है।

नई फ़ाइलों में कोड में फिर से मेरे परिवर्तनों को लागू करने के बारे में जाने का सबसे आसान तरीका क्या है। मुझे यह पता लगाने में बहुत अधिक समस्याएँ नहीं होंगी कि कौन से कमिट्स को फिर से लागू करने की आवश्यकता है (मैं बस उपयोग कर सकता हूं git log --stat)। लेकिन जहां तक ​​मैं बता सकता हूं, नई फ़ाइलों में परिवर्तन को फिर से लागू करने का कोई तरीका नहीं है। सबसे आसान चीज जो मैं अभी देख रहा हूं, वह मैन्युअल रूप से परिवर्तनों को फिर से लागू करना है, जो एक अच्छा विचार नहीं लग रहा है।

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


2
बिल्कुल वैसा ही नहीं, लेकिन यहाँ एक अच्छा सवाल है, जो लागू हो सकता है: stackoverflow.com/questions/2701790/…
Mariano Desanze

4
कोई भी उत्तर यह नहीं बताता है कि git अपने आप इस तरह मर्ज करने में सक्षम क्यों नहीं है। मुझे लगा कि नाम बदलने और स्वचालित रूप से उचित मर्ज करने के लिए पर्याप्त स्मार्ट होना चाहिए था?
जॉन

शायद यह सच नहीं था जब सवाल पूछा गया था, लेकिन git के आधुनिक संस्करण (मैं 1.9.5 का उपयोग कर रहा हूं) का नाम बदला और स्थानांतरित फ़ाइलों में मर्ज हो सकता है। यहां तक --rename-thresholdकि आवश्यक समानता की मात्रा को ट्विक करने का विकल्प भी है।
टोड ओवेन

1
@ToddOwen जो एक अपस्ट्रीम ब्रांच से वनीला रिबेस होने पर काम करेगा, लेकिन अगर आप चेरी पिकिंग या बैक-पोर्टिंग में बदलाव कर रहे हैं, तो आप मुसीबत में पड़ जाएंगे, जिसमें फ़ाइलों का नाम बदलना शामिल नहीं है। ।
ग्यूपैडपॉक

क्या यह समस्या पहले से ही थी यदि आप पहले एक रिबेस करते हैं?
13 अक्टूबर को hbogert

जवाबों:


132

मेरे पास एक समान मुद्दा था, और मैंने लक्ष्य फ़ाइल संगठन से मिलान करने के लिए अपने काम को फिर से शुरू करके इसे हल किया।

यह कहें कि आपने original.txtअपनी शाखा ( localशाखा) में संशोधन किया है , लेकिन मास्टर शाखा पर, original.txtएक दूसरे को कॉपी किया गया है, कहते हैं copy.txt। यह कॉपी एक कमिट में की गई है जिसे हम कमिट करते हैं CP

आप अपने सभी स्थानीय परिवर्तन, कमिट Aऔर Bनीचे लागू करना चाहते हैं, जो original.txtनई फ़ाइल पर किए गए थे copy.txt

 ---- X -----CP------ (master)
       \ 
        \--A---B--- (local)

moveअपने परिवर्तनों के शुरुआती बिंदु पर एक भगोड़ा शाखा बनाएँ git branch move X। कहने का तात्पर्य यह है कि moveब्रांच को कमिट करें X, कमिट करने से पहले जिसे आप मर्ज करना चाहते हैं; सबसे अधिक संभावना है, यह वह प्रतिबद्धता है जिससे आपने अपने परिवर्तनों को कार्यान्वित करने के लिए शाखाबद्ध किया है। जैसा कि उपयोगकर्ता @digory डू ने नीचे लिखा है, आप git merge-base master localखोजने के लिए कर सकते हैं X

 ---- X (move)-----CP----- (master)
       \ 
        \--A---B--- (local)

इस शाखा पर, निम्नलिखित नामकरण आदेश जारी करें:

git mv original.txt copy.txt

यह फ़ाइल का नाम बदल देता है। ध्यान दें कि copy.txtइस बिंदु पर अभी तक आपके पेड़ में मौजूद नहीं था।
अपना परिवर्तन करें (हम इस प्रतिबद्धता को नाम देते हैं MV)।

        /--MV (move)
       /
 ---- X -----CP----- (master)
       \ 
        \--A---B--- (local)

अब आप शीर्ष पर अपना काम फिर से कर सकते हैं move:

git rebase move local

यह समस्या के बिना काम करना चाहिए, और आपके परिवर्तन copy.txtआपकी स्थानीय शाखा में लागू होते हैं ।

        /--MV (move)---A'---B'--- (local)
       /
 ---- X -----CP----- (master)

अब, आपको MVअपनी मुख्य शाखा के इतिहास में आवश्यक रूप से नहीं होना चाहिए या नहीं करना चाहिए , क्योंकि इस कदम के संचालन CPसे मुख्य शाखा में कम से कम कॉपी ऑपरेशन के साथ संघर्ष हो सकता है ।

आपको केवल अपने काम को फिर से करना होगा, इस तरह के रूप में कदम संचालन को त्यागना होगा:

git rebase move local --onto CP

... CPवह प्रतिबद्ध कहाँ copy.txtहै जो दूसरी शाखा में पेश किया गया था। यह कमिट के copy.txtशीर्ष पर सभी परिवर्तनों को रद्द कर देता CPहै। अब, आपकी localशाखा बिल्कुल वैसी है जैसे कि आप हमेशा संशोधित होते हैं copy.txtऔर नहीं original.txt, और आप दूसरों के साथ विलय जारी रख सकते हैं।

                /--A''---B''-- (local)
               /
 -----X-------CP----- (master)

यह महत्वपूर्ण है कि परिवर्तन लागू होते हैं CPया अन्यथा copy.txtमौजूद नहीं होते हैं और परिवर्तन वापस लागू किए जाएंगे original.txt

आशा है कि यह स्पष्ट है। यह उत्तर देर से आता है, लेकिन यह किसी और के लिए उपयोगी हो सकता है।


2
यह बहुत काम है, लेकिन मुझे लगता है कि इसे सिद्धांत रूप में काम करना चाहिए। मुझे लगता है कि आपके पास रिबेज की तुलना में विलय के साथ बेहतर भाग्य होगा।
asmeurer

7
इस समाधान में केवल बेसिक गिट कमांड शामिल हैं, एडिटिंग पैच या उपयोग के विपरीत patch(जिसमें संभावित रूप से बहुत काम भी शामिल है), और इसीलिए मुझे लगा कि इसे दिखाना दिलचस्प हो सकता है। इसके अलावा, ध्यान दें कि मैंने वास्तव में परिवर्तनों को लागू करने की तुलना में उत्तर लिखने में अधिक समय लिया, मेरे मामले में। किस चरण में आप मर्ज का उपयोग करने की अनुशंसा करेंगे? और क्यों? एकमात्र अंतर जो मैं देख रहा हूं वह यह है कि रिबासिंग के द्वारा, मैं एक अस्थायी कमिट बनाता हूं जिसे बाद में छोड़ दिया जाता है (कमिट MV), जो केवल मर्ज के साथ संभव नहीं है।
coredump

1
रिबेस के साथ, आपके पास मर्ज टकराव से निपटने का एक उच्च मौका है, क्योंकि आप प्रत्येक प्रतिबद्ध के साथ सौदा करते हैं, जबकि एक मर्ज के साथ आप एक ही बार में सब कुछ के साथ सौदा करते हैं, जिसका अर्थ है कि रिबेस के साथ हुए कुछ बदलाव मर्ज के साथ कोई भी नहीं होगा। मैं क्या करूँगा मर्ज करना, फिर मैन्युअल रूप से मर्ज की गई फ़ाइल को स्थानांतरित करना। हो सकता है कि मैं आपके उत्तर के विचार को गलत समझूँ, हालाँकि।
asmeurer

4
मैंने दृष्टिकोण को स्पष्ट करने के लिए कुछ ASCII पेड़ जोड़े। उस मामले में मर्ज बनाम रिबेज के बारे में: मैं जो करना चाहता हूं, वह अपनी शाखा में 'ओरिजिनल.टेक्स्ट' पर सभी बदलावों को लेना है और उन्हें मास्टर ब्रांच में 'कॉपी.टेक्स्ट' पर लागू करना है, क्योंकि किसी कारण से, 'मूल। txt 'को कुछ बिंदु पर' copy.txt 'में कॉपी (और स्थानांतरित नहीं) किया गया था। उस प्रति के बाद, 'original.txt' भी मास्टर ब्रांच पर विकसित हो सकता है। यदि मैं सीधे विलीन हो जाता हूं, तो मूल.टैक्स पर मेरे स्थानीय परिवर्तन मास्टर शाखा में संशोधित मूल।टैक्स पर लागू हो जाएंगे, जो विलय करना मुश्किल होगा। सादर।
coredump

1
किसी भी तरह से, हालांकि, मेरा मानना ​​है कि यह समाधान काम करेगा (हालांकि मैं शुक्र है कि अभी इसमें प्रयास करने की स्थिति नहीं है), इसलिए अब मैं इसे उत्तर के रूप में चिह्नित करूंगा।
asmeurer

31

पैच को जेनरेट करने के लिए आप हमेशा git diff(या git format-patch) का उपयोग कर सकते हैं , फिर पैच में फाइलनाम को मैन्युअल रूप से संपादित करें, और इसे git apply(या git am) के साथ लागू करें ।

इसमें से कुछ ही, अपने आप काम करने का एकमात्र तरीका है यदि गिट के नाम का पता लगाने से यह पता लगाया जा सकता है कि पुरानी और नई फाइलें एक ही चीज हैं - जो यह लगता है कि वे वास्तव में आपके मामले में नहीं हैं, बस उनमें से एक हिस्सा है। यह सच है कि गिट ब्लब्स का उपयोग करता है, फाइलों का नहीं, लेकिन एक बूँद एक पूरी फाइल की सामग्री है, बिना फाइलनाम और मेटाडाटा के बिना। इसलिए यदि आपके पास दो फ़ाइलों के बीच स्थानांतरित कोड का एक हिस्सा है, तो वे वास्तव में एक ही बूँद नहीं हैं - बाकी बूँद की सामग्री अलग-अलग है, बस आम में हिस्सा है।


1
खैर, यह अब तक का सबसे अच्छा जवाब है। मैं समझ नहीं पा रहा था कि कैसे git format-patchकमिट के लिए काम किया जाए। अगर मैं करता हूँ git format-patch SHA1, यह पूरे इतिहास के लिए पैच फ़ाइलों का एक पूरा गुच्छा उत्पन्न करता है। लेकिन मुझे लगता है कि git show SHA1 > diff.patchअभी भी काम करेगा।
asmeurer

1
@asmeurer: -1विकल्प का उपयोग करें । प्रारूप-पैच के लिए ऑपरेशन का सामान्य मोड एक संशोधन रेंज है, जैसे origin/master..master, ताकि आप आसानी से एक पैच श्रृंखला तैयार कर सकें।
कास्केबेल

1
दरअसल, एक और नोट। git applyऔर git amबहुत picky हैं, क्योंकि वे एक ही पंक्ति संख्या चाहते हैं। लेकिन मैं इस समय UNIX patchकमांड के साथ सफल हो रहा हूं ।
asmeurer

24

यहाँ एक है मर्ज का नाम बदलें और संपादित साथ मर्ज संघर्ष का सामना और mergetool सही 3 मर्ज स्रोत फ़ाइलों को पहचानने के साथ इसे हल करने का समाधान।

  • मर्ज के बाद 'हटाई गई फ़ाइल' के कारण विफल हो जाता है जिसे आपको पता चला कि उसका नाम बदल दिया गया और संपादित किया गया:

    1. आप मर्ज को निरस्त करें।
    2. अपनी शाखा पर पुनर्नामित फ़ाइलें।
    3. और फिर से विलीन हो जाते हैं।

वाल्क-के माध्यम से:

एक फ़ाइल बनाएँ।

$ git init
Initialized empty Git repository in /tmp/git-rename-and-modify-test/.git/

$ echo "A file." > file.txt
$ git add file.txt
$ git commit -am "file.txt added."
[master (root-commit) 401b10d] file.txt added.
 1 file changed, 1 insertion(+)
 create mode 100644 file.txt

एक शाखा बनाएँ जहाँ आप बाद में संपादित करेंगे:

$ git branch branch-with-edits
Branch branch-with-edits set up to track local branch master.

नाम बनाएँ और मास्टर पर संपादित करें:

$ git mv file.txt renamed-and-edited.txt
$ echo "edits on master" >> renamed-and-edited.txt 
$ git commit -am "file.txt + edits -> renamed-and-edited.txt."
[master def790f] file.txt + edits -> renamed-and-edited.txt.
 2 files changed, 2 insertions(+), 1 deletion(-)
 delete mode 100644 file.txt
 create mode 100644 renamed-and-edited.txt

शाखा में स्वैप करें, और वहां भी संपादित करें:

$ git checkout branch-with-edits 
Switched to branch 'branch-with-edits'
Your branch is behind 'master' by 1 commit, and can be fast-forwarded.
  (use "git pull" to update your local branch)
$ 
$ echo "edits on branch" >> file.txt 
$ git commit -am "file.txt edited on branch."
[branch-with-edits 2c4760e] file.txt edited on branch.
 1 file changed, 1 insertion(+)

गुरु को मिलाने का प्रयास:

$ git merge master
CONFLICT (modify/delete): file.txt deleted in master and modified in HEAD. Version HEAD of file.txt left in tree.
Automatic merge failed; fix conflicts and then commit the result.

ध्यान दें कि संघर्ष को हल करना मुश्किल है - और फ़ाइलों का नाम बदल दिया गया था। गर्भपात, नाम बदलने की नकल करें:

$ git merge --abort
$ git mv file.txt renamed-and-edited.txt
$ git commit -am "Preparing for merge; Human noticed renames files were edited."
[branch-with-edits ca506da] Preparing for merge; Human noticed renames files were edited.
 1 file changed, 0 insertions(+), 0 deletions(-)
 rename file.txt => renamed-and-edited.txt (100%)

पुन: मर्ज करने का प्रयास करें:

$ git merge master
Auto-merging renamed-and-edited.txt
CONFLICT (add/add): Merge conflict in renamed-and-edited.txt
Recorded preimage for 'renamed-and-edited.txt'
Automatic merge failed; fix conflicts and then commit the result.

महान! विलय के साथ हल किए जा सकने वाले 'सामान्य' संघर्ष में परिणाम मिलाएं:

$ git mergetool
Merging:
renamed-and-edited.txt

Normal merge conflict for 'renamed-and-edited.txt':
  {local}: created file
  {remote}: created file
$ git commit 
Recorded resolution for 'renamed-and-edited.txt'.
[branch-with-edits 2264483] Merge branch 'master' into branch-with-edits

दिलचस्प। मुझे अगली बार इस मुद्दे पर आने की कोशिश करनी होगी।
asmeurer

1
उस समाधान में, यह मुझे लगता है कि पहला कदम, मर्ज जो निरस्त है, केवल यह पता लगाने के लिए उपयोगी है कि कौन सी फाइलें नामांकित थीं और दूरस्थ रूप से संपादित की गई थीं। यदि आप उन्हें पहले से जानते हैं। आप उस चरण को छोड़ सकते हैं और, मूल रूप से, समाधान केवल स्थानीय रूप से फ़ाइलों का नाम बदलने और फिर विलय करने और हमेशा की तरह हल करने के लिए है।

1
धन्यवाद। मेरी स्थिति यह थी कि मैं स्थानांतरित हुआ B.txt -> C.txtऔर A.txt -> B.txtgit mv के साथ, और git अपने आप मर्ज संघर्षों को सही ढंग से मेल नहीं कर सका (पुराने B.txtऔर नए के बीच मर्ज टकराव हो रहा था B.txt)। इस विधि का उपयोग करके, मर्ज विरोध अब सही फ़ाइलों के बीच हैं।
cib

यह केवल तभी काम करता है जब पूरी फ़ाइल को स्थानांतरित कर दिया जाता है, लेकिन गिट को आम तौर पर स्वचालित रूप से उस स्थिति का पता लगाना चाहिए। मुश्किल स्थिति तब होती है जब किसी फ़ाइल का केवल एक भाग ले जाया जाता है।
रॉबिन ग्रीन
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.