एक नई, अलग Git रिपॉजिटरी में कई उपनिर्देशिकाओं को अलग करें


135

यह प्रश्न अलग Git रिपॉजिटरी में Detach उपनिर्देशिका पर आधारित है

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

/apps
  /AAA
  /BBB
  /CCC
/libs
  /XXX
  /YYY
  /ZZZ

और मैं इसके बजाय यह चाहूंगा:

/apps
  /AAA
/libs
  /XXX

--subdirectory-filterकरने के लिए तर्क git filter-branchकाम है क्योंकि यह पहली बार यह रन दिए गए निर्देशिका के अलावा सब कुछ से छुटकारा हो जाता है नहीं होगा। मैंने सोचा कि --index-filterसभी अवांछित फ़ाइलों के लिए तर्क का उपयोग करना (यद्यपि थकाऊ) काम करेगा, लेकिन अगर मैं इसे एक से अधिक बार चलाने की कोशिश करता हूं, तो मुझे निम्न संदेश मिलता है:

Cannot create a new backup.
A previous backup already exists in refs/original/
Force overwriting the backup with -f

कोई विचार? TIA

जवाबों:


155

एक उप-सीमा से निपटने और ext glob (kynan के रूप में सुझाए गए) का उपयोग करने के बजाय, यह बहुत सरल दृष्टिकोण आज़माएं:

git filter-branch --index-filter 'git rm --cached -qr --ignore-unmatch -- . && git reset -q $GIT_COMMIT -- apps/AAA libs/XXX' --prune-empty -- --all

जैसा कि उनके टिप्पणी में void.pointer द्वारा उल्लेख किया गया है , यह वर्तमान रिपॉजिटरी को छोड़कर और सब कुछ हटा देगा ।apps/AAAlibs/XXX

प्रून खाली मर्ज कायम है

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

git filter-branch --prune-empty --parent-filter \
'sed "s/-p //g" | xargs -r git show-branch --independent | sed "s/\</-p /g"'

⚠️ चेतावनी : उपरोक्त को GNU संस्करण का उपयोग करना चाहिए sedऔर xargsअन्यथा यह सभी कमियों को xargsविफल कर देगा। brew install gnu-sed findutilsऔर फिर उपयोग करें gsedऔर gxargs:

git filter-branch --prune-empty --parent-filter \
'gsed "s/-p //g" | gxargs git show-branch --independent | gsed "s/\</-p /g"' 

4
इसके अलावा, --ignore-unmatch ध्वज को gm rm को पास किया जाना चाहिए, यह मेरे लिए बहुत पहले कमिट के लिए विफल रहा (अन्यथा रिपॉजिटरी को मेरे मामले में git svn क्लोन के साथ बनाया गया था)
Pontomedon

8
यदि आपके पास मिश्रण में टैग हैं, तो आपको संभवतः --tag-name-filter catअपने मापदंडों को जोड़ना चाहिए
योनातन

16
क्या आप बता सकते हैं कि यह लंबा आदेश क्या कर रहा है?
बुरहान अली

4
मैं सुखद आश्चर्यचकित हूं कि यह पूरी तरह से विंडोज पर गिट बश, का उपयोग कर काम करता है!
दाई

3
@BurhanAli इतिहास की प्रत्येक प्रतिबद्धता के लिए, यह उन सभी फ़ाइलों को हटा रहा है, जिन्हें आप रखना चाहते हैं। जब सब कुछ हो जाता है, तो आप केवल उस इतिहास के साथ, आपके द्वारा निर्दिष्ट पेड़ के हिस्से के साथ छोड़ दिए जाते हैं।
void.pointer

39

सरल git कमांड के साथ मैनुअल स्टेप्स

योजना को अलग-अलग निर्देशिकाओं को अपने स्वयं के भंडार में विभाजित करना है, फिर उन्हें एक साथ विलय करना है। निम्नलिखित मैनुअल चरणों ने geek-to-use स्क्रिप्ट्स को नहीं बल्कि आसानी से समझी जाने वाली आज्ञाओं को नियोजित किया और अतिरिक्त N उप-फ़ोल्डरों को किसी अन्य एकल भंडार में विलय करने में मदद कर सकता है।

