我正在尝试设置一个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上最新的主管 - 我从那时起就推动了更多更改。它是远程机器上的头。
我想我一定错过了什么。远程服务器上的挂钩如何"知道"当我推动掌握?
如何更改此项以便从本地推送自动复制到远程服务器?
答案 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命令会运行一堆钩子。目前的完整清单是:
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 push
到S
,请求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服务器上进行完全不同的设置,而不是后接收器挂钩。