合并两个Git仓库而不会破坏文件历史logging

我需要将两个Git仓库合并成一个全新的第三仓库。 我已经find了许多关于如何使用子树合并(例如JakubNarębski关于如何合并两个Git存储库 的答案 )的说明以及遵循这些说明的主要工作,除了当我提交子树合并所有文件从旧版本库被logging为新添加的文件。 当我做git log ,我可以看到来自旧版本库的提交历史git log ,但是如果我做的是git log <file>那么它只会显示一个对该文件的提交 – 子树合并。 从上述答案的评论来看,我并不是一个人看到这个问题,但我没有find任何发布的解决scheme。

有什么办法可以合并存储库,并保持个人文件的历史logging不变?

事实certificate,如果您只是试图将两个存储库粘合在一起,并使其看起来像一直如此,而不是pipe理外部依赖关系,则答案会更简单。 您只需将遥控器添加到旧的回购站,将它们合并到新的主控室,将文件和文件夹移动到子目录,提交移动并重复所有额外的回购。 子模块,子树合并和花式重定义是为了解决一个稍微不同的问题,并不适合我正在尝试做的事情。

以下是将两个存储库粘合在一起的示例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 dir > deleteme.txt git add . git commit -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 # Clean up our dummy file because we don't need it any more git rm .\deleteme.txt git commit -m "Clean up initial file" # 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" 

显然,如果你愿意的话,你可以将old_b合并到old_a(这将成为新的联合回购) – 修改脚本以适应。

如果您还想要引入进行中的function分支,请使用以下命令:

 # 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 

这是进程中唯一不明显的部分 – 这不是一个子树合并,而是正常recursion合并的一个参数,告诉Git我们重命名了目标,并帮助Git正确地排列所有内容。

我在这里写了一个更详细的解释。

这是一种不重写任何历史logging的方法,因此所有提交ID都将保持有效。 最终的结果是第二个repo的文件将在一个子目录中结束。

  1. 添加第二个回购作为一个远程:

     cd firstgitrepo/ git remote add secondrepo username@servername:andsoon 
  2. 确保你已经下载了所有secondrepo的提交:

     git fetch secondrepo 
  3. 从第二个回购分行创build一个本地分行:

     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 

您的存储库将有多个根提交,但这不应该造成问题。

请看看使用

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

在他们的生活早期连接两个历史。

如果你有重叠的path,请修复它们

 git filter-branch --index-filter 

当你使用日志时,确保你“find更难的副本”

 git log -CC 

这样你会发现path中的文件的任何移动。

我把@Flimm这个解决scheme变成了一个像这样的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" 

这个function将远程回购克隆到本地回购目录中:

 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 

利润!