This error occurs when Git cannot perform a fast-forward merge during a pull operation because your local branch and the remote branch have diverged. You have local commits that are not on the remote, and the remote has commits you don't have locally.
The "fatal: Not possible to fast-forward, aborting" error indicates that Git cannot cleanly apply remote changes to your local branch without creating a merge commit. This happens when the commit histories of your local and remote branches have diverged. In Git, a "fast-forward" merge is the simplest type of merge. It occurs when your local branch can simply move its pointer forward to match the remote branch because there are no new local commits. Think of it like moving a bookmark forward in a book you're reading together with someone else. However, when you have made local commits that don't exist on the remote, and the remote also has new commits, Git can't simply move the pointer forward. The branches have "diverged" and need to be reconciled through either a merge or a rebase. This error commonly appears when: - You have the `pull.ff only` configuration enabled (Git's default since version 2.27) - You're using `git pull --ff-only` explicitly - Multiple developers are working on the same branch - You've made local commits and someone else pushed changes to the remote
First, examine the relationship between your local and remote branches:
# Fetch the latest changes without merging
git fetch origin
# See how your branch differs from the remote
git log --oneline --graph HEAD origin/mainReplace main with your branch name. This shows you:
- Your local commits (on your branch)
- Remote commits (on origin/branch)
- Where they diverged
Example diverged output:
* abc1234 (HEAD -> main) Your local commit
| * def5678 (origin/main) Their remote commit
|/
* 789abcd Common ancestorIf you see a fork like this, your branches have diverged and need to be reconciled.
The cleanest solution is to rebase your local commits on top of the remote changes:
git pull --rebase origin mainOr simply:
git pull --rebaseWhat this does:
1. Fetches remote changes
2. Temporarily removes your local commits
3. Applies the remote commits
4. Replays your local commits on top
Result: A clean, linear history without merge commits.
If you encounter conflicts during rebase:
# Fix conflicts in the affected files, then:
git add <fixed-files>
git rebase --continue
# Or abort if you want to try a different approach:
git rebase --abortIf you prefer to keep a record of the branch divergence, use a merge:
# Explicitly allow merge commits
git pull --no-ff origin mainOr fetch and merge separately:
git fetch origin
git merge origin/mainWhat this does:
Creates a merge commit that combines both lines of development.
Pros:
- Preserves the exact history of what happened
- Easier to resolve complex conflicts
- Can be undone with a single git revert
Cons:
- Creates extra merge commits
- History can become harder to read
If you have uncommitted changes preventing the pull:
# Save your uncommitted changes
git stash
# Pull the remote changes
git pull
# Reapply your stashed changes
git stash popNote: This only helps with uncommitted changes. If you have local commits, use one of the other approaches.
If you have both uncommitted changes AND local commits:
# Stash uncommitted changes first
git stash
# Then rebase your commits
git pull --rebase
# Finally, restore your uncommitted changes
git stash popTo avoid this error in the future, configure Git to rebase by default when pulling:
For a specific repository:
git config pull.rebase trueFor all repositories (global):
git config --global pull.rebase trueAlternative: Allow merge commits during pull:
# Remove the ff-only restriction
git config pull.ff falseCheck your current pull configuration:
git config --get pull.ff
git config --get pull.rebaseAvailable pull.ff values:
- only - Only fast-forward (causes this error when not possible)
- true - Fast-forward when possible, otherwise merge
- false - Always create a merge commit
Warning: This discards local commit history. Only use if you're sure.
If your local history is very complicated and you want to start fresh:
# Save your current changes to a backup branch
git branch backup-my-work
# Reset your branch to match the remote exactly
git fetch origin
git reset --hard origin/main
# Cherry-pick specific commits from your backup if needed
git cherry-pick <commit-hash>Or, if you just want to preserve your file changes (not commits):
# Create a patch of your changes
git diff origin/main > my-changes.patch
# Reset to remote
git reset --hard origin/main
# Apply your changes as uncommitted modifications
git apply my-changes.patchThis gives you a clean slate with the remote history while preserving your work.
If someone force-pushed to the remote branch (rewrote history), you need special handling:
Check if the remote was force-pushed:
git fetch origin
git log --oneline origin/main # Look for unfamiliar commit hashesIf the remote was force-pushed and you have no local changes to keep:
git fetch origin
git reset --hard origin/mainIf you have local commits to preserve:
# Create a backup
git branch backup-before-force-push
# Rebase your work onto the new remote history
git fetch origin
git rebase origin/main
# If there are conflicts, resolve them and continue
git rebase --continuePrevent force-push issues in the future:
- Enable branch protection rules on your repository
- Require pull request reviews before merging
- Avoid using git push --force on shared branches (use --force-with-lease instead)
Understanding Git's Pull Behavior:
Since Git 2.27, the default behavior changed. Git now warns you and may refuse to pull when it detects diverged branches. This is controlled by pull.ff:
# Check your Git version
git --version
# See the default behavior
git config --get pull.ffThe Three Pull Strategies:
1. `git pull --ff-only` (or pull.ff = only)
- Only succeeds if fast-forward is possible
- Safest option, prevents accidental merges
- Causes this error when branches diverge
2. `git pull --rebase` (or pull.rebase = true)
- Rebases your local commits on top of remote
- Creates linear history
- May require conflict resolution for each commit
3. `git pull --no-ff` (or pull.ff = false)
- Always creates a merge commit
- Preserves branch structure
- Can make history harder to read
Team Workflow Recommendations:
For teams, consider these configurations in your .gitconfig or project documentation:
# Recommended: Rebase for pull, but protect against mistakes
git config --global pull.rebase true
git config --global rebase.autoStash trueThe rebase.autoStash option automatically stashes uncommitted changes before rebasing and restores them after.
Interactive Rebase for Complex Situations:
If you need more control, use interactive rebase:
git fetch origin
git rebase -i origin/mainThis lets you:
- Reorder commits
- Squash multiple commits into one
- Edit commit messages
- Drop unwanted commits
When to Use Merge vs Rebase:
| Scenario | Recommended Approach |
|----------|---------------------|
| Feature branch into main | Merge (preserves feature context) |
| Updating feature branch from main | Rebase (keeps feature commits together) |
| Shared branch with teammates | Merge (safer for collaboration) |
| Personal branch / cleanup | Rebase (cleaner history) |
| After code review | Squash merge (single commit per feature) |
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