Introduction
git push uploads local commits to a remote and updates that remote's branches and tags. It is how your work becomes visible to collaborators.
Basic push
git push # current branch to its upstream
git push origin main # explicit
git push -u origin feature/login # set upstream and push
The -u (or --set-upstream) flag links the local branch to the remote one so future git push and git pull need no arguments.
Pushing tags
Tags are not pushed by default:
git push origin v1.2.3
git push origin --tags # all tags
git push --follow-tags # tags reachable from pushed commits
--follow-tags is the safest default for release workflows.
Deleting remote branches
git push origin --delete feature/login
# equivalently
git push origin :feature/login
Force pushing safely
Rewriting history (rebase, amend, squash) requires a force push. Always prefer --force-with-lease, which refuses to clobber refs that moved on the server:
git push --force-with-lease
git push --force-with-lease=feature/login:abcd1234
Never --force blindly to a shared branch like main.
Configuring push defaults
git config --global push.default simple # default since 2.0
git config --global push.autoSetupRemote true # 2.37+: auto -u
With push.autoSetupRemote set, plain git push on a new branch creates the remote branch and links them automatically.
What happens during push
- Local Git negotiates with the remote to find common commits.
- It packs and uploads only the missing objects.
- The remote runs
pre-receiveandupdatehooks. - If accepted, refs are updated atomically.
- The
post-receivehook runs (CI, mirroring, etc.).
Atomic and signed pushes
--atomic turns multi-ref pushes into a single transaction; either every ref updates or none do, which prevents partial deployments. --signed sends a GPG-signed push certificate that some hosts log for audit:
git push --atomic origin main release/1.2
git push --signed origin main
For automated systems, the --receive-pack option lets you specify an alternate receive-pack on the server, occasionally needed for hosts with non-standard binaries. Always use git push --dry-run when scripting destructive pushes for the first time.
Common mistakes
Pushing to main when you meant a feature branch, because you forgot which branch you were on. Run git status first. Using git push --force against main and rewriting other people's commits; recover with the reflog of the remote (if you have shell access) or by re-pushing from a teammate's clone. Pushing without configured credentials and getting hung on a password prompt; configure a credential helper or SSH key. Finally, large pushes can fail at the last moment if a hook rejects them; if so, the rejection message names the offending commit. Read it carefully rather than re-running blindly.