svn - एसवीएन की तुलना में गिट में कैसे और/या क्यों विलय हो रहा है?




git version-control (5)

एक चीज जिसका उल्लेख अन्य उत्तरों में नहीं किया गया है, और यह वास्तव में एक डीवीसीएस का एक बड़ा फायदा है, यह है कि आप अपने परिवर्तनों को धक्का देने से पहले स्थानीय रूप से प्रतिबद्ध कर सकते हैं। एसवीएन में, जब मेरे पास कुछ बदलाव था, तो मैं जांचना चाहता था, और इस दौरान किसी ने पहले ही उसी शाखा पर एक प्रतिबद्धता कर ली थी, इसका मतलब था कि मुझे प्रतिबद्ध करने से पहले मुझे एक svn update करना था। इसका मतलब है कि मेरे परिवर्तन, और दूसरे व्यक्ति के परिवर्तन अब एक साथ मिश्रित हो गए हैं, और विलय को रद्द करने का कोई तरीका नहीं है (जैसे कि git reset या hg update -C ), क्योंकि वापस जाने के लिए कोई प्रतिबद्धता नहीं है। यदि विलय गैर-तुच्छ है, तो इसका मतलब है कि आप मर्ज परिणाम साफ़ करने से पहले अपनी सुविधा पर काम करना जारी नहीं रख सकते हैं।

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

मैंने कुछ स्थानों में सुना है कि वितरित संस्करण नियंत्रण प्रणाली चमकने के मुख्य कारणों में से एक, एसवीएन जैसे पारंपरिक उपकरणों की तुलना में बेहतर विलय कर रहा है। क्या वास्तव में यह दोनों अंतर्निहित मतभेदों के कारण है कि दोनों सिस्टम कैसे काम करते हैं, या विशिष्ट डीवीसी कार्यान्वयन जैसे कि गिट / मर्कुरियल में एसवीएन की तुलना में स्पष्ट रूप से विलय करने वाले एल्गोरिदम हैं?


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

गिट डिफ़ॉल्ट रूप से 3-तरफा मर्ज एल्गोरिदम का उपयोग करता है, जिसमें विलय के दोनों किनारों पर मौजूद ज्ञान के उपयोग को विलय करने और उपयोग करने के लिए एक आम पूर्वज ढूंढना शामिल है। यह गिट को विवादों से बचने में अधिक बुद्धिमान होने की अनुमति देता है।

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


जोएल के ब्लॉग पर एक लेख पढ़ें (दुख की बात है कि उसका आखिरी वाला)। यह Mercurial के बारे में है, लेकिन यह वास्तव में वितरित वीसी सिस्टम जैसे गिट के फायदे के बारे में बात करता है।

वितरित संस्करण नियंत्रण के साथ, वितरित हिस्सा वास्तव में सबसे दिलचस्प हिस्सा नहीं है। दिलचस्प हिस्सा यह है कि ये सिस्टम संस्करणों के संदर्भ में परिवर्तनों के संदर्भ में सोचते हैं।

here लेख पढ़ें।


बस रखें, SVN तुलना में Git में विलय कार्यान्वयन बेहतर किया गया है। 1.5 एसवीएन ने मर्ज एक्शन रिकॉर्ड नहीं किया था, इसलिए उपयोगकर्ता द्वारा सहायता के बिना भविष्य में विलय करने में असमर्थ था, जिसे एसवीएन रिकॉर्ड नहीं किया गया था, जो जानकारी प्रदान करने की आवश्यकता थी। 1.5 के साथ यह बेहतर हो गया, और वास्तव में एसवीएन स्टोरेज मॉडल थोड़ा अधिक सक्षम है कि गिट के डीएजी। लेकिन एसवीएन ने मर्ज जानकारी को एक बदले हुए रूप में संग्रहीत किया जो कि गिट की तुलना में विलयों को बड़े पैमाने पर अधिक समय लेता है - मैंने निष्पादन समय में 300 के कारकों को देखा है।

