फ़ाइल इतिहास को तोड़ने के बिना दो गिट रिपोजिटरी को मर्ज करें


226

मुझे दो Git रिपॉजिटरी को एक नए, तीसरे रिपॉजिटरी में मर्ज करना होगा। मैंने एक सबट्री मर्ज का उपयोग करते हुए यह करने के कई विवरण पाए हैं (उदाहरण के लिए जैकब नारęबस्की के जवाब पर कि आप दो Git रिपॉजिटरी को कैसे मर्ज करते हैं? ) और उन निर्देशों का पालन करते हुए ज्यादातर काम करता है, सिवाय इसके कि जब मैं सभी फ़ाइलों की सबट्री मर्ज करता हूं पुराने रिपॉजिटरी से नई जोड़ी गई फाइलों के रूप में रिकॉर्ड किया जाता है। जब मैं करता हूं तो मैं पुराने रिपॉजिटरी से कमिटेड हिस्ट्री देख सकता हूं git log, लेकिन अगर मैं ऐसा git log <file>करता हूं तो उस फाइल के लिए केवल एक कमिट दिखाता है- सबट्री मर्ज। उपरोक्त उत्तर पर टिप्पणियों से देखते हुए, मैं इस समस्या को देखने में अकेला नहीं हूं, लेकिन मुझे इसके लिए कोई प्रकाशित समाधान नहीं मिला है।

क्या किसी भी तरह से रिपोजिटरी का विलय होता है और व्यक्तिगत फाइल इतिहास को बरकरार रखता है?


मैं Git का उपयोग नहीं कर रहा हूं, लेकिन Mercurial में मैं पहले एक कन्वर्ट करता हूं यदि आवश्यक हो कि रिपोज के फाइल पथ को मर्ज करने के लिए, और फिर परिवर्तन को प्राप्त करने के लिए लक्ष्य में एक रेपो को बलपूर्वक खींचें और फिर एक करें विभिन्न शाखाओं का विलय। यह परीक्षण किया गया है और काम करता है;) शायद यह Git के लिए एक समाधान खोजने में मदद करता है ... सबट्री-मर्ज दृष्टिकोण की तुलना में मुझे लगता है कि कन्वर्ट कदम अलग है जहां इतिहास केवल एक मार्ग को मैप करने के बजाय फिर से लिखा जाता है (यदि मैं समझता हूं सही ढंग से)। यह तब फ़ाइल पथ के किसी भी विशेष हैंडलिंग के बिना एक चिकनी मर्ज सुनिश्चित करता है।
ल्यूकोरो

मुझे यह सवाल भी उपयोगी है stackoverflow.com/questions/1683531/…
nacross

मैंने एक अनुवर्ती प्रश्न बनाया। दिलचस्प हो सकता है: दो गिट रिपोजिटरी को मर्ज करें और मास्टर इतिहास रखें: stackoverflow.com/questions/42161910/…
दिमित्री डेवले

मेरे लिए काम करने वाला स्वचालित समाधान stackoverflow.com/a/30781527/239408
xverges

जवाबों:


269

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

यहां दो रिपॉजिटरी को एक साथ गोंद करने के लिए Powershell स्क्रिप्ट का उदाहरण दिया गया है:

# Assume the current directory is where we want the new repository to be created
# Create the new repository
git init

# Before we do a merge, we have to have an initial commit, so we'll make a dummy commit
git commit --allow-empty -m "Initial dummy commit"

# Add a remote for and fetch the old repo
git remote add -f old_a <OldA repo URL>

# Merge the files from old_a/master into new/master
git merge old_a/master --allow-unrelated-histories

# Move the old_a repo files and folders into a subdirectory so they don't collide with the other repo coming later
mkdir old_a
dir -exclude old_a | %{git mv $_.Name old_a}

# Commit the move
git commit -m "Move old_a files into subdir"

# Do the same thing for old_b
git remote add -f old_b <OldB repo URL>
git merge old_b/master --allow-unrelated-histories
mkdir old_b
dir exclude old_a,old_b | %{git mv $_.Name old_b}
git commit -m "Move old_b files into subdir"

स्पष्ट रूप से आप इसके बजाय पुराने_ब को पुराने_ए (जो नया संयुक्त रेपो बन जाता है) को मर्ज कर सकते हैं यदि आप ऐसा करेंगे - तो स्क्रिप्ट को सूट करने के लिए संशोधित करें।

यदि आप इन-प्रोग्रेस फ़ीचर शाखाओं को आगे लाना चाहते हैं, तो इसका उपयोग करें:

# Bring over a feature branch from one of the old repos
git checkout -b feature-in-progress
git merge -s recursive -Xsubtree=old_a old_a/feature-in-progress

यह प्रक्रिया का एकमात्र गैर-स्पष्ट हिस्सा है - यह एक उप-मर्ज नहीं है, बल्कि सामान्य पुनरावर्ती मर्ज के लिए एक तर्क है जो गिट को बताता है कि हमने लक्ष्य का नाम बदल दिया है और यह Git लाइन को सब कुछ सही ढंग से पूरा करने में मदद करता है।

मैंने यहाँ थोड़ा और विस्तृत विवरण लिखा ।


16
इस समाधान का उपयोग git mvइतनी अच्छी तरह से काम नहीं करता है। जब आप बाद git logमें स्थानांतरित की गई फ़ाइलों में से एक पर उपयोग करते हैं तो आपको केवल इस कदम से कमिट मिलता है। पिछला सारा इतिहास खो गया है। इस वजह से है git mvवास्तव में है git rm; git add, लेकिन एक ही चरण में
mholm815

15
यह Git में किसी भी अन्य चाल / नाम बदलने के संचालन के समान है: कमांड लाइन से आप कर के इतिहास के सभी प्राप्त कर सकते हैं git log --follow, या सभी GUI उपकरण स्वचालित रूप से आपके लिए करते हैं। एक सबट्री मर्ज के साथ आप व्यक्तिगत फ़ाइलों के लिए इतिहास प्राप्त नहीं कर सकते , जहाँ तक मुझे पता है, इसलिए यह विधि बेहतर है।
एरिक ली

3
@EricLee जब old_b रेपो को मर्ज किया जाता है तो मुझे बहुत सारे मर्ज टकराव होते हैं। क्या यह अपेक्षित है? मुझे CONFLICT (नाम बदलें / हटाएं)
जॉन

9
जब मैं "dir -exclude old_a।% {Git mv $ _। Name old_a}" का प्रयास करता हूं, तो मुझे sh.exe मिलता है: "dir: कमांड नहीं मिला और sh.exe": git: कमांड नहीं मिला। इस कार्य का उपयोग करना: ls -I old_a | xargs -I '{}' git mv '{}' old_a /
जॉर्ज

5
यह 1(नंबर एक) के लिए lsऔर पूंजी 'आंख' के लिए है xargs। इस टिप के लिए धन्यवाद!
डोमिनिक शीशी

149

यहां एक तरीका है जो किसी भी इतिहास को फिर से नहीं लिखता है, इसलिए सभी प्रतिबद्ध आईडी मान्य रहेंगे। अंतिम परिणाम यह है कि दूसरी रेपो की फाइलें एक उपनिर्देशिका में समाप्त हो जाएंगी।

  1. रिमोट के रूप में दूसरा रेपो जोड़ें:

    cd firstgitrepo/
    git remote add secondrepo username@servername:andsoon
    
  2. सुनिश्चित करें कि आपने सेकंडरेम्प के सभी हिट डाउनलोड कर लिए हैं:

    git fetch secondrepo
    
  3. दूसरी रेपो शाखा से एक स्थानीय शाखा बनाएँ:

    git branch branchfromsecondrepo secondrepo/master
    
  4. इसकी सभी फ़ाइलों को एक उपनिर्देशिका में ले जाएँ:

    git checkout branchfromsecondrepo
    mkdir subdir/
    git ls-tree -z --name-only HEAD | xargs -0 -I {} git mv {} subdir/
    git commit -m "Moved files to subdir/"
    
  5. पहली रेपो की मास्टर शाखा में दूसरी शाखा को मिलाएं:

    git checkout master
    git merge --allow-unrelated-histories branchfromsecondrepo
    

आपकी रिपॉजिटरी में एक से अधिक रूट कमिट होंगे, लेकिन इससे कोई समस्या नहीं होनी चाहिए।


1
चरण 2 मेरे लिए काम नहीं करता है: घातक: एक वैध वस्तु का नाम नहीं: 'सेकेंडरीपो / मास्टर'।
कीथ

@Keith: सुनिश्चित करें कि आपने "रेप्रो" नामक रिमोट के रूप में दूसरा रेपो जोड़ा है, और उस रेपो में "मास्टर" नाम की एक शाखा है (आप कमांड के साथ रिमोट रेपो पर शाखाएं देख सकते हैं git remote show secondrepo)
फ़्लिम

मुझे इसे नीचे लाने के लिए एक भ्रूण लाने की ज़रूरत थी। 1 और 2 के बीच में मैंने दूसरा क्रेप लिया
स्कैमुएल

@monkjack: मैंने git लाने के चरण को शामिल करने के लिए अपना उत्तर संपादित किया है। भविष्य में उत्तर को स्वयं संपादित करने के लिए स्वतंत्र महसूस करें।
फ्लिम

4
@MartijnHeemels Git के पुराने संस्करण के लिए, बस छोड़ दें --allow-unrelated-histories। इस उत्तर पोस्ट का इतिहास देखें।
फ्लिम

8

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

  1. Github में एक नया भंडार बनाएँ।

    यहां छवि विवरण दर्ज करें

  2. नए बनाए गए रेपो को डाउनलोड करें और पुराने रिमोट रिपॉजिटरी को जोड़ें।

    git clone https://github.com/alexbr9007/Test.git
    cd Test
    git remote add OldRepo https://github.com/alexbr9007/Django-React.git
    git remote -v
    
  3. पुरानी रेपो से सभी फ़ाइलों के लिए लाएं ताकि एक नई शाखा बने।

    git fetch OldRepo
    git branch -a
    

    यहां छवि विवरण दर्ज करें

  4. मास्टर शाखा में, पुराने रेपो को नए बनाए गए के साथ संयोजित करने के लिए मर्ज करें।

    git merge remotes/OldRepo/master --allow-unrelated-histories
    

    यहां छवि विवरण दर्ज करें

  5. OldRepo से जोड़ी गई सभी नई बनाई गई सामग्री को संग्रहीत करने के लिए एक नया फ़ोल्डर बनाएं और इसकी फ़ाइलों को इस नए फ़ोल्डर में स्थानांतरित करें।

  6. अंत में, आप संयुक्त रेपो से फाइलें अपलोड कर सकते हैं और पुराने रिप को GitHub से सुरक्षित रूप से हटा सकते हैं।

आशा है कि यह दूरस्थ रिपॉजिटरी के विलय से निपटने वाले किसी के लिए भी उपयोगी हो सकता है।


1
यह एकमात्र समाधान है जिसने मेरे लिए गिट इतिहास को संरक्षित करने के लिए काम किया। पुराने रेपो के साथ दूरस्थ लिंक को हटाने के लिए मत भूलना git remote rm OldRepo
हरूबियारी

7

का उपयोग करने पर एक नज़र है कृपया

git rebase --root --preserve-merges --onto

अपने जीवन के आरंभ में दो इतिहासों को जोड़ना।

यदि आपके पास ओवरलैप वाले रास्ते हैं, तो उन्हें ठीक करें

git filter-branch --index-filter

जब आप लॉग का उपयोग करते हैं, तो सुनिश्चित करें कि आप "प्रतियों को कठिन खोजें" के साथ

git log -CC

इस तरह से आप पथ में फ़ाइलों के किसी भी आंदोलन मिलेगा।


Git प्रलेखन में रिबासिंग
स्टीफन टर्नर

7

मैंने @ फाल्म से समाधान को इस git aliasतरह बदल दिया (मेरे साथ जोड़ा ~/.gitconfig):

[alias]
 mergeRepo = "!mergeRepo() { \
  [ $# -ne 3 ] && echo \"Three parameters required, <remote URI> <new branch> <new dir>\" && exit 1; \
  git remote add newRepo $1; \
  git fetch newRepo; \
  git branch \"$2\" newRepo/master; \
  git checkout \"$2\"; \
  mkdir -vp \"${GIT_PREFIX}$3\"; \
  git ls-tree -z --name-only HEAD | xargs -0 -I {} git mv {} \"${GIT_PREFIX}$3\"/; \
  git commit -m \"Moved files to '${GIT_PREFIX}$3'\"; \
  git checkout master; git merge --allow-unrelated-histories --no-edit -s recursive -X no-renames \"$2\"; \
  git branch -D \"$2\"; git remote remove newRepo; \
}; \
mergeRepo"

