वास्तव में git का "rebase --preserve-merges" क्या करता है (और क्यों?)


355

कमांड के लिएrebase Git का दस्तावेज काफी संक्षिप्त है:

--preserve-merges
    Instead of ignoring merges, try to recreate them.

This uses the --interactive machinery internally, but combining it
with the --interactive option explicitly is generally not a good idea
unless you know what you are doing (see BUGS below).

तो जब आप उपयोग करते हैं तो वास्तव में क्या होता है --preserve-merges? यह डिफ़ॉल्ट व्यवहार (उस ध्वज के बिना) से कैसे भिन्न होता है? एक "मर्ज" को फिर से बनाने के लिए इसका क्या मतलब है, आदि।


20
चेतावनी: Git 2.18 (Q2 2018, 5 साल बाद) के साथ शुरू, git --rebase-mergesअंततः पुराने को बदल देगा git --preserve-merges। देखें नीचे मेरा उत्तर
VonC

जवाबों:


464

एक सामान्य git रिबास की तरह, git --preserve-mergesपहले कमिट ग्राफ के एक हिस्से में किए गए कमिट्स की एक सूची की पहचान करता है, और फिर उन कॉमिट्स को दूसरे हिस्से के ऊपर रीप्ले करता है। --preserve-mergesचिंता के साथ मतभेद जो पुनरावृत्ति के लिए चुने जाते हैं और मर्जिंग के लिए पुनरावृत्ति कैसे काम करता है।

सामान्य और मर्ज-संरक्षण छूट के बीच मुख्य अंतर के बारे में अधिक स्पष्ट होने के लिए:

  • मर्ज-प्रोटेक्टिंग रिबास फिर से खेलना (कुछ) मर्ज कमिट्स के लिए तैयार है, जबकि सामान्य रीबेज मर्ज कमिट्स को पूरी तरह से नजरअंदाज कर देता है।
  • क्योंकि यह मर्ज करने के लिए फिर से खेलना करने के लिए तैयार है, मर्ज-संरक्षण रिबेज को परिभाषित करना है कि मर्ज कमेटी को फिर से खेलना, और कुछ अतिरिक्त झुर्रियों से निपटना है।
    • सबसे दिलचस्प हिस्सा, वैचारिक रूप से, शायद यह चुनने में है कि नई प्रतिबद्ध माता-पिता को क्या होना चाहिए।
    • मर्ज किए गए कमिट्स को रिप्लाई करने के लिए भी विशेष रूप से कमिट्स ( git checkout <desired first parent>) की स्पष्ट रूप से जांच करने की आवश्यकता होती है , जबकि सामान्य रीबेस को इसके बारे में चिंता करने की आवश्यकता नहीं होती है।
  • मर्ज-संरक्षण रिबेज़ पुनरावृत्ति के लिए कमिट का एक शुद्ध सेट मानता है:
    • विशेष रूप से, यह केवल सबसे हाल के मर्ज बेस (s) के बाद से किए गए कमिटमेंट पर ही विचार करेगा - यानी सबसे हालिया समय में दो शाखाओं को बदल दिया गया -, जबकि सामान्य रीबेस में पहली बार दो शाखाओं के डायट हो जाने के बाद वापस जाना शुरू हो सकता है ।
    • अनंतिम और अस्पष्ट होने के लिए, मेरा मानना ​​है कि यह "पुराने कमिट्स" को फिर से प्रदर्शित करने का एक साधन है जो पहले से ही "मर्ज कमिट" में "शामिल" हो चुका है।

पहले मैं "पर्याप्त रूप से ठीक" वर्णन करने की कोशिश करूंगा कि रिबास क्या --preserve-mergesकरता है, और फिर कुछ उदाहरण होंगे। उदाहरणों के साथ शुरू हो सकता है, अगर यह अधिक उपयोगी लगता है।

"संक्षिप्त" में एल्गोरिथ्म

यदि आप वास्तव में मातम में जाना चाहते हैं, तो गिट स्रोत डाउनलोड करें और फ़ाइल का अन्वेषण करें git-rebase--interactive.sh। (रीबेस गैट के सी कोर का हिस्सा नहीं है, बल्कि इसे बैश में लिखा गया है। और, पर्दे के पीछे, यह "इंटरैक्टिव रिबेस" के साथ कोड साझा करता है।)

लेकिन यहाँ मुझे लगता है कि मुझे लगता है कि इसका सार क्या होगा। सोचने के लिए चीजों की संख्या कम करने के लिए, मैंने कुछ स्वतंत्रताएं ली हैं। (उदाहरण के लिए मैं 100% सटीकता के साथ सटीक क्रम पर कब्जा करने की कोशिश नहीं करता हूं, जिसमें गणनाएँ होती हैं, और कुछ कम केंद्रीय-प्रतीत होने वाले विषयों को अनदेखा करते हैं, जैसे कि उन कमिटों के बारे में क्या करना है जो पहले से ही शाखाओं के बीच चेरी-पिक किए गए हैं)।

सबसे पहले, ध्यान दें कि एक गैर-मर्ज-संरक्षण रिबेज सरल नहीं है। यह कम या ज्यादा है:

