2016-12-30 126 views
4

我正在开发一个预先提交的钩子来重新格式化代码,并且通常它可以工作;它会重新格式化任何分阶段文件,并且结果提交包含所需的重新格式化代码。了解git commit - 只有和预先提交的钩子

但是,它不能很好地与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如何与索引交互以产生上面显示的结果 - 甚至更好,是否有办法让我的pre-commit钩子与它很好地搭配?

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

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

说明:此问题与Phpstorm and pre commit hooks that modify files有关,但重点在于解决git commit --only上下文中的问题。此外,JetBrains似乎也没有解决这个问题,正如在该问题的公认答案中所表明的那样。

+0

关于更多关于预提交对git commit行为的影响:https://stackoverflow.com/a/7230886/6309 – VonC

回答

5

从一个版本的Git到另一个版本的确切细节有所不同,有些人 - 我不是说JetBrains人员是其中的一员,因为我不知道 - 试图绕过Git做的事情,并且在过程,把事情弄糟,以至于无法解决问题,或者解决方法是依赖于Git版本。然而,在这些混帐挂钩的主要思路都是一样的:

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

这两个不一定是同步的,当你第一次运行git commit,如果您将文件添加到git commit命令,无论使用哪种--only--include,Git会必须再作指数,它可以从不同常规普通指数。所以现在我们结束一个环境变量,GIT_INDEX_FILE,设置为一个新的临时索引的路径。 因为所有Git命令都会自动遵守环境变量,所以预提交钩子将使用临时索引的文件,并且git write-tree将使用临时索引的文件。

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

即使有尊重环境变量的程序,仍然存在问题。假设我们有一个文件 - 我们称它为test,因为这是它的目的 - 最初包含“headders”,并且匹配当前(HEAD)提交。现在我们在工作树中修改它以包含“索引器”并运行git add testtest的索引版本因此读取“索引器”。现在我们在工作树中再次修改它,以包含“工作者”,并运行git commit --only testgit commit --include test

我们肯定知道应该进入新提交的内容:它应该是包含workvers的测试版本,因为我们特别告诉Git提交工作树版本。但是索引和工作树之后应该剩下什么?这是否取决于我们是否使用--include vs --only?我不知道该怎么考虑这里的“正确”答案!我可以告诉你的是,当我之前使用Git进行实验时,之后往往会包含workvers(无论是在索引中还是在工作树中)。也就是说,临时索引的版本成为普通索引的版本,并且工作树文件未被触及。 (如果你有操纵索引和/或工作树的Git钩子,你将能够撬开“将索引复制到保存索引,然后复制回来”和“将索引复制到temp-指数,然后用温度指数”。)


这是实际执行一次,当我在测试各种行为,但它可能是实际执行已经改变了一点。例如,Git可以将“正常”索引保存在一个临时文件中,然后替换正常索引,这样GIT_INDEX_FILE就是而不是。再次,它可能取决于--include--only

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

3

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


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

  1. Git的阶段,不支持 - https://youtrack.jetbrains.com/issue/IDEA-63391
  2. 它允许进行部分提交 - 提交单个文件。这对于支持IDE中的更改列表至关重要。

目前无法改变行为。

鉴于如下(红宝石)的pre-commit钩子:

`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