【问题标题】:`git diff` does not work when run from a git pre-commit hook从 git 预提交挂钩运行时,“git diff”不起作用
【发布时间】:2017-08-16 19:34:27
【问题描述】:

我有一个 git pre-commit 钩子,它在提交之前对任何修改过的文件进行一些样式检查。

实现无关紧要,但它首先调用git diff。这是我在(repo)/.git/hooks/pre-commit 中的内容。

#!/bin/sh


echo "=== Running script..."
git diff
echo "=== Done running script..."

# Other stuf
# ....

# Always exit with 1 so pre-commit hook always fails.
# Useful for testing
exit 1

当我实际尝试提交某些内容时,pre-commit 挂钩正确触发,但 git diff 命令不输出任何内容(肯定有修改过的文件)

> git commit --all -m "foo"
=== Running script...
=== Done running script...

但是,如果我直接/手动运行 pre-commit 挂钩脚本,它确实工作

> ./.git/hooks/pre-commit
=== Running script...
(... outputs git diff ...)
=== Done running script...

git 调用钩子与我手动调用它有什么不同?无论哪种方式,它都以相同的用户身份运行(我的用户名)

我也尝试了this thread 的建议,但unset GIT_DIR--git-dir=work-tree= 没有解决任何问题。

谢谢!

【问题讨论】:

    标签: git githooks


    【解决方案1】:

    您需要使用git diff --cached,因为更改已经暂存。

    【讨论】:

    • 这确实起到了作用,但出于我自己的好奇心 - 为什么当我在没有 --cached 的情况下手动运行 git diff 时它会起作用?更改尚未上演,因为我还没有在任何东西上运行git add(这就是为什么在提交时我运行git commit --all
    • 这就是原因。 git diff 显示工作目录和索引之间的差异。 git diff --cached 显示索引(分阶段)和 HEAD 之间的差异。因此,当您手动运行而不使用暂存时,您使用git diff,而当您运行git commit -a 时,您首先进行暂存,因此您需要将索引与 HEAD 进行比较,而不是工作目录。
    【解决方案2】:

    作为ishegg said,您在这里需要git diff --cached,但这不一定是全部。

    这里有两个陷阱需要提防。第一个是git diff 所做的:它比较两个(或类似树的东西)。第二个与短语索引有关。

    git diff 的索引和选择树

    通常,您区分的两棵树是与两个特定提交相关联的树:

    git diff <hash1> <hash2>
    

    (或与&lt;hash1&gt;..&lt;hash2&gt; 相同,与大多数 Git 命令不同,它the SPECIFYING RANGES section of the gitrevisions documentation 中描述的两点.. 操作的方式处理两个哈希) .

    将两棵树中的一棵作为您的工作树也很常见,这就是您运行时发生的情况:

    git diff HEAD
    

    例如:这会将 HEAD 命名的提交(即当前分支提示提交)与当前工作树进行比较。

    使用:

    git diff --cached
    

    告诉 Git 将 HEAD 提交与您的 index 表示的树进行比较。 Git 的索引,也称为 staging areacache,是 Git 构建 next 提交的地方。这就是为什么在提交之前必须运行git addgit add 命令将文件从工作树复制到索引。

    使用:

    git diff
    

    完全没有参数选择索引作为第一棵树,工作树作为第二棵树。一旦所有内容都从工作树复制到索引中,这个特定的差异将是空的,这就是您在此处看到的。

    索引 vs 索引

    第二个陷阱在于您使用的是git commit --all

    虽然 Git 谈到 索引,实际上每个工作树都有一个特定的、可区分的索引,但 Git 实际上允许多个索引,一次只使用一个.

    当您使用 git commit --all 或将文件名传递给 git commit 时,Git 会创建一个 临时 索引,它会在预提交过程中使用它。为了表明有一个临时索引在起作用,Git 设置了环境变量GIT_INDEX_FILE。这个临时索引被使用而不是普通索引,直到提交被允许和提交,或者直到提交被拒绝。

    如果提交被拒绝,Git 只会删除临时索引,一切都会恢复到以前的状态。如果事情不是git add-ed,它们仍然不是git add-ed。

    如果提交被接受,则临时索引成为“真实”或“主”索引,或多或少。这里事情可能会变得复杂,因为您可以在真实索引中暂存一些项目,运行git commit --allgit commit --include &lt;paths&gt;git commit --only &lt;paths&gt;,如果提交成功,则初始(主要或实际)索引和临时索引之间的差异索引很重要。

    如果您打算对工作树中的文件和/或它们在索引中的副本进行特殊处理,您可能希望简单地拒绝任何使用临时索引的尝试,以避免这些并发症。但是,这将禁止使用--all

    【讨论】:

    • 谢谢,了解区别及其工作原理非常有用!感谢您的见解,将尽可能避免使用--all
    • 但是看起来 git diff --cached 不使用 GIT_INDEX_FILE 指示的临时索引。如何获得 HEAD 和 GIT_INDEX_FILE 指示的索引之间的差异?
    • @Droopycom:只要变量GIT_INDEX_FILE指向正确的路径名,git diff就应该使用它。此处的相对路径名可能存在问题,或许在这种情况下设置 GIT_INDEX_FILE=$(realpath $GIT_INDEX_FILE) 会有所帮助。
    • 我的错,我的预提交脚本中的另一个错误使它看起来像 git commit -a 没有按预期工作。但确实如此。
    猜你喜欢
    • 1970-01-01
    • 2013-12-24
    • 2021-09-10
    • 2014-01-03
    • 1970-01-01
    • 2019-12-25
    • 1970-01-01
    • 1970-01-01
    • 2018-09-29
    相关资源
    最近更新 更多