# Fix Git Push Rejected Non-Fast-Forward Force Required
You try to push your local changes to the remote:
git push origin mainAnd receive the error:
! [rejected] main -> main (non-fast-forward)
error: failed to push some refs to 'https://github.com/user/repo.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.Your local branch is behind the remote, and Git refuses to overwrite the remote history.
Understanding Non-Fast-Forward
A "fast-forward" push means the remote branch is an ancestor of your local branch -- Git can simply move the remote pointer forward. A "non-fast-forward" push means the remote has commits that your local branch does not have, and pushing would discard those commits.
Visualize the situation:
Remote: A -- B -- C -- D
Local: A -- B -- E -- FYour local branch diverged from B. The remote has C and D that you do not have. Pushing your E and F would discard C and D.
Step 1: Fetch and Inspect the Difference
First, fetch the remote state without merging:
git fetch originThen compare:
```bash git log --oneline --graph HEAD..origin/main # Shows commits on remote that you don't have
git log --oneline --graph origin/main..HEAD # Shows commits you have that aren't on remote ```
Step 2: Pull and Rebase (Recommended)
Integrate the remote changes using rebase:
git pull --rebase origin mainThis replays your local commits on top of the remote:
``` Before: Remote: A -- B -- C -- D Local: A -- B -- E -- F
After pull --rebase: Remote: A -- B -- C -- D Local: A -- B -- C -- D -- E' -- F' ```
If there are conflicts, resolve them and continue:
git add resolved_file.txt
git rebase --continueThen push normally:
git push origin mainStep 3: Pull and Merge (Alternative)
If you prefer merge commits:
git pull origin mainThis creates a merge commit combining both histories:
A -- B -- C -- D
/ \
Remote: A -- B MERGE
\ /
Local: A -- B -- E -- FStep 4: When Force Push Is Appropriate
Sometimes you DO need to force push -- after an interactive rebase, for example:
git push --force-with-lease origin main**Always use --force-with-lease instead of --force**:
--forceblindly overwrites the remote, discarding any commits others may have pushed--force-with-leasechecks that the remote has not changed since your last fetch. If someone pushed while you were working, it refuses the push, protecting their work.
! [rejected] main -> main (stale info)
error: failed to push some refs to '...'
hint: The remote branch has changed since you last fetched.
hint: Run 'git fetch' and try again.This is exactly what you want -- it prevents accidentally discarding a colleague's push.
Step 5: Configure Default Pull Behavior
Set rebase as the default pull strategy to avoid merge commits:
git config --global pull.rebase true
git config --global fetch.prune trueThe fetch.prune setting removes local tracking references for deleted remote branches, keeping your local state clean.
Step 6: Push Rejected After Amend
If you amended a commit and try to push:
git commit --amend
git push origin main
# ! [rejected] non-fast-forwardThe amend creates a new commit hash. The remote still has the old commit. You must force push:
git push --force-with-lease origin mainThis is safe if you are the only person working on the branch. If others have pulled the branch, communicate with them before force pushing.
Preventing Future Rejections
Push frequently to avoid divergence:
```bash # Before starting work git pull --rebase
# After making commits git push
# Before force pushing git fetch origin git push --force-with-lease ```
Set up a pre-push hook to remind you:
```bash cat > .git/hooks/pre-push << 'EOF' #!/bin/bash remote=$(git rev-parse --symbolic-full-name "$2") local=$(git rev-parse "$1") remote_commit=$(git rev-parse "$remote" 2>/dev/null)
if [ -n "$remote_commit" ]; then ahead=$(git rev-list --count "$remote_commit..$local") behind=$(git rev-list --count "$local..$remote_commit") if [ "$behind" -gt 0 ]; then echo "WARNING: You are $behind commits behind $remote" echo "Run 'git pull --rebase' before pushing" exit 1 fi fi EOF chmod +x .git/hooks/pre-push ```