[英]Git: merge two repositories one over the other
我有一個存儲庫A(原始存儲庫),具有完整的提交集。 在某個時間點,決定不再使用存儲庫A,並且從頭開始創建了一個新的干凈存儲庫B,將所有回購A的內容都使用copy + paste(而不是通過將A的內容合並到干凈的B中,以便保留提交歷史記錄)。 我現在想要實現的是在第三個新的干凈的存儲庫C中將存儲庫的提交和歷史記錄粘合在一起(粘合歷史記錄非常重要)。最終目標是存儲庫C應該包含源存儲庫A和B按時間順序排列,好像根本沒有創建存儲庫B,例如好像在存儲庫A中進行了工作一樣。
所以目前的情況是:
回購A:提交A1>提交A2>提交A3> ...>提交An
存儲庫B:提交B1(= A1 + A2 + A3 + ... + An)>提交B2(=提交An + 1)> ...>提交Bk
嘗試了以下方法:
git remote add -f A <repo_A_url>
git merge A/master
git remote add -f B <repo_B_url>
git merge B/master
然后解決沖突。 由於歷史被某種方式弄亂了,所以部分工作了。
然后嘗試我認為最干凈的方法-僅將回購A合並到回購C中,然后櫻桃選擇回購B的提交,范圍從B2 ... Bk。 它可以工作,但是挑選合並提交會減慢合並過程。
我可能會重復一個主題,請告訴我我是否這樣做,因為我爬過許多線程,並且大多數情況下看到了如何通過將它們放置在最終存儲庫中的不同文件夾中來將兩個存儲庫添加到第三個存儲庫中。 如果您可以分享最合適,在語義上正確且對git友好的方法。
太謝謝了。
如果您的歷史記錄是線性的(例如,僅在A和B中都master
過),那還不錯。 如果有很多分支機構要應對,那么它將涉及更多事務(請參閱下面的更新),但這仍將提供一個起點:
首先,創建您的C庫。
git clone /url/for/repo/A C
cd C
現在從B獲取所有對象
git remote add B /url/for/repo/B
git fetch B
現在,我們應該在新的C庫中同時擁有這兩個歷史記錄。 將B回購提交重新編入A歷史記錄
git rebase --onto master --root B/master
現在您需要更新master
引用,並可能需要清理一點
git branch -f master
git remote remove B
現在,A被列為您的origin
遠程服務器; 您可能想將其推入或刪除為原點,這取決於您是否打算讓A包含以后的所有內容。
在拓撲方面,實際上確實存在三種總體方案:
1)在B中分支和合並
因此,讓我們假設您在B中有一個分支尖端(如果沒有,請通讀此書,然后參閱方案2),該分支可以追溯到一個根,您可以將其嫁接到A中的單個分支尖端(如果沒有看到下面的方案3,那么回到這個)。
A1 ---- A2 ---- A3 <--- (master)
\ /
A4 -- A5
---------------------------
B1 ---- B2 ---- B3 <--- (master)
\ /
B4 -- B5
A中可能有分支,但最終它們已經合並回去了,您不必擔心它們。 B中可能也有分支,盡管將它們合並回去,但如果重新設置基准使其線性,則可能會造成麻煩。
像上面一樣,首先創建C並導入兩個歷史記錄。 (最后,我將建議對此過程進行一些細微改動,與引用有關……但是讓我們回到這一點。)
現在,您在這里有兩個選擇。 到目前為止 ,最簡單的方法是使用filter-branch
(但可能會很耗時)。 找到將要移植到的提交的哈希值(上面標記為A3)並運行
git filter-branch --parent-filter 'sed "s/^\$/-p xxxxxxxxxx/"' B/master
(其中xxxxxxxxxx是哈希值)。
那確實是假設你已經過了; 它可在Windows或幾乎所有* nix系統上的git bash環境中使用。 如果沒有,您可以提出一個等效的過濾器。 (它所做的只是說:“如果輸入為空行,則寫出'-p',后跟我要嫁接的哈希值;否則將輸入作為我的輸出傳遞”。)
如果由於某種原因您不能執行此操作,或者似乎確實存在性能問題,則可以嘗試計划b:提供--preserve-merges
選項以重新設置rebase
……這將完成您想要的操作a很多時間。 但是有一些主要警告。
基本上,如果合並引入了手動更改-或者是因為需要手動解決沖突,或者因為合並是通過--no-commit
完成的,並且以這種方式引入了手動更改-那么即使重新合並,合並也無法由rebase正確地復制。此選項。
在發生沖突的情況下,應該停止重新設置基准,並讓您重新應用手動解析(您可以通過原始合並提交的路徑檢出( checkout ... -- .
)來進行操作。有人使用--no-commit
rebase甚至都不會意識到任何錯誤。
如果您知道這將是一個問題,但可以識別一個或兩個問題合並 ,則一個選擇是為每個問題合並的父級重新設置基礎,然后手動重做合並,然后從該點開始繼續。
如果您不知道是否/將在哪里發生問題 ,可以嘗試重新設置基准,然后運行驗證以比較提交。 在進行重新基准之前
git checkout master
git tag old-B-master
然后嘗試變基
git rebase --preserve-branches --onto master --root B/master
git tag new-B-master
然后執行您認為安全的任何級別的驗證。 (顯然,最低限度地使old-B-master
與new-B-master
區別開來。當我做這樣的事情時,我編寫了一個腳本來遞歸地遍歷提交祖先,比較提交與提交之間的關系。偏執狂?也許。)
除非這種情況進行得非常非常順利 ,否則您最好還是回到filter-branch
方法。
2)B中的多個分支提示
A1 ---- A2 ---- A3 <--- (master)
\ /
A4 -- A5
---------------------------
B1 ---- B2 <--- (master)
\
B4 -- B5 <--- (branch1)
也許您的B回購未完全合並。 這可能使事情復雜化,也可能不會使事情復雜化。 如果您使用filter-branch
,那么它可以一次處理許多引用。 您可能不能只說--all
(因為它可能捕獲A
樹中已經存在的引用,並且該操作最終可能失敗),但是您可以列出B
樹中的分支提示。
git filter-branch --parent-filter 'sed "s/^\$/-p xxxxxxxxxx/"' B/master B/branch1
如果您嘗試使用變基(或者如果您只想使用單個筆尖參考),則可以創建一個臨時章魚合並。
git checkout B/master
git checkout -b b-entry-point
git merge -s ours B/branch1 B/branch2 ...
產生的合並提交是臨時的。 (您可以在移植后刪除b-入口點分支。)它僅向B
提交樹中提供了一個“入口點”。
3)A中的多個分支提示
A --- Am <-- (master)
\
Ab1 <-- (branch1)
---------------------------------------
B1 ---- B2 <-- (master)
B3 ---- B4 <-- (branch1)
那么,如果A最初沒有完全合並怎么辦? 創建存儲庫B時,是否僅創建了一個新的提交B === Am
? 我猜是這樣,因為您不得不做一些奇怪的事情,例如多個歷史樹來包含Ab1
的表示形式,並且如果您想重新合並,您稍后會有點頭疼...
如果您確實有多棵樹要嫁接,那么我認為您只需要分別處理每棵樹即可。 這樣做並不會改善。
如果您有多個嫁接點,但之后已將它們重新合並到M
,則您可能必須分別嫁接M
的每個父母,然后將合並重新創建為M'
,然后繼續將M
的孩子嫁接到M'
。
好的,但是裁判呢?
現在,上面的方法就可以了,但是您可能需要關心的引用(在A和/或B中),而不僅僅是master
分支。
這是filter-branch
處理得更好的事情之一; 實際上,如果我沒記錯的話, rebase
不會重寫任何引用,除非分支提示了它的重新引用(即使是遠程分支引用也是如此)。
尤其是如果使用filter-branch
,您可能會發現通過克隆B並從A導入遠程引用來創建C(而不是相反,如上所示)很方便,這樣您就可以讓filter-branch
為您重寫本地引用。
即使這樣,您仍可能會找到需要重新定位的遠程引用的某種組合。 可以根據需要使用帶有-f
選項的branch和tag命令,將本地引用與最適合您的最終狀態的遠程引用對齊。
您可以通過單次提交選擇一系列提交。
$ git merge A/master # merge A/master into C/master
$ git cherry-pick B2^..Bk # cherry-pick all B2 to Bk commits
或者,您可以重新設置基准。 轉到C repo master
分支。 您可以簽出新的分支(例如rebase
),然后合並B / master並將其重新基准到A/master
$ git checkout -b rebase origin/master # checkout a new branch with clean C-remote/master history
$ git merge B/master # merge B/master into C/master
$ git rebase A/master # rebase C/master onto A/master
如果所有歷史記錄都正常,則用rebase
分支替換master
。
$ git checkout rebase # make sure current branch is rebase
$ git branch -D master # delete local master branch
$ git checkout -b master # create & checkout a new 'master' branch
$ git push -f orgin master # update C-remote/master
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.