Git仓库的存档方式

最近在将公司的基础设施从自托管的 Gitlab + JIRA + 单独购买的看板服务迁移到 Github 上,因为打算上 CI/CD,并且项目管理之类的也打算统一管理,虽然 Gitlab 新版也有相关的集成,CI/CD 也支持,但是比较吃性能,而且环境搭建也比较费劲,所以讨论了几次之后决定直接买服务完事。

持续集成和项目管理花了两天配置好了,但是原有这个 Gitlab 也有些年纪了,里面有好几十个零散的历史项目,放在不同的 Group 下。这些代码肯定不会再用了,但是也是公司资产的一部分,得迁移到 Github 上,但是逐个手动推送也太累了,于是琢磨了下有没有什么好办法存档一下。

首先 git 是通过 branch 和 tag 来指向一个 ref,然后 ref 再指向一个父 ref,这样组成一颗树。这样的话我是不是可以将多个仓库的 branch 放在一起,达到合并仓库的目的呢,不同仓库和 Group 的分支则带上 prefix 用于区分。

稍作测试后发现只要将 remote 指向本地文件夹,然后 fetch 到当前仓库,再手动把所有 tag 和 branch 创建到本地仓库就好了,花了半天写了个批处理干这个活,虽然有点慢,不过只是一次性处理,慢点也无所谓了。

@echo off
cls
setlocal enabledelayedexpansion

set root=%cd%
for /f "delims=" %%q in ('dir /a:d /b') do (
	cd %%q
	for /f "delims=" %%p in ('dir /a:d /b') do (
		cd %%p
		call :clone "%%q" "%%p" "!root!"
		cd ..
	)
	pushd !root!
	REM git gc -q --prune=now
	popd
	cd ..
)
pause
goto end

:clone
set first=%~1
set second=%~2
set rootex=%~3\
if NOT defined first goto end
if defined second (
	set winpath=%~1\%~2
	set gitpath=%~1/%~n2
) else (
	set winpath=%~1
	set gitpath=%~1
)
title Processing: !winpath!
call :process "!rootex!" "%~dp0" "!gitpath!" "!winpath!"
goto end

:process
set origpath=%~1
set repopath=%~2
set remote=%~3
set local=%~4
set remoteurl=
for /f "tokens=3" %%u in (.\config) do (
	echo %%u|findstr "http https git" >nul
	if !errorlevel! equ 0 set remoteurl=%%u
)
pushd !repopath!
if defined remoteurl (
	git remote add !remote! !remoteurl!
	:fetching
	git fetch -q --no-tags !remote! 2>nul
	if errorlevel 1 goto :fetching
)
popd
git repack -q
for /f "delims=	, tokens=2" %%r in (.\info\refs) do (
	for /f "delims=/, tokens=2,3" %%a in ("%%r") do (
		pushd !repopath!
		if %%a==heads (
			call :createbranch !origpath! !remote! !local! %%b %%r branch
			if defined remoteurl git branch -q -u refs/remotes/!remote!/%%b !remote!/%%b
		)
		if %%a==tags call :createbranch !origpath! !remote! !local! %%b %%r tag refs/tags/
		popd
	)
)
goto end

:createbranch
echo [info] %2 %6:%4 remote:%5
git fetch -q --no-tags %1%3 +%7%4:%7%2/%4
goto end

:end
sh

Gitlab 运行 dump 之后会 dump 出一个 tar 文件,在其中的 repositories 文件夹里初始化一个 git 仓库,然后把子文件夹里的所有 zip 文件解压到同目录下,在运行前述的批处理文件即可。存档完成后所有子目录下的仓库 branch/tag 会复制到刚才初始化的 git 仓库的 "子目录名称/仓库名称/branch 或 tag 名称" 下。

这时候只要把这个仓库全量推送到 Github 上并设置成只读就完事了,另外因为合并成了一个库,所以有些相同文件或者相似文件是可以压缩掉冗余的,从结果来说,原来 80 多个仓库,共 3G 多的容量仓库,合并完之后缩小了 500m 左右,再把大文件分离出来塞 lfs,就剩下不到 500m 了,算是个意外的好处。

除此之外其实还可以用 fossil 再存档一份,这个 scm 可以从 git 无损转存过去,结果是一个 sqlite 数据库,sqlite 自己的仓库也在用这个管理,对特定结构的源码压缩率会比 git 更好,而且是 self-contained 的,单个可执行文件包含 webui、命令行管理、远程服务器、项目管理等等功能,这一点很对我胃口,可惜没什么人用。