Git Developer Guide
git push rejected: Updates were rejected because the remote contains work that you do not have locally
What Git is refusing to do
You committed and ran git push, and Git sent nothing. Somebody, or you on another Mac, pushed to the same branch since you last fetched:
$ git commit -m "Add retry"
[main 7470f76] Add retry
1 file changed, 1 insertion(+)
create mode 100644 retry.txt
$ git push
To github.com:you/app.git
! [rejected] main -> main (fetch first)
error: failed to push some refs to 'github.com:you/app.git'
hint: Updates were rejected because the remote contains work that you do not
hint: have locally. This is usually caused by another repository pushing to
hint: the same ref. If you want to integrate the remote changes, use
hint: 'git pull' before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
A push may only move a remote branch forward. Your branch doesn't contain the remote's newest commit, so accepting the push would drop that commit from the branch, and the server refuses. Nothing is lost: your commit is still on your branch, and the remote is unchanged. The outputs on this page come from throwaway repositories on Git 2.54 (Apple Git-157) with default settings, two clones of one remote standing in for a laptop and a desktop, with the remote's address shown as github.com:you/app.git.
fetch first and non-fast-forward are the same problem
The word in brackets depends on what your repository knows. fetch first means the remote has a commit you have never downloaded. Before that fetch, git status can't see the problem and only reports your own commit:
$ git status
On branch main
Your branch is ahead of 'origin/main' by 1 commit.
(use "git push" to publish your local commits)
nothing to commit, working tree clean
After a fetch, the real picture shows up, and the same push is rejected with the other wording:
$ git fetch
From github.com:you/app
81a2fa2..7b832ad main -> origin/main
$ git status
On branch main
Your branch and 'origin/main' have diverged,
and have 1 and 1 different commits each, respectively.
(use "git pull" if you want to integrate the remote branch with yours)
nothing to commit, working tree clean
$ git push
To github.com:you/app.git
! [rejected] main -> main (non-fast-forward)
error: failed to push some refs to 'github.com:you/app.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. If you want to integrate the remote changes,
hint: use 'git pull' before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
Behind its remote counterpart doesn't mean you have nothing new. The branch has diverged: one commit only you have, one only the remote has. See both sides before you change anything (< is yours, > is theirs):
$ git log --oneline --left-right HEAD...@{u}
< 7470f76 Add retry
> 7b832ad Add API client
Fix 1: pull with rebase, then push
The hint says git pull, but on a diverged branch a bare pull stops and asks how to combine the two sides, ending with fatal: Need to specify how to reconcile divergent branches. (that message has its own guide). Say which one you want. Rebasing replays your unpushed commits on top of the remote's:
$ git pull --rebase
Successfully rebased and updated refs/heads/main.
$ git push
To github.com:you/app.git
7b832ad..de55ffa main -> main
$ git log --oneline --graph
* de55ffa Add retry
* 7b832ad Add API client
* 81a2fa2 init
Your commit got a new hash (7470f76 became de55ffa), which is fine for commits nobody else has. If the rebase stops on a conflict, fix the file, git add it and run git rebase --continue, or git rebase --abort to put everything back the way it was.
Fix 2: pull with merge, then push
If you'd rather not rewrite anything, merge the remote's work into yours and push the merge commit:
$ git pull --no-rebase
From github.com:you/app
de55ffa..d695224 main -> origin/main
Merge made by the 'ort' strategy.
docs.txt | 1 +
1 file changed, 1 insertion(+)
create mode 100644 docs.txt
$ git push
To github.com:you/app.git
d695224..171cd3a main -> main
Either way, the push works because your branch now contains the remote's commit, so the remote branch only moves forward. To stop choosing every time, set pull.rebase once, as the divergent-branches guide explains.
When you rewrote history on purpose
The same rejection appears after you amend or rebase commits you had already pushed. Here nobody else pushed; your amended commit just replaced one the remote already has:
$ git commit --amend -m "Retry with jitter"
[main 6154582] Retry with jitter
Date: Sun Oct 18 12:33:14 2026 -0600
1 file changed, 1 insertion(+)
$ git push
To github.com:you/app.git
! [rejected] main -> main (non-fast-forward)
...
Pulling now would merge the old commit back in, which isn't what you want. If the branch is yours alone, typically a feature branch under review, replace the remote branch with --force-with-lease:
$ git push --force-with-lease
To github.com:you/app.git
+ 974b70e...6154582 main -> main (forced update)
The + and forced update mean the old commit is no longer on the remote branch. The demo uses main to keep it short, but don't do this on a shared branch unless everyone on it knows; anyone who pulled the old commit will now see their branch diverge.
Why --force-with-lease and not --force
--force-with-lease overwrites the remote branch only if it still points where your last fetch saw it. In this run you rebased feature/cache onto a new main, but a teammate had pushed Add cache tests to the branch since your last fetch of it:
$ git push --force-with-lease
To github.com:you/app.git
! [rejected] feature/cache -> feature/cache (stale info)
error: failed to push some refs to 'github.com:you/app.git'
Stale info is the lease doing its job. Plain --force in exactly the same situation goes through, and the teammate's commit is silently dropped from the branch:
$ git push --force
To github.com:you/app.git
+ 91be917...5ee3e26 feature/cache -> feature/cache (forced update)
After a stale info rejection, don't just fetch and retry: a fetch renews the lease, and the retry would drop the commit as well. Fetch, look at what arrived, bring it into your rewritten branch, then push with the lease again:
$ git fetch
From github.com:you/app
a04962b..91be917 feature/cache -> origin/feature/cache
$ git log --oneline origin/feature/cache -2
91be917 Add cache tests
a04962b Add cache
$ git cherry-pick 91be917
[feature/cache 049e609] Add cache tests
Date: Sun Oct 18 12:33:41 2026 -0600
1 file changed, 1 insertion(+)
create mode 100644 test.txt
$ git push --force-with-lease
To github.com:you/app.git
+ 91be917...049e609 feature/cache -> feature/cache (forced update)
How GitMon shows it
This error usually means the same branch moved on two machines: you committed on the laptop while the desktop, or a teammate, pushed. GitMon lists every repository on your Mac with its unpushed (↑) and behind (↓) counts, so a repo showing both is diverged and will reject a push until you pull. It's read-only and never fetches, like git status: the counts come from the remote-tracking refs your last fetch left, so before a fetch the fetch first case shows up only as ↑. Pair it with a fetch loop from git pull or fetch every repo in a folder, and push from the machine you're leaving so the other one never has to reconcile. GitMon never pulls or pushes for you; use the commands above.