Sync a GitHub Fork Safely with gh repo sync
Add one flag to gh repo sync and a routine fork update turns into a hard reset that discards commits nobody backed up. This guide gets you a repeatable, ordinary sync, and a clear line for when --force is actually the right call. Allow about ten minutes for a routine sync, plus more if the branch has diverged.
The route
Jump straight to the step you need, or tick off Done means at the end.
- You need: the
ghcommand, an authenticated GitHub account with permission to update the destination, and a source repository whose branch you can read. Nothing here needssudo.
The examples use GitHub CLI 2.87.3, installed here on 23 September 2026. The sync changes repository state on GitHub, so treat the final command as an operational change, not a harmless status check.
1. Check the installed command
Start with read-only checks. This confirms the version and keeps the option names tied to the binary you are about to use:
$ gh --version
gh version 2.87.3 (2026-02-23)
https://github.com/cli/cli/releases/tag/v2.87.3
$ gh repo sync --help
The command has this shape:
gh repo sync [<destination-repository>] [flags]
The destination is optional. With no argument, gh uses the repository associated with the current local directory. With an argument such as OWNER/REPO, it updates that remote repository instead.
Checkpoint
Make sure the version output is from the intended installation, and that you understand whether the next command names a local repository or a remote destination.
2. Establish the source, destination and branch
By default the source is the destination repository's parent: the usual fork workflow, where the fork is the destination and its upstream is the source. Select another source with --source or -s.
The branch defaults to the default branch, which is not necessarily main or master. Name it explicitly with --branch or -b whenever it matters:
$ gh repo sync example/project-fork --branch main
For a source that is not the fork parent, specify both repositories:
$ gh repo sync example/project-fork \
--source example/project-upstream \
--branch main
These commands are shown for syntax only: run for real, they contact GitHub and update the destination branch. Replace every owner, repository and branch with values you have checked first, ideally in the GitHub web interface or an existing read-only review process. A typo in an owner or repository name is not a safe way to discover what this command does.
3. Perform an ordinary fast-forward sync
Once the values are confirmed, sync a fork from its parent without --force:
$ gh repo sync example/project-fork --branch main
Without --force, gh repo sync asks GitHub to move the destination branch forward so it matches the source, without replacing the destination's existing history. If the branches have diverged, this ordinary sync cannot make them equal by fast-forwarding. Stop and inspect the divergence rather than adding a flag out of habit.
A successful command may produce little or no output, so check its status immediately:
$ gh repo sync example/project-fork --branch main
$ printf 'sync exit status: %s\n' "$?"
sync exit status: 0
Exit status zero confirms the invocation completed; it does not confirm you selected the right repositories, so the pre-flight review still matters. For an independent check, compare the branch commit IDs through your normal GitHub review or API tooling.
Checkpoint
You should now have a destination branch updated by a non-forced operation, or a clear non-zero error explaining why the histories could not be fast-forwarded.
4. Treat --force as a history-changing operation
The --force flag hard-resets the destination branch to match the source branch:
$ gh repo sync example/project-fork --branch main --force
Destructive action
This can discard commits that exist on the destination branch but not on the source, and can disrupt people or automation using that branch. Do not reach for it merely to clear an error. First record the destination tip, review the commits that would disappear, and agree a maintenance window if the branch is shared.
Recovery
For a fork, make a backup reference before the forced sync, using a process your team already trusts, for example a clearly named backup branch pushed with Git or created in the GitHub interface, then verify that the backup commit is visible. If the forced operation has already happened and the old commit was not backed up, stop making further changes and use the repository's reflog, pull requests or hosting recovery facilities while the old commit is still reachable. gh repo sync itself has no undo option.
5. Diagnose the common failures
A non-zero result does not mean that --force is the answer. Check the most likely boundary first:
- Missing or misspelled repository: the destination or source value is wrong.
- Authentication or permission error: the current
ghaccount cannot perform the requested operation. Re-authenticate only through your normal controlled process; never paste a token into shell history. - Branch error: the named branch is absent, or the source and destination do not expose the branch you selected. Verify the spelling and repository contents.
- Divergence error: the destination has history that cannot be advanced by the ordinary update. Review that history before deciding whether to merge, rebase, preserve it, or deliberately replace it.
Keep diagnostics separate from the change. You can rerun the help command at any time:
$ gh repo sync --help
Do not run the sync repeatedly while investigating. Repetition will not make an incorrect source safe, and repeated forced operations can make recovery harder.
Done means
- You confirmed the installed GitHub CLI version and the command syntax.
- You identified the exact source, destination and branch.
- You used an ordinary sync first and checked its exit status.
- You know the defaults: the source is the destination's parent, the branch is the repository default.
- You will review and back up destination-only commits before using
--force. - You know a forced sync has no undo command.