【问题标题】:Can a Git hook automatically add files to the commit?Git 钩子可以自动将文件添加到提交中吗?
【发布时间】:2011-03-18 02:11:37
【问题描述】:

我想使用 Git 中的 pre-commit 或 post-commit 挂钩将自动生成的文件添加到同一个提交中,具体取决于在该提交中修改的文件。我该怎么办?

我已尝试将此作为预提交挂钩,但没有运气:

#!/bin/sh
files=`git diff --cached --name-status`
re="<files of importance>"
if [[ $files =~ $re ]]
then
  echo "Creating files"
  exec bundle exec create_my_files
  exec git add my_files
  exec git commit --amend -C HEAD
fi

这成功地将它们添加到存储库中,但不会将它们添加到提交中。我还尝试在提交后挂钩中使用最后两个 exec 行以及提交前检查,但也没有好处。

【问题讨论】:

  • 您需要从此代码中删除execs(请参阅man sh)。在exec 之后执行任何shell 命令都是不可能的,因为exec 破坏了当前进程,即用于解释命令的shell。

标签: git pre-commit-hook githooks pre-commit


【解决方案1】:

你可以使用update-index:

git update-index --add my_files

【讨论】:

  • 我用信息更新了问题。没有错误。文件已创建。 Git 状态显示它们已添加但未提交。
  • update-index 的另一个选项也可能是您所需要的:ftp.sunet.se/pub/Linux/kernel.org/software/scm/git/docs/…
  • 我肯定会将其视为一种选择。谢谢!
  • 我不知道@IanTerrell,但我仍然坚持这一点。我尝试了git add 和git update-index --add。在这两种情况下,文件都会添加到存储库中(因此它们会在 next 提交中),但不会添加到当前提交中。
  • rfunduk 的响应在 git 2.6.2 上非常适合我,即 git update-index --add my_files
【解决方案2】:

如果文件是自动生成的,并且它们可以在任何地方生成(暗示您希望在 Git 预提交挂钩中构建它们),那么您首先不应该将它们置于源代码控制之下。您应该只控制源文件——生成的文件应该作为构建脚本的一部分生成。

将生成的文件置于源代码控制之下的唯一原因是它需要独特/特权资源来生成(例如许可程序)或需要大量时间来生成。

已添加

来自http://git-scm.com/docs/githooks:

pre-commit 这个钩子由 git 调用 提交,并且可以绕过 --no-verify 选项。它不带参数,并且在之前被调用 获取建议的提交日志 消息并提交。退出 此脚本中的非零状态 导致 git 提交中止。

默认的预提交钩子,当 启用,捕获行的介绍 带有尾随空格和中止 找到这样一行时的提交。

所有的 git commit 钩子都被调用 与环境变量 GIT_EDITOR=: 如果命令不会 调出编辑器来修改 提交消息。

预提交挂钩的目的是在提交之前对工作区的状态和提交的内容进行通过失败检查。尝试更改提交内容是行不通的。

我的建议是在您的构建脚本中添加两个步骤:(1) 一个步骤将构建所有需要生成的过时文件(并将它们添加到工作区),以及 (2)一个步骤,将检查以确保所有生成的文件都是最新的,并返回非零状态代码。你的 Git 预提交钩子应该运行第二步。您的开发人员应接受培训,以便在必要时运行第一步。

【讨论】:

  • 正确,但不回答问题。他可能有很好的理由将生成的文件置于源代码控制之下,这不是我们可以决定的:)
  • 它们无法在任何地方生成:它们被从源代码控制部署到只读文件系统。
  • 你有它! :) 您可能会尝试将生成步骤放入部署脚本中,但这也可能不切实际。
  • 部署是通过 git push 自动进行的(它是 Heroku 上的 Rails 应用程序),因此将其放在那里并不是很实际。预提交确实是它的地方,因为我可以测试以查看是否有任何依赖文件已更改,并且仅在生成的文件发生更改时重建它们。
  • @Ian,听起来这真的是你的构建脚本的东西,在你提交之前,而不是尝试使用 Git 来自动生成。如果要在预提交步骤中添加任何内容,则应在提交前检查以确保文件是最新的(如果文件不同步,则提交失败)。
