एक अलग अभिभावक के लिए git पैरेंट पॉइंटर सेट करना


220

अगर मेरे पास अतीत में एक कमिट है जो एक माता-पिता को इंगित करता है, लेकिन मैं उस माता-पिता को बदलना चाहता हूं जो इसे इंगित करता है, तो मैं ऐसा कैसे करूंगा?

जवाबों:


404

का उपयोग कर git rebase। यह सामान्य "ले कमिट (s) है और इसे एक अलग पैरेंट (बेस)" Git में कमांड / प्लेप करें।

हालांकि, कुछ बातें जानना:

  1. चूंकि SH SHA में उनके माता-पिता शामिल होते हैं, जब आप किसी दिए गए प्रतिबद्ध के माता-पिता को बदल देते हैं, तो उसका SHA बदल जाएगा - जैसा कि सभी एसएचएएस करता है जो उसके बाद आने वाले (इससे अधिक हाल के) विकास की लाइन में है।

  2. यदि आप अन्य लोगों के साथ काम कर रहे हैं, और आपने पहले ही प्रश्न को सार्वजनिक करने के लिए प्रतिबद्ध कर दिया है, जहाँ उन्होंने इसे खींच लिया है, तो कमिट को संशोधित करना शायद एक बुरा विचार है। ™ यह # 1 के कारण है, और इस प्रकार परिणामी भ्रम अन्य उपयोगकर्ताओं के रिपॉजिटरी का सामना करेंगे, जब यह पता लगाने की कोशिश की जाएगी कि आपके एसएचएएस के कारण क्या हुआ, "उसी" के लिए उनके मेल नहीं खाते। (विवरण के लिए लिंक किए गए मैन पेज का "उत्तर प्रदेश से प्राप्त करें" अनुभाग देखें।)

उस ने कहा, यदि आप वर्तमान में कुछ कमिटमेंट वाली शाखा में हैं, जिसे आप एक नए अभिभावक के पास ले जाना चाहते हैं, तो यह कुछ इस तरह दिखाई देगा:

git rebase --onto <new-parent> <old-parent>

इसके बजाय मौजूदा शाखा के बाद सब कुछ स्थानांतरित हो जाएगा ।<old-parent><new-parent>


मैं माता-पिता को बदलने के लिए git rebase --onto का उपयोग कर रहा हूं, लेकिन ऐसा लगता है कि मैं केवल एक ही प्रतिबद्धता के साथ समाप्त करता हूं। मैंने इस पर एक सवाल रखा: stackoverflow.com/questions/19181665/…
एडुआर्डो मेलो

7
अहा, तो यह है कि क्या के --ontoलिए प्रयोग किया जाता है! प्रलेखन हमेशा मेरे लिए पूरी तरह से अस्पष्ट रहा है, इस उत्तर को पढ़ने के बाद भी !
डैनियल सी। सोबरल

6
यह पंक्ति: git rebase --onto <new-parent> <old-parent>मुझे इस बारे में सब कुछ बताया rebase --ontoकि कैसे उपयोग किया जाता है जो मेरे द्वारा पढ़ा गया प्रत्येक प्रश्न और दस्तावेज़ अब तक विफल रहा है।
jpmc26

3
मेरे लिए इस कमांड को पढ़ना चाहिए: rebase this commit on that oneया git rebase --on <new-parent> <commit>। यहां पुराने माता-पिता का उपयोग करने से मुझे कोई मतलब नहीं है।
समीर अगुइर

1
@kopranb बूढ़े माता-पिता के लिए महत्वपूर्ण है जब आप अपने सिर से दूरस्थ सिर तक के कुछ मार्ग को त्यागना चाहते हैं। आप जिस केस का वर्णन कर रहे हैं वह उसके द्वारा नियंत्रित किया जाता है git rebase <new-parent>
क्लैके

35

यदि यह पता चलता है कि आपको बाद में आने वाले कमिटिंग से बचने की आवश्यकता है (जैसे कि एक इतिहास फिर से लिखना अस्थिर होगा), तो आप गिट रिप्लेसमेंट (जीआईटी 1.6.5 और बाद में उपलब्ध ) का उपयोग कर सकते हैं ।

# …---o---A---o---o---…
#
# …---o---B---b---b---…
#
# We want to transplant B to be "on top of" A.
# The tree of descendants from B (and A) can be arbitrarily complex.

replace_first_parent() {
    old_parent=$(git rev-parse --verify "${1}^1") || return 1
    new_parent=$(git rev-parse --verify "${2}^0") || return 2
    new_commit=$(
      git cat-file commit "$1" |
      sed -e '1,/^$/s/^parent '"$old_parent"'$/parent '"$new_parent"'/' |
      git hash-object -t commit -w --stdin
    ) || return 3
    git replace "$1" "$new_commit"
}
replace_first_parent B A

# …---o---A---o---o---…
#          \
#           C---b---b---…
#
# C is the replacement for B.