Find all commits on B but not on A ("git log A..B")
Reset B to A ("git reset --hard A") 
Replay all those commits onto B one at a time in order.

Rebase --preserve-mergesतुलनात्मक रूप से जटिल है। यहां मैं उतना ही सरल हूं जितना कि मैं उन चीजों को खोए बिना बना सकता हूं जो बहुत महत्वपूर्ण लगती हैं:

Find the commits to replay:
  First find the merge-base(s) of A and B (i.e. the most recent common ancestor(s))
    This (these) merge base(s) will serve as a root/boundary for the rebase.
    In particular, we'll take its (their) descendants and replay them on top of new parents
  Now we can define C, the set of commits to replay. In particular, it's those commits:
    1) reachable from B but not A (as in a normal rebase), and ALSO
    2) descendants of the merge base(s)
  If we ignore cherry-picks and other cleverness preserve-merges does, it's more or less:
    git log A..B --not $(git merge-base --all A B)
Replay the commits:
  Create a branch B_new, on which to replay our commits.
  Switch to B_new (i.e. "git checkout B_new")
  Proceeding parents-before-children (--topo-order), replay each commit c in C on top of B_new:
    If it's a non-merge commit, cherry-pick as usual (i.e. "git cherry-pick c")
    Otherwise it's a merge commit, and we'll construct an "equivalent" merge commit c':
      To create a merge commit, its parents must exist and we must know what they are.
      So first, figure out which parents to use for c', by reference to the parents of c:
        For each parent p_i in parents_of(c):
          If p_i is one of the merge bases mentioned above:
            # p_i is one of the "boundary commits" that we no longer want to use as parents
            For the new commit's ith parent (p_i'), use the HEAD of B_new.
          Else if p_i is one of the commits being rewritten (i.e. if p_i is in R):
            # Note: Because we're moving parents-before-children, a rewritten version
            # of p_i must already exist. So reuse it:
            For the new commit's ith parent (p_i'), use the rewritten version of p_i.
          Otherwise:
            # p_i is one of the commits that's *not* slated for rewrite. So don't rewrite it
            For the new commit's ith parent (p_i'), use p_i, i.e. the old commit's ith parent.
      Second, actually create the new commit c':
        Go to p_1'. (i.e. "git checkout p_1'", p_1' being the "first parent" we want for our new commit)
        Merge in the other parent(s):
          For a typical two-parent merge, it's just "git merge p_2'".
          For an octopus merge, it's "git merge p_2' p_3' p_4' ...".
        Switch (i.e. "git reset") B_new to the current commit (i.e. HEAD), if it's not already there
  Change the label B to apply to this new branch, rather than the old one. (i.e. "git reset --hard B")

एक --onto Cतर्क के साथ रिबेस बहुत समान होना चाहिए। B के HEAD में कमिट प्लेबैक शुरू करने के बजाय, आप C के HEAD पर प्लेबैक करना शुरू करते हैं। (और B_new के बजाय C_new का उपयोग करें।)

उदाहरण 1

उदाहरण के लिए, कमिट ग्राफ लें

  B---C <-- master
 /                     
A-------D------E----m----H <-- topic
         \         /
          F-------G

m माता-पिता E और G के साथ एक मर्ज कमिट है।

मान लीजिए कि हमने एक सामान्य, गैर-मर्ज-संरक्षण छूट का उपयोग करके मास्टर (सी) के ऊपर विषय (एच) को विद्रोह कर दिया। (उदाहरण के लिए, चेकआउट विषय; रिबेट मास्टर ।) उस स्थिति में, गिट पुनरावृत्ति के लिए निम्नलिखित कमिटों का चयन करेगा:

  • डी चुनें
  • ई उठाओ
  • एफ चुनें
  • जी चुनें
  • एच उठाओ

और फिर कमिट ग्राफ़ को इस तरह अपडेट करें:

  B---C <-- master
 /     \                
A       D'---E'---F'---G'---H' <-- topic

(D ', D, आदि की पुनरावृत्ति के समतुल्य है)

ध्यान दें कि मर्ज कम मी को रीप्ले के लिए नहीं चुना गया है।

अगर हम इसके बजाय --preserve-mergesसी के शीर्ष पर एच का एक रिबेट करते हैं (उदाहरण के लिए, चेकआउट विषय; रिबेस - लेफ्ट-मर्ज मास्टर ।) इस नए मामले में, गिट पुनरावृत्ति के लिए निम्नलिखित कमिटों का चयन करेंगे।

  • डी चुनें
  • ई उठाओ
  • एफ चुनें ('उप' शाखा में डी पर)
  • जी ('उप' शाखा में एफ पर)
  • मर्ज शाखा को 'सबटॉपिक' विषय में चुनें
  • एच उठाओ

अब मी को रिप्ले के लिए चुना गया । यह भी ध्यान दें कि मर्ज करने वाले माता-पिता ई और जी को मर्ज कमिट मी से पहले शामिल करने के लिए चुना गया था।

यहाँ परिणामी प्रतिबद्ध ग्राफ़ है:

 B---C <-- master
/     \                
A      D'-----E'----m'----H' <-- topic
        \          / 
         F'-------G'

फिर, डी 'डी-सेम फॉर ई के लिए एक चेरी-पिक (यानी फिर से बनाया गया) संस्करण है, आदि .. मास्टर पर हर कमिट रिप्ले नहीं किया गया है। E और G (m के मर्ज माता-पिता) दोनों को M के माता-पिता के रूप में सेवा करने के लिए E 'और G' के रूप में फिर से बनाया गया है (रिबेस के बाद, पेड़ का इतिहास अभी भी वही है)।

उदाहरण 2

सामान्य रिबेस के विपरीत, मर्ज-प्रोटेक्टिंग रिबेज, अपस्ट्रीम हेड के कई बच्चे पैदा कर सकता है।

उदाहरण के लिए, विचार करें:

  B---C <-- master
 /                     
A-------D------E---m----H <-- topic
 \                 |
  ------- F-----G--/ 

यदि हम C (मास्टर) के शीर्ष पर H (विषय) को रिबेट करते हैं, तो रिबेस के लिए चुने गए कमेंट्स हैं:

  • डी चुनें
  • ई उठाओ
  • एफ चुनें
  • जी चुनें
  • एम उठाओ
  • एच उठाओ

और परिणाम ऐसा है:

  B---C  <-- master
 /    | \                
A     |  D'----E'---m'----H' <-- topic
       \            |
         F'----G'---/

उदाहरण 3

उपर्युक्त उदाहरणों में, मूल मर्ज किए गए मूल माता-पिता के बजाय, दोनों मर्ज कमिट और उसके दो माता-पिता फिर से शुरू होते हैं। हालांकि, अन्य विद्रोहियों में एक पुनरावृत्त मर्ज कमिटमेंट उन माता-पिता के साथ समाप्त हो सकता है जो मर्ज से पहले ही कमिट ग्राफ में थे।

उदाहरण के लिए, विचार करें:

  B--C---D <-- master
 /    \                
A---E--m------F <-- topic

यदि हम मास्टर (मर्ज को संरक्षित करते हुए) पर विषय को रीबेस करते हैं, तो रीप्ले के लिए कमिट होगा

  • मर्ज कमिट मी
  • एफ चुनें

फिर से लिखा गया ग्राफ़ ऐसा दिखेगा:

                     B--C--D <-- master
                    /       \             
                   A-----E---m'--F'; <-- topic

यहाँ फिर से मर्ज किए गए मर्ज कमिट मी 'को माता-पिता मिलते हैं, जो डी ग्राफ़ (मास्टर के हेड) और ई (मूल मर्ज कमिट मी के माता-पिता में से एक) के रूप में पहले से मौजूद थे।

उदाहरण 4

मर्ज-संरक्षण रिबेज कुछ "खाली प्रतिबद्ध" मामलों में भ्रमित हो सकता है। कम से कम यह git के कुछ पुराने संस्करणों (जैसे 1.7.8) के लिए सही है।

यह कमिट ग्राफ लें:

                   A--------B-----C-----m2---D <-- master
                    \        \         /
                      E--- F--\--G----/
                            \  \
                             ---m1--H <--topic

ध्यान दें कि दोनों एम 1 और एम 2 दोनों को बी और एफ से सभी परिवर्तनों को शामिल करना चाहिए।

यदि हम git rebase --preserve-mergesD (गुरु) पर H (विषय) करने की कोशिश करते हैं, तो निम्नलिखित रीप्ले रिप्ले के लिए चुने जाते हैं:

  • एम 1 चुनें
  • एच उठाओ

ध्यान दें कि m1 में परिवर्तन (B, F) को पहले से ही D. में शामिल किया जाना चाहिए (उन परिवर्तनों को पहले से ही A2 में शामिल कर लिया जाना चाहिए, क्योंकि m2, B और F के बच्चों को एक साथ मिला देता है।) इसलिए, अवधारणात्मक रूप से, m1 को शीर्ष पर पुन: जोड़ना चाहिए। डी शायद या तो एक न-ऑप होना चाहिए या एक खाली प्रतिबद्ध बनाना चाहिए (यानी जहां एक के बाद एक लगातार संशोधन खाली हैं)।

इसके बजाय, git, D के शीर्ष पर m1 को पुन: चलाने के प्रयास को अस्वीकार कर सकता है। आप इस तरह एक त्रुटि प्राप्त कर सकते हैं:

error: Commit 90caf85 is a merge but no -m option was given.
fatal: cherry-pick failed

ऐसा लगता है कि कोई व्यक्ति एक झंडे को पारित करने के लिए भूल गया, लेकिन अंतर्निहित समस्या यह है कि खाली अव्यवस्था बनाने वाले नापसंद।


6
मैंने देखा है कि git rebase --preserve-mergesहै बहुत धीमी एक से rebaseबिना --preserve-merges। क्या यह सही प्रभाव खोजने का एक दुष्परिणाम है? वहाँ कुछ भी करने के लिए इसे गति कर सकता है? (वैसे ... बहुत विस्तृत उत्तर के लिए धन्यवाद!)
डेविड एलन हजेले

7
ऐसा लगता है कि आपको हमेशा उपयोग करना चाहिए - अक्षर-मर्ज। अन्यथा इतिहास खोने की संभावना है यानी मर्ज कमिट होता है।
डार्वार

19
@DarVar आप हमेशा एक रिबेस पर इतिहास को ढीला करते हैं, क्योंकि आप दावा करते हैं कि जहां एक अलग कोडबेस में बदलाव होता है, वे वास्तव में जहां वे हैं।
19

5
क्या यह अभी भी एक "अनंतिम जवाब" है?
एंड्रयू ग्रिम

5
@ निश्चित रूप से आप सही कह रहे हैं, कि रिबासिंग हमेशा इतिहास को खोना शामिल करता है, लेकिन शायद डार्वार इस तथ्य से अवगत था, कि आप न केवल इतिहास को ढीला करते हैं, बल्कि कोड आधार में भी बदलाव करते हैं। संघर्ष रिज़ॉल्यूशन में वह जानकारी होती है जो सभी संभावित तरीकों से खो जाती है एक रिबेस हो सकती है। आपको हमेशा इसे फिर से करना होगा। क्या वास्तव में कोई रास्ता नहीं है, अपने संघर्ष के संकल्प को फिर से लाने के लिए? क्यों चेरी-मर्ज को कमिट नहीं कर सकते?
Nils_M

94

Git 2.18 (Q2 2018) --preserve-mergeएक नया विकल्प जोड़कर विकल्प में काफी सुधार करेगा ।

" git rebase" सीखा " --rebase-merges" प्रतिबद्ध ग्राफ के पूरे टोपोलॉजी को कहीं और प्रत्यारोपण करने के लिए

(नोट: Git 2.22, Q2 2019, वास्तव में पदावनत --preserve-merge , और Git 2.25, Q1 2020, इसे " git rebase --help" आउटपुट में विज्ञापन देना बंद कर देता है )

देखें 25cff9f प्रतिबद्ध , 7543f6f प्रतिबद्ध , 1131ec9 प्रतिबद्ध , 7ccdf65 प्रतिबद्ध , 537e7d6 प्रतिबद्ध , a9be29c प्रतिबद्ध , 8f6aed7 प्रतिबद्ध , 1644c73 प्रतिबद्ध , d1e8b01 प्रतिबद्ध , 4c68e7d प्रतिबद्ध , 9055e40 प्रतिबद्ध , cb5206e प्रतिबद्ध , a01c2a5 प्रतिबद्ध , 2f6b1d1 प्रतिबद्ध , प्रतिबद्ध bf5c057 (25 अप्रैल 2018) द्वारा जोहानिस Schindelin ( dscho)स्टीफन बेलर ( ) द्वारा प्रतिबद्ध f431d73 (25 अप्रैल 2018)
देखें ।stefanbeller
फिलिप वुड ( ) द्वारा 2429335 (25 अप्रैल 2018) प्रतिबद्ध देखें । ( जूनियो सी हमानो द्वारा विलय - में - २ सी , ई ६ , २३ मई २०१io )phillipwood
gitster

pull: --rebase-mergesशाखा टोपोलॉजी को फिर से बनाने के लिए स्वीकार करते हैं

कमांड preserveके --preserve-merges विकल्प को पास करने की विधा के समान, मोड बस विकल्प को पास करता है।rebasemerges--rebase-merges

यह उपयोगकर्ताओं को आसानी से गैर-तुच्छ प्रतिबद्ध टोपोलॉजी को फिर से शुरू करने की अनुमति देगा, जब उन्हें बिना चपटा किए, नए कमिट्स को खींचना होगा।


git rebaseमैन पेज में अब एक पूर्ण खंड है जो मर्ज के साथ पुन: इतिहास को समर्पित है

निकालें:

कई कारण हैं कि एक डेवलपर मर्ज कमिट को फिर से बनाना चाहता है: एकाधिक, अंतर-संबंधित शाखाओं पर काम करते समय शाखा संरचना (या "प्रतिबद्ध टोपोलॉजी") रखने के लिए।

निम्नलिखित उदाहरण में, डेवलपर एक विषय शाखा पर काम करता है जो कि बटन को परिभाषित करने के तरीके को रिफ्लेक्टर करता है, और एक अन्य विषय शाखा पर जो "रिफंडिंग ए बग" बटन को लागू करने के लिए उस रिफैक्टरिंग का उपयोग करता है।
उत्पादन git log --graph --format=%s -5इस तरह दिख सकता है:

*   Merge branch 'report-a-bug'
|\
| * Add the feedback button
* | Merge branch 'refactor-button'
|\ \
| |/
| * Use the Button class for all buttons
| * Extract a generic Button class from the DownloadButton one

हो सकता है कि डेवलपर master शाखा टोपोलॉजी को रखते हुए एक नए सिरे से कमिट करना चाहता हो , उदाहरण के लिए जब पहली विषय शाखा को masterदूसरे से पहले बहुत एकीकृत करने की उम्मीद की जाती है , तो कहना है DownloadButtonकि क्लास में किए गए बदलावों के साथ मर्ज संघर्षों को हल करना। में है master

--rebase-mergesविकल्प का उपयोग करके यह रिबेस किया जा सकता है।


एक छोटे से उदाहरण के लिए 1644c73 प्रतिबद्ध देखें :

rebase-helper --make-script: मर्ज करने के लिए एक ध्वज का परिचय

सिक्वेंसर ने सिर्फ शाखा संरचना ( आत्मा के समान --preserve-merges, लेकिन काफी कम टूटी हुई डिजाइन के साथ ) को फिर से बनाने के इरादे से नए आदेशों को सीखा ।

आइए rebase--helper, नए --rebase-mergesविकल्प द्वारा ट्रिगर किए गए इन आदेशों का उपयोग करके टूडू सूचियों को उत्पन्न करने की अनुमति दें ।
इस तरह एक प्रतिबद्ध टोपोलॉजी के लिए (जहां HE C को इंगित करता है):

- A - B - C (HEAD)
    \   /
      D

उत्पन्न टूडू सूची इस तरह दिखाई देगी:

# branch D
pick 0123 A
label branch-point
pick 1234 D
label D

reset branch-point
pick 2345 B
merge -C 3456 D # C

क्या अंतर है --preserve-merge?
प्रतिबद्ध 8f6aed7 बताते हैं:

एक बार, यहाँ डेवलपर ने सोचा: यह अच्छा नहीं होगा यदि, कहो, Git for Windows 'पैच ऑफ द कोर के ऊपर Git को शाखाओं की एक मोटी रेखा के रूप में दर्शाया जा सकता है, और कोर Git के शीर्ष पर रीबेड किया जा सकता है पैच श्रृंखला का चेरी-पिक'एबल सेट बनाए रखें?

इस जवाब देने के लिए मूल प्रयास किया गया था: git rebase --preserve-merges

हालांकि, उस प्रयोग को एक इंटरैक्टिव विकल्प के रूप में कभी नहीं किया गया था, और यह केवल गुल्लक-समर्थित था git rebase --interactiveक्योंकि उस आदेश का कार्यान्वयन पहले से ही बहुत, बहुत परिचित लग रहा था: यह उसी व्यक्ति द्वारा डिज़ाइन किया गया था जिसने डिजाइन किया था --preserve-merges: आपका वास्तव में।

और "तुम्हारा सही मायने में", लेखक खुद को संदर्भित करता है: जोहानस शिंडेलिन ( dscho) , जो मुख्य कारण है (कुछ अन्य नायकों के साथ - हेंस, स्टीफन, सेबस्टियन, ...) कि हमारे पास Git है विंडोज के लिए (भले ही दिन में वापस - 2009 - यह आसान नहीं था )।
वह सितंबर 2015 से Microsoft में काम कर रहे हैं , जो समझ में आता है कि Microsoft अब Git का भारी उपयोग करता है और उसे अपनी सेवाओं की आवश्यकता है।
यह प्रवृत्ति 2013 में वास्तव में TFS के साथ शुरू हुई थी । तब से, Microsoft ग्रह पर सबसे बड़ी गिट रिपॉजिटरी का प्रबंधन करता है ! और, अक्टूबर 2018 से, Microsoft ने GitHub का अधिग्रहण किया

अप्रैल 2018 में Git Merge 2018 के लिए आप जोहान्स को इस वीडियो में बोलते देख सकते हैं ।

कुछ समय बाद, कुछ अन्य डेवलपर (मैं आपको देख रहा हूं, एंड्रियास!; ;-)) ने फैसला किया कि यह एक अच्छा विचार होगा जिसे (कैविट्स के साथ) और गिट अनुरक्षक (अच्छी तरह से, अंतरिम गिट अनुचर) के साथ --preserve-mergesसंयोजित करने की अनुमति होगी। --interactiveजूनियो की अनुपस्थिति के दौरान, वह) सहमत है, और वह यह है कि --preserve-mergesडिजाइन का ग्लैमर जल्दी और असमान रूप से अलग होने लगा।

