为什么在git diff与git diff --staged下输出不同?

时间:2018-12-23 15:16:03

标签: git git-diff

为什么我们都需要这两个?在什么情况下它们在命令行上提供的输出会有所不同?

您能否解释一下在不同情况下(例如添加文件,暂存和修改)两者之间的区别。那么什么是分阶段和非分阶段的更改?

2 个答案:

答案 0 :(得分:3)

我认为您被误导了。 Git根本不存储更改。直到您意识到Git只是存储完好所有东西,然后以一种怪异的方式存储为止,整个事情似乎都非常神秘。

Git永久存储什么

首先也是最重要的一点,Git并不完全存储文件。这样做的目的是结束,但这是因为Git存储 commits ,并且每个单独的提交都包含(全部!)文件。也就是说,在开发的较早阶段,您(或某人)告诉Git:这是整个文件树,其中有一些文件夹/目录集,包含文件和子目录,而子目录又包含更多文件和子目录,等等。上。对它们现在的外观进行快照。该快照,即所有内容的整个副本,将被提交到新提交中。

接下来,提交一旦完成,大部分都是永久性的,并且完全是完全100%只读的。您不能更改提交内的任何内容。您可以将它们视为永久性的:一次提交才能真正消失的唯一途径是,如果您谨慎地安排确保以后没有人(不是您自己,也没有其他人)可以找到git reset或类似工具。

由于许多原因,包括如果进行多次提交以继续重复使用大多数文件的大多数旧版本,则可能使存储库变得不那么胖,存储在提交中的文件保存在特殊的压缩Git中仅限格式。由于提交中的文件被冻结,因此,除了一个文件之外,如果新提交 C9 与上一个提交 C8 一样,则两个提交将 share 所有相同的文件。

Git可以让您暂时使用什么

由于您无法更改任何提交,因此,如果Git没有从某些提交提取的所有文件的方式,它将是无用的。提取提交会从深度冻结中复制所有文件,然后解压缩文件,然后将它们转换为您和计算机可以使用的日常文件。这些文件是该Git提交中的内容的副本,但在这里,在此工作区- work-tree work tree 中,它们对于您和您的计算机很有用,您可以按自己喜欢的任何方式进行更改。

Git使其索引复杂化

现在有点棘手了。其他版本控制系统可能会在此处停止:它们也具有提交(以冻结形式永久保存文件)和可让您以普通形式处理文件的工作树。为了进行新提交,这些其他版本控制系统将缓慢,痛苦地一个接一个地逐步处理每个工作树文件,将其压缩以准备冻结,然后然后检查是否冻结的文件将与旧文件相同。如果是这样,他们可以重新使用旧文件!如果没有,他们会尽一切努力保存新文件。这非常慢,而且通常有多种方法可以加快速度,但是在这些非Git版本控制系统中,使用“ commit”命令后,您通常可以起身去喝咖啡,或去散步或吃午餐或其他东西。

Git所做的根本不同,与其他系统相比,git commit如此之快。当Git从冻结状态中取出文件放入工作树时,Git会保留半冻结的“ slushy”(如果可以的话)副本每个文件,以备进入 next 提交。最初,这些副本都与冻结的提交副本匹配。

这些肮脏的文件副本位于Git所谓的 index 临时区域缓存,具体取决于Git的谁或哪个部分正在执行呼叫。每个文件的这些索引副本与当前提交中的冻结副本之间的主要区别在于,提交副本实际上是冻结的。他们不能被更改。索引副本几乎只能被冻结:它们可以通过将新文件写入索引来代替旧文件而被更改。

最后,这意味着对于提交中的每个文件,当您告诉Git使该提交成为当前提交时,您得到的不是主动副本,而是两个三个 ,使用git checkout somebranch。 (此结帐选择 somebranch 作为当前的分支名称,因此也提取了Git称之为 tip commit >当前提交。此当前提交总有一个名称:Git称之为HEAD。)例如,假设master的尖端提交有两个文件,名为{{1 }}和README.md

main.py

此时,每个文件的所有三个副本相互匹配。也就是说,所有三个 HEAD index work-tree --------- --------- --------- README.md README.md README.md main.py main.py main.py 都相同,但格式不同:README.md中的一个是冻结的且仅Git;索引中的一个是半冻结且仅Git;您的工作树中的一个对您有用且有用;但所有三个代表相同的文件内容HEAD的三个副本也是如此。

现在假设您更改了一个(或两个)工作树文件。例如,假设您更改了工作树main.py。让我们用README.md标记它以表示不同,并用(2)标记旧的以记住旧的是:

(1)

您现在可以要求Git将每个文件的索引副本与每个文件的工作树副本进行比较,这一次,您将看到您的更改 HEAD index work-tree ------------ ------------ ------------ README.md(1) README.md(1) README.md(2) main.py(1) main.py(1) main.py(1)

运行README.md时,您实际上是在告诉Git:获取我要添加的文件的工作树副本,并准备将其冻结。 Git将复制工作git addREADME.md(或两者)的树副本返回索引,对内容进行Git修饰,为下一次冻结做好准备:

main.py

这一次,要求Git将 index 副本(所有内容)与 work-tree 副本(所有内容)进行比较,结果什么都没有!毕竟它们是一样的。要查看差异,您必须要求Git将 HEAD index work-tree ------------ ------------ ------------ README.md(1) README.md(2) README.md(2) main.py(1) main.py(1) main.py(1) 提交与索引进行比较,或将HEAD提交与工作树进行比较。任一个都足够了,因为现在索引和工作树再次匹配。

但是请注意,在使用HEAD之后,您可以再次更改工作树副本 。假设您又修改了git add次,给出了:

README.md

现在 HEAD index work-tree ------------ ------------ ------------ README.md(1) README.md(2) README.md(3) main.py(1) main.py(1) main.py(1) 的所有三个副本都匹配,但是main.py的所有三个副本都不同。因此,现在无论Git比较README.md与索引,还是HEAD与工作树,还是索引与工作树,这都很重要:每个对象都将显示对{的不同更改 {1}}。

Git通过索引进行 new 提交

何时以及如果您确实选择进行新提交(即现在所有文件的新永久快照,现在为 ),Git将使用索引中的半冻结文件来进行新提交的快照。提交动词与它们有关的所有工作都是完成冻结过程(在技术层面上,冻结过程包括制作 tree 对象来保存它们,但您不必知道这一点)。因此HEAD会收集您的姓名,电子邮件,时间,您的日志消息以及当前提交的哈希ID,冻结索引,然后将所有这些放在一起进行新的提交。新的提交成为 README.md提交,因此现在git commit引用了 new 提交。如果旧的提交是 C8 ,而新的提交是 C9 ,则HEAD以前是 C8 的意思,但现在是 C8 > C9

提交完成后,每个文件的HEAD和索引副本自动匹配。很明显,它们必须这样做,因为新的HEAD是从索引中制成的。因此,如果使用包含HEAD中间版本的索引进行新提交,则会得到:

HEAD

请注意,在此过程中,Git完全忽略了工作树!有一种方法可以告诉README.md,它应该查看工作树并自动 运行 HEAD index work-tree ------------ ------------ ------------ README.md(2) README.md(2) README.md(3) main.py(1) main.py(1) main.py(1) ,但让我们稍后再讲。

此特定部分的摘要是考虑索引的一种好方法:索引包含您建议进行的 next 提交。 {{1 }}命令的意思是:更新我建议的下一次提交。这说明了为什么必须一直git commit

Git的git add动词

因为每个文件都有这三个同时有效的副本-一个永久文件,一个为 next 提交建议的文件,一个可以实际使用的副本— Git需要一种方法来比较这些东西。 git add动词是您要求Git比较两件事的方式,它的选项是选择比较两件事的方式:

  • git add告诉Git:将提交A中的快照提取到临时区域;将提交B中的快照提取到一个临时区域,然后将它们进行比较,然后告诉我有什么不同。通常,这很有用,但是在进行 new 提交时并没有太大作用,因为它是关于现有的,冻结的,不可更改的提交。

  • diff-根本没有选项或提交说明符-告诉Git:比较工作树的索引。 Git不查看任何实际的提交,它只需查看索引(建议的下一次提交 ),然后将其与您可用的文件副本进行比较。每当有不同之处时,您可以使用diff将其复制到索引中。因此,这告诉您,git diff commit-A commit-B可以满足您的需求。

  • git diffgit add-选项具有完全相同的含义-告诉Git:比较git add对索引的提交。 ,Git根本不会查看您的工作树。它只是找出当前提交提议的下一次提交之间的区别。也就是说,如果您立即提交,这就是不同

  • git diff --cached(或更常见的是git diff --staged)告诉Git:将我命名的提交(例如HEAD)与工作中的内容进行比较- 。这次,Git忽略了您的 index ,仅执行特定的提交(例如git diff HEAD)和工作树的内容。这不如HEAD-vs-index或index-vs-work-tree比较有用,但是您可以根据需要进行。

当然,您有更多方法可以比较任意两个项目,因此git diff commit有很多选择。但是,这是目前主要的兴趣所在。

HEAD运行两个HEAD

请注意,在您积极开发时,上面的两个最有用的 git diffgit status,它告诉您将会如果您立即执行 git diff而没有任何选项,则表示不同,这告诉您如果现在运行git diff可能还有什么不同 。您应该经常使用的git diff --cached命令为您运行这两个差异! 1

它将在内部设置git diff标志的情况下运行它们,以便不显示实际差异,而仅显示文件名。

让我们再次看到:git add运行两个 git status命令。第一个是--name-status,即提议的提交的不同之处。这些是为提交而进行的更改。第二个是普通的git status,即索引(建议的提交)和工作树的不同之处。这些是尚未进行提交的更改。

因此,现在您知道git diff会告诉您什么,以及何时需要使用带有或不带有git diff --cached的{​​{1}}来查看不仅仅是名称的信息文件。请记住,git diff向您显示的更改是Git正在弄清楚的东西:索引内或工作树中的文件是完整的完整副本。它们可能彼此不同和/或与git status中完整的完整副本不同。


1 git diff的“状态”部分可以说是文件被添加了 —在索引中,但不在{{1 }}提交。或者,在某些情况下,它可以说文件已被重命名或进行了其他一些辅助更改,但在这里我们不做讨论。

答案 1 :(得分:0)

git diff报告当前文件和最后一次提交之间的未暂存更改。
git diff --cached报告已进行的变更(即您git add进行的变更)。