उपरोक्त प्रतिस्थापन के साथ, ऑब्जेक्ट B के लिए कोई भी अनुरोध वास्तव में ऑब्जेक्ट C को वापस कर देगा। C की सामग्री बिलकुल वैसी ही है, जैसे B की सामग्री पहले माता-पिता (एक ही माता-पिता को छोड़कर), एक ही पेड़, वही प्रतिबद्ध संदेश)।

प्रतिस्थापन डिफ़ॉल्ट रूप से सक्रिय होते हैं, लेकिन --no-replace-objectsविकल्प को git (कमांड नाम से पहले) का उपयोग करके या GIT_NO_REPLACE_OBJECTSपर्यावरण चर सेट करके चालू किया जा सकता है । प्रतिस्थापन refs/replace/*(सामान्य के अलावा refs/heads/*) को धक्का देकर साझा किया जा सकता है ।

आप पसंद नहीं है के लिए प्रतिबद्ध-munging (के साथ किया sed ऊपर), तो आप उच्च स्तर आदेशों का उपयोग करते हैं तो आपका प्रतिस्थापन बना सकते हैं:

git checkout B~0
git reset --soft A
git commit -C B
git replace B HEAD
git checkout -

बड़ा अंतर यह है कि यह अनुक्रम अतिरिक्त माता-पिता का प्रचार नहीं करता है यदि बी एक मर्ज कमिट है।


और फिर, आप मौजूदा रिपॉजिटरी में बदलावों को कैसे आगे बढ़ाएंगे? ऐसा लगता है कि स्थानीय रूप से काम करने के लिए, लेकिन उसके बाद कुछ भी धक्का नहीं देता
बैपटिस्ट विच

2
@ बैप्टिस्टविच: प्रतिस्थापन को रेफ के एक अलग सेट में संग्रहीत किया जाता है। आपको refs/replace/रेफ पदानुक्रम को पुश (और लाने) की व्यवस्था करने की आवश्यकता होगी । यदि आपको इसे एक बार करने की आवश्यकता है, तो आप git push yourremote 'refs/replace/*'स्रोत भंडार में, और git fetch yourremote 'refs/replace/*:refs/replace/*'गंतव्य रिपॉजिटरी में कर सकते हैं। यदि आपको इसे कई बार करने की आवश्यकता है, तो आप उन रिफ़ेक्सेस को एक remote.yourremote.pushकॉन्फ़िगर चर (स्रोत रिपॉजिटरी में) और एक remote.yourremote.fetchकॉन्फ़िगर चर (गंतव्य रिपॉजिटरी में) जोड़ सकते हैं।
क्रिस जॉन्सन

3
ऐसा करने का एक सरल तरीका उपयोग करना है git replace --grafts। निश्चित नहीं है कि यह कब जोड़ा गया था, लेकिन इसका पूरा उद्देश्य एक प्रतिस्थापन बनाना है जो प्रतिबद्ध बी के समान है लेकिन निर्दिष्ट माता-पिता के साथ है।
पॉल वैगलैंड

32

ध्यान दें कि Git में एक कमिट को बदलने के लिए यह आवश्यक है कि सभी कमेंट जो इसका पालन करते हैं उन्हें बदलना होगा। यदि आपने इतिहास के इस भाग को प्रकाशित किया है, तो यह हतोत्साहित किया जाता है, और हो सकता है कि किसी ने इतिहास पर अपने काम का निर्माण किया हो।

एम्बर की प्रतिक्रियाgit rebase में उल्लिखित वैकल्पिक समाधान का उपयोग करने के लिए ग्राफ्ट मैकेनिज्म का उपयोग करना है (देखें Git Glossary में Git Graft की परिभाषा और Git Repository Layout दस्तावेज़ीकरण में फ़ाइल के प्रलेखन), एक प्रतिबद्ध माता-पिता को बदलने के लिए, यह देखें कि उसने कुछ इतिहास दर्शक के लिए सही काम किया है ( , इत्यादि) और फिर इसे स्थायी बनाने के लिए (और इसके उदाहरण के "उदाहरण" खंड में वर्णित है ) का उपयोग करें (और फिर ग्राफ्ट को हटा दें, और वैकल्पिक रूप से उसके द्वारा समर्थित मूल रीफ़्रेश को हटा दें , या रिपॉजिटरी को पुनः प्राप्त करें):.git/info/graftsgitkgit log --graphgit filter-branchgit filter-branch

इको "$ कमिट-आईडी $ ग्राफ्ट-आईडी" >> .it / जानकारी / ग्राफ्ट
git फ़िल्टर-शाखा $ graft-id..HEAD

ध्यान दें !!! यह समाधान रिबेस के समाधान से अलग है , जिसमें परिवर्तनgit rebase / ट्रांसप्लांट में बदलाव होगा , जबकि ग्राफ्ट-आधारित समाधान बस उतना ही प्रभावी होगा , जो पुराने माता-पिता और नए माता-पिता के बीच अंतर को ध्यान में नहीं रखता है!


3
जो मैं समझता हूं ( git.wiki.kernel.org/index.php/GraftPoint से ), git replaceने git graft को अधिगृहीत कर लिया है (यह मानते हुए कि आपके पास 1.6.5 या बाद का git है)।
अलेक्जेंडर बर्ड

4
@ Thr4wn: बड़े पैमाने पर सच है, लेकिन अगर आप इतिहास को फिर से लिखना चाहते हैं तो ग्राफ्ट्स -गिट-फिल्टर-ब्रांच (या इंटरएक्टिव रीबेस, या फास्ट-एक्सपोर्ट + जैसे रिपोसर्जन) इसे करने का तरीका है। यदि आप इतिहास को संरक्षित करना चाहते हैं / चाहते हैं , तो git-प्रतिस्थापित graft से कहीं बेहतर है।
जैकब नारबस्की

@ JakubNarębski, आप आप से क्या मतलब समझा सकता है फिर से लिखने बनाम की रक्षा इतिहास? मैं git-replace+ का उपयोग क्यों नहीं कर सकता git-filter-branch? मेरे परीक्षण में, filter-branchप्रतिस्थापन का सम्मान करने के लिए लग रहा था, पूरे पेड़ को पुन: पेश कर रहा था।
cdunn2001

1
@ cdunn2001: git filter-branchइतिहास को फिर से लिखता है, ग्राफ्ट्स और प्रतिस्थापनों का सम्मान करता है (लेकिन फिर से लिखे गए इतिहास प्रतिस्थापनों में बदलाव किया जाएगा); धक्का गैर fasf- आगे परिवर्तन में परिणाम होगा। git replaceस्थानान्तरण योग्य स्थानापन्न प्रतिस्थापन बनाता है; धक्का तेजी से आगे होगा, लेकिन आपको refs/replaceइतिहास को सही करने के लिए प्रतिस्थापन को स्थानांतरित करने के लिए धक्का देना होगा । HTH
जैकब नरHसकी

23

उपरोक्त उत्तरों को स्पष्ट करने के लिए और बेशर्मी से मेरी अपनी स्क्रिप्ट को प्लग करें:

यह इस बात पर निर्भर करता है कि आप "रिबेस" करना चाहते हैं या "रीपेरेंट"। एक रिबेस , के रूप में द्वारा सुझाए गए एम्बर , चारों ओर घूमती है डिफ । एक reparent , के रूप में द्वारा सुझाए गए जेकब और क्रिस , चारों ओर घूमती है स्नैपशॉट पूरे वृक्ष की। यदि आप पीछे हटना चाहते हैं, तो मैं git reparentकाम को मैन्युअल रूप से करने के बजाय उपयोग करने का सुझाव देता हूं ।

तुलना

मान लीजिए कि आपके पास बाईं ओर की तस्वीर है और आप चाहते हैं कि वह दाईं ओर की तस्वीर की तरह दिखे:

                   C'
                  /
A---B---C        A---B---C

रिबासिंग और रिप्रेंटिंग दोनों एक ही तस्वीर का उत्पादन करेंगे, लेकिन C'अलग-अलग की परिभाषा । इसके साथ git rebase --onto A B, C'इसमें कोई परिवर्तन नहीं होगा B। के साथ git reparent -p A, C'समान होगा C(सिवाय इसके कि Bइतिहास में नहीं होगा)।


1
+1 मैंने reparentस्क्रिप्ट के साथ प्रयोग किया है ; यह खूबसूरती से काम करता है; अत्यधिक की सिफारिश की । मैं एक नया branchलेबल बनाने की भी सिफारिश करूंगा , और checkoutकॉल करने से पहले उस शाखा में (जैसा कि शाखा लेबल हट जाएगा)।
रोडबर्ब

यह भी ध्यान देने योग्य है कि: (1) मूल नोड बरकरार है, और (2) नया नोड मूल नोड के बच्चों को विरासत में नहीं देता है।
ररबबार

0

निश्चित रूप से @ जकूब के जवाब से मुझे कुछ घंटे पहले मदद मिली जब मैं ओपी के समान सटीक कोशिश कर रहा था।
हालांकि, git replace --graftअब ग्राफ्ट से संबंधित आसान उपाय है। इसके अलावा, उस समाधान के साथ एक बड़ी समस्या यह थी कि फ़िल्टर-शाखा ने मुझे हर उस शाखा को ढीला कर दिया था जिसे HEAD की शाखा में विलय नहीं किया गया था। फिर, git filter-repoपूरी तरह से और निर्दोष रूप से काम किया।

$ git replace --graft <commit> <new parent commit>
$ git filter-repo --force

अधिक जानकारी के लिए: डॉक्स में "री-ग्राफ्टिंग हिस्ट्री" सेक्शन को चेकआउट करें

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