How-to

git push: how to send your changes to a remote repository

Updated: · Skillaria.Top

git push sends local commits from the current branch to a remote repository and moves the branch pointer there. The first time, publish a branch with git push -u origin branch-name; after that, plain git push is enough. If the remote has commits you don't have, pull them with git pull first instead of force pushing.

What git push does

The git push command takes the commits on your local branch that the server doesn't have yet, uploads them to the remote repository (usually called origin), and moves the branch pointer there to your latest commit. Files in your working directory and uncommitted changes go nowhere: only history, meaning commits, gets sent.

It's important to understand that push is not a "sync". It works in one direction: from your local repository to the remote. To get other people's changes, you need git fetch or git pull. Mixing up these directions causes nearly every problem beginners end up asking a mentor about.

One more Git rule: by default, push only allows a fast-forward. The server accepts your commits if they continue the history it already has. If someone pushed their commits before you, your push will be rejected — and that's a safeguard, not a bug.

Command syntax

git push [<remote>] [<local-branch>[:<remote-branch>]]

If you run git push with no arguments, Git takes the current branch and pushes it to the branch set as its upstream (tracking branch). The explicit form is useful when no upstream is configured yet or you want to push a branch under a different name:

git push origin main                  # push local main to origin/main
git push origin feature/login         # push the feature/login branch
git push origin fix:hotfix-123        # push local fix as hotfix-123 on the remote
git push origin HEAD                  # push the current branch under the same name

How to push for the first time

The usual path from a new project to code on GitHub or GitLab looks like this. First you create an empty repository on the server, then connect your local one to it and push a branch.

git init
git add .
git commit -m "Initial commit"
git branch -M main
git remote add origin git@github.com:user/project.git
git push -u origin main

The -u flag (long form --set-upstream) links your local main branch to the remote origin/main. After that you can just type git push and git pull on that branch, and git status will start showing how many commits you're ahead of or behind the server.

To check where the remote points, run git remote -v. If the URL is wrong, fix it with git remote set-url origin <new-url> rather than deleting and recreating the repository.

How to push a new branch

You've created a branch for a task, made some commits, and typed git push. Git replies:

fatal: The current branch feature/login has no upstream branch.
To push the current branch and set the remote as upstream, use

    git push --set-upstream origin feature/login

This isn't an error, it's a hint: the branch has no counterpart on the server yet. Run the suggested command or the short form git push -u origin feature/login. To have Git do this automatically for every new branch, enable this setting (available since Git 2.37):

git config --global push.autoSetupRemote true

Why git push is rejected: rejected, non-fast-forward

The most common situation when working in a team:

! [rejected]        main -> main (fetch first)
error: failed to push some refs to 'github.com:user/project.git'
hint: Updates were rejected because the remote contains work that you do
hint: not have locally.

A teammate pushed their commits before you did. Your history and the server's history have diverged, and if Git accepted your push, your teammate's commits would be lost. The right fix is to pull their changes first and integrate yours on top:

git pull --rebase       # fetch their commits and replay yours on top
# on conflicts: fix the files, git add, git rebase --continue
git push

Instead of --rebase you can do a plain git pull — that creates a merge commit. Both options are correct; most teams prefer rebase for their own local commits to keep history linear. The main thing is not to "fix" the rejection with --force.

git push --force and --force-with-lease

A force push is needed when you've deliberately rewritten your branch's history: ran git commit --amend, git rebase, or squashed commits before review. A regular push won't accept such a branch, because the old commits on the server are no longer ancestors of the new ones.

git push --force-with-lease

--force-with-lease overwrites the branch on the server only if it's still where your local Git last saw it (via the origin/<branch> ref). If a teammate managed to push something, the command refuses, and you won't wipe out their work. A plain --force (-f) does no such check and silently overwrites everything.

There's a catch: if you run git fetch right before, the origin/<branch> ref gets updated and the protection stops working — Git assumes you've "seen" the other commits. So don't fetch right before a force push without looking at what came in.

CommandWhat it doesWhen to use it
git pushFast-forward onlyAlways, by default
git push --force-with-leaseOverwrite with a checkYour own branch after a rebase or amend
git push --forceOverwrite with no checksAlmost never; never on shared branches

Never force push to shared branches (main, develop): everyone who already pulled the old history will have it diverge from the server. Such branches are usually protected in the repository settings too, and the server will respond with protected branch hook declined.

How to push tags

A plain git push doesn't send tags. You have to push them explicitly:

git tag -a v1.2.0 -m "Release 1.2.0"
git push origin v1.2.0          # a single tag
git push --tags                 # all local tags
git push --follow-tags          # commits plus the annotated tags pointing to them

