不同git搜索命令之间的区别

Chr*_*oph 5 git

初学者的一个问题:git历史记录有多种搜索方式。对我来说,不清楚命令之间的区别到底是什么:

  1. git log,例如git log -S 'for what you search' --all在所有分支中搜索字符串并 git log -G 'search for regexpr' --all搜索 regexpr。例如参见这里
  2. git log --grep 'pattern'
  3. git rev-list,例如git rev-list --grep='string' --all
  4. git grep,例如git grep "string or regexpr" $(git rev-list --all)这里
  5. gitk,例如gitk file.txtGUI 界面
  6. git log --all --full-history -- **/thefile.*从这里搜索历史记录中的文件名。
  7. git diff --word-diff-regex=. > changes.txt结合grep -oP '\+[^\+]+\+' changes.txt | tr -d '\+'显示基于字符的变化。从这里。如果尚未提交更改,这甚至可以工作。

我的理解很差

  • git log在提交消息中搜索?似乎是基于rev-list?来自文档:

显示提交日志。该命令采用适用于 git rev-list 命令的选项来控制显示内容和方式,并采用适用于 git diff-* 命令的选项来控制每个提交引入的更改的显示方式。

  • git-rev-list似乎是一个更基本的命令,因为它对提交对象(或树对象?)进行操作。来自文档:

git-rev-list - 按时间倒序列出提交对象

  • git grep似乎到处搜索:在文件、blob、树和提交对象中?来自文档:

git-grep - 打印与模式匹配的行。在工作树中的跟踪文件、索引文件中注册的 blob 或给定树对象中的 blob 中查找指定模式。模式是由换行符分隔的一个或多个搜索表达式的列表。作为搜索表达式的空字符串匹配所有行。

我使用了Git 书作为参考。

tor*_*rek 3

我认为从对历史这个词的明确定义开始是有帮助的。这个词对不同的人来说有不同的含义,如果没有定义,我们可能会变得很困惑。

\n\n

Git仅存储一种历史记录。在 Git 中,历史就是提交;提交已成为历史。两者之间没有真正的区别。但git log可以显示文件历史记录\xe2\x80\x94,有点像\xe2\x80\x94,并且git blame可以显示文件中的行历史记录。使用git diff,您可以看到某种更改历史记录。Git 所做的就是按需生成或综合这些历史记录,并使用提交作为实际历史记录。

\n\n

要了解是如何工作的,我们必须查看提交的各个部分。最直接的方法是查看一些实际的提交。尝试运行:

\n\n
git rev-parse HEAD\n
Run Code Online (Sandbox Code Playgroud)\n\n

进而:

\n\n
git cat-file -p HEAD\n
Run Code Online (Sandbox Code Playgroud)\n\n

在存储库中。下面是 Git 本身的 Git 存储库中的一个示例(用空格sed替换了@额外的内容,可能是为了减少垃圾邮件):

\n\n
$ git rev-parse HEAD\n08da6496b61341ec45eac36afcc8f94242763468\n$ git cat-file -p HEAD | sed \'s/@/ /\'\ntree 27fee9c50528ef1b0960c2c342ff139e36ce2076\nparent 07f25ad8b235c2682a36ee76d59734ec3f08c413\nauthor Junio C Hamano <gitster pobox.com> 1570770961 +0900\ncommitter Junio C Hamano <gitster pobox.com> 1570771489 +0900\n\nEighth batch\n\nSigned-off-by: Junio C Hamano <gitster pobox.com>\n
Run Code Online (Sandbox Code Playgroud)\n\n

此提交的唯一哈希 ID 是08da6496b61341ec45eac36afcc8f94242763468这个丑陋的大字母和数字字符串代表了该提交的唯一 160 位数字,而不是其他提交。任何人所做的每一次提交都有其中之一,并且没有两个提交具有相同的一项。1

\n\n

我的 Git Git 存储库克隆具有使用此哈希 ID 的提交。实际上,该哈希 ID就是本次提交。我向 Git 提供哈希 ID,Git 可以提取您在上面看到的数据作为git cat-file -p.

\n\n

然后,在提交中,我们看到这些行:

\n\n
    \n
  • tree加上一个又大又难看的哈希 ID:这就是 Git 保存提交快照的方式。此时您不需要了解更多有关此内容的信息\xe2\x80\x94,或者也许永远不需要了解更多,除了术语“树”指的是这种保存的快照。

  • \n
  • parent加上一个又大又难看的哈希 ID:这就是 Git 知道哪个提交出现在commit之前的08da6496b61...方式。

  • \n
  • authorcommitter行:这些告诉您谁在何时进行了提交。它们通常是相同的\xe2\x80\x94或几乎相同的\xe2\x80\x94,就像这样,但是如果一个用户编写了提交的早期草稿,而其他一些用户实际上将最终版本放入存储库中,那么您得到两个不同的名字。(早期的草稿提交可能仍然在存储库的某些克隆中,至少几个月。)

  • \n
  • 可以有一些其他标题行(例如,有一个可选的用于编码的标题行),然后有一个空行;其余行是提交主题和消息正文。

  • \n
\n\n

对于我们的目的来说,所有这些中最重要的部分是每次提交都存储源代码的快照、日志消息父哈希 ID。一些提交存储两个父哈希 ID,并且至少一个提交不存储父哈希 ID。2 父哈希 ID 只是一个原始哈希 ID,正如您从上面的文本中看到的那样。

\n\n

给定任何提交的哈希 ID,Git 可以提取该提交的内容,就像我们上面看到的那样。这为 Git 提供了提交快照的哈希 ID ,让 Git 提取该提交中的所有文件:

\n\n
    \n
  • 每个快照都包含该提交中每个文件\xe2\x80\x94以及每个文件的完整副本。

  • \n
  • 如果您添加一个全新的文件并进行新的提交,显然旧的提交没有新文件。只有新提交才有新文件。不过,新提交仍然保留所有旧文件,因为每个快照都包含每个文件。

  • \n
  • 如果删除文件并进行新提交,则新提交没有该文件。旧的提交仍然有效。新的提交仍然包含所有其他文件。

  • \n
\n\n

因此,如果您给 Git两个提交哈希 ID,Git 可以提取两个快照,然后比较两者。大多数文件可能是相同的,在这种情况下,Git 无法对它们说什么。也许您在较新的提交中添加了新文件,和/或删除了旧文件。Git 可以说新文件被添加,旧文件被删除。当然,也许您在新旧提交之间更改了某些文件的内容,在这种情况下,Git 可以告诉您该文件已被修改。不仅如此,Git 还可以将该文件的旧快照与新快照进行比较,并告诉您哪些发生了更改。

\n\n
\n\n

1从技术上讲,两个不同的提交具有相同的哈希值可以的,只要它们永远不会相遇。我喜欢将这些称为doppelg\xc3\xa4nger commits。在真实情况中你不会发现这些。不过,它们可以通过蛮力找到,并且为了阻止这种机会,Git 最终转向更大、更丑陋的哈希 ID。在此之前,它们都是 SHA-1 校验和。

\n\n

这是它的工作原理。我上面显示的提交长度为 280 字节,如果计算字符串的 SHA-1 哈希值commit 280后跟 ASCII NUL,后跟上面文本的字节,您将获得哈希 ID:

\n\n
$ python3\n...\n>>> import subprocess\n>>> data = subprocess.check_output("git cat-file -p HEAD", shell=True)\n>>> header = \'commit {}\\0\'.format(len(data)).encode(\'ascii\')\n>>> header\nb\'commit 280\\x00\'\n>>> import hashlib\n>>> h = hashlib.sha1()\n>>> h.update(header)\n>>> h.update(data)\n>>> h.hexdigest()\n\'08da6496b61341ec45eac36afcc8f94242763468\'\n
Run Code Online (Sandbox Code Playgroud)\n\n

这是提交的哈希 ID。

\n\n

这就是为什么您不能更改任何提交的任何部分。 如果您尝试,进入上述哈希函数的数据会发生变化,这意味着哈希 ID 会发生变化。结果是一个新的提交!旧的提交继续存在,您所做的只是向存储库添加一个新的提交。

\n\n

2具有两个或更多父级的提交是合并提交没有父母的提交是提交。我们在这里不会担心这些。

