How to rename a Git branch, locally and remotely
To rename a git branch locally is one command. The upstream still points at the old remote branch afterwards, which is the step everyone misses.
Renaming a local branch is one command:
git branch -m new-name
Renaming it on the remote takes three, because git has no command to rename a remote branch. You push the new name and delete the old one:
git branch -m new-name
git push -u origin new-name
git push origin --delete old-name
Between the first and second commands there is a state that catches almost everyone, and it is invisible unless you look for it. All output here came from a real repository on git 2.50.0.
The step everyone misses
Before the rename, the branch tracks its remote counterpart:
git rev-parse --abbrev-ref --symbolic-full-name @{u}
origin/old-name
Now rename it locally:
git branch -m new-name
The local branch is called new-name. Ask about the upstream again:
origin/old-name
Still pointing at the old remote branch. git branch -m renames the local branch and does not touch the tracking configuration, because it has no way to rename anything on the server.
The consequence is that a plain git push now sends your commits to old-name on the remote, under a branch you are about to delete. Everything looks fine locally and the work goes to the wrong place.
The -u in the second command is what fixes it:
git push -u origin new-name
After that, and after deleting the old remote branch, the upstream is correct:
origin/new-name
and the remote lists only what you expect:
origin/main
origin/new-name
Never skip the -u. Without it the push creates new-name on the remote and leaves your local branch still tracking old-name, which is the same problem with an extra branch on the server.
Renaming the branch you are on
git branch -m new-name with one argument renames the current branch. That is the common case and the shortest form.
To rename a branch you are not on, give both names:
git branch -m old-name new-name
Unlike deleting, renaming the branch you are standing on is allowed. Git updates HEAD to follow it, so nothing is left dangling.
-m and -M
git branch -m new-name # refuse if new-name already exists
git branch -M new-name # overwrite it
-M is the force version, and here "force" means "discard the branch that already has this name". That is a genuine way to lose work, and unlike a forced delete there is no warning about unmerged commits.
Use -m and let it fail. If it does, the name is taken and you want to know that before deciding.
Case-only renames
git branch -m Feature feature
On Linux this is unremarkable. On macOS and Windows, where the filesystem is usually case-insensitive, git can decide the old and new names are the same and do nothing.
Rename via a temporary name:
git branch -m Feature temp-name
git branch -m temp-name feature
Two steps, and it works everywhere. The same trick applies to files, and it is worth remembering as the general workaround for case-only renames in git.
What renaming does not change
A branch is a file containing a commit hash, so renaming one renames a label and nothing else:
- No commits change. Every hash stays the same, which is why this is a safe operation and rebasing is not.
- No files change. Your working directory is untouched.
- Open pull requests do not follow. A PR is attached to a branch name on the host. Deleting the old remote branch typically closes the PR, and the new branch needs a new one. Check this before renaming a branch under review.
- CI configuration does not follow. Anything matching on branch names, such as a deploy rule for
release/*, keeps matching the old pattern.
The first two are why renaming feels trivial. The last two are why renaming a shared branch is not.
Renaming a shared branch
If anyone else has the branch, renaming it is a coordination problem rather than a git problem. From their side the old branch vanishes and a new one appears, and their local copy still tracks the deleted one.
Tell them to run:
git fetch --prune
git branch -m old-name new-name
git branch -u origin/new-name new-name
Fetch and prune to drop the dead remote-tracking reference, rename their local copy, then repoint its upstream. That third command is the same fix as the -u above, applied after the fact.
The --prune step matters for the reason covered in deleting a branch: a plain fetch does not remove branches that have disappeared from the remote, so without it they keep a stale origin/old-name indefinitely.
Given all that, the honest advice is to avoid renaming shared branches. A short-lived branch nobody else has is trivial to rename; a branch three people are working on is usually better left with an imperfect name.
Renaming master to main
The most common real instance of this, and worth spelling out because it has an extra step:
git branch -m master main
git push -u origin main
git push origin --delete master
The deletion will be refused if master is still the remote's default branch, which it will be. Change the default branch in the host's settings first, then delete. On GitHub that is Settings, then Branches; other hosts have an equivalent.
Also worth updating afterwards: branch protection rules, CI triggers, and anything with a hardcoded branch name in a deploy script.
For new repositories, set the default once and stop thinking about it:
git config --global init.defaultBranch main
If you already pushed to the old name
The awkward middle state: you renamed locally, ran git push without -u, and your commits went to old-name on the remote.
Nothing is lost. The commits are on the server, just under a branch you were about to delete. Push them where they belong and then clean up:
git push -u origin new-name
git push origin --delete old-name
Since both remote branches point at the same commits, the deletion removes only the label. Check before deleting, though:
git log --oneline origin/new-name -1
git log --oneline origin/old-name -1
If those two hashes match, the deletion is safe. If old-name is ahead, something was pushed there that has not made it across, and that needs resolving first.
Naming, so you rename less
Renaming is cheap locally and expensive once shared, so most of the value is in not needing it.
A convention that survives contact with a real project is usually just a prefix and a description:
feat/search-filters
fix/session-timeout
chore/upgrade-node
The prefix groups related work in a branch list and makes the intent obvious in a pull request title. Some teams add a ticket number, which is worth doing when the tracker is the source of truth and noise otherwise.
Two habits that avoid renames specifically:
Name the branch after the outcome, not the approach. fix/session-timeout stays accurate even if you rewrite the implementation twice. fix/add-redis-cache does not, and that is the branch you end up wanting to rename.
Do not put a date or a version in it. Both go stale, and neither helps anyone find the branch.
Checking it worked
Three commands, and all three should agree:
git branch --show-current # new-name
git branch -r # origin/new-name, no origin/old-name
git rev-parse --abbrev-ref --symbolic-full-name @{u} # origin/new-name
The third is the one worth actually running, because it is the one that silently stays wrong. The first two look correct even when tracking is broken.
Renaming versus starting over
For a branch with only a couple of commits, there is a simpler option than renaming that avoids the tracking problem entirely:
git switch -c better-name
git branch -d old-name
git switch -c creates a new branch at your current commit, so both names point at the same work. Delete the old one and you are done, with the new branch having no upstream yet and therefore nothing stale to fix.
The difference matters when the old branch was already pushed. Renaming keeps the connection to the remote branch and requires you to repair it; creating a new branch starts clean and leaves the old remote branch to be deleted separately.
Neither is more correct. The second is easier to reason about, which is worth something when the alternative is a tracking configuration you cannot see.
Three common mistakes
Pushing without -u. The commits go to the old remote branch, everything looks fine locally, and the branch you are about to delete is the one holding your work. This is the mistake this whole page exists for.
Using -M because -m complained. The complaint means a branch with that name already exists, and -M overwrites it. Unlike a forced delete there is no warning about unmerged commits, so read the error rather than escalating past it.
Renaming a branch with an open pull request. Deleting the old remote branch typically closes the PR, taking its review comments with it. Check for open PRs before renaming anything that has been pushed.
Quick reference
git branch -m new-name # rename current branch
git branch -m old-name new-name # rename another branch
git push -u origin new-name # push it and fix tracking
git push origin --delete old-name # remove the old remote branch
git branch -u origin/new-name # repoint tracking on its own
git branch -vv # show every branch's upstream
Want to rename, push and track branches where a mistake costs nothing? Work through Git basics in a real repository in the browser.
More from the blog
Objects are not valid as a React child: what to render
Objects are not valid as a React child, and React tells you which one by listing its keys. In React 19 the promise version throws a different error.
Read morenpm install errors: read the code, not the stack
Every npm ERR code names one cause. ERESOLVE, E404, EJSONPARSE and the rest, with what each one means, measured against real installs.
Read moreReady to write some code?
Put this into practice - start your first free lesson. No setup, no credit card.