Tự động đồng bộ Fork với Upstream Repository trên GitHub: Giữ dự án luôn cập nhật

Git tutorial - IT technology blog
Git tutorial - IT technology blog

Fork một repository trên GitHub là việc làm quen thuộc — bạn muốn đóng góp cho open source, muốn customize một thư viện, hoặc đơn giản giữ bản sao để thử nghiệm. Vấn đề bắt đầu khi dự án gốc (upstream) tiếp tục phát triển còn fork của bạn thì đứng yên.

Sau vài tháng, upstream đã có 200 commit mới, fix bug quan trọng, thêm tính năng mới — nhưng fork vẫn ở commit cũ từ hồi mới tạo. Lúc mở Pull Request, conflicts chồng chất, CI fail liên tục, và việc merge trở thành ác mộng. Đây không phải lỗi của ai — đây là cấu trúc của Git: fork và upstream phát triển độc lập, không có kết nối tự động.

Tại sao Fork bị “lạc hậu” và dấu hiệu cần sync ngay

Mỗi khi fork một repo, GitHub tạo bản sao độc lập tại thời điểm đó. Upstream merge PR mới, release hotfix, cập nhật dependencies — tất cả không tự động xuất hiện trong fork. Hai nhánh phát triển song song, và khoảng cách ngày càng rộng ra.

Dấu hiệu bạn cần sync ngay:

  • GitHub hiển thị “This branch is X commits behind upstream/main”
  • CI của bạn fail vì upstream đã thay đổi interface hoặc API
  • Bạn muốn mở PR lên upstream nhưng có quá nhiều conflicts
  • Security advisory vừa được patch ở upstream mà fork chưa có

3 cách đồng bộ Fork với Upstream — So sánh thực tế

Cách 1: GitHub UI “Sync fork” button

GitHub ra mắt nút “Sync fork” trực tiếp trên giao diện web từ 2022. Vào fork của bạn, nhìn vào dòng trạng thái branch, click Sync forkUpdate branch. Xong.

Đơn giản, không cần mở terminal. Nhưng giới hạn rõ ràng: chỉ sync được khi không có conflicts. Nếu bạn đã chỉnh sửa file mà upstream cũng chỉnh — GitHub UI chịu thua, bắt bạn tự xử bằng tay.

Cách 2: Git CLI thủ công

Cách truyền thống, toàn quyền kiểm soát. Lần đầu, thêm upstream remote rồi fetch:

# Lần đầu: thêm upstream remote
git remote add upstream https://github.com/original-owner/original-repo.git

# Fetch thay đổi từ upstream (chưa merge)
git fetch upstream

# Merge upstream vào branch local
git checkout main
git merge upstream/main

# Push lên fork
git push origin main

Hoặc dùng rebase thay merge để giữ history gọn:

git fetch upstream
git checkout main
git rebase upstream/main
git push origin main --force-with-lease

Lưu ý quan trọng ở đây: mình dùng --force-with-lease thay vì --force. Mình từng mất code quan trọng vì force push nhầm branch — từ đó luôn cẩn thận với git push --force. Flag --force-with-lease sẽ từ chối push nếu remote đã có commit mới mà local chưa biết, an toàn hơn nhiều so với force push mù quáng.

Cách 3: Tự động hóa với GitHub Actions

Nếu fork là dài hạn và bạn không muốn nhớ sync thủ công mỗi tuần, GitHub Actions là giải pháp triệt để. Tạo workflow chạy theo lịch, tự fetch upstream và merge — bạn không cần làm gì thêm.

Phân tích ưu nhược — Chọn cách nào phù hợp?

Tiêu chí GitHub UI Git CLI GitHub Actions
Độ dễ dùng Rất cao Trung bình Thấp (setup lần đầu)
Xử lý conflict Không Có (cần config)
Tự động hoàn toàn Không Không
Phù hợp khi Sync tạm thời, ít thay đổi Cần kiểm soát, có conflict Fork dài hạn, production

Quyết định thực tế: nếu fork chỉ để đọc code hoặc thử nghiệm ngắn hạn → dùng GitHub UI. Nếu bạn đang maintain fork với customizations riêng và cần kiểm soát từng thay đổi → CLI với rebase. Nếu fork là sản phẩm thực tế chạy production và bạn cần luôn theo kịp upstream → GitHub Actions tự động.

Triển khai GitHub Actions để tự động sync Fork

Tạo file .github/workflows/sync-upstream.yml trong fork của bạn:

name: Sync Fork with Upstream

on:
  schedule:
    # Chạy mỗi ngày lúc 6 giờ sáng UTC (1 giờ trưa JST)
    - cron: '0 6 * * *'
  workflow_dispatch:  # Cho phép chạy thủ công từ UI

jobs:
  sync:
    runs-on: ubuntu-latest
    permissions:
      contents: write

    steps:
      - name: Checkout fork
        uses: actions/checkout@v4
        with:
          token: ${{ secrets.GITHUB_TOKEN }}
          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/original-owner/original-repo.git
          git fetch upstream

      - name: Merge upstream into main
        run: |
          git checkout main
          git merge upstream/main --no-edit
          git push origin main

Thay original-owner/original-repo bằng URL upstream thực tế. Workflow chạy mỗi ngày, tự fetch upstream và merge vào main. Nếu không có gì mới từ upstream, bước merge kết thúc nhanh không làm gì cả.

Xử lý xung đột thông minh khi sync

Trường hợp khó: bạn đã chỉnh sửa file A trong fork, upstream cũng sửa file A. GitHub Actions sẽ fail bước merge. Có 2 chiến lược tùy tình huống:

Chiến lược 1: Ưu tiên upstream (override bằng theirs)

Khi bạn muốn luôn lấy version mới nhất từ upstream và chấp nhận override phần customization của mình tại các file conflict:

git fetch upstream
git checkout main
git merge -X theirs upstream/main
git push origin main

Dùng khi: fork chỉ thêm tính năng nhỏ ở file riêng, không sửa core files của upstream.

Chiến lược 2: Giữ customizations bằng branch riêng + rebase

Đây là pattern mình hay dùng nhất cho fork dài hạn: giữ main là mirror sạch của upstream, toàn bộ customizations nằm ở branch riêng (my-fork, custom-features…).

# main luôn sync với upstream
git fetch upstream
git checkout main
git merge upstream/main
git push origin main

# Rebase customizations lên đầu upstream mới nhất
git checkout my-custom-branch
git rebase main

# Nếu có conflicts, xử lý từng commit
# git add . && git rebase --continue   (sau khi fix conflict)
# git rebase --abort                   (nếu muốn huỷ và làm lại)

git push origin my-custom-branch --force-with-lease

Phân tách rõ ràng giữa “code từ upstream”“code của mình”, ít conflict hơn nhiều về lâu dài.

Tự động tạo Issue khi sync thất bại

Thêm step vào workflow để nhận thông báo khi có conflict cần xử lý thủ công:

      - name: Notify on failure
        if: failure()
        uses: actions/github-script@v7
        with:
          script: |
            github.rest.issues.create({
              owner: context.repo.owner,
              repo: context.repo.repo,
              title: 'Sync upstream failed — manual intervention needed',
              body: 'Workflow sync upstream bị fail. Cần xử lý conflict thủ công.\n\nRun ID: ${{ github.run_id }}'
            })

Khi workflow fail, một Issue tự động được tạo trong repo với link đến run log. Bạn không cần check Actions tab mỗi ngày.

Một vài lệnh kiểm tra trạng thái và lưu ý thực tế

Trước khi sync, kiểm tra fork đang lạc hậu bao nhiêu:

# Số commit fork đang behind upstream
git fetch upstream
git rev-list --count HEAD..upstream/main

# Danh sách commit upstream mà fork chưa có
git log HEAD..upstream/main --oneline

# File nào đã thay đổi ở upstream
git diff HEAD..upstream/main --name-only

Một số điểm cần nhớ khi làm việc với fork dài hạn:

  • Không commit thẳng vào main nếu fork có customizations — dễ conflict mỗi lần sync
  • Sync thường xuyên (ít nhất mỗi tuần) — đồng bộ sớm khi ít commit khác nhau hơn gom lại 3 tháng
  • Đọc CHANGELOG upstream trước khi merge major version — breaking changes cần test kỹ trước khi áp vào production
  • Tag version trước khi sync lớn — nếu có gì sai, bạn có điểm rollback rõ ràng: git tag v1.2.3-before-sync

Tự động sync upstream không phải là “set and forget” hoàn toàn — bạn vẫn cần xem thông báo khi workflow fail và xử lý conflict thủ công đôi khi. Nhưng thay vì mất 2 tiếng gỡ đống conflicts mỗi tháng vì để lâu không sync, bạn chỉ mất 10 phút mỗi lần, và fork luôn trong trạng thái sẵn dùng.

Share: