Someone force-pushed over my commit
Someone pushed with --force, and main no longer has your commit. Your clone still
does, which is all you need. Here is how it looks, how to put it back, and the one command that would quietly
lose it.
$ git fetch From ../origin + 7a20dd4...864afd7 main -> origin/main (forced update) $ 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
Put it back on top
git status says the branches have diverged, one commit each: theirs on origin/main,
yours on your main, and git log shows yours at the top. Rebase your branch onto what
origin/main is now: Git takes your commit off, moves main to their tip, and replays
yours on top. The push after it is a fast-forward.
$ git log --oneline 7a20dd4 api: add the endpoint ab5768d api: the first endpoint 07dec17 readme: the project $ git rebase origin/main Successfully rebased and updated refs/heads/main. $ git log --oneline 5b30b2d api: add the endpoint 864afd7 auth: check the token ab5768d api: the first endpoint 07dec17 readme: the project $ git push To ../origin.git 864afd7..5b30b2d main -> main
Both commits are on main again, yours with a new hash, since it has a new parent. If the two
touched the same lines, the replay stops on a conflict first. What the forced push
itself did is on the forced update page.
The pull that would drop it
git pull --rebase looks like the same move, and here it isn't. A pull works out which commits are
yours from the history of origin/main as your clone has seen it, the fork point. A commit that
was once on origin/main counts as upstream's, and since it is no longer there, the rebase drops
it, and says nothing:
$ git pull --rebase Successfully rebased and updated refs/heads/main. $ git log --oneline 864afd7 auth: check the token ab5768d api: the first endpoint 07dec17 readme: the project $ git reset --hard ORIG_HEAD HEAD is now at 7a20dd4 api: add the endpoint $ git rebase origin/main Successfully rebased and updated refs/heads/main. $ git log --oneline 5b30b2d api: add the endpoint 864afd7 auth: check the token ab5768d api: the first endpoint 07dec17 readme: the project
Successfully rebased, and the commit is gone from the log. ORIG_HEAD still points to
where the branch was before the rebase, so git reset --hard ORIG_HEAD is the way back, and then
the rebase above, with origin/main named on the command line, which turns the fork-point rule
off.
If it isn't on your machine
Then it is on the forcer's. Their remote-tracking branch keeps a reflog of its own, and
origin/main@{1} is where main was before their push. Pushing that back is one
command, with a lease so it can't erase anything newer:
$ git reflog show origin/main 864afd7 refs/remotes/origin/main@{0}: update by push 7a20dd4 refs/remotes/origin/main@{1}: fetch: fast-forward ab5768d refs/remotes/origin/main@{2}: update by push $ git push --force-with-lease origin origin/main@{1}:main To ../origin.git + 864afd7...7a20dd4 origin/main@{1} -> main (forced update)
That restores main to before the force, your commit included; the forcer's own commit is now the
one off main, and a rebase and a push, as above, puts it back properly. Hosts keep an erased
commit for a while too, and show forced pushes in a branch's activity, so its hash can still be found there.
Why the game does this to you
In Git Game a push --force erases every commit after the forcer's pointer, and the erased commits
go back to their owners' local branches: the same place Git leaves them. Next day a pull puts you back at the
tip and your push lands on top, which is the rebase above. A reflog armed in your pack does it at
once: it fires inside the forcer's pack and puts your erased commits back on top of main before
the day is over.
Play a day where this happens
One tap starts a game against the bot, which holds the push --force card too.
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.