फूट डालो

मान लेते हैं कि आपका मूल रेपो है: original_repo

1 - स्प्लिट ऐप्स:

git clone original_repo apps-repo
cd apps-repo
git filter-branch --prune-empty --subdirectory-filter apps master

2 - विभाजन लिबास

git clone original_repo libs-repo
cd libs-repo
git filter-branch --prune-empty --subdirectory-filter libs master

जारी रखें यदि आपके पास 2 से अधिक फ़ोल्डर हैं। अब आपके पास दो नए और अस्थायी गिट रिपॉजिटरी होंगे।

ऐप्स और लिबास को मर्ज करके जीतना

3 - एकदम नया रेपो तैयार करें:

mkdir my-desired-repo
cd my-desired-repo
git init

और आपको कम से कम एक प्रतिबद्ध बनाने की आवश्यकता होगी। यदि निम्नलिखित तीन पंक्तियों को छोड़ दिया जाना चाहिए, तो आपका पहला रेपो आपके रेपो की जड़ के नीचे दिखाई देगा:

touch a_file_and_make_a_commit # see user's feedback
git add a_file_and_make_a_commit
git commit -am "at least one commit is needed for it to work"

अस्थायी फ़ाइल के शुरू mergeहोने के बाद , बाद के अनुभाग में कमांड अपेक्षा के अनुरूप बंद हो जाएगी।

उपयोगकर्ता की प्रतिक्रिया से लेना, जैसे एक यादृच्छिक फ़ाइल जोड़ने के बजाय a_file_and_make_a_commit, आप एक .gitignore, या README.mdआदि जोड़ना चुन सकते हैं ।

4 - पहले एप्स को मर्ज करें:

git remote add apps-repo ../apps-repo
git fetch apps-repo
git merge -s ours --no-commit apps-repo/master # see below note.
git read-tree --prefix=apps -u apps-repo/master
git commit -m "import apps"

अब आपको अपने नए रिपॉजिटरी के अंदर ऐप्स डायरेक्टरी को देखना चाहिए । git logसभी प्रासंगिक ऐतिहासिक प्रतिबद्ध संदेश दिखाना चाहिए।

नोट: जैसा कि क्रिस ने टिप्पणी के नए संस्करण (> = 2.9) के लिए नीचे टिप्पणी की है, आपको इसके --allow-unrelated-historiesसाथ निर्दिष्ट करने की आवश्यकता हैgit merge

5 - उसी तरह से अगले दिन रेपो का विलय करें:

git remote add libs-repo ../libs-repo
git fetch libs-repo
git merge -s ours --no-commit libs-repo/master # see above note.
git read-tree --prefix=libs -u libs-repo/master
git commit -m "import libs"

यदि आपके पास विलय करने के लिए 2 से अधिक प्रतिनिधि हैं, तो जारी रखें।

संदर्भ: गिट के साथ एक और रिपॉजिटरी के एक उपनिर्देशिका को मर्ज करें


4
Git 2.9 के बाद से आपको मर्ज कमांड पर --lo - unrelated-histories का उपयोग करने की आवश्यकता है। अन्यथा यह मेरे लिए अच्छा काम करता है।
क्रिस

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

1
आप एक "प्रारंभिक प्रतिबद्ध" के लिए पहली प्रतिबद्ध का उपयोग कर सकते हैं जो जोड़ता है .gitignoreऔर README.mdफ़ाइलें।
जैक मिलर

2
दुर्भाग्य से यह दृष्टिकोण git merge .. git read-treeकदम में जोड़ी गई फ़ाइलों के लिए ट्रैकिंग-इतिहास को तोड़ने के लिए लगता है , क्योंकि यह उन्हें नए जोड़े गए फ़ाइलों के रूप में रिकॉर्ड करता है और मेरे सभी गिट गाइड उनके पहले के कमिट से संबंध नहीं बनाते हैं।
दाई

1
@ अक्षजाद, कोई विचार नहीं, ईमानदार होना। मैनुअल मर्ज का केंद्र बिंदु नई रेपो बनाने के लिए निर्देशिकाओं का चयन करना और अपनी प्रतिबद्ध इतिहास रखना है। मुझे यकीन नहीं है कि ऐसी स्थिति को कैसे संभाला जाए जहां एक प्रतिबद्ध फाइलों ने dirA, dirB, dirDrop में फाइल की और केवल dirA और dirB को नए रेपो के लिए चुना गया, कमिट इतिहास को मूल से कैसे संबंधित होना चाहिए।
chfw

27

आप filter-branchएक से अधिक बार क्यों चलाना चाहेंगे ? आप यह सब एक स्वीप में कर सकते हैं, इसलिए इसे लागू करने की आवश्यकता नहीं है (ध्यान दें कि extglobकाम करने के लिए आपको अपने शेल में सक्षम होने की आवश्यकता है ):

git filter-branch --index-filter "git rm -r -f --cached --ignore-unmatch $(ls -xd apps/!(AAA) libs/!(XXX))" --prune-empty -- --all

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

इस ऑपरेशन के बाद अवांछित निर्देशिकाओं को इसके बिना सूचीबद्ध के रूप में सूचीबद्ध किया जाएगा git status

$(ls ...)आवश्यक सेंट है extglobअपने बजाय खोल सूचकांक फिल्टर, जो का उपयोग करता है के द्वारा मूल्यांकन किया जाता है shनिर्मित eval(जहां extglobउपलब्ध नहीं है)। देखें कि मैं git में शेल विकल्प कैसे सक्षम करूं? उस पर अधिक जानकारी के लिए।


1
दिलचस्प विचार। मेरे पास एक समान समस्या है, लेकिन इसे काम करने के लिए नहीं मिल सका, देखें stackoverflow.com/questions/8050687/…
manol

यह बहुत ज्यादा है कि मुझे क्या चाहिए, हालांकि मैं अपने रेपो में फ़ाइलों और फ़ोल्डरों दोनों का छिड़काव कर रहा था ... धन्यवाद :)
notlesh

