एक गिट उपनिर्देशिका कैसे निकालें और उसमें से एक सबमॉड्यूल बनाएं?


119

मैंने कुछ महीने पहले एक परियोजना शुरू की और एक मुख्य निर्देशिका के भीतर सब कुछ संग्रहीत किया। मेरी मुख्य निर्देशिका "प्रोजेक्ट" में कई उपनिर्देशिकाएँ हैं जिनमें अलग-अलग चीज़ें हैं: प्रोजेक्ट / पेपर में LaTeX प्रोजेक्ट में लिखे गए दस्तावेज़ / sourcecode / RailsApp में मेरे रेल एप्लिकेशन शामिल हैं।

"प्रोजेक्ट" GITified है और "पेपर" और "RailsApp" निर्देशिका दोनों में बहुत सारे कमिट किए गए हैं। अब, जैसा कि मैं अपने "RailsApp" के लिए cruisecontrol.rb का उपयोग करना चाहूंगा, मुझे आश्चर्य है कि अगर इतिहास को खोए बिना "RailsApp" से बाहर एक सबमॉड्यूल बनाने का कोई तरीका है।


2
इसके अलावा एक बहुत अच्छा जवाब: stackoverflow.com/questions/359424/…
Rehno Lindeque

जवाबों:


122

आजकल गीट फ़िल्टर-शाखा का उपयोग करने की तुलना में इसे करने का एक बहुत आसान तरीका है: गिट सबट्री

स्थापना

नोट 1.7.11 के रूप git-subtreeमें git(यदि आप कंट्राब स्थापित करते हैं) का हिस्सा है , तो आप पहले से ही इसे स्थापित कर सकते हैं। आप निष्पादित करके जांच कर सकते हैं git subtree


स्रोत से git-subtree स्थापित करने के लिए (git के पुराने संस्करणों के लिए):

git clone https://github.com/apenwarr/git-subtree.git

cd git-subtree
sudo rsync -a ./git-subtree.sh /usr/local/bin/git-subtree

या अगर आप आदमी पृष्ठों और सभी चाहते हैं

make doc
make install

प्रयोग

एक बड़ा हिस्सा छोटे टुकड़ों में विभाजित करें:

# Go into the project root
cd ~/my-project

# Create a branch which only contains commits for the children of 'foo'
git subtree split --prefix=foo --branch=foo-only

# Remove 'foo' from the project
git rm -rf ./foo

# Create a git repo for 'foo' (assuming we already created it on github)
mkdir foo
pushd foo
git init
git remote add origin git@github.com:my-user/new-project.git
git pull ../ foo-only
git push origin -u master
popd

# Add 'foo' as a git submodule to `my-project`
git submodule add git@github.com:my-user/new-project.git foo

विस्तृत दस्तावेज (मैन पेज) के लिए, कृपया पढ़ें git-subtree.txt


10
गिट्टी उपशीर्षक चट्टानों!
साइमन वुडसाइड

3
लेकिन सबमॉड्यूल्स का उपयोग करने से बचने के लिए git-subtree की बात नहीं है? मेरा मतलब है, आप वास्तव में git-subtree के लेखक हैं (जब तक कि कोई उपनाम टकराव नहीं होता है), लेकिन यह git-subtree जैसा दिखता है, भले ही आप जो आदेश दिखाते हैं वह अभी भी मान्य लगता है। क्या मुझे यह सही समझ रहा है?
ब्लिसोरब्लेड

17
git-subtree अब git का हिस्सा है (यदि आप कंट्रिब स्थापित करते हैं) 1.7.11 के रूप में
जेरेमी

8
अच्छी तरह से git rm -rf ./fooहटाता है foo, HEADलेकिन my-projectपूरा इतिहास फ़िल्टर नहीं करता है । फिर, git submodule add git@github.com:my-user/new-project.git fooकेवल fooएक सबमॉड्यूल से शुरू होता है HEAD। उस संबंध में, स्क्रिप्टिंग filter-branchबेहतर है क्योंकि इसे प्राप्त करने की अनुमति है "ऐसा करें जैसे कि उपडिर शुरू से ही एक उप-विषय था"
ग्रेगरी पकोस्ज़

इसके लिए thx - git सबट्री डॉक्स बस थोड़ा सा चकरा देने वाला, और यह (मेरे लिए) सबसे स्पष्ट रूप से उपयोगी चीज है जो मैं इसके साथ करना चाहता था ...
hwjp

38

चेकआउट गिट फ़िल्टर-शाखा

मैन पेज का Examplesसेक्शन दिखाता है कि कैसे यह सब प्रोजेक्ट में एक सब-डायरेक्टरी को एक्सेप्ट करता है, जबकि यह सभी का इतिहास रखता है और अन्य फाइल्स / डायरेक्ट्रीज़ (जो आप खोज रहे हैं) का इतिहास छोड़ देता है।

रिपोजिटरी को फिर से लिखने के लिए मानो foodir/उसकी परियोजना जड़ हो गई थी, और अन्य सभी इतिहास को छोड़ दें:

   git filter-branch --subdirectory-filter foodir -- --all

इस प्रकार, आप कर सकते हैं, उदाहरण के लिए, एक पुस्तकालय उपनिर्देशिका को अपने स्वयं के भंडार में बदल सकते हैं।
ध्यान दें --कि filter-branchसंशोधन विकल्प से विकल्पों को अलग करता है , और --allसभी शाखाओं और टैग को फिर से लिखना है।


1
इसने मेरे लिए अच्छा काम किया। केवल नीचे की ओर मैंने देखा कि सभी कमिट्स के साथ एक ही मास्टर ब्रांच थी।
ऐसोफैस्पेड 15

@aceofspades: यह एक नकारात्मक पहलू क्यों है?
n

2
मेरे लिए एक git रेपो से कमिट करने का पूरा मुद्दा यह है कि मैं इतिहास को बनाए रखना चाहता हूं।
इस्कॉफैड्स

13

ऐसा करने का एक तरीका उलटा है - सब कुछ हटा दें लेकिन वह फ़ाइल जिसे आप रखना चाहते हैं।

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

उदाहरण के लिए, मेरे पास एक प्रोजेक्ट है जिसमें से मैं tvnamer.pyएक नई रिपॉजिटरी में फाइल निकालना चाहता हूं :

git filter-branch --tree-filter 'for f in *; do if [ $f != "tvnamer.py" ]; then rm -rf $f; fi; done' HEAD

यह git filter-branch --tree-filterप्रत्येक कमिट के माध्यम से जाने, कमांड चलाने और परिणामी निर्देशिका सामग्री को फिर से जोड़ने का उपयोग करता है। यह बेहद विनाशकारी है (इसलिए आपको केवल अपनी रिपॉजिटरी की कॉपी पर ही ऐसा करना चाहिए!), और 300 कमिट और लगभग 20 फाइलों के साथ रिपॉजिटरी पर लगभग 1 मिनट का समय लग सकता है।

उपरोक्त आदेश प्रत्येक संशोधन पर केवल निम्नलिखित शेल-स्क्रिप्ट चलाता है, जिसे आपको निश्चित रूप से संशोधित करना होगा (इसके बजाय अपनी उप-निर्देशिका को बाहर करने के लिए tvnamer.py):

for f in *; do
    if [ $f != "tvnamer.py" ]; then
        rm -rf $f;
    fi;
done

सबसे बड़ी समस्या यह है कि यह सभी प्रतिबद्ध संदेशों को छोड़ देता है, भले ही वे शेष फ़ाइल से असंबंधित हों। स्क्रिप्ट git-remove-blank-commits , इसे ठीक करता है ।।

git filter-branch --commit-filter 'if [ z$1 = z`git rev-parse $3^{tree}` ]; then skip_commit "$@"; else git commit-tree "$@"; fi'

आपको कुछ भी (जो मूल रूप से एक बैकअप है) के साथ फिर से -fचलने वाले बल तर्क का उपयोग करने की आवश्यकता हैfilter-branchrefs/original/

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

फिर से, केवल कभी इसे अपने भंडार की प्रति पर चलाएं! - लेकिन सारांश में, सभी फ़ाइलों को हटाने के लिए लेकिन "thisismyfilename.txt":

git filter-branch --tree-filter 'for f in *; do if [ $f != "thisismyfilename.txt" ]; then rm -rf $f; fi; done' HEAD
git filter-branch -f --commit-filter 'if [ z$1 = z`git rev-parse $3^{tree}` ]; then skip_commit "$@"; else git commit-tree "$@"; fi'

4
git filter-branchहै (आजकल?) एक निर्मित में खाली कमिट्स को हटाने का विकल्प, अर्थात् --prune-emptygit filter-branchइस प्रश्न के उत्तर में एक बेहतर मार्गदर्शिका है: stackoverflow.com/questions/359424/…
Blaisorblade

4

CoolAJ86 और apenwarr दोनों उत्तर बहुत समान हैं। मैं उन दोनों बिट्स को समझने की कोशिश कर रहा था, जो दोनों में से एक से गायब थे। नीचे उनमें से एक संयोजन है।

सबसे पहले Git Bash को विभाजन करने के लिए Git Bash के रूट पर नेविगेट करें। मेरे उदाहरण में यहाँ है~/Documents/OriginalRepo (master)

# move the folder at prefix to a new branch
git subtree split --prefix=SubFolderName/FolderToBeNewRepo --branch=to-be-new-repo

# create a new repository out of the newly made branch
mkdir ~/Documents/NewRepo
pushd ~/Documents/NewRepo
git init
git pull ~/Documents/OriginalRepo to-be-new-repo

# upload the new repository to a place that should be referenced for submodules
git remote add origin git@github.com:myUsername/newRepo.git
git push -u origin master
popd

# replace the folder with a submodule
git rm -rf ./SubFolderName/FolderToBeNewRepo
git submodule add git@github.com:myUsername/newRepo.git SubFolderName/FolderToBeNewRepo
git branch --delete --force to-be-new-repo

नीचे इसके बजाय अनुकूलित-सक्षम नामों के साथ ऊपर की एक प्रति है और इसके बजाय https का उपयोग कर रहा है। रूट फ़ोल्डर अब है~/Documents/_Shawn/UnityProjects/SoProject (master)

# move the folder at prefix to a new branch
git subtree split --prefix=Assets/SoArchitecture --branch=so-package

# create a new repository out of the newly made branch
mkdir ~/Documents/_Shawn/UnityProjects/SoArchitecture
pushd ~/Documents/_Shawn/UnityProjects/SoArchitecture
git init
git pull ~/Documents/_Shawn/UnityProjects/SoProject so-package

# upload the new repository to a place that should be referenced for submodules
git remote add origin https://github.com/Feddas/SoArchitecture.git
git push -u origin master
popd

# replace the folder with a submodule
git rm -rf ./Assets/SoArchitecture
git submodule add https://github.com/Feddas/SoArchitecture.git
git branch --delete --force so-package

3

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

  1. नया रिपॉजिटरी बनाएं।
  2. अपनी पुरानी रिपॉजिटरी के प्रत्येक संशोधन के लिए, अपने मॉड्यूल में परिवर्तन को नए रिपॉजिटरी में मर्ज करें। यह आपके मौजूदा प्रोजेक्ट इतिहास की "कॉपी" बनाएगा।

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

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

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