首页 > 解决方案 > 为什么我在 Git 日志中看不到任何输出,但在 Git Diff 中却看到了?

问题描述

团队,

我输入:git log branchA..branchB -- /dir1/dir2

键入此“git log”命令时,不会输出任何内容。但是,当我键入此“git diff”命令时,会输出许多文件。

git diff branchA..branchB -- /dir1/dir2

有谁知道为什么会有这种极端的差异?我已经阅读了文档,但不幸的是,这并没有给我一个明显的答案,我不得不冒险进入 Internet 的公海。

似乎如果“git diff”命令有输出,那么这意味着文件在分支(branchA 和 branchB)之间发生了更改。如果“git diff”命令证明这些分支之间有变化,那么“git log”命令中应该有一些东西。

为什么“git log”命令没有返回任何东西,但“git diff”命令确实返回了很多关于文件和代码更改的输出?

标签: git

解决方案


TL;博士

branchA..branchB首先,检查范围内是否有任何提交,git rev-list例如使用(--count如果您不想看到原始哈希 ID,请添加)。然后,如果有提交,请考虑添加--full-history并且可能添加-m到您的git log命令中。

这里有很多东西要解压。(如果您指向一个特定的存储库和该特定存储库中的两个特定分支名称,则可能会少很多。)

首先,git log它们git diff是完全不同的野兽。但值得注意的是,git log可以运行 git diff,或多或少;git diff无法运行git log。尽管如此,git diff能做的事git log不能做。其次,双点符号有多种不同的含义。它的含义与它的含义git log非常不同git diff。从某种意义上说,它根本没有任何意义,git diff因为您可以随时替换:

git diff X..Y

和:

git diff X Y

也就是说,这里的两个点仅用于与 分开XY您也可以通过将它们作为单独的参数给出来做到这一点。但是,这不是真的git log。这使得git diff相当简单,所以让我们从git diff.

区分两个提交

您已经运行(无论如何都有效):

git diff branchA branchB -- /file1/file2

这种形式的git diff,给定两个提交说明符和一个路径,1具有检查两个特定提交中的每一个并比较它们的效果。如果没有路径限制器,您将获得关于哪些操作集会将第一个提交更改为第二个提交的完整说明:添加文件 A 和 B,删除文件 C 和 D,修改文件 E 到 H,等等。使用路径说明符/file1/file22从输出中消除任何影响名称不以(或等于)此字符串开头的文件的指令。(此处的前导斜杠仅相对于存储库的顶层。)


1一些 Git 命令采用pathspec参数。git logandgit diff命令没有。如果你不使用复杂的东西,这里没有什么可担心的。如果您开始使用:/:or:(glob)和其他棘手的路径规范选项,请注意它们在此处不可用。

2请注意,您的操作系统可能要求在目录/文件夹中file1/file2命名的文件或在一个命名中命名的目录/文件夹命名。对于 Git,这些只是字符串——不涉及“文件夹”。不过,在这里说可能会更好。file2file1file2file1/dir1/dir2


使用git log

有很多要了解的git log;让我们从两点符号开始。在这种情况下,您的两个项目是branchAbranchB。Git 将——至少使用git log,以及许多其他命令,但不是 git diff——首先将其视为选择从 的尖端可到达的所有提交branchB,减去从branchA. 这使用了短语reachable from,它值得一个完整的网络教程:Think Like (a) Git。(去读吧!)

正如knittl 在评论中指出的那样,这个列表可以是空的,即使这两个名称指定了不同的提交。在这种情况下,git log将不打印任何内容,正如我们将看到的。要查找哈希 ID 列表(不查看其他任何内容),请使用git rev-list

git rev-list branchA..branchB

rev-list命令是许多 Git 命令使用的主要 Git 主力,要么通过运行它,要么通过将其内置到自身中。例如,所有cherry-pick, revert, rebase, log, shortlog, 和在内部push使用git rev-list。Agit fetch也使用它,但在另一个rev-listGit 上运行命令,而不是在你的 Git 上。

无论如何,git log现在实际上有一个提交列表。这个列表中有多少提交一点也不明显。如果列表中只有一个提交,你的git loggit diff会做同样的事情——产生相同的输出——所以我们可以安全地假设列表中要么没有提交,要么至少有两个提交。 如果它是空的,但提交不同,那么您的输出已经解释过了。

如果不是,事情会变得更复杂,但提交列表中可能至少有一个合并提交。如果我们假设它确实至少有一个合并,那也给我们一个解释。Git 现在要做的是按某种顺序遍历提交列表——如果你没有添加-- /file1/file2,它现在会做一些简单的解释:

  • 找到一个要被遍历的提交(在内部,这些实际上是从优先级队列中出来的,作为这个 build-a-list-and-simultaneously-walk-it 的一部分)。

    • 找到它的父母。如果此提交是合并,则查找所有父级。这实际上是 rev-list 操作的一部分:git log并且git rev-list从单一来源构建并共享其大部分代码,并且 rev-list 和 log 都使用此共享的步行代码遍历提交图。
    • 根据--pretty选择的格式,打印来自提交的日志消息,以及任何前导内容,如提交哈希。
    • 如果提交不是合并,git diff则在(单个)父级和提交本身之间运行,并显示该输出。如果提交合并,则不显示任何内容。
    • 将父母放在提交队列中以步行,然后返回顶部。

因此,您会看到git diff列表中的每个非合并提交,git log通过git rev-list枚举“可从branchB减去可访问的提交”中找到的提交branchA

现在,您会认为添加-- /file1/file2会影响这些差异中的每一个,因此您只能看到那些更改的文件......这是真的。但它做得更多:它打开了History Simplification

启用历史简化后,git log(并且git rev-list)故意切断可达性图的部分内容。特别是,在任何merge中,Git 都会对文档中称为 TREESAME 的内容进行精心计算。这种计算相当于将每个提交剥离到您在命令行上指定的文件和/或目录列表中的那些文件,并比较每个此类文件的内容。

当合并的一个“分支”在所有文件上都是 TREESAME 时,在剥离其他文件之后,另一个不是 TREESAME,Git会丢弃 not-TREESAME 分支,包括可通过该合并分支到达的所有提交. 如果合并是章鱼合并,并且至少一个父级具有 TREESAME-after-stripping 属性,Git 或多或少随机选择一个并跟随那个,丢弃所有其他分支。(如果所有腿都不同,Git 会跟随所有腿。)

然后,由于合并提交合并提交,git log默认情况下,不打印任何内容。即使git log没有为此跳过差异列表,3它也可能刚刚选择了一些 TREESAME 父级,因此差异列表无论如何都是空的。这就是 TREESAME 的含义:在剥离所有选择的文件后,其余的从合并提交到其选择的父文件是相同的。

选择了 TREESAME 父级后,Git 继续沿着这条轨道前进。如果branchA确定了丢弃的分支中的某些提交,您现在可以(我认为)了解为什么此输出git log可能为空:从这里到任何提交中的选定文件很可能没有更改,回到任何branchA重新加入的提交可达的地方从尖端branchB

如果实际上是历史简化丢弃了有趣的提交,那么添加--full-history到您的git log命令将显示您想要查看的提交。当然,您要查找的任何更改都可能是由“邪恶合并”引入的,因此您可能还需要该-m选项,该选项强制git log显示合并提交与其每个父项之间的完整差异。


3您可以告诉git log在合并时打印差异列表,使用-c--cc-m,每个都有不同的效果。


推荐阅读