This message appears on GitHub when your forked branch has diverged from the upstream repository. Your fork contains commits not in the original repo (ahead) and is missing commits from the original (behind). Syncing your fork resolves this divergence.
The message "This branch is X commits ahead, Y commits behind upstream/main" is not an error but an informational status shown by GitHub (and other Git hosting platforms) when your repository has diverged from its upstream source. **What "ahead" means**: Your branch has X commits that don't exist in the upstream repository. These are changes you (or others) made in your fork that haven't been merged into the original project. **What "behind" means**: The upstream repository has Y commits that your branch doesn't have yet. The original project has received updates since you last synced. This divergence commonly happens with forked repositories when: 1. You make changes to your fork (creates "ahead" commits) 2. The original repository receives new commits (puts you "behind") While this message doesn't prevent you from working, it indicates your fork is out of sync. Being significantly behind can lead to merge conflicts when you eventually try to contribute back to the upstream project.
First, verify your fork's relationship with the upstream repository:
# Check your remotes
git remote -v
# Expected output:
# origin https://github.com/YOUR-USERNAME/repo.git (fetch)
# origin https://github.com/YOUR-USERNAME/repo.git (push)
# upstream https://github.com/ORIGINAL-OWNER/repo.git (fetch)
# upstream https://github.com/ORIGINAL-OWNER/repo.git (push)
# If upstream is missing, add it:
git remote add upstream https://github.com/ORIGINAL-OWNER/repo.git
# Verify it was added
git remote -vReplace ORIGINAL-OWNER/repo with the actual original repository URL.
Download the latest commits from the upstream repository without merging them:
# Fetch all branches from upstream
git fetch upstream
# See the difference between your branch and upstream
git log HEAD..upstream/main --oneline
# This shows commits in upstream that you don't have
# To see commits you have that upstream doesn't:
git log upstream/main..HEAD --onelineThis helps you understand exactly what's different before making changes.
GitHub provides a simple way to sync your fork:
1. Go to your fork on GitHub (github.com/YOUR-USERNAME/repo)
2. Click the "Sync fork" button (or "Fetch upstream" on older UI)
3. Review the changes to be synced
4. Click "Update branch"
Limitations of this method:
- Only works if there are no conflicts
- Creates a merge commit
- Doesn't work if you have unpushed local commits
If GitHub shows conflicts or you prefer command-line control, use the manual methods below.
If you want to keep your fork's commits visible in the history:
# Make sure you're on your main branch
git checkout main
# Fetch upstream changes
git fetch upstream
# Merge upstream into your branch
git merge upstream/main
# If there are conflicts, resolve them:
# 1. Open conflicted files
# 2. Edit to resolve conflicts
# 3. Stage resolved files: git add <file>
# 4. Complete merge: git commit
# Push the merged result to your fork
git push origin mainThis creates a merge commit but preserves the complete history of both branches.
Rebasing creates a linear history by replaying your commits on top of upstream:
# Make sure you're on your main branch
git checkout main
# Fetch upstream
git fetch upstream
# Rebase your commits on top of upstream
git rebase upstream/main
# If conflicts occur:
# 1. Resolve conflicts in affected files
# 2. Stage resolved files: git add <file>
# 3. Continue rebase: git rebase --continue
# (repeat for each conflicting commit)
# Force push to update your fork (required after rebase)
git push origin main --force-with-leaseWarning: Only rebase if:
- No one else is working on your fork's main branch
- You haven't shared these commits with others
- You're comfortable with force pushing
If your ahead commits are unwanted (experiments, mistakes), you can reset to match upstream exactly:
# WARNING: This DELETES all your commits that aren't in upstream!
# First, backup your work if needed:
git branch backup-before-reset
# Fetch upstream
git fetch upstream
# Hard reset to match upstream exactly
git reset --hard upstream/main
# Force push to your fork
git push origin main --forceUse this when:
- Your "ahead" commits were experimental and you don't need them
- Your fork got into a messy state you want to start fresh
- You want an exact mirror of upstream
Do NOT use this if:
- You have commits you want to keep
- Others have forked your fork
To keep your fork automatically synced, create a GitHub Action:
Create .github/workflows/sync-fork.yml:
name: Sync Fork
on:
schedule:
- cron: '0 0 * * *' # Daily at midnight
workflow_dispatch: # Manual trigger
jobs:
sync:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
- name: Sync fork
run: |
git remote add upstream https://github.com/ORIGINAL-OWNER/repo.git
git fetch upstream
git checkout main
git merge upstream/main
git push origin mainReplace ORIGINAL-OWNER/repo with the actual upstream repository.
Note: This only works if there are no conflicts. For repos with frequent conflicts, manual syncing is better.
Best practices to minimize fork divergence:
# 1. Never commit directly to main - use feature branches
git checkout -b my-feature
# ... make changes ...
git push origin my-feature
# 2. Sync before starting new work
git checkout main
git fetch upstream
git merge upstream/main
git push origin main
git checkout -b new-feature
# 3. Keep feature branches short-lived
# Merge or close old branches promptly
# 4. Rebase feature branches before creating PRs
git checkout my-feature
git fetch upstream
git rebase upstream/mainKey principle: Keep your fork's main branch as a clean mirror of upstream. All your work should happen on feature branches.
### Understanding Fork Divergence
When you fork a repository on GitHub, you create an independent copy at that point in time. From that moment:
Time 0: Fork created
Upstream: A--B--C
Your fork: A--B--C (identical)
Time 1: Both repos receive commits
Upstream: A--B--C--D--E--F (upstream adds D, E, F)
Your fork: A--B--C--X--Y (you add X, Y)
Status: "2 commits ahead, 3 commits behind"### The "Commits Ahead" Problem
Commits you're "ahead" by typically fall into categories:
1. Intentional changes: Features or fixes you want to contribute back via PR
2. Local customizations: Changes specific to your use case
3. Accidental commits: Work that should have been on a branch
4. Merged PRs: Pull requests you merged into your fork but weren't accepted upstream
For intentional changes, create a PR to upstream. For local customizations, consider keeping them on a separate branch. For accidents, reset or revert them.
### Choosing Merge vs Rebase
| Situation | Use Merge | Use Rebase |
|-----------|-----------|------------|
| Shared fork (multiple contributors) | ✓ | ✗ |
| Solo fork with clean history preference | ✗ | ✓ |
| Commits already pushed | ✓ | Only with force push |
| Complex conflict resolution | ✓ | ✓ (commit by commit) |
| Public commits others may depend on | ✓ | ✗ |
### Handling Upstream Rebases
If the upstream repository rebased or force-pushed, your fork's history no longer shares common ancestors:
# This can cause "refusing to merge unrelated histories"
# Option 1: Hard reset (lose your changes)
git fetch upstream
git reset --hard upstream/main
git push --force origin main
# Option 2: Rebase with new upstream
git fetch upstream
git rebase upstream/main --onto upstream/main
# Option 3: Cherry-pick your commits onto fresh base
git checkout -b temp upstream/main
git cherry-pick <your-commit-hashes>
git checkout main
git reset --hard temp
git branch -d temp### Multiple Upstream Branches
If you need to sync multiple branches:
# Sync main
git checkout main
git fetch upstream
git merge upstream/main
git push origin main
# Sync develop
git checkout develop
git merge upstream/develop
git push origin develop
# Or use a loop for multiple branches
for branch in main develop release; do
git checkout $branch
git merge upstream/$branch
git push origin $branch
done### Fork vs Clone Considerations
| Scenario | Fork + Clone | Just Clone |
|----------|--------------|------------|
| Contributing to open source | ✓ | ✗ |
| Personal backup of a repo | Either | Either |
| Modifying for personal use | ✓ | ✗ |
| Team collaboration (org repo) | ✗ | ✓ |
| You have push access | ✗ | ✓ |
### Upstream Remote Conventions
While "upstream" is the conventional name, you might encounter:
# Common naming conventions:
upstream # Most common, recommended
original # Alternative
source # Alternative
# Check what you have
git remote -v
# Rename if needed
git remote rename old-name upstream### When "Ahead" Commits Are Desired
Sometimes being "ahead" is intentional:
- Customization forks: You maintain a modified version
- Staging forks: Testing changes before upstream PR
- Archive forks: Preserving deleted upstream repos
In these cases, the "ahead/behind" message is informational, not a problem to fix.
### Sync Strategies for Active Forks
For forks you actively develop:
1. Daily sync: git fetch upstream && git merge upstream/main
2. Before each PR: Sync, then create feature branch
3. After upstream PR merge: Sync to get your merged changes
For forks that are just backups or references:
1. Periodic sync: Monthly or when you remember
2. GitHub Sync button: Easiest, no local action needed
warning: BOM detected in file, this may cause issues
UTF-8 Byte Order Mark (BOM) detected in file
fatal: Server does not support --shallow-exclude
Server does not support --shallow-exclude
warning: filtering out blobs larger than limit
Git partial clone filtering large blobs warning
fatal: Server does not support --shallow-since
Server does not support --shallow-since in Git
kex_exchange_identification: Connection closed by remote host
Connection closed by remote host when connecting to Git server