मेरे मामले में, मेरे पास एक 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। आपकी माइलेज भिन्न हो सकती है।
संदर्भ: