理解git提交——仅提交和预提交挂钩

理解git提交——仅提交和预提交挂钩,git,githooks,jetbrains-ide,Git,Githooks,Jetbrains Ide,我正在开发一个预提交钩子来重新格式化代码,一般来说,它是有效的;它重新格式化并添加任何暂存文件,结果提交包含所需的重新格式化代码 然而,它不能很好地与git commit配合使用——只有(JetBrains IDEs使用的变体),我正在试图理解原因。git commit--only和pre-commit钩子的组合会导致不需要的索引/工作树状态,如以下事件序列所述: 如果我对一个文件做了一个格式错误的小更改,然后运行git status,我会看到: On branch master Your br

我正在开发一个预提交钩子来重新格式化代码,一般来说,它是有效的;它重新格式化并添加任何暂存文件,结果提交包含所需的重新格式化代码

然而,它不能很好地与git commit配合使用——只有(JetBrains IDEs使用的变体),我正在试图理解原因。
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是如何进行的吗?只有与索引交互才能产生上面显示的结果,甚至更好的是,是否有一种方法可以让我的预提交钩子很好地处理它

我的理解是,
git commit——只有
可以处理工作树中的文件版本,所以我尝试从pre-commit钩子中删除
git add
步骤,看看会发生什么,结果是提交的文件的格式不正确,而工作树中的文件格式正确(这与我对标准的
git提交的期望相符,但我不确定在
git提交的上下文中应该期望什么——只有

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


注意:这个问题与git commit相关,但重点是在git commit的上下文中解决这个问题——只有
。此外,JetBrains似乎没有解决这个问题,正如在对该问题的公认答案中所建议的那样。

git的各个版本的确切细节各不相同,因此me people——我不是说JetBrains的人也在其中,因为我不知道他们试图绕过Git的做事方式,在这个过程中,他们把事情搞砸了,要么他们无法解决,要么解决方案依赖于Git版本。然而,这些Git挂钩的主要思想是一样的:

  • 索引包含要进行的提交,以及
  • 工作树包含工作树
当您第一次运行
git commit
时,这两个文件不需要同步,如果您使用
--only
--include
将文件添加到
git commit
命令中,那么git必须创建一个新的索引,它可能不同于常规的普通索引。因此,现在我们使用一个环境变量
git\u index\u FILE
,设置为新的临时索引的路径。1由于所有Git命令都自动考虑环境变量,预提交挂钩将使用临时索引的文件,
Git write tree
将使用临时索引的文件

当然,任何不遵守临时索引的内容,或者根据
--include
--only
的不同,仅仅使用工作树的内容,都会得到错误的答案

尽管如此,即使程序尊重环境变量,仍然存在一个问题。假设我们有一个文件,我们称它为
test
,因为它最初包含“headvers”,并与当前(
HEAD
)提交相匹配。现在我们在工作树中将其修改为包含“indexvers”然后运行
git add test
test
的索引版本显示为“indexvers”。现在我们在工作树中再次修改它,以包含“workvers”,然后运行
git commit--only test
git commit--include test

我们确实知道新提交中应该包含什么:它应该是包含
workvers
的测试版本,因为我们特别告诉Git提交工作树版本。但是之后在索引和工作树中应该保留什么?这取决于我们是否使用
--include
而不是
--only
?我不知道在这里,我想告诉你的是,当我尝试Git之前,它倾向于包含<代码>工作人员< /代码>(索引和工作树中)。也就是说,临时索引的版本变成了正常索引的版本,并且工作树文件未被触动。 (如果您有操作索引和/或工作树的Git钩子,您将能够撬开“将索引复制到已保存的索引,然后复制回”与“将索引复制到临时索引,然后使用临时索引”之间的区别。)


1当我测试各种行为时,这是一次实际的实现,但实际的实现可能有点变化。例如,Git可以保存“正常”在临时文件中建立索引,然后替换普通索引,这样就不会设置
GIT\u index\u文件
。而且,它可能取决于
--包含
--仅


请注意,
git commit-a
也可能使用临时索引,也可能不使用。我相信,根据在另一个窗口中运行
git status
而在运行
git commit-a
的窗口中编辑提交消息的结果,这种行为在git 1.7和git 2.10之间发生了变化问题。这是我从喷气式飞机开发者德米特里·斯米尔诺夫那里得到的解决方案


git commit——由于以下几个原因,仅使用了

  • 不支持Git阶段-
  • 它允许执行部分提交-提交单个文件。这对于在IDE中支持更改列表至关重要
  • 目前不可能改变这种行为。
    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 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
    
    git update-index -g