A fork does not follow its upstream by itself. Spoon-Knife has not changed since 2014, so to see a sync Sam first rewinds his fork's main by one commit, then asks GitHub 29 to catch it up (the Sync fork button does the same):
F=binarybehemoth/booknest-fork-spoon-knife
git push -q --force origin HEAD~1:main
gh api repos/$F/compare/octocat:Spoon-Knife:main...main \
--jq '"ahead \(.ahead_by), behind \(.behind_by)"'
gh repo sync $Fahead 0, behind 1 ✓ Synced the "binarybehemoth:main" branch from "octocat:main"
A fork that is only behind is fast-forwarded. The harder case is a fork whose main has commits of its own: Sam rewinds it again and pushes a NOTES.md commit on top, one ahead and one behind. This time the sync makes a merge commit on GitHub, and his local rebase-and-force-push is stopped by --force-with-lease, because the remote branch moved after his last fetch:
gh repo sync $F
git fetch -q upstream && git rebase upstream/main
git push --force-with-lease origin main✓ Synced the "binarybehemoth:main" branch from "octocat:main" Successfully rebased and updated refs/heads/main. To https://github.com/binarybehemoth/booknest-fork-spoon-knife.git ! [rejected] main -> main (stale info) ...
On GitHub, the fork's main is now a merge commit, Merge branch 'octocat:main' into main, joining Sam's Add team notes with upstream's newest commit. If the sides conflict, the server-side sync gives up and you merge locally (Git). Keep the fork's main identical to upstream and work on branches, so every sync is a fast-forward; Sam restores that state with git reset --hard upstream/main and git push --force-with-lease origin main.