【解决方案3】:

不如编写一个post-commit 脚本来生成您的文件,然后让 执行(类似于)git add my_files; git commit --amend。

【讨论】:

  • git-scm.com/docs/githooks post-commit 在提交完成后运行——此时你不能修改它 afaics
  • git commit --amend 更改了之前的提交(正如你所说的刚刚发生)
【解决方案4】:

使用预提交钩子可以做你想做的事。我们为heroku 部署做了类似的事情(将coffeescript 编译成javascript)。你的脚本不工作的原因是你不正确地使用了exec 命令。

来自man page:

exec 内置命令用于用新命令替换当前运行的 shell 进程映像。成功完成后, exec 永远不会返回。 exec 不能在管道内使用。

只有您的第一个 exec 命令正在运行。之后,您的脚本基本上就被终止了。

试试这样的东西(作为预提交挂钩):

#!/bin/sh
files=`git diff --cached --name-status`
re="<files of importance>"
if [[ $files =~ $re ]]
then
  echo "Creating files"
  bundle exec create_my_files
  git add my_files
fi

【讨论】:

  • 这在最近的 git 中不起作用(我使用 1.7.9), git add in pre-commit 添加文件以供将来提交,而不是您刚刚使用的文件。
  • 在 git 1.7.10 中为我工作。这些文件不会添加到提交消息中,但会被提交。似乎提交消息中的“git status”是在预提交之前生成的。这对我来说似乎是一个错误,但我怀疑它是出于某种原因故意完成的。你可以做的是在预提交的末尾添加一行,看起来像 'git status; echo -n 按回车键继续...;读'。可能有更好的方法来解决这个问题,但这对我来说是一个快速的解决方案。
  • 我忘了提,我有 git 版本 1.7.7。根据之前的 cmets,此功能仅 1.7.10+ 版本可用。
  • 我有 git 1.8.3.4 并且在预提交挂钩中添加的任何内容似乎都不会在下一次提交之前暂存。
  • 实际上,我可以在我现在使用的 git 2.7.4 中确认这个 works。在编辑提交消息时它不会出现,但是当您实际提交时,您会看到您在预提交挂钩期间添加的文件出现在提交中。
【解决方案5】:

我也有同样的需求,这种方法对我来说效果很好:

#!/bin/sh
files='git diff --cached --name-only'
re="<files of importance>"
if [[ $files =~ $re ]]
then
   echo "Creating files"
   create_my_files && git add my_files
fi

其中“create_my_files”应该是可执行的,例如,如果它是一个 python 文件,您可以将其执行为“python create_my_files && git add my_files”

确实,您不需要预先提交即可再次提交(这将创建一个无限的讨厌循环:p)

【讨论】:

  • git add 中的 pre-commit 钩子在 Git 2.6.4 中无法正常工作
【解决方案6】:

您可以结合使用前提交和后提交脚本。

在预提交中:

  • 触摸一个文件 .commit 什么的。 (请务必将此添加到 .gitignore)

在提交后:

如果 .commit 存在,您就知道提交刚刚发生,但后提交尚未运行。因此,您可以在这里进行代码生成。此外,测试 .commit 是否存在:

  • 添加文件
  • commit --amend -C HEAD --no-verify(避免循环)
  • 删除 .commit 文件

这大致是我用来在从 Metastore 生成的存储库中存储 .metadata 文件的过程。

如果有人知道更好的方法,我会全力以赴,但它现在似乎有效。

【讨论】:

  • 应该是--amend 而不是--ammend。
【解决方案7】:

由于 git add 在预提交中也不适合我,我遵循了马克的想法,使用 .commit 文件并将过程分为提交前和提交后。

这里有一些代码应该很容易理解

