आप दो गिट रिपोजिटरी को कैसे मर्ज करते हैं?


1619

इस परिदृश्य पर विचार करें:

मैंने अपने स्वयं के गिट रेपो में एक छोटी प्रयोगात्मक परियोजना ए विकसित की है। यह अब परिपक्व हो गया है, और मैं बड़ी परियोजना बी का हिस्सा बनना चाहता हूं, जिसका अपना बड़ा भंडार है। मैं अब A को B की उपनिर्देशिका के रूप में जोड़ना चाहूंगा।

किसी भी इतिहास को खोए बिना, मैं A को B में कैसे मिलाऊं?


8
यदि आप दोनों रिपॉजिटरी को एक में रखने की कोशिश कर रहे हैं, तो दोनों रिपॉजिटरी को रखने की आवश्यकता के बिना, इस प्रश्न पर एक नज़र डालें: stackoverflow.com/questions/13040958/…
फ़्लिम

कस्टम डीआईआर में जिट रेपो को मर्ज करने के लिए सभी कॉमिट को बचाने के लिए stackoverflow.com/a/43340714/1772410
Andrey Izman

जवाबों:


436

एक अन्य रिपॉजिटरी की एक शाखा को आसानी से अपने इतिहास को बनाए रखते हुए एक उपनिर्देशिका के तहत रखा जा सकता है। उदाहरण के लिए:

git subtree add --prefix=rails git://github.com/rails/rails.git master

यह एक एकल कमिट के रूप में दिखाई देगा जहां रेल मास्टर शाखा की सभी फाइलें "रेल्स" डायरेक्टरी में जोड़ी जाती हैं। हालाँकि कमेटी के शीर्षक में पुराने इतिहास के पेड़ का संदर्भ है:

प्रतिबद्ध से 'रेल /' जोड़ें <rev>

जहाँ <rev>SHA-1 कमिट हैश है। आप अभी भी इतिहास देख सकते हैं, कुछ परिवर्तनों को दोष दे सकते हैं।

git log <rev>
git blame <rev> -- README.md

ध्यान दें कि आप निर्देशिका को उपसर्ग यहाँ से नहीं देख सकते हैं क्योंकि यह एक वास्तविक पुरानी शाखा है जो बरकरार है। आपको इसे एक सामान्य फ़ाइल चालन समिति की तरह व्यवहार करना चाहिए: इस तक पहुँचने पर आपको एक अतिरिक्त कूद की आवश्यकता होगी।

# finishes with all files added at once commit
git log rails/README.md

# then continue from original tree
git log <rev> -- README.md

मैन्युअल रूप से ऐसा करने या अन्य उत्तरों में वर्णित इतिहास को फिर से लिखने जैसे अधिक जटिल समाधान हैं।

Git-subtree कमांड आधिकारिक git-contrib का एक हिस्सा है, कुछ पैकेट मैनेजर इसे डिफ़ॉल्ट रूप से स्थापित करते हैं (OS X Homebreer)। लेकिन आपको इसे git के अलावा अपने आप से इंस्टॉल करना पड़ सकता है।


2
यहाँ कैसे Git सबट्री इंस्टॉल करने के निर्देशों (जून 2013 के रूप में) कर रहे हैं: stackoverflow.com/a/11613541/694469 (और मैं प्रतिस्थापित git co v1.7.11.3 के साथ ... v1.8.3)।
काजागमनस

1
नीचे दिए गए उत्तर के बारे में सिर के लिए धन्यवाद। 1.8.4 'सबट्री' के रूप में अभी भी शामिल नहीं है (कम से कम उबुन्टु 12.04 git ppa (ppa: git-core / ppa) पर नहीं)
मैट क्लेन

1
मैं पुष्टि कर सकता हूं कि इसके बाद, git log rails/somefileमर्ज कमिट को छोड़कर उस फ़ाइल के इतिहास को प्रदर्शित नहीं करेगा। जैसा कि @artfulrobot ने सुझाव दिया था, ग्रेग हेगिल के उत्तर की जाँच करें । और आपको git filter-branchउस रेपो पर उपयोग करने की आवश्यकता हो सकती है जिसे आप शामिल करना चाहते हैं।
जिफेंग झांग

6
या एरिक ली की "मर्जिंग टू गेट गिट रिपोजिटरीज़ इनटू वन रिपॉजिटरी विदाउट द फाइल हिस्ट्री" saintgimp.org/2013/01/22/…
झांग

4
जैसा कि दूसरों ने कहा है, git subtreeजो आप सोचते हैं वह नहीं कर सकते हैं! अधिक संपूर्ण समाधान के लिए यहां देखें ।
पॉल ड्रेपर

1906

यदि आप मर्ज करना चाहते project-aमें project-b:

cd path/to/project-b
git remote add project-a path/to/project-a
git fetch project-a --tags
git merge --allow-unrelated-histories project-a/master # or whichever branch you want to merge
git remote remove project-a

से लिया गया: git विभिन्न रिपोजिटरी को मर्ज करता है?

इस विधि ने मेरे लिए बहुत अच्छा काम किया, यह छोटा है और मेरी राय में बहुत क्लीनर है।

मामले में आप रखना चाहते हैं project-aएक उपनिर्देशिका, तो आप उपयोग कर सकते हैं में git-filter-repo( filter-branchहै हतोत्साहित )। ऊपर दिए गए आदेशों से पहले निम्न आदेश चलाएँ:

cd path/to/project-a
git filter-repo --to-subdirectory-filter project-a

2 बड़े रिपोजिटरी को मर्ज करने का एक उदाहरण, उनमें से एक को एक उपनिर्देशिका में रखा गया है: https://gist.github.com/x-yuri/9890ab1079cf4357d6f269d073fd9731

नोट:--allow-unrelated-histories पैरामीटर केवल Git> = 2.9 के बाद से ही मौजूद है। देखें Git - Git मर्ज प्रलेखन / --allow-असंबंधित-इतिहास

अपडेट : --tagsटैग रखने के लिए @jstadler द्वारा सुझाए गए अनुसार जोड़ा गया ।


8
इसने मेरे लिए व्यवसाय किया। .Gitignore फ़ाइल में केवल एक संघर्ष के साथ पहली बार एक आकर्षण की तरह काम किया! इसने प्रतिबद्ध इतिहास को पूरी तरह से संरक्षित किया। अन्य दृष्टिकोणों के मुकाबले बड़ा प्लस - सादगी के अलावा - यह है कि इसके साथ मर्ज किए गए रेपो का एक निरंतर संदर्भ नहीं होना चाहिए। हालाँकि एक बात और ध्यान रखनी चाहिए - यदि आप मेरे जैसे एक iOS डेवलपर हैं - तो लक्ष्य रेपो प्रोजेक्ट फ़ाइल को कार्यक्षेत्र में छोड़ने के लिए बहुत सावधान रहना चाहिए।
मैक्स मैकलॉड

30
धन्यवाद। मेरे लिए काम किया। मुझे एक उप-फ़ोल्डर में मर्ज किए गए निर्देशिका को स्थानांतरित करने की आवश्यकता थी इसलिए उपरोक्त चरणों का पालन करने के बाद मैंने बस इस्तेमाल कियाgit mv source-dir/ dest/new-source-dir
सिड

13
git mergeकदम के साथ यहां विफल रहता है fatal: refusing to merge unrelated histories; डॉक्स--allow-unrelated-histories में बताए अनुसार ठीक करता है ।
ssc जूल

19
--allow-unrelated-historiesgit 2.9 में पेश किया गया था । पहले के संस्करणों में यह डिफ़ॉल्ट व्यवहार था।
डगलस रॉयस

11
छोटा: git fetch /path/to/project-a master; git merge --allow-unrelated-histories FETCH_HEAD
jthill

614

यहां दो संभावित उपाय दिए गए हैं:

submodules

या तो रिपोजिटरी A को किसी बड़े प्रोजेक्ट B में एक अलग निर्देशिका में कॉपी करें, या (शायद बेहतर) क्लोन रिपोजिटरी A को प्रोजेक्ट B में एक उपनिर्देशिका में। फिर इस रिपॉजिटरी को रिपॉजिटरी B के सबमॉड्यूल बनाने के लिए git सबमॉड्यूल का उपयोग करें ।

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

सबट्री मर्ज

आप उप- मर्ज की रणनीति का उपयोग करके किसी प्रोजेक्ट B के उपनिर्देशिका में रिपॉजिटरी A को मर्ज कर सकते हैं । इसका वर्णन सबट्री मर्जिंग और यू मार्कस प्रिंज़ द्वारा किया गया है।

git remote add -f Bproject /path/to/B
git merge -s ours --allow-unrelated-histories --no-commit Bproject/master
git read-tree --prefix=dir-B/ -u Bproject/master
git commit -m "Merge B project as our subdirectory"
git pull -s subtree Bproject master

(विकल्प --allow-unrelated-histories> Git> = 2.9.0 के लिए आवश्यक है।)

या आप एपनवार (एवरी पेनारून) द्वारा गिट सबट्री टूल ( रिपॉजिटरी पर गिटहब) का उपयोग कर सकते हैं , जो उनके ब्लॉग पोस्ट में उदाहरण के लिए घोषित किया गया है। गिट सबमॉडल्स के लिए एक नया विकल्प: गिट उपट्री


मुझे लगता है कि आपके मामले में (ए को बड़ी परियोजना बी का हिस्सा होना चाहिए) सही समाधान सबट्री मर्ज का उपयोग करना होगा


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

55
यह अधूरा है । हां, आपको आवागमन का भार मिलता है, लेकिन वे अब सही रास्तों का उल्लेख नहीं करते हैं। git log dir-B/somefileएक मर्ज को छोड़कर कुछ भी नहीं दिखाएगा। देखें ग्रेग हेगिल का जवाब इस महत्वपूर्ण मुद्दे का संदर्भ देता है।
Artfulrobot

2
महत्वपूर्ण: git pull --no-rebase -s subtree Bproject मास्टर यदि आप ऐसा नहीं करते हैं, और आपने अपने आप को रीबेस करने के लिए सेट कर लिया है, तो आप "ऑब्जेक्ट पार्स नहीं कर सकते" के साथ समाप्त हो जाएंगे। देखें osdir.com/ml/git/2009-07/msg01576.html
एरिक बोमन -

4
यह उत्तर भ्रमित करने वाला हो सकता है क्योंकि इसमें B को मर्ज किए गए सबट्री के रूप में है जब प्रश्न में यह ए था। एक कॉपी और पेस्ट का परिणाम?
vfclists

11
यदि आप केवल दो रिपॉजिटरी को एक साथ गोंद करने की कोशिश कर रहे हैं, तो सबमॉड्यूल्स और सबट्री मर्ज का उपयोग करने के लिए गलत उपकरण है क्योंकि वे सभी फ़ाइल इतिहास को संरक्षित नहीं करते हैं (जैसा कि अन्य टिप्पणीकारों ने नोट किया है)। Stackoverflow.com/questions/13040958/… देखें ।
एरिक ली

194

यदि आप प्रोजेक्ट को अलग से बनाए रखना चाहते हैं, तो सबमॉड्यूल दृष्टिकोण अच्छा है। हालांकि, यदि आप वास्तव में दोनों परियोजनाओं को एक ही भंडार में विलय करना चाहते हैं, तो आपके पास करने के लिए थोड़ा और काम है।

पहली बात यह है git filter-branchकि दूसरी रिपॉजिटरी में सब कुछ के नाम को फिर से लिखने के लिए उपयोग करना होगा जहां आप उन्हें समाप्त करना चाहेंगे। तो बजाय foo.c, bar.html, आप के लिए होता है projb/foo.cऔर projb/bar.html

फिर, आपको निम्नलिखित जैसा कुछ करने में सक्षम होना चाहिए:

git remote add projb [wherever]
git pull projb

git pullएक करना होगा git fetchएक के बाद git merge। कोई संघर्ष नहीं होना चाहिए, अगर आप जिस रिपॉजिटरी को खींच रहे हैं, वह अभी तक एक projb/निर्देशिका नहीं है।

आगे की खोज बताती है कि ऐसा ही कुछ विलय gitkमें किया गया था git। जूनियो सी हामानो इसके बारे में यहाँ लिखते हैं: http://www.mail-archive.com/git@vger.kernel.org/msg03395.html


4
सबरी मर्ज बेहतर समाधान होगा, और इसमें शामिल परियोजना के इतिहास को फिर से लिखने की आवश्यकता नहीं है
जैकब नारबस्की

8
मैं जानना चाहता हूँ कि git filter-branchइसे प्राप्त करने के लिए कैसे उपयोग किया जाए। मैन पेज में यह विपरीत तरीके के बारे में कहता है: सबडिर बनाना / रूट बनना, लेकिन आसपास का दूसरा तरीका नहीं।
आर्टफ्लोरोबॉट

31
यह उत्तर बहुत अच्छा होगा यदि यह समझाया गया कि वांछित परिणाम प्राप्त करने के लिए फ़िल्टर-शाखा का उपयोग कैसे किया जाए
Anentropic

14
मैंने पाया कि यहां फिल्टर-ब्रांच का उपयोग कैसे किया जाता है: stackoverflow.com/questions/4042816/…
डेविड माइनर

3
ग्रेग की रूपरेखा के कार्यान्वयन के लिए इस उत्तर को देखें ।
पॉल ड्रेपर

