2009-05-20 9 views
130

हम वर्तमान में अपेक्षाकृत बड़े कोडबेस पर उपversण का उपयोग कर रहे हैं। प्रत्येक रिलीज को अपनी शाखा मिलती है, और ट्रंक के खिलाफ फिक्स का प्रदर्शन किया जाता है और svnmerge.pyMercurial: नामित शाखाओं बनाम एकाधिक रिपोजिटरीज

का मानना ​​है कि मेरा समय बेहतर स्रोत नियंत्रण पर जाने के लिए आया है, और मैं थोड़ी देर के लिए Mercurial के साथ टकरा रहा हूँ ।

हालांकि Mercurial का उपयोग कर ऐसी रिलीज संरचना के प्रबंधन के दो स्कूल होने लगते हैं। या तो प्रत्येक रिलीज को अपना खुद का रेपो मिल जाता है, और रिलीज शाखा के खिलाफ फिक्स किए जाते हैं और मुख्य शाखा (और किसी अन्य नई रिलीज शाखाओं) में धकेलते हैं या एक ही भंडार (या एकाधिक मिलान प्रतियां) के नामित शाखाओं का उपयोग करते हैं।

किसी भी मामले में ऐसा लगता है कि मैं रिलीज शाखाओं में शामिल करने के लिए चेरीपिक परिवर्तनों में प्रत्यारोपण जैसे कुछ का उपयोग कर रहा हूं।

मैं आपसे पूछता हूं; प्रत्येक दृष्टिकोण की सापेक्ष योग्यता क्या हैं?

उत्तर

129

सबसे बड़ा अंतर यह है कि इतिहास में शाखा के नाम कैसे दर्ज किए जाते हैं। नामित शाखाओं के साथ शाखा का नाम प्रत्येक परिवर्तन में एम्बेडेड है और इस प्रकार इतिहास का एक अपरिवर्तनीय हिस्सा बन जाएगा। क्लोन के साथ कोई स्थायी रिकॉर्ड होगा जहां से कोई विशेष परिवर्तन आया था।

इसका मतलब है कि क्लोन त्वरित प्रयोगों के लिए बहुत अच्छे हैं जहां आप शाखा का नाम रिकॉर्ड नहीं करना चाहते हैं, और नामित शाखा लंबी अवधि की शाखाओं ("1.x", "2.x" और इसी तरह के लिए अच्छी हैं)।

ध्यान दें कि एक एकल भंडार Mercurial में आसानी से कई हल्के वजन वाली शाखाओं को समायोजित कर सकता है। ऐसी इन-रिपोजिटरी शाखाओं को बुकमार्क किया जा सकता है ताकि आप आसानी से उन्हें फिर से ढूंढ सकें। मान लीजिए कि आप कंपनी भंडार क्लोन है जब यह इस तरह लग रही थीं:

[a] --- [b] 

आप दूर हैक और बनाने [x] और [y]:

[a] --- [b] --- [x] --- [y] 

मीन किसी भंडार में [c] और [d] डालता है, जबकि, इसलिए जब आप खींचते हैं तो आपको इस तरह का इतिहास ग्राफ मिलता है:

 
      [x] --- [y] 
     /
[a] --- [b] --- [c] --- [d] 

यहां सी में दो सिर हैं नगल भंडार। आपकी कामकाजी प्रति हमेशा एक एकल बदलाव, तथाकथित काम करने वाली प्रतिलिपि अभिभावक परिवर्तन को प्रतिबिंबित करेगी। इसे जांचें:

% hg parents 

मान लें कि यह [y] की रिपोर्ट करता है। आप

% hg heads 

के साथ सिर देख सकते हैं और इस [y] और [d] रिपोर्ट करेंगे।आप [d] की एक साफ चेकआउट करने के लिए अपने भंडार को अद्यतन करने के लिए, तो बस ([d] के लिए संशोधन संख्या के साथ स्थानापन्न [d]) करना चाहते हैं:

% hg update --clean [d] 

फिर आप देखेंगे कि hg parents रिपोर्ट [d]। इसका मतलब है कि आपकी अगली प्रतिबद्धता में [d] माता-पिता के रूप में होगा। आप इस प्रकार एक बग आप मुख्य शाखा में देखा है ठीक कर सकते हैं और changeset [e] बनाएँ:

 
      [x] --- [y] 
     /
[a] --- [b] --- [c] --- [d] --- [e] 

changeset [e] पुश करने के लिए केवल, आप

% hg push -r [e] 

जहां [e] changeset हैश है की ज़रूरत है। डिफ़ॉल्ट रूप से hg push बस रिपॉजिटरीज़ की तुलना करेगा और [x], [y], और [e] गायब हैं, लेकिन हो सकता है कि आप अभी तक [x] और [y] साझा नहीं करना चाहें।

