git-fork-workflow
Git Fork Workflow
Expert guidance for managing forked repositories, synchronizing with upstream, and contributing back cleanly.
When to Use This Skill
| Use this skill when... | Use git-upstream-pr instead when... |
|---|---|
| Understanding fork remote architecture | Ready to submit a PR to upstream |
| Diagnosing fork divergence from upstream | Need step-by-step PR creation workflow |
| Syncing fork's main with upstream | Cherry-picking specific commits for upstream |
| Deciding on a sync strategy | Creating a cross-fork PR via gh CLI |
Remote Architecture
Forks use two remotes:
| Remote | Points To | Purpose |
|---|---|---|
origin |
Your fork (you/repo) |
Push your work here |
upstream |
Original repo (owner/repo) |
Pull updates from here |
Setup
# Verify remotes
git remote -v
# Add upstream if missing
git remote add upstream https://github.com/owner/original-repo.git
# Verify
git fetch upstream
git remote -v
Identifying Fork vs Upstream
# Check if upstream remote exists
git remote get-url upstream
# Get fork owner
git remote get-url origin | sed -E 's#.*github\.com[:/]##; s#\.git$##'
# Get upstream owner
git remote get-url upstream | sed -E 's#.*github\.com[:/]##; s#\.git$##'
The Divergence Problem
When you squash-merge branches into your fork's main, the commit SHAs differ from upstream's commits. This creates divergence even when the code content is identical.
Detecting Divergence
# Fetch latest from both remotes
git fetch origin
git fetch upstream
# Count ahead/behind
git rev-list --left-right --count upstream/main...origin/main
# Show divergent commits
git log --oneline upstream/main..origin/main # Commits on fork not on upstream
git log --oneline origin/main..upstream/main # Commits on upstream not on fork
Reading the Output
git rev-list --left-right --count upstream/main...origin/main returns two numbers:
| Output | Meaning |
|---|---|
0 0 |
Perfectly in sync |
5 0 |
Fork is 5 behind upstream (upstream has 5 new commits) |
0 3 |
Fork is 3 ahead (fork has 3 commits not on upstream) |
5 3 |
Diverged: upstream has 5 new, fork has 3 unique |
Sync Strategies
Strategy 1: GitHub CLI Sync (Simplest)
# Syncs fork's default branch with upstream via GitHub API
gh repo sync owner/fork-repo
# Then pull locally
git pull origin main
Best when: fork has no unique commits worth preserving on main.
Strategy 2: Fast-Forward Merge (Clean Canary)
git fetch upstream
git merge --ff-only upstream/main
Best when: fork's main has not diverged. Fails cleanly if diverged (no messy merge commits).
Strategy 3: Hard Reset (Force Sync)
git fetch upstream
git reset --hard upstream/main
git push --force-with-lease origin main
Best when: fork's main has diverged and you want to discard fork-only commits. Destructive - ensure no unique work is on main.
Strategy 4: Rebase (Preserve Fork Work)
git fetch upstream
git rebase upstream/main
git push --force-with-lease origin main
Best when: fork has unique commits on main that should sit on top of upstream's history.
Strategy Selection
| Situation | Strategy |
|---|---|
| Fork main is clean, no unique commits | Fast-forward or gh repo sync |
| Fork main diverged, unique work expendable | Hard reset |
| Fork main diverged, unique work worth keeping | Rebase |
| Just want to match upstream exactly | Hard reset |
| Not sure | Try fast-forward first; it fails safely if diverged |
Golden Rule for Upstream PRs
Branch from upstream/main, not from your fork's main. This completely bypasses fork divergence:
git fetch upstream
git switch -c feat/my-contribution upstream/main
# Cherry-pick, code, or apply changes here
git push -u origin feat/my-contribution
# Create cross-fork PR targeting upstream
See git-upstream-pr for the complete workflow.
Cross-Fork PR Syntax
# Create PR from fork branch to upstream repo
gh pr create \
--repo owner/upstream-repo \
--base main \
--head your-username:feat/branch-name \
--title "feat: description" \
--body "PR description"
The --head must include the fork owner prefix (your-username:branch) when targeting a different repository.
Common Patterns
Check if Working in a Fork
# Has upstream remote = likely a fork
git remote get-url upstream 2>/dev/null && echo "Fork" || echo "Not a fork"
Periodic Sync Workflow
# Weekly sync routine
git fetch upstream
git switch main
git merge --ff-only upstream/main || echo "Diverged - manual sync needed"
git push origin main
View Upstream Changes Since Last Sync
git fetch upstream
git log --oneline origin/main..upstream/main
Agentic Optimizations
| Context | Command |
|---|---|
| Divergence count | git rev-list --left-right --count upstream/main...origin/main |
| Fork ahead commits | git log --oneline --format='%h %s' upstream/main..origin/main |
| Fork behind commits | git log --oneline --format='%h %s' origin/main..upstream/main |
| Quick sync check | git fetch upstream && git merge --ff-only upstream/main |
| Remote listing | git remote -v |
Related Skills
- git-upstream-pr - Submit clean PRs to upstream repositories
- git-branch-pr-workflow - General branch and PR workflow patterns
- git-rebase-patterns - Advanced rebase techniques
- git-conflicts - Resolve cherry-pick and merge conflicts
- git-repo-detection - Remote URL parsing patterns