--follow-tags is handier than --tags: it won't push stray local lightweight tags, only annotated ones reachable from the commits being pushed.

How to delete a remote branch

Deleting is also done with push:

git push origin --delete feature/login
# old syntax, does the same thing:
git push origin :feature/login

Your local branch stays; delete it with git branch -d feature/login. And your teammates will still have a stale origin/feature/login ref until they run git fetch --prune.

Useful git push flags

FlagPurpose
-u, --set-upstreamRemember the remote branch as upstream
-n, --dry-runShow what would be pushed without sending anything
--allPush all local branches
--tags / --follow-tagsPush tags
-d, --deleteDelete a branch or tag on the remote
--force-with-leaseSafely overwrite history
-v, --verboseVerbose output
--no-verifySkip the pre-push hook (use deliberately)

Common git push errors and how to fix them

Permission denied (publickey)

The server didn't accept your SSH key. Check that the key exists (ls ~/.ssh), that the public part is added to your account, and that the connection works: ssh -T git@github.com. If the remote uses HTTPS, Git will ask for a username and token, not a key — check git remote -v.

Authentication failed over HTTPS

GitHub and many other services stopped accepting your account password over HTTPS a long time ago. You need a personal access token or to switch to SSH. A credential helper can store the token: git config --global credential.helper store (stores it in plain text) or your OS credential manager.

src refspec main does not match any

You don't have a branch with that name. Most often the empty repository has no commits yet, or the branch is called master. Check git branch and either make a commit or rename the branch: git branch -M main.

Everything up-to-date, even though you have changes

You forgot to commit. git push sends commits, not files. Run git status, then git add and git commit. The other possibility is that you're on a different branch from the one you expect to see on the server.

File too large

Hosting services limit file size (100 MB on GitHub). If such a file has already made it into history, deleting it in a new commit isn't enough: you need to rewrite history or remove it from the not-yet-pushed commits (git reset --soft, then commit again). For large binary files, use Git LFS.

A typical workflow with git push

  1. git switch -c feature/x — a branch for the task.
  2. Commit in small, logical steps.
  3. git fetch and git rebase origin/main — bring in the latest main branch.
  4. git push -u origin feature/x — publish the branch.
  5. Open a pull request; after review, add more commits and run a regular git push.
  6. If you had to rewrite history — git push --force-with-lease.

Before pushing, it helps to see exactly what will go to the server: git log origin/main..HEAD --oneline lists the commits the server doesn't have yet.

One-time setup

A few global settings save you from most push surprises. push.default controls what git push sends with no arguments. The default in modern versions is simple: only the current branch is pushed, and only to the upstream branch with the same name. You usually don't need to change it, but check that an old config doesn't still have matching, which pushes all branches with matching names at once.

git config --global push.default simple
git config --global push.autoSetupRemote true
git config --global push.followTags true
git config --global pull.rebase true

push.followTags makes --follow-tags the default behavior, and pull.rebase spares you extra merge commits when a push is rejected and you need to pull other people's changes first.

The pre-push hook

The .git/hooks/pre-push file runs before pushing. If it exits with a non-zero code, the push doesn't happen. Teams often run tests or a linter there so broken code never reaches the server. Bypass the hook with --no-verify only when you know exactly why it's failing.

Wrapping up

git push publishes your commits to a remote repository and only works in one direction. Push a branch for the first time with -u; when rejected, pull other people's changes first; and push rewritten history on your own branch only with --force-with-lease. Working with remotes, push, pull and SSH keys is covered hands-on in the Git course.

FAQ

Does git push send uncommitted changes?

No. Push only sends commits; changes in your working directory and staging area stay local until you commit them.

How do I undo a git push?

On your own branch, you can undo the commit locally (git reset) and push with git push --force-with-lease. On a shared branch, it's safer to create an inverse commit with git revert and do a regular push.

Can I push just one commit?

Yes, by specifying its hash: git push origin abc1234:main pushes all history up to and including that commit, but none of the later ones. You can't push a commit without its ancestors.

What's the difference between git push and git push origin main?

With no arguments, Git uses the configured upstream of the current branch, while the explicit form names the remote and branch directly and works even without an upstream.

How do I push to two repositories at once?

Add a second remote and push to each by name, or give origin two push URLs with git remote set-url --add --push origin url (run it for both URLs, including the original one, otherwise pushes will go only to the second).

Learn this topic on video

This topic is covered in depth with practice in the course “Git from Scratch to Confident Developer”.

Go to course