TL;DR
主题行中问题的答案是“是的,但是”。 :-) 我会在这里改写一下,这样我们就可以讨论所有的技术细节。 “但是”是如果你使用git commit --only 会有很大的惊喜。我似乎记得曾经有一段时间它也严重影响了git commit -a,但现在更好了。 (我不确定哪个 Git 版本对 -a 表现不好。)
长
Git 从一个——或 ,在大多数情况下——索引进行每个新的提交。索引或任何索引本质上是一个提议的提交:它由一种扁平化的树组成,带有 blob 哈希号和文件名。它的形式特别方便git mktree,它将索引变成一系列tree对象,并返回顶级树对象的哈希ID,然后进入提交对象.
那么,这个问题涉及到这三个部分:
- 究竟哪个索引被用来构建新的提交?
- 可以修改该索引吗?如果是这样,新的提交会发生什么?
- 这对索引有何影响?
这里, 索引是与工作树一起使用的特殊的、可区分的索引。每个工作树都有一个这样的索引:如果您使用git worktree add,您将获得更多工作树,并且每个工作树都有自己的私有索引,但您只能在一个工作树中一次,所以“这个工作树的索引”是 索引。
但是,当您运行 git commit 时,您可以指示它(使用 --only 和/或其他命令行参数)构建自己的 private 索引,与 the 索引。如果你这样做了,它将运行带有环境变量GIT_INDEX_FILE 的各种钩子,该变量设置为临时索引的路径名。 (如果不是,GIT_INDEX_FILE 将包含 .git/index,这是 索引文件的路径。)
所以 Q1 的答案是:$GIT_INDEX_FILE。
现在,您实际上可以修改 Git 用于构建提交的任何索引,因为 Git 将在运行您的钩子后重新读取该索引。所以 Q2 的答案是:是的,这使得下一次提交使用正在使用的任何索引中的任何内容。
第三季度是最难的。如果你使用git commit --only <paths>,Git 必须创建一个临时索引:
- 是
HEAD的副本
- 除了指定的
<paths>
完全不影响 索引。但是,如果提交继续并成功,Git 现在必须修改 索引,以说明这些 <paths> 在新的 HEAD 提交中包含新的 blob。
事实上,Git 需要创建 两个 临时索引(索引?),一个用于建议的提交及其 --only 文件,另一个用于建议的提交结果。提议的提交结果变为index.lock,作为新的索引。
如果您在运行时git add 文件,它们将进入临时索引。但是 索引呢?让我们找出答案:
$ cat .git/hooks/pre-commit
#! /bin/sh
echo pre-commit hook, GIT_INDEX_FILE = $GIT_INDEX_FILE
git add sneaky
$ echo this is the base version of sneaky > sneaky
$ echo this is the base version of other > other
$ git add other sneaky
$ git commit -m 'create two files'
pre-commit hook, GIT_INDEX_FILE = .git/index
[master 5131b63] create two files
2 files changed, 2 insertions(+)
create mode 100644 other
create mode 100644 sneaky
如您所见,预提交钩子在这里触发,并添加了sneaky。这没什么大不了的,因为复制到此提交的索引中的内容(即 the 索引)已经是相同的基本版本。
然而,现在让我们修改sneaky、git add它的当前内容,同时修改other和git add它的当前内容,这样我们在 两个文件的索引...
$ echo version 2 of other > other
$ echo version 2 of sneaky > sneaky
$ git add other sneaky
此时,索引和工作树都有“版本 2”。现在让我们将两个工作树文件更新为“版本 3”git adding 他们:
$ echo version 3 of other > other
$ echo version 3 of sneaky > sneaky
$ git status --short
MM other
MM sneaky
这告诉我们HEAD、索引和工作树版本都不同:HEAD 版本是基础版本,索引版本是版本 2,工作树版本是版本 3。
现在我们运行git commit --only other:
$ git commit --only other -m 'jump other straight to v3'
pre-commit hook, GIT_INDEX_FILE = [path]/.git/next-index-72393.lock
[master 91ec03b] jump other straight to v3
2 files changed, 2 insertions(+), 2 deletions(-)
现在让我们看看提交、索引和工作树中有什么:
$ git status --short
MM sneaky
好的,other 的工作树版本与other 的索引版本匹配,HEAD 的other 版本匹配,所以:
$ cat other
version 3 of other
它们都是第 3 版。不过,sneaky 呢? HEAD 和索引不同,索引和工作树不同。让我们先看看HEAD 中的内容:
$ git show HEAD:sneaky
version 3 of sneaky
现在让我们看看工作树中有什么:
$ cat sneaky
version 3 of sneaky
啊哈,这些匹配!棘手的部分是查看索引版本,但我们也可以使用git show:
$ git show :0:sneaky
version 2 of sneaky
哇,看那个! 索引版本是旧版本!
那么,这就是 Q3 的答案:pre-commit 挂钩中的 git add 更新用于构建下一次提交的任何索引,但如果这是一个 临时 索引,它不更新将成为真正索引的内容。这可以说是一个错误:预提交钩子中的git add 也应该添加到将成为 索引的索引中。实现这一点有点棘手(git commit 可能会在钩子之前和之后读取临时索引并将任何更新复制到 index.lock 文件中)。
请注意,如果您在没有--only 的情况下提交会发生什么,我跳过了详细信息。幸运的是,在这种情况下,您添加的索引是 索引,所以一切都按您的预期进行:
$ git reset --hard 5131b63
HEAD is now at 5131b63 create two files
$ echo v2 > other && echo v2 > sneaky && git add other
$ git commit -m 'regular commit'
pre-commit hook, GIT_INDEX_FILE = .git/index
[master 10a8b20] regular commit
2 files changed, 2 insertions(+), 2 deletions(-)
$ git status --short
$
预提交git add 替换了sneaky 的索引版本,并且它一直被替换...即使提交失败,它也会被替换。
还要注意,当使用git commit -a 时,我们会得到一个不同的临时索引:
$ git commit -a -m test
pre-commit hook, GIT_INDEX_FILE = [path]/.git/index.lock
在这里,Git 为 git commit -a 所做的就是创建建议的新索引 as index.lock 文件。 git add 正常进行,以通常的方式添加文件,然后当提交成功时,Git renames index.lock 到 index。这将解锁索引文件,同时将修改后的索引放置到位,因此git commit -a 到 Q3 的答案是临时索引成为索引。这与git commit --only 不同。