75

git-subtree अच्छा है, लेकिन यह शायद वह नहीं है जो आप चाहते हैं।

उदाहरण के लिए, यदि projectAB में निर्देशिका बनाई गई है, के बाद git subtree,

git log projectA

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

ग्रेग हेगिल का जवाब सबसे करीब आता है, हालांकि यह वास्तव में पथ को फिर से लिखने के लिए नहीं कहता है।


समाधान आश्चर्यजनक रूप से सरल है।

(1) ए में,

PREFIX=projectA #adjust this

git filter-branch --index-filter '
    git ls-files -s |
    sed "s,\t,&'"$PREFIX"'/," |
    GIT_INDEX_FILE=$GIT_INDEX_FILE.new git update-index --index-info &&
    mv $GIT_INDEX_FILE.new $GIT_INDEX_FILE
' HEAD

नोट: यह इतिहास को फिर से लिखता है, इसलिए यदि आप इस रेपो ए का उपयोग जारी रखना चाहते हैं, तो आप इसे पहले एक फेंकने वाली प्रति को क्लोन (कॉपी) करना चाह सकते हैं।

नोट बेने: आपको उस मामले में sed कमांड के अंदर स्थानापन्न स्क्रिप्ट को संशोधित करना होगा जिसमें आप फ़ाइल नाम या पथ में गैर-अस्की अक्षर (या सफेद वर्ण) का उपयोग करते हैं। उस स्थिति में "ls-files -s" द्वारा निर्मित रिकॉर्ड के अंदर फ़ाइल स्थान उद्धरण चिह्न के साथ शुरू होता है।

(२) फिर बी में, भागो

git pull path/to/A

देखा! आपके पास projectAबी में एक निर्देशिका है। यदि आप चलाते हैं git log projectA, तो आप ए से सभी कमिट देखेंगे।


मेरे मामले में, मुझे दो उपनिर्देशिकाएँ चाहिए थीं, projectAऔर projectB। उस मामले में, मैंने बी को भी चरण (1) किया।


1
ऐसा लगता है कि आपने stackoverflow.com/a/618113/586086 से अपना उत्तर कॉपी कर लिया है ?
एंड्रयू माओ

1
@AndrewMao, मुझे ऐसा लगता है ... मैं वास्तव में याद नहीं कर सकता। मैंने इस स्क्रिप्ट का काफी उपयोग किया है।
पॉल ड्रेपर

6
मैं कहता हूँ कि OS X पर \ t काम नहीं करता है और आपको <टैब>
मुनीब अली

2
"$GIT_INDEX_FILE"उद्धृत किया जाना चाहिए (दो बार), अन्यथा आपका तरीका विफल हो जाएगा यदि उदाहरण में पथ में रिक्त स्थान हैं।
रोब डब्लू

4
आप सोच रहे हैं, तो एक <टैब> डालने के लिए OSX में, आप की जरूरत हैCtrl-V <tab>
केसी

48

यदि दोनों रिपॉजिटरी में एक ही तरह की फाइलें होती हैं (जैसे अलग-अलग प्रोजेक्ट्स के लिए दो रेल रिपॉजिटरी), तो आप अपने रिपॉजिटरी में सेकेंडरी रिपॉजिटरी का डेटा ला सकते हैं:

git fetch git://repository.url/repo.git master:branch_name

और फिर इसे वर्तमान भंडार में मर्ज करें:

git merge --allow-unrelated-histories branch_name

यदि आपका Git संस्करण 2.9 से छोटा है, तो निकालें --allow-unrelated-histories

इसके बाद, टकराव हो सकता है। आप उन्हें उदाहरण के लिए हल कर सकते हैं git mergetoolkdiff3पूरी तरह से कीबोर्ड के साथ उपयोग किया जा सकता है, इसलिए कोड को पढ़ते समय कुछ ही मिनटों में 5 संघर्ष फ़ाइल होती है।

मर्ज समाप्त करना याद रखें:

git commit

25

मर्ज का उपयोग करते समय मैंने इतिहास खो दिया है, इसलिए मैंने रिबेस का उपयोग करना समाप्त कर दिया है क्योंकि मेरे मामले में दो रिपॉजिटरी अलग-अलग हैं जो हर कमेटी में विलय करने के लिए पर्याप्त नहीं हैं:

git clone git@gitorious/projA.git projA
git clone git@gitorious/projB.git projB

cd projB
git remote add projA ../projA/
git fetch projA 
git rebase projA/master HEAD

=> संघर्षों को हल करें, फिर जारी रखें, जितनी बार जरूरत हो ...

git rebase --continue

ऐसा करने से एक परियोजना होती है जिसमें प्रोज से सभी कमिट होते हैं और उसके बाद प्रोज से कमिट किया जाता है


25

मेरे मामले में, मेरे पास एक my-pluginरिपॉजिटरी और एक main-projectरिपॉजिटरी थी, और मैं यह दिखावा करना चाहता my-pluginथा कि हमेशा की pluginsउपनिर्देशिका में विकसित किया गया था main-project

मूल रूप से, मैं my-pluginरिपॉजिटरी के इतिहास को फिर से लिखता हूं ताकि यह दिखाई पड़े कि सब डेवलपमेंट plugins/my-pluginसबडिरेक्ट्री में हुआ है। फिर, मैं के विकास के इतिहास जोड़ा my-pluginमें main-projectइतिहास, और दो पेड़ों को एक साथ मिला दिया गया। चूंकि रिपॉजिटरी plugins/my-pluginमें पहले से कोई निर्देशिका मौजूद नहीं थी main-project, यह एक तुच्छ नो-संघर्ष विलय था। परिणामी रिपॉजिटरी में दोनों मूल परियोजनाओं से सारा इतिहास समाहित था, और इसकी दो जड़ें थीं।

टी एल; डॉ

$ cp -R my-plugin my-plugin-dirty
$ cd my-plugin-dirty
$ git filter-branch -f --tree-filter "zsh -c 'setopt extended_glob && setopt glob_dots && mkdir -p plugins/my-plugin && (mv ^(.git|plugins) plugins/my-plugin || true)'" -- --all
$ cd ../main-project
$ git checkout master
$ git remote add --fetch my-plugin ../my-plugin-dirty
$ git merge my-plugin/master --allow-unrelated-histories
$ cd ..
$ rm -rf my-plugin-dirty

दीर्घ संस्करण

सबसे पहले, my-pluginरिपॉजिटरी की एक प्रति बनाएँ , क्योंकि हम इस रिपॉजिटरी के इतिहास को फिर से लिखने जा रहे हैं।

अब, my-pluginरिपॉजिटरी के रूट पर नेविगेट करें, अपनी मुख्य शाखा (शायद master) की जांच करें, और निम्न कमांड चलाएं। निश्चित रूप से, आपको अपने वास्तविक नामों के लिए my-pluginऔर pluginsजो भी आपके लिए विकल्प होना चाहिए ।

$ git filter-branch -f --tree-filter "zsh -c 'setopt extended_glob && setopt glob_dots && mkdir -p plugins/my-plugin && (mv ^(.git|plugins) plugins/my-plugin || true)'" -- --all

अब स्पष्टीकरण के लिए। हर उस कमिट पर कमांड git filter-branch --tree-filter (...) HEADचलाता है जो इससे (...)पहुंच योग्य है HEAD। ध्यान दें कि यह प्रत्येक प्रतिबद्ध के लिए संग्रहीत डेटा पर सीधे संचालित होता है, इसलिए हमें "वर्किंग डायरेक्टरी", "इंडेक्स", "स्टेजिंग", और इसी तरह की धारणाओं के बारे में चिंता करने की ज़रूरत नहीं है।

यदि आप एक filter-branchकमांड चलाते हैं जो विफल रहता है, तो यह .gitनिर्देशिका में कुछ फ़ाइलों को पीछे छोड़ देगा और अगली बार जब आप कोशिश filter-branchकरेंगे तो इस बारे में शिकायत करेंगे, जब तक कि आप -fविकल्प की आपूर्ति नहीं करते filter-branch

वास्तविक कमांड के लिए, मेरे पास वह सौभाग्य नहीं था bashजो मुझे चाहिए था, इसलिए इसके बजाय मैं एक कमांड निष्पादित zsh -cकरने के लिए उपयोग करता हूं zsh। पहले मैंने extended_globविकल्प सेट किया , जो वह है जो कमांड ^(...)में सिंटैक्स को सक्षम करता है mv, साथ ही glob_dotsविकल्प, जो मुझे ग्लॉब ( .gitignore) के साथ डॉटफ़ाइल्स (जैसे कि ) का चयन करने की अनुमति देता है ^(...)

अगला, मैं एक ही समय में और mkdir -pदोनों को बनाने के लिए कमांड का उपयोग करता हूं ।pluginsplugins/my-plugin

अंत में, मैं zsh"नकारात्मक ग्लोब" सुविधा ^(.git|plugins)का उपयोग करता है रिपॉजिटरी की रूट डायरेक्टरी में सभी फाइलों को .gitऔर नए बनाए गए my-pluginफोल्डर को छोड़कर । (यहां छोड़ना .gitआवश्यक नहीं हो सकता है, लेकिन एक निर्देशिका को अपने आप में स्थानांतरित करने की कोशिश करना एक त्रुटि है।)

मेरी रिपॉजिटरी में, प्रारंभिक कमेटी में कोई फाइल शामिल नहीं थी, इसलिए mvकमांड ने आरंभिक कमिट पर एक त्रुटि लौटा दी (क्योंकि स्थानांतरित करने के लिए कुछ भी उपलब्ध नहीं था)। इसलिए, मैंने एक || trueऐसा जोड़ा जो git filter-branchगर्भपात नहीं करेगा।

--allविकल्प बताता है filter-branchके लिए इतिहास के पुनर्लेखन के लिए सभी भंडार में शाखाओं, और अतिरिक्त --बताने के लिए आवश्यक है gitके बजाय के लिए एक विकल्प के रूप में, फिर से लिखने के लिए शाखाओं के लिए विकल्प सूची का एक हिस्सा के रूप में यह व्याख्या करने के लिए filter-branchही।

अब, अपनी main-projectरिपॉजिटरी में नेविगेट करें और जांच करें कि आप किस शाखा में विलय करना चाहते हैं। my-pluginदूरस्थ के रूप में रिपॉजिटरी की अपनी स्थानीय प्रति (इसके इतिहास में बदलाव के main-projectसाथ) जोड़ें:

$ git remote add --fetch my-plugin $PATH_TO_MY_PLUGIN_REPOSITORY

अब आपके पास अपने प्रतिबद्ध इतिहास में दो असंबंधित पेड़ होंगे, जिन्हें आप अच्छी तरह से उपयोग करके कल्पना कर सकते हैं:

$ git log --color --graph --decorate --all

उन्हें मर्ज करने के लिए, उपयोग करें:

$ git merge my-plugin/master --allow-unrelated-histories

ध्यान दें कि प्री-2.9.0 Git में, --allow-unrelated-historiesविकल्प मौजूद नहीं है। यदि आप इन संस्करणों में से किसी एक का उपयोग कर रहे हैं, तो विकल्प को छोड़ दें: त्रुटि संदेश जो --allow-unrelated-historiesरोकता है वह 2.9.0 में भी जोड़ा गया था

आपके पास कोई मर्ज विरोध नहीं होना चाहिए। यदि आप ऐसा करते हैं, तो इसका मतलब है कि या तो filter-branchकमांड सही तरीके से काम नहीं कर रहा है या पहले से ही एक plugins/my-pluginडायरेक्टरी है main-project

किसी भी भविष्य के योगदानकर्ताओं के लिए एक व्याख्यात्मक प्रतिबद्ध संदेश दर्ज करना सुनिश्चित करें कि दो जड़ों के साथ एक रिपॉजिटरी बनाने के लिए हैकरी क्या चल रही थी।

आप ऊपर दिए गए git logकमांड का उपयोग करते हुए नए कमिट ग्राफ की कल्पना कर सकते हैं, जिसमें दो रूट कमिट होने चाहिए । ध्यान दें कि केवल masterशाखा का विलय किया जाएगा । इसका मतलब यह है कि यदि आपके पास अन्य my-pluginशाखाओं पर महत्वपूर्ण काम है जो आप main-projectपेड़ में विलय करना चाहते हैं , तो आपको my-pluginरिमोट को हटाने से बचना चाहिए जब तक कि आपने ये विलय नहीं किया है। यदि आप नहीं करते हैं, तो उन शाखाओं से आने वाले main-projectभंडार अभी भी रिपॉजिटरी में होंगे, लेकिन कुछ अंतिम कचरा संग्रह के लिए अगम्य और अतिसंवेदनशील होंगे। (इसके अलावा, आपको उन्हें SHA द्वारा संदर्भित करना होगा, क्योंकि किसी रिमोट को हटाने से उसकी रिमोट-ट्रैकिंग शाखाएं हटा दी जाती हैं।)

वैकल्पिक रूप से, आपके द्वारा सब कुछ मर्ज किए जाने के बाद my-plugin, आप my-pluginरिमोट का उपयोग करके निकाल सकते हैं :

$ git remote remove my-plugin

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


मैक ओएस एक्स एल कैपिटन के साथ git --version 2.9.0और पर परीक्षण किया गया zsh --version 5.2। आपकी माइलेज भिन्न हो सकती है।

संदर्भ:


1
कहां --allow-unrelated-historiesसे आ रहे हैं?
12

3
@MarceloFilho चेक करें man git-mergeडिफ़ॉल्ट रूप से, git मर्ज कमांड उन हिस्टरी को मर्ज करने से इंकार करता है जो एक सामान्य पूर्वज साझा नहीं करते हैं। इस विकल्प का उपयोग इस सुरक्षा को ओवरराइड करने के लिए किया जा सकता है जब दो परियोजनाओं के इतिहास को विलय कर दिया जाए जो स्वतंत्र रूप से अपना जीवन शुरू कर रहे थे। जैसा कि एक बहुत ही दुर्लभ अवसर है, डिफ़ॉल्ट रूप से इसे सक्षम करने के लिए कोई कॉन्फ़िगरेशन चर मौजूद नहीं है और जोड़ा नहीं जाएगा।
रेडॉन रोज़बोरो

पर उपलब्ध होना चाहिए git version 2.7.2.windows.1?
xpto

2
@MarceloFilho यह 2.9.0 में जोड़ा गया था, लेकिन पुराने संस्करणों में आपको विकल्प पास नहीं करना चाहिए (यह सिर्फ काम करेगा)। github.com/git/git/blob/…
रेडॉन रोज़बोरो

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

9

मैं दिनों के लिए एक ही काम करने की कोशिश कर रहा हूं, मैं 2.7.2 git का उपयोग कर रहा हूं। सबट्री इतिहास को संरक्षित नहीं करता है।

आप इस पद्धति का उपयोग कर सकते हैं यदि आप फिर से पुरानी परियोजना का उपयोग नहीं करेंगे।

मैं आपको सुझाव दूंगा कि आप B को पहले ब्रांच करें और ब्रांच में काम करें।

यहां बिना शाखा के कदम हैं:

cd B

# You are going to merge A into B, so first move all of B's files into a sub dir
mkdir B

# Move all files to B, till there is nothing in the dir but .git and B
git mv <files> B

git add .

git commit -m "Moving content of project B in preparation for merge from A"


# Now merge A into B
git remote add -f A <A repo url>

git merge A/<branch>

mkdir A

# move all the files into subdir A, excluding .git
git mv <files> A

git commit -m "Moved A into subdir"


# Move B's files back to root    
git mv B/* ./

rm -rf B

git commit -m "Reset B to original state"

git push

यदि आप अब सबडिर ए में किसी भी फाइल को लॉग करते हैं तो आपको पूरा इतिहास मिल जाएगा

git log --follow A/<file>

यह पोस्ट थी जो मुझे ऐसा करने में मदद करती है:

http://saintgimp.org/2013/01/22/merging-two-git-repositories-into-one-repository-without-losing-file-history/


8

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

रेपो ए में, पहले रेपो बी उपलब्ध कराने के लिए निम्न कार्य करें:

git remote add B ../B # Add repo B as a new remote.
git fetch B

अब हम रेपो A में एक नई शाखा (केवल एक प्रतिबद्ध के साथ) बनाते हैं जिसे हम कहते हैं new_b_root। परिणामी कमिट में वे फाइलें होंगी जो रेपो बी की मास्टर शाखा के पहले कमिट में रखी गई थीं, लेकिन उन्हें उपनिर्देशिका में रखा गया था path/to/b-files/

git checkout --orphan new_b_root master
git rm -rf . # Remove all files.
git cherry-pick -n `git rev-list --max-parents=0 B/master`
mkdir -p path/to/b-files
git mv README path/to/b-files/
git commit --date="$(git log --format='%ai' $(git rev-list --max-parents=0 B/master))"

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

अब, हम B/masterनए बनाए गए शीर्ष पर एक नई शाखा बनाएंगे new_b_root। हम नई शाखा कहते हैं b:

git checkout -b b B/master
git rebase -s recursive -Xsubtree=path/to/b-files/ new_b_root

अब, हम अपनी bशाखा को मर्ज करते हैं A/master:

git checkout master
git merge --allow-unrelated-histories --no-commit b
git commit -m 'Merge repo B into repo A.'

अंत में, आप Bदूरस्थ और अस्थायी शाखाओं को हटा सकते हैं:

git remote remove B
git branch -D new_b_root b

अंतिम ग्राफ में इस तरह की संरचना होगी:

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


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

8

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

चेतावनी यह है कि यह केवल प्रत्येक भंडार की 'विकसित' शाखा को ध्यान में रखता है और इसे पूरी तरह से नए भंडार में एक अलग निर्देशिका में विलय कर देता है।

टैग और अन्य शाखाओं को नजरअंदाज किया जाता है - यह वह नहीं हो सकता जो आप चाहते हैं।

स्क्रिप्ट भी सुविधा शाखाओं और टैग को संभालती है - नए प्रोजेक्ट में उनका नाम बदलकर ताकि आप जानते हैं कि वे कहां से आए हैं।

#!/bin/bash
#
################################################################################
## Script to merge multiple git repositories into a new repository
## - The new repository will contain a folder for every merged repository
## - The script adds remotes for every project and then merges in every branch
##   and tag. These are renamed to have the origin project name as a prefix
##
## Usage: mergeGitRepositories.sh <new_project> <my_repo_urls.lst>
## - where <new_project> is the name of the new project to create
## - and <my_repo_urls.lst> is a file contaning the URLs to the respositories
##   which are to be merged on separate lines.
##
## Author: Robert von Burg
##            eitch@eitchnet.ch
##
## Version: 0.3.2
## Created: 2018-02-05
##
################################################################################
#

# disallow using undefined variables
shopt -s -o nounset

# Script variables
declare SCRIPT_NAME="${0##*/}"
declare SCRIPT_DIR="$(cd ${0%/*} ; pwd)"
declare ROOT_DIR="$PWD"
IFS=$'\n'

# Detect proper usage
if [ "$#" -ne "2" ] ; then
  echo -e "ERROR: Usage: $0 <new_project> <my_repo_urls.lst>"
  exit 1
fi


## Script variables
PROJECT_NAME="${1}"
PROJECT_PATH="${ROOT_DIR}/${PROJECT_NAME}"
TIMESTAMP="$(date +%s)"
LOG_FILE="${ROOT_DIR}/${PROJECT_NAME}_merge.${TIMESTAMP}.log"
REPO_FILE="${2}"
REPO_URL_FILE="${ROOT_DIR}/${REPO_FILE}"


# Script functions
function failed() {
  echo -e "ERROR: Merging of projects failed:"
  echo -e "ERROR: Merging of projects failed:" >>${LOG_FILE} 2>&1
  echo -e "$1"
  exit 1
}

