繁体   English   中英

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

[英]Understanding git commit --only and pre-commit hooks

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

但是,它与git commit --only (这是JetBrains IDE使用的变体)不能很好地兼容,我试图理解为什么。 git commit --only和pre-commit钩子的组合会导致不合需要的索引/工作树状态,如以下事件序列中所述:

如果我对文件进行了一次格式化错误的小改动,然后运行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如何git commit --only与索引交互以产生上面显示的结果 - 甚至更好,是否有办法让我的预提交钩子很好地与它一起玩?

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

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

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

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

  • 索引包含commit-to-make和
  • 工作树包含工作树。

这两个不一定是同步的,当你第一次运行git commit ,如果你将文件添加到git commit命令,无论使用哪种--only--include ,然后混帐必须做出新的索引,可以从常规的普通差异指数。 所以现在我们最终得到一个环境变量GIT_INDEX_FILE ,设置为一个新的临时索引的路径。 1由于所有Git命令都自动遵守环境变量,因此预提交钩子将使用临时索引的文件,而git write-tree将使用临时索引的文件。

当然,任何尊重临时索引或潜在的,这取决于--include VS --only ,只是使用的内容,工作树会得到错误的答案。

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

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

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


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

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

我遇到了同样的问题。 这是我从Jetbrains dev Dmitriy Smirnov那里得到的解决方案。


由于以下几个原因,使用git commit --only

  1. 不支持Git阶段 - https://youtrack.jetbrains.com/issue/IDEA-63391
  2. 它允许进行部分提交 - 提交单个文件。 这对于支持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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM