Git Developer Guide

Git push says “Everything up-to-date” but your branch is ahead

Find out what push is actually comparing

Everything up-to-date means push looked at the branch it was going to send, on the remote it was going to send it to, and found the remote already had it. Your branch is ahead of 'origin/main' by N commits compares your branch with its upstream. When the two disagree, push and status are looking at different things. Two commands show which:

git status -sb
git push --dry-run -v

The first line of git status -sb is the branch you are on and its upstream. --dry-run -v prints the remote push would use and each local -> remote branch pair, without sending anything:

$ git status -sb
## main...origin/main [ahead 1]
$ git push --dry-run -v
Pushing to [email protected]:you/app.git
To [email protected]:you/app.git
 = [up to date]      main -> main
Everything up-to-date

Here the push goes to a fork, not to origin, which is cause 3 below. Match what you see against these five causes. Each one was reproduced with Git 2.50 using a throwaway bare remote and two clones.

1. The unpushed commits are on a different branch

git push with no arguments pushes only the branch you have checked out (Git's default push.default=simple). If the commits are on another branch, there is nothing to send:

$ git branch -vv
  feature cd01bfa [origin/feature: ahead 1] more feature work
* main    1797aca [origin/main] init
$ git push
Everything up-to-date

The same happens if you name the wrong branch, such as git push origin main while your work is on feature. git branch -vv shows the ahead count for every local branch. Push the one that has the commits:

git push origin feature

2. The upstream is a branch with a different name

This is the most common one with pull request workflows. Creating a branch from a remote branch sets that remote branch as its upstream:

$ git switch -c fix-login origin/main
Switched to a new branch 'fix-login'
branch 'fix-login' set up to track 'origin/main'.

You commit, then push with git push origin fix-login or git push origin HEAD. That creates origin/fix-login, but the upstream is still origin/main, so git status keeps counting your commits against main, and every later push of the same branch has nothing to do:

$ git status
On branch fix-login
Your branch is ahead of 'origin/main' by 2 commits.
  (use "git push" to publish your local commits)
$ git push origin fix-login
Everything up-to-date

A plain git push refuses instead, with fatal: The upstream branch of your current branch does not match the name of your current branch. If you have set push.default to current, the plain push goes to origin/fix-login and says Everything up-to-date too. The fix is to point the upstream at the branch you actually push to:

git push -u origin fix-login
$ git push -u origin fix-login
Everything up-to-date
branch 'fix-login' set up to track 'origin/fix-login'.
$ git status
On branch fix-login
Your branch is up to date with 'origin/fix-login'.

git branch --set-upstream-to=origin/fix-login does the same without pushing. To stop it happening, create branches with git switch -c fix-login --no-track origin/main, or set git config --global branch.autoSetupMerge simple so Git only sets an upstream automatically when the names match.

3. Pushes go to a different remote

In a fork workflow, remote.pushDefault or branch.<name>.pushRemote sends pushes to your fork while the upstream, and so the ahead count, stays on origin. Once the fork has the commits, push has nothing to do, but origin still lacks them:

$ git status
On branch main
Your branch is ahead of 'origin/main' by 1 commit.
$ git push
Everything up-to-date

The Pushing to line from git push --dry-run -v gives it away. To see the setting and where it is configured:

git config --show-origin --get-regexp push

If origin really should get the commits, name it: git push origin main. Usually it should not, and the ahead count is just telling you the pull request has not been merged yet.

4. A stale remote-tracking ref (harmless)

If the same commits reached the remote some other way (pushed from another clone or machine, pushed by URL instead of by remote name, or pulled and pushed by a teammate), your origin/main is out of date and still shows them as unpushed. Push correctly finds nothing to send, and as part of the push Git updates origin/main, so the ahead count disappears:

$ git status
Your branch is ahead of 'origin/main' by 2 commits.
$ git push
Everything up-to-date
$ git status
Your branch is up to date with 'origin/main'.

Nothing to fix here. A git fetch clears it the same way. The opposite case, where status says up to date but the remote has moved on, is covered in git says your branch is up to date but the remote has new commits.

5. You committed on a detached HEAD

After checking out a commit or tag, or in the middle of a rebase, new commits belong to no branch. Pushing the branch you think you were on sends nothing, because that branch never moved:

$ git push origin main
Everything up-to-date
$ git log --oneline main..HEAD
374750e detached work

A plain git push fails instead, with fatal: You are not currently on a branch. Put the commits on a branch before you switch away, then push that branch:

git switch -c rescue
git push -u origin rescue

If you have already switched away, the commits can still be found. See Git detached HEAD: what it means and how to fix it.

How GitMon shows it

GitMon compares each repository's checked-out branch with the upstream configured in .git/config, the same one git status uses. So in the mismatched-upstream case it shows the same ↑ count until you run git push -u, and it flags a detached HEAD as its own state rather than as an ahead count. It is read-only and never pushes. Open the repo from its row and use the commands above.

Download GitMon Free Trial Learn more

Related Guides