जब आप Git मर्ज के बजाय Git छूट का उपयोग करते हैं?


1545

Git rebase बनाम Git मर्ज का उपयोग करने की अनुशंसा कब की जाती है?

क्या मुझे एक सफल छूट के बाद भी विलय करने की आवश्यकता है?




6
उन लोगों के साथ एक समस्या जो रिबास का उपयोग करना पसंद करते हैं, यह है कि यह उन्हें नियमित रूप से अपने कोड को धकेलने से रोकता है। इसलिए स्वच्छ इतिहास चाहते हैं, उन्हें अपना कोड साझा करने से रोकता है, जो मुझे लगता है कि अधिक महत्वपूर्ण है।
static_rtti

9
@static_rtti: यह सिर्फ सच नहीं है। यदि आप इसे अपने परिवर्तनों को नियमित रूप से धकेलने से रोकते हैं तो आप रिबास-आधारित प्रवाह गलत का उपयोग कर रहे हैं।
juzzlin

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

जवाबों:


1135

लघु संस्करण

  • मर्ज एक शाखा में सभी परिवर्तनों को लेता है और उन्हें एक प्रतिबद्ध में दूसरी शाखा में विलय कर देता है।
  • रेबेस कहते हैं कि मैं वह बिंदु चाहता हूं जिस पर मैंने एक नए शुरुआती बिंदु पर जाने के लिए शाखा की है

तो आप एक का उपयोग कब करते हैं?

मर्ज

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

रिबेस

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

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

19
मेरा मानना ​​है कि विलय के बारे में @ Spaaarky21 की धारणा सही नहीं है। यदि आप M में एक शाखा B को मर्ज करते हैं, तो M पर केवल एक ही कमिट होगा (भले ही B में कई कमिट हों), चाहे आप सादे या --squash मर्ज का उपयोग करें। एक माता-पिता के रूप में बी - संदर्भ क्या बी को खत्म कर देगा। एक अच्छा दृश्य यहाँ है: syntevo.com/smartgithg/howtos.html?page=workflows.merge
jspkin

14
@jpeskin वह नहीं है जो मैं देख रहा हूं। मैंने अभी सत्यापित करने के लिए एक त्वरित परीक्षण किया था। एक पाठ फ़ाइल, initएक नया रेपो, addफ़ाइल और के साथ एक निर्देशिका बनाएँ commit। एक नई सुविधा शाखा की जांच करें ( checkout -b feature।) पाठ फ़ाइल को बदलें, प्रतिबद्ध करें और दोहराएं ताकि सुविधा शाखा पर दो नए कमिट हों। तब checkout masterऔर merge feature। में log, मैं मास्टर पर अपनी प्रारंभिक प्रतिबद्धता देखता हूं, उसके बाद दोनों को फीचर से मिला दिया गया। यदि आप merge --squash feature, फीचर को मास्टर में विलय कर चुके हैं , लेकिन प्रतिबद्ध नहीं हैं, तो मास्टर पर एकमात्र नई प्रतिबद्धता वही होगी जो आप स्वयं बनाते हैं।
स्पाकार्की 21

21
@ Spaaarky21 ऐसा लगता है कि हम दोनों आधे सही हैं। जब एक फास्ट-फ़ॉर-फ़ॉर मर्ज संभव होता है (जैसा कि आपके उदाहरण में), git सुविधा शाखा B (या जैसा कि आप सुझाव देते हैं, सभी कमेंट्स को सम्मिलित करने के लिए डिफ़ॉल्ट रूप से शामिल करेंगे) का उपयोग कर सकते हैं - एकल कमिट में संयोजित करने के लिए उपयोग करें)। लेकिन उस मामले में जहां दो अलग-अलग शाखाएं हैं एम और बी जो आप विलय कर रहे हैं, जीआईटी में शाखा बी से सभी व्यक्तिगत कमिट शामिल नहीं होंगे यदि एम में विलय किया गया है (आप उपयोग करते हैं या नहीं --squash)।
jspkin

6
ऐसा क्यों है "(आप अंतरिम सभी को बनाए रखने के बारे में परवाह नहीं है)" इस जवाब में अभी भी एक तरफ? यह '09 में कोई मतलब नहीं था और यह अब कोई मतलब नहीं है। इसके अलावा, निश्चित रूप से आप केवल तब ही रिबेट करना चाहेंगे यदि किसी अन्य डेवलपर ने संबंधित परिवर्तन किए हैं जिनकी आपको आवश्यकता है - यदि वे असंबंधित परिवर्तन करते हैं, तो आपकी सुविधा शाखा को बिना किसी संघर्ष के आसानी से विलय करना चाहिए, और आपका इतिहास मुख्य होगा।
मार्क बूथ

372

यह आसान है। रिबेस के साथ आप अपने काम के नए आधार के रूप में एक और शाखा का उपयोग करने के लिए कहते हैं।

यदि आपके पास, उदाहरण के लिए, एक शाखा है master, तो आप एक नई सुविधा को लागू करने के लिए एक शाखा बनाते हैं, और कहते हैं कि आप इसे नाम देते हैं cool-feature, निश्चित रूप से मास्टर शाखा आपकी नई सुविधा के लिए आधार है।

अब एक निश्चित बिंदु पर आप masterशाखा में लागू नई सुविधा को जोड़ना चाहते हैं । आप बस शाखा में जा सकते हैं masterऔर विलय कर सकते हैं cool-feature:

$ git checkout master
$ git merge cool-feature

लेकिन इस तरह एक नई डमी कमिट जुड़ जाती है। यदि आप स्पेगेटी-इतिहास से बचना चाहते हैं, तो आप रिबेट कर सकते हैं :

$ git checkout cool-feature
$ git rebase master

और फिर इसे मर्ज करें master:

$ git checkout master
$ git merge cool-feature

इस बार, चूंकि विषय शाखा में मास्टर प्लस के समान ही हैं, नई सुविधा के साथ कमिट होते हैं, तो मर्ज सिर्फ एक फास्ट-फॉरवर्ड होगा।


31
but this way a new dummy commit is added, if you want to avoid spaghetti-history- यह कैसा बुरा है?
ア ッ ク

6
इसके अलावा, मर्ज का -no-ff झंडा बहुत उपयोगी है।
एल्डो 'जिओनी' गिएम्बेलुका

