use*_*113 5 git pre-commit-hook docker
我想在预提交 git hook 中运行脚本。我希望该脚本从 docker 映像中运行。此预提交挂钩的示例代码:
# pre-commit hook
#!/bin/bash
repo_root=$(git rev-parse --show-toplevel)
docker run -v ${repo_root}:${repo_root} -w ${repo_root}  <my_docker_image> <path_to_my_script.py>
my_script.py内部运行git status以确定要在预提交挂钩中处理哪些文件。
git status问题:当我运行 时,预提交挂钩中的输出与 docker 容器内的
输出不同git commit --all。例子:
# pre-commit hook
#!/bin/bash
git status
echo "------------------------------------"
repo_root=$(git rev-parse --show-toplevel)
docker run -v ${repo_root}:${repo_root} -w ${repo_root} <my_docker_image> git status
我希望通过在 docker 容器内运行git commit --all,git status我可以看到所有已上演的更改。
但是,这些更改不会在 docker 容器内进行。我之前编写的代码打印以下内容:
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)
    modified:   tools/git-hooks/pre-commit
------------------------------------
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:   tools/git-hooks/pre-commit
换句话说:在 docker 内部,git status不检测该--all选项;这些变化不是上演的。
我缺少什么?
TL;DR:添加-e GIT_INDEX_FILE. 但是,您可能希望允许其他 Git 变量。或者,您可以简单地禁止此类提交,或使用完全不同的机制(请参阅下面描述中的最后一段)。
当您使用 时git commit --all,Git 会创建一个临时索引来保存暂存文件,因为它们尚未暂存到普通索引中。如果提交成功,这个临时索引最终将成为常规索引。在那之前,它还不是常规索引。
现在,您的预提交脚本包含以下行:
\n\ndocker run -v ${repo_root}:${repo_root} -w ${repo_root} ...\n该-v选项在运行 docker 映像的子进程中安装文件系统,并将-w其设置为工作目录。但是,该docker命令会过滤子进程中的环境,删除所有未使用--envor --env-file(缩写为--envis -e)显式启用的可疑变量。
顶级文档页面有一个关于环境变量的部分,git其中包括:
\n\n\n\n\n
GIT_INDEX_FILE此环境允许指定备用索引文件。
\n$GIT_DIR/index如果未指定,则使用默认值。
当git commit --all设置临时索引时,它使用此环境变量来指示其所有子命令查看临时索引而不是$GIT_DIR/index.
值得注意的是,Git 也设置了$GIT_DIR,但它可能已将其设置为.,因此当docker run将其删除时,Git 会从当前工作目录开始查找存储库,您可以通过将其设置-w为存储库 root\xe2\ x80\x94so 剥离.结果是无害的。尽管如此,您可能需要考虑传递所有 Git 的环境变量。不过,这并不像盲目传递文档中列出的所有内容那么简单:例如,如果有一个GIT_ALTERNATE_OBJECT_DIRECTORIES需要导出的路径,您还需要将该路径中的每个元素安装到 docker 实例中,使用更多的-v选择。
幸运的是,当GIT_INDEX_FILE在这个特定点设置时,它被设置为 形式的路径名,因此无需安装额外的文件系统即可将 Git 临时索引获取到正在运行的映像中。这是因为该名称将被重命名,并且 Git 希望确保重命名可以作为原子文件系统操作来完成,这要求它与自身存在于同一安装点上。事实上,因为它只是,虽然使用了其他名称:表单需要多个临时索引文件,并且用于提交的文件不是成功后将成为正常索引的文件。.git/temporary-name.git/index.gitgit commit --all.git/index.lockgit commit --only--only
最后\xe2\x80\x94,这完全独立于 Docker\xe2\x80\x94,请注意,可以要求 Git 提交与当前工作树中的内容不匹配的暂存文件。例如,使用git add -p,可以很容易地在索引中存储文件的版本,该版本与工作树版本之间只有一些差异。HEAD我猜测您计划让 docker 环境对要提交的内容运行某种测试。很好\xe2\x80\x94,但请注意“要提交的内容”不一定是“工作树中的内容”。当使用--all临时索引时,它是包含要提交的内容的临时索引,并且该临时索引刚刚从工作树构建,因此它们将匹配;但是当不使用--all和 使用真实索引时,或者使用--only不同的临时索引时,“要提交的内容”不一定与工作树匹配。
编写一个好的预提交钩子来查看“要提交什么”是很棘手的,但并非不可能。一种方法是将索引中的所有内容提取到与当前工作树无关的临时目录中。然后,您可以在临时目录上运行测试系统,而不会受到存储库本身的干扰,也不会受到当前工作树的干扰。如果您在预提交脚本中执行了此操作,则可以挂载(通过-v和-w)此临时目录,而不必担心在 docker 映像内运行任何Git 命令。
#!线条您的示例可能会出于 StackOverflow 发布目的进行修改,但在:
\n\n# pre-commit hook\n#!/bin/bash\ngit status\n该#!线已变得毫无用处。这些行必须是脚本的第一行。原因是内核(Linux,或从中派生的 Unix)运行这样的脚本的方式是检查文件的前几个字节。如果前两个字节是#!,则第一行的其余部分\ xe2\x80\x94 直到第一个换行符 \xe2\x80\x94 的所有内容(在某些合理的限制内)都被视为解释器的名称,也许还有该解释器的选项。然后内核运行解释器,而不是脚本,从该#!行传递选项(如果有),然后传递脚本的名称。
但是,如果第一行不以 开头#!,内核将拒绝直接运行该文件(execve失败时为ENOEXEC)。shell 发现错误,检查文件本身,并确定该文件是否是 shell 脚本……如果是,shell本身就会运行该文件。这里的主要问题是 shell 可能会选择错误的 shell 来运行文件。在一行中包含正确#!解释器的名称可以避免这种情况。
| 归档时间: | 
 | 
| 查看次数: | 5067 次 | 
| 最近记录: |