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

118

这是我在公司服务器上的裸仓库中当前的挂钩:

git push origin master

该挂钩会将更改推送到Assembla。

我需要的是,当有人在我们的服务器上将更改推送到该分支时,只推送一个分支(最好是主分支),并忽略对其他分支的推送。是否可以从裸仓库中选择分支并仅将该分支推送到Assembla?


你的意思是什么?git push origin master 只会将 master 分支推送到 origin 远程,我猜测这个远程是定义为 Assembla 的。你是说只有当有人推送到 master 分支时才需要触发钩子,而不是 feature1 或其他分支吗? - Stefan Kendall
@Stefan 没错,就是那个词。我找不到它,呵呵。 - Jorge Guberte
请返回已翻译的文本:重复的https://dev59.com/0Ww15IYBdhLWcg3w0fKh - insign
7个回答

407

一个 post-receive hook 从 stdin 中获取其参数,格式如下:

<oldrev> <newrev> <refname>

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

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

一个可运行的代码片段大致如下:

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

2
“==” 对我不起作用。单个“=”对我很有效。 - Ray
1
抱歉翻出了一个旧帖子,但我在if语句处遇到了错误。致命错误:远程端意外挂断。侧带复用器出现错误。它将在if语句之外回显$branch。 - gin93r
5
@Ray,你是否使用#!/bin/sh而非#!/bin/bash - shuttle87
3
我能想到的一个风险是标签,因为它们的名称可能与分支名称重叠。如果你查找refs/heads/master而不是refs/tags/master,那么应该没问题。虽然我想不到其他类似的情况,但这可能是一个好的StackOverflow问题。 - pauljz
1
@pauljz 我使用 if branch=$(git rev-parse --symbolic --abbrev-ref $refname 2>/dev/null); then 这个命令,这样当我删除一个分支时,git就不会抱怨了。 - Jérôme
显示剩余5条评论

8
在post-receive hook中,stdin接收到的最后一个参数是改变的引用,因此我们可以使用它来检查该值是否为"refs/heads/master"。以下是类似于我在post-receive hook中使用的Ruby代码:
STDIN.each do |line|
    (old_rev, new_rev, ref_name) = line.split
    if ref_name =~ /master/
         # do your push
    end
end

请注意,每次推送都会得到一行引用,因此如果您推送的不仅是主分支,它仍将起作用。

谢谢提供 Ruby 的例子。我会做类似的事情。 - leifericf

6

Stefan的答案对我没有用,但是这个有用:

#!/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这样的分支名称时,它并不太有效。您是否有可以处理这些特殊字符的解决方案? - Shahar Hamuzim Rajuan

4

以上两种解决方案都对我无效。经过大量调试,发现使用“read”命令不起作用——相反,通常的解析命令行参数的方法可以很好地工作。

这是我刚在CentOS 6.3上成功测试的excat post-update钩子:

#!/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'命令读取(哇,从未想过我会这么说)。post-update钩子对我来说仍然使用$1有效。


2
就此而言,上述解决方案可能无法奏效,因为它们专门针对的是post-receive钩子,而不是post-update钩子。它们以不同的方式接收输入。 - pauljz
post-receive 接受标准输入,如此处所述:https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks - h4xnoodle

2

@pauljz的答案适用于某些git hooks,例如pre-push,但是pre-commit没有访问那些变量oldrev newrev refname的权限。

因此,我创建了这个替代版本,它适用于pre-commit或任何hook。 这是一个pre-commit hook,如果我们不在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语句之间的任何内容。

0

我为自己编写了一个 PHP 脚本来实现这个功能。

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

将此文件托管在您的服务器上,最好是存储库根目录,并在 GitHub Webhooks 中定义 URL。将第8行中的“allcommits”更改为您的分支名称,并在第18行添加您的代码/函数。

例如:

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

0

简单方法,在 git hook 中编写

read refname
echo $refname

简单 - 更多关于这个好链接的信息 hooking system


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