1
एचएम। यहां तक ​​कि एक्सग्लोब के साथ चालू होने पर मैं अपने कोष्ठक के पास एक त्रुटि प्राप्त कर रहा हूं: अप्रत्याशित टोकन के पास सिंटैक्स त्रुटि `('मेरा आदेश ऐसा दिखता है: git फ़िल्टर-शाखा -f --index- फ़िल्टर" git rm -r -f-cached -ignore-unmatch src / css / themes /! (some_theme *) "--prune-empty - --all a ls with src / css / themes /! (some_theme *) अन्य सभी थीमों को वापस कर देता है ताकि extglob दिखाई दे! काम कर रहे हो ...
डाकू

2
@ माइकग्राफ मुझे नहीं लगता कि यह वांछित परिणाम देगा: भागने का शाब्दिक अर्थ होगा "!" अपने रास्ते में आदि।
कीन

1
@ डेविड-स्माइली (अधिक हालिया) उत्तर बहुत समान दृष्टिकोण का उपयोग करता है, लेकिन विशेष रूप से gitआदेशों पर भरोसा करने का लाभ है , और इस प्रकार ls@Bae के रूप में ऑपरेटिंग सिस्टमों में व्याख्या की गई है कि कैसे मतभेदों के लिए अतिसंवेदनशील नहीं है ।
जेरेमी कैनी

20

मेरे अपने सवाल का जवाब यहाँ ... बहुत परीक्षण और त्रुटि के बाद।

मैं git subtreeऔर के संयोजन का उपयोग करके ऐसा करने में कामयाब रहा git-stitch-repo। ये निर्देश निम्न पर आधारित हैं:

सबसे पहले, मैंने उन निर्देशिकाओं को निकाला जिन्हें मैं अपने अलग भंडार में रखना चाहता था:

cd origRepo
git subtree split -P apps/AAA -b aaa
git subtree split -P libs/XXX -b xxx

cd ..
mkdir aaaRepo
cd aaaRepo
git init
git fetch ../origRepo aaa
git checkout -b master FETCH_HEAD

cd ..
mkdir xxxRepo
cd xxxRepo
git init
git fetch ../origRepo xxx
git checkout -b master FETCH_HEAD

मैंने तब एक नया खाली भंडार बनाया, और अंतिम दो को उसमें आयात / सिलाई की:

cd ..
mkdir newRepo
cd newRepo
git init
git-stitch-repo ../aaaRepo:apps/AAA ../xxxRepo:libs/XXX | git fast-import

यह दो शाखाएं बनाता है, master-Aऔर master-B, प्रत्येक एक सिले हुए प्रतिनिधि की सामग्री को पकड़े हुए है। उन्हें गठबंधन और साफ करने के लिए:

git checkout master-A
git pull . master-B
git checkout master
git branch -d master-A 
git branch -d master-B

अब मुझे पूरा यकीन नहीं है कि यह कैसे / कब होता है, लेकिन पहले checkoutऔर बाद में pull, कोड जादुई रूप से मास्टर शाखा में विलीन हो जाता है (यहाँ क्या हो रहा है, इस पर कोई भी जानकारी की सराहना की जाती है!)

ऐसा लगता है कि सब कुछ उम्मीद के मुताबिक काम कर रहा है, सिवाय इसके कि अगर मैं newRepoकमिटेड हिस्ट्री को देखता हूं , तो डुप्लिकेट्स होते हैं, जब बदलाव दोनों को प्रभावित करता है apps/AAAऔर libs/XXX। यदि डुप्लिकेट को हटाने का कोई तरीका है, तो यह सही होगा।


यहां आपको मिले नीट टूल। "चेकआउट" पर अंतर्दृष्टि: "गिट पुल" "गिट भ्रूण और& गिट मर्ज" के समान है। जब से आप "स्थानीय रूप से ला रहे हैं" "भ्रूण" हिस्सा सहज नहीं है। इसलिए मुझे लगता है कि यह चेकआउट कमांड "गिट मर्ज मास्टर-बी" के समान है, जो थोड़ा और अधिक स्पष्ट है। देखें kernel.org/pub/software/scm/git/docs/git-pull.html
phord

1
दुर्भाग्य से आजकल खराब-निर्भरता के कारण गिट-स्टिक-रेपो टूल टूट गया है।
हेनरिक

@ हेनरिक क्या समस्या आप बिल्कुल अनुभव कर रहे थे? यह मेरे लिए काम करता है, हालाँकि मुझे export PERL5LIB="$PERL5LIB:/usr/local/git/lib/perl5/site_perl/"अपने bash config में जोड़ना था ताकि यह Git.pm को खोज सके। फिर मैंने उसे cpan के साथ स्थापित किया।

git subtree addइस कार्य को करने के लिए उपयोग करना संभव है । देखें stackoverflow.com/a/58253979/1894803
laconbass

7

मैं वास्तव में इस समस्या को हल करने के लिए एक गिट फ़िल्टर लिख दिया है। इसका git_filter का शानदार नाम है और यहाँ github में स्थित है:

https://github.com/slobobaby/git_filter

यह उत्कृष्ट libgit2 पर आधारित है।

मुझे कई कमिट्स (~ 100000) के साथ एक बड़े रिपॉजिटरी को विभाजित करने की आवश्यकता थी और गिट फ़िल्टर-शाखा पर आधारित समाधानों को चलने में कई दिन लग गए। git_filter को एक ही काम करने में एक मिनट लगता है।


7

'Git स्प्लिट्स' git एक्सटेंशन का उपयोग करें

git splitsएक bash स्क्रिप्ट है जो कि एक रैपर है git branch-filterजिसे मैंने jkeating के समाधान के आधार पर एक git एक्सटेंशन के रूप में बनाया है ।

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

  1. स्थापित करें git splits
  2. एक स्थानीय शाखा में निर्देशिकाओं को विभाजित करें #change into your repo's directory cd /path/to/repo #checkout the branch git checkout XYZ
    #split multiple directories into new branch XYZ git splits -b XYZ apps/AAA libs/ZZZ

  3. कहीं एक खाली रेपो बनाएं। हम मान लेंगे कि हमने xyzGitHub पर एक खाली रेपो बनाया है जिसमें पथ है:git@github.com:simpliwp/xyz.git

  4. नए रेपो में पुश करें। #add a new remote origin for the empty repo so we can push to the empty repo on GitHub git remote add origin_xyz git@github.com:simpliwp/xyz.git #push the branch to the empty repo's master branch git push origin_xyz XYZ:master

  5. एक नए स्थानीय निर्देशिका में नए बनाए गए रिमोट रेपो को क्लोन करें
    #change current directory out of the old repo cd /path/to/where/you/want/the/new/local/repo #clone the remote repo you just pushed to git clone git@github.com:simpliwp/xyz.git


विभाजन में फ़ाइलों को जोड़ना और उन्हें बाद में अपडेट करना संभव नहीं लगता है, है ना?
एलेक्स

यह टन के साथ मेरे रेपो पर चलने के लिए धीमा लगता है
शिन्टा स्मिथ

git- विभाजन git --index फ़िल्टर का उपयोग करने के लिए लगता है जो कि --subdirectory- फ़िल्टर की तुलना में बहुत धीमा है। कुछ रिपोज के लिए यह अभी भी एक व्यवहार्य विकल्प हो सकता है, लेकिन बड़े रेपो (मल्टीपल गीगाबाइट, 6-डिजिट वाले कमिट) के लिए --index- फ़िल्टर प्रभावी रूप से चलाने के लिए सप्ताह लेता है, यहां तक ​​कि समर्पित क्लाउड हार्डवेयर पर भी।
जोस्टीन कोजनिग्सेन

6
git clone git@example.com:thing.git
cd thing
git fetch
for originBranch in `git branch -r | grep -v master`; do
    branch=${originBranch:7:${#originBranch}}
    git checkout $branch
done
git checkout master

git filter-branch --index-filter 'git rm --cached -qr --ignore-unmatch -- . && git reset -q $GIT_COMMIT -- dir1 dir2 .gitignore' --prune-empty -- --all

git remote set-url origin git@example.com:newthing.git
git push --all

अन्य सभी टिप्पणियों के माध्यम से पढ़ना मुझे सही रास्ते पर ले गया। हालाँकि, आपका समाधान सिर्फ काम करता है। यह सभी शाखाओं का आयात करता है, और कई निर्देशिकाओं के साथ काम करता है! महान!
jschober

1
forके बाद से अन्य समान उत्तर यह शामिल नहीं हैं पाश, स्वीकार करने लायक है। यदि आपके पास अपने क्लोन में प्रत्येक शाखा की एक स्थानीय प्रति नहीं है, तो filter-branchउसके पुनर्लेखन के हिस्से के रूप में उनके लिए कोई खाता नहीं होगा, जो संभावित रूप से अन्य शाखाओं में पेश की गई फ़ाइलों को बाहर कर सकता है, लेकिन अभी तक आपकी वर्तमान शाखा के साथ विलय नहीं हुआ है। (हालांकि यह git fetchकिसी भी शाखाओं पर करने के लायक है, जिसे आपने पहले से जाँच लिया है ताकि यह सुनिश्चित हो सके कि वे चालू रहें।)
जेरेमी कैनी

5

एक आसान समाधान: गिट-फिल्टर-रेपो

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

मौजूदा रिपॉजिटरी में निर्देशिकाओं के सबसेट से एक नया भंडार बनाने के लिए, आप कमांड का उपयोग कर सकते हैं:

git filter-repo --path <file_to_remove>

कई फाइलों / फ़ोल्डरों को छनकर छान लें:

git filter-repo --path keepthisfile --path keepthisfolder/

इसलिए, मूल प्रश्न का उत्तर देने के लिए , git-filter-repo के साथ आपको बस निम्नलिखित कमांड की आवश्यकता होगी:

git filter-repo --path apps/AAA/ --path libs/XXX/

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

3

हाँ। उस चेतावनी को ओवरराइड -fकरने के filter-branchलिए बाद की कॉल पर ध्वज का उपयोग करके बैकअप को अधिलेखित करने के लिए मजबूर करें। :) अन्यथा मुझे लगता है कि आपके पास समाधान है (यानी, एक समय में एक अवांछित निर्देशिका को मिटा दें filter-branch)।


-4

संदेश की तरह रेफरी / मूल में .it निर्देशिका के तहत मौजूद बैकअप को हटा दें। निर्देशिका छिपी हुई है।

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