了解 git commit --only 和 pre-commit 钩子
Understanding git commit --only and pre-commit hooks
我正在研究重新格式化代码的预提交挂钩,总的来说,它可以工作;它重新格式化和 git add
s 任何暂存文件,并且生成的提交包含所需的重新格式化的代码。
但是,它不能很好地与 git commit --only
(JetBrains IDE 使用的变体)一起使用,我正在尝试理解原因。 git commit --only
和预提交挂钩的组合导致不希望的 index/working 树状态,如以下事件序列所述:
如果我对文件进行了格式错误的小改动,然后 运行 git status
,这就是我所看到的:
On branch master
Your branch is up-to-date with 'origin/master'.
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: file.php
no changes added to commit (use "git add" and/or "git commit -a")
如果我随后使用 git commit --only -- file.php
、预提交挂钩 运行s 提交,并提交更改和重新格式化的 file.php
。
但是,如果我再 运行 git status
,结果是这样的(我的箭头注释):
On branch master
Your branch is ahead of 'origin/master' by 1 commit.
(use "git push" to publish your local commits)
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: file.php <-- contains original change, improperly formatted
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: file.php <-- contains original change, properly formatted (per the most recent commit)
新的阶段性变化和工作树的变化来自哪里?
有人可以准确解释 git commit --only
如何与索引交互以产生上面显示的结果 — 更好的是,是否有办法让我的预提交挂钩与它很好地配合?
我的理解是 git commit --only
与工作树中的文件版本一起工作,所以我尝试从预提交挂钩中删除 git add
步骤以查看会发生什么,并且它导致提交格式不正确的文件版本和工作树中格式正确的版本(这符合我对标准 git commit
的期望,但我不确定在上下文中会发生什么git commit --only
)。
我知道可以使用 clean
过滤器来重新格式化代码,而不是使用预提交挂钩,但是这种方法引入了一些情况复杂性,这对尽可能避免。
注意: 这个问题与 Phpstorm and pre commit hooks that modify files 有关,但重点是在 git commit --only
的上下文中解决问题。此外,JetBrains 似乎并未解决该问题,正如该问题的已接受答案中所建议的那样。
确切的细节因 Git 的一个版本而异,有些人——我不是说 JetBrains 的人也在其中,因为我不知道——试图绕过 Git =86=] 做事并在此过程中把事情搞砸,以至于它们无法解决,或者解决方法取决于 Git-version-dependent。然而,这些 Git 钩子的主要思想都是一样的:
- index 包含 commit-to-make,并且
- 工作树包含工作树。
当您第一次 运行 git commit
时,这两个不需要同步,如果您将文件添加到 git commit
命令,使用 --only
或 --include
,Git然后必须做一个new索引,可能和常规的普通索引不同。所以现在我们结束了一个环境变量,GIT_INDEX_FILE
,设置为一个新的临时索引的路径。1 因为所有 Git 命令自动尊重环境变量,预提交挂钩将使用临时索引的文件,git write-tree
将使用临时索引的文件。
当然,任何未能尊重临时索引的东西——或者,可能,取决于--include
vs --only
,只是使用的内容工作树——会得到错误的答案。
尽管如此,即使是 尊重环境变量的程序,仍然存在一个问题。假设我们有一个文件——我们称它为 test
,因为这是它的目的——最初包含 "headvers",并匹配当前 (HEAD
) 提交。现在我们在工作树中修改它以包含 "indexvers" 和 运行 git add test
。 test
的索引版本因此显示为 "indexvers"。现在我们在工作树中再次 修改它,以包含 "workvers" 和 运行 git commit --only test
或 git commit --include test
。
我们肯定知道新提交中应该包含什么:它应该是包含 workvers
的测试版本,因为我们特别告诉 Git 提交工作树版本。但是索引和工作树 之后 应该留下什么?这是否取决于我们使用的是 --include
还是 --only
?我不知道这里的 "right" 答案应该考虑什么!我只能告诉你的是,当我之前尝试使用 Git 时,它往往会在之后包含 workvers
(在索引和工作树中)。也就是说,临时索引的版本变成了普通索引的版本,工作树文件没有受到影响。
(如果你有 Git 操纵索引 and/or 工作树的挂钩,你将能够撬开 "copying index to saved-index, then copying back" 与 "copying index to temp-index, then using temp-index" 之间的区别。 )
1这是我一次测试各种行为时的实际实现,但实际实现可能会发生一些变化。例如,Git 可以将 "normal" 索引保存在一个临时文件中,然后替换普通索引,这样 GIT_INDEX_FILE
就 而不是 设置了.而且,同样,它可能取决于 --include
与 --only
.
注意 git commit -a
也可能使用临时索引,也可能不使用。我相信这种行为在 Git 1.7 和 Git 2.10 之间发生了变化,基于 运行ning git status
在另一个 window 中的结果,同时仍在编辑提交消息window 运行宁 git commit -a
.
我遇到了同样的问题。这是我从 Jetbrains 开发人员 Dmitriy Smirnov 那里得到的解决方案。
使用 git commit --only
有几个原因:
不支持 - Git 阶段 - https://youtrack.jetbrains.com/issue/IDEA-63391
- 它允许进行部分提交——提交单个文件。这对于支持 IDE.
中的更改列表至关重要
目前无法更改行为。
鉴于预提交挂钩如下 (ruby):
`git status --porcelain`.lines do |line|
changed_file = line.split(' ', 2)[1].strip()
if (File.extname(changed_file).downcase() == '.java')
system "java -jar bin/google-java-format-1.4-all-deps.jar --aosp --replace #{changed_file}"
system "git add #{changed_file}"
end
end
添加一个post-commit
挂钩:
git update-index -g
见
https://youtrack.jetbrains.com/issue/IDEA-81139#comment=27-295117
我正在研究重新格式化代码的预提交挂钩,总的来说,它可以工作;它重新格式化和 git add
s 任何暂存文件,并且生成的提交包含所需的重新格式化的代码。
但是,它不能很好地与 git commit --only
(JetBrains IDE 使用的变体)一起使用,我正在尝试理解原因。 git commit --only
和预提交挂钩的组合导致不希望的 index/working 树状态,如以下事件序列所述:
如果我对文件进行了格式错误的小改动,然后 运行 git status
,这就是我所看到的:
On branch master
Your branch is up-to-date with 'origin/master'.
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: file.php
no changes added to commit (use "git add" and/or "git commit -a")
如果我随后使用 git commit --only -- file.php
、预提交挂钩 运行s 提交,并提交更改和重新格式化的 file.php
。
但是,如果我再 运行 git status
,结果是这样的(我的箭头注释):
On branch master
Your branch is ahead of 'origin/master' by 1 commit.
(use "git push" to publish your local commits)
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: file.php <-- contains original change, improperly formatted
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: file.php <-- contains original change, properly formatted (per the most recent commit)
新的阶段性变化和工作树的变化来自哪里?
有人可以准确解释 git commit --only
如何与索引交互以产生上面显示的结果 — 更好的是,是否有办法让我的预提交挂钩与它很好地配合?
我的理解是 git commit --only
与工作树中的文件版本一起工作,所以我尝试从预提交挂钩中删除 git add
步骤以查看会发生什么,并且它导致提交格式不正确的文件版本和工作树中格式正确的版本(这符合我对标准 git commit
的期望,但我不确定在上下文中会发生什么git commit --only
)。
我知道可以使用 clean
过滤器来重新格式化代码,而不是使用预提交挂钩,但是这种方法引入了一些情况复杂性,这对尽可能避免。
注意: 这个问题与 Phpstorm and pre commit hooks that modify files 有关,但重点是在 git commit --only
的上下文中解决问题。此外,JetBrains 似乎并未解决该问题,正如该问题的已接受答案中所建议的那样。
确切的细节因 Git 的一个版本而异,有些人——我不是说 JetBrains 的人也在其中,因为我不知道——试图绕过 Git =86=] 做事并在此过程中把事情搞砸,以至于它们无法解决,或者解决方法取决于 Git-version-dependent。然而,这些 Git 钩子的主要思想都是一样的:
- index 包含 commit-to-make,并且
- 工作树包含工作树。
当您第一次 运行 git commit
时,这两个不需要同步,如果您将文件添加到 git commit
命令,使用 --only
或 --include
,Git然后必须做一个new索引,可能和常规的普通索引不同。所以现在我们结束了一个环境变量,GIT_INDEX_FILE
,设置为一个新的临时索引的路径。1 因为所有 Git 命令自动尊重环境变量,预提交挂钩将使用临时索引的文件,git write-tree
将使用临时索引的文件。
当然,任何未能尊重临时索引的东西——或者,可能,取决于--include
vs --only
,只是使用的内容工作树——会得到错误的答案。
尽管如此,即使是 尊重环境变量的程序,仍然存在一个问题。假设我们有一个文件——我们称它为 test
,因为这是它的目的——最初包含 "headvers",并匹配当前 (HEAD
) 提交。现在我们在工作树中修改它以包含 "indexvers" 和 运行 git add test
。 test
的索引版本因此显示为 "indexvers"。现在我们在工作树中再次 修改它,以包含 "workvers" 和 运行 git commit --only test
或 git commit --include test
。
我们肯定知道新提交中应该包含什么:它应该是包含 workvers
的测试版本,因为我们特别告诉 Git 提交工作树版本。但是索引和工作树 之后 应该留下什么?这是否取决于我们使用的是 --include
还是 --only
?我不知道这里的 "right" 答案应该考虑什么!我只能告诉你的是,当我之前尝试使用 Git 时,它往往会在之后包含 workvers
(在索引和工作树中)。也就是说,临时索引的版本变成了普通索引的版本,工作树文件没有受到影响。
(如果你有 Git 操纵索引 and/or 工作树的挂钩,你将能够撬开 "copying index to saved-index, then copying back" 与 "copying index to temp-index, then using temp-index" 之间的区别。 )
1这是我一次测试各种行为时的实际实现,但实际实现可能会发生一些变化。例如,Git 可以将 "normal" 索引保存在一个临时文件中,然后替换普通索引,这样 GIT_INDEX_FILE
就 而不是 设置了.而且,同样,它可能取决于 --include
与 --only
.
注意 git commit -a
也可能使用临时索引,也可能不使用。我相信这种行为在 Git 1.7 和 Git 2.10 之间发生了变化,基于 运行ning git status
在另一个 window 中的结果,同时仍在编辑提交消息window 运行宁 git commit -a
.
我遇到了同样的问题。这是我从 Jetbrains 开发人员 Dmitriy Smirnov 那里得到的解决方案。
使用
git commit --only
有几个原因:
-
不支持
- Git 阶段 - https://youtrack.jetbrains.com/issue/IDEA-63391
- 它允许进行部分提交——提交单个文件。这对于支持 IDE. 中的更改列表至关重要
目前无法更改行为。
鉴于预提交挂钩如下 (ruby):
`git status --porcelain`.lines do |line|
changed_file = line.split(' ', 2)[1].strip()
if (File.extname(changed_file).downcase() == '.java')
system "java -jar bin/google-java-format-1.4-all-deps.jar --aosp --replace #{changed_file}"
system "git add #{changed_file}"
end
end
添加一个post-commit
挂钩:
git update-index -g
见
https://youtrack.jetbrains.com/issue/IDEA-81139#comment=27-295117