Merge git repository in subdirectory
我想将工作中的Git存储库中的远程Git存储库合并为它的子目录。我希望得到的存储库包含两个存储库的合并历史,并且合并后的存储库的每个文件都保留其在远程存储库中的历史。我尝试使用子树策略,如如何使用子树合并策略中所述,但在执行该过程之后,虽然生成的存储库确实包含两个存储库的合并历史,但来自远程存储库的单个文件没有保留它们的历史(其中任何一个存储库上的"git log"只显示一条消息"merged分支……"。
另外,我不想使用子模块,因为我不希望两个组合的Git存储库再分开。
是否可以将远程Git存储库合并到另一个存储库中作为子目录,并保留来自远程存储库的各个文件的历史记录?
非常感谢你的帮助。
编辑:我目前正在尝试一种解决方案,该解决方案使用git filter分支重写合并后的知识库历史记录。它看起来确实有效,但我需要再测试一下。我会回去报告我的发现。
编辑2:希望我能更清楚地说明,我给出了与Git子树策略一起使用的确切命令,这会导致远程存储库文件的历史记录明显丢失。假设a是我目前正在工作的git repo,b是我想作为它的子目录合并到a中的git repo。它执行了以下操作:
1 2 3 4 | git remote add -f B <url-of-B> git merge -s ours --no-commit B/master git read-tree --prefix=subdir/Iwant/to/put/B/in/ -u B/master git commit -m"Merge B as subdirectory in subdir/Iwant/to/put/B/in." |
在这些命令之后,进入目录subdirectory/iwant/to/put/b/in,我看到b的所有文件,但是其中任何一个文件上的
似乎有用的(因为我是Git的初学者,我可能错了)是:
1 2 3 4 5 6 7 8 9 | git remote add -f B <url-of-B> git checkout -b B_branch B/master # make a local branch following B's master git filter-branch --index-filter \ 'git ls-files -s | sed"s-\t"*-&subdir/Iwant/to/put/B/in/-" | GIT_INDEX_FILE=$GIT_INDEX_FILE.new \ git update-index --index-info && mv"$GIT_INDEX_FILE.new""$GIT_INDEX_FILE"' HEAD git checkout master git merge B_branch |
上面关于filter branch的命令来自
要将修订版
1 | git subtree add -P [cc] <repo> <rev> |
Git子树以更加用户友好的方式实现子树合并策略。
在得到了对正在发生的事情的更全面的解释之后,我想我理解它,在任何情况下,在底部我都有一个解决办法。具体地说,我相信正在发生的是重命名检测被子树合并--prefix所愚弄。这是我的测试用例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | mkdir -p z/a z/b cd z/a git init echo A>A git add A git commit -m A echo AA>>A git commit -a -m AA cd ../b git init echo B>B git add B git commit -m B echo BB>>B git commit -a -m BB cd ../a git remote add -f B ../b git merge -s ours --no-commit B/master git read-tree --prefix=bdir -u B/master git commit -m"subtree merge B into bdir" cd bdir echo BBB>>B git commit -a -m BBB |
我们制作Git目录A和B,每个目录有几个提交。我们进行子树合并,然后进行新子树中的最后一次提交。
运行
好吧,我们可以玩一个把戏。我们可以使用--follow查看特定文件的预重命名历史记录。
我试着玩-m和-c,但我没能让它按照一个特定的文件。
所以,我觉得解决方案是告诉Git将作为子树合并的一部分发生的重命名。不幸的是,gitreadtree对于子树合并非常挑剔,所以我们必须通过一个临时目录来工作,但是在提交之前,这可能会消失。之后,我们可以看到完整的历史。
首先,创建一个"A"存储库并进行一些提交:
1 2 3 4 5 6 7 8 | mkdir -p z/a z/b cd z/a git init echo A>A git add A git commit -m A echo AA>>A git commit -a -m AA |
其次,创建一个"B"存储库并进行一些提交:
1 2 3 4 5 6 7 | cd ../b git init echo B>B git add B git commit -m B echo BB>>B git commit -a -m BB |
实现这一点的诀窍是:通过创建子目录并将内容移动到其中,强制Git识别重命名。
1 2 3 | mkdir bdir git mv B bdir git commit -a -m bdir-rename |
返回到存储库"A",获取并合并"B"的内容:
1 2 3 4 5 | cd ../a git remote add -f B ../b git merge -s ours --no-commit B/master git read-tree --prefix= -u B/master git commit -m"subtree merge B into bdir" |
要显示它们现在已合并:
1 2 3 | cd bdir echo BBB>>B git commit -a -m BBB |
为了证明完整的历史记录保存在一个连接的链中:
1 | git log --follow B |
这样做之后,我们就可以了解历史了,但问题是,如果您确实保留了旧的"B"回购协议,并且偶尔从中合并(假设它实际上是第三方单独维护的回购协议),则会遇到麻烦,因为第三方不会进行重命名。您必须尝试将新的更改合并到您的B版本和重命名,我担心这不会顺利进行。但是如果B要离开,你就赢了。
如果你真的想把东西缝在一起,那就去找嫁接吧。您还应该使用
我想
步骤1:重写源存储库中的历史记录,使其看起来所有文件都始终存在于子目录下。
为重写的历史创建临时分支。
1 | git checkout -b tmp_subdir |
然后使用
1 2 3 4 5 | git filter-branch --prune-empty --tree-filter ' if [ ! -e foo/bar ]; then mkdir -p foo/bar git ls-tree --name-only $GIT_COMMIT | xargs -I files mv files foo/bar fi' |
步骤2:切换到目标存储库。将源存储库作为远程存储添加到目标存储库中并获取其内容。
1 2 | git remote add sourcerepo .../path/to/sourcerepo git fetch sourcerepo |
步骤3:使用
1 | git rebase --preserve-merges --onto master --root sourcerepo/tmp_subdir |
你可以查看日志,看看这真的得到了你想要的。
1 | git log --stat |
步骤4:在重新平衡之后,您处于"分离的头部"状态。你可以把大师快进新的头脑。
1 2 3 4 | git checkout -b tmp_merged git checkout master git merge tmp_merged git branch -d tmp_merged |
步骤5:最后进行一些清理:删除临时远程。
1 | git remote rm sourcerepo |
我发现以下解决方案对我可行。首先,我进入项目B,创建一个新的分支,其中已经存在的所有文件都将移动到新的子目录中。然后我把这个新的分支推到原点。接下来,我转到项目A,添加并获取b的远程文件,然后签出移动的分支,返回master并合并:
1 2 3 4 5 6 7 8 9 10 11 12 | # in local copy of project B git checkout -b prepare_move mkdir subdir git mv <files_to_move> subdir/ git commit -m 'move files to subdir' git push origin prepare_move # in local copy of project A git remote add -f B_origin <remote-url> git checkout -b from_B B_origin/prepare_move git checkout master git merge from_B |
如果我转到
我不是一个git专家,所以我不能评论这是一个特别好的解决方案,或者它是否有警告,但到目前为止,一切似乎都很好。
您是否尝试将额外的存储库添加为Git子模块?它不会将历史记录与包含存储库合并,事实上,它将是一个独立的存储库。
我提过,因为你没有。