强制git运行接收后钩子,即使一切都是“最新的”


70

即使我没有要提交的新提交,如何强制在服务器上git运行post-receive挂钩?

背景

我使用git自动将网站部署到服务器。我在服务器的受保护区域中有一个裸仓库,还有一个post-receive钩子,用于检出内容并将某些文件系统地复制到public_html文件夹中。(受本教程的启发)

我已经厌倦了post-receive在服务器上手动修改钩子,所以post-receive现在我的钩子实际上从仓库中复制了自己的新版本:

#!/bin/sh

rm -rf ~/../protected/*
GIT_WORK_TREE=~/../protected git checkout -f

# Rewrite over this file with any updates from the post-receive file
cp ~/../protected/post-receive hooks/post-receive

# Delete public_html
# Copy stuff public_html

当然,问题在于新的post-receive挂钩永远不会运行。一个看似简单的解决方案只是重新推动,但现在一切都已更新。这很烦人,因为每次更新post-receive钩子时都要求我伪造新的提交。有没有一种方法可以在post-receive不伪造提交或ssh输入的情况下调用该钩子?

我尝试了什么

git push
git push -f

2
我知道这很晚,但是我想出了另一个有趣的解决方案,因为我实现了相同的部署机制。完成后,checkout -f我使用rm -rf ~/repo.git/refs/heads/master。这将删除master分支,允许每次推送。
山姆·霍姆斯

Answers:


56

使用'--allow-empty'

在初始推送替换脚本之后,您可以执行以下操作:

git commit --allow-empty -m 'push to execute post-receive'

--allow-empty标志覆盖了git的默认行为,即在没有更改的情况下阻止您进行提交。

使用别名,让您的生活更轻松

将以下内容添加到 ~/.gitconfig

[alias]
    pushpr = "!f() { git push origin master;git commit --allow-empty -m 'push to execute post-receive';git push origin master; }; f"

现在就做 git pushpr

git pushpr

这会将所有更改推送到master,在您的情况下,这将触发您的帖子接收替换脚本,然后再次推送(使用--allow-empty标志),然后执行更新的post-receive脚本。


1
如果您不想进行额外的提交,则可以--allow-empty--amend和组合使用-m The message from the last commit,但请注意:这将重写上一次提交的历史记录。除非您是唯一的开发人员,否则不要使用。您下次必须强制执行。
安德鲁(Andrew)

12
@Andrew您可以运行git commit --allow-empty --amend --no-edit以重新使用提交消息,而无需重新键入它或打开编辑器。
thomasd

@trdarr哦,太神奇了。我完全为此做了一个别名。
安德鲁(Andrew)

2
@thomasd好的建议。与--amend --no-edit您不再需要--allow-empty
–aughedelic

注意:这将在历史记录中创建一个新的提交。因此,基本上,这是一种伪造提交的便捷方法。问题作者不想要的东西。
pixelbrackets

24

我知道这可能会被视为“危险”,但我喜欢生活在边缘。

我只是删除远程分支,然后再次推送它。确保您本地的分支机构是最新的,以限制丢失东西的机会。

因此,如果要触发接收后,就我而言,要使测试分支进行配置,我要做的就是:

$ git push origin :testing
$ git push origin testing

但是不要接受这个作为答案。仅供参考。


4
如果它实际上只是一个只读的遥控器,它实际上很方便。
艾伦(Alan)

2
伟大的git的回购协议,Heroku的风格
奔绕线

1
您将需要运行git config receive.denyDeleteCurrent false才能删除远程分支
Ben Winding

13

恐怕您必须SSH到服务器并手动运行钩子脚本。如果没有添加任何内容(即当git打印所有内容为最新时git push,则不会使服务器运行pre-pushpre-receivepost-receive挂钩。

其余的答案是关于跟踪接收后挂钩的版本,因此您可以修改它而无需将其链接到服务器。

do-post-receive向本地存储库添加名为shell脚本:

$ ls -ld .git
$ echo 'echo "Hello, World!"' >do-post-receive
$ git add do-post-receive
$ git commit do-post-receive -m 'added do-post-receive'

hooks/post-receive服务器上的钩子替换为:

#! /bin/sh
while read OLDID NEWID BRANCH; do
  test "$BRANCH" = refs/heads/master && eval "$(git show master:do-post-receive)"
done

(确保chmod 755 hooks/post-receive在服务器上。)

将您的更改从本地存储库推送到服务器,并观察do-post-receive代码运行:

$ git push origin master
...
remote: Hello, World!
...

非常聪明!不幸的是,我可能暂时无法尝试这种方法来接受。但是请耐心等待我。
AndyL

听起来很有希望,比我的回答还要多。+1
VonC

这对我根本不起作用。我认为您最好每次清除更改并执行完整的存储库推送。
杰伊·泰勒

是的,不幸的是,它不起作用,并且如果没有更改,则服务器上不会运行任何预推送,预接收或后接收挂钩。
2013年

你快到了!问题在于,接收后脚本以--censored-管道作为其标准输入运行,因此在读取输入后,while read ...几乎没有或没有任何内容可供do-post-receive脚本读取。(我写的东西叫git_xhook允许多个扩展的钩子;对于此评论来说太大了,但这可以带您到那里。)
torek

11

如果您想避免进行伪造的提交,则只需使用

git commit --amend --no-edit

这将修改最后的提交记录,您将可以使用git push -f(假设您的回答对您的覆盖没问题)。

  • 方便:它不会产生额外的提交
  • 好:它在远程仓库中不使用显式路径作为钩子
  • 不好:您会覆盖历史记录,这在您的用例中很好,但是不应该在共享存储库中使用

我相对频繁地使用此命令来在推送前的最后一次提交中修复某些问题,因此我为其做了一个别名:

git config --global alias.amend 'commit --amend --no-edit'

现在,我可以将其直接用作您的用例(git amend),或者

  • 将(所有)修改过的文件添加到上一次提交: git amend -a
  • 修改消息: git amend -m 'better message'

1
谢谢,您为我省了很多麻烦:)
Zdenek F

别客气。我很高兴我的回答对其他人仍然有用。
–aughedelic

3

我尝试了空提交删除了上传分支

但是直接使用ssh命令呢?

ssh your_host /path/to/your_repo/hooks/post-receive

2
这种方法可能涉及的问题是交互式Shell登录的环境变量/配置可能与git push不同,并且您可能会得到意外的行为。
cgseller

如果您需要确保命令可以在远程环境中与远程环境变量一起运行ssh host bash -cl '/path/to/post-receive',我不确定,但是
网站的


2

我做了一个bash函数来做到这一点。假定您具有ssh访问权限即可进行相应~/.ssh/config设置。默认远程为原点

kick-git() {
    remote="${1:-origin}"
    ssh $(echo $(git remote get-url "$remote")/hooks/post-receive | tr ':' ' ')
}

来源并运行 kick-git [remote]


2

接收后是人工命令响应的错误位置。

您希望您的服务器端好东西在pre-receive出口(取决于更新的ref)-例如git update-ref refs/commands/update-hooks @在服务器上,然后可以例如git push server +@:commands/update-hooks在服务器的pre-receive中进行

while read old new ref; do case $ref in
commands/update-hooks)
        maybe check the incoming commit for authorization
        update hooks here
        echo the results from the update
        denypush=1
        ;;
refs/heads/master)
        extreme vetting on master-branch updates here
        ;;
esac; done

((denypush)) && exit $denypush

1

我喜欢杰米·卡尔(Jamie Carl)的建议,但是它不起作用,并且出现错误:

remote:错误:默认情况下,拒绝删除当前分支,因为下一个错误:'git clone'不会导致任何文件签出,从而引起混乱。

在我的情况下,我正在针对裸仓库测试本地主机上的post-receive挂钩。警告/非常重要,remote服务器位置上运行此命令!

git update-ref -d refs/heads/develop 

它将删除developer分支的引用(您可能还需要删除为该分支部署的任何文件),然后可以继续执行该git push deploy_localtest develop分支所需的push命令。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.