在不破坏文件历史记录的情况下合并两个Git存储库

Eri*_*Lee 209 git git-subtree

我需要将两个Git存储库合并到一个全新的第三个存储库中.我已经找到了很多关于如何使用子树合并进行此操作的描述(例如JakubNarębski关于如何合并两个Git存储库的答案)并遵循这些说明大多数工作,除了当我提交子树合并所有文件从旧存储库记录为新添加的文件.我可以在旧的存储库中看到提交历史记录git log,但如果我这样做git log <file>,则只显示该文件的一次提交 - 子树合并.从对上述答案的评论来看,我并不是唯一一个看到这个问题的人,但我发现没有公开的解决方案.

有没有办法合并存储库并保持个别文件历史记录完整?

Eri*_*Lee 255

事实证明,如果您只是尝试将两个存储库粘合在一起并使其看起来像是一直这样,而不是管理外部依赖,那么答案会更简单.您只需将遥控器添加到旧的存储库,将它们合并到新的主存储器,将文件和文件夹移动到子目录,提交移动,然后重复所有其他存储库.子模块,子树合并和花哨的rebase旨在解决稍微不同的问题,并不适合我试图做的事情.

这是一个将两个存储库粘合在一起的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
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"
Run Code Online (Sandbox Code Playgroud)

显然你可以将old_b合并到old_a(它成为新的组合repo),如果你更愿意这样做 - 修改脚本以适应.

如果您还要引入正在进行的功能分支,请使用以下命令:

# 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
Run Code Online (Sandbox Code Playgroud)

这是该过程中唯一不明显的部分 - 这不是子树合并,而是普通递归合并的参数,告诉Git我们重命名了目标,这有助于Git正确排列所有内容.

在这里写了一个稍微详细的解释.

  • 使用`git mv`的这个解决方案不能很好地工作.当你稍后在其中一个移动的文件上使用`git log`时,你只能从移动中获得提交.所有以前的历史都丢失了.这是因为`git mv`真的是`git rm; git add`但[一步到位](http://stackoverflow.com/a/1094392/959352). (15认同)
  • 它与Git中的任何其他移动/重命名操作相同:从命令行,您可以通过执行`git log --follow`获取所有历史记录,或者所有GUI工具自动为您执行此操作.根据子树合并,你不能**获取单个文件的历史记录,据我所知,所以这种方法更好. (13认同)
  • 当我尝试"dir -exclude old_a |%{git mv $ _.Name old_a}"时,我得到sh.exe":dir:command not found and sh.exe":git:command not found.使用它的工作原理:ls -I old_a | xargs -I'{}'git mv'{}'old_a / (7认同)
  • 在 osx/macos 上 ```dir -exclude old_a | %{git mv $_.Name old_a}``` 可以是 ```ls | grep -v 'old_a' | grep -v 'old_a' | xargs -I '{}' git mv '{}' old_a``` (4认同)
  • @EricLee当合并old_b repo时,我遇到了很多合并冲突.这是预期的吗?我得到了CONFLICT(重命名/删除) (3认同)
  • 对于`ls`来说,这是'1'(第一个),对于'xargs`来说是'eye'.谢谢你的提示! (3认同)

Fli*_*imm 140

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

  1. 将第二个repo添加为远程:

    cd firstgitrepo/
    git remote add secondrepo username@servername:andsoon
    
    Run Code Online (Sandbox Code Playgroud)
  2. 确保您已经下载了所有secondrepo的提交:

    git fetch secondrepo
    
    Run Code Online (Sandbox Code Playgroud)
  3. 从第二个repo的分支创建一个本地分支:

    git branch branchfromsecondrepo secondrepo/master
    
    Run Code Online (Sandbox Code Playgroud)
  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/"
    
    Run Code Online (Sandbox Code Playgroud)
  5. 将第二个分支合并到第一个repo的主分支中:

    git checkout master
    git merge --allow-unrelated-histories branchfromsecondrepo
    
    Run Code Online (Sandbox Code Playgroud)

您的存储库将有多个root提交,但这不会造成问题.

  • @MartijnHeemels对于旧版本的Git,只需省略`--allow-unrelated-histories`.查看此答复帖的历史记录. (4认同)

x-y*_*uri 15

假设您想将存储库合并ab(我假设它们并排放置):

cd b
git remote add a ../a
git fetch a
git merge --allow-unrelated-histories a/master
git remote remove a
Run Code Online (Sandbox Code Playgroud)

如果您想放入a子目录,请在执行上述命令之前执行以下操作:

cd a
git filter-repo --to-subdirectory-filter a
cd ..
Run Code Online (Sandbox Code Playgroud)

为此,您需要git-filter-repo安装(filter-branch劝阻)。

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

更多关于它在这里

  • 请注意,“git filter-repo”将重写历史记录。如果您不想这样做,可以使用“git mv”。 (2认同)

aba*_*sta 14

几年过去了,有一些基于良好投票的解决方案,但我想分享我的解决方案,因为它有点不同,因为我想将 2 个远程存储库合并到一个新的存储库中,而不删除以前存储库的历史记录。

  1. 在 Github 中创建一个新的存储库。

    在此处输入图片说明

  2. 下载新创建的 repo 并添加旧的远程存储库。

    git clone https://github.com/alexbr9007/Test.git
    cd Test
    git remote add OldRepo https://github.com/alexbr9007/Django-React.git
    git remote -v
    
    Run Code Online (Sandbox Code Playgroud)
  3. 从旧 repo 中获取所有文件,以便创建一个新分支。

    git fetch OldRepo
    git branch -a
    
    Run Code Online (Sandbox Code Playgroud)

    在此处输入图片说明

  4. 在 master 分支中,进行合并以将旧 repo 与新创建的 repo 合并。

    git merge remotes/OldRepo/master --allow-unrelated-histories
    
    Run Code Online (Sandbox Code Playgroud)

    在此处输入图片说明

  5. 创建一个新文件夹来存储从 OldRepo 添加的所有新创建的内容,并将其文件移动到这个新文件夹中。

  6. 最后,您可以从合并的存储库上传文件并从 GitHub 安全地删除 OldRepo。

希望这对处理合并远程存储库的任何人都有用。

  • 这是对我来说保存 git 历史记录的唯一解决方案。不要忘记使用“git Remote rm OldRepo”删除旧存储库的远程链接。 (3认同)
  • 我对这一点赞不绝口。一个非常简单、成功、明智的解决方案。谢谢你!感谢@Harubiyori 的最后润色。 (2认同)

Ada*_*ruk 8

请看看使用情况

git rebase --root --preserve-merges --onto
Run Code Online (Sandbox Code Playgroud)

在他们生命的早期联系两个历史.

如果您有重叠的路径,请使用它们进行修复

git filter-branch --index-filter
Run Code Online (Sandbox Code Playgroud)

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

git log -CC
Run Code Online (Sandbox Code Playgroud)

这样你就可以在路径中找到任何文件移动.

  • Git 文档建议不要变基... https://git-scm.com/book/en/v2/Git-Branching-Rebasing#_rebase_peril (2认同)

Fre*_*son 5

我将解决方案从@Flimm改为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"
Run Code Online (Sandbox Code Playgroud)

  • 只是好奇:你真的经常这样做需要别名吗? (8认同)
  • 不,我不知道,但从不记得怎么做,所以别名只是我记住它的一种方式。 (3认同)
  • 是的..但是尝试更换计算机并忘记移动您的别名;) (2认同)