Git post-receive hook无法正常工作?

时间:2018-04-09 08:08:18

标签: git

我正在尝试设置一个Git仓库,以便每当我从本地计算机上推送到GitHub上时,新的更改会自动部署在远程服务器上。但我认为我错过了关于钩子如何工作的基本原则。

我在远程服务器上设置了一个Alamofire.SessionManager.default.request("www.google.pl").responseData(queue: queue) { (data) in print(data) } 脚本,如下所示:

.git/hooks/post-receive

如果我运行它,它会这样做:

#!/bin/sh

GIT_WORK_TREE=/home/me/webapps/myapp git checkout -f master
GIT_WORK_TREE=/home/me/webapps/myapp git reset --hard

然而,$ /home/me/webapps/myapp/.git/hooks/post-receive Already on 'master' Your branch is up-to-date with 'origin/master'. HEAD is now at 527755e Initial commit 并不是GitHub上最新的主管 - 我从那时起就推动了更多更改。它是远程机器上的头。

我想我一定错过了什么。远程服务器上的挂钩如何"知道"当我推动掌握?

如何更改此项以便从本地推送自动复制到远程服务器?

1 个答案:

答案 0 :(得分:1)

  

我想我一定错过了什么。远程服务器上的挂钩如何"知道"当我推动掌握?

它没有 - 但如果设置正确,它可能没有。 "正确"取决于各种条件。

请记住,每个Git存储库都独立于任何其他Git存储库并控制自己的命运。为每个存储库命名可能有所帮助:A,B和C;或爱丽丝,鲍勃和卡罗尔;管他呢。在这里,我们假设有两个存储库:你的是repo R,服务器是repo S.

在回购R上做的事情影响了R.这些包括:

  • 检查特定提交,可能是通过分支名称;
  • 添加新提交;
  • 将分支名称从一个提交移动到另一个提交;
  • 正在运行git fetch

最后一个让你的Git调用另一个Git,比如S中的那个,并从中收集提交。收集提交后,您的Git会改变您的 Git的远程跟踪名称,例如origin/master。这对您自己的分支名称没有影响,这些名称不在此远程跟踪名称空间中,也不以origin/开头。

控制repo S的服务器上的某个人可以在那里做同样的事情,但通常没有人在服务器上工作。实际上,S上的存储库通常是存储库,特别是,因此 S上的任何人都无法对其进行任何操作。这种缺乏工作特别使得S成为git push目标(以下面提到的新的updateInstead内容为模)。< / p>

当Git git push的目标时,它:

  • 从其他存储库接收一些对象,通常是提交;然后
  • 从发送这些提交的任何人那里收到一些名称更新请求或命令。

请注意,这些名称更新已移至某个单独的名称空间。这与git fetch非常不同;我们马上回过头来看看。

S上的接收Git使用git receive-pack执行所有操作,这不是任何人通常自己运行的命令。但是,receive-pack命令会运行一堆钩子。目前的完整清单是:

  • 预先收到:一次,在开始任何名称更新之前;
  • 更新:每次更新一次;
  • 收到后:一次,在完成所有名称更新后;
  • 更新后:在完成所有名称更新后执行一次;
  • push-to-checkout:一次,推送到非裸存储库的当前分支,其中配置密钥receive.denyCurrentBranch设置为updateInstead

但其中一些是较新版本的Git中的新功能。即使在非常旧版本的Git中,标准三也可以预先接收,更新和接收后。 (有关每个钩子可以执行的操作的更多信息,请参阅the githooks documentation,最好是特定安装的文档,因为这些文档随时间发生了变化。)

那么这最终意味着什么呢?

请记住,正在执行git push的人通常会请求服务器设置repo S 分支。因此,在S获取并接受更改其自己的master的请求之后,存储库S中的名称master意味着我们刚收到并接受的提交

由于S在接收和执行名称更新请求之前,期间和之后运行这些挂钩,我们可以运行S:

git --work-tree=<path> checkout <args>

使用--work-tree=<path>环境变量的GIT_WORK_TREE或其等价物覆盖裸存储库S的&#34;裸&#34; -ness,以便S成为非裸存储库工作树。请注意,索引此(单个)工作树的S索引仍然与S本身相关联(它在Git目录中,作为名为index的文件):只有一个默认索引,因此它只能索引一个工作树。如果某些分支名称已更新,则任何git checkout <branch-name>现在都可以签出一些其他提交,与之前签出的提交不同。

此处存在明显的潜在竞争条件:如果某些存储库Q和R上的两个或多个用户同时运行git pushS ,请求S更改其master来自aaaaaaa...bbbbbbb...(Q)和ccccccc...(R)?对于名称更新本身,其中一个将赢得&#34; win&#34;比赛并首先设置名称到哈希映射。另一个将看到名称master被锁定并被告知退回再试一次。真正的&#34;同步&#34;情况还可以。但是,我们也可以成功更新master,然后启动post-receive挂钩。然后另一个可以出现,更新master。第一个post-receive挂钩可能会看到master的任一版本,第二个post-receive挂钩可能会在第一个仍在运行时开始运行。

现代Git中似乎没有代码可以防止这种情况发生。较旧版本的Git在大部分操作期间都会锁定文件,阻止第二个接收包启动,但我认为即使是那些在运行post-receive挂钩之前解锁了存储库。如果你想要真正的原子性并且正在做一些复杂的事情,那么实现你自己的锁定可能是明智的。

对于git checkout,这应该是无害的:赢得比赛的那个&#34;将签出旧的master或新的master - 它实际上看不到任何其他值的参考 - 然后失去竞争的一个,假设它的推送不作为非拒绝首先快进,将检查新的master。由于索引正在缓存工作树中的数据,因此结帐过程本身会在工作树更新时锁定索引文件。

因此,假设接收后挂钩位于 服务器

现在我们可以看到&#34;设置正确&#34;表示:post-receive hook必须在 (单个)服务器上运行,该服务器已指定为 服务器。

如果有多个服务器 - 例如,如果你使用GitHub作为中央存储库,但 web 服务器在其他地方 - 那么你就被卡住:没有办法知道,在 web 服务器, GitHub 服务器刚刚更新。

幸运的是,GitHub提供了#34;通知&#34; hooks:一旦收到这样的通知,你就可以进行设置,以便你的 web 服务器知道从GitHub服务器获取。但是,这需要在Web服务器上进行完全不同的设置,而不是后接收器挂钩。