\n\n
\n\n

现在我们了解了提交和哈希 ID,让我们看看它们的联系

\n\n

我们刚刚看到提交保存了其父级的哈希 ID的哈希 ID 。这看起来可能不多,但实际上几乎是我们需要的一切。

\n\n

想象一下,您有一个小型存储库,现在只有三个提交。这三个提交有三个又大又难看的哈希 ID,我们不知道它们是什么,但我们可以假装它们的 ID 是AB、 和C按这个顺序。

\n\n

我们可以绘制这个存储库:

\n\n
A <-B <-C\n
Run Code Online (Sandbox Code Playgroud)\n\n

提交 C 的哈希 ID 就是它的实际值,在里面C我们找到了B实际的哈希 ID。所以commitC 点要commit B。Git 可以使用 hash ID fromC来读取 commit B。在此提交中,我们找到了A\ 的实际哈希 ID,因此B指向A. Git 可以使用A哈希 ID 来读取它,并且由于这是第一次提交,因此它没有级,Git 知道这是第一次提交并可以在此停止。

\n\n

要找到C\ 的实际哈希 ID,Git 需要一些帮助。这就是分支名称的用武之地。类似的名称仅保存链中最后一次master提交的哈希 ID 。换句话说,如果是最后一次提交,则该名称保存的是 的实际哈希 ID 。我们说名称指向,我们可以将其绘制为:CmasterCmasterC

\n\n
A--B--C   <-- master\n
Run Code Online (Sandbox Code Playgroud)\n\n

正如我们在脚注 1 中看到的,存储在提交内的“箭头”\xe2\x80\x94hash ID\xe2\x80\x94 无法更改,因此我们可以偷懒,将它们绘制为连接线。然而,分支名称中的箭头确实发生了变化。为了进行新的提交,我们让 Git 写出一个新的快照\xe2\x80\x94所有文件\xe2\x80\x94并获取我们的姓名和电子邮件地址等以及一条日志消息。Git 需要将所有这些内容以及 commit 的哈希 IDC写入新的提交中,这将获得一个新的、独特的、丑陋的哈希 ID,我们将在此处调用它D。提交D将指向C

\n\n
A--B--C   <-- master\n       \\\n        D\n
Run Code Online (Sandbox Code Playgroud)\n\n

现在,Git 将通过将哈希 ID\xe2\x80\x94(无论它到底是什么 \xe2\x80\x94)写入 name 来使提交成为D链中的最后一个提交,以便指向而不是:D mastermasterDC

\n\n
A--B--C--D   <-- master\n
Run Code Online (Sandbox Code Playgroud)\n\n

这就是 Git 的工作原理。提交保存快照以及父级哈希 ID,因此提交指向其父级。分支名称指向最后一次提交,这就是 Git 开始的地方:最后。最后一次提交向后退一步,指向其父级。它的父级向后退一步,指向另一个较早的提交。该承诺指向后退一步,等等。我们跟踪每个提交,一次一个,最终我们到达根提交A并停止。

\n\n

要创建一个新分支,我们只需创建一个新名称,指向任何现有的提交\xe2\x80\x94(通常是我们现在拥有的提交),D例如master

\n\n
A--B--C--D   <-- master, new-branch\n
Run Code Online (Sandbox Code Playgroud)\n\n

现在我们的绘图中还需要一件东西。我们曾经在 commit 上D,现在仍然在 commit 上D,但是我们在哪个分支上?我们添加 name HEAD,附加到这些分支名称之一,以记住:

\n\n
A--B--C--D   <-- master, new-branch (HEAD)\n
Run Code Online (Sandbox Code Playgroud)\n\n

现在,如果我们进行新的提交E,Git 将更新附加的名称HEAD,因此我们将得到:

\n\n
A--B--C--D   <-- master\n          \\\n           E   <-- new-branch (HEAD)\n
Run Code Online (Sandbox Code Playgroud)\n\n

如果我们返回到master,通过附加HEAD到名称master并选择D要工作的提交,并进行新的提交F,这会将F\ 的哈希 ID 写入master

\n\n
           F   <-- master (HEAD)\n          /\nA--B--C--D\n          \\\n           E   <-- new-branch\n
