Part V: Chapter 5.5

git push

Sending commits — and why "non-fast-forward rejected" happens.

git pushasks the remote to move its branch pointer forward. The remote will only accept this if the move is a fast-forward — meaning your commits extend the remote's history without rewriting it. If the remote has work you don't, it refuses.

Push in Action

See a push accepted (local is ahead, remote fast-forwards) or walk through the fix for a rejected push (histories diverged).

Remote (GitHub)
ABCDEorigin/mainRemote is at C — 2 commits behind your local
Local (your machine)
ABCDEmain HEAD

Why the remote rejects

The remote only accepts a push if applying it would be a fast-forward — the remote's current commit must be an ancestor of yours. This guarantees nobody's work gets silently overwritten.

When a colleague pushes before you, the remote advances past your fork point. Your branch no longer extends the remote's history — it diverges from it. Pushing would rewrite the shared history, so Git refuses.

The fix is always the same: integrate the remote work first (fetch + merge or fetch + rebase), then push. Now your tip is ahead of the remote's tip — a clean fast-forward again.

Force push — when and why to avoid it

git push --forcetells the remote to accept the push even if it's not a fast-forward — it overwrites whatever was there. This rewrites shared history and destroys colleagues' commits.

It is only safe on branches where you are the sole author and all collaborators know the history is being rewritten — typically feature branches after an interactive rebase.

Prefer --force-with-lease: it fails if anyone has pushed since your last fetch, giving you a safety net against accidentally clobbering others' work.

Push commands reference

Terminal
$# Push current branch to its tracking remote:
$git push
$# Push and set upstream tracking in one step:
$git push -u origin feature/my-branch
Branch 'feature/my-branch' set up to track remote branch 'feature/my-branch' from 'origin'.
$# The fix when rejected:
$git fetch origin
$git rebase origin/main # or: git merge origin/main
$git push
$# Force push (solo feature branches only):
$git push --force-with-lease origin feature/my-branch
$# Delete a remote branch:
$git push origin --delete feature/old-branch