簡體   English   中英

git 將目錄移動到另一個存儲庫,同時保留歷史記錄

[英]git move directory to another repository while keeping the history

首先 - 很抱歉提出這個問題。 已經有很多關於它的話題。 但我對他們的運氣並不好。 我對 git 的不熟悉並沒有多大幫助。

我正在將一個文件夾從一個 git 存儲庫移動到另一個(已經存在)。 例如

repo-1
---- dir1
---- dir2
---- dir3
---- dir-to-move
---- dir5

repo-2
---- dir1
---- dir2
---- dir3

最后我希望回購看起來像

repo-1
---- dir1
---- dir2
---- dir3
---- dir-to-move
---- dir5

repo-2
---- dir1
---- dir2
---- dir3
---- dir-to-move

即,有一段時間dir-to-move將存在於兩個 repos 中。 但最終我會將最新的更改遷移到repo-2並從repo-1中刪除dir-to-move

我最初的研究讓我相信我需要使用filter-branch 例如

如何使用 `git format-patch` 和 `git am` 將文件從一個 git 存儲庫移動到另一個保留歷史的存儲庫

從那以后,我了解到子樹取代了這種方法。 但是它沒有按照我的預期進行。 我以為我可以做類似的事情

repo-1工作區

git subtree split -P dir-to-move -b split

拆分分支過濾為僅dir-to-move及其歷史記錄。 然后在repo-2工作區

git remote add repo-1 repo-1-url.git
git subtree add --prefix dir-to-move split

這確實移動了代碼。 它也有點包括歷史

例如

cd repo-2
git log

顯示來自repo-1 的提交

cd repo-2
git log dir-to-move

僅顯示“從提交中添加要移動的目錄....”

即包含歷史記錄,但在檢查特定文件/目錄時不會顯示。

我怎樣才能正確地做到這一點?

我無法幫助您使用git subtree ,但是使用filter-branch是可能的。

首先,您需要創建一個包含源分支和目標分支的公共存儲庫。 這可以通過在“來源”旁邊添加一個新的“遠程”並從新的遠程獲取來完成。

在源filter-branch上使用filter-branchrm -rfdir-to-move之外的所有目錄。 之后,您將擁有一個可以干凈地重新定位或合並到目標分支的提交歷史記錄。 我認為最簡單的方法是從源分支中cherry-pick所有非空提交。 這些提交的列表可以通過運行git rev-list --reverse source-branch -- dir-to-move

當然,如果dir-to-move的歷史是非線性的(已經包含了合並提交),那么cherry-pick 不會保留它,所以可以使用git merge代替。

示例創建通用存儲庫:

cd repo-2
git remote add source ../repo-1
git fetch source

示例過濾器分支

cd repo-2
git checkout -b source-master source/master
CMD="rm -rf dir1 dir2 dir3 dir5"
git filter-branch --tree-filter "$CMD"

示例cherry-pick 到目標主機

cd repo-2
git checkout master
git cherry-pick `git rev-list --reverse source-master -- dir-to-move`

FWIW,經過多次迭代后,以下內容對我有用。

將兩個 repos 克隆到臨時工作區。

git clone <repourl>/repo-1.git 
git clone <repourl>/repo-2.git
cd repo-1
git remote rm origin # delete link to original repository to avoid any accidental remote changes
git filter-branch --subdirectory-filter dir-to-move -- --all  # dir-to-move is being moved to another repo.  This command goes through history and files, removing anything that is not in the folder.  The content of this folder will be moved to root of the repo as a result. 
# This folder has to be moved to another folder in the target repo.  So, move everything to another folder.
git filter-branch -f --index-filter \
'git ls-files -s | /usr/local/bin/sed -e "s/\t\"*/&dir-to-move\//" |
    GIT_INDEX_FILE=$GIT_INDEX_FILE.new \
        git update-index --index-info &&
 mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE"' HEAD
# Above command will go through history and rewrite the history by adding dir-to-move/ to each files.  As a result, all files will be moved to a subfolder /dir-to-move.  Make sure to use gnu-sed as OSX sed doesn't handle some extensions correctly.  For eg. \t

現在切換到目標 repo 並從源中獲取所有內容。

