为什么git看到我的提交但不将更改应用于代码库?

时间:2019-03-27 19:02:11

标签: git github merge

我有一个GitHub存储库,其中的提交显示在日志中,并在GitHub Desktop应用程序中可见,但是提交中的更改未应用于代码(或在Visual Studio中可见)。

有问题的提交是一个月前发生的分支合并的一部分。我们将master合并到分支中,然后将分支合并回master中。 “将'主'合并到'分支'”提交显示了合并中的所有更改。其中一些更改未应用到代码中,Visual Studio表示受影响的页面自去年初以来就没有更新过。这与合并回master之后删除分支有关吗?

我们在合并后检查了所有提交,但这些提交都没有更改受此问题影响的页面上的任何代码。页面没有重命名,删除或移动。

我们试图还原合并以查看会发生什么,并且大多数提交在GitHub Desktop中消失了(从58个更改的文件更改为8个文件),仅对合并应用了一些更改。我们还尝试从此合并中挑选提交,并正确应用它们。由于我们可以这样做来解决问题,因此我们对为什么未将提交应用于相关代码文件更感兴趣。

更新: 我们尝试在合并提交之后对提交执行git checkout <sha1>,发现代码是不同的(以后3次提交-也是合并),但是这些提交中没有对代码文件的引用。

1 个答案:

答案 0 :(得分:1)

值得注意的是,提交不是,也不包含更改。提交包含完整快照。提交查看者故意撒谎(您通常希望这样做),以便显示一组更改。< / p>

这背后的机制很重要。提交是快照(例如,天气预报告诉您当前的温度是20°C),并且您想知道它现在的差异。您需要为何时选择另一个提交,例如“昨天”。那么如果昨天的温度为19˚C,今天的温度为20˚C,则差异就是1 itC的温度。但是要获得该结果,您必须选择前一天进行比较。

Git中的每个提交均由其哈希ID唯一标识。哈希ID是Git如何获取快照中的所有文件以及有关提交的元数据,例如谁进行创建,何时创建以及为什么生成(日志消息)。每次提交中保存的一项内容是上一个提交的哈希ID。这就是Git提交查看器用来构造差异的地方。

提交查看器不会显示带有哈希 H 的提交中每个文件的每一行。取而代之的是,它将找到提交的前身-用Git的说法是 parent 。那个父母有一些不同的哈希 G 。查看器提取两个提交,并进行比较,然后告诉您:在G和H之间,这些文件是不同的,并且对这些行进行了更改。通常要短得多,而且比有用得多-这是H中的完整快照。

但这在合并时会分解。如果我们绘制一组不错的线性提交:

... <-F <-G <-H   <-- you-are-here

(箭头指向后,因为每个提交记录了其父对象;父母不记得自己的孩子),比较GH很容易。但最终您将两条开发线结合在一起:

       o--...--o--K
      /            \
...--*              M   <-- mainline
      \            /
       o--o--...--L   <-- branch

主线在某个点分开,形成了两个不同的人或团体。然后,我们(或无论如何)使用了git checkout mainline; git merge branch,并经历了 merge 操作的整个可怕的 1 和神奇的 2 过程,从而导致此合并提交 M

提交M与其他任何提交一样,都具有快照和一些元数据。快照与其他任何快照一样。 M唯一的特别之处在于,在其元数据中,它不仅将提交K列为其父项。而是将两者提交-K L-列为其两个父母。


1 这实际上并不可怕。

2 这也不是魔术。见下文。


Git的自动合并如何工作

让我们快速了解git merge合并冲突。如果没有冲突,Git会自己进行整个合并。通常,这些情况不会引起这种困惑,所以让我们看看发生冲突时会发生什么。

要开始合并,Git只需将*K进行比较(就像Git总是比较任何简单的提交对一样),以找出不同之处。然后,Git比较* -vs-L,以找出不同之处。然后,Git 将两组更改组合在一起。这是合并的 或我想称做的动词 的一部分。合并的更改将应用于提交* 中的快照。

