Post

GitHub Fork 仓库如何自动同步上游更新

GitHub Fork 仓库如何自动同步上游更新

很多人在 GitHub 上 fork 一个开源仓库之后,会以为自己的 fork 会自动跟随原仓库更新。实际上,GitHub 的 fork 默认不会自动同步上游仓库。原仓库更新后,你的 fork 不会自动变化,除非手动同步或配置自动同步任务。

本文介绍如何通过 GitHub Actions 实现 fork 的自动定时同步,这是目前最省心、最可靠的方案。

一、Fork、Origin、Upstream 是什么?

假设原仓库是:

1
https://github.com/original-owner/original-repo

你 fork 之后得到:

1
https://github.com/your-name/original-repo

在 Git 里,一般有两个远程仓库概念:

  • origin = 你自己的 fork 仓库
  • upstream = 原始仓库,也就是被 fork 的仓库

同步 fork 的本质就是:

1
从 upstream 拉取更新 → 合并到你的 fork → 推送到 origin

二、使用 GitHub Actions 自动同步

如果你想让 fork 自动跟随上游仓库更新,可以使用 GitHub Actions 定时任务。

在你的 fork 仓库中创建文件:

1
.github/workflows/sync-fork.yml

填入以下内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
name: Sync fork with upstream

on:
  schedule:
    - cron: "0 3 * * *"
  workflow_dispatch:

permissions:
  contents: write

env:
  UPSTREAM_REPOSITORY: original-owner/original-repo
  UPSTREAM_BRANCH: main
  TARGET_BRANCH: main

jobs:
  sync:
    name: Sync fork
    runs-on: ubuntu-latest

    steps:
      - name: Checkout target branch
        uses: actions/checkout@v4
        with:
          ref: ${{ env.TARGET_BRANCH }}
          fetch-depth: 0

      - name: Configure Git
        run: |
          git config user.name "github-actions[bot]"
          git config user.email "github-actions[bot]@users.noreply.github.com"

      - name: Add upstream remote
        run: |
          git remote add upstream https://github.com/${{ env.UPSTREAM_REPOSITORY }}.git
          git remote -v

      - name: Fetch upstream
        run: |
          git fetch upstream ${{ env.UPSTREAM_BRANCH }}

      - name: Merge upstream into fork
        run: |
          git merge upstream/${{ env.UPSTREAM_BRANCH }}

      - name: Push changes
        run: |
          git push origin ${{ env.TARGET_BRANCH }}

需要修改的地方主要有三个环境变量:

字段含义
UPSTREAM_REPOSITORY原仓库地址,不要带 https://github.com/
UPSTREAM_BRANCH原仓库要同步的分支
TARGET_BRANCH你的 fork 要更新的分支

例如,原仓库是:

1
https://github.com/someone/demo-project

你的 fork 想同步 main 分支,那么就写:

1
2
3
4
env:
  UPSTREAM_REPOSITORY: someone/demo-project
  UPSTREAM_BRANCH: main
  TARGET_BRANCH: main

文件提交后,进入仓库的 Actions 标签页,手动触发一次 Sync fork with upstream,看到绿色对勾就说明配置成功了。

三、如果 fork 有自己的修改怎么办?

如果你的 fork 不只是上游的副本,你自己也在上面提交过代码,那么自动同步就要多留个心眼。

例如:

1
2
上游仓库更新了 A 文件
你的 fork 也修改了 A 文件

这时候可能产生冲突。上面的 workflow 使用 git merge,它会尝试自动合并——但冲突严重时仍然可能失败,需要你手动介入。

更稳妥的做法是:

  1. main 分支只用于同步上游;
  2. 自己的改动放在单独分支;
  3. 定期从同步后的 main rebase 或 merge 到自己的开发分支。

例如:

1
2
git checkout my-feature
git rebase main

或者:

1
2
git checkout my-feature
git merge main

这样结构更清晰。

四、定时任务时间怎么写?

这段配置表示每天 UTC 03:00 运行一次:

1
2
3
on:
  schedule:
    - cron: "0 3 * * *"

GitHub Actions 的 cron 默认按 UTC 时间计算。

常见写法如下:

1
2
3
4
5
6
7
8
9
10
11
# 每天 UTC 03:00
- cron: "0 3 * * *"

# 每 12 小时一次
- cron: "0 */12 * * *"

# 每周一 UTC 03:00
- cron: "0 3 * * 1"

# 每月 1 号 UTC 03:00
- cron: "0 3 1 * *"

不建议设置得太频繁,对于同步 fork 来说,每天一次通常已经足够。

五、手动触发同步

workflow 里有这一段:

1
workflow_dispatch:

它的作用是允许你手动运行同步任务。

添加之后,你可以进入:

1
GitHub 仓库 → Actions → Sync fork with upstream → Run workflow

手动触发一次同步,在第一次测试时非常有用。

六、常见问题排查

1. 提示没有权限 push

如果日志里出现类似:

1
Permission denied

或者:

1
403

一般是 GitHub Actions 没有写入权限。

检查 workflow 里是否有:

1
2
permissions:
  contents: write

同时检查仓库设置:

1
Settings → Actions → General → Workflow permissions

建议选择:

1
Read and write permissions

如果仓库开启了分支保护规则,还要确认 GitHub Actions 是否允许推送到目标分支。

2. 定时任务没有运行

首先确认 workflow 文件在默认分支上。GitHub Actions 的 schedule 只会在默认分支上触发。

如果你的默认分支是 main,那么这个文件必须存在于:

1
main 分支的 .github/workflows/sync-fork.yml

其次,公共仓库长时间没有活动时,GitHub 可能会自动停用定时 workflow。如果发现定时任务不运行,可以进入 Actions 页面重新启用 workflow。

3. merge 失败

如果日志里出现合并冲突,说明你的 fork 和上游仓库在同一处代码上有不同的修改。

常见原因是:

  1. 你在 fork 的 main 分支上提交过自己的改动;
  2. 上游仓库也更新了同一处代码;
  3. 两边无法自动合并。

解决方法:

手动在本地解决冲突:

1
2
3
git fetch upstream
git checkout main
git merge upstream/main

如果有冲突,解决冲突后:

1
2
3
git add .
git commit
git push origin main

如果你完全不需要保留 fork 的改动,可以强制重置:

1
2
3
4
git fetch upstream
git checkout main
git reset --hard upstream/main
git push origin main --force

注意:reset --hard--force 会覆盖你的 fork 分支历史。只有在你确定 fork 只是镜像仓库、不需要保留自己的提交时,才应该这样做。

七、如果上游是私有仓库怎么办?

如果上游仓库是公开仓库,前面的配置可以直接使用。

如果上游仓库是私有仓库,普通的 HTTPS 地址可能无法拉取,这时需要使用 Personal Access Token(PAT)。

可以在 fork 仓库里添加 secret:

1
Settings → Secrets and variables → Actions → New repository secret

例如添加:

1
GH_PAT

然后把 checkout 和 fetch 相关配置改成使用 token。

示例:

1
2
3
4
5
6
- name: Checkout target branch
  uses: actions/checkout@v4
  with:
    ref: ${{ env.TARGET_BRANCH }}
    fetch-depth: 0
    token: ${{ secrets.GH_PAT }}

添加 upstream 时也可以写成:

1
git remote add upstream https://x-access-token:${{ secrets.GH_PAT }}@github.com/${{ env.UPSTREAM_REPOSITORY }}.git

注意:不要把 token 直接写进 workflow 文件,必须放进 GitHub Secrets。

八、推荐实践

如果你只是想让 fork 保持和原仓库一致,推荐:

1
使用 GitHub Actions + git merge

优点是:

  1. 不会因为 fork 轻微分叉就停止同步;
  2. 出现真正冲突时会失败并提醒你处理;
  3. 纯镜像 fork 场景下 merge 等价于 fast-forward,不会产生多余 commit。

如果你 fork 后还要自己开发,推荐:

1
2
main 分支同步上游
自己的修改放到 feature 分支

例如:

1
2
main        用来跟随 upstream/main
my-feature  你的自定义开发分支

这样以后维护会轻松很多。

九、总结

GitHub fork 仓库默认不会自动同步上游更新。

如果想长期保持更新,推荐使用 GitHub Actions 自动同步:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
name: Sync fork with upstream

on:
  schedule:
    - cron: "0 3 * * *"
  workflow_dispatch:

permissions:
  contents: write

env:
  UPSTREAM_REPOSITORY: original-owner/original-repo
  UPSTREAM_BRANCH: main
  TARGET_BRANCH: main

jobs:
  sync:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4
        with:
          ref: ${{ env.TARGET_BRANCH }}
          fetch-depth: 0

      - run: |
          git config user.name "github-actions[bot]"
          git config user.email "github-actions[bot]@users.noreply.github.com"

          git remote add upstream https://github.com/${{ env.UPSTREAM_REPOSITORY }}.git
          git fetch upstream ${{ env.UPSTREAM_BRANCH }}

          git merge upstream/${{ env.UPSTREAM_BRANCH }}

          git push origin ${{ env.TARGET_BRANCH }}

这套方案适合大多数只想同步上游更新的 fork 仓库。

如果你的 fork 有大量自定义修改,就不要盲目自动合并,最好保留一个干净的 main 分支同步上游,把自己的开发放在单独分支里。

This post is licensed under CC BY 4.0 by the author.