在预提交中:

  • 触摸一个文件 .commit 什么的。 (请务必将此添加到 .gitignore)
#!/bin/sh 
echo 
touch .commit 
exit

在提交后:

如果 .commit 存在,您知道提交刚刚发生,但是 提交后尚未运行。因此,您可以在这里进行代码生成。 此外,测试 .commit 是否存在:

  • 添加文件
  • commit --amend -C HEAD --no-verify(避免循环)
  • 删除 .commit 文件
#!/bin/sh
echo
if [ -e .commit ]
    then
    rm .commit
    git add yourfile
    git commit --amend -C HEAD --no-verify
fi
exit

希望这能让没有 bash 知识的人更容易理解 mark 的想法。

【讨论】:

  • 不应该-a 是-e 来测试.commit 是否存在?
  • @AlbertNetymk - 几乎没有区别; -a 主要是作为 KornShell 提案引入的,并进入了 POSIX,它今天仅支持向后兼容。后来添加了-e,以避免混淆-a primary 与-a binary 运算符。
  • @EliranMalka 你能提供一个参考吗?我无法找到 -a。 linux.die.net/man/1/test我发现的唯一用途是逻辑and。
  • @AlbertNetymk,当然,这是POSIX test command man page on unix.com。在页面中搜索术语“早期提案使用了 KornShell -a primary”
  • @EliranMalka 我明白了。非常感谢。 -a 可能永远不应该被用作布尔运算符,以避免混淆。
【解决方案8】:
#!/bin/sh
#
#  .git/hooks/pre-commit
#

git add file.xyz

这对我来说效果很好。 它将成为当前提交的一部分。

git version 1.7.12.4 (Apple Git-37)

【讨论】:

  • 这也对我有用,除了我需要使用 cd $(git rev-parse --show-toplevel) 更改脚本的工作目录
  • 也为我工作。如果只是git commit,添加的文件不会出现在自动生成的提交消息中,但无论如何都会被添加。
【解决方案9】:

是的,您可以使用 git hooks 在提交时自动添加生成的文件!但它需要一个棘手的脚本。

在这里您可以找到已解决的问题。在那里,它会在每次提交时更新文件版本,添加新的修改文件并根据需要修改提交。它完全正常工作: https://github.com/evandrocoan/.versioning

然后,您只需用您的算法替换文件“updateVersion.sh”上的“版本文件替换”算法。也许您需要更改一些内容,例如删除分支限制,因为那里的脚本只有在您位于“开发”分支时才会运行。

此外,它只会更改指定的文件,如果已暂存。如果文件没有暂存,那么它只会做正常/通常的提交。更准确地说,它会打印出每一步都在做什么。

我将解释这个技巧。这很棘手。在 prepare-commit-msg-hook 上,它检测是否正在暂存和提交所需的文件。之后,它会创建一个标志文件,并停止 prepare-commit-msg-hook。 稍后在 post-commit-hook 上,它检查标志文件是否存在。如果是,它会修改提交时的文件。

注意,它会创建一个无限循环,因为它会再次调用 prepare-commit-msg-hook(正如我们正在修改的那样)。但它不会因为标志文件而发生。当 prepare-commit-msg-hook 运行并找到标志文件时,它“知道”发生了什么。然后只是删除标志文件并且不再创建它。这样做,它将阻止 post-commit-hook 再次修改提交,从而使提交永远完成。

【讨论】:

    【解决方案10】:

    我在预提交挂钩中也面临同样的问题。我正在修改一个文件并提交,但它正在使用以前的文件而不是更新的文件,因此通过在 pre-commit 挂钩中添加 git 命令(如下所示),它解决了。

    git add $file
    

    注意:$file 是您要添加的文件。

    谢谢,

    【讨论】:

      猜你喜欢
      • 2019-01-17
      • 1970-01-01
      • 2014-05-17
      • 2018-05-21
      • 2016-10-16
      • 1970-01-01
      • 1970-01-01
      • 2015-03-15
      • 1970-01-01
      相关资源
      最近更新 更多