साथ ही, एसवीएन ने स्थानांतरित फ़ाइलों के विलय की सहायता के लिए नामों को ट्रैक करने का दावा किया है। लेकिन असल में यह अभी भी उन्हें एक प्रतिलिपि और एक अलग हटाने की क्रिया के रूप में संग्रहीत करता है, और विलय एल्गोरिदम अभी भी उन पर संशोधित / नाम बदलें स्थितियों में ठोकर खा रहा है, यानी, जहां एक शाखा पर एक फ़ाइल संशोधित की जाती है और दूसरी पर नाम बदलती है, और उन शाखाएं हैं विलय करने के लिए। ऐसी परिस्थितियां अभी भी नकली विलय संघर्ष उत्पन्न करती हैं, और निर्देशिका नाम के मामले में यह भी संशोधन की चुप हानि की ओर ले जाती है। (एसवीएन लोग तब इंगित करते हैं कि संशोधन अभी भी इतिहास में हैं, लेकिन जब वे मर्ज परिणाम में नहीं होते हैं तो वे बहुत मदद नहीं करते हैं, जहां उन्हें दिखना चाहिए।

दूसरी तरफ, गिट नामों को ट्रैक भी नहीं करता है लेकिन तथ्य के बाद उन्हें बाहर निकाल देता है (विलय समय पर), और बहुत ही जादूगर रूप से करता है।

एसवीएन मर्ज प्रतिनिधित्व में भी समस्याएं हैं; 1.5 / 1.6 में आप ट्रंक से शाखा में जितनी बार पसंद करते हैं, उतनी बार विलय कर सकते हैं, लेकिन अन्य दिशा में एक विलय ( --reintegrate ) की घोषणा करने की आवश्यकता होती है, और शाखा को एक अनुपयोगी स्थिति में छोड़ दिया जाता है। बाद में उन्हें पता चला कि यह वास्तव में मामला नहीं है, और यह कि) - --reintegrate स्वचालित रूप से पता लगाया जा सकता है, और बी) दोनों दिशाओं में बार-बार विलय संभव है।

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

उत्तर में किए गए अन्य बिंदु, एसवीएन में शाखाओं की जबरन वैश्विक दृश्यता के रूप में, क्षमताओं को मर्ज करने के लिए प्रासंगिक नहीं हैं (लेकिन प्रयोज्यता के लिए)। साथ ही, 'गिट स्टोर बदलते हैं जबकि एसवीएन स्टोर्स (कुछ अलग)' ज्यादातर बिंदु से बाहर होते हैं। गिट अवधारणा से प्रत्येक प्रतिबद्धता को एक अलग पेड़ (एक tar फ़ाइल की तरह) के रूप में संग्रहीत करता है, और फिर उस कुशलतापूर्वक स्टोर करने के लिए कुछ ह्यूरिस्टिक्स का उपयोग करता है। दो प्रतिबद्धताओं के बीच परिवर्तन की गणना भंडारण कार्यान्वयन से अलग है। सच यह है कि गिट इतिहास डीएजी को एक और अधिक सरल रूप में संग्रहीत करता है जो एसवीएन अपने मर्जिनोफो करता है। जो भी बाद में समझने की कोशिश कर रहा है उसे पता चलेगा कि मेरा क्या मतलब है।

संक्षेप में: गिट एसवीएन की तुलना में संशोधन को संग्रहीत करने के लिए एक बहुत ही सरल डेटा मॉडल का उपयोग करता है, और इस प्रकार यह प्रतिनिधित्व => व्यावहारिक रूप से बेहतर विलय से निपटने की कोशिश करने के बजाय वास्तविक मर्ज एल्गोरिदम में बहुत अधिक ऊर्जा डाल सकता है।


सबवर्सन की तुलना में डीवीसीएस में विलय क्यों बेहतर है इसका दावा काफी हद तक इस बात पर आधारित था कि कुछ समय पहले सबवर्सन में कैसे काम करना और विलय करना था। 1.5.0 से पहले सबवर्जन ने शाखाओं को विलय करने के बारे में कोई जानकारी नहीं दी थी, इस प्रकार जब आप विलय करना चाहते थे तो आपको निर्दिष्ट करना होगा कि संशोधनों की किस श्रेणी को विलय करना था।

तो सबवर्सन क्यों विसर्जित हो गया?

इस उदाहरण पर विचार करें:

      1   2   4     6     8
trunk o-->o-->o---->o---->o
       \
        \   3     5     7
b1       +->o---->o---->o

जब हम ट्रंक में बी 1 के परिवर्तनों को merge करना चाहते हैं तो हम निम्न आदेश जारी करेंगे, ट्रंक चेक किए गए फ़ोल्डर पर खड़े होने पर:

svn merge -r 2:7 {link to branch b1}

... जो b1 से आपके स्थानीय कामकाजी निर्देशिका में परिवर्तनों को मर्ज करने का प्रयास करेगा। और फिर आप किसी भी विवाद को हल करने के बाद परिवर्तनों को पूरा करते हैं और परिणाम का परीक्षण करते हैं। जब आप संशोधन पेड़ करते हैं तो ऐसा दिखाई देगा:

      1   2   4     6     8   9
