I need to merge two Git repositories into a brand new, third repository. I've found many descriptions of how to do this using a subtree merge (for example Jakub Narębski's answer on How do you merge two Git repositories?) and following those instructions mostly works, except that when I commit the subtree merge all of the files from the old repositories are recorded as new added files. I can see the commit history from the old repositories when I do git log, but if I do git log <file> it shows only one commit for that file - the subtree merge. Judging from the comments on the above answer, I'm not alone in seeing this problem but I've found no published solutions for it.

有没有办法合并存储库,并保持单个文件历史完整?


当前回答

该函数将远程回购克隆到本地回购目录:

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

通知。这个脚本可以重写提交,但会保存所有作者和日期,这意味着新的提交将有另一个哈希值,如果你试图将更改推到远程服务器,它只能用强制键,也会重写服务器上的提交。所以请在启动前进行备份。

利润!

其他回答

我把解决方案从@Flimm this变成了一个git别名,就像这样(添加到我的~/.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"

假设你想要合并存储库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)。

一个合并两个大型存储库的示例,将其中一个存储库放入子目录:https://gist.github.com/x-yuri/9890ab1079cf4357d6f269d073fd9731

这里有更多。

请看一下使用方法

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

将他们早年的两段历史联系起来。

如果有重叠的路径,用

git filter-branch --index-filter

当您使用日志时,确保您“更难找到副本”

git log -CC

这样,您将发现路径中文件的任何移动。

我根据x-yuri的答案创建了一个库,其中使用了filter-repo。使用我的脚本,您可以轻松地将所有分支和标记移动到新的存储库中,而不会因为指定不同的子dirs而发生合并冲突。

按照以下步骤,将一个repo嵌入到另一个repo中,通过合并两个git历史,从而拥有一个git历史。

克隆你想合并的两个回购。

Git克隆git@github.com:user/parent-repo.git Git克隆git@github.com:user/child-repo.git

转到child repo

cd child-repo /

运行以下命令,将路径my/new/subdir(3次出现)替换为您想要拥有子repo的目录结构。

Git filter-branch - pruneempty -tree filter ' 如果[!-e my/new/subdir];然后 Mkdir -p my/new/subdir git ls-tree——name-only $GIT_COMMIT | xargs -I files mv files my/new/subdir fi '

去父回购

cd . . / parent-repo /

为父repo添加一个远程,指向子repo的路径

Git远程添加child-remote ../child-repo/

取回子repo

Git获取子远程

合并历史

Git merge -allow-unrelated- history - child-remote/master

如果你现在检查父repo中的git日志,它应该已经合并了子repo提交。您还可以看到来自提交源的标记。

下面的文章帮助我将一个回购嵌入到另一个回购中,通过合并两个git历史,拥有一个单一的git历史。

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

希望这能有所帮助。 编码快乐!