如何强制 prod 用户提交 --author <the_real_author>

How can I force commit from prod user to have --author <the_real_author>

在我的工作场所,我们使用 git 在 Linux(centos 7 风格)下进行版本控制。我们有用户 ID,但也有一些我们每个人都可以模拟的产品 ID。

因为我们很弱,所以我们允许产品 ID 提交并推送回购协议,通常是为了在我们热修复问题时实用。我们应该在这样做时使用 --author 但我们有时会忘记。

有没有办法在使用特定产品用户时强制使用 --author ?

一旦提交,就永远无法更改。但是您可以选择 不发送 提交,还有另一个选项:拒绝接受 提交。因此,这里正好有三个选项:

  • 始终正确提交,方法是在将要提交的地方做一些检查以确保提交正确,如果不正确则不继续。例如,不要使用命令 git commit 进行提交,而是编写您自己的程序。或者,编写一个预提交钩子并在那里进行检查,永远不要绕过钩子。

  • 发送前检查。如果您使用 git push 将提交从 Git 存储库 A 发送到 Git 存储库 B,则 发送 Git (Git A) 将运行 其pre-push hook 在调用Git B 之后,但在传输commits 到Git B 之前,此时,你可以,在pre-push挂钩,检查提交:如果它们不是您喜欢的形式,请不要发送它们。

  • 接收前检查,在预接收或更新挂钩中。这需要对服务器本身进行操作。由于您提到了 Bitbucket,请参阅 their page on server-side hooks。预接收挂钩可以拒绝 git push(整个事情);更新挂钩可以拒绝 git push(一个名称更新)的一部分。

客户端挂钩(预提交和预推送)的优势在于它们在您的控制之下。这也是他们的缺点:每个 使用git commitgit push 的人都必须将挂钩安装到他们自己的 存储库中,这让他们有机会忘记安装它;他们可以 绕过 故意挂钩,或者卸载它们。

服务器端挂钩的优点是没有客户端(即您的 none 用户)可以无意或有意地绕过它。服务器强制执行它。同样,这也是它的缺点。执行完全取决于服务器,因此除非您控制 服务器——或者控制服务器的任何人将此权限委托给您——否则您甚至无法首先安装挂钩。

因为我不使用 Bitbucket,所以我无法详细介绍如何使用 Bitbucket 的客户端控制服务器端挂钩,但如果您通读我链接的页面,并点击更多链接,您应该能够了解如何使用他们提供的内容。

编写预提交挂钩

预提交挂钩相对简单:Git 将在 .git/hooks/ 内部查找名为 pre-commit 的文件。如果此文件存在 并且可执行 git commit 将 运行 它。该文件只是一个程序,可以用您喜欢的任何语言编写:Python、Perl、sh/bash、C、C++、Java、Go 等等。如果您使用解释语言编写,请确保 OS 的 exec 系统调用将调用解释器。也就是说,对于/bin/sh脚本,将文件的第一行设为#! /bin/sh,这样exec系统调用就可以直接运行它,然后chmod +x 文件。

你的程序的工作是检查将要提交的内容——包括,例如,命令行选项,如果你能以某种方式弄清楚它们是什么——如果提交应该以零(成功)状态退出被允许,或者一个非零(例如,1)退出状态,如果提交应该被拒绝。

对于你的特殊情况,你在某种程度上是幸运的:钩子是 运行 after Git 设置 GIT_AUTHOR_NAME和其他 Git 环境变量(这是基于 Git 2.21 的测试;请注意默认的 CentOS Git 可能很古老,因此请检查您的 Git 版本并验证这是否适用于您的版本,或在需要时升级)。例如,这里有一个有点愚蠢的预提交挂钩,我只是为了测试它而写的:

$ cat .git/hooks/pre-commit
#! /bin/sh
env | grep GIT | sed 's/@/ /'
exit 1

这个预提交挂钩简单地拒绝 每次 提交,在显示所有包含字符串 GIT 的环境变量(并删除 @在我 post 之后减少垃圾邮件)。所以现在(在 chmod +x 之后):

$ git commit
GIT_EXEC_PATH=/usr/local/libexec/git-core
GIT_INDEX_FILE=.git/index
GIT_AUTHOR_NAME=Chris Torek
GIT_PREFIX=
GIT_AUTHOR_EMAIL=chris.torek gmail.com
GIT_AUTHOR_DATE= 1570904668 -0700

或者:

$ git commit --author='A U Thor <thor@example.com>'
GIT_EXEC_PATH=/usr/local/libexec/git-core
GIT_INDEX_FILE=.git/index
GIT_AUTHOR_NAME=A U Thor
GIT_PREFIX=
GIT_AUTHOR_EMAIL=thor example.com
GIT_AUTHOR_DATE= 1570904927 -0700

