
Migrating git repos away from a “master” branch without breaking anyone’s local clones
We’ve started creating all new repos with a main default branch, but there are a number of existing git repositories that still use master as their default branch name. GitHub has been making a similar transition on their site along with the git community.
Searching online will reveal a lot of articles about how to change the default branch on github.com or changing the default branch on a local machine, but we have found that making changes to the origin can cause problems for people who don’t also make certain changes locally. We use some tools which can get confused if git’s internal files (the .git directory) are not accurate anymore.
Below I’ll document the steps we take when we change the default branch of a repo. Then I’ll dig into what the symbolic refs are and why they might not get updated automatically if one follows some other online instructions.
How we change the default branch both remotely and locally
Using the github.com UI, we rename the master branch to main.

This blog post is assuming the remote is named origin; if your remote has a different name then you’ll have to substitute it.
We then locally run:
$ git fetch origin
$ git remote set-head origin -a
$ git branch -m master main
$ git branch -u origin/main main
Below I’ll try to explain each command in detail.
1) git fetch origin
First, we’ve made a change on github.com so we fetch to update the local .git objects and refs about the remote. Fetching will add any missing objects into .git/objects and update the files in .git/refs/remotes/origin/. We can see how those change by lsing before and after the fetch:
$ ls .git/refs/remotes/origin
HEAD master
$ git fetch origin
From github.com:shareup/base64url-apple
- [deleted] (none) -> origin/master
(refs/remotes/origin/HEAD has become dangling)
* [new branch] main -> origin/main
$ ls .git/refs/remotes/origin
HEAD main
We can see the file master is gone and main has appeared. There also is the file HEAD which is curious because there is also a (strange) message from fetch that “refs/remotes/origin/HEAD has become dangling.”
The HEAD symbolic ref for a remote is “optional” according to the documentation for git remote, but every repo on all our machines has it set and some of our internal tooling relies on it being accurate.
We can check what git thinks the HEAD of the origin remote is:
$ git symbolic-ref refs/remotes/origin/HEAD
refs/remotes/origin/master
We could also just cat the file:
$ cat .git/refs/remotes/origin/HEAD
ref: refs/remotes/origin/master
The git fetch command doesn’t update this HEAD file. The git remote command does.
2) git remote set-head origin -a
The git fetch above showed a strange “dangling” error and we can use another command to check on where git thinks different remote branches point to:
$ git branch -r
warning: ignoring broken ref refs/remotes/origin/HEAD
origin/main
We need to update the HEAD of origin in our local .git database over to the main branch.
We can use git remote to query the remote repo which should output the correct HEAD:
$ git remote show origin
* remote origin
Fetch URL: git@github.com:shareup/base64url-apple.git
Push URL: git@github.com:shareup/base64url-apple.git
HEAD branch: main
Remote branch:
main tracked
Local branch configured for 'git pull':
master merges with remote master
So we ask git remote to update the local HEAD ref automatically (that’s what -a means):
$ git remote set-head origin -a
origin/HEAD set to main
And now our remote tracking information from git branch is accurate again:
$ git branch -r
origin/HEAD -> origin/main
origin/main
3) git branch -m master main
Next we rename the local master branch to main. A local branch doesn’t have to be the same name as the remote branch it tracks, but it’s super confusing if it’s not the same. This command doesn’t have any output if it works.
However, renaming a local branch does not change its upstream tracking settings. We can check that with git branch:
$ git branch -vv
* main c75a5de [origin/master] Add test…
We could also cat the .git/config to see the same info:
$ cat .git/config
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
ignorecase = true
precomposeunicode = true
[remote "origin"]
url = git@github.com:shareup/base64url-apple.git
fetch = +refs/heads/*:refs/remotes/origin/*
[branch "main"]
remote = origin
merge = refs/heads/master
4) git branch -u origin/main main
Finally we repoint the local branch to track the new remote branch which will update .git/config:
$ git branch -u origin/main main
Branch 'main' set up to track remote branch 'main' from 'origin'.
We can check and it has indeed been updated:
$ git branch -vv
* main c75a5de [origin/main] Add test…
$ cat .git/config
…
[branch "main"]
remote = origin
merge = refs/heads/main
And we can use git pull to verify that all is working and wired up correctly:
$ git pull
Already up to date.
Why write yet another article about how to rename a git branch?
Many articles online (and GitHub themselves) say to rename the origin’s branch up on github.com and then run some commands locally which end up leaving the remote HEAD ref set incorrectly, leaving an old master branch, or other similar problems.
Update: I spoke to a friend at GitHub and they’ve updated their instructions to include a git remote set-head origin -a line. So now if you follow what the GitHub UI shows everything should work out. 😎 🆒 (•_•) ( •_•)>⌐■-■ (⌐■_■)
Maybe you’ve already renamed your branch away from master and now you are getting some error that seems related. You can check where origin’s HEAD is and update it like this:
# Check where it is now
$ git symbolic-ref refs/remotes/origin/HEAD
refs/remotes/origin/master
# Update it to point to its main
$ git remote set-head origin -a
What even is a symbolic ref?
Symbolic refs are little text files inside the .git/refs directory. Long ago, git used symbolic links to keep track of what a branch “points to”, but now is using little text files because it’s compatible with more OS’s.
We can actually print them out to see what they point to. Here are some examples from my local repo:
$ cat .git/refs/remotes/origin/HEAD
ref: refs/remotes/origin/main
$ cat .git/refs/remotes/origin/main
c75a5dee7dc3bce2eba6d5ee116c4c921022d871
$ cat .git/refs/heads/main
c75a5dee7dc3bce2eba6d5ee116c4c921022d871
$ cat .git/HEAD
ref: refs/heads/main
You can see from this that my origin’s HEAD is pointed to its main branch. The origin’s main branch is pointed to commit c75a5de and so is my local main branch. And finally you can see my local HEAD is pointed at my local main branch. 🤓
Happy git branch renaming 🥳🎊🎉
I hope this can help if you are changing a bunch of default branches (like I am today) 😆