12
बस जिज्ञासु: क्या आप वास्तव में एक उपनाम की आवश्यकता के लिए अक्सर ऐसा करते हैं?
पार्कर

1
नहीं, लेकिन मुझे यह याद नहीं है कि यह कैसे करना है ताकि एक उपनाम मेरे लिए इसे याद रखने का एक तरीका हो।
फ्रेड्रिक एर्लडसन

1
हाँ .. लेकिन कंप्यूटर बदलने की कोशिश करो और अपने उपनामों को भूल जाओ;)
quetzalcoatl

1
का मूल्य क्या है $GIT_PREFIX?
neowulf33

github.com/git/git/blob/… 'GIT_PREFIX' को वर्तमान चालू निर्देशिका से 'git Rev-parse --show-prefix' चलाकर लौटाया जाता है। लिंकगिट देखें: git-Rev-parse [1]।
फ्रेड्रिक एरलैंडसन

3

यह फ़ंक्शन रिमोट रेपो को स्थानीय रेपो डायर में क्लोन करेगा:

function git-add-repo
{
    repo="$1"
    dir="$(echo "$2" | sed 's/\/$//')"
    path="$(pwd)"

    tmp="$(mktemp -d)"
    remote="$(echo "$tmp" | sed 's/\///g'| sed 's/\./_/g')"

    git clone "$repo" "$tmp"
    cd "$tmp"

    git filter-branch --index-filter '
        git ls-files -s |
        sed "s,\t,&'"$dir"'/," |
        GIT_INDEX_FILE="$GIT_INDEX_FILE.new" git update-index --index-info &&
        mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE"
    ' HEAD

    cd "$path"
    git remote add -f "$remote" "file://$tmp/.git"
    git pull "$remote/master"
    git merge --allow-unrelated-histories -m "Merge repo $repo into master" --edit "$remote/master"
    git remote remove "$remote"
    rm -rf "$tmp"
}

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

cd current/package
git-add-repo https://github.com/example/example dir/to/save

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

फायदा!


मैं बैश के बजाय zsh का उपयोग कर रहा हूं, और g2.13.0 git का। कोई फर्क नहीं पड़ता कि मैंने क्या कोशिश की है, मैं git filter-branch --index-filterकाम करने में सक्षम नहीं हूं । आमतौर पर मुझे एक त्रुटि संदेश मिलता है कि .new इंडेक्स फ़ाइल मौजूद नहीं है। क्या वह कोई घंटी बजाता है?
पैट्रिक बीयर्ड

@PatrickBeard मैं zsh नहीं जानता, आप git-add-repo.shऊपर दी गई फ़ंक्शन के साथ अलग फ़ाइल बना सकते हैं, फ़ाइल के अंत में यह लाइन डाल सकते हैं git-add-repo "$@"। उसके बाद आप इसे zsh जैसे cd current/git/packageऔरbash path/to/git-add-repo.sh https://github.com/example/example dir/to/save
Andrey Izman

समस्या पर यहां चर्चा की गई: stackoverflow.com/questions/7798142/… mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE" कभी-कभी विफल रहता है, इसलिए आपको एक जोड़ना होगा if test
पैट्रिक बीयर्ड

1
मैं इस विधि का उपयोग नहीं करता! मैंने स्क्रिप्ट, भोलेपन और वाचालता की कोशिश की (मैं केवल उस हिस्से के लिए खुद को दोषी ठहरा सकता हूं), और इसने मेरे स्थानीय गिट रेपो को बंद कर दिया। इतिहास ज्यादातर सही लग रहा था, लेकिन जीथब को वापस धक्का देने के परिणामस्वरूप "RPC को नुकसान पहुँचाया गया; कर्ल 55 वीएम SSL_write () SYSCALL, इरनो = 32" त्रुटि वापस आ गई। मैंने इसकी मरम्मत करने की कोशिश की, लेकिन यह पूरी तरह से टूट गया। मैं एक नए स्थानीय रेपो में चीजों को फिर से बनाने के लिए समाप्त हो गया।
मेसन