3
@ @ Puts ッ ク ア के रूप में उपयोगकर्ता Sean Schofieldइसे एक टिप्पणी में डालता है: "Rebase भी अच्छा है क्योंकि एक बार यू अंततः अंततः मास्टर में उर सामान को मर्ज करता है (जो कि पहले से वर्णित के रूप में तुच्छ है) आपने इसे यूर प्रतिबद्ध इतिहास के" शीर्ष "पर बैठाया है। ऐसी परियोजनाएँ जहाँ सुविधाएँ लिखी जा सकती हैं, लेकिन कई सप्ताह बाद विलय हो जाती हैं, आप उन्हें केवल मास्टर में मर्ज नहीं करना चाहते हैं क्योंकि उन्हें इतिहास में मास्टर तरीके से "भरवां" मिलता है। व्यक्तिगत रूप से मुझे गिट लॉग को देखने और देखने में सक्षम होना पसंद है। "शीर्ष" पर हाल ही की सुविधा सही है। नोट करें कि प्रतिबद्ध तारीखें संरक्षित हैं - रिबास उस जानकारी को नहीं बदलता है। "
एड्रियन बी

4
याद है कि इन सभी शब्दों (- मैं इसे यहाँ दोहरा भालू लगता है merge, rebase, fast-forward, आदि) एक निर्देशित अचक्रीय ग्राफ के विशिष्ट जोड़तोड़ की बात कर रहे। वे मन में उस मानसिक मॉडल के साथ तर्क करना आसान हो जाता है।
रॉय टिंकर

10
@ एल्डो एक विद्रोही इतिहास के बारे में कुछ भी "साफ" या "साफ" नहीं है। यह आम तौर पर गंदी और IMHO भयानक है क्योंकि आपको पता नहीं है कि वास्तव में क्या हुआ था। "सबसे साफ" गिट इतिहास वह है जो वास्तव में हुआ था। :)
मार्नेन लाईबो-कोसर

269

TSamper द्वारा उल्लिखित मेरे अपने उत्तर के पूरक के लिए ,

  • एक रिबेस एक मर्ज से पहले करने के लिए अक्सर एक अच्छा विचार है, क्योंकि विचार यह है कि आप अपनी शाखा में Yउस शाखा के काम को एकीकृत करते हैं जिस Bपर आप विलय करेंगे।
    लेकिन फिर से, विलय करने से पहले, आप अपनी शाखा में किसी भी संघर्ष को हल करते हैं (जैसे: "रिबास", जैसा कि "मेरी शाखा में अपना काम फिर से खेलना है, शाखा से हाल के बिंदु से शुरू करना B)।
    यदि सही ढंग से किया जाता है, तो बाद में आपकी शाखा से विलय होता है। शाखा Bतेजी से आगे बढ़ सकती है।

  • एक मर्ज सीधे गंतव्य शाखा को प्रभावित करता है B, जिसका अर्थ है कि विलय बेहतर रूप से तुच्छ हो, अन्यथा वह शाखा Bएक स्थिर स्थिति में वापस आने के लिए लंबी हो सकती है (आपके लिए सभी संघर्षों को हल करने का समय)


छूट के बाद विलय की बात?

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

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

डिफ़ॉल्ट रूप से एक गिट ट्री जब हमने विलय नहीं किया है और न ही विद्रोह किया है

rebase1

हम रिबासिंग करवाते हैं:

rebase3

यह दूसरा परिदृश्य सभी के बारे में है: मैं मास्टर में नए-फ़ीचर वापस कैसे ला सकता हूं।

मेरी बात, पहले रिबास परिदृश्य का वर्णन करते हुए, सभी को याद दिलाना है कि एक रिबेस को प्रारंभिक कदम के रूप में भी इस्तेमाल किया जा सकता है (जो कि "मास्टर में नया-फीचर वापस लाएं")।
आप नई-फीचर शाखा में पहले मास्टर लाने के लिए रिबास का उपयोग कर सकते हैं: रिबेस नए-फीचर कमिट को फिर से चलाएगा HEAD master, लेकिन फिर भी नए-फीचर ब्रांच में, प्रभावी ढंग से आपकी शाखा को एक पुराने मास्टर कमिट से शुरू करने के लिए स्थानांतरित करेगा HEAD-master
यह आपको अपनी शाखा में किसी भी संघर्ष को हल करने की अनुमति देता है (मतलब, अलगाव में, जबकि मास्टर को समानांतर में विकसित करने के लिए जारी रखने की अनुमति देता है यदि आपका संघर्ष समाधान चरण बहुत लंबा लगता है)।
फिर आप मास्टर में जा सकते हैं और मर्ज कर सकते हैं new-feature(या यदि आप अपने में किए गए कमिट्स को संरक्षित करना चाहते हैं तो रिबास new-featureकर masterसकते हैं)new-feature डाली)।

इसलिए:

  • "रिबेस बनाम मर्ज", पर कहते हैं, एक काम आयात करने के लिए दो तरीके के रूप में देखी जा सकती है master
  • लेकिन "रिबेस तब मर्ज" अलगाव में संघर्ष को पहले हल करने के लिए एक वैध वर्कफ़्लो हो सकता है, फिर अपने काम को वापस लाएं।

17
विद्रोह के बाद मर्ज संघर्षों को हल करने के बिना एक तुच्छ उपवास है।
obecalp

4
@obelcap: वास्तव में, यह एक तरह का विचार है: आप अपने पर्यावरण में सभी समस्या-टकराव (अपनी नई-सुविधा शाखा के भीतर रिबास मास्टर) को लेते हैं, और फिर सह-मास्टर, नए-फीचर को मर्ज करते हैं: 1 पिको-सेकंड (तेज- आगे) यदि मास्टर के पास कोई
संकल्प

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

3
@ जो: मानसिक रूप से, आप कह रहे हैं कि "मेरे किसी भी परिवर्तन (मेरी निजी शाखा में अलगाव में किए गए) को उस अन्य शाखा के शीर्ष पर फिर से खेलना, लेकिन रिबास होने के बाद मुझे मेरी निजी शाखा में छोड़ दें"। यह स्थानीय इतिहास को साफ़ करने का एक अच्छा अवसर है, "चेकपॉइंट कमिट्स", टूटे हुए द्विशताब्दी और गलत दोष परिणामों से बचें। "Git वर्कफ़्लो" देखें: sandofsky.com/blog/git-workflow.html
VonC

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

229

टी एल; डॉ

यदि आपको कोई संदेह है, तो मर्ज का उपयोग करें।

संक्षिप्त जवाब

एक रिबेस और मर्ज के बीच केवल अंतर हैं:

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

तो इसका संक्षिप्त उत्तर यह है कि आप अपने इतिहास को कैसा दिखना चाहते हैं, उसके आधार पर रिबास या मर्ज चुनें

लंबा जवाब

कुछ कारक हैं जिन पर आपको विचार करना चाहिए कि किस ऑपरेशन का उपयोग करना है।

क्या वह शाखा है जिसे आप अपनी टीम के बाहर अन्य डेवलपर्स के साथ साझा कर रहे हैं (जैसे खुला स्रोत, सार्वजनिक)?

यदि हां, तो रिबास न करें। रेबेस शाखा को नष्ट कर देता है और जब तक वे उपयोग नहीं करते हैं, तब तक उन डेवलपर्स के पास टूटी हुई / असंगत रिपॉजिटरी होगीgit pull --rebase । अन्य डेवलपर्स को जल्दी से परेशान करने का यह एक अच्छा तरीका है।

आपकी विकास टीम कितनी कुशल है?

रीबेस एक विनाशकारी ऑपरेशन है। इसका मतलब है, यदि आप इसे सही तरीके से लागू नहीं करते हैं, तो आप प्रतिबद्ध काम खो सकते हैं और / या अन्य डेवलपर के रिपॉजिटरी की स्थिरता को तोड़ सकते हैं।

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

क्या शाखा स्वयं उपयोगी जानकारी का प्रतिनिधित्व करती है

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

मैंने उन टीमों पर भी काम किया है, जिन्होंने शाखा-प्रति-डेवलपर मॉडल का उपयोग किया है (हम सभी वहां मौजूद हैं)। इस मामले में शाखा स्वयं कोई अतिरिक्त जानकारी नहीं देती है (कमिट पहले से ही लेखक के पास है)। रिबासिंग में कोई नुकसान नहीं होगा।

क्या आप किसी भी कारण से मर्ज वापस करना चाहते हैं?

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

क्या आप एक टीम पर काम करते हैं? यदि हां, तो क्या आप इस शाखा में सभी या कुछ भी नहीं लेने के लिए तैयार हैं?

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

आम मिथक

मर्ज इतिहास को नष्ट कर देता है (स्क्वैश कमिट)

मान लें कि आपके पास निम्न मर्ज है:

    B -- C
   /      \
  A--------D

कुछ लोग बताएंगे कि मर्ज "इतिहास" को नष्ट कर देता है क्योंकि यदि आप केवल मास्टर शाखा (ए - डी) के लॉग को देखते हैं, तो आप बी और सी में निहित महत्वपूर्ण प्रतिबद्ध संदेशों को याद करेंगे।

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

रिबेस सुरक्षित / सरल विलय के लिए अनुमति देता है

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

रिबेस कूलर / सेक्सियर / अधिक पेशेवर है

यदि आप "समय बचाने" के लिए उपनाम rmदेना पसंद करते हैं rm -rfतो शायद आपके लिए रिबास है।

मेरे दो सेंट

मैं हमेशा सोचता हूं कि किसी दिन मैं एक ऐसे परिदृश्य में आऊंगा जहां Git rebase एक भयानक उपकरण है जो समस्या को हल करता है। बहुत कुछ मुझे लगता है कि मैं एक ऐसे परिदृश्य में आऊँगा जहाँ Git reflog एक भयानक उपकरण है जो मेरी समस्या को हल करता है। मैंने अब पांच साल के लिए Git के साथ काम किया है। ऐसा नहीं हुआ है।

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

अपडेट (4/2017)

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


5
मान्य उत्तर होना चाहिए।
मिक 378

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

5
मुझे ज्यादातर यह जवाब पसंद है। लेकिन: Rebase एक "साफ" इतिहास नहीं बनाता है। यह एक अधिक रैखिक इतिहास बनाता है, लेकिन यह एक ही बात नहीं है, क्योंकि अब कौन जानता है कि "गंदगी" प्रत्येक प्रतिबद्ध छुपा रही है? सबसे साफ, सबसे साफ गिट इतिहास वह है जो शाखा और निष्ठा रखता है।
मार्नेन लाईबो-कोसर

3
"आम मिथक, आप देखते हैं कमिट्स B और C": जरूरी नहीं !! आप वास्तव में केवल बी और सी को देखते हैं यदि मर्ज एक फास्ट-फॉरवर्ड मर्ज था और यह केवल तभी संभव है, यदि कोई संघर्ष नहीं था। यदि संघर्ष होते हैं, तो आप एक ही प्रतिबद्धता प्राप्त करते हैं! हालाँकि: आप पहले मास्टर को फ़ीचर में मर्ज कर सकते हैं और वहाँ विरोधाभासों को हल कर सकते हैं और यदि आप तब मास्टर में फ़ीचर मर्ज करते हैं, तो आपको कमिट्स B और C मिलते हैं, और (पहले) से एक सिंगल कमिट X आपके इतिहास में फीचर में मर्ज हो जाता है।
जेरेमी बैंक्स

बहुत अच्छी व्याख्या! इसे और बढ़ाना होगा!
प्रातः

185

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

मर्ज आपके कमिट को अनपढ़ नहीं करता है। मेराज इतिहास रचता है! (बस gitk को देखें) रिबेस इतिहास को फिर से लिखता है, जिसे आपने इसे धकेलने के बाद एक खराब चीज है।

मर्ज का उपयोग करें - जब भी आप पहले ही धक्का दे चुके हों, तब रिबास न करें

यहाँ लिनुस '(गिट के लेखक) इसे लेते हैं (अब मेरे अपने ब्लॉग पर होस्ट किया गया है, जैसा कि वेबैक मशीन द्वारा पुनर्प्राप्त किया गया है )। यह वास्तव में अच्छा पढ़ा है।

या आप एक ही विचार के अपने स्वयं के संस्करण को नीचे पढ़ सकते हैं।

मास्टर पर एक शाखा रिबासिंग:

  • एक गलत विचार प्रदान करता है कि कैसे कमिट बनाए गए थे
  • इंटरमीडिएट के एक समूह के साथ प्रदूषित मास्टर करता है जो शायद अच्छी तरह से परीक्षण नहीं किया गया है
  • वास्तव में इन इंटरमीडिएट कमिट पर बिल्ड ब्रेक की शुरूआत कर सकता है क्योंकि मूल विषय शाखा के निर्माण के दौरान और जब इसे विद्रोह किया गया था, तो इसमें बदलाव किए गए थे।
  • कठिन जांच करने के लिए मास्टर में अच्छी जगहें ढूंढता है।
  • पेड़ में अपने कालानुक्रमिक क्रम के साथ संरेखित नहीं करने के लिए समय पर कारण होता है। तो आप देखेंगे कि मास्टर में ए प्रीसैट कमिट बी है, लेकिन प्रतिबद्ध बी पहले लिखा गया था। (क्या?!)
  • अधिक विरोधाभास पैदा करता है, क्योंकि विषय शाखा में व्यक्तिगत रूप से प्रत्येक में मर्ज संघर्ष शामिल हो सकता है जिसे व्यक्तिगत रूप से हल किया जाना चाहिए (प्रत्येक प्रतिबद्ध में क्या हुआ है इसके बारे में इतिहास में झूठ बोलना)।
  • इतिहास का पुनर्लेखन है। यदि आपके द्वारा पुन: लिखी जा रही शाखा को कहीं भी धकेला गया है (स्वयं के अलावा किसी और के साथ साझा किया गया है) तो आपने बाकी सभी लोगों से पंगा ले लिया है जिनके पास आपका इतिहास फिर से लिखा गया है।

इसके विपरीत, विषय शाखा को मास्टर में विलय करना:

  • इतिहास को संरक्षित करता है जहां विषय शाखाएं बनाई गई थीं, जिसमें मास्टर से लेकर विषय शाखा तक कोई भी मर्ज शामिल है ताकि इसे चालू रखने में मदद मिल सके। आप वास्तव में एक सटीक विचार प्राप्त करते हैं कि डेवलपर किस कोड के साथ काम कर रहे थे जब वे निर्माण कर रहे थे।
  • मास्टर एक शाखा है जो अधिकतर मर्जों से बनी होती है, और उनमें से प्रत्येक मर्ज कमिट आम ​​तौर पर इतिहास में 'अच्छे अंक ’होते हैं जो चेक आउट करने के लिए सुरक्षित होते हैं, क्योंकि यही वह विषय है जहां विषय शाखा एकीकृत होने के लिए तैयार थी।
  • विषय शाखा के सभी व्यक्तिगत आवागमन संरक्षित हैं, इस तथ्य सहित कि वे एक विषय शाखा में थे, इसलिए उन परिवर्तनों को अलग करना स्वाभाविक है और आप जहां आवश्यक हो वहां ड्रिल कर सकते हैं।
  • मर्ज संघर्षों को केवल एक बार (मर्ज के बिंदु पर) हल किया जाना है, इसलिए विषय शाखा में किए गए मध्यवर्ती प्रतिबद्ध परिवर्तन को स्वतंत्र रूप से हल करने की आवश्यकता नहीं है।
  • आसानी से कई बार किया जा सकता है। यदि आप अपनी विषय शाखा को समय-समय पर मास्टर करने के लिए एकीकृत करते हैं, तो लोग विषय शाखा पर निर्माण रख सकते हैं, और इसे स्वतंत्र रूप से विलय किया जा सकता है।

3
इसके अलावा, git मर्ज में "--no-ff" (कोई फास्ट-फॉरवर्ड नहीं) विकल्प है जो आपको एक निश्चित मर्ज द्वारा शुरू किए गए सभी परिवर्तनों को वास्तव में आसानी से वापस करने की अनुमति देता है।
टिआगो

3
बस ti इसे और अधिक स्पष्ट करें: आप उस स्थिति का उल्लेख करते हैं 'जब भी आपने पहले धक्का दिया हो' - यह बोल्ड होना चाहिए। लिनुस पोस्ट का लिंक महान है, btw।, इसे स्पष्ट करता है।
honzajde

2
लेकिन अपने विषय शाखा में मास्टर से "अपडेट" करना सबसे अच्छा अभ्यास नहीं है, इससे पहले कि आप पीआर के माध्यम से मास्टर में विषय शाखा को मर्ज करें (अपनी शाखा में विरोधों को सुलझाने के लिए, मास्टर नहीं)? हम ऐसा कर रहे हैं कि अधिकांश विषय शाखाओं में अंतिम प्रतिबद्ध "विषय-शाखा में मर्ज ब्रांच -..." है, लेकिन यहां इसे रिबासिंग की "विशेषता" के रूप में सूचीबद्ध किया गया है और किसी ने भी विलय के लिए इसका उल्लेख नहीं किया है ...?
ProblemsOfSumit

2
@AndrewArnott "अधिकांश विषय शाखाओं को अपनी लक्षित शाखाओं में संघर्ष के बिना विलय करने में सक्षम होना चाहिए" जब 30 शाखाओं पर 20 देव काम कर रहे हों तो यह कैसे संभव होना चाहिए? जब आप आप पर काम कर रहे हों तो मर्ज हो जाएगा - इसलिए निश्चित रूप से आपको पीआर बनाने से पहले लक्ष्य से विषय शाखा को अपडेट करना होगा ... नहीं?
ProblemsOfSumit

3
आमतौर पर, @Sumit नहीं। गिट या तो दिशा को ठीक कर सकते हैं भले ही दोनों शाखाओं में या तो परिवर्तन किए गए हों। केवल जब कोड की एक ही पंक्तियों (या बहुत करीब) को दो शाखाओं में संशोधित किया जाता है, तो आपको संघर्ष मिलेगा। यदि ऐसा किसी भी टीम में अक्सर होता है, तो टीम को इस बात पर पुनर्विचार करना चाहिए कि कैसे वे काम को वितरित करते हैं क्योंकि संघर्षों को हल करना एक कर है और उन्हें धीमा कर देता है।
एंड्रयू आर्नोट

76

TLDR: यह इस बात पर निर्भर करता है कि सबसे महत्वपूर्ण क्या है - एक सुव्यवस्थित इतिहास या विकास के अनुक्रम का सच्चा प्रतिनिधित्व

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

यदि अनुक्रम का सच्चा प्रतिनिधित्व सबसे महत्वपूर्ण है, तो आप बिना पुनरावृत्ति के विलय करेंगे।

मर्ज का अर्थ है: एक नया कमिट बनाएं जो मेरे बदलावों को गंतव्य में विलय कर दे। नोट: इस नई कमिट में दो अभिभावक होंगे - आपकी कमिट के तार से नवीनतम कमिट और आप जिस अन्य शाखा में विलय कर रहे हैं उसकी नवीनतम कमेटी।

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

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

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

एक और समय जब आप रिबास करना चाहते हैं, जब आप ऊपर की तरफ धकेलने से पहले अपनी शाखा से आवागमन से छुटकारा पाना चाहते हैं। उदाहरण के लिए: कमिट जो कुछ डिबगिंग कोड को जल्दी शुरू करते हैं और दूसरे उस कोड को साफ करते हैं। ऐसा करने का एकमात्र तरीका एक इंटरैक्टिव रिबास प्रदर्शन है:git rebase -i <branch/commit/tag>

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

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

अद्यतन 3: Does one still need to merge after a successful rebase?हाँ, आप करते हैं। कारण यह है कि एक रिबेट में अनिवार्य रूप से एक "स्थानांतरण" होता है। जैसा कि मैंने ऊपर कहा है, इन कमिट्स की गणना की जाती है, लेकिन अगर आपके पास ब्रांचिंग के बिंदु से 14 कमिट्स थे, तो यह मानकर कि आपके रिबेस के साथ कुछ भी गलत नहीं होता है, आप 14 कमिट्स आगे होंगे (जिस बिंदु पर आप रिबास कर रहे हैं) रिबास किया जाता है। आपके पास रिबास से पहले एक शाखा थी। आपके पास एक ही लंबाई की एक शाखा होगी। अपने परिवर्तन प्रकाशित करने से पहले आपको अभी भी विलय करने की आवश्यकता है। दूसरे शब्दों में, जितनी बार चाहें उतनी बार रीबेज करें (फिर से, केवल तभी जब आपने अपने परिवर्तनों को ऊपर की ओर नहीं धकेला हो)। पुनर्जन्म के बाद ही विलय करें।


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

