Table of Contents

Introduction

git push is how your commits leave your machine. It sends the commits the remote doesn't have and then asks the remote to move its branch to your tip. Both halves can be seen on a graph, and the second one explains the rejection message everyone eventually gets.

In this article, we'll:

  1. Watch git push send one commit and move origin/main
  2. See what the remote checks before accepting
  3. Cover the rejected-push case and what to do next

What is git push?

git push (with an upstream set) uploads to the remote every commit reachable from your branch that the remote doesn't already have, then updates the remote's branch to point at your tip. The remote accepts only if the update is a fast-forward, meaning its current tip is an ancestor of yours. Locally, origin/main moves to match, so your view of the remote stays current.

Watch it happen

Our sample repo has one local commit that origin does not have. Here's git push:

  1. Before: main and HEAD are one commit ahead of origin/main, which is still at 8c02d5b.
  2. Git sends 80fd5fd to origin and moves origin's main to it. origin/main moves to 80fd5fd to match.
  3. main and HEAD don't move, and no local commits are created. Push changes the remote, not your branch.

Before and after

Before, git status says main is ahead of origin/main by 1. After, the two labels share a commit and status says nothing about being ahead. Git reports the range it pushed:

The raw git output, if you want to read along in text

git log --oneline --graph --all

before

* 80fd5fd (HEAD -> main) Add contact page
* 8c02d5b (origin/main, origin/HEAD) Update dependencies
* a0b2db3 Add user settings page
| * 1117a34 (origin/feature, feature) Add search tests
| * e5869f0 Fix typo in search box
| * fc19889 Add search box
|/  
* 96c4fc2 Fix header layout
* ae65976 Add login page
* 3e1ffe4 Add project skeleton
* 4114b2c Initial commit

after

* 80fd5fd (HEAD -> main, origin/main, origin/HEAD) Add contact page
* 8c02d5b Update dependencies
* a0b2db3 Add user settings page
| * 1117a34 (origin/feature, feature) Add search tests
| * e5869f0 Fix typo in search box
| * fc19889 Add search box
|/  
* 96c4fc2 Fix header layout
* ae65976 Add login page
* 3e1ffe4 Add project skeleton
* 4114b2c Initial commit

what git printed

To ../your_project.git
   8c02d5b..80fd5fd  main -> main

When the remote says no

If someone else pushed to main since your last fetch, the remote's tip is no longer an ancestor of yours, and the push is rejected:

! [rejected]        main -> main (fetch first)

The remote is refusing to throw away their commit. The fix is to bring their work in first, with git pull (or git fetch then merge or rebase), and push again. --force overrides the check, and is its own page because of what it can overwrite.

git-sim's push simulation runs the real push against a temporary clone of the remote, which was the only way I found to show the result honestly without touching the actual remote. It's also why the pre-flight report says "as of last fetch" about the remote's state: from your machine, that's all anyone can know.

Is it safe?

Safe git-sim pre-flight

Pushes 1 commit(s) to origin/main.

A normal push adds commits to the remote and fast-forwards its branch. The remote's own check refuses anything that would drop commits, so a plain push can't lose work on either side.

Useful forms

  • git push -u origin <branch> pushes a new branch and sets it as upstream.
  • git push origin <tag> or git push --tags sends tags, which plain push skips.
  • git push origin --delete <branch> removes a remote branch.
  • git push --force-with-lease overrides a rejection, but only if the remote is where you last saw it. Its own page.

How to undo it

If the pushed commit shouldn't be there, git revert it and push again. That's the honest undo once a commit is public. Rewinding the remote with a force push works only if nobody has pulled yet.

Try it on your repository

pip install git-sim
git-sim push

git-sim shows which commits would be sent and where the remote branch would end up, and tells you if the push would be rejected.

Common questions

What does git push do?

It uploads the commits your branch has that the remote lacks, then moves the remote's branch to your tip, provided that's a fast-forward.

Why was my push rejected?

The remote branch has commits you don't have, so moving it to your tip would drop them. Pull first, then push.

Does git push send all branches?

No, only the current branch by default (with push.default = simple). git push --all sends every branch, and the git push documentation covers the other push.default modes and flags.

Does git push send tags?

No. git push origin <tag> or git push --tags.

Summary

In this article, we watched git push send one commit and move origin/main while main stayed put, and covered why the remote rejects a push when it has moved and what to do about it.

Next steps

git push --force covers overriding a rejection and what that costs. git pull is the usual way to resolve one.