Rename a Git branch without losing track of its upstream
Renaming a local Git branch is a ref update, not a rewrite of its commits. The files, commit IDs, and ancestry stay put; the human-readable name moves. That makes the local operation simple, but a production r…
Renaming a local Git branch is a ref update, not a rewrite of its commits. The files, commit IDs, and ancestry stay put; the human-readable name moves. That makes the local operation simple, but a production repository can still become confusing when an upstream, open pull request, CI rule, deployment trigger, or teammate continues using the old name.
First decide what “rename” includes
If the branch has never left your workstation, the change is local and reversible. If it has been pushed, there is no atomic remote rename: you publish the new ref, update consumers, and eventually delete the old ref. A hosting platform may also attach pull requests, protection rules, default-branch settings, environments, and webhooks to the old name.
Freeze automated releases while changing a production branch. Then capture the current state without modifying it:
git status --short --branch
git branch --show-current
git branch -vv
git rev-parse HEAD
git remote -v
Record the current commit ID. A clean working tree is not strictly required for a branch rename, but it removes ambiguity during verification. If git branch --show-current prints nothing, HEAD is detached; switch to the intended branch before renaming it.
Rename a branch that exists only locally
While checked out on the branch:
git branch -m release-ready
From another branch, name both sides:
git branch -m release-candidate release-ready
Current Git documentation defines -m as move/rename and confirms that Git moves the branch configuration and reflog with it. Use -M only when deliberately overwriting an existing destination name; it is the forced form, not a special case-only rename switch.
Verify identity rather than trusting the prompt:
git branch --show-current
git rev-parse HEAD
git reflog show release-ready -5
git status --short --branch
The commit ID should equal the value recorded before the rename. On a case-insensitive filesystem, a capitalization-only change can be awkward for surrounding tools. Prefer a temporary intermediate name over forcing through an unexpected collision:
git branch -m temporary-branch-name
git branch -m Release-Ready
Move a published branch as a controlled migration
Fetch first and identify the remote tip. Do not delete anything yet.
git fetch origin --prune
git ls-remote --heads origin \
refs/heads/release-candidate \
refs/heads/release-ready
After the local rename, publish the same commit under the new name and establish its upstream:
git push --set-upstream origin release-ready
Test the new remote branch through the real workflow: clone or fetch it in a disposable directory, run the build, confirm CI and deployment policy, and update pull requests, branch protections, scheduled jobs, repository documentation, and scripts. Only after consumers have moved should you remove the old remote ref:
git push origin --delete release-candidate
git fetch origin --prune
git branch -vv
A remote-tracking name such as origin/release-candidate is local observation state, not the server branch itself. Deleting it locally does not rename or delete the remote ref.
Failure modes that look like data loss
- “A branch named … already exists”: inspect both tips with
git show-ref --verify. Do not use-Muntil you know why the destination exists. - The new branch pushes to the old upstream: inspect
git branch -vv, then usegit branch --set-upstream-to=origin/release-readyif needed. - CI no longer runs: branch filters and protected environments often match literal names. Restore the old remote name temporarily or update the policy under review.
- A collaborator recreates the old branch: their checkout still tracks the retired ref. Communicate the cutover and give an explicit fetch/switch sequence.
- The default branch was renamed: change the hosting platform’s default setting before deleting the old name; local Git commands alone cannot update server-side policy.
Rollback and recovery
Before remote deletion, rollback is simply another local rename. After publishing both refs, recreate the old remote name from the recorded commit while diagnosing dependent systems:
git push origin <recorded-commit-id>:refs/heads/release-candidate
If the local name was moved incorrectly, the branch reflog records the rename and the original commit remains addressable. Create a recovery branch from the recorded ID instead of guessing:
git branch recovery/release-rename <recorded-commit-id>
Do not run garbage collection or delete reflogs during recovery. A rename normally changes no commit objects, so a mismatched tree or commit ID signals a different operation and should stop the rollout.
Production trade-offs
A cleaner name improves discoverability, but every published name is an interface. Long-lived release branches can be referenced by permissions, cache keys, badges, deployment dashboards, and external automation. Keeping both remote names briefly reduces cutover risk but allows divergence if someone pushes to the old ref. If overlap is necessary, protect or freeze the old branch and publish a retirement time.
A branch rename is cheap inside Git; the expensive part is finding every system that treated the old name as a contract.
Attribution and verification
This guide was prompted by Forrest’s Stack Overflow question and siride’s accepted answer, used under CC BY-SA. The rename, reflog, forced-move, upstream, push, and remote-deletion behavior was independently verified against the current official Git documentation for git branch and git push.
Primary source: Review the official reference ↗