git add -p(交互式补丁)和编辑模式会出现“在第XX行的损坏补丁”错误。

6
我将我的更改添加到一个提交中,使用命令git add -p,现在我想使用手动块编辑模式。
我只是有这个块:
# Manual hunk edit mode -- see bottom for a quick guide
@@ -46,6 +46,7 @@ function signIn(email, password) {
             }
         })
         .catch((error) => {
+            console.log(error);
             ToastAndroid.show(translations.translations.error_occurred, ToastAndroid.SHORT);
             that.setState({isLoading: false});
         });
# ---
# To remove '-' lines, make them ' ' lines (context).
# To remove '+' lines, delete them.
# Lines starting with # will be removed.

如果我完全删除添加的那一行(带有 console.log),然后保存并退出编辑器,我会收到以下错误提示:
error: corrupt patch at line 12

我完全不知道我做错了什么。


1
补丁块所做的唯一更改是添加那一行。如果您将其删除,则补丁变成了通过对文件不进行任何更改来更改文件的指令,这是不明智的。这里的错误消息有点傻,但您需要执行的操作很简单:“跳过此项”。 - torek
好的,谢谢。那么如果有其他我没有修改过的行,我就不会出现任何错误了吗?而且我不想跳过这一行,但我想取消更改。我需要在跳过更改后再进行checkout吗?(希望有更快的方法来实现这个目标。) - Simon
是的,如果还有其他更改,则修改后的补丁应该适用。请注意,您正在编辑位于索引中的文件副本。每个文件始终有三个副本。两个明显的副本是当前提交中的已提交版本 - 您无法更改此版本! - 和您的工作树中易于查看和编辑的副本。您可以随时在喜欢的编辑器中编辑它。但是,还有一个第三个副本,存储在Git称为索引暂存区缓存(三个名称相同)的位置中。(接下来) - torek
索引副本最初与提交的文件匹配。如果运行 git add <filename>,Git 只需将 工作树 版本复制到索引中,因此现在索引副本与工作树版本匹配。这是两种最常见的情况:匹配 HEAD匹配工作树。(如果 HEAD 和工作树副本匹配,则索引副本可以并且通常确实同时匹配两者。)git add -p 的作用是获取工作树副本和索引副本之间的任何不同之处,并将其中的一些差异添加到索引副本中。这样可以保持工作树版本不变!它只修补索引副本。 - torek
谢谢您提供的所有解释,您可以发布第一个回复,以便我接受它。 - Simon
好的。顺便说一下,我搞糊涂了关于 git reset -p 的描述,所以我会删除那个评论。 - torek
1个回答

5
这个补丁 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.txtREADME.txt不再匹配,但是:README.txtREADME.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命令。第一个是:

  • HEAD与索引有何不同?

对于每个完全相同的文件,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)提交,这时才会出现三个该新文件的副本。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接