【发布时间】:2016-11-09 22:12:42
【问题描述】:
我在 SO 上看到了following 问题,它询问如何在 git 中进行跟踪但不能暂存以及如何取消暂存但不能取消跟踪文件。作为一个 git 初学者,我很好奇这两种情况在哪些特定场景下有用?
【问题讨论】:
-
请澄清您的反对意见,以便我改进问题。谢谢。
标签: git
我在 SO 上看到了following 问题,它询问如何在 git 中进行跟踪但不能暂存以及如何取消暂存但不能取消跟踪文件。作为一个 git 初学者,我很好奇这两种情况在哪些特定场景下有用?
【问题讨论】:
标签: git
从技术上讲,您不能“跟踪但不暂存”文件,在某种意义上也不能“不暂存而不跟踪”,除非 Git 和人们都使用“未暂存”一词来指代别的东西。 (我会稍微描述一下“unstage”的含义。)
如果文件在 Git 所谓的“索引”(即“暂存区”)中有一个条目,则该文件在 Git 中被“跟踪”。 git add 和 git rm 都在索引中创建或替换了一个条目——git rm 放入了一种“whiteout”条目,意思是“它只是现在在这里,但在我提交之后,让它不在一切都是为了不再被追踪。” (事实上,git add 可以像git rm --cached 一样工作,如果文件在索引中,但在工作树中缺失,则这样做。)
您可以通过git ls-files --stage 看到(大部分)索引中的内容;请参见下面的示例。每个文件路径名最多有四个索引“槽”。其中三个仅在合并期间使用。因此,除了合并时,索引中的所有内容始终处于“阶段零”。
要stage 一个文件,我们只需运行git add <em>path</em>(或git add -- <em>path</em>,如果path 部分类似于-p,这很有用,尽管您也可以将文件 -p 命名为 ./-p 以防止它看起来像补丁标志)。如果文件已经在索引中,则更新它(通过替换现有的索引条目)。
要“取消暂存”文件,我们通常运行git reset <em>path</em>(如果需要,请添加--,如前所述)。但是,除了一种情况,这不会删除路径的索引条目!相反,它重置(因此得名)索引条目以匹配HEAD 提交。它确实完全删除条目的一种情况是HEAD 提交在该路径名下没有文件。
因此,“取消暂存”文件并没有真正取消-暂存文件:相反,它重新-暂存(或者,更好的是,重置 em>) 将其返回到当前(HEAD)提交,但不触及工作树中的副本。不过这个动作大家都用“unstage”这个词,所以我们不妨习惯一下。 :-)
您链接到的问题是关于使用git add -N。这在很长一段时间内完全被破坏了,在 Git 2.5 版本中有所修复,在 Git 2.10.1 中仍然部分被破坏(我刚刚在我的测试中)。它有效地将占位符放入索引中,以便跟踪文件 (因为它在索引中,当然),但同时那里没有实际文件。这个索引条目被标记为“特殊”,很像一个白化条目。该行为实际上与 whiteout 条目非常相似——即,git write-tree 确实不将文件写入结果树——除了 Intent-to-add 特殊条目在提交后保留,而 whiteout提交后条目被删除。
至于它有什么用途:在我看来,它没有。 :-) 显然,解决错误(-N 在低于 2.5 的 Git 版本中完全失败,2.5 到 2.10 中的小问题)并不是很重要,因此可能很少有人(如果有的话)真正使用它。
它完成的一件事是让git diff 将文件视为已跟踪文件,以便将当前索引与工作树进行比较的git diff 显示添加的文件内容:
$ git status --short
?? thirdfile
(这表示“第三个文件”未被跟踪)
$ git diff
(无输出:git diff 不查看未跟踪的文件)
$ git add -N thirdfile
$ git status --short
AM thirdfile
(这表明该文件现在被跟踪——在索引中,而不是在HEAD 提交中,因此将A 声明为第一个字母——并且也被修改了,因为索引条目是假的但文件存在内容真实)
$ git diff
diff --git a/thirdfile b/thirdfile
index e69de29..1ecbedd 100644
--- a/thirdfile
+++ b/thirdfile
@@ -0,0 +1 @@
+data for file 3
(在这里我们看到了真正的内容)。如果我们使用git ls-index --stage 查看索引,我们会看到1 一个零阶段条目——一个普通文件——其哈希值是空文件的哈希值:
$ git ls-files --stage
100644 e5c143f6fec374d115ef674fc036649d78c625d2 0 README
100644 6b8bd59d197cba9032b59ee3aa3f4e71cfbdc2df 0 dummyfile
100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 thirdfile
$ git hash-object -t blob --stdin < /dev/null
e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
如果我们清空文件,工作树中的 real 文件现在与存储在索引中的哈希匹配。奇怪的是,虽然git diff 现在什么都没有显示,但git status 仍然表示文件已被修改,与索引版本相比:
$ : > thirdfile
$ git status --short
AM thirdfile
$ git diff
(最后一个命令不产生输出)。与工作树相比,缓存条目被标记为“意图添加”的文件始终被视为“已修改”,这是“在 Git 2.10.1 中部分损坏”案例的来源,因为这愚弄了 @987654353 @ into 允许提交,即使新提交的内容与HEAD 提交的内容匹配。
一旦你git add(没有-N)文件,索引中带有“intent to add”标记的stage-0 条目将成为具有真实哈希的真实blob(即文件)的真实条目指向存储库中的真实对象。也就是说,真实的条目会覆盖虚假的条目。如果您首先从工作树中删除该文件,然后在该路径上运行 git add,Git 将完全删除带有特殊标记的索引条目,就像您在其上运行 git rm --cached 时 Git 所做的一样。这与在真实(非特殊标记)文件上使用 git rm --cached 不同,在该文件中,Git 将白化条目写入索引。2
1不清楚为什么git ls-files --staged 显示这些意图添加标记的条目。相同的命令跳过白出条目。
2我们可以使用git status --short 来查看白化条目。我们只需在工作树中创建文件,或使用git rm --cached 以便在将白化条目写入索引时文件保留在工作树中。此时git ls-files --stage 不 显示文件,git diff 显示文件已删除,git status 显示文件已删除并且未跟踪,@ 987654366@ 为文件生成 两个 条目:
$ git rm --cached secondfile
$ git status --short
D secondfile
?? secondfile
?? thirdfile
要拥有D 条目,索引中必须有内容;但是要拥有?? 条目,Git 必须假装索引中没有任何内容。因此这里有一个secondfile 的白化条目,在索引的一些 扫描中可见,但thirdfile 没有,就在刚才,我在索引中作为其中之一意图添加虚假条目。
【讨论】: