इतिहास को संरक्षित करते हुए फ़ाइलों को एक git रेपो से दूसरे (क्लोन नहीं) में कैसे स्थानांतरित किया जाए


483

हमारे Git रिपॉजिटरीज़ एक एकल राक्षस SVN रिपॉजिटरी के कुछ हिस्सों के रूप में शुरू हुईं जहाँ व्यक्तिगत प्रोजेक्ट्स में से प्रत्येक का अपना पेड़ ऐसा था:

project1/branches
        /tags
        /trunk
project2/branches
        /tags
        /trunk

जाहिर है, फ़ाइलों को एक से दूसरे में ले जाना बहुत आसान था svn mv। लेकिन Git में, प्रत्येक परियोजना के लिए अपने स्वयं भंडार में है, और आज मैं से एक उपनिर्देशिका को स्थानांतरित करने के लिए कहा गया था project2करने के लिए project1। मैंने कुछ इस तरह किया:

$ git clone project2 
$ cd project2
$ git filter-branch --subdirectory-filter deeply/buried/java/source/directory/A -- --all
$ git remote rm origin  # so I don't accidentally overwrite the repo ;-)
$ mkdir -p deeply/buried/different/java/source/directory/B
$ for f in *.java; do 
>  git mv $f deeply/buried/different/java/source/directory/B
>  done
$ git commit -m "moved files to new subdirectory"
$ cd ..
$
$ git clone project1
$ cd project1
$ git remote add p2 ../project2
$ git fetch p2
$ git branch p2 remotes/p2/master
$ git merge p2 # --allow-unrelated-histories for git 2.9+
$ git remote rm p2
$ git push

लेकिन यह बहुत जटिल लगता है। क्या इस तरह की चीज को सामान्य रूप से करने का एक बेहतर तरीका है? या क्या मैंने सही तरीका अपनाया है?

ध्यान दें कि इसमें इतिहास को एक मौजूदा रिपॉजिटरी में विलय करना शामिल है, बजाय इसके कि एक दूसरे के हिस्से से एक नया स्टैंडअलोन रिपॉजिटरी बनाया जाए ( जैसा कि पहले के प्रश्न में है )।


1
यह मेरे लिए एक उचित दृष्टिकोण की तरह लगता है; मैं आपके तरीके को बेहतर बनाने के लिए कोई स्पष्ट तरीका नहीं सोच सकता। यह अच्छा है कि Git वास्तव में यह आसान बनाता है (मैं उदाहरण के लिए, तोड़फोड़ में विभिन्न रिपॉजिटरी के बीच फ़ाइलों की निर्देशिका को स्थानांतरित करने का प्रयास नहीं करना चाहूंगा )।
ग्रेग हेवगिल सेप

1
@ebneter - मैंने शेल स्क्रिप्ट का उपयोग करके मैन्युअल रूप से (एक svn रेपो से दूसरे में इतिहास स्थानांतरित) किया है। मूल रूप से मैंने इतिहास (अलग-अलग, संदेश लॉग्स) को एक विशेष रिपॉजिटरी में विशेष फाइलों / डायरियों से दोहराया है।
एडम मोंसेन

1
मुझे आश्चर्य है कि आप git fetch p2 && git merge p2इसके बजाय क्यों नहीं करते हैं git fetch p2 && git branch .. && git merge p2? संपादित करें: ठीक है, ऐसा लगता है कि आप P2 नाम की नई शाखा में परिवर्तन प्राप्त करना चाहते हैं, वर्तमान शाखा नहीं।
लेकेनस्टीएन

1
क्या डायरेक्टरी संरचना को नष्ट करने से बचाव करने के लिए कोई तरीका नहीं है? फ़ाइल मिटाने और फ़ाइल क्रिएशन से भरे एक बड़े पैमाने पर "git mv" कदम का परिणाम है।
एडवर्ड फॉक

1
ध्यान दें कि git के रूप में 2.9 असंबंधित इतिहास को विलय करना डिफ़ॉल्ट रूप से अस्वीकृत है। इसे काम करने के लिए, इसे काम --allow-unrelated-historiesकरने के लिए अंतिम में जोड़ें git merge
स्कॉट बेरेवोएट्स

जवाबों:


55

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

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


1
क्या होगा यदि आपकी फ़ाइल कई निर्देशिकाओं के माध्यम से चली गई है, और अब एक में रहती है - क्या उपनिर्देशिका-फ़िल्टर अभी भी काम करेगा? (यानी मैं मान रहा हूँ कि अगर मैं सिर्फ एक फ़ाइल को स्थानांतरित करना चाहता हूं, तो मैं इसे अपनी उपनिर्देशिका में स्थानांतरित कर सकता हूं और यह काम करेगा?)
rogerdpack

1
@rogerdpack: नहीं, यह नाम बदलने के माध्यम से फ़ाइल का पालन नहीं करेगा। मेरा मानना ​​है कि यह उस बिंदु पर बनाया गया होगा, जिसे चयनित उपनिर्देशिका में स्थानांतरित किया गया था। आप बस एक फ़ाइल का चयन करना चाहते हैं, पर एक नजर है --index-filterमें filter-branchमैनपेज।
Cascabel

8
क्या कोई नाम है जिसका मैं नाम बदल सकता हूं?
नाइट वॉरियर

मुझे लगता है कि इतिहास को बनाए रखना और उस पर अंकुश लगाना गिट के मुख्य बिंदुओं में से एक है।
आर्टबर्कर्ट

287

यदि आपका इतिहास समझदार है, तो आप कमिट को पैच के रूप में ले सकते हैं और उन्हें नए भंडार में लागू कर सकते हैं:

cd repository
git log --pretty=email --patch-with-stat --reverse --full-index --binary -- path/to/file_or_folder > patch
cd ../another_repository
git am --committer-date-is-author-date < ../repository/patch 

या एक लाइन में

git log --pretty=email --patch-with-stat --reverse -- path/to/file_or_folder | (cd /path/to/new_repository && git am --committer-date-is-author-date)

( Exherbo के डॉक्स से लिया गया )


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

8
मैंने विकल्प जोड़े हैं ताकि बाइनरी फाइलें (छवियों की तरह) भी ठीक से माइग्रेट हो जाएं git log --pretty=email --patch-with-stat --full-index --binary --reverse -- client > patch:। AFAICT समस्याओं के बिना काम करता है।
इमैनुएल टौजरी

35
लागू कदम पर मैंने --committer-date-is-author-dateफ़ाइलों को स्थानांतरित करने की तारीख के बजाय मूल प्रतिबद्ध तारीख को संरक्षित करने के विकल्प का उपयोग किया ।
darrenmc

6
मर्ज इतिहास में "am" कमांड को तोड़ता है। आप ऊपर git लॉग कमांड में "-m --first-parent" जोड़ सकते हैं, फिर इसने मेरे लिए काम किया।
गैबोर लिप्टेक

6
@ डैनियल गोल्डन मैं उन फ़ाइलों के साथ समस्या को ठीक करने में कामयाब रहा, जिन्हें स्थानांतरित कर दिया गया है (जो कि बग का परिणाम है git log, ताकि यह दोनों --followऔर --reverseसही तरीके से काम न करे )। मैंने इस उत्तर का उपयोग किया है , और यहाँ एक पूरी स्क्रिप्ट है जिसका उपयोग मैं अब फ़ाइलों को स्थानांतरित करने के लिए करता हूँ
tsayen

75

फ़ाइल या फ़ोल्डर को एक Git रिपॉजिटरी से दूसरे में स्थानांतरित करने के लिए विभिन्न तरीकों की कोशिश करने के बाद, केवल वही जो मज़बूती से काम करता है, नीचे उल्लिखित है।

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

पहला चरण

  1. रिपॉजिटरी ए की एक प्रतिलिपि बनाएं क्योंकि निम्न चरण इस प्रतिलिपि में बड़े बदलाव करते हैं जिन्हें आपको धक्का नहीं देना चाहिए!

    git clone --branch <branch> --origin origin --progress \
      -v <git repository A url>
    # eg. git clone --branch master --origin origin --progress \
    #   -v https://username@giturl/scm/projects/myprojects.git
    # (assuming myprojects is the repository you want to copy from)
    
  2. इसमें cd

    cd <git repository A directory>
    #  eg. cd /c/Working/GIT/myprojects
    
  3. गलती से कोई दूरस्थ परिवर्तन करने से बचने के लिए मूल रिपॉजिटरी से लिंक को हटा दें (जैसे धक्का देकर)

    git remote rm origin
    
  4. अपने इतिहास और फ़ाइलों के माध्यम से जाओ, कुछ भी जो निर्देशिका में नहीं है को हटा दें। 1. परिणाम निर्देशिका 1 की सामग्री का भंडार ए के आधार पर उगल दिया गया है।

    git filter-branch --subdirectory-filter <directory> -- --all
    # eg. git filter-branch --subdirectory-filter subfolder1/subfolder2/FOLDER_TO_KEEP -- --all
    
  5. केवल एकल फ़ाइल के लिए: जो कुछ बचा है उससे गुजरें और वांछित फ़ाइल को छोड़कर सब कुछ हटा दें। (आपको उन फ़ाइलों को हटाने की आवश्यकता हो सकती है जो आप एक ही नाम और प्रतिबद्ध के साथ नहीं चाहते हैं।)

    git filter-branch -f --index-filter \
    'git ls-files -s | grep $'\t'FILE_TO_KEEP$ |
    GIT_INDEX_FILE=$GIT_INDEX_FILE.new \
    git update-index --index-info && \
    mv $GIT_INDEX_FILE.new $GIT_INDEX_FILE || echo "Nothing to do"' --prune-empty -- --all
    # eg. FILE_TO_KEEP = pom.xml to keep only the pom.xml file from FOLDER_TO_KEEP
    

स्टेज दो

  1. सफाई कदम

    git reset --hard
    
  2. सफाई कदम

    git gc --aggressive
    
  3. सफाई कदम

    git prune
    

आप इन फ़ाइलों को रिपॉजिटरी B में एक निर्देशिका में आयात करना चाहते हैं, रूट नहीं:

  1. उस निर्देशिका को बनाओ

    mkdir <base directory>             eg. mkdir FOLDER_TO_KEEP
    
  2. फ़ाइलों को उस निर्देशिका में ले जाएँ

    git mv * <base directory>          eg. git mv * FOLDER_TO_KEEP
    
  3. उस निर्देशिका में फ़ाइलें जोड़ें

    git add .
    
  4. अपने परिवर्तन करें और हम इन फ़ाइलों को नए भंडार में मर्ज करने के लिए तैयार हैं

    git commit
    

स्टेज तीन

  1. यदि आपके पास पहले से कोई नहीं है, तो रिपॉजिटरी B की एक प्रति बनाएँ

    git clone <git repository B url>
    # eg. git clone https://username@giturl/scm/projects/FOLDER_TO_KEEP.git
    

    (संभालने के लिए FOLDER_TO_KEEP उस नए रिपॉजिटरी का नाम है जिसे आप कॉपी कर रहे हैं)

  2. इसमें cd

    cd <git repository B directory>
    #  eg. cd /c/Working/GIT/FOLDER_TO_KEEP
    
  3. रिपॉजिटरी A में रिपॉजिटरी B में एक शाखा के रूप में एक दूरस्थ कनेक्शन बनाएँ

    git remote add repo-A-branch <git repository A directory>
    # (repo-A-branch can be anything - it's just an arbitrary name)
    
    # eg. git remote add repo-A-branch /c/Working/GIT/myprojects
    
  4. इस शाखा से खींचो (केवल उस निर्देशिका को जिसमें आप स्थानांतरित करना चाहते हैं) भंडार बी में।

    git pull repo-A-branch master --allow-unrelated-histories
    

    पुल फ़ाइलों और इतिहास दोनों को कॉपी करता है। नोट: आप एक पुल के बजाय मर्ज का उपयोग कर सकते हैं, लेकिन पुल बेहतर काम करता है।

  5. अंत में, आप शायद रिपॉजिटरी ए के रिमोट कनेक्शन को हटाकर थोड़ा साफ करना चाहते हैं

    git remote rm repo-A-branch
    
  6. पुश और आप सभी सेट हैं।

    git push
    

मैं यहाँ उल्लिखित अधिकांश चरणों से गुज़रा हूँ, हालाँकि ऐसा लगता है कि यह केवल फ़ाइल के प्रतिबद्ध इतिहास की नकल करता है या मास्टर से (और किसी भी अन्य शाखाओं से नहीं)। क्या वह सही है?
बाओ-लॉन्ग गुयेन-ट्रोंग

मुझे लगता है कि यह सही है और आपको किसी भी शाखाओं के लिए इसी तरह के चरणों से गुजरना होगा, जहां से आप फ़ाइलों या फ़ोल्डरों को स्थानांतरित करना चाहते हैं। शाखा में स्विच करें उदा। रिपॉजिटरी ए, फिल्टर-ब्रांच आदि में MyBranch आप फिर रिपॉजिटरी B. में "रिप खींच-ए-ब्रांच MyBranch"
marsans

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

मुझे डर है कि मुझे पता नहीं है, लेकिन लगता है कि वे होंगे।
माकर्न्स 12

1
@ मायर्स दुर्भाग्य से, यह विश्वसनीय तरीका नहीं है, हालांकि ऐसा लगता है। यह अन्य सभी समाधानों की तरह ही समस्या से ग्रस्त है - यह इतिहास को अतीत के नाम पर बरकरार नहीं रखता है। मेरे मामले में, जब मैंने निर्देशिका / फ़ाइल का नाम बदला तो बहुत पहली प्रतिबद्धता है। उससे परे सब कुछ खो गया है।
XZero

20

मुझे यह मिल गया बहुत उपयोगी लगा। यह एक बहुत ही सरल तरीका है जहाँ आप नए रेपो पर लागू होने वाले पैच बनाते हैं। अधिक जानकारी के लिए लिंक किया हुआ पृष्ठ देखें।

इसमें केवल तीन चरण हैं (ब्लॉग से कॉपी किया गया):

# Setup a directory to hold the patches
mkdir <patch-directory>

# Create the patches
git format-patch -o <patch-directory> --root /path/to/copy