1
@mbx git mergeउस --no-ffविकल्प का समर्थन करता है जो इसे मर्ज करने के लिए बाध्य करता है ।
गाविन एस। यानसी

63

विलय को निश्चित रूप से बदलावों को एकीकृत करने का सबसे आसान और सबसे आम तरीका है, यह एकमात्र नहीं है: रिबेस एकीकरण का एक वैकल्पिक साधन है।

मर्ज को थोड़ा बेहतर समझना

जब Git एक मर्ज करता है, तो वह तीन कमिटों को देखता है:

  • (१) सामान्य पूर्वज प्रतिबद्ध। यदि आप किसी परियोजना में दो शाखाओं के इतिहास का पालन करते हैं, तो उनके पास हमेशा कम से कम एक प्रतिबद्ध होता है: इस समय, दोनों शाखाओं में एक ही सामग्री थी और फिर अलग-अलग रूप से विकसित हुई।
  • (२) + (३) प्रत्येक शाखा का समापन बिंदु। एक एकीकरण का लक्ष्य दो शाखाओं की वर्तमान स्थिति को संयोजित करना है। इसलिए, उनके संबंधित नवीनतम संशोधन विशेष रुचि के हैं। इन तीन कमिटों को मिलाने से हम उस एकीकरण में परिणत होंगे, जिसका हम लक्ष्य बना रहे हैं।

फास्ट-फॉरवर्ड या मर्ज कमिट

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

यहां छवि विवरण दर्ज करें

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

यहां छवि विवरण दर्ज करें

बहुत सारे मामलों में, हालांकि, दोनों शाखाएँ व्यक्तिगत रूप से आगे बढ़ीं।

यहां छवि विवरण दर्ज करें

एकीकरण करने के लिए, Git को एक नई कमिट बनानी होगी जिसमें उनके बीच का अंतर हो - मर्ज कमिट।

यहां छवि विवरण दर्ज करें

मानव कमिट और मर्ज कमिट

आम तौर पर, एक इंसान द्वारा सावधानीपूर्वक कमिट बनाई जाती है। यह एक सार्थक इकाई है जो केवल संबंधित परिवर्तनों को लपेटती है और उन्हें टिप्पणी के साथ एनोटेट करती है।

एक मर्ज कमिट थोड़ा अलग है: एक डेवलपर द्वारा बनाए जाने के बजाय, यह गिट द्वारा स्वचालित रूप से बनाया जाता है। और संबंधित परिवर्तनों का एक सेट लपेटने के बजाय, इसका उद्देश्य गाँठ की तरह, दो शाखाओं को जोड़ना है। यदि आप बाद में एक मर्ज ऑपरेशन को समझना चाहते हैं, तो आपको दोनों शाखाओं के इतिहास और संबंधित प्रतिबद्ध ग्राफ पर एक नज़र डालने की आवश्यकता है।

रिबेस के साथ एकीकरण

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

यहां छवि विवरण दर्ज करें

चलो एक रिबेस ऑपरेशन कदम से कदम के माध्यम से चलते हैं। परिदृश्य पिछले उदाहरणों की तरह ही है: हम शाखा-बी से परिवर्तन को शाखा-ए में एकीकृत करना चाहते हैं, लेकिन अब रिबेस का उपयोग करके।

यहां छवि विवरण दर्ज करें

हम इसे तीन चरणों में करेंगे

  1. git rebase branch-A // Synchronises the history with branch-A
  2. git checkout branch-A // Change the current branch to branch-A
  3. git merge branch-B // Merge/take the changes from branch-B to branch-A

सबसे पहले, Git, "पूर्ववत" करेगा, जो शाखा-ए पर होता है, जो लाइनों को शाखा से निकालने के बाद (सामान्य पूर्वजों की प्रतिबद्धता के बाद) हुआ था। हालांकि, निश्चित रूप से, यह उन्हें नहीं छोड़ेगा: इसके बजाय आप उन लोगों के बारे में सोच सकते हैं जो "अस्थायी रूप से बच गए" हैं।

यहां छवि विवरण दर्ज करें

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

यहां छवि विवरण दर्ज करें

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

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

यहां छवि विवरण दर्ज करें

अंत में, आपको एक साफ शाखा शाखा-ए मिलती है जिसमें कोई अवांछित और ऑटो उत्पन्न नहीं होता है।

नोट: भयानक पोस्ट से लिया गया git-towerनुकसान की rebaseभी एक अच्छा एक ही पोस्ट में पढ़ा है।


बहुत शांत आरेखों के लिए +1। मैं हमेशा एक उदाहरण के साथ इसी तरह से एक git प्रवाह उदाहरण का वर्णन करने में सक्षम होना चाहता था।
मिकाईल अब्दुल्लायेव

60

विलय / छूट से पहले:

A <- B <- C    [master]
^
 \
  D <- E       [branch]

के बाद git merge master:

A <- B <- C
^         ^
 \         \
  D <- E <- F

के बाद git rebase master:

A <- B <- C <- D' <- E'

(ए, बी, सी, डी, ई और एफ कमिट हैं)

इस उदाहरण और बहुत अच्छी तरह से Git के बारे में सचित्र जानकारी Git द बेसिक्स ट्यूटोरियल में पाई जा सकती है ।


30

इस वाक्य को यह मिलता है:

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

स्रोत: 3.6 गिट ब्रांचिंग - रिबासिंग, रिबेस बनाम मर्ज


25

यह उत्तर Git Flow के आसपास व्यापक रूप से उन्मुख है । टेबल अच्छा के साथ उत्पन्न हो रही हैं ASCII तालिका जेनरेटर , और इस अद्भुत आदेश (के साथ इतिहास के पेड़ एलियास के रूप में git lg):

git log --graph --abbrev-commit --decorate --date=format:'%Y-%m-%d %H:%M:%S' --format=format:'%C(bold blue)%h%C(reset) - %C(bold cyan)%ad%C(reset) %C(bold green)(%ar)%C(reset)%C(bold yellow)%d%C(reset)%n''          %C(white)%s%C(reset) %C(dim white)- %an%C(reset)'

