- 我可以在 git hooks 中调用 git 吗?
是的,但您必须谨慎行事,因为环境中设置了许多东西,并且您正在处理一些正在完成的事情:
-
GIT_DIR 设置为 Git 目录的路径。
-
GIT_WORKTREE 可以设置为工作树的路径(来自git --work-tree)。
- 也可以从命令行设置其他 Git 变量,例如
GIT_NO_REPLACE_OBJECTS。
(如果您继续使用当前存储库,则应保留这些设置,但如果您使用的是不同存储库,请将它们清除。)
- 如果 1. 没问题:如果我执行 git commit -am"bla",什么时候会调用预提交挂钩?特别是 git 是否先进行暂存,然后调用 pre-commit 钩子?
这很复杂。
git commit 在内部使用了三种“模式”。 (对此没有任何承诺,但这就是多年来一直实施的方式,所以这三种模式的东西看起来相当稳定。)模式是:
这里的底层实现细节涉及 Git 调用的东西,不同的是,index、暂存区和(现在很少)缓存 em>,它通常实现为一个名为 $GIT_DIR/index 的文件(其中 $GIT_DIR 是关于第 1 点的注释中的环境变量)。通常,只有以下之一: 索引。它包含您打算提交的内容。1 当您运行git commit 时,Git 会将索引中的任何内容打包作为下一次提交。
但是,在操作 git commit 的过程中,最多可能有三个 索引文件。对于普通的git commit,只有一个索引,您的预提交钩子可以使用它,甚至可以更新它。 (我建议不要更新它,原因我们稍后会看到。)
但是,如果您执行git commit -a 或git commit --include file.ext,则现在有两个 索引文件。有准备提交的内容——常规索引——和一个额外索引,它是原始索引加上在file.ext上执行git add的结果或所有文件(相当于git add -u)。所以现在有两个索引文件。
在这种模式下,Git 将常规索引文件作为常规索引文件。该文件像往常一样位于$GIT_DIR/index。 second 索引文件以及额外添加的内容位于$GIT_DIR/index.lock 中,并且环境变量GIT_INDEX_FILE 设置为保存该路径。如果提交失败,Git 将删除index.lock 文件,一切都好像你根本没有运行git commit。如果提交成功,Git 会将index.lock 重命名为index,同时释放锁并更新(标准、常规)索引。
最后,还有 third 模式,例如,当您运行 git commit --only file.ext 时会得到该模式。这里,现在有三个索引文件:
-
$GIT_DIR/index:标准索引,保存它通常做的事情。
-
$GIT_DIR/index.lock:file.ext 已成为 git add-ed 的标准索引的副本。
-
$GIT_DIR/index<em>suffix</em>:file.ext 已成为 git add-ed 的 HEAD 提交2 的副本。
环境变量GIT_INDEX_PATH 指向第三个索引。如果提交成功,Git 会将index.lock 文件重命名为index,使其成为 索引。如果提交失败,Git 将删除 index.lock 文件,以便索引恢复到您开始之前的状态。 (无论哪种情况,Git 都会删除第三个索引,它现在已经达到了它的目的。)
请注意,通过预提交挂钩,您可以检测 git commit 是标准提交(GIT_INDEX_FILE 未设置或设置为 $GIT_DIR/index)还是两种特殊模式之一。在标准模式下,如果要更新 索引,可以照常进行。在这两种特殊模式下,您可以使用git add 修改GIT_INDEX_FILE 命名的文件,这将修改提交的内容;如果您在--include 样式提交中,这也会修改将成为成功标准索引的内容。但如果您处于--only 模式,修改提议的提交不会影响标准index,也不index.lock 将成为标准索引。
考虑一个具体的例子,假设用户做了:
git add file1 file2
以便标准索引与HEAD 匹配,file1 和 file2 除外。然后用户运行:
git commit --only file3
因此提议的提交是HEAD 的副本,添加了file3,并且,如果此提交成功,Git 将替换标准索引为其中file1,@ 987654381@ 和 file3 都已添加(但由于 file3 将匹配新的 HEAD 提交,因此只有文件 1 和 2 将在新索引中修改)。
现在假设您的提交挂钩运行git add file4 并且整个过程成功(新提交成功)。 git add 步骤会将file4 的工作树版本复制到临时索引中,以便提交将同时更新file3 和 file4。然后 Git 将重命名 index.lock 文件,以便 file3 将匹配新的 HEAD 提交。但是index.lock 中的file4 从未更新,因此它不会 匹配HEAD 提交。在用户看来,file4 以某种方式被还原了! git status 将显示对它的待定更改,暂存于提交,git diff --cached 将显示HEAD 和索引之间的区别在于file4 已更改回以匹配@987654402 中的file4 @。
您可以对此模式进行预提交挂钩测试,并在此模式下拒绝git add 文件,以避免出现问题。 (或者,您甚至可以偷偷地将file4 添加到index.lock,使用第二个git add 命令!)但通常最好让您的钩子拒绝提交,并建议用户执行任何git adds他们自己,这样您就不必首先了解所有这些实现秘密关于 git commit。
1索引还包含一些额外的信息:缓存数据关于工作树。这就是为什么它有时被称为缓存。我在这里描述的这些额外副本是通过复制原始索引制作的,因此额外副本也具有相同的缓存数据,除非它们通过git add 更新。
2未指定 Git 是否通过内部等效项制作此副本:
TMP=$GIT_DIR/index<digits>
cp $GIT_DIR/index $TMP
GIT_INDEX_FILE=$TMP git reset
GIT_INDEX_FILE=$TMP git add file3
或其他方式(例如,git read-tree 的内部等效项),但由于此特定副本总是在进程结束时被删除,所以没关系:工作树的任何缓存信息都变为无关紧要。