trunk o-->o-->o---->o---->o-->o      "the merge commit is at r9"
       \
        \   3     5     7
b1       +->o---->o---->o

हालांकि संशोधनों की श्रेणियों को निर्दिष्ट करने का यह तरीका हाथ से बाहर हो जाता है जब संस्करण वृक्ष बढ़ता है क्योंकि उपवर्तन के पास कोई मेटा डेटा नहीं होता है जब और किस संशोधन में विलय हो जाता है। बाद में क्या होता है पर विचार करें:

           12        14
trunk  …-->o-------->o
                                     "Okay, so when did we merge last time?"
              13        15
b1     …----->o-------->o

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

इस सबवर्सन को कम करने के लिए अब शाखा के लिए मेटा डेटा स्टोर और मर्ज करें। इससे सभी समस्याओं का समाधान ठीक होगा?

और ओह, वैसे, सबवर्सन अभी भी बेकार है ...

एक केंद्रीकृत प्रणाली पर, उपversण की तरह, आभासी निर्देशिका चूसना। क्यूं कर? क्योंकि हर किसी के पास उन्हें देखने की पहुंच है ... यहां तक ​​कि कचरा प्रयोगात्मक भी। यदि आप प्रयोग करना चाहते हैं तो शाखाकरण अच्छा है लेकिन आप हर किसी और उनके चाची प्रयोग नहीं देखना चाहते हैं । यह गंभीर संज्ञानात्मक शोर है। जितनी अधिक शाखाएं आप जोड़ते हैं, उतना अधिक बकवास आपको देखना होगा।

आपके पास भंडार में जितनी अधिक सार्वजनिक शाखाएं हैं, वे सभी अलग-अलग शाखाओं का ट्रैक रखना कठिन होगा। तो आपके पास सवाल यह होगा कि यदि शाखा अभी भी विकास में है या यदि यह वास्तव में मृत है तो किसी केंद्रीकृत संस्करण नियंत्रण प्रणाली में बताना मुश्किल है।

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

तो डीवीसीएस, जैसे कि गिट, मर्कुरियल और बाज़ार, ब्रांचिंग और विलय में सबवर्जन से बेहतर क्यों हैं?

एक बहुत ही सरल कारण है: ब्रांचिंग एक प्रथम श्रेणी की अवधारणा है । डिज़ाइन और शाखाओं द्वारा कोई वर्चुअल निर्देशिका नहीं है DVCS में हार्ड ऑब्जेक्ट्स हैं जिन्हें इसे रिपॉजिटरीज़ (यानी पुश और पुल ) के सिंक्रनाइज़ेशन के साथ काम करने के लिए ऐसा करने की आवश्यकता होती है।

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

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

विलय का एक बहुत ही सरल उदाहरण यह होगा; origin और एक उपयोगकर्ता, ऐलिस नामक एक केंद्रीय भंडार की कल्पना करें, जो उसकी मशीन पर भंडार क्लोनिंग कर रही है।

         a…   b…   c…
origin   o<---o<---o
                   ^master
         |
         | clone
         v

         a…   b…   c…
alice    o<---o<---o
                   ^master
                   ^origin/master

क्लोन के दौरान क्या होता है कि प्रत्येक संशोधन को ऐलिस को ठीक उसी तरह कॉपी किया जाता है (जिसे विशिष्ट रूप से पहचाने जाने योग्य हैश-आईडी द्वारा मान्य किया जाता है), और अंक जहां मूल की शाखाएं हैं।

एलिस उसके बाद अपने रेपो पर काम करती है, अपने खुद के भंडार में काम करती है और अपने बदलावों को धक्का देने का फैसला करती है:

         a…   b…   c…
origin   o<---o<---o
                   ^ master

              "what'll happen after a push?"


         a…   b…   c…   d…   e…
alice    o<---o<---o<---o<---o
                             ^master
                   ^origin/master

समाधान अपेक्षाकृत सरल है, origin भंडार को केवल एक ही चीज है जो सभी नए संशोधनों को लेना है और इसकी शाखा को नवीनतम संशोधन में ले जाना है (जो गिट "फास्ट-फॉरवर्ड" कहता है):

         a…   b…   c…   d…   e…
origin   o<---o<---o<---o<---o
                             ^ master

         a…   b…   c…   d…   e…
alice    o<---o<---o<---o<---o
                             ^master
                             ^origin/master

उपयोग केस, जिसे मैंने ऊपर दिखाया है, को कुछ भी मर्ज करने की आवश्यकता नहीं है । तो मुद्दा वास्तव में विलय एल्गोरिदम के साथ नहीं है क्योंकि तीन-तरफा मर्ज एल्गोरिदम सभी संस्करण नियंत्रण प्रणालियों के बीच काफी समान है। मुद्दा किसी भी चीज़ की तुलना में संरचना के बारे में अधिक है

तो आप मुझे एक उदाहरण कैसे दिखाते हैं जिसमें वास्तविक विलय है?

माना जाता है कि उपर्युक्त उदाहरण एक बहुत ही सरल उपयोग केस है, इसलिए एक अधिक आम एक यद्यपि एक और अधिक मोड़ कर सकते हैं। याद रखें कि origin तीन संशोधन के साथ शुरू हुआ? खैर, जिसने उन्हें किया, वह उसे बॉब कहता है, अपने आप पर काम कर रहा है और अपने खुद के भंडार पर प्रतिबद्ध है:

         a…   b…   c…   f…
bob      o<---o<---o<---o
                        ^ master
                   ^ origin/master

                   "can Bob push his changes?" 

         a…   b…   c…   d…   e…
origin   o<---o<---o<---o<---o
                             ^ master

अब बॉब अपने परिवर्तन सीधे origin भंडार में नहीं डाल सकता है। सिस्टम यह कैसे पता लगाता है कि बॉब के संशोधन सीधे origin से निकलते हैं, जो इस मामले में नहीं है। धक्का देने के किसी भी प्रयास के परिणामस्वरूप सिस्टम " उह ... " मुझे डर लगता है कि आप उस बॉब को नहीं दे सकते । "

इसलिए बॉब को पुल-इन करना है और फिर परिवर्तनों को मर्ज करना है (गिट के pull ; या एचजी की pull और merge ; या बीजीआर का merge )। यह एक दो-चरणीय प्रक्रिया है। पहले बॉब को नए संशोधन लाने होंगे, जो उन्हें origin भंडार के रूप में कॉपी करेंगे। अब हम देख सकते हैं कि ग्राफ अलग हो गया है:

                        v master
         a…   b…   c…   f…
bob      o<---o<---o<---o
                   ^
                   |    d…   e…
                   +----o<---o
                             ^ origin/master

         a…   b…   c…   d…   e…
origin   o<---o<---o<---o<---o
                             ^ master

पुल प्रक्रिया का दूसरा चरण अलग-अलग युक्तियों को मर्ज करना और परिणाम की प्रतिबद्धता बनाना है:

                                 v master
         a…   b…   c…   f…       1…
bob      o<---o<---o<---o<-------o
                   ^             |
                   |    d…   e…  |
                   +----o<---o<--+
                             ^ origin/master

उम्मीद है कि मर्ज विवादों में नहीं चलेगा (यदि आप उन्हें उम्मीद करते हैं तो आप गेट में दो चरणों को मैन्युअल रूप से fetch और merge साथ कर सकते हैं)। बाद में उन परिवर्तनों को फिर से शुरू करने की आवश्यकता origin , जो परिणामस्वरूप तेजी से आगे बढ़ने के लिए प्रेरित होंगे, जिसके परिणामस्वरूप विलय प्रतिबद्धता origin भंडार में नवीनतम का प्रत्यक्ष वंशज है:

                                 v origin/master
                                 v master
         a…   b…   c…   f…       1…
bob      o<---o<---o<---o<-------o
                   ^             |
                   |    d…   e…  |
                   +----o<---o<--+

                                 v master
         a…   b…   c…   f…       1…
origin   o<---o<---o<---o<-------o
                   ^             |
                   |    d…   e…  |
                   +----o<---o<--+

रीटेज नामक गिट और एचजी में विलय करने का एक और विकल्प है, जो नए बदलावों के बाद बॉब के बदलावों को आगे बढ़ाएगा। चूंकि मैं नहीं चाहता कि यह उत्तर अब और अधिक वर्बोज़ हो, मैं इसके बजाय आपको इसके बारे में git , mercurial या bazaar दस्तावेज़ पढ़ने दूंगा।

पाठक के लिए एक अभ्यास के रूप में, यह पता लगाने का प्रयास करें कि यह शामिल किसी अन्य उपयोगकर्ता के साथ कैसे काम करेगा। यह बॉब के ऊपर उपरोक्त उदाहरण के समान ही किया जाता है। भंडारों के बीच विलय करना आपके विचार से आसान है क्योंकि सभी संशोधन / प्रतिबद्धता विशिष्ट रूप से पहचाने जाने योग्य हैं।

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

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





merge