टेबल्स इतिहास के पेड़ों के साथ अधिक सुसंगत होने के लिए रिवर्स कालानुक्रमिक क्रम में हैं। पहले git mergeऔर git merge --no-ffपहले के बीच का अंतर भी देखें (आप आमतौर पर इसका उपयोग करना चाहते हैं git merge --no-ffक्योंकि यह आपके इतिहास को वास्तविकता के करीब लाता है):

git merge

आदेश:

Time          Branch "develop"             Branch "features/foo"
------- ------------------------------ -------------------------------
15:04   git merge features/foo
15:03                                  git commit -m "Third commit"
15:02                                  git commit -m "Second commit"
15:01   git checkout -b features/foo
15:00   git commit -m "First commit"

परिणाम:

* 142a74a - YYYY-MM-DD 15:03:00 (XX minutes ago) (HEAD -> develop, features/foo)
|           Third commit - Christophe
* 00d848c - YYYY-MM-DD 15:02:00 (XX minutes ago)
|           Second commit - Christophe
* 298e9c5 - YYYY-MM-DD 15:00:00 (XX minutes ago)
            First commit - Christophe

git merge --no-ff

आदेश:

Time           Branch "develop"              Branch "features/foo"
------- -------------------------------- -------------------------------
15:04   git merge --no-ff features/foo
15:03                                    git commit -m "Third commit"
15:02                                    git commit -m "Second commit"
15:01   git checkout -b features/foo
15:00   git commit -m "First commit"

परिणाम:

*   1140d8c - YYYY-MM-DD 15:04:00 (XX minutes ago) (HEAD -> develop)
|\            Merge branch 'features/foo' - Christophe
| * 69f4a7a - YYYY-MM-DD 15:03:00 (XX minutes ago) (features/foo)
| |           Third commit - Christophe
| * 2973183 - YYYY-MM-DD 15:02:00 (XX minutes ago)
|/            Second commit - Christophe
* c173472 - YYYY-MM-DD 15:00:00 (XX minutes ago)
            First commit - Christophe

git merge बनाम git rebase

पहला बिंदु: हमेशा सुविधाओं को विकसित करने में विलय करें, कभी भी सुविधाओं से पुन: विकास न करें । यह रिबासिंग के स्वर्णिम नियम का परिणाम है :

इसका स्वर्णिम नियम git rebaseयह है कि इसे सार्वजनिक शाखाओं पर कभी प्रयोग न करें ।

दूसरे शब्दों में :

कभी भी आपके द्वारा कहीं भी धक्का दिए जाने पर किसी भी चीज़ का खंडन न करें।

मैं व्यक्तिगत रूप से जोड़ूंगा: जब तक कि यह एक सुविधा शाखा नहीं है और आप और आपकी टीम को परिणामों के बारे में पता है

तो git mergeबनाम का प्रश्न git rebaseलगभग केवल सुविधा शाखाओं पर लागू होता है (निम्नलिखित उदाहरणों में, --no-ffहमेशा विलय करते समय उपयोग किया गया है)। ध्यान दें कि चूंकि मुझे यकीन नहीं है कि एक बेहतर समाधान ( एक बहस मौजूद है ), मैं केवल यह बताऊंगा कि दोनों कमांड कैसे व्यवहार करते हैं। मेरे मामले में, मैं git rebaseइसका उपयोग करना पसंद करता हूं क्योंकि यह एक अच्छे इतिहास का पेड़ पैदा करता है :)

सुविधा शाखाओं के बीच

git merge

आदेश:

Time           Branch "develop"              Branch "features/foo"           Branch "features/bar"
------- -------------------------------- ------------------------------- --------------------------------
15:10   git merge --no-ff features/bar
15:09   git merge --no-ff features/foo
15:08                                                                    git commit -m "Sixth commit"
15:07                                                                    git merge --no-ff features/foo
15:06                                                                    git commit -m "Fifth commit"
15:05                                                                    git commit -m "Fourth commit"
15:04                                    git commit -m "Third commit"
15:03                                    git commit -m "Second commit"
15:02   git checkout -b features/bar
15:01   git checkout -b features/foo
15:00   git commit -m "First commit"

परिणाम:

*   c0a3b89 - YYYY-MM-DD 15:10:00 (XX minutes ago) (HEAD -> develop)
|\            Merge branch 'features/bar' - Christophe
| * 37e933e - YYYY-MM-DD 15:08:00 (XX minutes ago) (features/bar)
| |           Sixth commit - Christophe
| *   eb5e657 - YYYY-MM-DD 15:07:00 (XX minutes ago)
| |\            Merge branch 'features/foo' into features/bar - Christophe
| * | 2e4086f - YYYY-MM-DD 15:06:00 (XX minutes ago)
| | |           Fifth commit - Christophe
| * | 31e3a60 - YYYY-MM-DD 15:05:00 (XX minutes ago)
| | |           Fourth commit - Christophe
* | |   98b439f - YYYY-MM-DD 15:09:00 (XX minutes ago)
|\ \ \            Merge branch 'features/foo' - Christophe
| |/ /
|/| /
| |/
| * 6579c9c - YYYY-MM-DD 15:04:00 (XX minutes ago) (features/foo)
| |           Third commit - Christophe
| * 3f41d96 - YYYY-MM-DD 15:03:00 (XX minutes ago)
|/            Second commit - Christophe
* 14edc68 - YYYY-MM-DD 15:00:00 (XX minutes ago)
            First commit - Christophe

git rebase

आदेश:

Time           Branch "develop"              Branch "features/foo"           Branch "features/bar"
------- -------------------------------- ------------------------------- -------------------------------
15:10   git merge --no-ff features/bar
15:09   git merge --no-ff features/foo
15:08                                                                    git commit -m "Sixth commit"
15:07                                                                    git rebase features/foo
15:06                                                                    git commit -m "Fifth commit"
15:05                                                                    git commit -m "Fourth commit"
15:04                                    git commit -m "Third commit"
15:03                                    git commit -m "Second commit"
15:02   git checkout -b features/bar
15:01   git checkout -b features/foo
15:00   git commit -m "First commit"

परिणाम:

*   7a99663 - YYYY-MM-DD 15:10:00 (XX minutes ago) (HEAD -> develop)
|\            Merge branch 'features/bar' - Christophe
| * 708347a - YYYY-MM-DD 15:08:00 (XX minutes ago) (features/bar)
| |           Sixth commit - Christophe
| * 949ae73 - YYYY-MM-DD 15:06:00 (XX minutes ago)
| |           Fifth commit - Christophe
| * 108b4c7 - YYYY-MM-DD 15:05:00 (XX minutes ago)
| |           Fourth commit - Christophe
* |   189de99 - YYYY-MM-DD 15:09:00 (XX minutes ago)
|\ \            Merge branch 'features/foo' - Christophe
| |/
| * 26835a0 - YYYY-MM-DD 15:04:00 (XX minutes ago) (features/foo)
| |           Third commit - Christophe
| * a61dd08 - YYYY-MM-DD 15:03:00 (XX minutes ago)
|/            Second commit - Christophe
* ae6f5fc - YYYY-MM-DD 15:00:00 (XX minutes ago)
            First commit - Christophe

