导读 | 如今咱们将要学习如何搭建 git 服务器,如何编写自定义的 Git 钩子来在特定的事件触发相应的动做(例如通知),或者是发布你的代码到一个站点。 |
直到如今,咱们主要讨论的仍是以一个使用者的身份与 Git 进行交互。这篇文章中我将讨论 Git 的管理,而且设计一个灵活的 Git 框架。你可能会以为这听起来是 “高阶 Git 技术” 或者 “只有狂热粉才能阅读”的一句委婉的说法,可是事实是这里面的每一个任务都不须要很深的知识或者其余特殊的训练,就能基本理解 Git 的工做原理,有可能须要一丁点关于 Linux 的知识。html
共享 Git 服务器linux
建立你本身的共享 Git 服务器意外地简单,并且在不少状况下,遇到的这点麻烦是彻底值得的。不只仅是由于它保证你有权限查看本身的代码,它还能够经过扩展为 Git 的使用敞开了一扇大门,例如我的 Git 钩子、无限制的数据存储、和持续集成与分发(CI & CD)。git
若是你知道如何使用 Git 和 SSH,那么你已经知道怎么建立一个 Git 服务器了。Git 的设计方式,就是让你在建立或者 clone 一个仓库的时候,就完成了一半服务器的搭建。而后容许用 SSH 访问仓库,并且任何有权限访问的人均可以使用你的仓库做为 clone 的新仓库的基础。github
可是,这是一个小的点对点环境。按照一些方案你能够建立一些带有一样的功能的设计优良的 Git 服务器,同时有更好的拓展性。web
首要之事:确认你的用户们,如今的用户以及以后的用户都要考虑。若是你是惟一的用户那么没有任何改动的必要。可是若是你试图邀请其余的代码贡献者使用,那么你应该容许一个专门的分享系统用户给你的开发者们。shell
假定你有一个可用的服务器(若是没有,这不成问题,Git 会帮忙解决,CentOS 的 树莓派 3 是个不错的开始),而后第一步就是只容许使用 SSH 密钥认证的 SSH 登陆。这比使用密码登陆安全得多,由于这能够免于暴力破解,也能够经过直接删除用户密钥而禁用用户。数组
一旦你启用了 SSH 密钥认证,建立 gituser 用户。这是给你的全部受权的用户们的公共用户:安全
$ su -c 'adduser gituser'
而后切换到刚建立的 gituser 用户,建立一个 ~/.ssh 的框架,并设置好合适的权限。这很重要,若是权限设置得太开放会使本身所保护的 SSH 没有意义。服务器
$ su - gituser $ mkdir .ssh && chmod 700 .ssh $ touch .ssh/authorized_keys $ chmod 600 .ssh/authorized_keys
authorized_keys 文件里包含全部你的开发者们的 SSH 公钥,你开放权限容许他们能够在你的 Git 项目上工做。他们必须建立他们本身的 SSH 密钥对而后把他们的公钥给你。复制公钥到 gituser 用户下的 authorized_keys 文件中。例如,为一个叫 Bob 的开发者,执行如下命令:app
$ cat ~/path/to/id_rsa.bob.pub >> /home/gituser/.ssh/authorized_keys
只要开发者 Bob 有私钥而且把相对应的公钥给你,Bob 就能够用 gituser 用户访问服务器。
可是,你并非想让你的开发者们能使用服务器,即便只是以 gituser 的身份访问。你只是想给他们访问 Git 仓库的权限。由于这个特殊的缘由,Git 提供了一个限制的 shell,准确的说是git-shell 。以 root 身份执行如下命令,把 git-shell添加到你的系统中,而后设置成gituser 用户的默认 shell。
# grep git-shell /etc/shells || su -c "echo `which git-shell` >> /etc/shells" # su -c 'usermod -s git-shell gituser'
如今 gituser 用户只能使用 SSH 来 push 或者 pull Git 仓库,而且没法使用任何一个能够登陆的 shell。你应该把你本身添加到和 gituser 同样的组中,在咱们的样例服务器中这个组的名字也是 gituser。
举个例子:
# usermod -a -G gituser seth
仅剩下的一步就是建立一个 Git 仓库。由于没有人能在服务器上直接与 Git 交互(也就是说,你以后不能 SSH 到服务器而后直接操做这个仓库),因此建立一个空的仓库 。若是你想使用这个放在服务器上的仓库来完成工做,你能够从它的所在处clone 下来,而后在你的 home 目录下进行工做。
严格地讲,你不是必须建立这个空的仓库;它和一个正常的仓库同样工做。可是,一个空的仓库没有工做分支(working tree) (也就是说,使用 checkout并无任何分支显示)。这很重要,由于不容许远程使用者们push 到一个有效的分支上(若是你正在 dev 分支工做而后忽然有人把一些变动push 到你的工做分支,你会有怎么样的感觉?)。由于一个空的仓库能够没有有效的分支,因此这不会成为一个问题。
你能够把这个仓库放到任何你想放的地方,只要你想要放开权限给用户和用户组,让他们能够在仓库下工做。千万不要保存目录到好比说一个用户的 home 目录下,由于那里有严格的权限限制。保存到一个常规的共享地址,例如/opt 或者 /usr/local/share。
以 root 身份建立一个空的仓库:
# git init --bare /opt/jupiter.git # chown -R gituser:gituser /opt/jupiter.git # chmod -R 770 /opt/jupiter.git
如今任何一个用户,只要他被认证为gituser 或者在gituser 组中,就能够从 jupiter.git 库中读取或者写入。在本地机器尝试如下操做:
$ git clone gituser@example.com:/opt/jupiter.git jupiter.clone Cloning into 'jupiter.clone'... Warning: you appear to have cloned an empty repository.
谨记:开发者们必定要把他们的 SSH 公钥加入到gituser 用户下的 authorized_keys文件里,或者说,若是他们有服务器上的用户(若是你给了他们用户),那么他们的用户必须属于 gituser 用户组。
Git 钩子
运行你本身的 Git 服务器最赞的一件事之一就是可使用 Git 钩子。Git 托管服务有时提供一个钩子类的接口,可是他们并不会给你真正的 Git 钩子来让你访问文件系统。Git 钩子是一个脚本,它将在一个 Git 过程的某些点运行;钩子能够运行在当一个仓库即将接收一个commit 时、或者接受一个 commit以后,或者即将接收一次 push 时,或者一次 push以后等等。
这是一个简单的系统:任何放在.git/hooks 目录下的脚本、使用标准的命名体系,就可按设计好的时间运行。一个脚本是否应该被运行取决于它的名字;pre-push 脚本在push 以前运行,post-receive 脚本在接受 commit 以后运行等等。这或多或少的能够从名字上看出来。
脚本能够用任何语言写;若是在你的系统上有能够执行的脚本语言,例如输出 ‘hello world’ ,那么你就能够这个语言来写 Git 钩子脚本。Git 默认带了一些例子,可是并不有启用。
想要动手试一个?这很简单。若是你没有现成的 Git 仓库,首先建立一个 Git 仓库:
$ mkdir jupiter $ cd jupiter $ git init .
而后写一个 “hello world” 的 Git 钩子。由于我为了支持老旧系统而使用 tsch,因此我仍然用它做为个人脚本语言,你能够自由的使用本身喜欢的语言(Bash,Python,Ruby,Perl,Rust,Swift,Go):
$ echo "#/!/bin/tcsh" > .git/hooks/post-commit $ echo "echo 'POST-COMMIT SCRIPT TRIGGERED'" >> ~/jupiter/.git/hooks/post-commit $ chmod +x ~/jupiter/.git/hooks/post-commit
如今测试它的输出:
$ echo "hello world" > foo.txt $ git add foo.txt $ git commit -m 'first commit' ! POST-COMMIT SCRIPT TRIGGERED [master (root-commit) c8678e0] first commit 1 file changed, 1 insertion(+) create mode 100644 foo.txt
如今你已经实现了:你的第一个有功能的 Git 钩子。
有名的 push-to-web 钩子
Git 钩子最流行的用法就是自动 push 更改的代码到一个正在使用中的产品级 Web 服务器目录下。这是摆脱 FTP 的很好的方式,对于正在使用的产品保留完整的版本控制,整合并自动化内容的发布。
若是操做正确,网站发布工做会像之前同样很好的完成,并且在某种程度上,很精准。Git 真的好棒。我不知道谁最初想到这个主意,可是我是从 Emacs 和 Git 方面的专家,IBM 的 Bill von Hagen 那里第一次听到它的。他的文章包含关于这个过程的权威介绍:Git 改变了分布式网页开发的游戏规则。
Git 变量
每个 Git 钩子都有一系列不一样的变量对应触发钩子的不一样 Git 行为。你需不须要这些变量,主要取决于你写的程序。若是你只是须要一个当某人 push 代码时候的通用邮件通知,那么你就不须要什么特殊的东西,甚至也不须要编写额外的脚本,由于已经有现成的适合你的样例脚本。若是你想在邮件里查看commit 信息和 commit的做者,那么你的脚本就会变得相对麻烦些。
Git 钩子并非被用户直接执行,因此要弄清楚如何收集可能会混淆的重要信息。事实上,Git 钩子脚本相似于其余的脚本,像 BASH、Python、C++ 等等同样从标准输入读取参数。不一样的是,咱们不会给它提供这个输入,因此,你在使用的时候,须要知道可能的输入参数。
在写 Git 钩子以前,看一下 Git 在你的项目目录下 .git/hooks 目录中提供的一些例子。举个例子,在这个 pre-push.sample文件里,注释部分说明了以下内容:
# $1 -- 即将 push 的远程仓库的名字 # $2 -- 即将 push 的远程仓库的 URL # 若是 push 的时候,并无一个命名的远程仓库,那么这两个参数将会同样。 # # 提交的信息将如下列形式按行发送给标准输入 # <local ref> <local sha1> <remote ref> <remote sha1>
并非全部的例子都是这么清晰,并且关于钩子获取变量的文档依旧缺少(除非你去读 Git 的源码)。可是,若是你有疑问,你能够从线上其余用户的尝试中学习,或者你只是写一些基本的脚本,好比 echo $1, $2, $3 等等。
分支检测示例
我发现,对于生产环境来讲有一个共同的需求,就是须要一个只有在特定分支被修改以后,才会触发事件的钩子。如下就是如何跟踪分支的示例。
首先,Git 钩子自己是不受版本控制的。 Git 并不会跟踪它本身的钩子,由于对于钩子来讲,它是 Git 的一部分,而不是你仓库的一部分。因此,Git 钩子能够监控你的 Git 服务器上的一个空仓库的 commit 记录和 push 记录,而不是你本地仓库的一部分。
咱们来写一个 post-receive(也就是说,在 commit 被接受以后触发)钩子。第一步就是须要肯定分支名:
#!/bin/tcsh foreach arg ( $< ) set argv = ( $arg ) set refname = $1 end
这个 for 循环用来读入第一个参数 $1 ,而后循环用第二个参数$2 去覆盖它,而后用第三个参数 $3 再这样。在 Bash 中有一个更好的方法,使用 read命令,而且把值放入数组里。可是,这里是 tcsh,而且变量的顺序能够预测的,因此,这个方法也是可行的。
当咱们有了 commit 记录的 refname,咱们就能使用 Git 去找到这个分支的供人看的名字:
set branch = `git rev-parse --symbolic --abbrev-ref $refname` echo $branch #DEBUG
而后把这个分支名和咱们想要触发的事件的分支名关键字进行比较:
if ( "$branch" == "master" ) then echo "Branch detected: master" git / --work-tree=/path/to/where/you/want/to/copy/stuff/to / checkout -f $branch || echo "master fail" else if ( "$branch" == "dev" ) then echo "Branch detected: dev" Git / --work-tree=/path/to/where/you/want/to/copy/stuff/to / checkout -f $branch || echo "dev fail" else echo "Your push was successful." echo "Private branch detected. No action triggered." endif
给这个脚本分配可执行权限:
$ chmod +x ~/jupiter/.git/hooks/post-receive
如今,当一个用户提交到服务器的 master 分支,那些代码就会被复制到一个生产环境的目录,提交到 dev 分支则会被复制到另外的地方,其余分支将不会触发这些操做。
同时,创造一个pre-commit 脚本也很简单。好比,判断一个用户是否在他们不应push 的分支上push 代码,或者对 commit 信息进行解析等等。
Git 钩子也能够变得复杂,并且它们由于 Git 的工做流的抽象层次不一样而变得难以理解,可是它们确实是一个强大的系统,让你可以在你的 Git 基础设施上针对全部的行为进行对应的操做。若是你是一个 Git 重度用户,或者一个全职 Git 管理员,那么 Git 钩子是值得学习的,只有当你熟悉这个过程,你才能真正掌握它。
在咱们这个系列下一篇也是最后一篇文章中,咱们将会学习如何使用 Git 来管理非文本的二进制数据,好比音频和图片。
via: https://opensource.com/life/16/8/how-construct-your-own-git-server-part-6
做者:Seth Kenlon 译者:maywanting 校对:wxy