Run Code Online (Sandbox Code Playgroud)\n\n

请注意,现有提交没有更改。进一步注意,提交A现在D两个分支上。3 那是因为 Git 并不太重视分支。重要的是提交。分支名称只是为了找到终点

\n\n

像这样绘制提交及其互连,会生成提交图。在数学/计算机科学理论中,图被定义为G = (V, E),其中V是一组顶点或节点,E是一组连接节点的边。这里的节点是提交,边缘是向后指向的单向箭头。4

\n\n

从这些不同的末端开始\xe2\x80\x94,或者,如果你给 Git 一个原始哈希 ID,从任何提交开始\xe2\x80\x94Git 总是可以向后工作,直到历史的开始。一般来说,这就是 Git 所做的事情。与图论和图算法一样,我们称之为“走图”

\n\n

请注意,当我们确实遍历此图时,我们一次会得到成对的提交:从F,我们移回到D,这样我们就有(D, F)作为一对。然后从D回到C,这样我们就有(C, D)作为一对。这个过程会重复进行,而且非常简单,就像这样的图表,直到我们开始:之前没有任何东西A可以配对。为了让它工作,我们让 Git 假装(_, A)是一对:Git 伪造它,是_一种假空提交:带有树的提交作为其快照的提交。

\n\n

如果我们创建一个合并提交,那么向后走时就会遇到问题。考虑这个小图:

\n\n
          I--J\n         /    \\\n...--G--H      M--...\n         \\    /\n          K--L\n
Run Code Online (Sandbox Code Playgroud)\n\n

我们像往常一样从末尾开始,然后回到M。但然后呢?我们可以去J L。正如我们稍后将看到的,Git 通常会同时执行这两项操作,但这会变得相当棘手。

\n\n
\n\n

3 Git 在这方面有点奇怪:提交时记得你在哪个分支上进行提交。许多其他版本控制系统确实会记住并永远保留该信息。实际上,Git 认为这些信息比无用更糟糕:它只是噪音,干扰了有价值的信号。你可能同意也可能不同意,但这就是 Git 所做的:它只记录向后查找的链哈希 ID,而不记录分支名称。

\n\n

4当边是单向箭头时,图论人士称其为。这种有向边的图就是有向图Git进一步限制该图不包含循环,这使其成为有向循环DAG。DAG 有许多很好的属性,Git 依赖于它们。

\n\n
\n\n

现在(终于!)我们可以回答有关各种命令的问题

\n\n

让我们从列表中的这些 Git 命令开始:

\n\n
    \n
  • git log:这会遍历提交历史记录,显示提交。它有很多选择。

  • \n
  • git rev-list:这基本上只是git log变相的(和/或反之亦然)。它们之间的区别在于它们的使用方式:git logGit 称之为瓷器,或面向用户的命令,而git rev-listGit 称之为管道,或设计用于构建其他命令的命令。git pushRev-list 是 Git 的一个关键工具,它实现了和的一些内部结构git fetch

    \n\n

    一般来说,您使用的git rev-list方式与使用 的方式相同git log,只是自动git rev-list打印每次提交的哈希 ID。这对于另一个需要哈希 ID 的 Git 命令的输入(或参数)特别有用。5

  • \n
  • git grep:无论如何,这会查看一张快照,或者一次一张。

  • \n
  • git diff:这通常会查看两个快照并进行比较。(这个主题有很多变化,因为git diff可以看到不完全是快照的东西,并且还有一些我们不会在这里讨论的特殊用途模式。)

  • \n
\n\n

对于这些我们可以添加:

\n\n
    \n
  • gitk:这是 Git 附带的扩展,但实际上并不是Git一部分。它使用Tcl/Tk绘制提交的图形表示,并包含更多信息,并且可以在提交上运行各种 Git 命令(或者,为了显示差异、提交对)。git rev-list它实际上是通过在后台运行、收集其输出并动态更新显示的信息来工作的,直到git rev-list完成来工作的。我不太用它。有时它对于浏览提交非常方便,并且可能能够做更多的事情,但由于我并没有真正使用它,所以我不能对它说太多。
  • \n