# Apply the patches in the new repo using a 3 way merge in case of conflicts
# (merges from the other repo are not turned into patches). 
# The 3way can be omitted.
git am --3way <patch-directory>/*.patch

मेरे पास एकमात्र मुद्दा यह था कि मैं एक बार उपयोग करने पर सभी पैच लागू नहीं कर सकता था

git am --3way <patch-directory>/*.patch

विंडोज के तहत मुझे एक InvalidArgument त्रुटि मिली। इसलिए मुझे एक के बाद एक सभी पैच अप्लाई करने पड़े।


मेरे लिए कुछ बिंदु के रूप में काम नहीं किया श-हेस गायब थे। इससे मुझे मदद मिली: stackoverflow.com/questions/17371150/…
dr0i

"गिट लॉग" दृष्टिकोण के विपरीत, इस विकल्प ने मेरे लिए पूरी तरह से काम किया! धन्यवाद!
अलेजांद्रोवडी

1
नए रेपो के लिए चलती परियोजनाओं के लिए अलग-अलग तरीकों की कोशिश की। यह एकमात्र ऐसा है जिसने मेरे लिए काम किया। विश्वास नहीं कर सकता कि इस तरह के एक सामान्य कार्य को जटिल होना चाहिए।
क्रिस_D_TURK

रॉस हेंड्रिकसन के ब्लॉग को साझा करने के लिए धन्यवाद । इस दृष्टिकोण ने मेरे लिए काम किया।
कौशिक आचार्य

1
यह बहुत ही सुंदर समाधान है, हालांकि, फिर से, यह अन्य सभी समाधानों के समान मुद्दे से ग्रस्त है - यह इतिहास को अतीत के नाम से नहीं रखेगा।
XZero

6

प्रत्यक्ष नाम का उपयोग करना

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

मेरा समाधान ट्री-फिल्टर का उपयोग करना था और स्रोत रिपॉजिटरी के अस्थायी क्लोन से केवल अवांछित फ़ाइलों और निर्देशिकाओं को निकालना था, फिर उस क्लोन से मेरे लक्ष्य रिपॉजिटरी में 5 सरल चरणों में खींचें।

# 1. clone the source
git clone ssh://<user>@<source-repo url>
cd <source-repo>
# 2. remove the stuff we want to exclude
git filter-branch --tree-filter "rm -rf <files to exclude>" --prune-empty HEAD
# 3. move to target repo and create a merge branch (for safety)
cd <path to target-repo>
git checkout -b <merge branch>
# 4. Add the source-repo as remote 
git remote add source-repo <path to source-repo>
# 5. fetch it
git pull source-repo master
# 6. check that you got it right (better safe than sorry, right?)
gitk

यह स्क्रिप्ट आपके मूल रेपो में कोई संशोधन नहीं करेगी। यदि मैप फ़ाइल में निर्दिष्ट डेस्ट रेपो मौजूद नहीं है, तो यह स्क्रिप्ट बनाने का प्रयास करेगा।
चेतबाहन

1
मुझे यह भी लगता है कि निर्देशिका के नामों को अक्षुण्ण रखना महत्वपूर्ण है। अन्यथा आपको लक्ष्य रिपॉजिटरी में अतिरिक्त नाम बदलना शुरू हो जाएगा।
ipuustin

6

जो मैं हमेशा उपयोग करता हूं वह यहां है http://blog.neutrino.es/2012/git-copy-a-file-or-directory-from-another-repository-preserves-history/ । सरल और तेज।

स्टैकवॉयरफ़्लो मानकों के अनुपालन के लिए, यहाँ प्रक्रिया है:

mkdir /tmp/mergepatchs
cd ~/repo/org
export reposrc=myfile.c #or mydir
git format-patch -o /tmp/mergepatchs $(git log $reposrc|grep ^commit|tail -1|awk '{print $2}')^..HEAD $reposrc
cd ~/repo/dest
git am /tmp/mergepatchs/*.patch

5

यह उत्तर दिलचस्प कमांड प्रदान करता है git am उदाहरणों और चरणों का उपयोग करके प्रस्तुत किया जाता है।

उद्देश्य

  • आप कुछ या सभी फ़ाइलों को एक रिपॉजिटरी से दूसरे में स्थानांतरित करना चाहते हैं।
  • आप उनका इतिहास रखना चाहते हैं।
  • लेकिन आपको टैग और शाखाएं रखने की परवाह नहीं है।
  • आप नामांकित फ़ाइलों के लिए सीमित इतिहास (और नामांकित निर्देशिकाओं में फ़ाइलें) स्वीकार करते हैं।

प्रक्रिया

  1. ईमेल प्रारूप का उपयोग करके इतिहास निकालें
    git log --pretty=email -p --reverse --full-index --binary
  2. फ़ाइल ट्री को पुनर्गठित करें और इतिहास में फ़ाइल नाम परिवर्तन को अपडेट करें [वैकल्पिक]
  3. नया इतिहास लागू करें git am

1. ईमेल प्रारूप में इतिहास निकालें

उदाहरण: का इतिहास निकालें file3, file4औरfile5

my_repo
├── dirA
│   ├── file1
│   └── file2
├── dirB            ^
│   ├── subdir      | To be moved
│   │   ├── file3   | with history
│   │   └── file4   | 
│   └── file5       v
└── dirC
    ├── file6
    └── file7

अस्थायी निर्देशिका गंतव्य को साफ़ करें

export historydir=/tmp/mail/dir  # Absolute path
rm -rf "$historydir"             # Caution when cleaning

अपने रेपो स्रोत को साफ करें

git commit ...           # Commit your working files
rm .gitignore            # Disable gitignore
git clean -n             # Simulate removal
git clean -f             # Remove untracked file
git checkout .gitignore  # Restore gitignore

ईमेल प्रारूप में प्रत्येक फ़ाइल का इतिहास निकालें

cd my_repo/dirB
find -name .git -prune -o -type d -o -exec bash -c 'mkdir -p "$historydir/${0%/*}" && git log --pretty=email -p --stat --reverse --full-index --binary -- "$0" > "$historydir/$0"' {} ';'

दुर्भाग्य से विकल्प --followया --find-copies-harderके साथ जोड़ा नहीं जा सकता --reverse। यही कारण है कि जब फ़ाइल का नाम बदला जाता है (या जब एक मूल निर्देशिका का नाम बदला जाता है) तो इतिहास कट जाता है।

के बाद: ईमेल प्रारूप में अस्थायी इतिहास

/tmp/mail/dir
    ├── subdir
    │   ├── file3
    │   └── file4
    └── file5

इतिहास में फ़ाइल परिवर्तन और अद्यतन फ़ाइल नाम को बदलें [वैकल्पिक]

मान लीजिए आप इन तीन फाइलों को इस अन्य रेपो में स्थानांतरित करना चाहते हैं (वही रेपो हो सकता है)।

my_other_repo
├── dirF
│   ├── file55
│   └── file56
├── dirB              # New tree
│   ├── dirB1         # was subdir
│   │   ├── file33    # was file3
│   │   └── file44    # was file4
│   └── dirB2         # new dir
│        └── file5    # = file5
└── dirH
    └── file77

इसलिए अपनी फ़ाइलों को पुनर्गठित करें:

cd /tmp/mail/dir
mkdir     dirB
mv subdir dirB/dirB1
mv dirB/dirB1/file3 dirB/dirB1/file33
mv dirB/dirB1/file4 dirB/dirB1/file44
mkdir    dirB/dirB2
mv file5 dirB/dirB2

आपका अस्थायी इतिहास अब है:

/tmp/mail/dir
    └── dirB
        ├── dirB1
        │   ├── file33
        │   └── file44
        └── dirB2
             └── file5

इतिहास में फ़ाइल नाम भी बदलें:

cd "$historydir"
find * -type f -exec bash -c 'sed "/^diff --git a\|^--- a\|^+++ b/s:\( [ab]\)/[^ ]*:\1/$0:g" -i "$0"' {} ';'

नोट: यह पथ और फ़ाइल नाम के परिवर्तन को प्रतिबिंबित करने के लिए इतिहास को फिर से लिखता है।
      (यानी नए रेपो के भीतर नए स्थान / नाम का परिवर्तन)


3. नया इतिहास लागू करें

आपका अन्य रेपो है:

my_other_repo
├── dirF
│   ├── file55
│   └── file56
└── dirH
    └── file77

अस्थायी इतिहास फ़ाइलों से लागू करें:

cd my_other_repo
find "$historydir" -type f -exec cat {} + | git am 

आपका अन्य रेपो अब है:

my_other_repo
├── dirF
│   ├── file55
│   └── file56
├── dirB            ^
│   ├── dirB1       | New files
│   │   ├── file33  | with
│   │   └── file44  | history
│   └── dirB2       | kept
│        └── file5  v
└── dirH
    └── file77

git statusधकेले जाने के लिए तैयार कमिट की मात्रा देखने के लिए उपयोग करें :-)

नोट: जैसा कि इतिहास को पथ और फ़ाइल नाम परिवर्तन को प्रतिबिंबित करने के लिए फिर से लिखा गया है:
      (यानी पिछले रेपो के भीतर स्थान / नाम की तुलना में)

  • git mvस्थान / फ़ाइल नाम बदलने की आवश्यकता नहीं है ।
  • git log --followपूर्ण इतिहास तक पहुंचने की आवश्यकता नहीं है ।

एक्स्ट्रा ट्रिक: अपने रेपो के अंदर बदला हुआ / स्थानांतरित फ़ाइलों का पता लगाएँ

फ़ाइलों का नाम बदलने के लिए:

find -name .git -prune -o -exec git log --pretty=tformat:'' --numstat --follow {} ';' | grep '=>'

अधिक अनुकूलन: आप git logविकल्पों का उपयोग करके कमांड को पूरा कर सकते हैं --find-copies-harderया --reverse। आप cut -f3-पूर्ण पैटर्न '{*। * =>। *}} का उपयोग करके और पहले वाले दो कॉलम भी निकाल सकते हैं ।

find -name .git -prune -o -exec git log --pretty=tformat:'' --numstat --follow --find-copies-harder --reverse {} ';' | cut -f3- | grep '{.* => .*}'

3

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

संक्षिप्त संस्करण यह है कि यह $objectमौजूदा रिपॉजिटरी से दिए गए फ़ाइल या निर्देशिका ( ) के पैच फाइल बनाता है:

cd old_repo
git format-patch --thread -o "$temp" --root -- "$object"

जो तब एक नए भंडार पर लागू होता है:

cd new_repo
git am "$temp"/*.patch 

जानकारी के लिए कृपया देखें:


2

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

cd repo1

यह उल्लेखित सभी निर्देशिकाओं को हटा देगा, केवल इन निर्देशिकाओं के लिए इतिहास को संरक्षित करेगा

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

अब आप अपने git रिमोट में अपना नया रेपो जोड़ सकते हैं और उस पर धकेल सकते हैं

git remote remove origin <old-repo>
git remote add origin <new-repo>
git push origin <current-branch>

-fअधिलेखित करने के लिए जोड़ें


चेतावनी: गिट-फिल्टर-शाखा में गन्ने का एक ग्लूट होता है, जो आम तौर पर इतिहास का पुनर्लेखन करता है। गर्भपात करने के लिए आगे बढ़ने से पहले Ctrl-C को हिट करें, इसके बाद वैकल्पिक फ़िल्टरिंग टूल जैसे कि 'git filter-repo' ( github.com/newren/git-filter-repo ) का उपयोग करें। अधिक विवरण के लिए फ़िल्टर-शाखा मैनुअल पेज देखें; इस चेतावनी को हटाने के लिए, FILTER_BRANCH_SQUELCH_WARNING = 1 सेट करें।
कॉलिन

1

Http://blog.neutrino.es/2012/git-copy-a-file-or-directory-from-another-repository-presistent-history/ से प्रेरणा का उपयोग करते हुए , मैंने वही करने के लिए यह पॉवर्सशेल फ़ंक्शन बनाया, जिसमें है मेरे लिए अब तक बहुत अच्छा काम किया:

# Migrates the git history of a file or directory from one Git repo to another.
# Start in the root directory of the source repo.
# Also, before running this, I recommended that $destRepoDir be on a new branch that the history will be migrated to.
# Inspired by: http://blog.neutrino.es/2012/git-copy-a-file-or-directory-from-another-repository-preserving-history/
function Migrate-GitHistory
{
    # The file or directory within the current Git repo to migrate.
    param([string] $fileOrDir)
    # Path to the destination repo
    param([string] $destRepoDir)
    # A temp directory to use for storing the patch file (optional)
    param([string] $tempDir = "\temp\migrateGit")

    mkdir $tempDir

    # git log $fileOrDir -- to list commits that will be migrated
    Write-Host "Generating patch files for the history of $fileOrDir ..." -ForegroundColor Cyan
    git format-patch -o $tempDir --root -- $fileOrDir

    cd $destRepoDir
    Write-Host "Applying patch files to restore the history of $fileOrDir ..." -ForegroundColor Cyan
    ls $tempDir -Filter *.patch  `
        | foreach { git am $_.FullName }
}

इस उदाहरण के लिए उपयोग:

git clone project2
git clone project1
cd project1
# Create a new branch to migrate to
git checkout -b migrate-from-project2
cd ..\project2
Migrate-GitHistory "deeply\buried\java\source\directory\A" "..\project1"

आपके द्वारा ऐसा करने के बाद, आप migrate-from-project2विलय से पहले शाखा पर फ़ाइलों को फिर से व्यवस्थित कर सकते हैं ।


1

मैं कुछ मजबूत और पुन: प्रयोज्य (एक-कमांड-एंड-गो + पूर्ववत फ़ंक्शन) चाहता था, इसलिए मैंने निम्नलिखित बैश स्क्रिप्ट लिखी। मेरे लिए कई मौकों पर काम किया, इसलिए मैंने सोचा कि मैं इसे यहाँ साझा करूँगा।

यह एक मनमाना फ़ोल्डर को स्थानांतरित करने में सक्षम है /path/to/foo से repo1में /some/other/folder/barकरने के लिए repo2(फ़ोल्डर पथ उसी या अन्य, रूट फ़ोल्डर से दूरी अलग हो सकता है हो सकता है)।

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

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

यदि कोई फ़ाइल नाम क्लैश नहीं हैं, तो आपको बस करने की आवश्यकता है git commit मर्ज को अंतिम रूप देने के लिए अंत में ।

नकारात्मक पक्ष यह है कि यह संभवतः फ़ाइल नाम (के बाहर) का पालन नहीं करेगा REWRITE_FROM स्रोत रेपो में फ़ोल्डर के ) - इसके लिए समायोजित करने के लिए GitHub पर अनुरोधों का स्वागत करें।

गिटहब लिंक: गिट-मूव-फोल्डर-बीच-रिपोज-की-हिस्ट्री

#!/bin/bash

# Copy a folder from one git repo to another git repo,
# preserving full history of the folder.

SRC_GIT_REPO='/d/git-experimental/your-old-webapp'
DST_GIT_REPO='/d/git-experimental/your-new-webapp'
SRC_BRANCH_NAME='master'
DST_BRANCH_NAME='import-stuff-from-old-webapp'
# Most likely you want the REWRITE_FROM and REWRITE_TO to have a trailing slash!
REWRITE_FROM='app/src/main/static/'
REWRITE_TO='app/src/main/static/'

verifyPreconditions() {
    #echo 'Checking if SRC_GIT_REPO is a git repo...' &&
      { test -d "${SRC_GIT_REPO}/.git" || { echo "Fatal: SRC_GIT_REPO is not a git repo"; exit; } } &&
    #echo 'Checking if DST_GIT_REPO is a git repo...' &&
      { test -d "${DST_GIT_REPO}/.git" || { echo "Fatal: DST_GIT_REPO is not a git repo"; exit; } } &&
    #echo 'Checking if REWRITE_FROM is not empty...' &&
      { test -n "${REWRITE_FROM}" || { echo "Fatal: REWRITE_FROM is empty"; exit; } } &&
    #echo 'Checking if REWRITE_TO is not empty...' &&
      { test -n "${REWRITE_TO}" || { echo "Fatal: REWRITE_TO is empty"; exit; } } &&
    #echo 'Checking if REWRITE_FROM folder exists in SRC_GIT_REPO' &&
      { test -d "${SRC_GIT_REPO}/${REWRITE_FROM}" || { echo "Fatal: REWRITE_FROM does not exist inside SRC_GIT_REPO"; exit; } } &&
    #echo 'Checking if SRC_GIT_REPO has a branch SRC_BRANCH_NAME' &&
      { cd "${SRC_GIT_REPO}"; git rev-parse --verify "${SRC_BRANCH_NAME}" || { echo "Fatal: SRC_BRANCH_NAME does not exist inside SRC_GIT_REPO"; exit; } } &&
    #echo 'Checking if DST_GIT_REPO has a branch DST_BRANCH_NAME' &&
      { cd "${DST_GIT_REPO}"; git rev-parse --verify "${DST_BRANCH_NAME}" || { echo "Fatal: DST_BRANCH_NAME does not exist inside DST_GIT_REPO"; exit; } } &&
    echo '[OK] All preconditions met'
}

# Import folder from one git repo to another git repo, including full history.
#
# Internally, it rewrites the history of the src repo (by creating
# a temporary orphaned branch; isolating all the files from REWRITE_FROM path
# to the root of the repo, commit by commit; and rewriting them again
# to the original path).
#
# Then it creates another temporary branch in the dest repo,
# fetches the commits from the rewritten src repo, and does a merge.
#
# Before any work is done, all the preconditions are verified: all folders
# and branches must exist (except REWRITE_TO folder in dest repo, which
# can exist, but does not have to).
#
# The code should work reasonably on repos with reasonable git history.
# I did not test pathological cases, like folder being created, deleted,
# created again etc. but probably it will work fine in that case too.
#
# In case you realize something went wrong, you should be able to reverse
# the changes by calling `undoImportFolderFromAnotherGitRepo` function.
# However, to be safe, please back up your repos just in case, before running
# the script. `git filter-branch` is a powerful but dangerous command.
importFolderFromAnotherGitRepo(){
    SED_COMMAND='s-\t\"*-\t'${REWRITE_TO}'-'

    verifyPreconditions &&
    cd "${SRC_GIT_REPO}" &&
      echo "Current working directory: ${SRC_GIT_REPO}" &&
      git checkout "${SRC_BRANCH_NAME}" &&
      echo 'Backing up current branch as FILTER_BRANCH_BACKUP' &&
      git branch -f FILTER_BRANCH_BACKUP &&
      SRC_BRANCH_NAME_EXPORTED="${SRC_BRANCH_NAME}-exported" &&
      echo "Creating temporary branch '${SRC_BRANCH_NAME_EXPORTED}'..." &&
      git checkout -b "${SRC_BRANCH_NAME_EXPORTED}" &&
      echo 'Rewriting history, step 1/2...' &&
      git filter-branch -f --prune-empty --subdirectory-filter ${REWRITE_FROM} &&
      echo 'Rewriting history, step 2/2...' &&
      git filter-branch -f --index-filter \
       "git ls-files -s | sed \"$SED_COMMAND\" |
        GIT_INDEX_FILE=\$GIT_INDEX_FILE.new git update-index --index-info &&
        mv \$GIT_INDEX_FILE.new \$GIT_INDEX_FILE" HEAD &&
    cd - &&
    cd "${DST_GIT_REPO}" &&
      echo "Current working directory: ${DST_GIT_REPO}" &&
      echo "Adding git remote pointing to SRC_GIT_REPO..." &&
      git remote add old-repo ${SRC_GIT_REPO} &&
      echo "Fetching from SRC_GIT_REPO..." &&
      git fetch old-repo "${SRC_BRANCH_NAME_EXPORTED}" &&
      echo "Checking out DST_BRANCH_NAME..." &&
      git checkout "${DST_BRANCH_NAME}" &&
      echo "Merging SRC_GIT_REPO/" &&
      git merge "old-repo/${SRC_BRANCH_NAME}-exported" --no-commit &&
    cd -
}

# If something didn't work as you'd expect, you can undo, tune the params, and try again
undoImportFolderFromAnotherGitRepo(){
  cd "${SRC_GIT_REPO}" &&
    SRC_BRANCH_NAME_EXPORTED="${SRC_BRANCH_NAME}-exported" &&
    git checkout "${SRC_BRANCH_NAME}" &&
    git branch -D "${SRC_BRANCH_NAME_EXPORTED}" &&
  cd - &&
  cd "${DST_GIT_REPO}" &&
    git remote rm old-repo &&
    git merge --abort
  cd -
}

importFolderFromAnotherGitRepo
#undoImportFolderFromAnotherGitRepo

0

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

#Source directory
git remote rm origin
#Target directory
git remote add branch-name-from-old-repo ../source_directory

उन दो चरणों में, मैं उसी रेपो में प्रदर्शित होने के लिए दूसरे रेपो की शाखा प्राप्त करने में सक्षम था।

अंत में, मैंने लक्ष्य रेपो की मेनलाइन का पालन करने के लिए इस शाखा (जिसे मैंने दूसरे रेपो से आयात किया) को सेट किया (इसलिए मैं उन्हें अलग नहीं कर सका)

git br --set-upstream-to=origin/mainline

अब इसने ऐसा व्यवहार किया जैसे कि यह सिर्फ एक और शाखा थी जिसे मैंने उसी रेपो के खिलाफ धकेला था।


0

यदि विचाराधीन फ़ाइलों के लिए मार्ग दो रिपॉजिट में समान हैं और आप केवल एक फाइल या संबंधित फाइलों का एक छोटा सेट लाना चाहते हैं, तो इसका उपयोग करने का एक आसान तरीका है git cherry-pick

पहला कदम दूसरे रेपो से अपने स्वयं के स्थानीय रेपो का उपयोग करके कमिट्स को लाना है git fetch <remote-url>। यह FETCH_HEADअन्य रेपो से सिर की ओर इशारा करते हुए निकल जाएगा ; अगर आप अन्य भ्रूणों को पूरा करने के बाद उस संदर्भ का संदर्भ सुरक्षित रखना चाहते हैं, तो आप इसे टैग करना चाहते हैं git tag other-head FETCH_HEAD

फिर आपको उस फ़ाइल के लिए एक प्रारंभिक प्रतिबद्ध बनाने की आवश्यकता होगी (यदि यह मौजूद नहीं है) या फ़ाइल को उस स्थिति में लाने के लिए एक प्रतिबद्ध है जिसे आप जिस अन्य रेपो से लाना चाहते हैं, उससे पहले प्रतिबद्ध के साथ पैच किया जा सकता है। git cherry-pick <commit-0>यदि commit-0आप चाहते हैं कि शुरू की गई फ़ाइलों के साथ ऐसा करने में सक्षम हो , या आपको 'हाथ से' प्रतिबद्ध बनाने की आवश्यकता हो सकती है। -nयदि आपको प्रारंभिक प्रतिबद्ध को संशोधित करने की आवश्यकता है, तो चेरी-पिक विकल्प में जोड़ें , उदाहरण के लिए, उस फ़ाइल को ड्रॉप करें जिसे आप नहीं लाना चाहते हैं।

उसके बाद, आप git cherry-pickबाद के कमिट्स को जारी रख सकते हैं , फिर से -nजहां आवश्यक हो , का उपयोग करके । सबसे सरल मामले में (सभी कमिट वही हैं जो आप चाहते हैं और सफाई से लागू होते हैं) आप चेरी-पिक कमांड लाइन पर कमिट्स की पूरी सूची दे सकते हैं git cherry-pick <commit-1> <commit-2> <commit-3> ...:।


0

यह गिट-फिल्टर-रेपो का उपयोग करके सरल हो जाता है।

में स्थानांतरित project2/sub/dirकरने के लिए project1/sub/dir:

# Create a new repo containing only the subdirectory:
git clone project2 project2_subdir
cd project2_subdir
git filter-repo --force --path sub/dir

# Merge the new repo:
cd ../project1
git remote add project2_subdir ../project2_subdir/
git merge remotes/project2_subdir/master --allow-unrelated-histories
git remote remove project2_subdir

उपकरण को बस स्थापित करने के लिए: pip3 install git-filter-repo ( README में अधिक विवरण और विकल्प )

# Before: (root)
.
|-- project1
|   `-- 3
`-- project2
    |-- 1
    `-- sub
        `-- dir
            `-- 2

# After: (project1)
.
├── 3
└── sub
    └── dir
        └── 2

-2

सभी शाखाओं को बनाए रखने और इतिहास को संरक्षित करके GITLab में मेरे GIT स्टैश को स्थानांतरित करने के लिए नीचे दी गई विधि।

स्थानीय के लिए पुराने भंडार पर क्लोन करें।

git clone --bare <STASH-URL>

GitLab में एक खाली भंडार बनाएँ।

git push --mirror <GitLab-URL>

ऊपर मैंने प्रदर्शन किया जब हमने अपना कोड स्लैश से गिटलैब में स्थानांतरित कर दिया और इसने बहुत अच्छा काम किया।

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