बग सुधार भी आप प्रभाव हैं, तो आप अपनी सुविधा शाखा के साथ मर्ज करना चाहते हैं:

% hg update [y] 
% hg merge 

कि आपके भंडार ग्राफ इस तरह लग रही छोड़ देंगे:

 
      [x] --- [y] ----------- [z] 
     /     /
[a] --- [b] --- [c] --- [d] --- [e] 

जहां [z] मर्ज है [y] और [e] के बीच। तुम भी शाखा फेंक करने का विकल्प चुना सकता है:

% hg strip [x] 

इस कहानी का मेरा मुख्य बिंदु यह है: एक एकल क्लोन आसानी से विकास के कई पटरियों का प्रतिनिधित्व कर सकते हैं। यह किसी भी एक्सटेंशन का उपयोग किये बिना हमेशा "सादा एचजी" के लिए सच रहा है। bookmarks extension हालांकि, एक बड़ी मदद है। यह आपको परिवर्तनों के लिए नाम (बुकमार्क) असाइन करने की अनुमति देगा। उपर्युक्त मामले में आप अपने विकास के सिर पर एक बुकमार्क और अपस्ट्रीम हेड पर एक बुकमार्क चाहते हैं। बुकमार्क को को मर्कुरियल 1.6 के साथ धक्का दिया और खींच लिया गया और Mercurial 1.8 में अंतर्निहित सुविधा बन गई है।

आप दो क्लोन बनाने का विकल्प चुना था, अपने विकास क्लोन [x] और [y] करने के बाद इस तरह दिख रही है |:

[a] --- [b] --- [x] --- [y] 

और आपके अपस्ट्रीम क्लोन में शामिल होंगे:

[a] --- [b] --- [c] --- [d] 

अब आप बग नोटिस करें और इसे ठीक करें। अपस्ट्रीम क्लोन उपयोग करने के लिए तैयार होने के बाद यहां आपको hg update की आवश्यकता नहीं है।आप के लिए प्रतिबद्ध है और बनाने [e]:

[a] --- [b] --- [c] --- [d] --- [e] 

अपने विकास क्लोन में बग सुधार शामिल करने के लिए तुम वहाँ में खींच:

 
[a] --- [b] --- [x] --- [y] 
      \ 
      [c] --- [d] --- [e] 

और मर्ज:

 
[a] --- [b] --- [x] --- [y] --- [z] 
      \     /
      [c] --- [d] --- [e] 

ग्राफ पराक्रम अलग दिखता है, लेकिन इसकी एक ही संरचना है और अंतिम परिणाम वही है। क्लोन का उपयोग करके आपको थोड़ा कम मानसिक बहीखाता करना पड़ा।

नामित शाखाएं वास्तव में यहां तस्वीर में नहीं आईं क्योंकि वे काफी वैकल्पिक हैं। नामांकित शाखाओं का उपयोग करने के लिए स्विच करने से पहले कई वर्षों तक Mercurial खुद को दो क्लोन का उपयोग करके विकसित किया गया था। हम 'डिफ़ॉल्ट' शाखा के अलावा 'स्थिर' नामक एक शाखा बनाए रखते हैं और 'स्थिर' शाखा के आधार पर हमारी रिलीज करते हैं। अनुशंसित वर्कफ़्लो के विवरण के लिए विकी में standard branching पृष्ठ देखें।

+1

यदि परिवर्तन एक अलग उपयोगकर्ता से आया है, जो रिकॉर्ड किया गया होता, तो क्लोन का उपयोग करना कुछ भी बुरा नहीं है। एक नई सुविधा को दबाते समय अक्सर यह जानना अनिच्छुक होता है कि आपने एक अलग रेपो से ऐसा किया था। एक स्थानीय ब्रांच एक्सटेंशन भी है, जो आपको स्थानीय स्थानीय शाखा देता है। रिपो क्लोनिंग करते समय उपयोगी उच्च लागत (समय/स्थान) से जुड़ा होता है। –

+2

का संदर्भ: 'क्लोन त्वरित प्रयोगों के लिए बहुत अच्छे हैं' - नहीं, वे नहीं हैं! क्या होगा यदि आपके पास रेपो में कुछ हजार फाइलें हैं? क्लोनिंग में उम्र लग जाएगी (किसी भी समय 1 मिनट से ऊपर) जबकि शाखा केवल एक पल स्विचिंग (<1 सेकंड)। अभी भी नामित शाखाओं का उपयोग चेंजलॉग प्रदूषित करेगा। क्या यह एक मृत अंत नहीं है? या मुझे कुछ याद आ रहा है? – seler

+0

ठीक है Seler; अपने मूल तर्क में संशोधन की तरह लगता है; क्लोन अच्छे होते हैं जहां कई पूर्ण प्रतियों का ओवरहेड आपके लिए महत्वपूर्ण नहीं होता है, या जब आप प्रति शाखा अलग-अलग स्थानीय कार्य प्रतियों की लागत को कम करने के लिए एचजी के सिम्लिंक/हार्डलिंक्स का उपयोग कर सकते हैं। –

5

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

29

मुझे लगता है कि आप एक इतिहास में पूरा इतिहास चाहते हैं। अल्पावधि रेपो से बाहर निकलना अल्पकालिक प्रयोगों के लिए है, रिलीज जैसी प्रमुख घटनाएं नहीं।

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

+20

आप आसानी से ऐसी सुविधा शाखा बना सकते हैं: "एचजी अपडेट" अपनी शाखा बिंदु पर, संपादित करें और "एचजी प्रतिबद्ध"। आपने नए विकास की एक अलग रेखा बनाई है - नई गतिविधियां इस शाखा का विस्तार करेंगी। इससे छुटकारा पाने के लिए "एचजी क्लोन-आर" का प्रयोग करें, या इसे "एचजी स्ट्रिप" द्वारा इनलाइन हटा दें। तो कृपया निराश न हों, या अपने फीचर अनुरोधों के साथ Mercurial मेलिंग सूचियों पर आएं। –

+8

ऐसा लगता है कि 'एचजी स्ट्रिप' जो मैं चाहता हूं। ऑनलाइन दस्तावेज दावा शाखाओं को क्यों हटाया नहीं जा सकता है? –

+1

नॉर्मन: आपको Mercurial के साथ विनाशकारी व्यवहार प्राप्त करने के लिए mq या histedit (~ 'git rebase -i') जैसे एक्सटेंशन को सक्षम करना होगा। फिर आप अवांछित शाखाओं को हटा सकते हैं। –

2

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

14

आपको दोनों करना चाहिए।

@ नॉर्मन से स्वीकृत उत्तर के साथ शुरू करें: प्रति रिलीज एक नाम वाली शाखा के साथ एक संग्रह का उपयोग करें।

फिर, इमारत और परीक्षण के लिए प्रति रिलीज शाखा में एक क्लोन है।

एक महत्वपूर्ण नोट यह है कि यदि आप एकाधिक भंडारों का उपयोग करते हैं, तो आपको transplant का उपयोग करके उनके बीच परिवर्तनों को स्थानांतरित करने के लिए उपयोग करना चाहिए क्योंकि 1) यह हैश बदलता है, और 2) यह उन बगों को पेश कर सकता है जो विवादित होने पर पता लगाना बहुत कठिन हैं आपके द्वारा प्रत्यारोपित परिवर्तन और लक्ष्य शाखा के बीच परिवर्तन। आप के बजाय हमेशा की तरह मर्ज करना चाहते हैं (और premerge बिना: हमेशा नेत्रहीन मर्ज का निरीक्षण) है, जो क्या @mg उसके जवाब के अंत में कहा में परिणाम होगा:

ग्राफ पराक्रम अलग दिखता है, लेकिन यह है एक ही संरचना और अंतिम परिणाम वही है।

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

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

लेकिन फिर भी आपको एक क्लोन प्रति शाखा/रिलीज को बनाए रखने की आवश्यकता है जिसे आपको बनाने और परीक्षण करने की आवश्यकता है। यह छोटा है क्योंकि आप hg clone <main repo>#<branch> <branch repo> कर सकते हैं, और फिर शाखा रेपो में hg pull केवल उस शाखा पर नए बदलावों को खींचेंगे (साथ ही पूर्व शाखाओं पर पूर्वजों के परिवर्तनों को विलय कर दिया जाएगा)।

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


यहां इस सेटअप के लिए @ एमजी का उदाहरण सुनाया गया है। प्रारंभ बिंदु:

[a] - [b] 

रिलीज संस्करण के लिए नामित शाखा बनाएं, "1.0" कहें, जब आप अल्फा रिलीज प्राप्त करते हैं। इस पर बग फिक्स प्रतिबद्ध:

[a] - [b] ------------------ [m1] 
     \    /
      (1.0) - [x] - [y] 

(1.0) कोई वास्तविक changeset के बाद से नामित शाखा अस्तित्व में नहीं है जब तक आप प्रतिबद्ध नहीं है। (आप नाम जोड़ने वाली शाखाओं को ठीक तरह से बनाए जाने के लिए टैग जोड़ने जैसे एक छोटी सी प्रतिबद्धता बना सकते हैं।)

