【问题标题】:Why does git see my commit but does not apply the changes to code base?为什么 git 看到我的提交但没有将更改应用到代码库?
【发布时间】:2025-12-17 01:15:02
【问题描述】:

我有一个 GitHub 存储库,其中的提交出现在日志中并且在 GitHub 桌面应用程序中可见,但提交中的更改未应用于代码(或在 Visual Studio 中可见但有责备)。

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

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

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

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

【问题讨论】:

  • 您是否在本地获取/checkout-newer-version ?
  • 是的。即使在 GitHub.com 上查看 master 分支上的文件也会出现同样的问题。
  • 但是,如果您检查一个月前的那些修订,是否有变化?
  • 是的。如果我检查我们将 master 合并到功能分支的提交,那么更改就在那里。此提交后的所有提交都不包含对有问题的文件的更改。
  • 嗯...如果你回到历史并且你可以看到一些变化......然后你回到未来并且没有变化,那么它们已经以某种方式在过程......在我们正在谈论的这两个修订之间的修订中。如果检查代码很容易,您可以考虑使用 bisect 来了解更改被删除的位置。

标签: git github merge


【解决方案1】:

值得注意的是,提交不是,也不包含,更改。提交包含完整的快照。提交 viewers 故意对您撒谎(您通常希望他们这样做)以便将提交 显示作为一组更改。

这背后的机制很重要。提交是一个快照——例如告诉你当前温度为 20˚C 的天气预报——并且你想知道它现在有多么不同。你必须选择另一个提交——例如“昨天”——when 你想要比较。那么如果昨天是 19˚C,今天是 20˚C,那么区别是高了 1˚C。但要得到它,您必须选择前一天进行比较。

在 Git 中,每个提交都由其哈希 ID 唯一标识。哈希 ID 是 Git 如何获取快照中的所有文件以及有关提交的元数据,例如提交者、时间和原因(日志消息)。每个提交中保存的一项是 previous 提交的哈希 ID。这就是 Git 提交查看器用来构建差异的原因。

提交查看器不会用哈希 H 显示提交中每个文件的每一行。相反,它会找到提交的前身——它的 parent,用 Git 的术语来说。那个父母有一些不同的散列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并经历了整个可怕的1和神奇的2合并操作过程,这导致了这个合并提交 M.

提交M 就像任何其他提交一样,它有一个快照和一些元数据。快照就像任何其他快照一样。 M 的唯一特别之处在于,在其元数据中,它不只是将提交 K 列为其父项。相反,它将 both 提交(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 可以将提交 * 中的 both 更改应用到文件 B。合并的那部分现在也完成了。

Git 计算出我们在 C 中更改了哪些 ,以及它们更改了哪些行。哦哦,这次我们都改变了相同行。 Git 使用冲突标记将更改的组合写入工作树,并将合并声明为冲突

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

Git 不会检查他们写的内容,它只是将他们放入最终文件中的任何内容。如果他们把所有东西都搞砸了,例如完全扔掉你的代码,Git 没问题! Git 假设他们知道他们在做什么。

它们现在运行git commitgit merge --continue,Git 使用已完成的合并快照进行合并提交M,看起来就像我们绘制的那样。

回到你手头的问题

让我们回到我们的提交查看器。你要求它查看提交M。它像往常一样向您显示元数据——提交者的姓名,等等。它可能会或可能不会向您显示两个父哈希 ID,具体取决于查看器。它可能会向您显示运行git merge 的人用来记录为什么他们进行合并的日志消息,并保存任何重要的注释。如果这个人超级勤奋,那么日志消息甚至可能会很有用......但是,唉,大多数人使用自动生成的、几乎毫无价值的日志消息:“合并分支......”。

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

这里的实际答案取决于观众。有些观众只是完全放弃了,什么都没有。例如,git log -p 就是这样做的。听起来您可能正在使用这种查看器。另一个查看器,git show 运行的查看器试图发挥作用:它实际上将合并 Mboth 父级 K L 进行比较。但可惜的是,这个查看器试图 提供帮助。它关注合并可能有合并冲突的地方,因此它不会显示M 中的文件完全匹配的任何文件任何一个 K 中的那个,或者L 中的那个。

如果进行合并的人错误地丢弃一些应该M中的文件更改,这种查看器同样会从显示中丢弃这些更改。在这种情况下,文件 C 与来自提交 L他们的 副本完全匹配。所以git show,作为合并查看器,不会显示文件 C

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

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

...--K
      \
       M
      /
...--L

你可以让他们假装他们有:

...--K--M1

...--L--M2

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

底线,因为它是

如果有人制作了糟糕的合并快照,许多观众不会向您展示。找到它的方法是查看合并前后的提交。如果有人继续这样做,您需要教他们如何正确合并。 将他们的更改扔掉并使用我的更改是很少见的是正确的。 Git 提供了此选项,但他们应谨慎使用该选项,而不仅仅是因为它解决了他们的冲突。

【讨论】:

  • Git Show 显示有问题的文件。感谢您的详尽解释!
  • @torek 这是一个被严重低估的答案,它澄清了 git 中的几件事。谢谢!
最近更新 更多