Skip to content
masterclass Buddy

Git · Command

git push

git push copies your local commits to a remote repository and moves that remote's branches, tags or other refs to match. It sends only the objects the remote does not already have.

Last updated

How it works

Git first picks a destination. That is either the remote you name, the current branch's upstream, or origin. Next it decides which refs to push, checking these sources in order: refspecs or flags like --all/--tags on the command line, then remote.<name>.push, then push.default (simple by default). Each refspec has the form [+]<src>[:<dst>] and links a local object to a remote ref. Git sends the objects the remote is missing and asks it to update those refs. The remote applies safety rules, such as allowing only fast-forwards on branches, unless you force the update or a hook or config rejects it.

Syntax

git push [--all | --branches | --mirror | --tags] [--follow-tags] [--atomic] [-n | --dry-run] [--receive-pack=<git-receive-pack>] [--repo=<repository>] [-f | --force] [-d | --delete] [--prune] [-q | --quiet] [-v | --verbose] [-u | --set-upstream] [-o <string> | --push-option=<string>] [--[no-]signed | --signed=(true|false|if-asked)] [--force-with-lease[=<refname>[:<expect>]] [--force-if-includes]] [--no-verify] [<repository> [<refspec>…]]

Parameters

NameTypeDescription
<repository>URL | remote name | remote groupWhere to push. This can be a URL, a configured remote, or a remote group. If you leave it out, Git uses the current branch's upstream, or origin.
<refspec>...[+]<src>[:<dst>]Says which source object updates which remote ref. A leading + forces that one ref. An empty <src> deletes <dst>. Patterns with * and exclusions starting with ^ are allowed.
--force-with-lease[=<refname>[:<expect>]]optionForces the update only if the remote ref still has the value you expect. By default, the expected value is your remote-tracking branch.
-u, --set-upstreamflagRecords an upstream (tracking) reference for every branch that is pushed or already up to date.
--tags / --follow-tagsflag--tags pushes every tag. --follow-tags also pushes missing annotated tags that point at commits being pushed.
-d, --deleteflagDeletes the listed refs on the remote.
-n, --dry-runflagRuns the whole push except actually sending the updates.

Key points

  • --atomic asks the server to update all the refs together: either every ref changes or none does. If the server doesn't support atomic pushes, the whole push fails.
  • --follow-tags pushes only the annotated tags that point at the commits you're pushing. That keeps release tags with their commits without uploading every local tag.
  • -u links each pushed branch to its remote branch. After that, plain git pull and git status know which remote branch to compare against.
  • To remove refs on the remote, use --delete or a refspec with an empty source, such as origin :dev. Both do the same thing.
  • Tags are treated differently from branches: an existing tag on the remote can't be moved by a normal push. Branch updates must be fast-forwards and must point at commits.
  • The local pre-push hook runs by default and can stop a push. --no-verify skips it.
  • The default push.default mode, simple, pushes the current branch to a remote branch with the same name.

Common mistakes

  • --force-with-lease with no explicit expected value compares against your remote-tracking refs. If something runs git fetch in the background (an IDE or a cron job, for example), those refs get updated and the protection is lost.
  • --force turns off the ancestry check, the other push rules and any lease checks, so commits on the remote can be lost.
  • --force applies to every ref in the push, not just the current branch. With push.default=matching or several remote.<name>.push entries, it can overwrite other branches. To force a single ref, put + in front of its refspec instead.
  • You can't combine --atomic with a remote group. Git refuses because each remote gets its own separate push.

Example: Publish a branch, then safely rewrite it

#!/usr/bin/env bash
set -euo pipefail
cd "$(mktemp -d)"

# A bare repo stands in for the server
git init --bare -q server.git
git clone -q server.git work && cd work
git config user.email dev@example.com
git config user.name Dev

git switch -q -c feature
echo one > a.txt && git add a.txt && git commit -qm "first"

# First push: create remote branch and record upstream
git push -u origin feature

# Preview what would happen
git commit -q --amend -m "first (reworded)"
git push --dry-run --force-with-lease origin feature

# Rewrite history safely: fails if someone else moved the branch
git push --force-with-lease --force-if-includes origin feature

# Tag a release and push it with its commit
git tag -a v1.0 -m "v1.0"
git push --follow-tags

# Clean up the remote branch
git push origin --delete feature

The script creates a local bare remote, publishes a branch with an upstream, previews and then performs a lease-protected force push, pushes an annotated tag along with its commit, and finally deletes the remote branch.

When to use it

Sharing a new feature branch

You push a new branch with -u so your teammates can see it and later pull and push commands need no arguments.

Updating a rebased pull request

After an interactive rebase, you use --force-with-lease (optionally with --force-if-includes) so you don't overwrite commits a reviewer pushed in the meantime.

Release publishing in CI

A pipeline uses --atomic to push a release branch and its tag together, so the remote never ends up with only one of them.

Mirroring a repository

--mirror copies every ref under refs/ to a backup server, including forced updates and deletions.

Interview questions

QWhat is the difference between --force and --force-with-lease?

--force overwrites the remote ref no matter what it currently points to, so other people's commits can be lost. --force-with-lease only overwrites if the remote ref still has the value you expect. By default that is your remote-tracking branch, but you can give an exact commit with <refname>:<expect>. If someone pushed in the meantime, the push fails and you can fetch and look before trying again.

Follow-up: Why add --force-if-includes? A background fetch can quietly update your remote-tracking ref, which makes a plain lease pass anyway. --force-if-includes also checks that the remote tip appears in your local branch's reflog, meaning you actually integrated it.

QYour push is rejected as non-fast-forward. Why, and how do you fix it without losing work?

A branch update is only accepted if the remote's current tip is an ancestor of the commit you push. When someone else has pushed commits you don't have, your push would discard them. Fetch and merge (git pull), or use git pull --rebase to replay your work on top of theirs, then push the result, which is now a fast-forward.

Follow-up: When is a plain --force push acceptable? Only when you really mean to replace history, for example after amending a commit you already pushed, and you're sure nobody has fetched or built on the old commit.

QWhat does a bare git push do with no arguments?

It pushes to the current branch's remote, or origin if none is set. Because no refspec was given, it checks remote.<name>.push and then push.default. With the default simple mode, it pushes the current branch to a remote branch with the same name, and it errors if you're pushing back to your pull remote and the upstream has a different name.

Follow-up: How can you avoid passing -u on every new branch? Set push.autoSetupRemote=true, which turns on --set-upstream automatically the first time you push a branch that has no upstream.

  • git fetch
  • git pull
  • git remote
  • git config
  • git receive-pack
  • git send-pack
  • githooks
  • gitrevisions

Source: Git - git-push Documentation. Checked 2026-11-02.