如果尚未在开发分支中,则 Git 预接收挂钩可防止合并到 master

如何解决如果尚未在开发分支中,则 Git 预接收挂钩可防止合并到 master

我们的 git 流程是这样的。主分支是 master,我们从中创建功能分支。当我们认为我们完成了一个功能时,我们将它合并到开发分支中,并将其构建到集成环境中进行测试。测试完成后,我们从功能分支向 master 提交 MR。

我正在尝试制作一个 pre-receive git hook,它会阻止 MR 被接受,除非分支已合并到 develop 中并因此进行了集成测试。但是我正在努力弄清楚如何使用钩子中可用的信息来做到这一点。

我当前的脚本如下

while read oldrev newrev refname; do
    # Get the name of the branch that HEAD is pointing to
    headbranch=$(git symbolic-ref --short HEAD)
    # Get the current branch name
    branch=$(echo $refname | sed 's/refs\/heads\///g')
    # During a merge request both the branch and head branch will be master
    if [[ "$branch" == "master" && "$headbranch" == "master" ]]; then
        # Grab the PREVIOUS revision of the current revision as this is the last commit before the current merge commit
        prevrev=$(git rev-list -2 $newrev | sed -n '2 p')
        # Check that revision is in the develop branch already,otherwise reject the commit
        $(git merge-base --is-ancestor $prevrev develop)
        if [ $? -eq 1 ]; then
            echo "The branch has not been merged with \"develop\" or you have not pushed \"develop\" to the remote server." && exit 1
        fi
    fi
done

有时有效,但有时会失败,因为 newrevprevrev 都是与合并请求相关的随机散列,并且它们都没有出现在任何历史记录中.我想不出一种方法来区分这些我不关心的 MR 哈希值和尚未合并的代码提交的实际哈希值。

是否有其他方法可以做到这一点,或者这是不可能的?

注意:我也希望脚本拒绝直接提交给 master。

解决方法

是的,你的钩子代码肯定有点错误。 :-)

一个完美的解决方案非常困难,原因有一个:git push 可以在一次推送中接收多个更新请求。正如你所说:

我正在尝试制作一个 pre-receive git hook,以防止 MR 被接受,除非分支已合并到 develop 中并因此进行了集成测试。

我们先说最一般的情况,然后再说更具体的情况。假设一个推送请求说:

  • develop 更改为指向提交 a123456——当前为 a654321
  • 将 HEAD-ref 分支(假设是 master)更改为指向提交 b789abc;和
  • 更新 MR1234(可能是 refs/merges/1234)以指向提交 c765432

现在进一步假设 a654321develop当前 值)包含 c765432。也就是说,MR1234 之前的develop。因此,它已经通过了某种集成测试。显然这失败了,因为 develop 正在被撤回a123456,它是“before”a654321 以便 after 这个推送完成,a654321 不会develop了。

与此同时,在 b789abc 的祖先中的某处,存在提交 a123456。那是旧的 MR1234——在这次推送中被替换的那个。

就我个人而言,我不知道该怎么处理这个烂摊子。幸运的是,pre-receive 钩子只有两个选项:全部接受,或者全部拒绝。 update 钩子——如果 pre-receive 钩子允许的话,存储库中的钩子将用于这三个更新中的每一个——可以选择一次接受或拒绝一个请求,但不会无法获取此类全局信息。

鉴于您正在使用一个系统,其中一个人发出合并请求,等待某种 CI/CD 系统来测试它们,然后才尝试将它们提交以进行最终处理,这与 有关这种推送——试图同时更新masterdevelop和一些合并请求——只是拒绝它,并带有以下形式的消息:don不要一次做所有的事情,我需要一次处理一个请求

这需要将一些逻辑混合在一起或进行多次传递。您可以使用您喜欢的任何方法,但在 Git 钩子中执行多次传递有点棘手,因为 Git 将这些数据通过管道发送。1 这意味着您只能读取 一次。您可以读取并保存它,然后对数据进行多次传递。在 shell 中,您通常会通过制作自己的临时文件来做到这一点:

TF=$(mktemp)
trap "rm -f $TF" 0 1 2 3 15
cat > $TF

现在您可以根据需要多次阅读$TF。多次阅读效率较低,但更容易正确。


1我认为这是 Git 的一个错误,但这完全是另一个问题。


接下来,推送请求还可以创建新分支或删除现有分支。它可以创建或删除一个标签(或者被要求更新一个标签,但在旧版本的 Git 中意外地在标签名称上使用了分支规则,只有在强制的情况下)。我认为,它也可以用于既不是分支名称也不是标签名称的名称(例如,refs/notes/* 名称)。我们可以将它们分开并允许推送,例如,创建一个标签更新一个分支。但目前让我们从“仅一次操作”开始:

count=0
while read line; do : $((count+=1)); done < $TF
if [ $count -gt 1 ]; then
    echo "Only one operation per push,please!" 1>&2
    exit 1
fi

接下来,让我们做同样的事情:找出接收裸仓库中的 HEAD 分支是什么。这是主线分支的名称(mainmaster 等):

headbranch=$(git symbolic-ref --short HEAD)

这个命令可能失败,如果 HEAD 被分离,或者指向一个还不存在的分支。这永远不会发生,但如果我们想变得非常聪明,我们可能应该对此进行测试并打印一些内容并退出:

headbranch=$(git symbolic-ref --short HEAD) || {
    echo "detached HEAD - please get an admin to fix me" 1>&2
    exit 1
}

让我们再添加一件事:零哈希。为了防止 sha256 的未来,我们将读取 head ref 的哈希值并将所有十六进制值转换为零:

zerohash=$(git rev-parse HEAD) || {
    echo "missing HEAD - please get an admin to fix me" 1>&2
    exit 1
}
zerohash=$(echo $zerohash | sed 's/[1-9a-z]/0/g')

现在我们可以通读更新请求并对其进行审查。为了编码的完整性,我们可能应该使用 shell 函数。我们将在文件的前面定义 shell 函数(以便我们可以使用它),但现在让我们编写使用它的代码。我们知道这里只有一个操作,因为我们之前计算过它们,但为了干净起见,我将再次循环读取它们:

while read old new hash; do check $old $new $hash; done < $TF

我们会让 check 返回一个状态,所以让我们在这个 while 循环之前设置

exitcode=0
while read old new hash; do
    check $old $new $hash
    status=$?
    if [ $status -ne 0 ]; then exitcode=$status; fi
done < $TF
exit $exitcode

现在我们需要我们的 check 函数。它将继承$headbranch。它接受三个参数并返回一个状态(0 = OK,非零 = 已发送错误消息)。让我们先看看请求是否是branch 名称:

check() {
    local old=$1 new=$2 ref=$3
    local shortref

    case $ref in
    refs/heads/*) shortref=${ref#refs/heads/};;
    *) return 0;;
    esac

case 构造允许使用 glob 表达式进行大量测试,这在这里很方便。 (在非常旧的 shell 版本中,它也比 if 快,但如果您使用的是 bash 或现代 sh,则没有速度差异。)

现在我们需要查看分支是否是我们感兴趣的分支(master 或主分支是什么)。如果没有,我们就允许这样做。

    if [ "$shortref" != "$headbranch" ]; then return 0; fi

此时我们正在更新 master 或任何头部引用。我们现在将要求 (1) 这是一个合并提交,并且 (2) 合并提交的 second 父项位于 develop 的第一个父项列表中。除非,推送可能是由一些合适的管理员完成的(我会把这个测试留给你,特别是因为“谁实际上在做这个推送”可能很难测试——它完全取决于你的服务器框架的其余部分) .所以:

    # if person doing push is admin: return 0
    # Make sure this is neither creation nor deletion
    local op
    case $old,$new in
    $zerohash,*) op=create;;
    *,$zerohash) op=delete;;
    *) op=update;;
    esac
    if [ $op != update ]; then
        echo "not allowed to create or delete $headbranch" 1>&2
        return 1
    fi

    # require that this add exactly 1 2-parent merge commit: find
    # all parents with ^@ suffix and set them as our $1,$2,...,$n
    set -- $(git rev-parse $new^@)
    case $# in
    0) echo "root commit: forbidden on $headbranch" 1>&2; return 1;;
    1) echo "not a merge commit: forbidden on $headbranch" 1>&2; return 1;;
    2) ;;
    *) echo "$#-parent merge: forbidden on $headbranch" 1>&2; return 1;;

    # Now $1 is parent #1,which had better be the OLD commit;
    # $2 is the commit being merged,which needs to be in the
    # history of `develop`.
    if [ $1 != $old ]; then
        echo "this push adds more than one commit to $headbranch," 1>&2
        echo "which is not allowed: one commit at a time only please" 1>&2
        return 1
    fi

    if ! git merge-base --is-ancestor $2 develop; then
        echo "this push tries to merge $2," 1>&2
        echo "which is not present in branch `develop` on the server;" 1>&2
        echo "this is not allowed"
        return 1
    fi

    # all tests passed
    return 0
}

注意:以上均未经过测试。但是,这些位中有许多是来自标准钩子的片段,因此它们至少应该是“接近的”。要测试它,请克隆一些 repo 并通过它在其标准输入上运行一些示例输入。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-
参考1 参考2 解决方案 # 点击安装源 协议选择 http:// 路径填写 mirrors.aliyun.com/centos/8.3.2011/BaseOS/x86_64/os URL类型 软件库URL 其他路径 # 版本 7 mirrors.aliyun.com/centos/7/os/x86
报错1 [root@slave1 data_mocker]# kafka-console-consumer.sh --bootstrap-server slave1:9092 --topic topic_db [2023-12-19 18:31:12,770] WARN [Consumer clie
错误1 # 重写数据 hive (edu)&gt; insert overwrite table dwd_trade_cart_add_inc &gt; select data.id, &gt; data.user_id, &gt; data.course_id, &gt; date_format(
错误1 hive (edu)&gt; insert into huanhuan values(1,&#39;haoge&#39;); Query ID = root_20240110071417_fe1517ad-3607-41f4-bdcf-d00b98ac443e Total jobs = 1
报错1:执行到如下就不执行了,没有显示Successfully registered new MBean. [root@slave1 bin]# /usr/local/software/flume-1.9.0/bin/flume-ng agent -n a1 -c /usr/local/softwa
虚拟及没有启动任何服务器查看jps会显示jps,如果没有显示任何东西 [root@slave2 ~]# jps 9647 Jps 解决方案 # 进入/tmp查看 [root@slave1 dfs]# cd /tmp [root@slave1 tmp]# ll 总用量 48 drwxr-xr-x. 2
报错1 hive&gt; show databases; OK Failed with exception java.io.IOException:java.lang.RuntimeException: Error in configuring object Time taken: 0.474 se
报错1 [root@localhost ~]# vim -bash: vim: 未找到命令 安装vim yum -y install vim* # 查看是否安装成功 [root@hadoop01 hadoop]# rpm -qa |grep vim vim-X11-7.4.629-8.el7_9.x
修改hadoop配置 vi /usr/local/software/hadoop-2.9.2/etc/hadoop/yarn-site.xml # 添加如下 &lt;configuration&gt; &lt;property&gt; &lt;name&gt;yarn.nodemanager.res