导读

现在我们将要学习如何搭建 git 服务器,如何编写自定义的 Git 钩子来在特定的事件触发相应的动作(例如通知),或者是发布你的代码到一个站点。

直到现在,我们主要讨论的还是以一个使用者的身份与 Git 进行交互。这篇文章中我将讨论 Git 的管理,并且设计一个灵活的 Git 框架。你可能会觉得这听起来是 “高阶 Git 技术” 或者 “只有狂热粉才能阅读”的一句委婉的说法,但是事实是这里面的每个任务都不需要很深的知识或者其他特殊的训练,就能基本理解 Git 的工作原理,有可能需要一丁点关于 Linux 的知识。



共享 Git 服务器



创建你自己的共享 Git 服务器意外地简单,而且在很多情况下,遇到的这点麻烦是完全值得的。不仅仅是因为它保证你有权限查看自己的代码,它还可以通过扩展为 Git 的使用敞开了一扇大门,例如个人 Git 钩子、无限制的数据存储、和持续集成与分发(CI & CD)。

如果你知道如何使用 Git 和 SSH,那么你已经知道怎么创建一个 Git 服务器了。Git 的设计方式,就是让你在创建或者 clone 一个仓库的时候,就完成了一半服务器的搭建。然后允许用 SSH 访问仓库,而且任何有权限访问的人都可以使用你的仓库作为 clone 的新仓库的基础。

点对点环境。按照一些方案你可以创建一些带有同样的功能的设计优良的 Git 服务器,同时有更好的拓展性。

首要之事:确认你的用户们,现在的用户以及之后的用户都要考虑。如果你是唯一的用户那么没有任何改动的必要。但是如果你试图邀请其他的代码贡献者使用,那么你应该允许一个专门的分享系统用户给你的开发者们。

假定你有一个可用的服务器(如果没有,这不成问题,Git 会帮忙解决,CentOS 的 树莓派 3 是个不错的开始),然后第一步就是只允许使用 SSH 密钥认证的 SSH 登录。这比使用密码登录安全得多,因为这可以免于暴力破解,也可以通过直接删除用户密钥而禁用用户。

gituser



$ su -c 'adduser gituser'



gituser 用户,创建一个 ~/.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



$ cat ~/path/to/id_rsa.bob.pub >> /home/gituser/.ssh/authorized_keys


gituser 用户访问服务器。

rustdesk服务器docker搭建 rust自己搭建服务器_freebsd

gituser 的身份访问。你只是想给他们访问 Git 仓库的权限。因为这个特殊的原因,Git 提供了一个限制的 shell,准确的说是git-shell 。以 root 身份执行以下命令,把 git-shell添加到你的系统中,然后设置成gituser


# 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



clone

checkout并没有任何分支显示)。这很重要,因为不允许远程使用者们push 到一个有效的分支上(如果你正在 dev 分支工作然后突然有人把一些变更push

/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


$ 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 钩子



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 钩子



push


rustdesk服务器docker搭建 rust自己搭建服务器_c语言_02

如果操作正确,网站发布工作会像以前一样很好的完成,而且在某种程度上,很精准。Git 真的好棒。我不知道谁最初想到这个主意,但是我是从 Emacs 和 Git 方面的专家,IBM 的 Bill von Hagen 那里第一次听到它的。他的文章包含关于这个过程的权威介绍:Git 改变了分布式网页开发的游戏规则。



Git 变量



commit 信息和 commit的作者,那么你的脚本就会变得相对麻烦些。

Git 钩子并不是被用户直接执行,所以要弄清楚如何收集可能会混淆的重要信息。事实上,Git 钩子脚本类似于其他的脚本,像 BASH、Python、C++ 等等一样从标准输入读取参数。不同的是,我们不会给它提供这个输入,所以,你在使用的时候,需要知道可能的输入参数。

.git/hooks 目录中提供的一些例子。举个例子,在这个 pre-push.sample文件里,注释部分说明了如下内容:


# $1 -- 即将 push 的远程仓库的名字
# $2 -- 即将 push 的远程仓库的 URL
# 如果 push 的时候,并没有一个命名的远程仓库,那么这两个参数将会一样。
#
# 提交的信息将以下列形式按行发送给标准输入
# <local ref> <local sha1> <remote ref> <remote sha1>


echo $1, $2, $3



分支检测示例



我发现,对于生产环境来说有一个共同的需求,就是需要一个只有在特定分支被修改之后,才会触发事件的钩子。以下就是如何跟踪分支的示例。

commit 记录和 push 记录,而不是你本地仓库的一部分。

rustdesk服务器docker搭建 rust自己搭建服务器_freebsd_03

post-receive(也就是说,在 commit



#!/bin/tcsh
foreach arg ( $< )
set argv = ( $arg )
set refname = $1
end



$1 ,然后循环用第二个参数$2 去覆盖它,然后用第三个参数 $3 再这样。在 Bash 中有一个更好的方法,使用 read命令,并且把值放入数组里。但是,这里是 tcsh,并且变量的顺序可以预测的,所以,这个方法也是可行的。

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

Git 钩子也可以变得复杂,而且它们因为 Git 的工作流的抽象层次不同而变得难以理解,但是它们确实是一个强大的系统,让你能够在你的 Git 基础设施上针对所有的行为进行对应的操作。如果你是一个 Git 重度用户,或者一个全职 Git 管理员,那么 Git 钩子是值得学习的,只有当你熟悉这个过程,你才能真正掌握它。

在我们这个系列下一篇也是最后一篇文章中,我们将会学习如何使用 Git 来管理非文本的二进制数据,比如音频和图片。