合并后Git提交丢失

合并后Git提交丢失,git,Git,我们有3家分公司(A、B、C),如下所示: ---\--A1--\------Am------Merge1---An---Merge2--- \ \ / / \ \--C1---C2---/ / \ / \--B1--------------Bn---------/ 问题出现在Merge2。分支C上

我们有3家分公司(A、B、C),如下所示:

---\--A1--\------Am------Merge1---An---Merge2---
    \      \              /             /
     \      \--C1---C2---/             /
      \                               /
       \--B1--------------Bn---------/
问题出现在Merge2。分支C上的一些提交(不是全部,而是一些,比方说C2)在Merge2之后在分支A上丢失,这在Merge1和Merge2之间存在

执行Merge2时,只有一个文件冲突,与丢失的提交(C2)无关。我们解决了冲突并成功地完成了合并

似乎C2被分支A上的Merge2逆转,没有任何日志。


发生了什么事?出现这种情况的原因可能是什么?

您的意思不是提交本身丢失,而是在提交过程中(对某些文件)所做的更改已恢复(未进行)

这里值得注意的是,提交并不是真正“更改”文件。相反,每次提交都存储一组完整的文件,完整无缺。当您要求Git向您显示一些提交时(假设其ID为
c2c2c2…
):

Git所做的是:

  • 提取提交
    c2c2c2…
  • 提取
    c2c2…
  • 从父级到子级生成差异列表
  • 这就是Git如何向您展示所发生的变化:它将“提交前您所拥有的”与“提交后您所拥有的”进行比较。(Git可以非常快速地完成这项工作,并进行了优化,因为每个文件都被简化为一个唯一的散列“指纹”,Git可以首先比较指纹(散列)。如果散列是相同的,则文件是相同的。如果散列不同,它只需要麻烦地提取实际的文件数据。)

    保存整个文件而不是累积更改的过程称为“存储快照”。(其他版本控制系统倾向于累积更改,这被称为“存储增量”。他们这样做是因为保存文件的更改显然比保存文件占用的空间小得多。Git以一种巧妙的方式绕过了这个问题,最终使用的磁盘空间比旧的基于增量的版本控制系统要少。)

    为什么“Git存储快照,而不是Delta”在这里如此重要 合并提交是一种特殊的、显而易见的方式。查看自己的图表并考虑<代码> Melg1 < /Cord>。什么承诺就在它之前

    这里的答案是,
    Am
    C2
    都出现在“
    Merge1
    之前”。也就是说,commit
    Merge1
    有两个父提交

    如果您要求Git显示提交
    Merge1
    ,它应该与哪个父级进行比较

    这就是事情变得特别奇怪的地方。两个命令
    gitlog-p
    gitshow
    看起来非常相似。事实上,它们非常相似。一个明显的区别是
    git log-p
    显示多个提交,而
    git show
    只显示您告诉它显示的一个提交。您可以运行
    git log-n1-p
    只显示一个提交,现在看起来它们完全一样

    他们不是

    当您在合并提交中使用
    git show
    时,git试图通过同时对两个父级进行比较来解决“要比较的提交”问题。由此产生的差异称为组合差异

    但是,当您在合并提交中使用
    git log-p
    时,git只是伸出隐喻之手,说“我无法显示针对两个父项的补丁”,然后放弃并继续下一次提交换句话说,
    git log-p
    甚至懒得尝试使用diff进行合并。

    但是等等,还有更多 现在,在本例中,您可能想知道是否可以在两个合并上,特别是在
    Merge2
    上,使用
    git show
    ,从提交
    c2c2…
    中找出文件发生了什么情况,其中更改得到了恢复。但是默认情况下,
    gitshow
    会生成一个组合的diff,而组合的diff会故意忽略很多diff输出。特别是,一个组合的差异

    假设从
    C2
    恢复更改的文件是file
    f2
    。从图中可以看出,
    Merge2
    的两个父项是
    An
    (它有
    f2
    您想要的方式)和
    Bn
    (它没有)


    这里实际发生的是,在创建
    Merge2
    的合并过程中,您不知何故告诉Git使用commit
    Bn
    中的
    f2
    版本。也就是说,
    Merge2
    中的文件
    f2
    Bn
    中的文件
    f2
    完全相同,与提交
    中的
    f2
    不同

    如果使用
    git show
    查看
    Merge2
    ,组合的差异将跳过
    f2
    ,因为它与
    Bn
    中的
    f2
    相同

    同样的道理,更糟糕的是,对于
    git log-p
    :它完全跳过了合并,因为显示差异太难了

    即使没有
    -p
    ,当您请求“文件更改”时,
    git log
    也会做同样的事情,完全跳过合并。这就是为什么在日志输出中看不到它

    (顺便说一句,
    git log master--f2
    从不显示commit
    C2
    本身的原因是在
    git log
    的选项中添加文件名会打开“历史简化”在我认为有点冒牌行为的情况下,Git会把太多的历史简化掉,这样它就不会显示提交“代码> C2 < /代码>。在<代码>之前添加<代码>完整的历史< /代码> F2 < /代码>将代码> C2 < /代码>恢复到所示的一组提交。但是,合并仍然没有,因为<代码> Git日志跳过它。

    如何看待变化 有一个解决办法。
    git show
    git log
    都使用了一个额外的标志,
    -m
    ,它“拆分”合并。也就是说,它们不会将
    Merge2
    视为合并提交,而是将合并分解为两个“虚拟联合体”
    $ git show c2c2c2