---
url: /article/26ytrk59/index.md
---
## 前言

这两天 Github 开源界发生了几件大事：

* Clash\_for\_windows 的作者宣布停更，原因不详；
* Clash 内核作者删库，原因不详；
* **我的账号被封**，原因不详；

![suspended](https://cdn.jsdelivr.net/gh/Ma-yeah/picx-images-hosting@master/2023-11-04/suspended.5gl47o7oq8w0.webp)

这可把我难受的，虽然我那仓库中的代码对开源没啥贡献，但好歹也是我从学习到工作，一步步趟过来的，承载了不少的记忆。

**拥有的时候从不在意，失去之后追悔莫及。**

不过好在之前有一部分代码放在了 Gitee 上，本地仓库也有一部分代码，大约有 95% 的仓库还能恢复，恢复不了的只能算了，毕竟记忆也不都是完整的，损失尚可接受。

这次号封的太突然了，事先连个邮件招呼都没有。搜索了下发现类似的情况还挺多。有尝试过找回，不过应该希望不大，好多人等了几个月最终也没有解封。

同样突然封号的事情在俄乌冲突期间也发生过，Github 当时封禁了很多俄罗斯的账号。虽说开源项目无国界，可平台政治有立场。

为了尽量避免突然封号导致的损失，将仓库代码同步到各个平台是多么重要的一件事，毕竟系统上线都还考虑异地容灾的问题。

## 1. 选择平台

当前代码版本控制主要使用到 Git ，而支持 Git 的主流平台大致有如下几个：

* 国外平台：Github、Gitlab；
* 国内平台：Gitee、Coding、阿里云效、极狐Gitlab；

最终我的选择是 Github、Gitlab 和 Gitee。

> 这么选的理由是什么？

首先，我的目标是要多个平台，且要包含国内和国外的平台。

* Github 的特点是拥有非常多的国内外开源项目，这是一定是要有的。
* Gitlab 的特点是对代码分析，CI/CD 等方面的支持比较好，并且支持自建，企业内部使用比较多，多熟悉有好处。
* Gitee 的特点是国内使用比较多，有挺多国内开源项目，访问方便且速度快。
* Coding 和 阿里云效 主要做 saas 服务，小公司一般会选择使用，不是很必要。
* 极狐Gitlab 它拥有 Gitlab 的功能，又是国内平台，这两个特点相当于 Gitlab 和 Gitee，故不必选择。

> 这里说一下 Gitlab 和 极狐Gitlab 的联系。

为积极响应国家“十四五规划和2035年远景目标纲要”指导精神，进一步推动中国开源、开放GitOps技术在各“产学研”领域的规范化实施和落地。
由红杉宽带跨境数字产业基金高成资本投资，成立极狐信息技术(湖北)有限公司，加上 Gitlab 官方授权的品牌技术加持，就是极狐Gitlab。
它的目标用户群体是中国大陆及港澳地区的开发者，由中国本地团队管理，服务器在中国大陆，数据中心和运营商在中国香港，因此极狐Gitlab 也被称为“中国版 Gitlab”。

OK，现在已经确定了要使用的平台，接下来就是琢磨怎么在这三个平台之间同步了。

## 2. 同步模式

首先要明确一点，同步是个什么需求。`同步`这个词的意思并不是很清晰，既可以指一次性同步，也可以指持续性同步。那么可以细化出两种理解：

* 仓库导入(import)：这是一次性同步，表示将其他平台的仓库导入到当前平台中，即完成了一次同步。导入完成后，后续如果在原平台有更新，当前平台不会同步更新。
* 仓库镜像(mirror)：这表示目标仓库完全是源仓库的镜像，源仓库中有任何变化，都会反应在目标仓库中。也就是说如果在原平台有更新，当前平台也会同步更新。

很明显，我们的需求是仓库镜像(mirror)。

这里再提一嘴，Gitee 和 Gitlab 的导入(import)功能都做挺好的，它们都支持在新建项目(仓库)时，选择导入，然后登录 Github 账号，批量选择需要导入的仓库，还可以选择私有仓库，一键导入非常方便。
而 Github 就稍显弱一点了，它只能通过 URL 导入，这自然就只能导入公开仓库了，私有仓库是访问不到的。

而仓库镜像(mirror)又有两种模式：

* `push 模式`：把当前平台选定仓库中的代码推送到另一个平台选定的仓库中，在原仓库配置。Gitlab 和 Gitee 都支持这种模式。这个功能 Gitlab 是免费支持的，但 Gitee 是限时开放至 2023 年 12 月 31 日，届时会更新产品策略，不确定是否会变成付费功能。
* `pull 模式`：把另一个平台选定仓库中的代码拉取到当前平台选定的仓库中，在目标仓库和源仓库配置(Webhook)。当前仅 Gitee 支持这种模式，同样的是限时开放至 2023 年 12 月 31 日。

而 Github 则不支持 push 模式，但可以通过配置 Webhook 实现 pull 模式，不过 Webhook 需要有额外的三方服务来支持处理 hook 请求，弄起来不是很方便，而且也不是非要这么做，所以这里就不细究了。

下面详细说说 push 和 poll 这两种模式。

### 2.1 push 模式

例如，我在 Gitlab 上创建了一个仓库`sync-repo`，我想把它导入到 Github 上，同时后续如果在 Gitlab 上有新的提交，Github 也会同步提交记录。

这里我在 Gitlab 仓库初始化时勾选了自描述文件，也就是会创建 README.md 文件。

![01\_2](https://cdn.jsdelivr.net/gh/Ma-yeah/picx-images-hosting@master/2023-11-04/01_2.1_01.3c081hwrihy0.webp)

仓库创建完成，`README.md`文件内容如下。

![01\_2](https://cdn.jsdelivr.net/gh/Ma-yeah/picx-images-hosting@master/2023-11-04/01_2.1_02.1c1xicauclq8.webp)

接下来在 Github 上也创建一个仓库`sync-repo`，但是不创建 README.md 文件，也就是一个空仓库不包含任何文件，因为一会我们要把 Gitlab 上的 README.md 文件同步过来。

![02\_2](https://cdn.jsdelivr.net/gh/Ma-yeah/picx-images-hosting@master/2023-11-04/02_2.1_03.7ja9fszt8yo.webp)

接着就需要在 Github 上创建一个私人令牌。
登录自己的 Github 账号之后，在主页右上角点击头像，选择`Settings`，然后在左侧菜单最底部选择`Developer settings`，再选择`Personal access tokens`，点击`Tokens(classic)`，接着点击`Generate new token`，选择`Generate new token(classic)`。
然后在弹出的对话框中填写`Note`，接着勾选`repo`，`Expiration`那里是选择 token 的有效期，这个看个人需求，如果没有特别需求，直接就`no expiration`即永不过期。
然后拉到最下面点击`Generate token`，就得到了一个私人令牌，格式为`ghp_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX`。
注意这里生成的 token 只会显示一次，后续就看不到了，如果忘记了只能重新生成，所以最好把这个 token 复制下来，后面会多次用到。

![02\_2](https://cdn.jsdelivr.net/gh/Ma-yeah/picx-images-hosting@master/2023-11-04/02_2.1_04.4ireuuj1gb60.webp)

刚刚我们在 Github 上创建的仓库`sync-repo`，复制仓库 clone 地址`https://github.com/Ma-yeah/picx-images-hosting.git` ，注意选择 HTTPS 协议。
然后我们回到 Gitlab 平台的`sync-repo`中，在左侧菜单选择`设置`，然后选择`仓库`，接着展开`镜像仓库`一栏，添加新的。

要填写的地方有三个：

* `Git 仓库 URL`：这里把刚刚复制的 Github 仓库 clone 地址粘贴过来，另外需要注意在`https://`后面加上你的 Github 用户名并用`@`分隔。这里我的用户名是`Ma-yeah`，所以最终需要填写的是`https://Ma-yeah@github.com/Ma-yeah/picx-images-hosting.git`；
* `用户名`：这里填写的是 Github 用户名，也就是`Ma-yeah`；
* `密码`：这里填写的是刚刚在 Github 上创建的私人令牌，也就是`ghp_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX`。

![02\_2](https://cdn.jsdelivr.net/gh/Ma-yeah/picx-images-hosting@master/2023-11-04/02_2.1_05.236oz36a9veo.webp)

然后点击`镜像仓库`即为保存。这样一来我们就配置好了 Github 与 Gitlab 的关联，后续如果在 Gitlab 上有新的提交，Github 也会同步提交记录。
如果网络不好，自动同步可能会有一定的延迟或者失败，此时我们可以手动同步。

![02\_2](https://cdn.jsdelivr.net/gh/Ma-yeah/picx-images-hosting@master/2023-11-04/02_2.1_06.5s35ozj5v5k0.webp)

同步完成后，我们回到 Github 上的`sync-repo`仓库，可以看到已经同步过来了`README.md`文件。

![02\_2](https://cdn.jsdelivr.net/gh/Ma-yeah/picx-images-hosting@master/2023-11-04/02_2.1_07.69m7pb2n3x00.webp)

***

另外，如果想把 Gitlab 的仓库同步到 Gitee 上，也是同样的操作。在 Gitee 的设置中，创建私人令牌。
之后在 Gitlab 中的操作就一模一样了，这里不再赘述。

![02\_2](https://cdn.jsdelivr.net/gh/Ma-yeah/picx-images-hosting@master/2023-11-04/02_2.1_08.2wxvt7885ta0.webp)

***

如果想要把 Gitee 上的仓库同步到 Github 上就更简单了。
在 Gitee 上的指定仓库中，选择`设置` -> `仓库镜像管理` -> `添加镜像仓库`，登录了自己的 Github 账号之后，在`镜像仓库`中选定要同步到 Github 上的仓库，然后在`私人令牌`中填写上面的 Github 私人令牌，然后点击`添加`即可。

![02\_2](https://cdn.jsdelivr.net/gh/Ma-yeah/picx-images-hosting@master/2023-11-04/02_2.1_09.1ms7dti6jejk.webp)

***

这种模式的好处在于，可以同步私有仓库，相当于你把自家(Github)钥匙(token)给别人(Gitlab/Gitee)了，而这把钥匙可以开你家(Github)大门(公开仓库)和你的卧室门(私有仓库)。
Gitlab/Gitee 随时可以拿这个令牌主动访问 Github 上的仓库。

## 2.2 pull 模式

这里 pull 模式只有 Gitee 支持。例如，当我们需要把 Github 上的仓库同步到 Gitee 上时，使用了 pull 模式。
需要注意的是，这种模式下，在创建 Github 私人令牌时，不用勾选`repo`，而是需要勾选`admin:repo_hook`授权，也就是允许 Gitee 去 Github 的仓库中创建 Webhook。

如果 Github 的令牌没有`admin:repo_hook`授权，那么就需要[手动配置 Webhook](https://help.gitee.com/repository/settings/sync-between-gitee-github#%E5%A6%82%E4%BD%95%E6%89%8B%E5%8A%A8%E9%85%8D%E7%BD%AE-Webhook)。

![02\_2](https://cdn.jsdelivr.net/gh/Ma-yeah/picx-images-hosting@master/2023-11-04/02_2.2_01.2xuytxx44os0.webp)

因为 pull 模式实际上是在 Github 中填写了一个 Webhook 链接，也就是当我们向 Github 提交了代码，就会触发 Webhook 向 Gitee 发送一个 POST 请求，而 Gitee 是有相应的服务来处理这个请求的。
Gitee 就会把 Github 对应仓库重新 clone 覆盖本平台仓库。

![02\_2](https://cdn.jsdelivr.net/gh/Ma-yeah/picx-images-hosting@master/2023-11-04/02_2.2_02.45ml6r2nj3s0.webp)

需要注意的是，实测发现 Gitee 的 pull 模式同步延迟比较大，不过终究是会自动触发。
另外，实测发现，如果 Gitee 的仓库设置为私有时，向 github 提交代码，Gitee 不会触发自动同步，这个时候手动执行`更新`即可同步。
在 Gitea(一个 Gitee 的开源版本)中，有过用户报告说私有仓库的 Webhook 存在[自动触发问题](https://github.com/go-gitea/gitea/issues/9412)。

![02\_2](https://cdn.jsdelivr.net/gh/Ma-yeah/picx-images-hosting@master/2023-11-04/02_2.2_03.53bgupv6n7o0.webp)

Gitee 中的仓库镜像管理功能，在一个仓库中，只能存在一个 poll 的操作，但可以存在多个 push 的操作。
想想就可以知道为什么不能存在多个 poll 操作。因为多个源对同一个目标做覆盖(poll)这是无意义的，但可以把一个源推送(push)给多个目标，对于单个目标来说它们还是只有一个源，这并不冲突。

### 2.3 模式总结

值得注意的是，无论使用 push 模式还是 pull 模式，都会覆盖掉目标仓库，也就是每次触发都是全量更新的。
所以操作的时候千万要注意看清楚，别首次同步时搞错了仓库，把其他仓库的代码给覆盖了，那就找不回来了，除非你本地还有那个仓库的代码，再重新上传，否则就悲剧了，千万注意要做好备份。

## 3. 同步方案

通过上文的介绍，Github 原生不支持镜像仓库，所以不能直接向 Github 提交代码；Gitee 的镜像仓库只能与 Github 交互，但可以用 push 和 poll 模式；Gitlab 的镜像仓库公功能是通用的，但只能用 push 模式。
那么我们可以总结出如下几个同步方向：

* Gitlab -- push --> Github
* Gitlab -- push --> Gitee
* Gitee -- push --> Github
* Gitee <-- pull -- Github

当我们选择把代码提交到某一个平台，然后自动同步到其他平台，这个链路可以有三种：

1. Gitlab -- push --> Gitee -- push --> Github
2. Gitlab -- push --> Github -- pull --> Gitee
3. Gitlab -- push --> Github, Gitlab -- push --> Gitee

通过上文的介绍，由于 Gitee 的仓库镜像功能是限时的，到期是否会收费未可知，不应过于依赖，因此 Gitee 只适合放在链路的末尾，即随时可以剔除而不影响其他平台，所以方案 1 被排除。
又因为 Gtiee 对于私有仓库的 Webhook 存在自动触发问题，pull 模式使用会受限，所以方案 2 也被排除。

因此，我们最后选择方案 3，即始终向 Gtilab 提交代码，然后自动向 Github 和 Gitee 推送。

![02\_2](https://cdn.jsdelivr.net/gh/Ma-yeah/picx-images-hosting@master/2023-11-04/02_2.2_04.3zvioag5zl60.webp)

## 结语

如此一来，Gitlab 中的所有仓库都可以设置为私有，不对外暴露，然后同步到 Github 和 Gitee 上的仓库可以选择性公开或私有，对外暴露。

***

完结，撒花 ✿✿ヽ(°▽°)ノ✿