\n\n
\n\n

5请注意,git rev-list可以为未提交的内容生成哈希 ID,但默认情况下,它仅显示提交哈希 ID。相比之下,git log只能真正打印有关提交的内容。因此,它们是相关的,但绝对不完全相同,尽管它们是从单个主驱动程序源文件构建的(链接了 Git 的其余大部分内容,包括引擎git diff)。

\n\n
\n\n

git log

\n\n

作为一个图表行走者,git log可以做很多相当令人惊奇的事情。

\n\n

我们已经注意到它从末尾开始并向后工作,当它这样做时,它通常会成对提交。让我们看看这两项的后果:

\n\n
    \n
  • 通过从最后开始并向后工作,git log可以向我们显示每个提交的日志消息。这是它的默认操作:显示哈希 ID、作者和/或提交者以及日志消息...然后转到上一个提交,并显示哈希 ID 以及作者和日志消息,然后继续再次。

  • \n
  • 因为它在查看每个提交时手头有每个提交的父级git log,所以可以调用git diff父子对来查找两个快照中的差异。父级和子级之间的差异(如果有)显示了该提交中发生的更改

  • \n
  • 我们可以git log 打印一些提交。这实际上非常有用。假设我们已经git log遍历了历史,从结尾一直到开头,一次一个提交,查看提交对。当它这样做时,我们让它调用git diff父级和子级。在根提交中,我们将空树与根提交进行比较,以便添加每个文件。

    \n\n

    同时,我们指示git log 不要打印有关提交的任何内容,除非该差异表明该文件interesting.ext已更改,或者已添加或删除。我们git log将通过一次后退一个来遍历它可以到达的所有提交,但它只会告诉我们有趣的提交:修改(或创建或删除)有趣文件的提交。

    \n\n

    看起来像文件历史记录。它不是\xe2\x80\x94,它只是选择的提交历史记录\xe2\x80\x94,但它通常正是我们要求文件历史记录时想要的。

  • \n
  • 或者,我们可以查看git log提交消息。如果提交消息包含某些特定单词,我们会显示该提交。否则,我们不会显示提交。这是git log --grep

  • \n
  • 或者,我们可以git log像以前一样运行父子对比,但这一次,我们不询问文件是否interesting.ext更改,而是询问:无论更改哪个文件,差异文本是否有一些字符串或正则表达式在里面吗? 这些是git log -Ggit log -S

    \n\n

    -G和的区别在于-S,它-G在 diff 中查找其正则表达式参数,而-S查找其参数\xe2\x80\x94(默认情况下是一个字符串,而不是正则表达式\xe2\x80\x94)以具有不同的父级和子级中出现的次数。给定您编写的源语言func(args)git log -G func将找到任何显示func已更改其参数的调用的差异,同时将找到您添加调用或删除现有调用的git log -S func任何位置,但不会找到您从哪里去的地方, 例如。funcfuncfunc(true)func(false)

  • \n
\n\n

还有更多内容,包括我在这里不会涉及的内容,但使用git log. 请记住,合并提交M(就像我们的带有父项I和两者的示例提交一样K)有两个父项,而不是只有一个。这是一个问题,并且git log因此具有许多特殊性。

\n\n

首先,当涉及到 diff 时,git log通常会放弃。要查看常规提交中发生的情况,git log请比较父提交与子提交。合并至少有两个父级,可能更多。没有简单的方法可以将所有父母与孩子进行比较(但见git diff下文),因此默认情况下,git log甚至不会尝试。它根本没有区别它们。这意味着所有“检查差异”选项\xe2\x80\x94git log -Ggit log -S(主要是\xe2\x80\x94)在这里也不执行任何操作。

\n\n

其次,为了跟随父母双方git log使用优先级队列。事实上,它使用相同的机制来处理如下命令:

\n\n
git log master feature\n
Run Code Online (Sandbox Code Playgroud)\n\n

您告诉 Git 从两次提交开始图形遍历。Git 无法做到这一点,因此它将每个提交的哈希 ID 放入队列中。这两个提交之一变得比另一个更重要,并且git log接下来将选择那个提交,因为它的“获取父级,也许做一些差异等”步骤。

\n\n

