我必须合并两个单独的git存储库ORIG和WORK。 WORK是作为ORIG子目录的衍生物创建的,其中一些实验性更改尚未提交给ORIG。
~> mkdir WORK
~> cp -a ORIG/src/* WORK
~> cd WORK
~/WORK> # apply some experimental changes to WORK
~/WORK> git init
~/WORK> git add .
~/WORK> git commit -m "Entirely disconnected commit."
因此,WORK不知道它来自ORIG,并且缺少文件名的src
前缀。
是否有可能
./src/FILE
代替./FILE
),目前我已经通过
解决了这个问题git mv
将WORK中的每个文件添加到新创建的./src
目录将ORIG/master
合并到WORK的master
git merge -X theirs --allow-unrelated-histories ORIG/master
使用theirs
合并策略,然后重新应用
手动更改,使用git diff
查找相关部分,
但结果却是一段不洁的历史:
./src
,而./src
以外的文件不存在于access
中
工作历史。我如何制作干净,合并的历史记录?
答案 0 :(得分:2)
是否有可能
某种程度上非常开放,所以是的。
- 识别创建了WORK的ORIG中的提交
简单的方法是:记住它。另一种方法是找到提交,其中该提交的源树与您保存的子树匹配。这很困难(但并非不可能,而且如果您愿意进行精确的子树匹配,请相当快:使用子树的哈希ID),但会打开多个匹配的可能性:取决于源存储库,ORIG
中的许多提交似乎很可能有一个与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
等),并强制执行垃圾收集通行证,以及 poof ,您的原始提交已经消失,并且您只有替换提交。
但是:提交是快照。您拥有ORIG
中的所有快照,您可以从WORK
(或修改后的替换副本)添加您喜欢的所有快照通过git filter-branch
)制作。结果只是提交的总和。这不是两组作品交织在一起的历史:它只是一个历史,实际上说" on< date>这些被合并在一起,在此之前我们有这两个独立的历史"。例如,ORIG
可能如下所示:
root--o--(history graph)---o <-- master
\ /
o--(branchy)--o--o <-- feature
,过滤后的WORK
可能如下所示:
o--o
/ \
root2--o--o------o <-- master
将两者都放入一个存储库,将WORK
的{{1}}的名称更改为其他内容,然后您拥有:
master
您现在可以运行 o--o
/ \
root2--o--o------o <-- workmaster
root--o--(history graph)---o <-- master
\ /
o--(branchy)--o--o <-- feature
,解决所有合并冲突 - Git会抱怨git checkout master; git merge workmaster
提示中的src/*
提交中的每个文件都有添加/添加冲突,因为常见的起点是&#34;没有文件&#34; - 并从合并的结果中提交:
master
您现在拥有一个基于 o--o
/ \
root2--o-------------o------o <-- workmaster
\
root--o--(history graph)---o--o <-- master
\ /
o--(branchy)--o--o <-- feature
的存储库,其中包含一个新的提交,可以连接这两个历史记录。
如果这足以满足您的目的,那么您现在已经完成了。如果没有,其余的可能并没有真正帮助,但无论如何我都会勾勒出来。
简单ORIG
很难,因为所有文件都会发生冲突。但是,如果您找到了所有匹配的文件的点,您可以使用git merge
进行临时移植,而不是像上面那样合并。然后,您可以更轻松地合并,甚至可以使替换永久化(使用另一个git replace
,这意味着所有这些。)
我们从与上面相同的绘图开始,但选择一个点&#34; root2&#34;匹配主存储库中的一些提交filter-branch
。请注意,我也在这里标记了X
的孩子:
root2
我们现在使用 o--o
/ \
root2--Y--o------o <-- workmaster
root--o--...--X----...-----o <-- master
\ /
o--(branchy)--o--o <-- feature
告诉Git:不要查看提交git replace
,而是查看新的替换提交Y
以便大多数Git看到了这个:
Y'
提交 o--o
/ \
Y'---o------o <-- workmaster
/
root--o--...--X----...-----o <-- master
\ /
o--(branchy)--o--o <-- feature
和Y
仍在那里,只是因为Git不再在他们身上看(除了像root2
,或使用git gc
选项运行的任何命令。)
要进行此替换,我们会在--no-replace-objects
之后找到子项提交Y
- 幸运的是只有一个,但如果有几个我们可以root2
所有这些 - 并运行:
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
和Y
提交,改为使用root2
和X
。换句话说,这些移植物现在永久存在于我们新的又重新改写的存储库中(另一个标志日转换,虽然运气好或计划好,相同的日,因此只有一个标志天)。