自己的 git 托管
争名夺利几时休,早起迟眠不自由。
骑着驴骡思骏马,官居宰相望王侯。
自打在服务器上完成了代码环境的配置,我就在想,要是有一个自己的远程 git 存储库该多好?
gitlab
我按照网上的教程,我更新了清华镜像源,下载安装 gitlab。孰料一次一次在安装过程卡死,而且情况十分严重,CPU 占用率长期维持在 50%以上,内存更是飙升到 90%,不仅安装进行不下去了,甚至一切操作都无法执行,包括Ctrl+C想要杀死当前进程的请求也会石沉大海。没办法,只得强制重启。
如是反复若干次,最终死心,将 gitlab 彻底从云服务器卸载,“革职为民,永不叙用”。
经过上网查找,原因果然出现在配置上。gitlab 推荐的最小内存是 4G,但是目前我们只有 2G,内存爆满也就理所当然了。没办法,期限之内不能更改配置,就算能更改,我都穷到薅资本主义羊毛了,还能有钱升级配置?笑死。
就因为这个问题,我们就不干了?这是不行滴,小同志。
不久之后,黄四郎同志发来了一篇阮一峰的博客:最简单的 git 服务器。看起来似乎不错,但是失之简略。幸好,我在 git 官方教程Pro Git上找到了另一部分,两个拼一拼、试一试,最终成功了。
前置
首先,我建议为 git 托管创建专门的用户。这样做有以下几个目的:
- 安全
- 如果使用常用用户,仓库托管在家目录下的某一级目录,容易一个不注意自己把仓库删了
- 如果使用 root 用户,找一个目录进行管理,则权限过高,也容易被误操作;除此之外,root 用户一般不会设置为 ssh 登录,这就意味着想简单创建一个库,我们需要先 ssh 登陆,然后切换到 root 用户、进入存放目录/创建,最后退出,整个操作过程较为烦人且无法自动化实现
- 方便使用。创建专门用户后,我们可以为之配置 ssh 密钥,从而实现免密登陆,而且创建新仓库、clone 仓库都较为简单(因为直接托管在家目录之下,不需要记较长的路径名)
当然,创建单独用户的理由不止这么简单,它在我们使用 http 来托管仓库的时候有专门的作用,下文会讨论到。
我为其创建了一个名为git
的用户,家目录/home/git
,但不授予 sudo 权限。
ssh 服务
首先,本方法要求有一个本地 git 存储库,并且已经有 commit。如果是本地新建的仓库,需要先有一个 commit,然后继续操作。这个 commit
可以用编写.gitignore
或者push.sh
等不甚重要的文件来凑数。
远程仓库
# 创建远程仓库,这里也叫test吧
ssh git@127.0.0.1 git init --bare test.git
注意,本处指明的 git 为远程用户名,127.0.0.1 代表云服务器的公网 ip,test.git
为远程仓库名。使用的时候都需要换成自己的。如果仓库不想直接存储在用户目录下,需要指出其存储路径,如code/fuck/test.git
本地与远程连接
本地和远程都有了,下一步就是建立联系了。
# 本地添加远程库信息
git remote add origin git@127.0.0.1:test.git
# 此时尚不能直接推送,因为并未指定上游对应分支,需要指定
git push --set-upstream origin master
# 之后就可以直接推送了
bash push.sh
自动化
整体操作过程可以说比较简单了。既然如此,脚本,启动!
#!/bin/bash
read -p "Local repo name: " local_name
read -p "Remote repo name: " remote_name
# 远程仓库创建,用的时候记得改服务器地址
tmp="ssh your-server git init --bare $remote_name.git"
eval "$tmp"
# 本地仓库创建
mkdir "$local_name"
cd "$local_name"
git init
# 本地仓库初始化
# 编写.gitignore
cat > .gitignore << EOF
*.sh
*.bat
*.exe
*.[oa]
*.pyc
__pycache__
*.vscode
*.swp
EOF
# 编写push.sh
cat > push.sh << EOF
git add .
git commit
git push
EOF
chmod +x push.sh
# 提交初始化commit
git add .
git commit -m "Initial commit"
tmp="git remote add origin aliyun-git:$remote_name.git"
eval "$tmp"
git push --set-upstream origin master
echo "Success!"
Attention please:
- 本地仓库名和远程仓库名不要加.git 后缀,程序会自动添加
- 如果要把这个脚本改写为其他语言的脚本的话,在转换本地当前工作目录(进入本地新创建的仓库)的时候,一定要使用语言自带的调整当前工作目录的函数,不要调用系统命令(如写为
C
语言程序的时候,应当是调用库函数
chdir()
而不是选择system("cd <目录名>")
),否则编译器/解释器自动在这里作多线程执行,创建一个新的线程,运行一个终端,进入了该目录,然后没有后续,线程挂了;另一个线程在原来的代码上继续执行,但根本没进入目录,也就是执行操作的位置不对,最终出现错误。
http(s)服务
到这里似乎已经万事俱备了,但在服务器迁移的过程中,我发现了问题:使用 ssh 服务,就意味着每个仓库记录的远程库都是其 ip
地址(或者自己在~/.ssh/config
里规定的服务器别名),当服务器迁移后,需要手动进入一个个本地仓库修改远程库地址,非常麻烦。相比之下,如果使用像 github 一样的
http(s)服务,我们就只需要做好远程库迁移、dns 信息修改和服务器设置即可,本地仓库完全不需要改动。
于是,我发现了一篇教程,照葫芦画瓢起来。
需要注意的是,仍然建议为 git 仓库专门建用户。
配套软件安装
首先,我们需要安装git
和nginx
,这里不再赘述。除此之外,我们会用到以下软件:
git-http-backend
:git 官方提供的 git 的 http(s) 服务后端。该软件包含在git-core
中,一般会在安装 git 时就默认装了,可以通过which git-http-backend
查看是否安装,没有的话需要安装git-core
。默认安装位置在/usr/lib/git-core/git-http-backend
,需要查看自己的路径,后边会用。fcgiwrap
:一个用于将 FastCGI 转换为 HTTP 协议的工具。htpasswd
:用于创建和更新基于文本的认证文件,用于存储用户名和密码。它是apache2-utils
的一部分,可以通过which htpasswd
查看是否安装,没有的话需要安装apache2-utils
。
nginx 配置与说明
最初的设置是这样的:
server
{
# 这里用于将 http 请求重定向到 https,是一种常用的方式
listen 80;
server_name git.player.com;
return 301 https://$host$request_uri;
}
server
{
server_name git.player.com;
listen 443 ssl;
ssl_certificate /etc/letsencrypt/live/git.player.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/git.player.com/privkey.pem;
location / {
include fastcgi_params;
# git-http-backend的路径,每次请求都会转发到这个脚本
fastcgi_param SCRIPT_FILENAME /usr/lib/git-core/git-http-backend;
# git-http-backend默认只能访问目录下有git-daemon-export-ok的仓库
# 这里设置为可以访问所有仓库
fastcgi_param GIT_HTTP_EXPORT_ALL "";
# git仓库根目录
fastcgi_param GIT_PROJECT_ROOT /home/git/;
fastcgi_param PATH_INFO $uri;
# 如果有认证,传递给cgi脚本git-http-backend
fastcgi_param REMOTE_USER $remote_user;
fastcgi_pass unix:/var/run/fcgiwrap.socket;
}
}
nginx 重新加载配置之后,我们就可以正常git clone https://git.player.com/test.git
了。但当我们 push 时,会出现 403 错误:
$ git push
fatal: unable to access 'https://git.player.com/test.git/': The requested URL returned error: 403
为了解决这个错误,我们可以在 git-http-backend
的官网文档 上找到这样的一段描述:
By default, only the upload-pack service is enabled, which serves git fetch-pack and git ls-remote clients, which are invoked from git fetch, git pull, and git clone. If the client is authenticated, the receive-pack service is enabled, which serves git send-pack clients, which is invoked from git push.
第一次读这段话可能会有些不知所云,这是因为我们对这里提到的 upload-pack/fetch-pack/receive-pack/send-pack
这几个概念还没有什么认识。但是我们大抵可以猜出来,默认情况下,只有认证的用户才可以 push 代码。
我们可以在仓库中执行 git config http.receivepack true
来开启 push 权限,但是这样的话,所有人都可以 push
代码了,这显然不是我们想要的。我们可以通过 git config http.receivepack false
来关闭 push 权限,这样的话,所有人都不能 push
代码了,这也不是我们想要的。那么,我们应该怎么做呢?更好的做法是这样的:
$HTTP["querystring"] =~ "service=git-receive-pack" {
include "git-auth.conf"
}
$HTTP["url"] =~ "^/git/.*/git-receive-pack$" {
include "git-auth.conf"
}
看上去挺简单,但是想要理解为什么这样配置,就必须了解下 Git 的内部原理。正如上面 git-http-backend 文档上的那段描述,当 Git 客户端执行
git fetch/git pull/git clone
时,会调用 upload-pack
服务,当执行 git push
时,会调用 receive-pack
服务。我们可以查看 nginx 的访问日志,目录在/var/log/nginx/access.log
:
执行 git clone:
[27/Nov/2018:22:18:00] "GET /test.git/info/refs?service=git-upload-pack HTTP/1.1" 200 363 "-" "git/1.9.1"
[27/Nov/2018:22:18:00] "POST /test.git/git-upload-pack HTTP/1.1" 200 306 "-" "git/1.9.1"
执行 git pull:
[27/Nov/2018:22:20:25] "GET /test.git/info/refs?service=git-upload-pack HTTP/1.1" 200 363 "-" "git/1.9.1"
[27/Nov/2018:22:20:25] "POST /test.git/git-upload-pack HTTP/1.1" 200 551 "-" "git/1.9.1"
执行 git push:
[27/Nov/2018:22:19:33] "GET /test.git/info/refs?service=git-receive-pack HTTP/1.1" 401 204 "-" "git/1.9.1"
admin [27/Nov/2018:22:19:33] "GET /test.git/info/refs?service=git-receive-pack HTTP/1.1" 200 193 "-" "git/1.9.1"
admin [27/Nov/2018:22:19:33] "POST /test.git/git-receive-pack HTTP/1.1" 200 63 "-" "git/1.9.1"
可以看到执行 clone 和 pull 请求的接口是一样的,先请求 /info/refs?service=git-upload-pack
,然后再请求
/git-upload-pack
;而 push 是先请求 /info/refs?service=git-receive-pack
,然后再请求
/git-receive-pack
,所以在上面的的配置中我们看到了两条记录,如果要对 push 做访问控制,那么对这两个请求都要限制。关于 Git 传输的原理可以参考 《Pro
Git》的 Git
内部原理 - 传输协议 这一节。
于是,我们对 nginx 的配置文件进行修改:
server
{
listen 80;
server_name git.player.com;
return 301 https://$host$request_uri;
}
server
{
server_name git.player.com;
listen 443 ssl;
ssl_certificate /etc/letsencrypt/live/git.player.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/git.player.com/privkey.pem;
location @auth {
auth_basic "Git Server";
# 生成的认证文件,下文专门说明
auth_basic_user_file /etc/nginx/conf.d/git.htpasswd;
include fastcgi_params;
# git-http-backend的路径,每次请求都会转发到这个脚本
fastcgi_param SCRIPT_FILENAME /usr/lib/git-core/git-http-backend;
# git-http-backend默认只能访问目录下有git-daemon-export-ok的仓库
# 这里设置为可以访问所有仓库
fastcgi_param GIT_HTTP_EXPORT_ALL "";
# git仓库根目录
fastcgi_param GIT_PROJECT_ROOT /home/git/;
fastcgi_param PATH_INFO $uri;
# 如果有认证,传递给cgi脚本git-http-backend
fastcgi_param REMOTE_USER $remote_user;
fastcgi_pass unix:/var/run/fcgiwrap.socket;
}
location / {
error_page 418 = @auth;
if ( $query_string = "service=git-receive-pack" ) {
return 418;
}
if ( $uri ~ "git-receive-pack$" ) {
return 418;
}
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME /usr/lib/git-core/git-http-backend;
fastcgi_param GIT_HTTP_EXPORT_ALL "";
fastcgi_param GIT_PROJECT_ROOT /home/git/;
fastcgi_param PATH_INFO $uri;
fastcgi_param REMOTE_USER $remote_user;
fastcgi_pass unix:/var/run/fcgiwrap.socket;
}
}
这里我们可以看到,我们对/git/.*/git-receive-pack
和/info/refs?service=git-receive-pack
进行了限制,如果没有认证,就会返回
418 错误,然后 nginx 会将其转发到@auth
这个 location,这个 location
会对请求进行认证,如果认证成功,就会将请求转发到fastcgi_pass
指定的脚本,也就是git-http-backend
,这样就可以正常 push 了。
为什么不直接在
/git/.*/git-receive-pack
和/info/refs?service=git-receive-pack
这两个 location 中进行认证呢?如果这样做的话,会导致 clone 和 pull 也需要认证,那多不合适了~418 是什么,http 协议没见过这个啊?这是 http 协议中的一个状态码,叫做 I’m a teapot(我是一个茶壶),这个状态码是作为一个愚人节玩笑而创建的,但是它确实存在,可以参考 RFC 2324。我们这里用它来做认证标识,以免和别的状态码冲突。当然你也可以自己规定状态码。
认证文件
nginx 的配置中我们用到了认证文件,它的生成是这样的:
# 创建认证文件并添加第一个用户
htpasswd -cd <文件名> <用户名> <密码>
# 在已有的文件中继续添加用户
htpasswd -d <文件名> <用户名> <密码>
到了这里,我们重启 nginx 然后进行测试,也许就可以正常使用了……吗?
问题
我在测试的时候发现了一个问题:
$ git clone https://git.player.com/test.git
Cloning into 'test'...
fatal: repository 'https://git.player.com/test.git/' not found
看起来很匪夷所思:我们明明把仓库放进了指定目录啊,nginx 也已经配置好了,为什么就是找不到呢?我在网上找了很久也没找到答案。最后自己发现了问题所在:仓库文件夹权限不对。
Linux 权限描述
当我们使用ls -l
命令查看文件夹时,会看到类似这样的输出:
drwxr-xr-x 2 git git 4096 Nov 27 22:19 test.git
其中前 10 个字符就是用来描述该目录权限的。
- 第一位字符代表文件类型
d
表示目录-
表示文件l
表示链接文件b
表示块设备文件c
表示字符设备文件p
表示管道文件s
表示套接字文件。
- 接下来 9 个字符每 3 个一组,分别表示文件所有者、文件所有者所在组、其他用户对该文件的权限。
r
表示可读w
表示可写x
表示可执行-
表示无对应权限
其中,读权限和写权限时不言自明的,可是当我们在讨论文件夹的执行权限的时候,我们究竟在说什么?其实,这里的执行权限指的是进入该文件夹的权限。如果一个文件夹没有执行权限,那么我们就无法进入该文件夹,也就无法访问该文件夹下的文件,即使有对应权限。
回到这个问题,我们可以看到,我们仓库存放的文件夹的权限很有可能是没有对应读写或进入权限。所以修改办法也很简单,改权限。
解决方案
修改权限的方法有以下几种:
- 直接把对应库及其下的所有文件/文件夹对所有其他用户赋予读写执行权限(权限使用
chmod -R 777 test.git
)。缺点很明显:- 不安全,任何人都可以访问该文件夹下的文件,尤其是 root 和 nginx 用户,前者权限太高,一旦出错足以破坏一切,后者容易导致危险(如强制停止或内存泄漏时)
- 不方便。每次创建库都需要手动修改仓库及文件的权限,十分不便
- 不为 git 仓库设立单独用户,直接由原有用户或 root 用户接管一切。缺点:
- 如果使用原有用户,仓库会和其他文件混在一起,不方便管理,且容易误操作
- 如果使用 root 用户,权限太高,而且一般 root 不会设置为 ssh 登陆,这就意味着想简单创建一个库操作比较复杂
- 设立单独用户,将 nginx 的工作用户(一般为 www-data 或
root,在
/etc/nginx/nginx.conf
文件第一行规定)添加到该用户所在组。这种方式避免了文件混杂不方便管理的问题,也一定程度上避免了权限过高问题(注意,权限问题依然存在),可以说是一个比较好的解决办法。
这也就是上文我推荐为 git 仓库托管设立单独用户的原因。因而,最后我选择了第三种方案。具体操作如下:
usermod -aG git root
usermod -aG git www-data
此时再进行测试,应该就可以正常使用了。
cgit 拥抱图形化
到了 http(s)服务这里,我们在命令行里进行 git 操作的需求已经基本得到了满足。但是,生命不息,折腾不止,我们发现有一个能图形化显示仓库的界面、并且要仍然能在命令行里进行仓库操作,最是舒坦。
- GitList 的界面看起来不错,而且能展示源码、clone 链接之类的,整体非常像 github 的界面,可惜使用的是我不会的 php 语言,而且没有找到详细一些的安装使用教程
- cgit 是一个用纯 C 语言开发的一个 git 裸库展示,虽然界面看起来比较古早,但功能简单、强大,能展示源码、自由切换分支、方便地查看提交历史(diss 一下 github,github 查看提交历史看起来真的很不方便很不直观)。诸如Linux 内核等项目都在使用。
选择了 cgit,我找到了又一位大佬的博客,非常详细。需要注意的是,我们仍然需要一个专门的用户。
依赖
nginx
/git
/vim
等工具不必赘述,还有一些依赖项需要安装:
# apache2-utils是用其htpasswd命令创建认证文件的
# fcgiwrap是用于将 FastCGI 转换为 HTTP 协议的工具
# 这两个工具上文均已提到和使用,不再赘述
sudo apt update
sudo apt install -y apache2-utils fcgiwrap
# 编译过程中需要openssl的头文件
sudo apt install -y libssl-dev
# 建议为cgit提供lua支持,用来进行个性化的设置
# 本文以 lua5.1 为例
sudo apt install liblua5.1-0 liblua5.1-0-dbg liblua5.1-dev lua5.1
cgit 安装
cgit 最近的正式发行版已经好几年了,但是它的开发仍然很活跃,所以建议从它的 git 仓库中获取最新的代码,而非直接安装:
git clone https://git.zx2c4.com/cgit
cd cgit
git submodule init
git submodule update
在仓库目录下创建cgit.conf
文件,用来存放 cgit 构建时可以覆盖的配置:
sed -n '3,31p' Makefile > cgit.conf
我们可以按需编辑之:
CGIT_VERSION = v1.2.3
CGIT_SCRIPT_NAME = cgit.cgi
CGIT_SCRIPT_PATH = /var/www/cgit # 本文只改了这里
CGIT_DATA_PATH = $(CGIT_SCRIPT_PATH)
CGIT_CONFIG = /etc/cgitrc # 默认配置文件路径
CACHE_ROOT = /var/cache/cgit
prefix = /usr/local
libdir = $(prefix)/lib
filterdir = $(libdir)/cgit/filters
docdir = $(prefix)/share/doc/cgit
htmldir = $(docdir)
pdfdir = $(docdir)
mandir = $(prefix)/share/man
SHA1_HEADER = <openssl/sha.h>
GIT_VER = 2.39.0
GIT_URL = https://www.kernel.org/pub/software/scm/git/git-$(GIT_VER).tar.xz
INSTALL = install
COPYTREE = cp -r
MAN5_TXT = $(wildcard *.5.txt)
MAN_TXT = $(MAN5_TXT)
DOC_MAN5 = $(patsubst %.txt,%,$(MAN5_TXT))
DOC_HTML = $(patsubst %.txt,%.html,$(MAN_TXT))
DOC_PDF = $(patsubst %.txt,%.pdf,$(MAN_TXT))
ASCIIDOC = asciidoc
ASCIIDOC_EXTRA =
ASCIIDOC_HTML = xhtml11
ASCIIDOC_COMMON = $(ASCIIDOC) $(ASCIIDOC_EXTRA)
TXT_TO_HTML = $(ASCIIDOC_COMMON) -b $(ASCIIDOC_HTML)
编译 && 安装:
# 如果不需要lua支持
make NO_LUA=1
# 有lua支持的话
make LUA_PKGCONFIG=lua5.1
# 安装,注意一下安装路径
# 不妨将输出写到日志里,以便查看
sudo make install | tee install.log
nginx 配置
首先,参照这里生成一个自己的认证文件,再继续往下看。
在/etc/nginx/git-http-backend.conf
中写入以下内容,注意把域名、ssl 路径、htpasswd 认证文件换成自己的:
# /etc/nginx/git-http-backend.conf
fastcgi_pass unix:/var/run/fcgiwrap.socket;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME /usr/lib/git-core/git-http-backend;
fastcgi_param GIT_HTTP_EXPORT_ALL "";
fastcgi_param GIT_PROJECT_ROOT /home/git;
fastcgi_param PATH_INFO $1;
fastcgi_param REMOTE_USER $remote_user;
而后,在/etc/nginx/conf.d/cgit.conf
中写:
# /etc/nginx/conf.d/cgit.conf
server {
listen 80;
server_name git.player.com;
return 301 https://$server_name$request_uri;
}
server {
server_name git.player.com;
listen 443 ssl http2;
ssl_certificate /etc/letsencrypt/live/git.player.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/git.player.com/privkey.pem;
# SSL Security
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256;
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:10m;
# Site Log path
access_log /var/log/nginx/cgit-access.log;
error_log /var/log/nginx/cgit-error.log;
root /var/www/cgit;
try_files $uri @cgit;
client_max_body_size 10m;
location @cgit {
include fastcgi_params;
# cgit's CGI script path
fastcgi_param SCRIPT_FILENAME /var/www/cgit/cgit.cgi;
fastcgi_param DOCUMENT_ROOT /usr/lib/git-core;
fastcgi_pass unix:/var/run/fcgiwrap.socket;
fastcgi_param PATH_INFO $uri;
fastcgi_param QUERY_STRING $args;
fastcgi_param HTTP_HOST $server_name;
fastcgi_param GIT_HTTP_EXPORT_ALL "";
fastcgi_param GIT_PROJECT_ROOT /home/git;
if ($arg_service = git-receive-pack) {
rewrite (/.*) /git_write/$1 last;
}
if ($uri ~ ^/.*/git-receive-pack$) {
rewrite (/.*) /git_write/$1 last;
}
if ($arg_service = git-upload-pack) {
rewrite (/.*) /git_read/$1 last;
}
if ($uri ~ ^/.*/git-upload-pack$) {
rewrite (/.*) /git_read/$1 last;
}
}
location ~ /git_read/(.*) {
include git-http-backend.conf;
}
location ~ /git_write/(.*) {
# HTTP Basic Authentication
auth_basic "Authentication Required To Push";
auth_basic_user_file /etc/nginx/conf.d/git.htpasswd;
include git-http-backend.conf;
}
}
最后,重启 nginx 服务:
sudo nginx -s reload
看吧,一个个仓库,向我们列队走来!
cgit 高级配置
可以安装一些包,用于 cgit 的代码高亮、Markdown 渲染、Gravatar 头像渲染等:
sudo apt install -y python3-docutils python3-markdown highlight python3-pygments
# 编译安装 LuaoSSL
# https://25thandclement.com/~william/projects/luaossl.html
git clone https://github.com/wahern/luaossl.git && cd luaossl
make LUAPKG=lua5.1
sudo make install LUAPKG=lua5.1
sudo mkdir -p /usr/local/share/cgit
sudo ln -s /usr/local/lib/cgit/filters /usr/local/share/cgit/filters
sudo chown -R www-data:www-data /usr/local/share/cgit/
# 给下面使用 filter api 的脚本赋予可执行权限,比如
sudo chmod +x /usr/local/share/cgit/filters/email-gravatar.lua
然后编辑上边我们已经指定的配置文件/etc/cgitrc
,更多配置项参见cgitrc.5.txt。
# /etc/cgitrc
# 包含 cgit 的所有运行时设置
# 格式 NAME=VALUE
# 以 "#" 开头的行是注释
# 全局配置
css=/cgit.css
logo=/cgit.png
favicon=/favicon.ico
#footer=
virtual-root=/
# 禁用哑克隆
enable-http-clone=0
# Smart HTTP
# 记得改成自己的链接
clone-url=https://git.player.com/$CGIT_REPO_URL
# 首页标题显示的内容,改成你想要的
root-title=GIT.PLAYER.COM
root-desc=YOUR.WORDS
# 在首页展示的介绍信息,可用md/man/html等
# 详参/usr/local/share/cgit/filters/about-formatting.sh
root-readme=/var/www/cgit/README.md
# 建议配置
enable-index-owner=1
enable-index-links=1
enable-blame=1
enable-log-filecount=1
enable-log-linecount=1
enable-commit-graph=1
# 禁止搜素引擎索引
robots=noindex, nofollow
branch-sort=age
commit-sort=date
max-stats=quarter
snapshots=tar.gz zip
# 使用 RAM 的缓存大小 单位 MB
cache-size=1024
# 代码高亮
source-filter=/usr/local/share/cgit/filters/syntax-highlighting.py
# 格式化贡献者,显示Gravatar头像
email-filter=lua:/usr/local/share/cgit/filters/email-gravatar.lua
# 格式化 about 页面
about-filter=/usr/local/share/cgit/filters/about-formatting.sh
readme=:README.md
readme=:readme.md
readme=:README.txt
readme=:readme.txt
readme=:README
readme=:readme
# MIME 类型
mimetype.html=text/html
mimetype.gif=image/gif
mimetype.jpg=image/jpeg
mimetype.jpeg=image/jpeg
mimetype.png=image/png
mimetype.webp=image/webp
mimetype.pdf=application/pdf
mimetype.svg=image/svg+xml
# 移除 .git 后缀,很有必要
remove-suffix=1
# 扫描路径
scan-path=/home/git
# 每个存储库配置
#repo.url=reponame
#repo.path=/home/git/reponame.git
#repo.desc=Some description here
#repo.owner=Owner Name
#repo.logo=/repo-logo.png
高亮风格
在上面的配置文件里,我们使用了Pygments的代码高亮。其默认使用的高亮是 pastie,我们可以根据自己的喜好修改高亮风格。
首先,看看有哪些可用的高亮风格。
# 查看可用的高亮风格
pygmentize -L styles
# 编辑我们使用的代码高亮脚本
sudo vim /usr/local/share/cgit/filters/syntax-highlighting.py
Gravatar 头像
在上述配置中,我们使用了 Gravatar 头像。Gravatar 是一个全球通用的头像服务,根据你使用的邮箱(而非用户身份)来为你提供头像。换言之,只要你的邮箱注册了 Gravatar,那么你在任何一个支持 Gravatar 的网站上都可以使用你的 Gravatar 头像。这对于我们这种不想做登录、又想展示用户的网站来说,是一个很好的选择。
由于 Gravatar 中文官网访问比较慢(不知道为什么,英文官网我挂了梯子还上不去),我们可以使用国内的镜像服务。这里有一篇常用镜像服务的博客。我使用的是Cravatar。
使用流程都是基本一致的,在这个网站上注册账号,上传头像,然后根据网站提供的 API
来获取。一般方式为https://域名.com/avatar/邮箱的md5值
。在我们使用的脚本/usr/local/share/cgit/filters/email-gravatar.lua
中,将原有的域名替换为我们使用的域名即可。
cgit 的官网上不仅能实现 Gravatar 头像,还能在鼠标移动到头像上的时候以大图显示。这不是原生功能,而是需要动 lua 脚本自己实现。原博客大佬在 cgit 的邮件列表找到了实现方式。
sudo vim /usr/local/share/cgit/filters/email-libravatar-korg.lua
在脚本中写入以下内容:
-- This script may be used with the email-filter or repo.email-filter settings in cgitrc.
-- It adds gravatar icons to author names. It is designed to be used with the lua:
-- prefix in filters. It is much faster than the corresponding python script.
--
-- Requirements:
-- luaossl
-- <http://25thandclement.com/~william/projects/luaossl.html>
--
local digest = require("openssl.digest")
function md5_hex(input)
local b = digest.new("md5"):final(input)
local x = ""
for i = 1, #b do
x = x .. string.format("%.2x", string.byte(b, i))
end
return x
end
function filter_open(email, page)
buffer = ""
md5 = md5_hex(email:sub(2, -2):lower())
end
function filter_close()
html("<span class='libravatar'>" ..
"<img class='inline' src='//www.gravatar.com/avatar/" .. md5 .. "?s=13&d=retro' />" ..
"<img class='onhover' src='//www.gravatar.com/avatar/" .. md5 .. "?s=128&d=retro' />" ..
"</span>" .. buffer)
return 0
end
function filter_write(str)
buffer = buffer .. str
end
只有 lua 还不够,我们需要将以下内容添加到/var/www/cgit/cgit.css
中:
/* libgravatar */
div#cgit span.libravatar img.onhover {
display: none;
border: 1px solid gray;
padding: 0px;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
width: 128px;
height: 128px;
}
div#cgit span.libravatar img.inline {
-webkit-border-radius: 3px;
-moz-border-radius: 3px;
border-radius: 3px;
width: 13px;
height: 13px;
margin-right: 0.4em;
opacity: 0.9;
}
div#cgit span.libravatar:hover > img.onhover {
display: block;
position: absolute;
margin-left: 1.5em;
background-color: #eeeeee;
box-shadow: 5px 5px 3px #bbb;
}
而后,修改/etc/cgitrc
中的email-filter
的值为我们新建的脚本lua:/usr/local/share/cgit/filters/email-libravatar-korg.lua
,就好了。
添加 README
在/etc/cgitrc
中,我们指定了root-readme
,这是用来在网站主页展示 README 的。我们可以编写一个 README
文件,然后在/etc/cgitrc
中指明它的路径。README
可以使用markdown
/man
/rst
/html/
txt`等格式。
我使用的是markdown
格式,在/var/www/cgit/
中创建了一个README.md
文件,然后在/etc/cgitrc
中指明了路径。
样式修改
对于 cgit 显示出来的界面,你可能并不太满意(比如我就嫌界面的字太小、颜色不舒适啥的)。修改办法也很简单,找到/var/www/cgit/cgit.css
文件,然后修改之。
而对于我们的 Markdown 或者别的什么语言写的 README,如果觉得渲染效果不好,都是可以自己去修改的。
找到/usr/local/share/cgit/filters/about-formatting.sh
文件,我们会看到以下内容:
#!/bin/sh
# This may be used with the about-filter or repo.about-filter setting in cgitrc.
# It passes formatting of about pages to differing programs, depending on the usage.
# Markdown support requires python and markdown-python.
# RestructuredText support requires python and docutils.
# Man page support requires groff.
# The following environment variables can be used to retrieve the configuration
# of the repository for which this script is called:
# CGIT_REPO_URL ( = repo.url setting )
# CGIT_REPO_NAME ( = repo.name setting )
# CGIT_REPO_PATH ( = repo.path setting )
# CGIT_REPO_OWNER ( = repo.owner setting )
# CGIT_REPO_DEFBRANCH ( = repo.defbranch setting )
# CGIT_REPO_SECTION ( = section setting )
# CGIT_REPO_CLONE_URL ( = repo.clone-url setting )
cd "$(dirname $0)/html-converters/"
case "$(printf '%s' "$1" | tr '[:upper:]' '[:lower:]')" in
*.markdown|*.mdown|*.md|*.mkd) exec ./md2html; ;;
*.rst) exec ./rst2html; ;;
*.[1-9]) exec ./man2html; ;;
*.htm|*.html) exec cat; ;;
*.txt|*) exec ./txt2html; ;;
esac
从这里可以看到,负责渲染的是/usr/local/share/cgit/filters/html-converters
文件夹中的对于脚本。我们可以自己修改这些脚本,或者自己添加新的脚本,来实现自己想要的渲染效果。
当然,除了网站主页的 README,我们还可以在每个仓库的主页上添加 README。只需要在仓库的根目录下添加 README 文件即可。然后网页上仓库的标签页就会有“About”标签页,显示 README.md 的内容。
到这里,cgit 的配置就基本完成了。重启 nginx 服务,然后访问你的域名,就可以看到一个很漂亮的界面了;命令行里,也可以 clone、push、fetch、pull 我们托管的仓库。大功告成!
其他存在的问题
除了上述问题已经解决之外,还有一些问题依然存在:
- 不管是 nginx 还是 git,在使用 http 上传的时候都会有一定的缓冲区限制,如果上传文件过大或累计多个 commit
才上传,很可能被拒收导致上传失败。这个问题在 github 上也存在,但是 github
的缓冲区限制比较大,一般不会出现这个问题;我们的服务器配置比较低,所以这个问题就比较严重了。解决办法是在 nginx
的配置文件中添加
client_max_body_size 100m;
,这样就可以将缓冲区限制扩大到 100M,一般来说足够了。 - 即使解决了缓冲区大小,偶尔也会被拒收,原因尚未查清
- 尚未能通过访问特定链接来实现 git 仓库的创建。据说是用 nginx 调用脚本,但暂时没弄出来
这篇博客前前后后有二十多天了,有空再折腾吧。看电视去也~
2024.1.3