से developएक सुविधा शाखा को

git merge

आदेश:

Time           Branch "develop"              Branch "features/foo"           Branch "features/bar"
------- -------------------------------- ------------------------------- -------------------------------
15:10   git merge --no-ff features/bar
15:09                                                                    git commit -m "Sixth commit"
15:08                                                                    git merge --no-ff develop
15:07   git merge --no-ff features/foo
15:06                                                                    git commit -m "Fifth commit"
15:05                                                                    git commit -m "Fourth commit"
15:04                                    git commit -m "Third commit"
15:03                                    git commit -m "Second commit"
15:02   git checkout -b features/bar
15:01   git checkout -b features/foo
15:00   git commit -m "First commit"

परिणाम:

*   9e6311a - YYYY-MM-DD 15:10:00 (XX minutes ago) (HEAD -> develop)
|\            Merge branch 'features/bar' - Christophe
| * 3ce9128 - YYYY-MM-DD 15:09:00 (XX minutes ago) (features/bar)
| |           Sixth commit - Christophe
| *   d0cd244 - YYYY-MM-DD 15:08:00 (XX minutes ago)
| |\            Merge branch 'develop' into features/bar - Christophe
| |/
|/|
* |   5bd5f70 - YYYY-MM-DD 15:07:00 (XX minutes ago)
|\ \            Merge branch 'features/foo' - Christophe
| * | 4ef3853 - YYYY-MM-DD 15:04:00 (XX minutes ago) (features/foo)
| | |           Third commit - Christophe
| * | 3227253 - YYYY-MM-DD 15:03:00 (XX minutes ago)
|/ /            Second commit - Christophe
| * b5543a2 - YYYY-MM-DD 15:06:00 (XX minutes ago)
| |           Fifth commit - Christophe
| * 5e84b79 - YYYY-MM-DD 15:05:00 (XX minutes ago)
|/            Fourth commit - Christophe
* 2da6d8d - YYYY-MM-DD 15:00:00 (XX minutes ago)
            First commit - Christophe

git rebase

आदेश:

Time           Branch "develop"              Branch "features/foo"           Branch "features/bar"
------- -------------------------------- ------------------------------- -------------------------------
15:10   git merge --no-ff features/bar
15:09                                                                    git commit -m "Sixth commit"
15:08                                                                    git rebase develop
15:07   git merge --no-ff features/foo
15:06                                                                    git commit -m "Fifth commit"
15:05                                                                    git commit -m "Fourth commit"
15:04                                    git commit -m "Third commit"
15:03                                    git commit -m "Second commit"
15:02   git checkout -b features/bar
15:01   git checkout -b features/foo
15:00   git commit -m "First commit"

परिणाम:

*   b0f6752 - YYYY-MM-DD 15:10:00 (XX minutes ago) (HEAD -> develop)
|\            Merge branch 'features/bar' - Christophe
| * 621ad5b - YYYY-MM-DD 15:09:00 (XX minutes ago) (features/bar)
| |           Sixth commit - Christophe
| * 9cb1a16 - YYYY-MM-DD 15:06:00 (XX minutes ago)
| |           Fifth commit - Christophe
| * b8ddd19 - YYYY-MM-DD 15:05:00 (XX minutes ago)
|/            Fourth commit - Christophe
*   856433e - YYYY-MM-DD 15:07:00 (XX minutes ago)
|\            Merge branch 'features/foo' - Christophe
| * 694ac81 - YYYY-MM-DD 15:04:00 (XX minutes ago) (features/foo)
| |           Third commit - Christophe
| * 5fd94d3 - YYYY-MM-DD 15:03:00 (XX minutes ago)
|/            Second commit - Christophe
* d01d589 - YYYY-MM-DD 15:00:00 (XX minutes ago)
            First commit - Christophe

साइड नोट्स

git cherry-pick

जब आपको बस एक विशिष्ट प्रतिबद्ध की आवश्यकता होती है, git cherry-pickतो एक अच्छा समाधान होता है ( -xविकल्प एक पंक्ति को जोड़ता है जो कहता है " (चेरी प्रतिबद्ध से ...) " मूल प्रतिबद्ध संदेश शरीर के लिए, इसलिए यह आमतौर पर इसका उपयोग करने के लिए एक अच्छा विचार है - git log <commit_sha1>देखने के लिए यह):

आदेश:

Time           Branch "develop"              Branch "features/foo"                Branch "features/bar"
------- -------------------------------- ------------------------------- -----------------------------------------
15:10   git merge --no-ff features/bar
15:09   git merge --no-ff features/foo
15:08                                                                    git commit -m "Sixth commit"
15:07                                                                    git cherry-pick -x <second_commit_sha1>
15:06                                                                    git commit -m "Fifth commit"
15:05                                                                    git commit -m "Fourth commit"
15:04                                    git commit -m "Third commit"
15:03                                    git commit -m "Second commit"
15:02   git checkout -b features/bar
15:01   git checkout -b features/foo
15:00   git commit -m "First commit"

परिणाम:

*   50839cd - YYYY-MM-DD 15:10:00 (XX minutes ago) (HEAD -> develop)
|\            Merge branch 'features/bar' - Christophe
| * 0cda99f - YYYY-MM-DD 15:08:00 (XX minutes ago) (features/bar)
| |           Sixth commit - Christophe
| * f7d6c47 - YYYY-MM-DD 15:03:00 (XX minutes ago)
| |           Second commit - Christophe
| * dd7d05a - YYYY-MM-DD 15:06:00 (XX minutes ago)
| |           Fifth commit - Christophe
| * d0d759b - YYYY-MM-DD 15:05:00 (XX minutes ago)
| |           Fourth commit - Christophe
* |   1a397c5 - YYYY-MM-DD 15:09:00 (XX minutes ago)
|\ \            Merge branch 'features/foo' - Christophe
| |/
|/|
| * 0600a72 - YYYY-MM-DD 15:04:00 (XX minutes ago) (features/foo)
| |           Third commit - Christophe
| * f4c127a - YYYY-MM-DD 15:03:00 (XX minutes ago)
|/            Second commit - Christophe
* 0cf894c - YYYY-MM-DD 15:00:00 (XX minutes ago)
            First commit - Christophe

git pull --rebase

मुझे यकीन नहीं है कि मैं इसे डेरेक गोरले से बेहतर समझा सकता हूं ... मूल रूप से, git pull --rebaseइसके बजाय का उपयोग करें git pull:) लेख में क्या गायब है हालांकि, यह है कि आप इसे डिफ़ॉल्ट रूप से सक्षम कर सकते हैं :