现在应该很明显如何检查 "author" 设置了。你可以选择总是这样做,或者只有当 id 说你 运行 宁作为 "prod user"(无论这在实践中意味着什么)。

编写预推送挂钩

预推送挂钩类似,但它发生在 提交或提交存在之后。请注意,您 无法更改提交 — 为时已晚;用户必须丢弃任何错误的提交以支持新的和改进的替换——但是你 can check 提交你的 Git即将发送。

和以前一样,您的钩子可以用您喜欢的任何语言编写,尽管 shell 脚本在这里通常是最简单的。它的工作是检查已经存在的提交,验证您是否愿意将它们发送到接收方 Git,如果是,则退出 0。如果预推送挂钩退出非零,则整个推送将中止。

预推挂钩不是命令行参数,而是在其标准输入上接收输入,一次一行。所以你的钩子必须读取 all 输入行。每行有四个字符串,每个字符串由一个 space 分隔,并以换行符结束(没有回车 -return):

<local ref> SP <local sha1> SP <remote ref> SP <remote sha1> LF

(如 the githooks documentation 中所述)。

不幸的是,在 git push 时,远程 sha-1 哈希 ID 可能在也可能不在本地存储库中。如果不是,您唯一知道的是,如果您和其他 Git 都允许推送完成,则远程引用中的一些提交将会丢失。因此,对于这种情况,您可能想要中止推送,因为只有在用户使用 --force 并且 git push --force 通常是不受欢迎的情况下才会成功。 (如果用户真的需要强制推送,he/she/they/whatever 也可以使用 git push --n-verify -f 来绕过预推送钩子......或者,他们可以先 运行 git fetch,这样提交就存在于本地。)

验证本地存在远程哈希 ID 后,您可以检查传出提交(and/or 提交将被删除),您可以使用 git rev-list 获取此类提交的哈希 ID .

作为一个完全未经测试的示例,请考虑以下 shell 脚本:

while read localref localhash remoteref remotehash; do
    case $remoteref in
    refs/heads/*)
        remotebranch=${localref#refs/heads/};;
    *)
        echo "pushing to non-branch $remoteref - not checking this one"
        continue;;
    esac
    if ! git rev-parse --quiet --verify $remotehash >/dev/null; then
        echo "push to branch $remotebranch aborted"
        echo "need commits from them; please run git fetch first"
        exit 1
    fi
    git rev-list $remotehash..$remotehash | while read hash; do
        if ! check-commit $hash; then
            echo "git push to branch $remotebranch aborted: commit $hash failed check"
            exit 1
        fi
    done
    n_removed=$(git rev-list --count $localhash..$remotehash)
    if [ $n_removed -ne 0 ]; then
        echo "warning: force push discards $n_removed commits from $remotebranch"
    fi
done

如果这个 shell 脚本片段是正确的(记住,它是未经测试的),它仍然需要您编写自己的 shell 函数 check-commit。此函数应检查将要发送的提交,并确保您愿意将其发送给其他人 Git.

编写服务器端预接收挂钩

如果您可以完全控制服务器 Git,您可以编写自己的预接收挂钩。和以前一样,它只是一个可执行程序,用您喜欢的任何语言编写,OS 可以通过其 exec 系统调用调用,存储在名为 .git/hooks/pre-receive.[=51= 的文件中]

与上面的预推挂钩一样,预接收挂钩将其输入作为其标准输入上的一系列行,每个请求的参考更新一行。您的工作是阅读所有行,验证是否允许所有传入提交,如果是,则退出零。如果某些提交应该被拒绝,您只需退出非零即可。打印出为什么你拒绝提交是一个非常好的主意,这样无论谁运行宁git push 看到你的输出,前缀为 remote:,这样他们就可以告诉 为什么 他们的推送被拒绝了。但是仅仅退出非零将导致整个推送被拒绝。

编写服务器端更新挂钩

如果您可以完全控制服务器 Git,您可以编写自己的更新挂钩。和以前一样,它只是一个可执行程序,用您喜欢的任何语言编写,OS 可以通过其 exec 系统调用调用,存储在名为 .git/hooks/update.[=51= 的文件中]

更新挂钩将其参数作为实际参数接收。参数的顺序不同于提供给预接收挂钩的行的顺序。详情请咨询 the githooks documentation。与预接收挂钩一样,您的工作是检查建议的参考更新,并退出 0 以允许更新,或非零以拒绝它。与预接收挂钩不同,拒绝一个更新不会自动拒绝其他更新。与预接收挂钩一样,打印一条关于为什么更新被拒绝的消息是一个非常好的主意