मर्ज [m1] इस सेटअप की कुंजी है। एक डेवलपर रिपोजिटरी के विपरीत जहां असीमित संख्या में सिर हो सकते हैं, आप अपने मुख्य रेपो में कई सिर नहीं लेना चाहते हैं (पुरानी, ​​मृत रिलीज शाखा को छोड़कर जैसा कि पहले उल्लेख किया गया है)। इसलिए जब भी आपके पास रिलीज शाखाओं पर नए बदलाव होते हैं, तो आपको उन्हें तुरंत डिफ़ॉल्ट शाखा (या बाद में रिलीज शाखा) में विलय करना होगा। यह गारंटी देता है कि एक रिलीज में किसी भी बग फिक्स को बाद में रिलीज़ में भी शामिल किया गया है।

  ------- [c] - [d] 
     /
[a] - [b] ------------------ [m1] 
     \    /
      (1.0) - [x] - [y] 

और हमेशा की तरह, आप डिफ़ॉल्ट शाखा पर दो सिर मर्ज करने के लिए की जरूरत है::

डिफ़ॉल्ट शाखा पर इस बीच विकास में अगली फिल्म की ओर जारी है

  ------- [c] - [d] ------- 
     /      \ 
[a] - [b] ------------------ [m1] - [m2] 
     \    /
      (1.0) - [x] - [y] 

और यह है 1.0 शाखा क्लोन:

[a] - [b] - (1.0) - [x] - [y] 

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


मुझे उम्मीद है कि उदाहरण मेरे पहले के अंक स्पष्ट करता है।संक्षेप में, इस दृष्टिकोण के फायदे हैं:

  1. एकल आधिकारिक भंडार जिसमें पूर्ण परिवर्तन और संस्करण इतिहास शामिल है।
  2. रिलीज प्रबंधन साफ़ और सरलीकृत।
  3. डेवलपर्स और इंटीग्रेटर के लिए वर्कफ़्लो साफ़ और सरलीकृत।
  4. वर्कफ़्लो पुनरावृत्तियों (कोड समीक्षा) और स्वचालन (स्वत: खाली विलय) की सुविधा प्रदान करें।

अद्यतन एचजी ही does this: main repo डिफ़ॉल्ट और स्थिर शाखाओं होते हैं, और stable repo स्थिर शाखा क्लोन है। यह संस्करण वाली शाखा का उपयोग नहीं करता है, हालांकि, स्थिर शाखा के साथ संस्करण टैग इसके रिलीज प्रबंधन उद्देश्यों के लिए पर्याप्त हैं।

0

मैं वास्तव में संस्करणों के लिए नामित शाखाओं का उपयोग करने के खिलाफ सलाह दूंगा। यह वास्तव में टैग के लिए क्या हैं। नामित शाखाएं लंबे समय तक चलने वाली मोड़ के लिए हैं, जैसे stable शाखा।

तो टैग का उपयोग क्यों न करें? एक बुनियादी उदाहरण:

  • विकास एक भी शाखा पर होता है
  • जब भी कोई रिहाई बनाई गई है, तो आप इसे टैग तदनुसार
  • विकास सिर्फ वहाँ से पर जारी
  • आप कुछ कीड़े को ठीक करने के लिए है (या जो भी हो), आप एक निश्चित रिलीज में, बस अपने टैग को अपडेट करें,

इससे default शाखा, उर्फ ​​पर एक नया, अनाम नाम होगा। एक अज्ञात शाखा, जो एचजी में पूरी तरह ठीक है। फिर आप किसी भी बिंदु पर विलय कर सकते हैं बगफिक्स मुख्य विकास ट्रैक में वापस आ जाता है। नामित शाखाओं की कोई ज़रूरत नहीं है।

+0

यह आपकी प्रक्रिया पर एक बड़ा सौदा निर्भर करता है। एक वेब ऐप, उदाहरण के लिए, एक स्थिर/परीक्षण/डेवेल शाखा पदानुक्रम के साथ अच्छी तरह से काम करता है। डेस्कटॉप सॉफ़्टवेयर का निर्माण करते समय, हमारे पास आमतौर पर विकास शाखा (डिफ़ॉल्ट) के साथ-साथ रखरखाव में एक से तीन (!) विभिन्न शाखाएं होती हैं। भविष्यवाणी करना मुश्किल है कि हमें किसी शाखा की पुनरीक्षण करने की आवश्यकता हो सकती है, और शाखा को प्रमुख.मिनर संस्करण ट्रैक करने के बारे में एक निश्चित लालित्य है। –

संबंधित मुद्दे