合并没有通用历史记录的git存储库,但使用公共文件

时间:2018-02-22 14:14:16

标签: git git-merge

我必须合并两个单独的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前缀。

是否有可能

  • 识别创建了WORK的ORIG中的提交和
  • 更改WORK以使整个文件路径 (即./src/FILE代替./FILE),
  • 将存储库重新合并而不会丢失历史记录?

目前我已经通过

解决了这个问题
  • git mv将WORK中的每个文件添加到新创建的./src目录
  • 将ORIG添加为遥控器,
  • ORIG/master合并到WORK的master

    git merge -X theirs --allow-unrelated-histories ORIG/master
    

    使用theirs合并策略,然后重新应用 手动更改,使用git diff查找相关部分,

但结果却是一段不洁的历史:

  • 历史并不代表WORK和ORIG提交的共同祖先。
  • 在WORK的历史记录中,文件显示在根目录中 而不是./src,而./src以外的文件不存在于access中 工作历史。

我如何制作干净,合并的历史记录?

1 个答案:

答案 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-branchY提交,改为使用root2X。换句话说,这些移植物现在永久存在于我们新的又重新改写的存储库中(另一个标志日转换,虽然运气好或计划好,相同的日,因此只有一个标志天)。