"git commit ." 预提交挂钩修改暂存文件时的意外行为
Unexpected behavior with "git commit ." when pre-commit hook modifies staged files
根据我的经验,git commit -a
具有与 git commit .
相同的行为,但是,最近我创建了一个预提交挂钩,可以自动格式化我的源代码,现在 git commit .
有一些意想不到的事情副作用:提交命令完成后,提交的文件最终在工作目录和索引中被修改。 git commit -a
不会发生这种情况。我试图了解导致这种情况发生的 运行 git commit .
时幕后发生的事情,看看是否有办法在我的预提交挂钩脚本中正确处理它。
预提交挂钩:
git_toplevel=$(git rev-parse --show-toplevel)
git --no-pager diff -z --cached --name-only --diff-filter=ACMRT | $git_toplevel/meta/reformat.bash -s files
git --no-pager diff -z --name-only --diff-filter=ACMRT | xargs -0 --no-run-if-empty git add
目前正在使用 git 版本 1.8.3.1,但我在更新的版本中看到了相同的行为。
以下是在行首添加的简单 space 的命令序列:
[]$ git status
# On branch eroller/format-clean-filter
# Your branch is ahead of 'origin/eroller/format-clean-filter' by 1 commit.
# (use "git push" to publish your local commits)
#
# Changes not staged for commit:
# (use "git add <file>..." to update what will be committed)
# (use "git checkout -- <file>..." to discard changes in working directory)
#
# modified: src/host/cnv/denovo/denovo_cnv.cpp
#
no changes added to commit (use "git add" and/or "git commit -a")
-
[]$ git diff
diff --git a/src/host/cnv/denovo/denovo_cnv.cpp b/src/host/cnv/denovo/denovo_cnv.cpp
index 7cfb8dc..14058e3 100644
--- a/src/host/cnv/denovo/denovo_cnv.cpp
+++ b/src/host/cnv/denovo/denovo_cnv.cpp
@@ -28,7 +28,7 @@ using namespace std;
namespace cnv {
namespace denovo {
-SegmentsBySample LoadCallsForSamples(const vector<string>& callFiles, const ReferenceDictionary& reference)
+ SegmentsBySample LoadCallsForSamples(const vector<string>& callFiles, const ReferenceDictionary& reference)
{
function<SegmentsBySample::value_type(const string&)> loadCalls = [&](string callFile) {
return LoadCalls(callFile, reference);
-
[]$ git commit -m 'test' .
-
[]$ git status
# On branch eroller/format-clean-filter
# Your branch is ahead of 'origin/eroller/format-clean-filter' by 2 commits.
# (use "git push" to publish your local commits)
#
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# modified: src/host/cnv/denovo/denovo_cnv.cpp
#
# Changes not staged for commit:
# (use "git add <file>..." to update what will be committed)
# (use "git checkout -- <file>..." to discard changes in working directory)
#
# modified: src/host/cnv/denovo/denovo_cnv.cpp
#
-
[]$ git diff
diff --git a/src/host/cnv/denovo/denovo_cnv.cpp b/src/host/cnv/denovo/denovo_cnv.cpp
index 14058e3..7cfb8dc 100644
--- a/src/host/cnv/denovo/denovo_cnv.cpp
+++ b/src/host/cnv/denovo/denovo_cnv.cpp
@@ -28,7 +28,7 @@ using namespace std;
namespace cnv {
namespace denovo {
- SegmentsBySample LoadCallsForSamples(const vector<string>& callFiles, const ReferenceDictionary& reference)
+SegmentsBySample LoadCallsForSamples(const vector<string>& callFiles, const ReferenceDictionary& reference)
{
function<SegmentsBySample::value_type(const string&)> loadCalls = [&](string callFile) {
return LoadCalls(callFile, reference);
-
[]$ git diff --cached
diff --git a/src/host/cnv/denovo/denovo_cnv.cpp b/src/host/cnv/denovo/denovo_cnv.cpp
index 7cfb8dc..14058e3 100644
--- a/src/host/cnv/denovo/denovo_cnv.cpp
+++ b/src/host/cnv/denovo/denovo_cnv.cpp
@@ -28,7 +28,7 @@ using namespace std;
namespace cnv {
namespace denovo {
-SegmentsBySample LoadCallsForSamples(const vector<string>& callFiles, const ReferenceDictionary& reference)
+ SegmentsBySample LoadCallsForSamples(const vector<string>& callFiles, const ReferenceDictionary& reference)
{
function<SegmentsBySample::value_type(const string&)> loadCalls = [&](string callFile) {
return LoadCalls(callFile, reference);
更新:使用来自@torek 的非常详尽的答案(谢谢!),如果用户尝试使用 git commit .
或 git commit [--only] -- <files>
,我决定在预提交挂钩中给出一个错误。这是我的预提交脚本中的检查:
if [[ $GIT_INDEX_FILE != *"/index" ]] && [[ $GIT_INDEX_FILE != *"/index.lock" ]] ; then
echo "Error: pre-commit reformatting using unsupported index file ($GIT_INDEX_FILE)." >&2
echo " Are you using 'git commit [--only] -- <files>' to bypass staging?" >&2
echo " Use git commit -a or stage your files before committing using git add -- <files>" >&2
echo " Use '--no-verify' to bypass reformatting (not recommended)" >&2
exit 1
fi
这里的根本问题是 Git 不是从工作树而是从索引进行提交,这就是为什么你首先需要 git add
文件——但是 索引是一种善意的谎言,因为 可以 索引文件多于一个标准文件。 (该索引也称为 暂存区 或 缓存 ,具体取决于 Git 的哪个部分进行调用。)
索引,我指的是标准索引,是 .git
中名为 index
的文件。如果您检查 .git
目录,您会发现这样一个文件。以前真的只有这一个文件。在现代 Git(2.5 以上)中,由于添加了工作树,图片变得更加模糊:实际上每个工作树都有一个索引文件,因此 .git/index
只是 the main 工作树的索引。每个工作树都有一个辅助 the 索引——但这并不是我要表达的意思,在这里,它只是一个例子,说明假设已经存在一个索引在边缘磨损。无可否认,您使用的是 Git 1.8.3.1(确实很旧),但它也比漂亮的简单善意的“一个索引”设置更复杂。
当您使用 git commit -a
时,Git 会创建一个新的额外索引。当您使用 git commit .
时,您正在调用 git commit --only .
(see the documentation for details),并且 Git 使 two 新的额外索引(索引? ).
Git 的所有部分都能够重定向 Git 的 rest 以使用不同的非标准索引,这些不同的选项git commit
利用此功能。请注意,git commit -a
等同于 git commit --include
后跟任何需要添加的文件的名称。真正棘手的情况是您正在使用的情况,git commit --only
。
一旦开始增加索引文件,事情就会变得混乱!
请记住,索引本质上是提议的下一次提交。如果只有一个索引(对于这个工作树,如果我们谈论 Git 2.5 或更高版本),则只有一个提议的下一次提交。这并不难,我们只需要考虑每个文件有三个个副本即可。让我们选择一个文件,例如 README.md
:
HEAD:README.md
是 README.md
当前提交的版本。你无法改变它。 (您可以移动 HEAD
本身,但是 README.md
的已提交副本在提交中,由提交的哈希 ID 找到,并且不会更改。)
名称HEAD:README.md
仅在Git 中有效。该名称访问这个冻结的、Git 化的、冻干的文件副本;这个副本永远不会改变。例如,您可以通过 git show HEAD:README.md
查看它。
:README.md
是索引中 README.md
的副本。本来和HEAD:README.md
是一样的,但是如果你运行git add README.md
,现在可能不一样了。
名称:README.md
也只能在Git 中使用。该名称访问存储在索引中的此可替换但 Git 化(冻干格式)文件副本。您可以随时将其替换为 git add
.
最后,README.md
是一个普通的(非Git化)文件。它不在 Git 中!它不在索引中!它位于您的 工作树 中,您可以在其中使用所有常规计算机工具查看和处理它。 Git 真的不使用这个文件做任何事情,它只是在你检查其他提交时覆盖它或删除它。 Git 唯一用它做的事情,除了用 git status
等检查它,是让你用 git add
把它 复制 回索引,覆盖之前的内容(并在此过程中冷冻干燥)。
运行 git status
运行两个git diff
:
首先将 HEAD
提交与索引进行比较,即当前提交中的内容与提议的下一次提交中的内容。任何不同 在这里被列为暂存提交。一样的东西,Git只是默默的不说。
第二个 git diff
将索引与工作树进行比较,即建议提交中的内容与您可以复制到索引中的内容。任何 不同 在这里被列为 not staged for commit。再一次,任何相同的东西,Git 安静地什么都不说。
(然后最后一次检查工作树中根本不在索引中的文件。Git 会抱怨这些,说它们未被跟踪, 除非你在 .gitignore
中列出它们。在 .gitignore
中列出不会改变索引中是否有文件的副本,它只会改变 Git 是否抱怨。)
当你 运行 git commit
, Git 打包索引中的任何内容,并使用它来进行新提交... unless 您使用 --only
、--include
或 -a
。
指数out the wazoo
使用 git commit --only
,Git 使 三个 个索引文件:
- 一个是标准的。它一开始就没有动过。这是正常的
.git/index
.
- 一个是那个的副本,
--only
个文件 git add
添加到它。它在某个时候位于 .git/index.lock
中。 也许它一直都在这里!如果是这样,那将提供一种方法来处理我在下面概述的案例。但是没有文档可以保证这一点。
- 第三个是新的,首先提取
HEAD
,然后 git add
将 --only
文件添加到其中。
如果在 之前 运行 git commit -a
,第一个和第三个索引文件匹配,因为添加 --only
个文件添加到常规索引与从 HEAD
创建一个新的临时索引并将 --only
个文件添加到它具有相同的效果。但除此之外,所有三个文件可能都不同!
Git 然后从 third 索引进行新的提交。如果新提交成功,Git 将常规索引替换为 second 索引(此替换通过 rename
系统调用发生)。否则 Git 返回正常索引。 (请注意,工作树根本没有任何变化。)
如果你使用git commit --include
或git commit -a
,Git只会产生一个额外的索引,所以你有:
.git/index
中的标准索引,以及您目前添加的任何内容;和
- 临时文件中的额外索引:这开始时是标准索引的副本,但随后 Git 将列出的文件或其他修改过的文件添加到该索引。
然后 Git 开始提交过程。如果一切顺利,当 Git 完成后,Git 会重命名临时索引,使其成为标准索引。如果情况不妙,Git 删除临时索引,标准索引保持不变。同样,工作树没有任何反应。
引入预提交挂钩
Git 运行 在准备任何额外的索引文件后,这是您的预提交挂钩。特殊环境变量 $GIT_INDEX_FILE
命名 Git 将用于进行新提交的索引。所以一共有三种情况,两种还算不错,一种很糟糕:
- 您正在进行正常提交。
GIT_INDEX_FILE
命名正常索引,一切正常
- 您正在执行
git commit --include
或 git commit -a
和 GIT_INDEX_FILE
命名第二个索引;没有第三个索引;如果提交完成,Git 将重命名第二个索引。
- 您正在执行
git commit --only
和 GIT_INDEX_FILE
命名第三个索引。 找到第二个索引并不容易,如果提交成功,第二个索引将在提交后就位!
如果您选择对存储在索引中的文件进行更改,您的工作就是将它们添加到 Git 将用于提交的索引中。为此,您可以根据需要使用 git add
,因为这会将文件从工作树复制到 $GIT_INDEX_FILE
中命名的索引。
不过,第一个问题是您不能查看工作树中的文件。它们无关紧要!它们可能包含与索引中的内容完全不同的内容。在 git commit --only
.
期间尤其如此
第二个也是更大的问题是,如果您更新了 git commit --only
使用的 third 索引,您还应该更新 second git commit --only
正在使用的索引。这部分很棘手,因为没有简单的方法可以找到它,除了假设它在 .git/index.lock
中。虽然这可能有用,但我不会在这里建议。
对此我真的没有任何建议——您发现的任何偷偷摸摸的方法都可能会失败,因为处理第三个索引(当前 2.21-ish Git 称为“错误索引”)的代码已经在 1.8 和现代 Git 之间发生了很大变化。通常的最佳实践建议是 不要 在 Git 挂钩中进行任何特殊格式化。相反,让 Git 挂钩仅检查 文件的索引副本是否 格式正确:如果是,则继续提交,如果不是,则中止提交。剩下的交给用户。
还有一个选择
我看到并使用的另一种方法是检查 $GIT_INDEX_FILE
的实际设置。如果设置为 .git/index
,则用户使用的是 git commit
,无需任何特殊设置。 this same pre-commit hook 中的另一个技巧(调用 clang-format 和 autopep8)是比较要格式化的文件的索引和工作树,如果不匹配则拒绝 运行。
根据我的经验,git commit -a
具有与 git commit .
相同的行为,但是,最近我创建了一个预提交挂钩,可以自动格式化我的源代码,现在 git commit .
有一些意想不到的事情副作用:提交命令完成后,提交的文件最终在工作目录和索引中被修改。 git commit -a
不会发生这种情况。我试图了解导致这种情况发生的 运行 git commit .
时幕后发生的事情,看看是否有办法在我的预提交挂钩脚本中正确处理它。
预提交挂钩:
git_toplevel=$(git rev-parse --show-toplevel)
git --no-pager diff -z --cached --name-only --diff-filter=ACMRT | $git_toplevel/meta/reformat.bash -s files
git --no-pager diff -z --name-only --diff-filter=ACMRT | xargs -0 --no-run-if-empty git add
目前正在使用 git 版本 1.8.3.1,但我在更新的版本中看到了相同的行为。
以下是在行首添加的简单 space 的命令序列:
[]$ git status
# On branch eroller/format-clean-filter
# Your branch is ahead of 'origin/eroller/format-clean-filter' by 1 commit.
# (use "git push" to publish your local commits)
#
# Changes not staged for commit:
# (use "git add <file>..." to update what will be committed)
# (use "git checkout -- <file>..." to discard changes in working directory)
#
# modified: src/host/cnv/denovo/denovo_cnv.cpp
#
no changes added to commit (use "git add" and/or "git commit -a")
-
[]$ git diff
diff --git a/src/host/cnv/denovo/denovo_cnv.cpp b/src/host/cnv/denovo/denovo_cnv.cpp
index 7cfb8dc..14058e3 100644
--- a/src/host/cnv/denovo/denovo_cnv.cpp
+++ b/src/host/cnv/denovo/denovo_cnv.cpp
@@ -28,7 +28,7 @@ using namespace std;
namespace cnv {
namespace denovo {
-SegmentsBySample LoadCallsForSamples(const vector<string>& callFiles, const ReferenceDictionary& reference)
+ SegmentsBySample LoadCallsForSamples(const vector<string>& callFiles, const ReferenceDictionary& reference)
{
function<SegmentsBySample::value_type(const string&)> loadCalls = [&](string callFile) {
return LoadCalls(callFile, reference);
-
[]$ git commit -m 'test' .
-
[]$ git status
# On branch eroller/format-clean-filter
# Your branch is ahead of 'origin/eroller/format-clean-filter' by 2 commits.
# (use "git push" to publish your local commits)
#
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# modified: src/host/cnv/denovo/denovo_cnv.cpp
#
# Changes not staged for commit:
# (use "git add <file>..." to update what will be committed)
# (use "git checkout -- <file>..." to discard changes in working directory)
#
# modified: src/host/cnv/denovo/denovo_cnv.cpp
#
-
[]$ git diff
diff --git a/src/host/cnv/denovo/denovo_cnv.cpp b/src/host/cnv/denovo/denovo_cnv.cpp
index 14058e3..7cfb8dc 100644
--- a/src/host/cnv/denovo/denovo_cnv.cpp
+++ b/src/host/cnv/denovo/denovo_cnv.cpp
@@ -28,7 +28,7 @@ using namespace std;
namespace cnv {
namespace denovo {
- SegmentsBySample LoadCallsForSamples(const vector<string>& callFiles, const ReferenceDictionary& reference)
+SegmentsBySample LoadCallsForSamples(const vector<string>& callFiles, const ReferenceDictionary& reference)
{
function<SegmentsBySample::value_type(const string&)> loadCalls = [&](string callFile) {
return LoadCalls(callFile, reference);
-
[]$ git diff --cached
diff --git a/src/host/cnv/denovo/denovo_cnv.cpp b/src/host/cnv/denovo/denovo_cnv.cpp
index 7cfb8dc..14058e3 100644
--- a/src/host/cnv/denovo/denovo_cnv.cpp
+++ b/src/host/cnv/denovo/denovo_cnv.cpp
@@ -28,7 +28,7 @@ using namespace std;
namespace cnv {
namespace denovo {
-SegmentsBySample LoadCallsForSamples(const vector<string>& callFiles, const ReferenceDictionary& reference)
+ SegmentsBySample LoadCallsForSamples(const vector<string>& callFiles, const ReferenceDictionary& reference)
{
function<SegmentsBySample::value_type(const string&)> loadCalls = [&](string callFile) {
return LoadCalls(callFile, reference);
更新:使用来自@torek 的非常详尽的答案(谢谢!),如果用户尝试使用 git commit .
或 git commit [--only] -- <files>
,我决定在预提交挂钩中给出一个错误。这是我的预提交脚本中的检查:
if [[ $GIT_INDEX_FILE != *"/index" ]] && [[ $GIT_INDEX_FILE != *"/index.lock" ]] ; then
echo "Error: pre-commit reformatting using unsupported index file ($GIT_INDEX_FILE)." >&2
echo " Are you using 'git commit [--only] -- <files>' to bypass staging?" >&2
echo " Use git commit -a or stage your files before committing using git add -- <files>" >&2
echo " Use '--no-verify' to bypass reformatting (not recommended)" >&2
exit 1
fi
这里的根本问题是 Git 不是从工作树而是从索引进行提交,这就是为什么你首先需要 git add
文件——但是 索引是一种善意的谎言,因为 可以 索引文件多于一个标准文件。 (该索引也称为 暂存区 或 缓存 ,具体取决于 Git 的哪个部分进行调用。)
索引,我指的是标准索引,是 .git
中名为 index
的文件。如果您检查 .git
目录,您会发现这样一个文件。以前真的只有这一个文件。在现代 Git(2.5 以上)中,由于添加了工作树,图片变得更加模糊:实际上每个工作树都有一个索引文件,因此 .git/index
只是 the main 工作树的索引。每个工作树都有一个辅助 the 索引——但这并不是我要表达的意思,在这里,它只是一个例子,说明假设已经存在一个索引在边缘磨损。无可否认,您使用的是 Git 1.8.3.1(确实很旧),但它也比漂亮的简单善意的“一个索引”设置更复杂。
当您使用 git commit -a
时,Git 会创建一个新的额外索引。当您使用 git commit .
时,您正在调用 git commit --only .
(see the documentation for details),并且 Git 使 two 新的额外索引(索引? ).
Git 的所有部分都能够重定向 Git 的 rest 以使用不同的非标准索引,这些不同的选项git commit
利用此功能。请注意,git commit -a
等同于 git commit --include
后跟任何需要添加的文件的名称。真正棘手的情况是您正在使用的情况,git commit --only
。
一旦开始增加索引文件,事情就会变得混乱!
请记住,索引本质上是提议的下一次提交。如果只有一个索引(对于这个工作树,如果我们谈论 Git 2.5 或更高版本),则只有一个提议的下一次提交。这并不难,我们只需要考虑每个文件有三个个副本即可。让我们选择一个文件,例如 README.md
:
HEAD:README.md
是README.md
当前提交的版本。你无法改变它。 (您可以移动HEAD
本身,但是README.md
的已提交副本在提交中,由提交的哈希 ID 找到,并且不会更改。)名称
HEAD:README.md
仅在Git 中有效。该名称访问这个冻结的、Git 化的、冻干的文件副本;这个副本永远不会改变。例如,您可以通过git show HEAD:README.md
查看它。:README.md
是索引中README.md
的副本。本来和HEAD:README.md
是一样的,但是如果你运行git add README.md
,现在可能不一样了。名称
:README.md
也只能在Git 中使用。该名称访问存储在索引中的此可替换但 Git 化(冻干格式)文件副本。您可以随时将其替换为git add
.最后,
README.md
是一个普通的(非Git化)文件。它不在 Git 中!它不在索引中!它位于您的 工作树 中,您可以在其中使用所有常规计算机工具查看和处理它。 Git 真的不使用这个文件做任何事情,它只是在你检查其他提交时覆盖它或删除它。 Git 唯一用它做的事情,除了用git status
等检查它,是让你用git add
把它 复制 回索引,覆盖之前的内容(并在此过程中冷冻干燥)。
运行 git status
运行两个git diff
:
首先将
HEAD
提交与索引进行比较,即当前提交中的内容与提议的下一次提交中的内容。任何不同 在这里被列为暂存提交。一样的东西,Git只是默默的不说。第二个
git diff
将索引与工作树进行比较,即建议提交中的内容与您可以复制到索引中的内容。任何 不同 在这里被列为 not staged for commit。再一次,任何相同的东西,Git 安静地什么都不说。(然后最后一次检查工作树中根本不在索引中的文件。Git 会抱怨这些,说它们未被跟踪, 除非你在
.gitignore
中列出它们。在.gitignore
中列出不会改变索引中是否有文件的副本,它只会改变 Git 是否抱怨。)
当你 运行 git commit
, Git 打包索引中的任何内容,并使用它来进行新提交... unless 您使用 --only
、--include
或 -a
。
指数out the wazoo
使用 git commit --only
,Git 使 三个 个索引文件:
- 一个是标准的。它一开始就没有动过。这是正常的
.git/index
. - 一个是那个的副本,
--only
个文件git add
添加到它。它在某个时候位于.git/index.lock
中。 也许它一直都在这里!如果是这样,那将提供一种方法来处理我在下面概述的案例。但是没有文档可以保证这一点。 - 第三个是新的,首先提取
HEAD
,然后git add
将--only
文件添加到其中。
如果在 之前 运行 git commit -a
,第一个和第三个索引文件匹配,因为添加 --only
个文件添加到常规索引与从 HEAD
创建一个新的临时索引并将 --only
个文件添加到它具有相同的效果。但除此之外,所有三个文件可能都不同!
Git 然后从 third 索引进行新的提交。如果新提交成功,Git 将常规索引替换为 second 索引(此替换通过 rename
系统调用发生)。否则 Git 返回正常索引。 (请注意,工作树根本没有任何变化。)
如果你使用git commit --include
或git commit -a
,Git只会产生一个额外的索引,所以你有:
.git/index
中的标准索引,以及您目前添加的任何内容;和- 临时文件中的额外索引:这开始时是标准索引的副本,但随后 Git 将列出的文件或其他修改过的文件添加到该索引。
然后 Git 开始提交过程。如果一切顺利,当 Git 完成后,Git 会重命名临时索引,使其成为标准索引。如果情况不妙,Git 删除临时索引,标准索引保持不变。同样,工作树没有任何反应。
引入预提交挂钩
Git 运行 在准备任何额外的索引文件后,这是您的预提交挂钩。特殊环境变量 $GIT_INDEX_FILE
命名 Git 将用于进行新提交的索引。所以一共有三种情况,两种还算不错,一种很糟糕:
- 您正在进行正常提交。
GIT_INDEX_FILE
命名正常索引,一切正常 - 您正在执行
git commit --include
或git commit -a
和GIT_INDEX_FILE
命名第二个索引;没有第三个索引;如果提交完成,Git 将重命名第二个索引。 - 您正在执行
git commit --only
和GIT_INDEX_FILE
命名第三个索引。 找到第二个索引并不容易,如果提交成功,第二个索引将在提交后就位!
如果您选择对存储在索引中的文件进行更改,您的工作就是将它们添加到 Git 将用于提交的索引中。为此,您可以根据需要使用 git add
,因为这会将文件从工作树复制到 $GIT_INDEX_FILE
中命名的索引。
不过,第一个问题是您不能查看工作树中的文件。它们无关紧要!它们可能包含与索引中的内容完全不同的内容。在 git commit --only
.
第二个也是更大的问题是,如果您更新了 git commit --only
使用的 third 索引,您还应该更新 second git commit --only
正在使用的索引。这部分很棘手,因为没有简单的方法可以找到它,除了假设它在 .git/index.lock
中。虽然这可能有用,但我不会在这里建议。
对此我真的没有任何建议——您发现的任何偷偷摸摸的方法都可能会失败,因为处理第三个索引(当前 2.21-ish Git 称为“错误索引”)的代码已经在 1.8 和现代 Git 之间发生了很大变化。通常的最佳实践建议是 不要 在 Git 挂钩中进行任何特殊格式化。相反,让 Git 挂钩仅检查 文件的索引副本是否 格式正确:如果是,则继续提交,如果不是,则中止提交。剩下的交给用户。
还有一个选择
我看到并使用的另一种方法是检查 $GIT_INDEX_FILE
的实际设置。如果设置为 .git/index
,则用户使用的是 git commit
,无需任何特殊设置。 this same pre-commit hook 中的另一个技巧(调用 clang-format 和 autopep8)是比较要格式化的文件的索引和工作树,如果不匹配则拒绝 运行。