Merge two Git repositories without breaking file history
我需要将两个Git存储库合并到一个全新的第三个存储库中。我发现了许多关于如何使用子树合并(例如JakubNar)来实现这一点的描述。BSKI关于如何合并两个Git存储库的答案?)遵循这些指示基本上是可行的,除了当我提交子树时,合并来自旧存储库的所有文件都被记录为新添加的文件。当我执行
是否有任何方法可以合并存储库并保持单个文件历史完整?
事实证明,如果您只是简单地尝试将两个存储库粘合在一起,并使其看起来一直都是这样,而不是管理一个外部依赖关系,那么答案就简单多了。您只需将远程设备添加到旧的Repo中,将它们合并到新的主服务器中,将文件和文件夹移动到子目录中,提交移动,然后对所有其他Repo重复此操作。子模块、子树合并和花哨的钢筋旨在解决稍有不同的问题,不适合我正在尝试的工作。
下面是将两个存储库粘合在一起的PowerShell脚本示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | # 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" |
显然,如果您愿意的话,您可以将旧的_b合并为旧的_a(这将成为新的组合回购),修改脚本以适应。
如果您还希望引入正在进行的功能分支,请使用以下命令:
1 2 3 | # 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我们重命名了目标,这有助于Git正确地排列所有内容。
我在这里写了一个更详细的解释。
这是一种不重写任何历史记录的方法,因此所有提交ID都将保持有效。最终结果是第二个repo的文件将结束在一个子目录中。
将第二个repo添加为远程:
1 2 | cd firstgitrepo/ git remote add secondrepo username@servername:andsoon |
确保您已下载所有secondrepo的提交:
1 | git fetch secondrepo |
从第二个回购分支机构创建本地分支机构:
1 | git branch branchfromsecondrepo secondrepo/master |
将其所有文件移动到子目录中:
1 2 3 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/" |
将第二个分行合并为第一个回购主分行:
1 2 | git checkout master git merge --allow-unrelated-histories branchfromsecondrepo |
您的存储库将有多个根提交,但这不会造成问题。
请看一下使用
1 | git rebase --root --preserve-merges --onto |
把他们早期的两个历史联系起来。
如果有重叠的路径,请使用
1 | git filter-branch --index-filter |
使用日志时,请确保使用
1 | git log -CC |
这样,您就可以在路径中找到文件的任何移动。
我把这个解决方案从@flimm变成了这样的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | [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" |
此功能将远程repo克隆到本地repo目录:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | 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" } |
如何使用:
1 2 | cd current/package git-add-repo https://github.com/example/example dir/to/save |
利润!
几年过去了,有很多基于投票的解决方案,但是我想分享我的,因为它有点不同,因为我想将两个远程存储库合并为一个新的存储库,而不从以前的存储库中删除历史。
在GitHub中创建新的存储库。
下载新创建的repo并添加旧的远程存储库。
1 2 3 4 | git clone https://github.com/alexbr9007/Test.git cd Test git remote add OldRepo https://github.com/alexbr9007/Django-React.git git remote -v |
从旧repo中获取所有文件,以便创建新的分支。
1 2 | git fetch OldRepo git branch -a |
在主分支中,进行合并以将旧回购与新创建的回购合并。
1 | git merge remotes/OldRepo/master --allow-unrelated-histories |
创建一个新文件夹以存储从Oldrepo添加的所有新创建内容,并将其文件移动到此新文件夹中。
最后,您可以从组合的repos上传文件,并从github安全删除oldrepo。
希望这对处理合并远程存储库的任何人都有用。
按照步骤将一个回购嵌入到另一个回购中,通过合并两个git历史记录来拥有一个git历史。
git clone [email protected]:user/parent-repo.git
git clone [email protected]:user/child-repo.git
cd child-repo/
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'
cd ../parent-repo/
git remote add child-remote ../child-repo/
git fetch child-remote
git merge --allow-unrelated-histories child-remote/master
如果现在检查父repo中的git日志,则应该合并子repo提交。您还可以看到从提交源指示的标记。
下面的文章帮助我将一个回购嵌入到另一个回购中,通过合并两个git历史记录,获得了一个git历史。
http://ericlathrop.com/2014/01/combing-git-stores/
希望这有帮助。快乐编码!