TL;DR
主题行中的问题的答案是“是的,但是”。 :-) 我将在这里稍微改一下措辞,以便我们可以讨论所有技术细节。 “但是”是如果你使用的话会有一个很大的惊喜git commit --only
。我好像记得有段时间它影响了git commit -a
也很糟糕,但现在好多了。 (我不确定哪些 Git 版本表现不佳-a
.)
Long
Git 使每个新的提交都来自——或the,在大多数情况下——索引。索引或任何索引本质上是建议的提交:它由一种扁平树组成,带有 blob 哈希值和文件名。它的形式特别方便git mktree
,它将索引变成一系列tree对象并返回顶级树对象的哈希 ID,然后该对象将进入提交对象。
那么,问题涉及这三个部分:
- 恰恰which索引被用来构建新的提交?
- 那个索引可以修改吗?如果是这样,新的提交会发生什么?
- 这有何影响the index?
Here, the索引是与工作树一起使用的特殊的、区分的索引。每个工作树有一个这样的索引:如果您使用git worktree add
你会得到更多的工作树,并且每个工作树都有自己的私有索引,但你只能in一次一个工作树,所以“这个工作树的索引”是the index.
然而,当你跑步时git commit
,你可以指示它(使用--only
和/或其他命令行参数)来构建自己的private索引,与the指数。如果您这样做了,它将使用环境变量运行每个不同的钩子,GIT_INDEX_FILE
,设置为临时索引的路径名。 (如果不,GIT_INDEX_FILE
将包含.git/index
,这是通往the索引文件。)
所以问题1的答案是:$GIT_INDEX_FILE
.
现在,您实际上可以修改 Git 用于构建提交的索引,因为 Git 会在运行钩子后重新读取该索引。所以问题 2 的答案是:是的,这使得下一次提交使用正在使用的索引中的任何内容。
Q3是最难的。如果你使用git commit --only <paths>
,Git 必须创建一个临时索引:
不打扰the根本没有索引。但是,如果提交继续并成功,Git 现在必须修改the索引来说明这些事实<paths>
新的中有新的斑点HEAD
commit.
事实上,Git 需要使two临时索引(索引?),一个用于建议的提交及其--only
文件,一个用于建议的提交结果。提议的提交结果变为index.lock
,充当新索引。
If you git add
文件运行时,它们将进入临时索引。但是关于the指数?让我们来了解一下:
$ 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
, and git add
它,这样我们就有了新的内容the两个文件的索引...
$ echo version 2 of other > other
$ echo version 2 of sneaky > sneaky
$ git add other sneaky
此时,索引和工作树都有“版本2”。现在让我们将两个工作树文件更新为“版本 3”without git add
荷兰国际集团他们:
$ 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
, so:
$ cat other
version 3 of other
它们都是版本 3。那又如何呢?sneaky
, 尽管?这HEAD
和索引不同,索引和工作树也不同。让我们看看里面有什么HEAD
first:
$ git show HEAD:sneaky
version 3 of sneaky
现在让我们看看工作树中有什么:
$ cat sneaky
version 3 of sneaky
啊哈,这些很匹配!棘手的部分是查看索引版本,但我们也可以这样做git show
:
$ git show :0:sneaky
version 2 of sneaky
哇,看看那个!The索引版本是旧版本!
那么,这就是问题 3 的答案:git add
在预提交挂钩中更新用于构建下一个提交的索引,但如果这是一个暂时的索引,它does not更新将成为真实索引的内容。这可以说是一个错误:git add
在预提交挂钩中还应该添加到将成为的索引the指数。实现这一点有点棘手(git commit
也许可以在挂钩之前和之后读取临时索引并将任何更新复制到index.lock文件)。
请注意,我跳过了如果您在没有提交的情况下提交会发生什么的详细信息--only
。幸运的是,在这种情况下,您添加到的索引是the索引,所以一切都按您的预期进行:
$ 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
,并且它保持被替换......即使提交它也会被替换fails.
另请注意,使用时git commit -a
,我们得到一个不同的临时索引:
$ git commit -a -m test
pre-commit hook, GIT_INDEX_FILE = [path]/.git/index.lock
在这里,Git 的用途git commit -a
是创建建议的新索引as the index.lock
文件。这git add
正常进行,按照通常的方式添加文件,然后当提交成功时,Gitrenames index.lock
to index
。这会解锁索引文件,同时将修改后的索引放在适当的位置,以便答案git commit -a
到Q3就是临时索引变成了索引。这与对于git commit --only
.