Home / Alt manpages / git-push(1)

  • git-push(1)
  • User command
  • linux

Push a Git Branch Safely with Upstream Checks and Leases

You will push one local Git branch to the intended remote branch, confirm what changed, and know how to recover from the two common surprises: an upstream mismatch and a non-fast-forward rejection. The examples use Git 2.43.0, as installed with the local git-man package.

Allow about 10 minutes if the remote is already configured. You need a Git repository with at least one commit and permission to write to its remote. No elevated privileges are needed. Do not use sudo to solve an authentication or remote-branch problem: it can change which credentials and configuration Git uses.

1. Check the branch and its destination

Start in the repository that contains the commit you want to publish. Check the current branch, working tree and configured remotes:

$ git branch --show-current
$ git status --short --branch
$ git remote -v

The first command should print a branch name such as feature/reporting. The status output should show the branch and, ideally, no unexpected file changes. The remote list should show the repository you expect, commonly named origin.

Before pushing, inspect the upstream relationship rather than relying on memory:

$ git branch -vv
* feature/reporting 1a2b3c4 [origin/feature/reporting: ahead 2] Add report export

The line in brackets identifies the remote-tracking branch used for comparison. If there is no bracketed upstream, the branch has not been connected to one. A plain git push normally consults the current branch's remote configuration, then defaults the remote to origin if no remote is configured. With no explicit refspec, Git's default is normally simple: push the current branch to a same-named upstream, and refuse when the names do not match.

2. Preview the exact update

Use a dry run before the real push. It contacts the remote and checks the proposed update, but does not send the ref update:

$ git push --dry-run origin HEAD:feature/reporting
To ssh://[email protected]/team/reports.git
   9f8e7d6..1a2b3c4  HEAD -> feature/reporting

HEAD:feature/reporting means the commit checked out now becomes the remote branch named feature/reporting. Naming both sides avoids a branch-name guess. A dry run is a check, not a reservation: another writer can update the remote before the real command.

If the preview names an unexpected host or branch, stop. Correct the remote or refspec and preview again:

$ git remote get-url origin
$ git ls-remote --heads origin feature/reporting

The second command prints the remote object ID and full ref when the branch exists. It makes the destination visible without changing either repository.

3. Push the branch and set its upstream when needed

For an existing, same-named upstream, the short command is usually enough:

$ git push

For a new local branch, explicitly choose the remote branch and record that relationship with --set-upstream:

$ git push --set-upstream origin feature/reporting
branch 'feature/reporting' set up to track 'origin/feature/reporting'.

After this, plain git push and git pull know the usual destination for this branch. The upstream is local configuration; setting it does not create a second copy of your work.

For a script or a branch whose local name differs from the destination, keep using an explicit refspec:

$ git push origin HEAD:refs/heads/release-candidate

The destination is an actual remote ref. A source such as HEAD can be an object expression, but the destination must name a ref. If the push succeeds, Git reports a ref status such as feature/reporting -> feature/reporting. A leading * means a new remote ref; a blank flag means a successful fast-forward; = means already up to date.

4. Resolve a non-fast-forward rejection without deleting history

A branch push is normally accepted only when the remote tip is an ancestor of the commit you are sending. If somebody else pushed first, Git may report:

! [rejected]        feature/reporting -> feature/reporting (non-fast-forward)
error: failed to push some refs

Fetch the remote history, then inspect the divergence:

$ git fetch origin
$ git log --oneline --graph --decorate --all -20

Integrate the remote commit with your team's chosen workflow. A merge keeps both lines of history:

$ git merge origin/feature/reporting
$ git push

Or rebase your unpublished commits on the remote tip if your project permits rebasing:

$ git rebase origin/feature/reporting
$ git push

Resolve any conflicts, then continue or abort the operation as Git instructs. For a merge, git merge --abort returns to the pre-merge state when possible. For a rebase, git rebase --abort does the equivalent. Do not run git push --force just to silence this rejection. A forced update can make commits unreachable from the branch.

5. Rewrite an existing branch only with a lease

Rewriting a branch after an amend or rebase is sometimes intentional, but it changes published history. Warn collaborators first and make sure you have a recovery point. Prefer a lease over unrestricted force:

$ git fetch origin
$ git push --force-with-lease origin HEAD:feature/reporting

The lease asks Git to update the remote only if its current value matches the remote-tracking value you have. If the remote moved since your fetch, the command fails instead of overwriting that work. For the strongest, explicit check, record the expected object ID and supply it:

$ git rev-parse origin/feature/reporting
9f8e7d6c5b4a32100112233445566778899aabb
$ git push --force-with-lease=feature/reporting:9f8e7d6c5b4a32100112233445566778899aabb \
    origin HEAD:feature/reporting

Do not treat a lease as a lock. Background fetches can update your remote-tracking refs and change what the lease means. Never use --mirror or --prune casually: they can update or remove multiple remote refs. Deleting a single remote branch is also destructive:

$ git push origin --delete feature/obsolete

Only run that command after checking the exact branch name and agreeing that its remote ref may disappear. Recovery depends on the commit still being available locally or in another clone, so record its object ID before deletion if the history matters.

6. Verify the remote result

After a successful push, compare the remote ref with your intended commit:

$ git rev-parse HEAD
1a2b3c4d5e6f77889900aabbccddeeff00112233
$ git ls-remote --heads origin feature/reporting
1a2b3c4d5e6f77889900aabbccddeeff00112233	refs/heads/feature/reporting

The IDs should match. If the remote reports remote rejected, the server may have rejected a hook, deletion or non-fast-forward update. If it reports a transport or remote failure, check connectivity and the server before repeating a command that could have partially completed elsewhere.

Done means

  • You checked the current branch, remote URL and upstream destination.
  • You previewed the intended ref update with git push --dry-run.
  • You pushed the expected branch and verified its remote object ID.
  • You integrated remote work instead of forcing past a non-fast-forward rejection.
  • For an intentional rewrite, you used a reviewed --force-with-lease and warned collaborators.