在子目录中合并git存储库
我想合并一个远程git仓库在我的工作git仓库作为它的子目录。 我希望生成的存储库包含两个存储库的合并历史logging,并且合并存储库中的每个文件都保留其在远程存储库中的历史logging。 我尝试使用如何使用子树合并策略中提到的子树策略 ,但在遵循该过程后,尽pipe生成的存储库确实包含两个存储库的合并历史logging,但来自远程文件的单个文件并没有保留其历史logging(他们中的任何一个“git日志”只显示一条消息“合并分支…”)。
另外我不想使用子模块,因为我不希望这两个组合的git存储库分开了。
是否有可能合并另一个远程git存储库作为子目录与来自远程存储库保存其历史的个别文件?
非常感谢您的帮助。
编辑:我目前正在尝试一个解决scheme,使用git filter-branch来重写合并存储库的历史logging。 它似乎工作,但我需要再testing一下。 我会回来报告我的发现。
编辑2:希望我让自己更清楚我给了我用git的子树战略,这导致远程存储库的文件的历史明显丢失的确切命令。 让A是我目前工作的git repo,B是git repo,我想把它作为子目录并入到A中。 它做了以下事情:
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."
在这些命令进入目录subdir / Iwant / to / put / B / in后,我看到B的所有文件,但是git log
其中的任何一个都只显示提交消息“合并B作为subdir / Iwant / /把/ B /中“。 它们在B中的文件历史logging丢失。
什么似乎工作(因为我是一个初学者,我可能是错的)是以下几点:
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命令是从git help filter-branch
,其中我只改变了subdirpath。
得到了更完整的解释后,我想我明白了,无论如何,最后我有一个解决方法。 具体来说,我相信正在发生的事情是重命名检测正在被–prefix的子树合并愚弄。 这是我的testing案例:
mkdir -pz/az/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 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,每个提交一个提交。 我们做一个子树合并,然后我们在新的子树中做最后的提交。
运行gitk
(z / a)显示历史确实出现,我们可以看到它。 运行git log
显示历史确实出现。 但是,看一个特定的文件有一个问题: git log bdir/B
那么,我们可以玩的一个技巧。 我们可以使用–follow来查看特定文件的预重命名历史logging。 git log --follow -- B
这是好的,但不是很好,因为它没有链接预合并的历史和合并后的历史。
我试着玩-M和-C,但是我不能让它跟随一个特定的文件。
所以,我认为解决scheme是告诉git有关将作为子树合并的一部分进行的重命名。 不幸的是,git-read-tree对于子树的合并非常挑剔,所以我们必须通过一个临时目录,但是在我们提交之前这个目录可能会消失。 之后,我们可以看到完整的历史。
首先,创build一个“A”仓库并进行一些提交:
mkdir -pz/az/b cd z/a git init echo A>A git add A git commit -m A echo AA>>A git commit -a -m AA
其次,创build一个“B”存储库并进行一些提交:
cd ../b git init echo B>B git add B git commit -m B echo BB>>B git commit -a -m BB
这个工作的诀窍是 :强制Git通过创build一个子目录并将内容移动到其中来识别重命名。
mkdir bdir git mv B bdir git commit -a -m bdir-rename
返回存储库“A”并获取并合并“B”的内容:
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"
为了表明他们现在合并:
cd bdir echo BBB>>B git commit -a -m BBB
为了certificate完整的历史被保存在一个连接的链中:
git log --follow B
我们在这样做之后得到了历史,但问题是,如果实际上保留旧的“b”回购协议并偶尔合并(假设它实际上是第三方单独维护的回购协议),那么自第三方将不会做重命名。 你必须尝试合并新的变化到你的版本与重命名,我担心,不会顺利。 但是,如果b离开,你赢了。
git-subtree
是一个脚本,专门用于将多个存储库合并为一个,同时保留历史logging(和/或分割子树的历史logging,尽pipe这似乎与此问题无关)。 它是自1.7.11版以来作为git树的一部分发布的 。
要将版本<rev>
的存储库<repo>
合并为子目录<prefix>
,请使用git subtree add
,如下所示:
git subtree add -P <prefix> <repo> <rev>
git-subtree以更友好的方式实现了子树合并策略 。
如果你真的想把东西缝在一起,看起来嫁接。 你也应该使用git rebase --preserve-merges --onto
。 还有一个选项可以保留提交者信息的作者date。
你有没有尝试添加额外的存储库作为一个git子模块? 它不会将历史与包含的存储库合并,事实上,它将是一个独立的存储库。
我提到它,因为你没有。
我发现以下解决scheme对我可行。 首先我进入项目B,创build一个新的分支,其中所有的文件将被移动到新的子目录。 然后我推动这个新的分支来源。 接下来我去项目A,添加和获取B的远程,然后我签出移动的分支,我回到主和合并:
# 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 log --follow
,仍然有历史。
我不是一个混帐专家,所以我不能评论这是一个特别好的解决scheme,或者如果它有警告,但到目前为止,似乎一切正常。