Skip to main content

git push — Uploading to a Remote

What It Does

git push uploads your local commits to a remote repository, making them available to other team members. It transfers only the objects (commits, trees, blobs) that the remote does not yet have, making it efficient.

A push is a fast-forward operation by default — the remote will only accept a push if its branch can be fast-forwarded to your commit (i.e., your local branch is ahead of the remote with no divergence). If the remote has commits you don't have, Git will reject the push and ask you to pull first.


Syntax

git push [remote] [branch]

Common Usage

Push current branch to origin

git push origin feature/JIRA-123-add-export

Push and set upstream tracking (first push of a new branch)

git push -u origin feature/JIRA-123-add-export
# After this, plain `git push` and `git pull` work without specifying remote/branch

Push to a differently named remote branch

git push origin local-branch:remote-branch

Push all local branches

git push --all origin

Push tags to remote

# Push a specific tag
git push origin v1.2.0

# Push all tags
git push --tags

Delete a remote branch

git push origin --delete feature/JIRA-123-add-export
# or shorthand:
git push origin :feature/JIRA-123-add-export

Force Push

Force push replaces the remote branch with your local branch, discarding any commits on the remote that are not in your local history. Use with extreme care.

--force (dangerous — avoid on shared branches)

git push --force origin feature/my-branch

This can destroy your teammates' commits if they have pushed to the same branch.

--force-with-lease (safer force push)

This is the correct force push to use. It refuses to force-push if the remote has commits you have not fetched — protecting against accidentally overwriting someone else's work:

git push --force-with-lease origin feature/my-branch

When is force push acceptable?

  • After rebasing or amending your own feature branch (before others have based work on it)
  • Cleaning up a PR branch before merge
  • Pushing to your own fork

When is force push NEVER acceptable?

  • main / master
  • develop
  • release/**
  • Any branch that is shared with teammates who have already pulled from it

Protect branches from force push

Configure branch protection in GitHub / GitLab / Bitbucket:

# .github/branch-protection.yml (GitHub Actions / API)
# Require PRs, prevent force push, require status checks on main:
branches:
- name: main
protection:
required_pull_request_reviews:
required_approving_review_count: 1
allow_force_pushes: false
allow_deletions: false
required_status_checks:
strict: true

Upstream Tracking

When a local branch tracks a remote branch, Git knows how far ahead or behind you are:

# Check tracking relationships
git branch -vv

# Output:
# * feature/JIRA-123 a3f9bc2 [origin/feature/JIRA-123: ahead 2] feat: add export
# main 91bc3f0 [origin/main] chore: bump Spring Boot version

# Set tracking on an existing branch
git branch --set-upstream-to=origin/main main

Push Hooks

Git runs hooks before and after push. Useful for pre-push validation:

# .git/hooks/pre-push  (must be executable: chmod +x)
#!/bin/bash

echo "Running pre-push checks..."

# Block push if tests fail
./mvnw test -q
if [ $? -ne 0 ]; then
echo "❌ Tests failed — push blocked"
exit 1
fi

echo "✅ Pre-push checks passed"
exit 0

Useful Flags Summary

FlagMeaning
-u / --set-upstreamSet tracking and push
--allPush all local branches
--tagsPush all tags
--forceOverwrite remote unconditionally (dangerous)
--force-with-leaseForce push only if remote hasn't changed (safe)
--deleteDelete a remote branch or tag
--dry-runShow what would be pushed without doing it
-v / --verboseShow details of what is being transferred
--no-verifySkip pre-push hooks (use sparingly)

Never Force-Push to Shared Branches

git push --force on main or develop will rewrite the public history and corrupt every teammate's local clone. Use --force-with-lease on feature branches only, and never on protected branches.