Git预提交钩子+ Docker = 不同的Git状态

5
我希望在 pre-commit git hook 中运行脚本,我想让该脚本从 docker 映像中运行。以下是 pre-commit hook 的示例代码:
# 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 来确定在 pre-commit hook 中要处理哪些文件。

问题: 当我运行 git commit --all 时,git status 在 pre-commit hook 中和 docker 容器内不同。例如:

# 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 选项,也就是说更改不会被暂存。
我错过了什么?

你是否将代码文件夹挂载到容器中,然后运行脚本? - karthik101
是的!甚至尝试这个:`repo_root=$(git rev-parse --show-toplevel)` `docker run -v ${repo_root}:${repo_root} -w ${repo_root} alpine/git status` - user1011113
@user1011113 你想实现什么样的工作流程? - vmonteco
@vmonteco 我希望当用户运行 git commitgit commit --all 时,一些检查脚本(如 linters 等)可以分析 staged 文件,即提交的文件。我希望这些脚本从 Docker 镜像中运行,以确保每个开发人员都拥有相同的环境。使用 git commit 没有问题,但是使用 git commit --all 时会出现问题,因为 --all 选项似乎没有在 docker 容器内反映出来(未暂存的更改未添加到暂存区)。 - user1011113
@user1011113 你是从哪里尝试提交代码的?Docker容器里吗? - vmonteco
@vmonteco 不是的,用户从主机提交。这会启动预提交挂钩脚本,该脚本调用docker run。然后,我期望在提交挂钩(bash脚本)内与从提交挂钩调用的docker容器内相同的git状态。感觉 --all 选项会进行一些魔法,并将文件暂存到一个无法访问的单独索引中,但我真的不知道,我不理解 --all 标志发生了什么。 - user1011113
1个回答

5
TL;DR: 添加-e GIT_INDEX_FILE。但是,您可能希望允许其他Git变量。或者,您可以简单地禁止这样的提交,或使用完全不同的机制(请参见下面描述中的最后一段)。
描述:
当您使用git commit --all时,Git会创建一个临时索引来保存已暂存的文件,因为它们尚未在正常索引中暂存。如果提交成功,此临时索引将最终成为常规索引。在那之前,它不是常规索引。
现在,您的预提交脚本包括以下行:
docker run -v ${repo_root}:${repo_root} -w ${repo_root} ...
-v选项在运行docker镜像的子进程中挂载文件系统,-w将其设置为工作目录。然而,docker命令会过滤子进程中的环境变量,剥离所有未显式启用--env--env-file--env的短格式是-e)的可疑变量。 Git的顶级文档页面有一个关于环境变量的部分,其中包括:

GIT_INDEX_FILE

    此环境变量允许指定替代索引文件。如果未指定,则使用默认值$GIT_DIR/index
git commit --all设置临时索引时,它使用此环境变量将所有子命令指向临时索引,而不是$GIT_DIR/index
值得注意的是,Git还设置了$GIT_DIR,但它可能将其设置为“。”,因此当docker run将其剥离时,Git将从当前工作目录开始查找存储库,您通过-w将其设置为存储库根目录,因此删除是无害的。尽管如此,您可能想考虑通过所有Git的环境变量。然而,这并不像盲目地传递文档中列出的所有内容那么简单:例如,如果有需要导出的GIT_ALTERNATE_OBJECT_DIRECTORIES路径,您需要使用更多的-v选项将该路径中的每个元素挂载到docker实例中。
幸运的是,在此特定点设置GIT_INDEX_FILE时,它被设置为一个形如.git/temporary-name的路径名,因此无需挂载其他文件系统即可将Git临时索引带入运行镜像。这是因为此名称将被重命名为.git/index,而Git希望确保重命名可以作为原子文件系统操作完成,这要求其存在于与.git本身相同的挂载点上。实际上,对于git commit --all来说,它只是.git/index.lock,但git commit --only使用其他名称:使用--only形式需要多个临时索引文件,正在用于提交的那个不是成功后将成为正常索引的那个。
最后——这与 Docker 完全无关——请注意,可以要求 Git 提交暂存文件,它们与当前的工作树中的内容不匹配。例如,使用 git add -p ,可以轻松地将仅包含某些差异的文件版本存储在索引中,这些差异是 HEAD 版本和工作树版本之间的部分差异。我猜您计划让 Docker 环境对即将提交的内容进行某种测试。 这很好-但请注意,“将要提交的内容”不一定是“工作树中的内容”。当使用--all和临时索引时,将要提交的内容位于临时索引中,并且该临时索引刚刚从工作树中构建,因此它们将匹配;但是当不使用--all并且使用真实索引或使用带有不同临时索引的--only时,“将要提交的内容”不一定与工作树相匹配。
编写一个可以查看“将要提交的内容”的良好预提交钩子是有技巧的,但并非不可能。其中一种方法是将索引中的任何内容提取到与当前工作树无关的临时目录中。然后,您可以在临时目录上运行测试系统,不会受到存储库本身或当前工作树的干扰。如果您在预提交脚本中执行此操作,则可以通过 -v -w 挂载此临时目录,而不必担心在 Docker 映像中运行任何 Git 命令。
注:#! 行
您的示例可能已更改以用于 StackOverflow 发布,但是:
# pre-commit hook
#!/bin/bash
git status

#! 行已经变得无用了。这些行必须是脚本的 第一行。原因是内核(Linux 或派生自 Unix 的操作系统)运行此类脚本时会检查文件的前几个字节。如果前两个字节是 #!,那么第一行的剩余部分(在一定合理范围内)直到第一个换行符为止,将被视为解释器的名称,以及可能带有传递给该解释器的选项信息。然后内核运行该解释器,而不是脚本,传递任何来自 #! 行的选项,然后再传递脚本的名称。

如果第一行不以 #! 开头,内核就会拒绝直接运行该文件(execve 失败并返回 ENOEXEC)。Shell 检测到错误后,会自行检查文件,并确定该文件是否为 Shell 脚本... 如果是,那么就由 Shell 本身 运行该文件。主要问题在于 Shell 可能选择错误的 Shell 来运行该文件。通过使用 #! 行指定正确的解释器名称可以避免这个问题。


讲解不能再好了,非常感谢@torek!那真是清晰明了。关于“#!”的附注,是的,它只是为了在StackOverflow上发布,以明确我正在引用那个特定的文件。有更好的惯例吗? - user1011113
我不确定“惯例”是什么意思,但通常我会选择“灰色阴影的计算机文本=机器显示的内容”,并尝试使用其他东西来代替它,比如周围的文本或斜体(在SO上有些棘手),或者像这种情况一样,只需将#!行放在第一行,然后将“# pre-commit hook”注释作为第二行即可 :-) - torek

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