我正在处理一个预提交挂钩来重新格式化代码,一般来说,它是有效的;它重新格式化并git adds任何暂存文件,结果提交包含所需的重新格式化代码。
但是,它不能很好地与git commit --only(jetbrains ides使用的变体)一起使用,我试图理解为什么。git commit --only和预提交挂钩的组合会导致不需要的索引/工作树状态,如以下事件序列所述:
如果我对一个文件做了一个小的更改,但出现格式错误,然后运行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提交,则会运行预提交挂钩,并提交已更改和重新格式化的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如何与索引交互以产生上面所示的结果吗-甚至更好,是否有办法让我的pre-commit钩子很好地处理它?
我的理解是git commit --only与工作树中的文件版本一起工作,因此我尝试从预提交挂钩中删除git add步骤,以查看会发生什么,它导致提交的文件的格式不正确,并且在工作树中提交的文件的格式正确(这与我对标准git commit的期望相符,但我不确定在git commit --only上下文中会发生什么)。
我知道使用clean过滤器来重新格式化代码的可能性,而不是使用预提交挂钩,但是这种方法引入了一些情况上的复杂性,如果可能的话,最好避免这种情况。
注意:这个问题与Phpstorm and pre commit hooks that modify files有关,但重点是在git commit --only上下文中解决这个问题。此外,这一问题似乎并没有得到喷气式飞机的解决,正如对这一问题的公认答案所建议的那样。

最佳答案

确切的细节因git的不同版本而不同,有些人——我不是说jetbrains的人就在其中,因为我不知道git是怎么做的,在这个过程中,把事情搞砸了,要么他们无法解决,要么解决的问题取决于git的版本。但是,这些git钩子的主要思想是相同的:
索引包含要进行的提交,并且
工作树包含工作树。
第一次运行git commit时,这两个索引不需要同步,如果使用git commit--only--include命令添加文件,git必须创建一个新索引,该索引可能不同于常规的普通索引。因此,现在我们得到一个环境变量GIT_INDEX_FILE,设置为一个新的临时索引的路径。1因为所有git命令都自动尊重环境变量,预提交挂钩将使用临时索引的文件,而git write-tree将使用临时索引的文件。
当然,任何不尊重临时索引的东西,或者,根据--includevs--only,仅仅使用工作树的内容都会得到错误的答案。
尽管如此,仍然存在一个问题,即使程序确实尊重环境变量。假设我们有一个文件,我们称它为test,因为它的目的是最初包含“headvers”,并匹配当前(HEAD)提交。现在我们在工作树中修改它以包含“indexvers”并运行git add test。因此,test的索引版本读作“indexvers”。现在我们在工作树中再次修改它,以包含“workvers”,并运行git commit --only testgit commit --include test
我们肯定地知道新提交中应该包含什么:它应该是包含workvers的测试版本,因为我们特别告诉git提交工作树版本。但是之后索引和工作树中应该留下什么呢?这是否取决于我们是否使用了--include--only?我不知道该怎么考虑这个“正确”的答案!我所能告诉你的是,当我以前使用git进行实验时,它往往在之后(在索引和工作树中)包含workvers。也就是说,临时索引的版本变成了普通索引的版本,并且工作树文件未被修改。
(如果您有操作索引和/或工作树的git钩子,则可以撬开“将索引复制到保存的索引,然后复制回”与“将索引复制到临时索引,然后使用临时索引”之间的区别。)
1这是一次真正的实现,当时我正在测试各种行为,但有可能实际的实现发生了一些变化。例如,git可以将“normal”索引保存在临时文件中,然后替换normal索引,这样就不会设置GIT_INDEX_FILE。同样,它可能依赖于--includevs--only
注意git commit -a也可以使用临时索引,或者不使用。我相信这种行为在git 1.7和git 2.10之间发生了变化,这是基于在另一个窗口中运行git status的结果,同时仍然在运行git commit -a的窗口中编辑提交消息。

08-27 19:32
查看更多