यहां जोनाथन सुसे से एंड्रियास श्वाब के बारे में बात कर रहे हैं ।
आप उनकी कुछ चर्चाओं को 2012 में देख सकते हैं ।

कारण? में --preserve-mergesमोड, किसी मर्ज के माता पिता (या उस बात के लिए की प्रतिबद्ध किसी भी प्रतिबद्ध) स्पष्ट रूप से कहा नहीं कर रहे थे, लेकिन थे गर्भित प्रतिबद्ध नाम के लिए पारित द्वारा pickआदेश

यह असंभव बना दिया, उदाहरण के लिए, कमिट को फिर से चालू करने के लिए
विषय शाखाओं को दो में विभाजित करने के लिए, शाखाओं या देवता के बीच कमिट को स्थानांतरित करने का उल्लेख नहीं करना।

काश, इन कमियों ने उस मोड को भी रोक दिया (जिसका मूल उद्देश्य विंडोज की जरूरतों के लिए गिट की सेवा करना था, अतिरिक्त उम्मीद के साथ कि यह दूसरों के लिए भी उपयोगी हो सकता है) विंडोज की जरूरतों के लिए सेवा प्रदान करने से।

पांच साल बाद, जब यह वास्तव में एक अनिर्दिष्ट हो गया था, आंशिक रूप से संबंधित, बड़े रूप से संबंधित विंडोज में गिट के पैच-थ्रू पैच श्रृंखला, जो कि विंडोज के लिए समय-समय पर कोर गिट के टैग पर बगावत की गई थी (डेवलपर के अवांछनीय क्रोध अर्जित करना) git-remote-hgविंडोज के प्रतिस्पर्धात्मक दृष्टिकोण के लिए पहली बार गिट का पालन करने वाली बीमार श्रृंखला, जिसे बाद में अनुचर के बिना छोड़ दिया गया था) वास्तव में अस्थिर था, " गिट गार्डन कैंची " का जन्म हुआ : एक स्क्रिप्ट, इंटरैक्टिव रीबेज के शीर्ष पर गुल्लक-बैकिंग, यह पहले पैच के शाखा टोपोलॉजी को फिर से निर्धारित करने के लिए निर्धारित करेगा, आगे के संपादन के लिए एक छद्म टूडू सूची बनाएं, परिणाम को एक वास्तविक टूडू सूची में बदल देगा (भारी उपयोग करना)exec लापता टूडू सूची आदेशों को "लागू" करने के लिए कमांड करें और अंत में नए बेस कमिट के शीर्ष पर पैच श्रृंखला को फिर से बनाएं।

( 9055e40 में इस पैच में गिट गार्डन कैंची लिपि को संदर्भित किया गया है )

यह 2013 में था।
और डिजाइन के साथ आने और इसे आउट-ऑफ-ट्री स्क्रिप्ट के रूप में लागू करने में लगभग तीन सप्ताह लग गए। कहने की जरूरत नहीं कि कार्यान्वयन को स्थिर करने के लिए काफी कुछ वर्षों की आवश्यकता थी, जबकि सभी डिजाइन खुद को ध्वनि साबित करते थे।

इस पैच के साथ, गीट गार्डन कैंची की अच्छाई git rebase -iअपने आप आती हैविकल्प
पास करना --rebase-mergesएक टूडू सूची उत्पन्न करेगा जिसे आसानी से समझा जा सकता है, और जहां यह स्पष्ट है कि कैसे फिर से शुरू होता है । कमांड
डालकर labelऔर कॉल करके नई शाखाओं को पेश किया जा सकता है merge <label>
और एक बार जब यह मोड स्थिर हो जाएगा और सार्वभौमिक रूप से स्वीकार कर लिया जाएगा, तो हम उस डिज़ाइन की गलती को हटा सकते हैं जो थी--preserve-merges


Git 2.19 (Q3 2018) के --rebase-mergesसाथ काम करके नए विकल्प में सुधार करता है --exec

