【问题标题】:Git merge does not deleting files from another repositoryGit合并不会从另一个存储库中删除文件
【发布时间】:2018-08-13 17:00:26
【问题描述】:

存在两个存储库:rep1、rep2。 两者都是来自同一个 SVN 实例的单向同步的结果。 (SVN中的变化同步到git,但是git中的变化没有同步回SVN) 这两个存储库不是彼此的分支。

rep1/master 分支包含一个文件 file1.txt

rep2/master 分支包含文件 file1.txt,但该文件在 rep2/ 中被删除

rep2 作为远程添加到 rep1 中以便合并。 (远程2)

分支 rep2/master 已创建

git checkout -b rep2_master_branch --track rep2/master

从 rep1/master 的尖端创建一个“合并”分支

git checkout -b merge-master-branches master 

使用选项“他们的”和壁球执行合并。 由于合并来自另一台服务器,因此需要允许无关历史记录。

git merge -Xtheirs --squash rep2_master_branch  --allow-unrelated-histories

观察到的是 rep1/file1.txt 没有被删除。

进一步观察,合并期间的冲突已正确解决,修改和添加的文件也是如此。似乎唯一遗漏的是在 rep2 中已删除的文件。

我该如何解决这个问题?或者更好的是,如何解决?

【问题讨论】:

    标签: git git-merge git-remote svnsync


    【解决方案1】:

    就 Git 而言,正确的合并结果。

    Git 的git merge 通过将两个分支提示与一个公共合并基础进行比较来工作。也就是说,如果你绘制提交图,它看起来像这样:

                 o--o--L   <-- branch1
                /
    ...--o--o--*
                \
                 o--o--R   <-- branch2
    

    然后将branch1 (commit L) 与branch2 (commit R) 合并的结果将通过比较commit *(两个分支派生的共同点)的内容来计算提交L的内容和提交R的内容。

    *L 的任何变化都发生在左侧。从*R 的任何变化都发生在右侧。 Git 将这两个“发生的事情”结合起来,将这些结合后的更改应用于* 的内容,如果一切顺利,则从结果中进行新的提交。

    但是您使用的是--allow-unrelated-histories在没有共同提交时才需要这样做:我们绘制了 LR 的历史记录,发现它们从未偏离共同点。例如,我们可能会得到:

    A--o--o--...--L   <-- branch1
    
    B--o--...--o--R   <-- branch2
    

    ABboth 根提交。那么应该 Git 使用什么作为共同的起点,来弄清楚从那时起发生了什么变化?

    人们可以为自己选择的各种起点争论不休,但 Git 的回答是:使用空提交。 也就是说,使用 --allow-unrelated-histories,Git 会假装有一个没有文件的公共提交完全没有:

      A--o--o--...--L   <-- branch1
     /
    *
     \
      B--o--...--o--R   <-- branch2
    

    比较空提交* 和提交L,Git 发现branch1 上的每个文件都是以L 中的形式新创建的。比较*R,Git 发现branch2 上的每个文件都是以R 中的形式新创建的。无论这些文件在哪里匹配,一切都很好:我们只需要那个文件。无论L 有一个R 没有的文件,反之亦然,一切都很好:我们只需要那个文件。只要LR 都有文件,但它们的内容不同,就会发生合并冲突。

    由于您使用了-X theirs,Git 解决了每个冲突,方法是从空的合并基础提交中获取“他们的”更改(“他们的”是您命名的提交,而不是您作为 HEAD 进行的提交)。

    如果你想让 Git 假装其他提交是合并基础,你可以使用 git replace --graft 插入一些假的父链接。例如,假设您想在任意选择的提交 C 处将两者假绑定在一起:

    A--o--o--...--L   <-- branch1
             /
    B--...--C--...--R   <-- branch2
    

    以便 Git 将 CL 进行比较,并将 CR 进行比较。就git merge 本身而言,您在A--...--L 链中选择哪个提交并不重要; 任何 提交都可以(包括 AL 本身),但请注意 Git 只会“看到”自您选择的提交以来的更改(由于 C-vs-@987654368 的差异@ 和 C-vs-R)。因此,在链中选择一些适当的提交并运行:

    git replace --graft <hash-ID-of-chosen-commit> <hash-ID-of-C>
    

    并且 Git 现在将使用替换(移植)提交而不是选择的提交。 git merge 现在将使用 C 的内容作为公共源。 (您可以在git merge 退出后立即删除嫁接,即使它因合并冲突而退出。因此,您可以编写一个插入嫁接的小脚本,运行git merge,并删除嫁接,以任意合并-选择的祖先。只要它在B--...--R 行上,就插入它作为L 的替换,或者只要它在A--...--L 行上,就将它作为R 的替换插入。)

    【讨论】:

    • 感谢您非常清楚的解释,它让我得到了答案。然而,我要补充一点,有一个细节有点误导。 “任何提交都可以”:只有当您认为只有在“选择”提交之后的更改才会在合并过程中被考虑在内时,这才是正确的。所以在我的例子中,文件在提交 Y 中的 branch1 上被删除,所以我必须选择一个提交 X 来替换在 Y 之前出现的 C。(我只是为其他可能正在阅读这篇文章的人注意这一点可能会发现它很有用。)除此之外,原因和内部工作原理都得到了非常清楚的解释。
    • 啊,对了,如果你想让 Git 在 LR 两个分支提示处看到一些特殊的变化,你需要选择一个基数 C (与 @987654383 相比) @ 和 R) 还没有这些变化。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-07-18
    • 2013-05-28
    • 2011-01-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多