理解git commit --only和pre-commit hooks

jus*_*ach 4 git jetbrains-ide githooks

我正在使用预提交钩子来重新格式化代码,一般来说,它可以工作; 它重新格式化和git add任何暂存的文件,结果提交包含所需的重新格式化的代码.

但是,它不能很好地使用git commit --only(这是JetBrains IDE使用的变体),我试图理解为什么.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")
Run Code Online (Sandbox Code Playgroud)

如果我然后提交使用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)
Run Code Online (Sandbox Code Playgroud)

新的分阶段变化和工作树的变化来自哪里?

有人可以确切地解释如何git commit --only与索引进行交互以产生上面显示的结果 - 甚至更好的是,是否有办法让我的预提交钩子很好地与它一起玩?

我的理解是git commit --only使用工作树中的文件版本,所以我尝试git add从预提交钩子中删除步骤,看看会发生什么,并导致提交的文件的格式不正确的版本和工作树中的格式正确(符合我对标准的期望git commit,但我不确定在上下文中会发生什么git commit --only).

我知道使用clean过滤器来重新格式化代码的可能性,而不是预先提交的钩子,但是这种方法引入了一些情境复杂性,如果可能的话,这将很好地避免.

注意:这个问题与修改文件的Phpstorm和预提交钩子有关,但重点是在上下文中解决问题git commit --only.此外,JetBrains似乎没有解决这个问题,正如在该问题的公认答案中所表明的那样.

tor*_*rek 5

精确的细节从Git的一个版本到另一个版本有所不同,有些人 - 我不是说JetBrains人员就在其中,因为我不知道 - 试图绕过Git做事的方式,在这个过程中,搞砸东西要么它们不能解决,要么解决方案是依赖于Git版本的.但是,这些Git钩子的主要思想是完全相同的:

  • 指数包含了提交到化妆,和
  • 工作树包含了工作树.

当你第一次运行时git commit,这两个不需要同步,并且如果你向git commit命令添加文件,使用--only或者--include,Git必须创建一个索引,这可能与常规普通索引不同.所以现在我们结束了一个环境变量GIT_INDEX_FILE,设置为一个新的临时索引的路径.1 由于所有Git命令都自动遵守环境变量,因此预提交钩子将使用临时索引的文件,git write-tree并将使用临时索引的文件.

当然,任何尊重临时索引的东西 - 或者,可能取决于--includevs --only,只是使用工作树的内容 - 将得到错误的答案.

还有一个问题,不过,即便如此程序尊重的环境变量.假设我们有一个文件让它调用它,test因为它的目的 - 最初包含"headvers",并匹配current(HEAD)提交.现在我们在工作树中修改它以包含"indexvers"并运行git add test.因此索引版本test读取"索引器".现在我们在工作树中再次修改它,包含"工作者",然后运行git commit --only test或者git commit --include test.

我们肯定知道应该进入新提交的内容:它应该是包含的测试版本workvers,因为我们特意告诉Git提交工作树版本.但是,应该在索引和工作树留给后来?这取决于我们是否使用了--includevs --only?我不知道在这里考虑什么"正确"的答案!我可以告诉你的是,当我之前尝试使用Git时,它往往包含在workvers后面(在索引和工作树中).也就是说,临时索引的版本成为普通索引的版本,并且工作树文件未受影响.

(如果你有操纵索引和/或工作树的Git钩子,你将能够撬开"将索引复制到保存索引,然后复制"与"将索引复制到temp-index,然后使用"之间的区别TEMP-指数").


1这是我在测试各种行为时的实际实现,但实际实现可能有所改变.例如,Git可以将"普通"索引保存在临时文件中,然后替换普通索引,这样GIT_INDEX_FILE不会设置.并再次,它可能取决于--includeVS --only.

注意,git commit -a也可以使用临时索引.我相信这种行为在Git 1.7和Git 2.10之间已经发生了变化,基于git status在另一个窗口中运行的结果,同时仍然在正在运行的窗口中编辑提交消息git commit -a.