git config --global pull.rebase true

git rerere

फिर, अच्छी तरह से यहाँ समझाया । लेकिन सीधे शब्दों में कहें, अगर आप इसे सक्षम करते हैं, तो आपको एक ही संघर्ष को कई बार हल नहीं करना पड़ेगा।


15

प्रो Git किताब पर एक बहुत अच्छी व्याख्या है रिबेसिंग पेज

मूल रूप से एक मर्ज दो कमिट ले जाएगा और उन्हें संयोजित करेगा।

एक रिबेस दो पर आम पूर्वज के पास जाएगा और एक दूसरे के ऊपर परिवर्तन को लागू करेगा। यह एक 'क्लीनर' और अधिक रैखिक इतिहास के लिए बनाता है।

लेकिन जब आप रिबेट करते हैं, तो आप पिछले कमिट्स को छोड़ देते हैं और नए बनाते हैं। इसलिए आपको कभी भी एक रिपॉजिटरी को सार्वजनिक नहीं करना चाहिए। रिपॉजिटरी पर काम करने वाले दूसरे लोग आपसे नफरत करेंगे।

उस कारण से मैं अकेले लगभग विलीन हो गया। 99% समय मेरी शाखाओं में इतना अंतर नहीं होता है, इसलिए यदि संघर्ष होते हैं तो यह केवल एक या दो स्थानों पर होता है।


1
मर्ज कमिट्स को संयोजित नहीं करता है - जो इतिहास को फिर से लिखना होगा। रिबेस ऐसा करता है।
केलीफज

4

Git rebase का इस्तेमाल हिस्ट्री क्लीनर और रिपॉजिटरी स्ट्रक्चर को रैखिक बनाने के लिए ब्रांचिंग रास्तों को बनाने के लिए किया जाता है।

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

रिबेस करने के बाद हमें एक अतिरिक्त कमिटमेंट से भी छुटकारा मिलता है जिसे हम देखते थे कि क्या हम एक सामान्य मर्ज करते हैं।

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


4

यदि आप केवल एक डेवलपर हैं, तो आप स्पष्ट इतिहास रखने के लिए मर्ज के बजाय रिबास का उपयोग कर सकते हैं

यहां छवि विवरण दर्ज करें


3

कुछ व्यावहारिक उदाहरण, कुछ हद तक बड़े पैमाने पर विकास से जुड़े हुए हैं जहां जेरिट का उपयोग समीक्षा और वितरण एकीकरण के लिए किया जाता है:

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

git fetch
git checkout origin/my_feature
git merge origin/master
git commit
git push origin HEAD:refs/for/my_feature

जब मैं एक डिलीवरी कमिट तैयार करता हूं, तो मैं विलीन हो जाता हूं।

git fetch
git checkout origin/master
git merge --squash origin/my_feature
git commit
git push origin HEAD:refs/for/master

जब मेरा डिलीवरी कमिटमेंट किसी भी कारण से एकीकरण में विफल रहता है, तो मैं इसे रिजेक्ट कर देता हूं, और मुझे इसे एक नए दूरस्थ मास्टर की ओर अपडेट करना होगा।

git fetch
git fetch <gerrit link>
git checkout FETCH_HEAD
git rebase origin/master
git push origin HEAD:refs/for/master

3

यह कई बार समझाया गया था कि क्या रिबेस और मर्ज क्या है, लेकिन आपको कब उपयोग करना चाहिए?

आपको रिबास का उपयोग कब करना चाहिए?

  • जब आपने शाखा को धक्का नहीं दिया है / कोई अन्य उस पर काम नहीं कर रहा है
  • आप पूरा इतिहास चाहते हैं
  • आप सभी ऑटो-जनरेट किए गए "मर्ज किए गए" संदेशों से बचना चाहते हैं

जैसे-जैसे गिट रीबेस इतिहास बदलता है। इसलिए आपको इसका उपयोग तब नहीं करना चाहिए जब कोई अन्य उसी शाखा पर काम कर रहा हो / यदि आपने उसे धक्का दिया हो। लेकिन अगर आपके पास एक स्थानीय शाखा है तो आप एक क्लीनर इतिहास रखने के लिए अपनी शाखा को वापस मास्टर में विलय करने से पहले एक मर्ज रिबेस मास्टर कर सकते हैं। ऐसा करने से, मास्टर शाखा में विलय के बाद यह दिखाई नहीं देगा कि आपने मास्टर शाखा में एक शाखा का उपयोग किया है - इतिहास "क्लीनर" है क्योंकि आपके पास ऑटो-जनरेट "मर्ज .." नहीं है, लेकिन फिर भी है "मर्ज किए गए .." के बिना आपके मास्टर ब्रांच में पूरा इतिहास।

सुनिश्चित करें कि, आप git merge feature-branch --ff-onlyयह सुनिश्चित करने के लिए उपयोग करते हैं कि जब आप अपनी सुविधा को मुख्य रूप से मर्ज कर रहे हैं, तो कोई भी एकल कमिट बनाने का कोई विरोध न हो। यह दिलचस्प है यदि आप अपने द्वारा काम करने वाले प्रत्येक कार्य के लिए सुविधा शाखाओं का उपयोग कर रहे हैं जैसा कि आप सुविधा शाखा का इतिहास प्राप्त करते हैं, लेकिन "मेरिट .." प्रतिबद्ध नहीं है

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

आपको मर्ज का उपयोग कब करना चाहिए?

  • जब आपने शाखा को धक्का दिया है / अन्य लोग भी इस पर काम कर रहे हैं
  • आपको पूर्ण इतिहास की आवश्यकता नहीं है
  • बस विलय आपके लिए काफी अच्छा है

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


-4

मैं कब उपयोग git rebaseकरूं? लगभग कभी नहीं, क्योंकि यह इतिहास को फिर से लिखता है। git mergeलगभग हमेशा बेहतर विकल्प है, क्योंकि यह इस बात का सम्मान करता है कि वास्तव में आपकी परियोजना में क्या हुआ है।


1
@benjaminhull धन्यवाद! - मुझे उम्मीद है कि मेरा उत्तर तथ्य आधारित है। IMHO की राय में इस तरह की बहुत कम जगह है: यह एक तथ्य है कि आपका वास्तविक इतिहास खो जाने के बाद जीवन कठिन हो जाता है।
मार्नेन लाईबो-कोसर

1
इस बात से सहमत। मर्ज कभी भी दूषित इतिहास आदि को जन्म नहीं देगा (जब आप अपने धकेल दिए गए कमिट्स को पुनः प्राप्त करते हैं)
surfrider
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.