Introduction
git push uploads commits to a remote and updates that remote's refs. Unlike fetch, it modifies state on the server, so safety mechanisms exist to prevent overwriting other people's work.
The sequence
- Resolve the remote URL and target refspec.
- Open a connection.
- Negotiate capabilities.
- Server advertises current values of refs.
- Client checks each push: is it a fast-forward of the server's ref?
- Client packs missing objects and uploads them.
- Server runs
pre-receivehook. - For each ref, server runs
updatehook and atomically updates the ref. - Server runs
post-receivehook. - Server reports success or rejection to the client.
Commands
git push # current branch to upstream
git push origin main # explicit
git push -u origin feature/login # set upstream
git push origin --delete feature/login # delete remote branch
git push origin v1.0.0 # push a tag
git push --follow-tags
git push --force-with-lease
Fast-forward enforcement
By default, a push is rejected if it would not fast-forward the remote ref. This protects against accidentally overwriting work others have pushed:
! [rejected] main -> main (non-fast-forward)
error: failed to push some refs to 'origin'
hint: Updates were rejected because the tip of your current branch is behind ...
Resolve by fetching and integrating, then pushing again.
Force pushing
git push --force # dangerous: ignores remote state
git push --force-with-lease # safer: refuses if remote moved
git push --force-with-lease=feature/login:abc1234
--force-with-lease requires that the remote ref still equals what your tracking ref says. Always prefer it.
Atomic pushes
git push --atomic origin branchA branchB
With --atomic, either all updates succeed or none do; useful when refs must move together.
Push policies
git config --global push.default simple # current branch to its upstream
git config --global push.default current # current branch to remote of same name
git config --global push.default upstream # current to its upstream's name
git config --global push.autoSetupRemote true # 2.37+: auto -u for new branches
Server-side hooks
Hosting platforms run pre-receive and update hooks to enforce policies (signed commits, branch naming, mandatory CI green). When a push is rejected, the server message names the offending ref or commit. Read the message before retrying.
Common mistakes
Force-pushing to a shared branch like main and rewriting other people's commits. Coordinate, or use --force-with-lease on private branches only. Pushing tags individually and forgetting to push commits, leaving dangling tags on the server. Use --follow-tags. Pushing to a wrong remote because you have multiple configured; git push -v shows exactly where it is going. Finally, expecting git push --delete to delete files; it deletes refs. To delete files, commit a removal and push that commit.