请记住,每个提交都包含一个快照。提交*时,所有文件的状态都与创建*时的状态相同。提交K的所有文件都处于其他状态,而提交L的所有文件都处于第三状态。 K中甚至可能有*中没有的文件,和/或L中没有的*中有文件,依此类推,但是通常大多数文件大部分都在这三个输入中。

假设“我们”是指在K行中工作的人,而“他们”是指在L行中工作的人。我们更改了文件A,B和C。更改了文件B,C和D。然后Git仅将我们对A的所有更改以及对D的所有更改。这很容易,因为我们没有碰触D和他们没有碰到A。合并的那部分完成了。

现在,Git找出了我们在文件B中更改了的几行 ,以及同一文件中它们的哪些行已更改。如果我们的行根本不重叠(请注意,Git有时会将“触摸”视为重叠),那么Git可以将提交*两者更改应用于文件B。合并的那部分现在也已完成。

Git找出我们在C语言中更改了哪些,以及它们更改了哪些行。嗯,这次我们都更改了 same 行。 Git用冲突标记将更改的组合写入工作树,并声明合并冲突

由于合并 发生冲突,因此Git停止并从正在执行合并的人那里获得帮助。解决问题是他们的工作。有很多修复方法,但都以相同的方式结束:进行修复的人将C文件的正确版本写入工作树并运行{{ 1}}告诉Git:这是正确的结果。

Git不会检查他们写的内容,只会将他们放入最终文件中的内容带走。如果他们完全搞砸了所有事情,例如完全丢掉了您的代码,那么Git就可以了! Git假设他们知道他们在做什么。

它们现在运行git add Cgit commit,Git使用完成的合并快照进行合并提交git merge --continue,看起来就像我们绘制了它。

回到您遇到的问题

因此,让我们回到提交查看器。您要求它查看提交M。它像往常一样显示元数据-进行提交的人的名字,依此类推。根据查看者的不同,它可能不会显示两个父哈希ID。它可能会向您显示一条日志消息,表明运行M的人员曾经用来记录为什么进行合并并保存任何重要注释。如果此人非常勤奋,则该日志消息甚至可能很有用……但是,大多数人使用自动生成的,几乎毫无价值的日志消息:“合并分支...”。

现在,您的查看器应该继续显示您要在此提交中进行更改。但是现在有一个问题。要显示发生了什么更改,查看者必须查看父提交并进行比较。没有一个父母。有两个父母。观众会使用哪一个?

此处的实际答案取决于查看者。有些观看者只是完全放弃而没有向您显示什么。例如,git merge就是这样做的。听起来您可能正在使用这种查看器。 git log -p运行的另一个查看器试图发挥作用:它实际上将合并git show两个父母,M K。但是,a,这位观众试图帮忙。它与合并可能存在合并冲突的地方有关,因此如何不显示任何L中的文件与完全匹配的文件 {{1}中的一个或M中的一个。

如果进行合并的人通过扔掉 不正确对它进行了错误,则 应该包含在{{1}中},这种查看器也将丢弃这些更改。在这种情况下,文件C与提交K中的副本完全匹配。因此L,作为合并查看器,不会向您显示文件C

(当然,使用M作为提交查看器会更糟:即使这四个文件有所更改,它也不会显示A,B,C或D中的任何一个。 )

您可以指示 L(和git show分解合并提交成两个虚拟提交。也就是说,给定:

git log

您可以让他们假装他们拥有:

git log

先显示git show-vs-...--K \ M / ...--L ,然后显示...--K--M1 ...--L--M2 -vs-K。对于这些情况,这通常很有用。为此,请将M1添加到LM2中。 (请注意,-mgit log永远不会进入存储库,它们只是在查看合并提交的“显示差异”部分期间假装提交。)

最重要的是

如果某人制作了错误的合并快照,那么许多查看者将不会向您显示。找到它的方法是查看合并前后的提交。如果有人保持的操作,则需要教他们如何正确合并。 很少扔掉他们的更改并改用我的是正确的。 Git提供了此选项,但他们应该谨慎使用该选项,不仅仅是因为它解决了他们的冲突。