如何在Git pre-commit钩子中将文件添加到索引?

18

我已经查找了重复内容,虽然有些标题相似,但我没有发现有人遇到和我一样的问题,以下是我的问题。

我编写了一个脚本在 pre-commit 上运行,并使用 git status --porcelain 的输出来编译项目中发生改变的任何 LESS 文件。这部分可以正常工作。但我希望将 .css 文件包含在当前提交中。因此,除了运行编译器外,我的脚本还运行 git add <filename>。这就是问题所在。

该文件确实添加到了索引中,但它不是当前提交的索引。因此,如果我修改了 style.less,并运行 git commit -a(或手动执行 git add style.less),编译器应该会生成 style.cssstyle.min.css 并将它们添加到当前提交。但我注意到的行为是只有 style.less 被提交了,尽管两个 .css 文件添加到了索引以供下一次提交。

因此,我的问题是:有没有办法在 pre-commit 钩子中将文件添加到提交中,以便它们对那个提交生效?请注意,在运行 pre-commit 钩子之前,这两个 .css 文件没有被修改,因此我不能在这之前添加它们。我也知道可以使用非零状态退出钩子,以便取消提交但添加文件,但我希望避免这种情况。有更好的想法吗?

4个回答

14

我无法复现你的问题。我的初步猜测是你的 pre-commit 钩子取消了 GIT_INDEX_FILE 环境变量的设置。然而,当我尝试从 pre-commit 中取消设置 GIT_INDEX_FILE 时,我遇到了一个不同的问题(Git 抱怨说 .git/index 被锁定)。

以下是一个示例脚本,展示 Git 的功能与你的期望相符,因此肯定有其他问题。该脚本初始化了一个新的测试仓库,创建了一个模拟你的钩子的 pre-commit 钩子,并进行了几个测试提交:

#!/bin/sh

# initialize the test repository
rm -rf testrepo
git init testrepo
cd testrepo

# create the pre-commit hook
cat <<\EOF >.git/hooks/pre-commit
#!/bin/sh
git status --porcelain | while IFS= read -r line; do
    # todo: handle renames and deletions of a *.less file
    f=${line#???}
    case ${f} in
        *.less)
            fb=${f%.less}
            echo bar >>"${fb}".css
            echo baz >>"${fb}".min.css
            git add "${fb}".css "${fb}".min.css
            ;;
    esac
done
EOF
chmod +x .git/hooks/pre-commit

# create foo.less, commit it
echo foo >foo.less
git add foo.less
git commit -m "add foo.less"

# modify foo.less, commit it
echo foo2 >>foo.less
git commit -a -m "modify foo.less"

如果你在测试仓库中运行 git log -p 并查看生成的提交记录,你会发现每当修改了 foo.lessfoo.cssfoo.min.css 也会被修改。

这就是我认为更改/取消设置 GIT_INDEX_FILE 环境变量导致问题的原因:

运行 git commit -a 时,Git 会创建一个临时的索引文件来代替默认的 .git/index 来创建提交。为了使像 git add 这样的操作能够在 pre-commit 钩子内部工作,Git 在运行 pre-commit 之前将 GIT_INDEX_FILE 环境变量设置为所创建的临时索引的名称。如果你的钩子取消设置 GIT_INDEX_FILE 或将其设置为 .git/index,那么你的钩子内的所有 Git 操作都将尝试修改原始索引,而不是用于生成提交的临时索引。

然而,临时索引文件还充当了对原始索引文件的锁定。如果钩子尝试修改原始索引,并且临时索引存在,则 Git 将中止并报错。


我运行了你的脚本,它按预期工作,与我的真实存储库实际行为相反。稍后我会更深入地研究这个问题,如果我找到了解决方法,我会再联系你。谢谢! - Jimmy Sawczuk
1
因为你的努力,我给了你奖赏,尽管我仍然无法弄清楚发生了什么。再次感谢! - Jimmy Sawczuk

0

你可以利用修改最后的提交记录来进行操作,这只是一个想法。


我确实考虑过这个选项,但我希望避免它。它需要运行一个提交,运行钩子,然后让原始提交完成,然后再运行第二个提交(除非我漏掉了什么),这似乎手动操作很复杂,自动使用钩子操作也有点荒谬。 - Jimmy Sawczuk
虽然当被挂钩自动运行时,它会无缺地完成工作。 - Xobb

0

我想到了一种方法,可以将git属性“clean”设置为一个脚本,用于更新生成的文件。通常该钩子可以运行一个脚本来整理源代码,但我认为它可以用于“清理”生成的输出。
当你执行'git add'时,就会触发该属性,你可以让脚本在生成的文件上执行另一个'git add'。


你能详细说明一下吗?我在谷歌上搜索了一些关于Git属性的信息,但没有找到如何将脚本附加到它们上面或者add属性可能是如何布局的。 - Jimmy Sawczuk

-5

首先,你为什么要这样做呢?你试图将生成的文件包含到版本控制中,这通常不是一个好主意。如果你需要在检出时有style.min.css,为什么不能在构建步骤之后生成它呢?


因为CSS文件不是二进制文件,它们只是静态文本文件。生成的文件不会放入版本控制中,因为它们通常会导致合并冲突,而你没有一个很好的解决方法(二进制文件),但对于CSS文件,你很少遇到这个问题。我不想在我们的Web服务器上安装LESS编译器来进行合并/更新后的操作。然而,尽管不理想,所有这些都证明是实现我想要做的事情的更容易的方法。 - Jimmy Sawczuk
2
为什么这个被接受为答案?它根本没有解决问题。 - frhd
1
我猜他接受了我的“你做错了”的非答案,因为在这种情况下它解决了他的问题(可以说没有回答他的问题)。如果他一开始就检查LESS文件,那么“如何将它们混合到提交中”这个问题就不存在了。 - plaugg

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接