git - merge 没有通用历史记录但具有通用文件的git存储库

标签 git git-merge

我必须合并两个独立的git存储库orig和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,并且缺少文件名的前缀。
有没有可能
在orig中标识创建工作的提交,以及
更改工作以使完整文件路径
(即src而不是./src/FILE)存在,
合并存储库而不丢失历史?
目前我已经通过
将工作中的每个文件放到新创建的目录中,
将orig添加为远程,
./FILE合并到工作git mv
git merge -X theirs --allow-unrelated-histories ORIG/master

使用./src合并策略,并重新应用
手动更改,使用ORIG/master查找相关部分,
但其结果充其量只是一段不洁的历史:
历史并不代表工作和原始承诺的共同祖先。
在工作历史中,文件出现在根目录中
而不是在master和来自外部的文件theirs不存在于
工作史。
我该如何创造一个干净的,融合的历史?

最佳答案

有没有可能
不知怎么的,是非常开放的,所以是的。
在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创建的修改后的替换副本)添加到其中。结果只是提交的总和。这不是一部两部作品交织在一起的历史:它只是一部说,实际上,“在<日期>这两部作品合并在一起,在那之前,我们有这两部独立的历史”。例如,ORIG可能如下所示:

root--o--(history graph)---o   <-- master
       \                  /
        o--(branchy)--o--o   <-- feature

过滤后的WORK可能如下所示:
            o--o
           /    \
root2--o--o------o   <-- master

将两者放在一个存储库中,将WORK'smaster的名称更改为其他名称,您将拥有:
            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提示提交中)都有一个add/add冲突,因为常见的起点是“没有文件”-并根据合并的结果进行提交:
                       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”与主存储库中的一些commitX匹配。注意,我在这里也标记了root2的孩子:
            o--o
           /    \
root2--Y--o------o   <-- workmaster

root--o--...--X----...-----o   <-- master
       \                  /
        o--(branchy)--o--o   <-- feature

我们现在使用git replace来告诉git:不要查看commitY,而是查看新的替换commitY',这样大多数git都会看到:
                       o--o
                      /    \
                Y'---o------o   <-- workmaster
               /
root--o--...--X----...-----o   <-- master
       \                  /
        o--(branchy)--o--o   <-- feature

提交Yroot2仍然存在,只是git不再查看它们(除了git gc或使用--no-replace-objects选项运行的任何命令之外)。
要进行此替换,我们会在Y之后找到子提交root2-幸运的是只有一个,但是如果有几个,我们可以将它们全部提交并运行:
git replace --graft <hash-of-Y> <hash-of-X>

这使得替换提交git replace。这给了我们上面的绘图,现在Y'将把git merge作为合并两个分支提示的公共提交。
我们的合并会更容易(也许)并且我们得到:
                       o--o
                      /    \
                Y'---o------o   <-- workmaster
               /             \
root--o--...--X----...-----o--o   <-- master
       \                  /
        o--(branchy)--o--o   <-- feature

作为我们的结果。
如果我们运行一个no filterX-并且我们确保不使用git filter-branch-filter分支将复制存储库,而不使用原始的git --no-replace-objects filter-branchY提交,而是使用root2X提交。换言之,这些嫁接,现在是永久的,在我们的新的,又一次重写储存库(另一个国旗日转换,虽然运气好或计划好,同一天,只有一个国旗日)。

关于git - merge 没有通用历史记录但具有通用文件的git存储库,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48929586/

相关文章:

Git:将提交 merge 到不同的分支

未在 Windows 上执行 Git clean 过滤器

linux - Bash、变量和转义符号

git - 如何将两个独立的不相关的 Git 存储库 merge 为一个具有单一历史时间轴的存储库

git - 恢复 git merge --abort 后删除的 git 文件

git - 我如何找出哪个 Git 提交导致冲突?

git - 标签和分支 merge

linux - 通过加载项管理器对 monodevelop 的 Fsharp 绑定(bind)无法解析依赖项

git - 将丑陋的代码提交到 git 存储库的约定是什么?

git - 使用 Git,如何记录两个特定分支之间 merge 提交的所有提交?