git-fork-workflow

v2026.09.24

Fork management and upstream sync. Use when working with forks, syncing with upstream, detecting divergence, or preparing commits for contribution.

GitHub
Install command
npx skhub add laurigates/git-fork-workflow
Markdown
SKILL.md

Git Fork Workflow

Expert guidance for managing forked repositories, synchronizing with upstream, and contributing back cleanly.

Data-Gathering Script

Run this first to detect the upstream remote, compute ahead/behind via git rev-list, and get a recommended sync strategy (a pure function — it runs no mutations):

bash "${CLAUDE_SKILL_DIR}/scripts/git-fork-workflow.sh" --home-dir "$HOME" --project-dir "$(pwd)"

Parse STATUS= and ISSUES: from the output. It emits IS_FORK, UPSTREAM / ORIGIN, BEHIND / AHEAD (from git rev-list --left-right --count upstream/main...origin/main), and RECOMMENDED_STRATEGY (one of in-sync, fast-forward, ahead-only, rebase, not-a-fork). The strategy is a recommendation only — executing any destructive sync (reset, rebase, force-push) stays your call, per the strategy prose below.

When to Use This Skill

Use this skill when...Use git-upstream-pr instead when...
Understanding fork remote architectureReady to submit a PR to upstream
Diagnosing fork divergence from upstreamNeed step-by-step PR creation workflow
Syncing fork's main with upstreamCherry-picking specific commits for upstream
Deciding on a sync strategyCreating a cross-fork PR via gh CLI
Contributing from an environment that can't reach upstreamUpstream is directly reachable for a normal cross-fork PR

Remote Architecture

Forks use two remotes:

RemotePoints ToPurpose
originYour fork (you/repo)Push your work here
upstreamOriginal 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

The data-gathering script (above) fetches both remotes and computes the ahead/behind counts — read BEHIND and AHEAD from its output. To view the specific divergent commits behind those counts:

# 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 Counts

The script's BEHIND / AHEAD come from git rev-list --left-right --count upstream/main...origin/main:

BEHIND AHEADMeaning
0 0Perfectly in sync
5 0Fork is 5 behind upstream (upstream has 5 new commits)
0 3Fork is 3 ahead (fork has 3 commits not on upstream)
5 3Diverged: 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

The script's RECOMMENDED_STRATEGY maps the BEHIND/AHEAD counts to a strategy keyword via a pure function; use it as the starting point and confirm against this table before running any destructive command:

RECOMMENDED_STRATEGYSituationStrategy
in-sync0 behind, 0 aheadNothing to do
fast-forwardBehind only, fork main cleanFast-forward or gh repo sync
ahead-onlyAhead only, nothing to pullNo sync needed; fork leads upstream
rebaseDiverged, unique work worth keepingRebase (Strategy 4)
(diverged, work expendable)Unique work expendable / match upstream exactlyHard reset (Strategy 3)
(unsure)Not sureTry fast-forward first; it fails safely if diverged

The recommendation never executes a sync — picking and running Strategy 3 (hard reset) or 4 (rebase + force-push) stays your decision.

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.

Fork-Main as an Upstream Staging Area

When the environment cannot reach upstream directly — e.g. a Claude Code session whose repository scope only includes your fork, so issues/PRs against owner/repo are denied — don't let that block development. Stage the contribution in your fork's main, then resubmit upstream from a session that can reach it.

The pattern

  1. Stage in the fork. Branch (ideally from upstream/main), implement, open a PR into your fork's main, self-review, and merge. The change is now landed and usable immediately, independent of upstream reachability.

  2. Contain the divergence. Merging into fork main diverges it from upstream/main (different SHAs — the Divergence Problem above). That is the accepted cost of staging. Keep it bounded by periodically pulling upstream/main back in (rebase or merge) so the fork doesn't rot.

  3. Resubmit cleanly — never PR fork:main → upstream:main. That drags every staged commit plus the divergence into one PR. Instead, from a session that can reach upstream, branch fresh from upstream/main and cherry-pick only the one contribution's commit(s), then open the cross-fork PR (syntax below). Each upstream PR stays atomic and reviewable — what maintainers actually accept.

  4. Track upstream status. Stamp staged commits so a later session knows what still needs a PR:

    Upstream-status: pending          # staged in fork, not yet submitted
    Upstream-PR: https://github.com/owner/repo/pull/NN   # once submitted
    

    List what's still pending with git log --grep='Upstream-status: pending' upstream/main..origin/main.

When to use vs. the Golden Rule

SituationApproach
You can reach upstream nowBranch from upstream/main, open the cross-fork PR directly (Golden Rule above)
You cannot reach upstream from this environmentStage in fork main now; cherry-pick onto an upstream/main branch and PR later

The staging pattern is the Golden Rule deferred — it still ends with a clean upstream/main-based branch; it just buys you an unblocked checkpoint in between.

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

ContextCommand
Divergence countgit rev-list --left-right --count upstream/main...origin/main
Fork ahead commitsgit log --oneline --format='%h %s' upstream/main..origin/main
Fork behind commitsgit log --oneline --format='%h %s' origin/main..upstream/main
Quick sync checkgit fetch upstream && git merge --ff-only upstream/main
Remote listinggit remote -v

Related Skills

Discovery
Tags

No tags published for this skill.

Version
Latest version metadata

Version

v2026.09.24

Published

Sep 24, 2026

Category

Uncategorized

License

MIT

Source path

git-plugin/skills/git-fork-workflow

Default branch

main

Latest commit

1668324

Tree SHA

b2d4cc3