提交的优先级顺序取决于git log参数,例如--date-order--author-date-order--topo-order。使用git log --graph武力--topo-order。这些都有点复杂,这里就不详细说了。要记住的重要一点是,每当git log两个提交要显示时,它仍然一次只显示一个:

\n\n
          I--J   <-- branch1\n         /\n...--G--H\n         \\\n          K--L   <-- branch2\n
Run Code Online (Sandbox Code Playgroud)\n\n

运行git log branch1 branch2会选择两个提交之一J并且L. 这个提交从队列中出来,该队列现在保存着另一个提交。Git 显示\xe2\x80\x94 或不显示\xe2\x80\x94 所选提交,与其父级进行比较IK酌情进行比较。然后它将父\xe2\x80\x94IK\xe2\x80\x94 放入队列中。现在,队列中包含未显示的两个队列中的一个,以及显示的队列的父级。它选择这两个之一并显示(或不显示)它,并将该提交的父项放入队列中。最终它会放入H队列中,通常此时它已经显示,或即将显示,IK下一个或很快就会显示。这会H再次放入,但由于这是多余的,因此不会。H所以现在队列中只有:从队列中git log取出H(队列变空),显示H(与其父队列相比G),然后放入G队列中,队列中现在只有一次提交。

\n\n

当向后遍历合并时,会发生相同的过程:git log所有父项放入队列中。当一个到达队列的前面时,它会根据需要被取出、显示或跳过,并且它的父级进入队列,并且重复该过程。根提交没有父提交,因此没有父提交进入队列,这会让队列耗尽并git log停止。

\n\n

git log命令可以执行 Git 所说的历史简化 (History Simplification) 功能。这主要包括在遍历合并提交时放入所有父级。还有其他类型的历史简化。要了解它们,请阅读文档。有关简化的内容很复杂且难以解释,并且文档可以使用更多示例。

\n\n

如果您git log在没有起始点提交的情况下运行,git log则用于HEAD查找其起始提交。因为这只是其中之一提交,因此优先级队列的所有复杂性都消失了,至少直到您在历史记录中遇到合并提交为止。

\n\n

git rev-list

\n\n

描述这一点的简短方法是,git log除了从不使用它之外,您只是将其输出提供给另一个 Git 命令。不同的是git loggit rev-list 需要一个起点,因此要使用它git log,您通常会运行git rev-list HEADgit log另外,请注意,和的文档git rev-list是从通用源文件生成的。这意味着某些仅在两个命令之一中有意义或仅允许在其中之一的选项会泄漏到另一个命令的文档中。

\n\n

git grep

\n\n

git grep命令是为了搜索文件而构建的,通常是在提交中找到的。但是,如下git diff所示,您可以让它使用您的工作树索引。(我们还没有触及 Git 的索引;见git diff下文。)

\n\n

您可以提供git grep提交标识符。 任何标识符都可以:名称(例如branch-a)解析为提交哈希 ID,它指定快照。该名称HEAD解析为您现在签出的提交的哈希 ID。原始提交哈希 ID指定快照的提交哈希 ID。

\n\n

grep 命令将搜索关联的文件。它有很多选择;请参阅其文档

\n\n

git diff

\n\n

一般来说,git diff比较两个提交。 任何两次提交都可以:只需给它两个提交哈希 ID,它就会从左侧哈希 ID 中提取快照,从右侧哈希 ID 中提取快照,然后比较这两个快照。

\n\n

的输出git diff是一组指令:针对每个文件对此文件进行这些更改。如果您拍摄左侧快照并进行所示的更改,您将获得与右侧快照中相同的文件。这不一定是某人实际更改文件的方式,但它会产生相同的效果。如果旧文件和新文件相同,Git 根本不需要提及该文件:其中没有任何可更改的内容。

\n\n

将当前提交\xe2\x80\x94(HEAD\xe2\x80\x94 中的提交)与工作树中的提交进行比较非常有用,因此您可以这样做。但 Git 实际上是根据索引暂存区域中的内容构建新的提交。

\n\n

索引 / staging-area\xe2\x80\x94 这是同一事物的两个名称,加上第三个名称大部分已不再使用,它​​被称为缓存\ xe2\x80\x94 最初保存一个复制从提交中获取的每个文件的6 份HEAD,即您上次签出的文件。当您更改工作树中的文件时,不会影响索引副本。这就是为什么您必须不断地git add文件:将文件从工作树复制到索引中。然后索引保存建议的下一次提交,并且当您运行git commit,Git 将文件的索引副本转换为快照副本。现在,新提交与索引匹配,我们回到了您签出提交时所遇到的情况,该提交现在是您刚刚所做的新提交的父级:所有文件的索引副本都与提交的副本。

\n\n

所以:

\n\n
    \n
  • git diff将索引/临时区域\xe2\x80\x94中的内容与您现在建议的下一个提交中的内容\xe2\x80\x94与工作树中的内容进行比较。与比较两个提交一样,您会得到一组指令,告诉您如何将每个文件的索引副本更改为该文件的工作树副本。如果两个文件相同,则不需要任何说明,并且git diff不会提及这两个副本。

  • \n
  • git diff --cached或者将当前提交的提交\xe2\x80\x94(换句话说\xe2\x80\x94)与索引/暂存区域git diff --staged进行比较。HEAD也就是说,如果您现在做出承诺,情况就会有所不同。请注意,工作树在这里无关紧要!

  • \n
  • git diff HEADHEAD将提交与您的工作树进行比较。

  • \n
  • git diff commit将给定的提交与工作树进行比较。参数commit可以是命名提交的任何内容,包括分支名称或原始哈希 ID。

  • \n
  • git diff commit1 commit2比较两个给定的提交。

  • \n
\n\n

现在,git diff有一些特殊的语法技巧。其中之一是,形式A..Bgit rev-list和意味着一件事git log,而对 则意味着完全不同的事情git diff。事实上,这与将两个点替换为空格的含义相同:

\n\n
    \n
  • git diff A..B只是意味着。git diff A B
  • \n
\n\n

然而,当你使用三个点时,git diff会做一些更奇特的事情。这个答案太长,无法详细说明。

\n\n

该命令git showgit diff. 虽然它可以做很多其他事情,但它的主要作用是git diff从您命名的提交的父级运行到您命名的提交。所以向git show您展示了发生了什么变化。与 一样git log,它首先显示提交哈希 ID、作者和日志消息。

\n\n

最后要提到的是git diff\xe2\x80\x94,因此git show\xe2\x80\x94 还有最后一个狡猾的伎俩。请记住,我们提到git log通常不会尝试处理合并提交差异,因为它们很困难。但git diff/git show愿意更加努力地工作。如果您git show进行合并提交,Git一次提取每个父项,并将其与子项进行比较。通常只有两个父级,这意味着它运行两个内部git diff。然后它结合了差异。

\n\n

组合差异非常棘手为了变得有用,Git从这个组合差异中删除了许多实际差异。假设 mergeM有父级JL,并且与Jto 的差异M表示更改 file_J.txt,但不更改 file_L.txt,而与Lto的差异M表示更改 file_L.txt 但不更改 file_J.txt。现在,这个组合差异将不会说明任何一个文件J但是从到 的diffM表示要更改,从到 的file_both.txtdiff 也是如此。合并后的 diff 通常会说明有关. 我相信这样做的目的是向您展示合并必须更加努力的文件,但有时,这根本不是您想要的。LMfile_both.txt

\n\n

关于组合差异,您应该记住的主要一点是它省略了一些文件。要查看哪些,请查阅文档(此链接转到git diff-tree,这是它的一个管道变体,git diff可以很容易地生成组合差异)。

\n\n

您可以git log使用--cc或生成组合差异-c,但请记住这些会忽略文件。你可以用git log来做更奇特的事情-m,但我现在真的需要停止写作。

\n\n
\n\n

6从技术上讲,索引保存对内部、Git 格式、冻结和压缩文件的引用,以及它们在当前或下一次提交中出现或将出现的方式,而不是数据的实际副本。但大多数时候,您无法真正分辨出其中的差异,因此您可以将其视为拥有每个文件的完整副本,并且相差不远。

\n


归档时间:

查看次数:

641 次

最近记录:

6 年,4 月 前