能否以某种方式确定WORK是基于ORIG的哪个提交创建的?
易:最简单的方法是记住它。另一种方法是查找源树与已保存子树匹配的提交记录。这很困难(但不是不可能的,如果您愿意进行确切的子树匹配,速度相对较快:使用子树的哈希ID),但打开了多个匹配的可能性:根据源存储库,很可能有许多提交符合条件。在这种情况下,有可能会有一个或多个适合的提交记录。
更改WORK,使得所有文件路径都为全路径(即./src/FILE而不是./FILE)是否可行?
是的,有点可行;或不,这要看您的意思是什么。您可以使用git filter-branch来复制存储库,并在副本中进行更改。该副本不再与原始存储库兼容,但是如果您计划有一个标志日并将所有人都转换为副本,则非常简单。
如何将两个存储库合并在一起而不丢失历史记录?
这是真正棘手的部分。Git从未真正丢失历史记录:在Git中,历史记录就是提交。提交是永久且不变的。但是,Git通过分支名称(以及其他名称,如标签)记住提交记录,因此如果您强制分支名称停止记住某些提交记录(例如,这正是git filter-branch在将所有过滤后的提交记录复制到新提交记录后所做的),那么这些提交就会被有效地遗忘。最终,如果您删除了找到这些提交的所有能力,则Git将通过垃圾回收将它们删除:git gc。
再次强调一下,这就是
git filter-branch
的工作原理:您告诉它将
每个提交复制到一个新的提交中,新的提交与原始提交非常相似,但是每个
FILE
都已重命名为
src/FILE
。然后,您使所有分支名称指向新副本的最后一个,而不是原始副本的最后一个。您删除任何保存的原始名称(
git filter-branch
会复制原始引用以防万一),删除所有其他备份安全带和安全线(
git reflog expire
等),并强制进行垃圾收集,
瞬间,您的原始提交集就消失了,只剩下替换提交。
但是:提交是快照。您拥有ORIG
中的所有快照,可以从WORK
(或通过git filter-branch
制作的修改后的替换副本)添加所有所需的快照。结果只是提交的总和。这不是两个工作集相互交织的历史记录:它只是一段历史记录,即“在<日期>这些内容被合并在一起,在此之前,我们有这两个单独的历史记录”。例如,ORIG
可能如下所示:
root--o--(history graph)---o <-- master
\ /
o--(branchy)--o--o <-- feature
你筛选后的工作
可能是这样的:
o--o
/ \
root2--o--o------o <-- master
将两者放入单个存储库中,将
WORK
的
master
更改为其他名称,您将得到以下结果:
o--o
/ \
root2--o--o------o <-- workmaster
root--o--(history graph)---o <-- master
\ /
o--(branchy)--o--o <-- feature
现在你可以运行git checkout master; git merge workmaster
,解决所有合并冲突——Git会抱怨src/*
中的每个文件在master
顶部提交和workmaster
顶部提交都有一个添加/添加冲突,因为共同的起点是“没有文件”,然后从合并结果中创建一个提交:
o--o
/ \
root2--o-------------o------o <-- workmaster
\
root--o--(history graph)---o--o <-- master
\ /
o--(branchy)--o--o <-- feature
现在你拥有一个基于ORIG
的仓库,并加入了连接两个历史记录的新提交。
如果这对你来说已经足够了,那么你现在就完成了。如果不是,接下来的内容可能并没有真正帮助,但我仍然会概述一下。
使合并更容易和/或处理历史记录
一个直接的git merge
很困难,因为所有文件都冲突了。但是,如果你找到了一个所有文件都匹配的点,你可以使用git replace
来创建一个临时移植。然后你可以更容易地进行合并,甚至可以使替换永久化(通过另一个filter-branch
,其中包含所有这意味着的内容)。
我们从与上面相同类型的图开始,但选择一个"root2"与主仓库中的某个提交X
匹配的点。请注意,我在这里也标记了root2
的子项:
o--o
/ \
root2--Y--o------o <-- workmaster
root--o--...--X----...-----o <-- master
\ /
o--(branchy)--o--o <-- feature
我们现在使用
git replace
命令告诉Git:
不要查看提交记录Y
,而是查看新的替换提交记录Y'
。这样,大多数Git会看到以下内容:
o--o
/ \
Y'---o------o <-- workmaster
/
root--o--...--X----...-----o <-- master
\ /
o--(branchy)--o--o <-- feature
提交记录 Y
和 root2
仍然存在,只是Git不再“查看”它们了(除非像git gc
这样的东西,或者你使用--no-replace-objects
选项运行任何命令)。
为了进行替换操作,我们找到了root2
后的子提交记录Y
- 如果幸运的话只有一个,但如果有多个,我们可以用 git replace
替换所有的记录,并运行以下命令:
git replace --graft <hash-of-Y> <hash-of-X>
这使得替代提交Y'
,我们得到了上面制作的绘图,现在git merge
将把X
视为合并两个分支端点的公共提交。
这样我们的合并就会更容易(也许),我们得到:
o--o
/ \
Y'---o------o <-- workmaster
/ \
root--o--...--X----...-----o--o <-- master
\ /
o--(branchy)--o--o <-- feature
作为我们的结果。
如果我们运行一个没有过滤器的 git filter-branch
命令,并且确保我们不使用 git --no-replace-objects filter-branch
,filter-branch 将会复制该代码库而不包含原始的 Y
和 root2
提交记录,相反它会使用 X
和 Y'
。 换句话说,在我们新的、再次重写的代码库中,这些嫁接是永久性的(另一个标志日更改,但是通过运气或良好的规划,可能在同一天进行,以便只有一个标志日)。