@MasonFreed इस स्क्रिप्ट में दोनों रेपो के मिश्रण के साथ एक नया गिट इतिहास बनाता है, इसलिए इसे पुराने रेपो में धकेला नहीं जा सकता है, इसके लिए एक नया बनाने या बल कुंजी के साथ पुश करने की आवश्यकता होती है, इसका मतलब है कि यह आपके रेपो को सर्वर पर पुनः
लिखता है

2

एक प्रतिनिधि को दूसरे रेपो में एम्बेड करने के लिए चरणों का पालन करें, दोनों गिट इतिहास को विलय करके एक एकल गिट इतिहास।

  1. क्लोन करने के लिए आप चाहते हैं दोनों repos क्लोन।

git clone git@github.com: उपयोगकर्ता / माता-पिता-प्रतिनिधि

git क्लोन क्लोन git@github.com: उपयोगकर्ता / बच्चा-प्रतिनिधि

  1. बच्चे रेपो में जाओ

सीडी बाल-रेपो /

  1. नीचे कमांड चलाएं, my/new/subdirनिर्देशिका संरचना के साथ पथ (3 घटनाओं) को बदलें जहां आप बच्चे को रेपो करना चाहते हैं।

git फ़िल्टर-शाखा --prune-blank --tree- फ़िल्टर 'यदि [! -मेरी / नई / उपदिर]; तब mkdir -p my / new / subdir git ls-tree --name-only $ GIT_COMMIT | xargs-I फ़ाइलें mv फ़ाइलें मेरे / नई / उप-फाई फाई '

  1. जनक रेपो में जाओ

सीडी ../parent-repo/

  1. चाइल्ड रेपो के लिए रास्ता बताते हुए, माता-पिता के रेपो में एक रिमोट जोड़ें

git Remote add चाइल्ड-रिमोट ../child-repo/

  1. बच्चे रेपो को प्राप्त करें

git लाने का बच्चा-बच्चा

  1. इतिहास को मिलाएं

git मर्ज --allow-unrelated-histories चाइल्ड-रिमोट / मास्टर

यदि आप पेरेंट रेपो में git लॉग की जांच करते हैं, तो इसमें चाइल्ड रेपो कमिट मर्ज होना चाहिए। आप टैग को संकेत स्रोत से भी देख सकते हैं।

नीचे दिए गए लेख ने मुझे एक प्रतिनिधि को दूसरे रेपो में एम्बेड करने में मदद की, जिसमें दोनों गिट इतिहासों को विलय करके एक एकल गिट इतिहास था।

http://ericlathrop.com/2014/01/combining-git-repositories/

उम्मीद है की यह मदद करेगा। हैप्पी कोडिंग!


सिंटैक्स त्रुटि के साथ चरण 3 मेरे लिए विफल रहा। अर्ध-कॉलोन गायब हैं। फिक्सgit filter-branch --prune-empty --tree-filter ' if [ ! -e my/new/subdir ]; then mkdir -p my/new/subdir; git ls-tree --name-only $GIT_COMMIT | xargs -I files mv files my/new/subdir; fi'
यूरी एल

1

आप भंडार मर्ज करना चाहते हैं कहो aमें b(मैं यह सोचते कर रहा हूँ कि वे एक दूसरे के बगल में स्थित हैं):

cd b
git remote add a ../a
git fetch a
git merge --allow-unrelated-histories a/master
git remote remove a

यदि आप aउपनिर्देशिका में डालना चाहते हैं तो ऊपर दिए गए आदेशों से पहले निम्न कार्य करें:

cd a
git filter-repo --to-subdirectory-filter a
cd ..

यह आप की जरूरत के लिए git-filter-repoस्थापित ( filter-branchहै हतोत्साहित )।

2 बड़े रिपोजिटरी को विलय करने का एक उदाहरण, उनमें से एक को एक उपनिर्देशिका में रखा गया है: https://gist.github.com/x-yuri/9890ab1079cf4357d6f269d073fd9731

यहाँ पर अधिक है

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