What you will achieve
You will keep a fork of an open-source repository up to date with its upstream, sync your branches cleanly, and avoid the common pitfall of letting your fork drift weeks behind.
Step 1: confirm the remote setup
git remote -v
You want two remotes: origin for your fork and upstream for the source.
# If upstream is missing
git remote add upstream [email protected]:original-owner/project.git
git remote -v
Step 2: fetch upstream
git fetch upstream
This downloads new commits, branches, and tags from upstream without changing your working tree.
Step 3: update local main
git checkout main
git merge upstream/main --ff-only
--ff-only guarantees no merge commit; if local main has diverged (you committed to main directly), the merge fails. That is a feature - it tells you something needs untangling.
Step 4: push to your fork
git push origin main
Your fork's main now matches upstream's.
Step 5: rebase feature branches
For each active feature branch:
git checkout feature/login
git rebase main
# resolve conflicts if any
git push --force-with-lease
Step 6: configure pull behaviour
git config pull.rebase true
git config pull.ff only
Now git pull rebases by default and refuses non-fast-forward merges - your fork stays clean automatically.
Optional: GitHub's "Sync fork" button
The GitHub UI offers a "Sync fork" button on your fork's main page. It performs the equivalent of the above without the CLI. Useful for quick syncs; less flexible than the local workflow.
Optional: gh repo sync
gh repo sync yourname/project --branch main
The GitHub CLI command runs the sync server-side. Combine with a periodic GitHub Action for fully automated forks:
# .github/workflows/sync.yml
name: Sync upstream
on:
schedule:
- cron: '0 6 * * 1' # weekly
workflow_dispatch:
jobs:
sync:
runs-on: ubuntu-latest
steps:
- uses: tgymnich/[email protected]
with:
owner: original-owner
base: main
head: main
Optional: pulling tags
git fetch upstream --tags
git push origin --tags
Useful if you depend on upstream tags (releases, version markers).
Common scenarios
Local main has diverged
git checkout main
git merge upstream/main --ff-only
# error: not possible to fast-forward, aborting
Reset local main to upstream's main (you lose any direct commits, which you should not have made):
git reset --hard upstream/main
git push --force-with-lease origin main
Upstream renamed default branch
Upstream changed master to main. Fix locally:
git fetch upstream
git branch -m master main
git branch --set-upstream-to=upstream/main main
git push origin :master main
# then update default branch on your fork via the GitHub UI
Forgot to keep in sync for months
git fetch upstream
git checkout main
git reset --hard upstream/main
git push --force-with-lease origin main
# rebase each feature branch
git checkout feature/whatever
git rebase main
Long divergence makes rebase conflict-prone. Sync weekly to keep conflicts small.
Best practice cadence
- Daily for active contributors to busy projects.
- Weekly for casual contributors.
- Before each PR at minimum - reviewers expect your branch to start from current main.
The result
Your fork tracks upstream cleanly. Your feature branches start from current main. Reviewers do not bounce your PR with "please rebase". The work is mechanical and takes thirty seconds when done regularly.