function commit_merge() {
  current_branch="$(git symbolic-ref HEAD 2>/dev/null)"
  if [[ ! -f ".git/MERGE_HEAD" ]] ; then
    echo -e "INFO:   No commit required."
    echo -e "INFO:   No commit required." >>${LOG_FILE} 2>&1
  else
    echo -e "INFO:   Committing ${sub_project}..."
    echo -e "INFO:   Committing ${sub_project}..." >>${LOG_FILE} 2>&1
    if ! git commit -m "[Project] Merged branch '$1' of ${sub_project}" >>${LOG_FILE} 2>&1 ; then
      failed "Failed to commit merge of branch '$1' of ${sub_project} into ${current_branch}"
    fi
  fi
}


# Make sure the REPO_URL_FILE exists
if [ ! -e "${REPO_URL_FILE}" ] ; then
  echo -e "ERROR: Repo file ${REPO_URL_FILE} does not exist!"
  exit 1
fi


# Make sure the required directories don't exist
if [ -e "${PROJECT_PATH}" ] ; then
  echo -e "ERROR: Project ${PROJECT_NAME} already exists!"
  exit 1
fi


# create the new project
echo -e "INFO: Logging to ${LOG_FILE}"
echo -e "INFO: Creating new git repository ${PROJECT_NAME}..."
echo -e "INFO: Creating new git repository ${PROJECT_NAME}..." >>${LOG_FILE} 2>&1
echo -e "===================================================="
echo -e "====================================================" >>${LOG_FILE} 2>&1
cd ${ROOT_DIR}
mkdir ${PROJECT_NAME}
cd ${PROJECT_NAME}
git init
echo "Initial Commit" > initial_commit
# Since this is a new repository we need to have at least one commit
# thus were we create temporary file, but we delete it again.
# Deleting it guarantees we don't have conflicts later when merging
git add initial_commit
git commit --quiet -m "[Project] Initial Master Repo Commit"
git rm --quiet initial_commit
git commit --quiet -m "[Project] Initial Master Repo Commit"
echo


# Merge all projects into the branches of this project
echo -e "INFO: Merging projects into new repository..."
echo -e "INFO: Merging projects into new repository..." >>${LOG_FILE} 2>&1
echo -e "===================================================="
echo -e "====================================================" >>${LOG_FILE} 2>&1
for url in $(cat ${REPO_URL_FILE}) ; do

  if [[ "${url:0:1}" == '#' ]] ; then
    continue
  fi

  # extract the name of this project
  export sub_project=${url##*/}
  sub_project=${sub_project%*.git}

  echo -e "INFO: Project ${sub_project}"
  echo -e "INFO: Project ${sub_project}" >>${LOG_FILE} 2>&1
  echo -e "----------------------------------------------------"
  echo -e "----------------------------------------------------" >>${LOG_FILE} 2>&1

  # Fetch the project
  echo -e "INFO:   Fetching ${sub_project}..."
  echo -e "INFO:   Fetching ${sub_project}..." >>${LOG_FILE} 2>&1
  git remote add "${sub_project}" "${url}"
  if ! git fetch --tags --quiet ${sub_project} >>${LOG_FILE} 2>&1 ; then
    failed "Failed to fetch project ${sub_project}"
  fi

  # add remote branches
  echo -e "INFO:   Creating local branches for ${sub_project}..."
  echo -e "INFO:   Creating local branches for ${sub_project}..." >>${LOG_FILE} 2>&1
  while read branch ; do
    branch_ref=$(echo $branch | tr " " "\t" | cut -f 1)
    branch_name=$(echo $branch | tr " " "\t" | cut -f 2 | cut -d / -f 3-)

    echo -e "INFO:   Creating branch ${branch_name}..."
    echo -e "INFO:   Creating branch ${branch_name}..." >>${LOG_FILE} 2>&1

    # create and checkout new merge branch off of master
    if ! git checkout -b "${sub_project}/${branch_name}" master >>${LOG_FILE} 2>&1 ; then failed "Failed preparing ${branch_name}" ; fi
    if ! git reset --hard ; then failed "Failed preparing ${branch_name}" >>${LOG_FILE} 2>&1 ; fi
    if ! git clean -d --force ; then failed "Failed preparing ${branch_name}" >>${LOG_FILE} 2>&1 ; fi

    # Merge the project
    echo -e "INFO:   Merging ${sub_project}..."
    echo -e "INFO:   Merging ${sub_project}..." >>${LOG_FILE} 2>&1
    if ! git merge --allow-unrelated-histories --no-commit "remotes/${sub_project}/${branch_name}" >>${LOG_FILE} 2>&1 ; then
      failed "Failed to merge branch 'remotes/${sub_project}/${branch_name}' from ${sub_project}"
    fi

    # And now see if we need to commit (maybe there was a merge)
    commit_merge "${sub_project}/${branch_name}"

    # relocate projects files into own directory
    if [ "$(ls)" == "${sub_project}" ] ; then
      echo -e "WARN:   Not moving files in branch ${branch_name} of ${sub_project} as already only one root level."
      echo -e "WARN:   Not moving files in branch ${branch_name} of ${sub_project} as already only one root level." >>${LOG_FILE} 2>&1
    else
      echo -e "INFO:   Moving files in branch ${branch_name} of ${sub_project} so we have a single directory..."
      echo -e "INFO:   Moving files in branch ${branch_name} of ${sub_project} so we have a single directory..." >>${LOG_FILE} 2>&1
      mkdir ${sub_project}
      for f in $(ls -a) ; do
        if  [[ "$f" == "${sub_project}" ]] ||
            [[ "$f" == "." ]] ||
            [[ "$f" == ".." ]] ; then
          continue
        fi
        git mv -k "$f" "${sub_project}/"
      done

      # commit the moving
      if ! git commit --quiet -m  "[Project] Move ${sub_project} files into sub directory" ; then
        failed "Failed to commit moving of ${sub_project} files into sub directory"
      fi
    fi
    echo
  done < <(git ls-remote --heads ${sub_project})


  # checkout master of sub probject
  if ! git checkout "${sub_project}/master" >>${LOG_FILE} 2>&1 ; then
    failed "sub_project ${sub_project} is missing master branch!"
  fi

  # copy remote tags
  echo -e "INFO:   Copying tags for ${sub_project}..."
  echo -e "INFO:   Copying tags for ${sub_project}..." >>${LOG_FILE} 2>&1
  while read tag ; do
    tag_ref=$(echo $tag | tr " " "\t" | cut -f 1)
    tag_name_unfixed=$(echo $tag | tr " " "\t" | cut -f 2 | cut -d / -f 3)

    # hack for broken tag names where they are like 1.2.0^{} instead of just 1.2.0
    tag_name="${tag_name_unfixed%%^*}"

    tag_new_name="${sub_project}/${tag_name}"
    echo -e "INFO:     Copying tag ${tag_name_unfixed} to ${tag_new_name} for ref ${tag_ref}..."
    echo -e "INFO:     Copying tag ${tag_name_unfixed} to ${tag_new_name} for ref ${tag_ref}..." >>${LOG_FILE} 2>&1
    if ! git tag "${tag_new_name}" "${tag_ref}" >>${LOG_FILE} 2>&1 ; then
      echo -e "WARN:     Could not copy tag ${tag_name_unfixed} to ${tag_new_name} for ref ${tag_ref}"
      echo -e "WARN:     Could not copy tag ${tag_name_unfixed} to ${tag_new_name} for ref ${tag_ref}" >>${LOG_FILE} 2>&1
    fi
  done < <(git ls-remote --tags --refs ${sub_project})

  # Remove the remote to the old project
  echo -e "INFO:   Removing remote ${sub_project}..."
  echo -e "INFO:   Removing remote ${sub_project}..." >>${LOG_FILE} 2>&1
  git remote rm ${sub_project}

  echo
done


# Now merge all project master branches into new master
git checkout --quiet master
echo -e "INFO: Merging projects master branches into new repository..."
echo -e "INFO: Merging projects master branches into new repository..." >>${LOG_FILE} 2>&1
echo -e "===================================================="
echo -e "====================================================" >>${LOG_FILE} 2>&1
for url in $(cat ${REPO_URL_FILE}) ; do

  if [[ ${url:0:1} == '#' ]] ; then
    continue
  fi

  # extract the name of this project
  export sub_project=${url##*/}
  sub_project=${sub_project%*.git}

  echo -e "INFO:   Merging ${sub_project}..."
  echo -e "INFO:   Merging ${sub_project}..." >>${LOG_FILE} 2>&1
  if ! git merge --allow-unrelated-histories --no-commit "${sub_project}/master" >>${LOG_FILE} 2>&1 ; then
    failed "Failed to merge branch ${sub_project}/master into master"
  fi

  # And now see if we need to commit (maybe there was a merge)
  commit_merge "${sub_project}/master"

  echo
done


# Done
cd ${ROOT_DIR}
echo -e "INFO: Done."
echo -e "INFO: Done." >>${LOG_FILE} 2>&1
echo

exit 0

आप इसे http://paste.ubuntu.com/11732805 से भी प्राप्त कर सकते हैं

पहले प्रत्येक रिपॉजिटरी में URL के साथ एक फाइल बनाएं, जैसे:

git@github.com:eitchnet/ch.eitchnet.parent.git
git@github.com:eitchnet/ch.eitchnet.utils.git
git@github.com:eitchnet/ch.eitchnet.privilege.git

फिर स्क्रिप्ट को प्रोजेक्ट का नाम और स्क्रिप्ट का रास्ता बताएं:

./mergeGitRepositories.sh eitchnet_test eitchnet.lst

स्क्रिप्ट में बहुत सारी टिप्पणियां हैं जो यह बताती हैं कि यह क्या करता है।


पाठकों को एक उत्तर देने के बजाय, उत्तर को यहां पोस्ट करें (उर्फ संपादित करें जो आपने उस टिप्पणी में कहा था)।
josliber

1
ज़रूर, बस यह बेहतर है कि अपने आप को न दोहराएं ... =)
eitch

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

गजब का! विंडोज बैश प्रॉम्प्ट पर काम नहीं किया, लेकिन यह ubuntu चलाने वाला एक वैग्रंट बॉक्स बनाकर त्रुटिपूर्ण रूप से चला। क्या समय बचाने वाला!
xverges

सेवा के लिए खुश होना =)
9

7

मुझे पता है कि यह इस तथ्य के बाद लंबा है, लेकिन मैं यहां पाए गए अन्य उत्तरों से खुश नहीं था, इसलिए मैंने यह लिखा:

me=$(basename $0)

TMP=$(mktemp -d /tmp/$me.XXXXXXXX)
echo 
echo "building new repo in $TMP"
echo
sleep 1

set -e

cd $TMP
mkdir new-repo
cd new-repo
    git init
    cd ..

x=0
while [ -n "$1" ]; do
    repo="$1"; shift
    git clone "$repo"
    dirname=$(basename $repo | sed -e 's/\s/-/g')
    if [[ $dirname =~ ^git:.*\.git$ ]]; then
        dirname=$(echo $dirname | sed s/.git$//)
    fi

    cd $dirname
        git remote rm origin
        git filter-branch --tree-filter \
            "(mkdir -p $dirname; find . -maxdepth 1 ! -name . ! -name .git ! -name $dirname -exec mv {} $dirname/ \;)"
        cd ..

    cd new-repo
        git pull --no-commit ../$dirname
        [ $x -gt 0 ] && git commit -m "merge made by $me"
        cd ..

    x=$(( x + 1 ))
done

2
यह ठीक वही है जिसकी मुझे तलाश थी। धन्यवाद! हालाँकि, मुझे लाइन 22 को बदलना था:if [[ $dirname =~ ^.*\.git$ ]]; then
हीमैन

2
^। * blarg $ बेकार लालची है RE। कहने के लिए बेहतर है .blarg $ और फ्रंट एंकर को छोड़ दें।
जेट्टेरो

7

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


1
आपका समाधान केवल नए रिपॉजिटरी के लिए अच्छी तरह से काम करता है, लेकिन फाइल संघर्ष के साथ रेपो को दूसरे में कैसे मर्ज किया जाए?
एंड्रे इज़मैन

6

मेरे पास एक समान चुनौती थी, लेकिन मेरे मामले में, हमने रेपो ए में कोडबेस का एक संस्करण विकसित किया था, फिर उत्पाद के नए संस्करण के लिए एक नए रेपो, रेपो बी में क्लोन किया। रेपो ए में कुछ बग्स को ठीक करने के बाद, हमें रेपो बी में बदलाव की आवश्यकता थी।

  1. रेपो बी में एक रिमोट जोड़ना जो रेपो ए की ओर इशारा करता है (जीआईटी रिमोट ऐड ...)
  2. वर्तमान शाखा को खींचना (हम बग फिक्स के लिए मास्टर का उपयोग नहीं कर रहे थे) (git खींचो RemoteForRepoA बगफिक्सबॉच)
  3. धक्का मारना मथना

एक इलाज काम किया :)


5

@Smar के समान लेकिन फ़ाइल सिस्टम पथ का उपयोग करता है, PRIMARY और SECONDARY में सेट किया गया है:

PRIMARY=~/Code/project1
SECONDARY=~/Code/project2
cd $PRIMARY
git remote add test $SECONDARY && git fetch test
git merge test/master

तब आप मैन्युअल रूप से मर्ज करते हैं।

( अनार मनफोव द्वारा पोस्ट से अनुकूलित )


5

2 रिपोजिशन मर्ज करना

git clone ssh://<project-repo> project1
cd project1
git remote add -f project2 project2
git merge --allow-unrelated-histories project2/master
git remote rm project2

delete the ref to avoid errors
git update-ref -d refs/remotes/project2/master

4

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

अब, .git/info/graftsनिम्नलिखित सामग्री के साथ बनाएँ :

<commit-id of master> <list of commit ids of all parents>

भागो git filter-branch -- head^..head head^2..head head^3..head। यदि आपकी तीन से अधिक शाखाएँ हैं, तो जितनी शाखाएँ हैं, उतने ही जोड़ दें head^n..head। टैग अपडेट करने के लिए, संलग्न करें --tag-name-filter cat। हमेशा इसे न जोड़ें, क्योंकि इससे कुछ कमिट्स का पुनर्लेखन हो सकता है। विवरण के लिए फ़िल्टर-शाखा का मैन पेज देखें , "ग्राफ्ट्स" खोजें।

अब, आपकी अंतिम प्रतिबद्धता में सही माता-पिता जुड़े हुए हैं।


1
रुको, आप एक ही प्रतिबद्ध में तीन परियोजनाओं का विलय क्यों करना चाहेंगे?
स्टीव बेनेट

मैंने रिपॉजिटरी, रिपॉजिटरी-क्लाइंट और मॉडलर के साथ अलग-अलग गिट प्रोजेक्ट्स के रूप में शुरुआत की। सहकर्मियों के लिए यह कठिन था, इसलिए मैंने उन्हें एक एकल परियोजना में शामिल किया। यह सुनिश्चित करने में सक्षम होने के लिए कि नई परियोजना की "जड़" तीन अन्य परियोजनाओं से उत्पन्न हुई है, मैं एकल मर्ज कमिटमेंट करना चाहता था ।
कोपरपोर

4

B के भीतर A को मर्ज करने के लिए:

1) प्रोजेक्ट ए में

git fast-export --all --date-order > /tmp/ProjectAExport

2) परियोजना में बी

git checkout -b projectA
git fast-import --force < /tmp/ProjectAExport

इस शाखा में वे सभी ऑपरेशन करें जो आपको करने और उन्हें करने की आवश्यकता है।

ग) फिर गुरु के पास और दो शाखाओं के बीच एक शास्त्रीय विलय:

git checkout master
git merge projectA

2

यह फ़ंक्शन रिमोट रेपो को स्थानीय रेपो डायर में क्लोन करेगा, विलय के बाद सभी कमिट सहेजे git logजाएंगे , मूल कमिट और उचित रास्ते दिखाए जाएंगे:

function git-add-repo
{
    repo="$1"
    dir="$(echo "$2" | sed 's/\/$//')"
    path="$(pwd)"

    tmp="$(mktemp -d)"
    remote="$(echo "$tmp" | sed 's/\///g'| sed 's/\./_/g')"

    git clone "$repo" "$tmp"
    cd "$tmp"

    git filter-branch --index-filter '
        git ls-files -s |
        sed "s,\t,&'"$dir"'/," |
        GIT_INDEX_FILE="$GIT_INDEX_FILE.new" git update-index --index-info &&
        mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE"
    ' HEAD

    cd "$path"
    git remote add -f "$remote" "file://$tmp/.git"
    git pull "$remote/master"
    git merge --allow-unrelated-histories -m "Merge repo $repo into master" --edit "$remote/master"
    git remote remove "$remote"
    rm -rf "$tmp"
}

कैसे इस्तेमाल करे:

cd current/package
git-add-repo https://github.com/example/example dir/to/save

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

repo="https://github.com/example/example"
path="$(pwd)"

tmp="$(mktemp -d)"
remote="$(echo "$tmp" | sed 's/\///g' | sed 's/\./_/g')"

git clone "$repo" "$tmp"
cd "$tmp"

GIT_ADD_STORED=""

function git-mv-store
{
    from="$(echo "$1" | sed 's/\./\\./')"
    to="$(echo "$2" | sed 's/\./\\./')"

    GIT_ADD_STORED+='s,\t'"$from"',\t'"$to"',;'
}

# NOTICE! This paths used for example! Use yours instead!
git-mv-store 'public/index.php' 'public/admin.php'
git-mv-store 'public/data' 'public/x/_data'
git-mv-store 'public/.htaccess' '.htaccess'
git-mv-store 'core/config' 'config/config'
git-mv-store 'core/defines.php' 'defines/defines.php'
git-mv-store 'README.md' 'doc/README.md'
git-mv-store '.gitignore' 'unneeded/.gitignore'

git filter-branch --index-filter '
    git ls-files -s |
    sed "'"$GIT_ADD_STORED"'" |
    GIT_INDEX_FILE="$GIT_INDEX_FILE.new" git update-index --index-info &&
    mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE"
' HEAD

GIT_ADD_STORED=""

cd "$path"
git remote add -f "$remote" "file://$tmp/.git"
git pull "$remote/master"
git merge --allow-unrelated-histories -m "Merge repo $repo into master" --edit "$remote/master"
git remote remove "$remote"
rm -rf "$tmp"

नोटिस
पथ के माध्यम से बदलता है sed, इसलिए सुनिश्चित करें कि यह विलय के बाद उचित पथ में स्थानांतरित हो गया है। पैरामीटर केवल Git> = 2.9 के बाद से ही मौजूद है।
--allow-unrelated-histories


1

मेरे द्वारा सुझाया गया सर्वोत्तम संभव समाधान है।

git subtree add --prefix=MY_PROJECT git://github.com/project/my_project.git master

1

मैं परियोजनाओं को थोड़ा मैन्युअल रूप से मर्ज करता हूं, जो मुझे मर्ज संघर्ष से निपटने की आवश्यकता से बचने की अनुमति देता है।

पहले, दूसरे प्रोजेक्ट से फ़ाइलों में कॉपी करें, हालांकि आप उन्हें चाहते हैं।

cp -R myotherproject newdirectory
git add newdirectory

इतिहास में अगला पुल

git fetch path_or_url_to_other_repo

बताइए कि आखिरी पड़ी हुई चीज के इतिहास में विलय करने की बात कहिए

echo 'FETCH_HEAD' > .git/MERGE_HEAD

हालांकि अब आप सामान्य रूप से प्रतिबद्ध होंगे

git commit

0

मैं एक छोटी परियोजना को एक बड़े के उपनिर्देशिका में स्थानांतरित करना चाहता था। चूंकि मेरे छोटे से प्रोजेक्ट में कई कमिट नहीं थे, इसलिए मैंने इस्तेमाल किया git format-patch --output-directory /path/to/patch-dir। फिर बड़े प्रोजेक्ट पर, मैंने इस्तेमाल किया git am --directory=dir/in/project /path/to/patch-dir/*

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

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