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