Guides

How to rename a Git branch: local, remote, and master to main

Git rename branch: git branch -m old new renames it locally. Then push the new name, delete the old one on the remote, update clones, move master to main.

Published ~10 min read
On this page

Short answer: git branch -m old-name new-name renames a local branch, and on the branch you have checked out git branch -m new-name is enough. A branch on the remote cannot be renamed in place: push it under the new name with git push -u origin new-name, then delete the old name with git push origin --delete old-name. Everyone else who has a clone then fetches with --prune and points their local branch at the new name.

any system · rename locally, then on the remote
git branch -m feature/login-form feature/login     # local rename
git push -u origin feature/login                    # publish the new name, track it
git push origin --delete feature/login-form         # remove the old name on the remote

Every command and every output block below comes from Git 2.39.5, the version Debian 12 ships, run in a throwaway repository with a bare repository beside it as the remote. That is why the push lines read To ../app.git; yours show your Git host’s address.

Cheat sheet: renaming a branch

TaskCommand
Rename the branch you are ongit branch -m new-name
Rename any other local branchgit branch -m old-name new-name
Rename and overwrite an existing branchgit branch -M old-name new-name
Copy instead of rename (keeps the old one)git branch -c old-name new-name
Push the new name and track itgit push -u origin new-name
Delete the old name on the remotegit push origin --delete old-name
Both remote steps in one pushgit push -u origin new-name :old-name
Another clone: drop deleted remote branchesgit fetch --prune
Another clone: track the new namegit branch -u origin/new-name new-name
Refresh origin/HEAD after a default branch changegit remote set-head origin -a
Check a name before using itgit check-ref-format --branch "new-name"
Name new repositories main from the startgit config --global init.defaultBranch main

Rename a local branch with git branch -m

-m stands for move. With two names it renames the first branch to the second, whether or not it is checked out. With one name it renames the branch you are on. Nothing about the commits changes: the branch is a pointer, and only the pointer gets a new name. Git also moves the branch’s reflog and its settings along, including the upstream it tracks, and that is the part that catches people:

any system
git branch -m feature/login-form feature/login
git branch -vv
Example output
* feature/login a937de3 [origin/feature/login-form] Add login form
  main          ef64f71 [origin/main] Initial commit

The local branch is now feature/login, but it still tracks origin/feature/login-form. A plain git push refuses to push between two different names under the default push.default=simple:

Example output · git push after a local rename
fatal: The upstream branch of your current branch does not match
the name of your current branch.  To push to the upstream branch
on the remote, use

    git push origin HEAD:feature/login-form

To push to the branch of the same name on the remote, use

    git push origin HEAD

If the branch was never pushed, you are done after git branch -m. If it was, carry on with the remote below. Git also accepts git branch -m on a brand new repository before the first commit, which is the hint git init prints when it creates master: git branch -m main right after git init just changes the name the first commit will land on.

Rename the branch on the remote

A push can create and delete branch names on a remote, but it has no rename operation. Renaming on the remote is therefore two pushes: one that creates the new name from your local branch and one that deletes the old name.

  1. 1Push the new name and set it as upstream
    any system
    git push -u origin feature/login
    Example output
    To ../app.git
     * [new branch]      feature/login -> feature/login
    branch 'feature/login' set up to track 'origin/feature/login'.

    -u (long form --set-upstream) replaces the stale tracking entry, so plain git push and git pull work again.

  2. 2Delete the old name on the remote
    any system
    git push origin --delete feature/login-form
    Example output
    To ../app.git
     - [deleted]         feature/login-form

    The older spelling git push origin :feature/login-form (an empty source before the colon) does the same. Both steps fit into one push: git push -u origin feature/login :feature/login-form.

  3. 3Check the result
    any system
    git branch -vv
    git branch -a
    Example output of git branch -vv
    * feature/login a937de3 [origin/feature/login] Add login form
      main          ef64f71 [origin/main] Initial commit

Update other clones after a rename

Every other clone, a teammate’s laptop or the checkout on your server, still has a local branch with the old name that tracks a remote branch that no longer exists. A normal fetch keeps the stale origin/old-name around; --prune removes it and shows what happened:

any system · on the other clone
git fetch --prune
git branch -vv
Example output · master was renamed to main on the remote
From ../app
 - [deleted]         (none)     -> origin/master
   (refs/remotes/origin/HEAD has become dangling)
 * [new branch]      main       -> origin/main
* master 95da554 [origin/master: gone] Initial commit

gone means the upstream was deleted. A git pull at this point fails with Your configuration specifies to merge with the ref 'refs/heads/master' from the remote, but no such ref was fetched. Three commands bring the clone in line:

any system · on the other clone
git branch -m master main
git branch -u origin/main main
git remote set-head origin -a
Example output
branch 'main' set up to track 'origin/main'.
origin/HEAD set to main

The last line only matters when the default branch changed: it updates origin/HEAD, the clone’s note of which branch the remote treats as the default. The has become dangling line in the fetch output above means that note still pointed at the deleted branch, and this command repairs it. To make every fetch prune, set git config --global fetch.prune true once.

Rename master to main

Renaming the default branch is the same procedure with one extra step in the middle, and the order matters: the remote refuses to delete the branch it uses as its default. This is what deleting master before switching the default looks like on a bare repository; hosted remotes refuse in a similar way:

Example output · git push origin --delete master, too early (shortened)
remote: error: refusing to delete the current branch: refs/heads/master
To ../app.git
 ! [remote rejected] master (deletion of the current branch prohibited)
error: failed to push some refs to '../app.git'
  1. 1Rename locally and push main
    any system
    git switch master
    git pull
    git branch -m master main
    git push -u origin main
  2. 2Make main the default branch on the remote

    On a hosted remote, change the default branch in your Git host’s repository settings. On a bare repository you host yourself, the default is whatever its HEAD points at; check it from your clone and change it on the server:

    any system · check from the clone
    git ls-remote --symref origin HEAD
    Example output
    ref: refs/heads/master	HEAD
    95da5545b8e1b113e6a8ac91e8ab839e33e04499	HEAD
    on the server · inside the bare repository
    git symbolic-ref HEAD refs/heads/main
  3. 3Delete master on the remote
    any system
    git push origin --delete master
    git remote set-head origin -a
  4. 4Update every reference to the old name

    CI pipelines, deploy scripts, server-side hooks and documentation often name the branch. A recursive grep finds them: grep -rn --exclude-dir=.git 'master' .. Then each clone runs the three commands from the previous section.

For repositories you create from now on, set the initial name once and git init uses it, without the hint it otherwise prints about master:

any system
git config --global init.defaultBranch main

git branch -M: rename even if the name exists

-M is short for --move --force. Where -m stops with fatal: a branch named 'main' already exists, -M goes ahead and replaces the existing branch with the one you rename.

-M also refuses to overwrite the branch you are on: fatal: cannot force update the branch 'master' checked out at '…'. Switch to another branch first.

Common errors and what they mean

MessageCauseFix
fatal: a branch named 'X' already existsThe new name is takenPick another name, or delete the other branch first; -M only if you mean to discard it
fatal: No branch named 'X'.Typo, or the branch exists only on the remotegit branch -a; for a remote-only branch, git switch X creates the local one first
fatal: 'X' is not a valid branch nameSpaces, two dots, a leading dash, a name ending in .lock and similarTest names with git check-ref-format --branch
The upstream branch of your current branch does not match the name of your current branchRenamed locally, still tracking the old remote namegit push -u origin new-name
(deletion of the current branch prohibited)Deleting the remote's default branchChange the default branch first
Your configuration specifies to merge with the ref 'refs/heads/X'Another clone, after the renamegit fetch --prune, then git branch -u origin/new-name
[origin/X: gone]The upstream was deleted on the remotegit branch -u origin/new-name, or git branch --unset-upstream

Changing only the case of a name

On Linux, git branch -m Main main works like any other rename (it did on the Debian 12 test box). macOS and Windows file systems ignore case by default, and Git can store branch names as files there, so a case-only rename may be refused as already exists. Go through a temporary name instead of reaching for -M: git branch -m Main tmp-rename, then git branch -m tmp-rename main.

On a server: deploy checkouts and self-hosted remotes

A rename reaches a server in two places. A checkout that deploys with git pull is just another clone: after the remote rename its next pull stops with the merge with the ref error above, and the deploy quietly keeps serving the old code. Run the three commands from updating other clones in that checkout, as the user that owns it, and update any script or timer that names the branch.

If the remote is a bare repository on your own server, for example one you push to over SSH, set its HEAD with git symbolic-ref as in step 2 above, and read its hooks/post-receive: a hook that checks out refs/heads/master into the web root ignores pushes to main without a word. Pushing over SSH to a server you run needs a key, set up as in generating an SSH key, and an entry in your SSH config file saves typing the port and user each time. A Linux VPS with root access is enough to host bare repositories like this for a small team. If git itself is missing on a fresh server, see git: command not found.

Frequently asked

How do I rename a Git branch?

Run git branch -m old-name new-name, or git branch -m new-name to rename the branch you are on. That renames it locally only. If the branch was already pushed, also run git push -u origin new-name and git push origin --delete old-name.

How do I rename a branch on the remote?

A remote branch cannot be renamed in place. Push your local branch under the new name with git push -u origin new-name, then delete the old name with git push origin --delete old-name. Every other clone then runs git fetch --prune, renames its local branch and points it at the new one with git branch -u origin/new-name.

How do I rename master to main?

Run git branch -m master main and git push -u origin main, then make main the default branch in your Git host's settings, or with git symbolic-ref HEAD refs/heads/main inside a bare repository you host yourself. Only then delete master with git push origin --delete master: the remote refuses to delete its default branch.

What is the difference between git branch -m and git branch -M?

-m refuses to rename a branch onto a name that already exists. -M is move with force: it replaces the existing branch with the renamed one, and the replaced branch's commits are then only reachable through the remote, another branch or the reflog. Use -M only when you mean to throw the target branch away.

Related articles

All guides