" --exec" विकल्प " git rebase --rebase-merges" ने निष्पादन कमांड को गलत स्थानों पर रखा, जिसे सही कर दिया गया है।

देखें 1ace63b प्रतिबद्ध (09 अगस्त 2018), और f0880f7 प्रतिबद्ध द्वारा (06 अगस्त 2018) जोहानिस Schindelin ( dscho)
(द्वारा विलय Junio सी Hamano - gitster- में प्रतिबद्ध 750eb11 , 20 अगस्त 2018)

rebase --exec: इसके साथ काम करो --rebase-merges

के विचार --execएक संलग्न करने के लिए है execप्रत्येक के बाद कॉल pick

चूंकि fixup!/ एस शुरू होने के बाद quash!, इस विचार को "पिक, संभवतः एक फिक्सअप / स्क्वैश चेन" के लिए लागू करने के लिए बढ़ाया गया था, यानी एक निष्पादन को pickइसके और इसी fixupया squashलाइनों में से किसी के बीच नहीं डाला जाएगा ।

वर्तमान कार्यान्वयन इसे प्राप्त करने के लिए एक गंदे चाल का उपयोग करता है: यह मानता है कि केवल पिक / फिक्सअप / स्क्वैश कमांड हैं, और फिर किसी भी पहले लेकिन पहले लाइनों को सम्मिलित करता है और एक अंतिम जोड़ देता है।execpick

के द्वारा उत्पन्न टूडू सूचियों के साथ git rebase --rebase-merges, यह सरल कार्यान्वयन इसकी समस्याओं को दिखाता है: यह सटीक गलत चीज़ पैदा करता है जब वहाँ होते हैं label, resetऔर mergeआदेश।

: आइए परिवर्तन कार्यान्वयन वास्तव में ऐसा करने के लिए हम क्या चाहते हैं के लिए देखो pickलाइनों, किसी भी फिक्स-अप / स्क्वैश जंजीरों को छोड़, और फिर सम्मिलित exec लाइन । बल्कि, कुल्ला, दोहराएं।

नोट: हम जब भी संभव हो, टिप्पणी लाइनों से पहले सम्मिलित करने के लिए दर्द उठाते हैं, क्योंकि खाली कमेंट्स को टिप्पणी-आउट पिक लाइनों द्वारा दर्शाया जाता है (और हम इस तरह की रेखा से पहले एक पूर्ववर्ती पिक की निष्पादन रेखा सम्मिलित करना चाहते हैं , बाद में नहीं)।

हालांकि, यह भी आज्ञाओं के execबाद लाइनों को जोड़ते हैं merge, क्योंकि वे pickआज्ञाओं की भावना के समान हैं : वे नए आवागमन जोड़ते हैं।


Git 2.22 (Q2 2019), रिबेज मध्यवर्ती राज्यों को संग्रहीत करने के लिए Refs / rewritten / hierarchy के उपयोग को ठीक करता है, जो स्वाभाविक रूप से प्रति वर्कट्री प्रति पदानुक्रम बनाता है।

देखें b9317d5 प्रतिबद्ध , 90d31ff प्रतिबद्ध , 09e6564 प्रतिबद्ध (07 मार्च 2019) द्वारा गुयेन थाई Ngọc Duy ( pclouds)
(द्वारा विलय Junio सी Hamano - gitster- में प्रतिबद्ध 917f2cd , 09 अप्रैल 2019)

सुनिश्चित करें कि प्रति-कार्य / फिर से लिखा गया है

a9be29c (सीक्वेंसर: labelकमांड वर्कट्री-लोकल, 2018-04-25, Git 2.19) द्वारा जनरेट किया गया रिफ्स बनाते हैं refs/rewritten/
दुर्भाग्य से (मेरा बुरा) कुछ स्थानों पर हैं जिन्हें यह सुनिश्चित करने के लिए अद्यतन की आवश्यकता है कि यह वास्तव में प्रति-कार्य-विषय है।

- add_per_worktree_entries_to_dir()यह सुनिश्चित करने के लिए अद्यतन किया जाता है कि refs/rewritten/प्रति-रेपो-प्रति के बजाय प्रति-कार्य-सूची को देखें ।

  • common_list[]अपडेट किया गया है ताकि git_path()सही स्थान लौटाया जा सके। इसमें " rev-parse --git-path" शामिल हैं ।

यह गंदगी मेरे द्वारा बनाई गई है।
मैंने इसे शुरू करने की कोशिश की, refs/worktree,जहां सभी रेफरी विशेष उपचार के बिना प्रति-वर्कट्री होंगे।
दुर्भाग्यपूर्ण refs / rewritten refs / worktree से पहले आया था इसलिए यह सब हम कर सकते हैं।


Git 2.24 (Q4 2019) के साथ, " git rebase --rebase-merges" विभिन्न मर्ज रणनीतियों को चलाना और उनके लिए रणनीति विशिष्ट विकल्प पास करना सीखा।

एलिजा न्यूरेन ( ) द्वारा प्रतिबद्ध 476998 डी (04 सितंबर 2019) देखें । प्रतिबद्ध e1fac53 देखें , a63f990 प्रतिबद्ध करेंnewren
, 5dcdd74 प्रतिबद्ध , e145d99 प्रतिबद्ध , 4e6023b प्रतिबद्ध , f67336d प्रतिबद्ध , a9c7107 प्रतिबद्ध , b8c6f24 प्रतिबद्ध , d51b771 प्रतिबद्ध , प्रतिबद्ध c248d32 , प्रतिबद्ध 8c1e240 , प्रतिबद्ध 5efed0e , प्रतिबद्ध 68b54f6 , 2e7bbac प्रतिबद्ध , प्रतिबद्ध 6180b20 , d5b581f प्रतिबद्ध 31 ( जुलाई 2019) द्वाराजोहान्स शिंडेलिन ( dscho)
(द्वारा विलय Junio सी Hamano - gitster- में प्रतिबद्ध 917a319 , 18 सितं, 2019)


Git 2.25 (Q1 2020) के साथ, लॉजिक वर्कट्री लोकल और रिपॉजिटरी ग्लोबल रीफ्स को अलग-अलग बताने के लिए इस्तेमाल किया जाता है, संरक्षित-मर्ज को सुविधाजनक बनाने के लिए।

देख प्रतिबद्ध f45f88b , प्रतिबद्ध c72fc40 , 8a64881 , 7cb8c92 के लिए प्रतिबद्ध , SZEDER गैबोर () द्वारा e536b1fszeder (21 अक्टूबर 2019 )
(द्वारा विलय Junio सी Hamano - gitster- में db806d7 प्रतिबद्ध , 10 नवंबर 2019)

path.c: matchमान के बिना फ़ंक्शन को कॉल न करेंtrie_find()

साइन-ऑफ-बाय: SZEDER गैबर

'लॉग / रेफ्स' एक कार्यशील पेड़-विशिष्ट पथ नहीं है, लेकिन चूंकि प्रतिबद्ध b9317d55a3 (सुनिश्चित करें कि refs / rewritten / per-worktree है, 2019-03-07, v2.22.0-rc0) ' git rev-parse --git-path' एक फर्जी मार्ग लौटा रहा है यदि एक अनुगामी ' /' मौजूद है:

$ git -C WT/ rev-parse --git-path logs/refs --git-path logs/refs/
/home/szeder/src/git/.git/logs/refs
/home/szeder/src/git/.git/worktrees/WT/logs/refs/

हम एक trieडेटा संरचना का उपयोग कुशलतापूर्वक यह तय करने के लिए करते हैं कि कोई पथ सामान्य डायर का है या पेड़-विशिष्ट काम कर रहा है।

जैसा कि ऐसा होता है b9317d55a3 एक बग को ट्रिगर करता है जो trieकार्यान्वयन के रूप में पुराना है, 4e09cf2acf (" path: आम डायर चेकिंग का अनुकूलन करें " में जोड़ा गया , 2015-08-31, Git v2.7.0-rc0 - बैच # 2 में सूचीबद्ध मर्ज )।

  • वर्णन करने वाली टिप्पणी के अनुसार trie_find(), इसे केवल दिए गए मैच फंक्शन को 'fn' के लिए "/ -or- \ 0-कुंजी के लिए प्री-टर्मिनेटेड उपसर्ग के लिए कॉल करना चाहिए, जिसके लिए ट्राइ में एक मान होता है"।
    यह सच नहीं है: तीन जगह हैं जहां trie_find () मैच फ़ंक्शन को कॉल करता है, लेकिन उनमें से एक मूल्य के अस्तित्व के लिए चेक को याद कर रहा है।

  • b9317d55a3 ने दो नई कुंजियाँ जोड़ींtrie :

    • ' logs/refs/rewritten', और
    • ' logs/refs/worktree', पहले से मौजूद ' logs/refs/bisect' के बगल में ।
      इससे trieमार्ग ' logs/refs/' के साथ एक नोड उत्पन्न हुआ , जो पहले मौजूद नहीं था, और जिसका कोई मूल्य संलग्न नहीं है।
      ' logs/refs/' के लिए एक क्वेरी इस नोड को ढूंढती है और फिर उस matchफ़ंक्शन को कॉल करती है जो मान के अस्तित्व की जांच नहीं करता है, और इस प्रकार matchफ़ंक्शन NULLको मान के रूप में आमंत्रित करता है ।
  • जब matchफ़ंक्शन check_common()को एक NULLमान के साथ लागू किया जाता है , तो यह 0 देता है, जो इंगित करता है कि queried पथ सामान्य निर्देशिका से संबंधित नहीं है, जिसके परिणामस्वरूप अंततः ऊपर दिखाए गए बोगस पथ।

लापता स्थिति को जोड़ें trie_find()ताकि यह गैर-मौजूदा मूल्य के साथ मैच फ़ंक्शन को कभी भी आमंत्रित न करे।

check_common() उसके बाद यह जांचने के लिए नहीं होगा कि उसे एक गैर-पूर्ण मान मिला है, इसलिए उस शर्त को हटा दें।

मेरा मानना ​​है कि ऐसे अन्य मार्ग नहीं हैं जो समान बोगस आउटपुट का कारण बन सकते हैं।

AFAICT केवल दूसरी कुंजी है जिसके परिणामस्वरूप मैच फ़ंक्शन को एक NULLमान के साथ बुलाया जा रहा है, ' co' (कुंजियों के कारण ' common' और ' config')।

हालांकि, जैसा कि वे एक निर्देशिका में नहीं हैं जो आम निर्देशिका से संबंधित है, जिसके परिणामस्वरूप कार्य-विशिष्ट पेड़-विशिष्ट पथ अपेक्षित है।


3
मुझे लगता है कि यह शीर्ष उत्तर होना चाहिए, --preserve-mergesवास्तव में मर्ज को "संरक्षित" नहीं करना चाहिए जैसा कि आप चाहते हैं, यह बहुत भोला है। यह आपको मर्ज किए गए कमिट्स और उनके माता-पिता के रिश्तों को संरक्षित करने की अनुमति देता है, जबकि आपको एक इंटरैक्टिव रिबास का लचीलापन देता है। यह नई सुविधा बहुत बढ़िया है और यदि यह अच्छी तरह से लिखे गए एसओ उत्तर के लिए नहीं है, तो मुझे नहीं पता होगा!
इगूसीकर

@egucciar धन्यवाद। और यह Git 2.18 ( stackoverflow.com/search?q=user%3A6309+%22git+2.18%22 ) की एकमात्र विशेषता नहीं है , और Git 2.19 ( stackoverflow.com/search?q ) .user%3A6309+%22git+2.19%9 22 )
VonC

1
अत्यधिक उपयोगी है अगर आप के आसपास इस क्यू / ए, में की तरह प्रतिबद्ध की जिसे स्थानांतरित करने के लिए कोशिश कर रहे हैं stackoverflow.com/questions/45059039/...
okovko

1
ओह, यह वास्तव में है जो मैं थोड़ी देर से देख रहा था! मेरे पास इस तरह के मामलों के लिए एक मैनुअल वर्कअराउंड था , जिस पर सभी मर्ज में शामिल होने के लिए एक काल्पनिक कमिट बनाना चाहिए।
कार्निशियन

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