git clone repo-2
git remote add master ../repo-1/
git pull master master --allow-unrelated-histories
git push  # push everything to remote 

以上步驟假設主分支用於源和目標。 然而,標簽和分支被忽略了。

我將dir-to-moverepo 2 首先將repo 1克隆到新位置,然后 cd 到repo 1文件夾。 在我的情況下,我將文件夾從repo 1分支developrepo 2 ,其中develop尚不存在。

git filter-branch --subdirectory-filter dir-to-move -- --all     #line 1
git remote -v                                                    #line 2
git remote set-url origin repo_2_remote_url                      #line 3
git remote -v                                                    #line 4
git push origin develop                                          #line 5

每行說明:

  1. 清除與dir-to-move無關的所有提交,刪除該文件夾外的所有文件,移動根文件夾repo 1中的所有內容。 git 文檔

只查看觸及給定子目錄的歷史記錄。 結果將包含該目錄(並且僅包含該目錄)作為其項目根目錄

  1. 您仍然指向您的 origin repo 1 url
  2. 替換當前文件夾的原始 url 以指向repo 2
  3. 檢查 URL 是否已正確更新
  4. 推送到repo 2 (新創建的,在這種情況下) develop分支

您現在可以克隆、拉取或獲取您的repo 2存儲庫。 您將在repo 2文件夾下擁有dir-to-move的內容以及預期的相關歷史記錄。

另一種解決方案是使用git-filter-repo ,這是git filter-branch 官方推薦的

在我的關於如何將影響給定路徑的提交推送到新源​​的回答中找到一個完整的示例,包括有關 Windows 用戶安裝的提示

git:將文件夾從一個存儲庫移動到另一個存儲庫,同時保留歷史記錄

cd target-repo
git remote add source ../source-repo
git fetch source
git checkout -b source source/master

# filter out history of a single folder
git filter-branch --subdirectory-filter ./dir -- --all

# put the folder history on top of your target repo history
git rebase master

你甚至可以提交關於repo-1 ,刪除以外的所有目錄dir-to-move本地(通過這種方式無需使用fitler-branch ),然后拉來repo-2

git clone https://path/to/repo-1
cd repo-1
rm dir1 dir2 dir3 dir5 -rf
git commit -m "Removed unwanted directory for repo-2
cd ../
git clone https://path/to/repo-2
cd repo-2
git remote add repo-1-src ../repo-1
git pull repo-1-src master --allow-unrelated-histories

注意:確保git版本為2.9或更高版本以使用--allow-unrelated-histories選項

這確實可以使用git subtree

在 repo-1 中創建一個子樹:

git subtree split -P dir-to-move -b <split>

split分支現在將只包含dir-to-move目錄。 您現在需要將分支從 repo-1 拉入 repo-2 中的分支。

如果 repo-2 恰好是一個新的存儲庫(例如,就在git init ),事情就像檢查一個沒有歷史記錄的分支並從 repo-1 中拉取一樣簡單。

cd repo-2
git checkout <branch>
git pull <path-to-repo-1.git> <split>

但是,如果 repo-2 是已經提交的現有存儲庫(如本問題中的情況),則需要從 repo-2 中的孤立分支合並:

cd repo-2
git checkout --orphan <temp>                    # Create a branch with no history
git pull <path-to-rpeo-1.git> <split>           # Pull the commits from the subtree
git checkout <branch>                           # Go back to the original branch
git merge --allow-unrelated-histories <temp>    # Merge the unrelated commits back
git branch -d <temp>                            # Delete the temporary branch

如果所有其他都失敗,則愚蠢的解決方案手動解決方案:

  1. 從第一個 repo 下載所有文件
  2. 創建一個新的倉庫
  3. 將第二個 repo 克隆到磁盤。
  4. 解壓要保留的第一個 repo 文件(新結構)
  5. 將更改推送到第二個 repo。
  6. 確認你有你想要的
  7. 從您不想要的第一個存儲庫中刪除文件/文件夾。

你當然失去了可追溯性......

我最喜歡的解決方案是http://blog.neutrino.es/2012/git-copy-a-file-or-directory-from-another-repository-preserving-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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM