我所在的团队在使用 GitHub。GitHub在中国大陆没有架设服务器。由于众所周知的原因,从大陆访问GitHub,速度相当感人。

我们fork了 UnrealEngine 仓库后,发现clone/pull的时候,不但速度难以忍受,还经常断线。于是我在办公室内网环境下做了GitHub的仓库镜像。一开始只支持了UnrealEngine,后来扩充到支持任意用户名下的任意仓库。

本文整理自我在团队内部撰写的文档,讲述搭建镜像的完整步骤。使用的操作系统为 Ubuntu Server 14.04.4 LTS

安装最新的Git

用 PPA 安装最新的 git 和 git-daemon-sysvinit,后者以 SysVinit 方式管理 git-daemon 服务。

sudo add-apt-repository ppa:git-core/ppa
sudo apt-get update
sudo apt-get install git git-daemon-sysvinit

创建Git服务用户

sudo adduser github

后续操作,除了需要 sudo 的,均以用户 github 来执行:

sudo su github

创建仓库存放目录

cd ~
mkdir -p mirror/repos/[username]

从GitHub Clone仓库

cd ~/mirror/repos/[username]
git clone --mirror https://github.com/[username]/[repo].git

配置git-daemon服务

修改 /etc/default/git-daemon 为:

# Defaults for git-daemon initscript
# sourced by /etc/init.d/git-daemon
# installed at /etc/default/git-daemon by the maintainer scripts

#
# This is a POSIX shell fragment
#

GIT_DAEMON_ENABLE=true
GIT_DAEMON_USER=github
GIT_DAEMON_BASE_PATH=/home/github/mirror/repos
GIT_DAEMON_DIRECTORY=/home/github/mirror/repos

# Additional options that are passed to the Daemon.
GIT_DAEMON_OPTIONS="--export-all"

其中 --export-all 选项将使 git-daemon 对 /home/github/mirror/repos 下的所有仓库提供服务。

重启git-daemon服务(需要先切换到管理员用户,执行后切换回github用户)

sudo service git-daemon restart

定时更新镜像仓库

设置 credential store

如果要镜像的仓库是private的,则需要authentication。因此需要设置一下账号密码的存储:

$ cd ~/mirror/repos/[username]/[repo].git
$ git config credential.helper store
$ git remote update
Username: <type your username>
Password: <type your password>

注意,密码(如果开启了2FA则密码是Personal Access Token)将被明文存储在 ~/.git-credentials 中。我的做法是为团队注册了一个单独的bot用户,设定只读权限,并分配一个只具有仓库访问权的Personal Access Token。

更详细的文档,见 git-credential-store

更新脚本

编辑 ~/mirror/pull.sh

#/bin/bash

set -e

cd ~/mirror

# Iterate through all .git directories
while IFS= read -r -d $'
#/bin/bash set -e cd ~/mirror # Iterate through all .git directories while IFS= read -r -d $'\0' REPO_DIR; do pushd $REPO_DIR > /dev/null echo -n "$(date +"%F %T") " echo -n "$REPO_DIR " git remote update popd > /dev/null done < <(find repos/ -name *.git -print0) 
' REPO_DIR; do pushd $REPO_DIR > /dev/null echo -n "$(date +"%F %T") " echo -n "$REPO_DIR " git remote update popd > /dev/null done < <(find repos/ -name *.git -print0)

此脚本会遍历 ~/mirror/repos 下的所有仓库目录,依次做 git remote update

赋予执行权限:

chmod u+x ~/mirror/pull.sh

编辑crontab

执行 crontab -e,在文件最后添加:

* * * * * bash -c ~/mirror/pull.sh >> ~/mirror/pull.log 2>&1
* * 1 * * rm -vf ~/mirror/pull.log

解读

  • 第1行将每分钟执行 pull.sh,从GitHub获取仓库的最新提交
  • 第2行将在每月1日凌晨清理日志文件 pull.log

使用

对于GitHub上的仓库 https://github.com/[username]/[repo].git ,其内网地址为:

git://[intranet-domain]/[username]/[repo].git

使用的时候,从内网镜像的地址clone,然后把 Push URL 修改为GitHub上的仓库地址:

git remote set-url --push origin https://github.com/[username]/[repo].git

运行 git remote -v,检查 Fetch URL 和 Push URL,将会看到:

origin    git://[intranet-domain]/rog2/UnrealEngine.git (fetch)
origin    https://github.com/rog2/UnrealEngine.git (push)

需要注意的是:

  • 镜像是 Read-Only 的,无法push到此镜像。
  • 镜像使用git协议,无加密和鉴权,在内网部署没有问题,但如果某日想要部署在公网上,必须引入VPN等机制,否则就等于是裸奔了。

https://zhiguang.me/2016/05/05/creating-github-mirror/