# Fix Git Push Rejected Non-Fast-Forward Force Required

You try to push your local changes to the remote:

bash
git push origin main

And receive the error:

bash
! [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:

bash
Remote:  A -- B -- C -- D
Local:   A -- B -- E -- F

Your 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:

bash
git fetch origin

Then 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 ```

Integrate the remote changes using rebase:

bash
git pull --rebase origin main

This 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:

bash
git add resolved_file.txt
git rebase --continue

Then push normally:

bash
git push origin main

Step 3: Pull and Merge (Alternative)

If you prefer merge commits:

bash
git pull origin main

This creates a merge commit combining both histories:

bash
A -- B -- C -- D
          /              \
Remote:  A -- B            MERGE
          \              /
Local:    A -- B -- E -- F

Step 4: When Force Push Is Appropriate

Sometimes you DO need to force push -- after an interactive rebase, for example:

bash
git push --force-with-lease origin main

**Always use --force-with-lease instead of --force**:

  • --force blindly overwrites the remote, discarding any commits others may have pushed
  • --force-with-lease checks that the remote has not changed since your last fetch. If someone pushed while you were working, it refuses the push, protecting their work.
bash
! [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:

bash
git config --global pull.rebase true
git config --global fetch.prune true

The 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:

bash
git commit --amend
git push origin main
# ! [rejected] non-fast-forward

The amend creates a new commit hash. The remote still has the old commit. You must force push:

bash
git push --force-with-lease origin main

This 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 ```