编写一个 git post-receive 钩子来处理特定的分支

Posted

技术标签:

【中文标题】编写一个 git post-receive 钩子来处理特定的分支【英文标题】:Writing a git post-receive hook to deal with a specific branch 【发布时间】:2011-11-13 04:08:38 【问题描述】:

这是我目前在公司服务器中的一个裸仓库中的钩子: git push origin master 这个钩子推送到 Assembla。 当有人将更改推送到我们服务器上的那个分支时,我需要只推送一个分支(理想情况下是主分支),而忽略对其他分支的推送。是否可以从裸仓库中选择分支并仅将该分支推送到 Assembla?

【问题讨论】:

什么意思? git push origin master 只会将master 分支推送到origin 远程,我假设它被定义为Assembla。你是说只有当有人推送到master而不是feature1或类似的东西时才需要触发钩子? @Stefan 正是这样。找不到这个词,呵呵。 复制***.com/questions/6372334/git-commit-hooks-per-branch 【参考方案1】:

post-receive 钩子从标准输入获取参数,格式为:

<oldrev> <newrev> <refname>

由于这些参数来自标准输入,而不是命令行参数,您需要使用read 而不是$1 $2 $3

post-receive 钩子可以一次接收多个分支(例如,如果有人执行git push --all),因此我们还需要将read 包装在while 循环中。

一个工作的 sn-p 看起来像这样:

#!/bin/bash
while read oldrev newrev refname
do
    branch=$(git rev-parse --symbolic --abbrev-ref $refname)
    if [ "master" = "$branch" ]; then
        # Do something
    fi
done

【讨论】:

“==”对我不起作用。使用单个“=”对我很有效。 很抱歉提出一个旧线程,但我在 if 语句中遇到错误。致命:远端意外挂断。错误:边带解复用器中的错误。它会在 if 语句之外回显 $branch。 @Ray,你有#!/bin/sh 而不是#!/bin/bash 我能想到的一个风险是标签,因为它们的名称可能与分支名称重叠。如果您查找refs/heads/master 而不是refs/tags/master,您应该没问题。可能还有其他像这样的边缘情况,但我想不出。这本身就是一个很好的 *** 问题。 @pauljz 我使用if branch=$(git rev-parse --symbolic --abbrev-ref $refname 2&gt;/dev/null); then 这样当我删除一个分支时 git 不会抱怨。【参考方案2】:

post-receive 钩子在 stdin 上获取的最后一个参数是 ref 被更改的内容,因此我们可以使用它来检查该值是否为“refs/heads/master”。有点类似于我在接收后挂钩中使用的红宝石:

STDIN.each do |line|
    (old_rev, new_rev, ref_name) = line.split
    if ref_name =~ /master/
         # do your push
    end
end

请注意,它会为每个推送的 ref 获取一行,因此如果您推送的不仅仅是 master,它仍然可以工作。

【讨论】:

感谢 Ruby 示例。我会做类似的事情。【参考方案3】:

@pauljz 的答案适用于某些 git 钩子,例如 pre-push,但 pre-commit 无法访问这些变量 oldrev newrev refname

所以我创建了这个替代版本,它适用于预提交,或者真的和钩子。这是一个pre-commit 钩子,如果我们不在master 分支上,它将运行husky 脚本。

#!/bin/bash
# git 'commit' does not have access to these variables: oldrev newrev refname
# So get the branch name off the head

branchPath=$(git symbolic-ref -q HEAD) # Something like refs/heads/myBranchName
branch=$branchPath##*/      # Get text behind the last / of the branch path

echo "Head: $branchPath";
echo "Current Branch: $branch";

if [ "master" != "$branch" ]; then

   # If we're NOT on the Master branch, then Do something
   # Original Pre-push script from husky 0.14.3

   command_exists () 
     command -v "$1" >/dev/null 2>&1
   

   has_hook_script () 
     [ -f package.json ] && cat package.json | grep -q "\"$1\"[[:space:]]*:"
   

   cd "frontend" # change to your project directory, if .git is a level higher

   # Check if precommit script is defined, skip if not
   has_hook_script precommit || exit 0

   # Node standard installation
   export PATH="$PATH:/c/Program Files/nodejs"

   # Check that npm exists
   command_exists npm || 
     echo >&2 "husky > can't find npm in PATH, skipping precommit script in package.json"
     exit 0
   

   # Export Git hook params
   export GIT_PARAMS="$*"

   # Run npm script
   echo "husky > npm run -s precommit (node `node -v`)"
   echo

   npm run -s precommit || 
     echo
     echo "husky > pre-commit hook failed (add --no-verify to bypass)"
     exit 1
   
fi

我希望对某人有所帮助。您可以根据需要轻松修改 iffi 语句之间的任何内容。

【讨论】:

【参考方案4】:

简单的方法,在git hook

read refname
echo $refname

简单 - 更多信息在这个伟大的链接hooking system

【讨论】:

【参考方案5】:

我为自己编写了一个 php 脚本来执行此功能。

https://github.com/fotuzlab/githubdump-php

将此文件托管在您的服务器上,最好是 repo root 并在 github webhooks 中定义 url。将第 8 行的“allcommits”更改为您的分支名称,并在第 18 行添加您的代码/函数。

例如

function githubdump($payload_object) 
    // Write your code here.
    exec('git push origin master');

【讨论】:

【参考方案6】:

上述解决方案都不适合我。经过大量调试,结果发现使用“读取”命令不起作用——相反,以通常的方式解析命令行参数可以正常工作。

这是我刚刚在 CentOS 6.3 上成功测试的确切更新后挂钩。

#!/bin/bash

echo "determining branch"

branch=`echo $1 | cut -d/ -f3`

if [ "master" == "$branch" ]; then
    echo "master branch selected"
fi

if [ "staging" == "$branch" ]; then
    echo "staging branch selected"
fi

exec git update-server-info

更新:更奇怪的是,pre-receive 钩子通过 stdin 获取输入,因此使用“read”读取(哇,从没想过我会这么说)。更新后挂钩对我来说仍然适用于 $1。

【讨论】:

对于它的价值,上述解决方案可能不起作用,因为它们专门用于 post-receive 钩子,而不是 post-update 钩子。他们以不同的方式接受他们的意见。 post-receive 采用标准输入,如此处所述:git-scm.com/book/en/v2/Customizing-Git-Git-Hooks【参考方案7】:

Stefan 的回答对我不起作用,但 this 起作用:

#!/bin/bash

echo "determining branch"

if ! [ -t 0 ]; then
  read -a ref
fi

IFS='/' read -ra REF <<< "$ref[2]"
branch="$REF[2]"

if [ "master" == "$branch" ]; then
  echo 'master was pushed'
fi

if [ "staging" == "$branch" ]; then
  echo 'staging was pushed'
fi

echo "done"

【讨论】:

为具有简单名称(master、test 等)的分支工作,但是当我有分支名称时:prod12/proj250/ropesPatch12。它不能很好地工作。您有可以处理这些特殊字符的解决方案吗?

以上是关于编写一个 git post-receive 钩子来处理特定的分支的主要内容,如果未能解决你的问题,请参考以下文章

git自定义项目钩子和全局钩子

[转] 利用git钩子,使用python语言获取提交的文件列表

git hook裸仓库自动部署

post-receive in Windows---git hooks trigger Jenkins to build artifcat

使用git签出文件时如何找出导致文件权限更改的原因?

git报错remote: error: cannot run hooks/post-receive: No such file or directory