这个补丁 hunk 唯一的变化就是添加了一行代码。如果你删掉这个添加的行,那么这个补丁就成为了一个指令,告诉你通过不改变文件来改变这个文件,这很不合理。虽然这里的错误信息有点傻,但你需要执行的操作就是“跳过这个”。
让我们多讲些细节,如果你感兴趣可以继续阅读。注意,在你准备提交下一个 commit 的时候,每个文件都会有三个我称之为“活动副本”的状态。更准确地说,它们最多只有三种状态。第一个状态是你以前提交的内容,或者说是你在运行
git checkout
命令时所得到的那个 commit 内部的内容。由于该副本被存储在一个 commit 中,因此基本上是只读的:你不能更改它,因为它是该特定 commit 的一部分。但你随时都可以访问它。例如,对于一个
README.txt
文件,你可以运行:
git show HEAD:README.txt
要查看它。在内部,该文件以一种特殊的Git-only格式(zlib-deflated甚至进一步压缩)存储-通常情况下,非Git程序无法读取该文件的副本。
同样,您的工作目录中也有该文件的副本,您可以在其上进行操作。该副本以普通计算机格式存储,以便您可以阅读它。您的编辑器-atom或emacs或sublime或vim或任何您使用的编辑器-都可以读取和编写该文件。如果您编译程序,则编译器可以读取和编写它等等。您无需对文件执行任何特殊操作,因为它就像任何其他文件一样。奇怪的是,Git几乎不关心这个README.txt
副本-Git必须保持某种程度的不干预,因为任何都可能改变它!
但是还有一个第三个README.txt
副本,即在Git称之为索引(index),暂存区(staging area)或缓存(cache)的位置。(Git文档使用哪个名称取决于您查看的Git文档)。这个第三份副本,可以使用git show:README.txt
查看,以特殊的Git-only格式存储,但与提交(commit)中的副本不同,您可以覆盖它。您通常使用git add
来覆盖此副本,它只需将工作目录文件复制到索引(index)中即可。
因此,索引(index): README.txt
开始匹配HEAD:README.txt
文件。如果更改工作目录副本,则HEAD和索引版本仍然匹配。如果然后执行git add README.txt
,则会从工作目录副本覆盖索引副本,现在HEAD:README.txt
和README.txt
不再匹配,但是:README.txt
与README.txt
匹配。
这就是git add -p
发挥作用的地方:如果某个文件的索引(index)副本和工作目录副本不同,则可以让Git生成一组补丁-每个补丁块都指示添加和/或删除一些行-如果应用,则会将索引版本更改为与工作目录版本匹配。如果Git遵循所有补丁中的说明,那么将更改该文件的索引副本以匹配工作目录副本,就像git add
一样。
但是现在Git要求您逐个补丁块地查看该补丁,并:
- 告诉Git按原样应用它;或者
- 告诉Git完全跳过它;甚至
- 调整更改指令,以便部分应用补丁。
当您选择应用特定的补丁块(可能在编辑后),Git会提取文件的索引版本,应用该组指令,然后继续查看下一个。如果跳过它,Git只会继续到下一个。
请注意,有一个与git add -p
相伴的工具:运行git reset -p README.txt
告诉Git它应该比较HEAD:README.txt
和:README.txt
(HEAD和索引版本),并准备一个补丁,如果完全遵循,将更改索引副本,使其再次与HEAD
副本匹配。然后Git执行与git add -p
相同的过程。
最后,让我们快速查看一下git status
。这做了很多有用的事情,但其中告诉您关于已暂存更改和未暂存更改的变化的部分,实质上包括运行两个git diff
命令。第一个是:
对于每个完全相同的文件,Git仅保持沉默。但是如果HEAD:README.txt
和:README.txt
不同,则Git告诉您在README.txt
中有一些已暂存更改。
列出所有这些文件后,Git运行第二个diff,这次是为了找出:
在这里,任何不同的文件,Git都会告诉您存在一些未暂存更改。这是因为您可以在该文件上运行git add
,将其复制到索引中。
如果您现在运行git commit
,Git将从当前索引中的任何内容创建新提交,因此HEAD
-vs-index告诉您:这些文件将在此新提交中与当前提交中的内容不同。同时,index-vs-work-tree告诉您:这些文件可以不同,但除非您git add
它们,否则不会有变化。
这份差异同样会发现任何“未跟踪”的文件。所谓未跟踪的文件,简单来说,就是工作树中存在但索引中不存在的文件。如果这样的文件被“忽略”,Git 将对其保持沉默。如果未跟踪的文件没有被忽略,Git 就会告诉你它没有被跟踪。需要注意的是,已经在索引中的文件被定义为已跟踪,因此 Git 永远不会检查忽略指令。
正是因为新建 / 未跟踪文件的情况,每个文件会有最多三个副本。如果您创建一个新文件,它根本不在 HEAD 提交中。在您将该文件添加到索引并拷贝进入之前,它也未被跟踪。因此,您最开始只有一个副本,只存在于工作树中;然后您有了两个副本,同时存在于索引和工作树中。接下来再次执行 git commit 命令,把它变成当前(HEAD)提交,这时才会出现三个该新文件的副本。
git add <filename>
,Git 只需将 工作树 版本复制到索引中,因此现在索引副本与工作树版本匹配。这是两种最常见的情况:匹配 HEAD 或 匹配工作树。(如果 HEAD 和工作树副本匹配,则索引副本可以并且通常确实同时匹配两者。)git add -p
的作用是获取工作树副本和索引副本之间的任何不同之处,并将其中的一些差异添加到索引副本中。这样可以保持工作树版本不变!它只修补索引副本。 - torekgit reset -p
的描述,所以我会删除那个评论。 - torek