git push rejected: non-fast-forward
Someone else's commit reached main before yours, and your commit doesn't have theirs in its
history. Git won't move a branch backwards, so the remote refuses. Here is the line, what Git checked, and the
pull that fixes it.
$ git push
To ../origin.git
! [rejected] main -> main (non-fast-forward)
error: failed to push some refs to '../origin.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.
What Git checked
A push asks the remote to move its main from the tip it has to the commit you're sending. Git
moves a branch forward only: the commit you send must have the remote's tip among its ancestors, so that
nothing already on main falls off it. A move like that is a fast-forward.
Yours isn't one. The remote's tip is bot's commit; your commit was built on the tip before it, so bot's commit
is nowhere in your history. Moving main to your commit would make theirs vanish from the branch.
The remote refuses, and git push prints the refusal beside the ref it couldn't update,
main -> main, with the reason in brackets. The error: line sums it up, and the
hint: lines are Git's advice.
Two hints for one refusal
Which hint you get depends on whether your clone has heard of the other commit. If you fetched or pulled after
it landed, your clone knows origin/main is ahead of what you built on, and Git says your branch
is behind its remote counterpart. If you haven't, your clone has never seen that commit, the reason
in brackets changes to (fetch first), and the hint names what usually did it: another repository
pushing to the same ref.
$ git push
To ../origin.git
! [rejected] main -> main (fetch first)
error: failed to push some refs to '../origin.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.
Same refusal, same cure. In the game, that other repository is another player's pack, run before yours in the day's random order.
What to do now
Pull, then push. A pull fetches the commit you lack and either puts your commit on top of it
(--rebase) or joins the two with a merge commit (--no-rebase). With the other commit
in your history, the push is a fast-forward:
$ git pull --rebase Successfully rebased and updated refs/heads/main. $ git push To ../origin.git 7a20dd4..febe3f7 main -> main
The push prints the move it made, old tip to new. A plain git pull, with no choice between rebase
and merge made yet, stops and asks for one instead:
$ git pull
hint: You have divergent branches and need to specify how to reconcile them.
hint: You can do so by running one of the following commands sometime before
hint: your next pull:
hint:
hint: git config pull.rebase false # merge
hint: git config pull.rebase true # rebase
hint: git config pull.ff only # fast-forward only
hint:
hint: You can replace "git config" with "git config --global" to set a default
hint: preference for all repositories. You can also pass --rebase, --no-rebase,
hint: or --ff-only on the command line to override the configured default per
hint: invocation.
fatal: Need to specify how to reconcile divergent branches.
If the two commits changed the same lines, the pull stops on a conflict, and you resolve it before pushing.
That line, CONFLICT (content), has a page of its own.
What not to do on a shared branch: git push --force. The remote would take your commit anyway,
and the other one would vanish from main. Git marks that move (forced update); it
has a page too.
Why the game does this to you
In Git Game nobody takes turns. Everyone writes a pack for the day, and when the day closes the remote runs
every pack against the shared main in a random order. If another player's push lands before
yours, your push meets exactly this line. That is why a pack pulls before each push, and why the pull is where
you choose what happens to your commit if the two clash. Today a rejected push still costs the op you spent on
it; whether it should is one of the questions the beta is asking.
Play a day where this happens
One tap starts a game against the bot. It pushes every day, so sooner or later it reaches
main first.
Output captured from Git 2.50.1 (Apple Git-155) by scripts/git-output.sh, in a scratch repository with two clones, ana's and bot's, and fixed dates, so the hashes are the same on every run. It is captured again at every Git release.