Moving from 'master' to 'main' in Git with local changes

March 27, 2023

One of the things that various open source Git repositories are doing is changing their main branch from being called 'master' to being called 'main'. As a consumer of their repository, this is generally an easy switch for me to deal with; some day, I will do a 'git pull', get a report that there's a new 'main' branch but there's no upstream 'master', and then I'll do 'git checkout main' and I'm all good. However, with some repositories I have my own local changes, which I handle through Git rebasing. Recently I had to go through a 'master' to 'main' switch on such a repository, so I'm writing down what I did for later use.

The short version is:

git checkout main
git cherry-pick origin/master..master

(This is similar to something I did before with Darktable.)

In general I could have done this with either 'git rebase' or 'git cherry-pick', and in theory according to my old take on rebasing versus cherry-picking the 'proper' answer might have been a rebase, since I was moving my local commits onto the new 'main' branch. However it was clear to me that I would probably have wanted to use the full three-argument form of 'git rebase', which is at least somewhat tricky to understand and to be sure I was doing right. Cherry-picking was much simpler; I could easily reason about what it was doing, and it left my old 'master' state alone in case.

(Switching from rebasing to cherry-picking is an experience I've had before.)

Now that I've written this I've realized that there was probably a third way, because at a mechanical level branches in git don't entirely exist. The upstream 'master' and 'main' branches cover the same commits (up until possibly the 'main' branch adds some on top). The only thing that says my local changes are on 'master' instead of 'main' is a branch head. In theory, what I could have done was just relabeling my current state as being on 'main' instead of 'master', and then possibly a 'git pull' to be current with the new 'main'.

(In the case of this particular repository, it was only a renaming of the main branch; upstream, both the old 'master' and the new 'main' are on the same commit.)

Since I just tried it on a copy of my local repository in question, the commands to do this are:

git branch --force main master
git checkout main
# get up to date:
git pull

I believe that you only need the pull if the upstream main is ahead of the old upstream master.

This feels more magical than the rebase or cherry-pick version, so I'm probably not likely to use it in the future unless there's some oddity about the situation. One potential reason would be if I've published my repository, I don't expect upstream development (just the main branch being renamed), and other people might have changes on top of my changes. At that point, a cherry-pick (or a rebase) would change the commit hashes of my changes, while simply sticking the 'main' branch label on to them doesn't, so people who have changes on top of my changes might have an easier time.

Comments on this page:

By Zertrin at 2023-03-27 23:01:28:

Alternatively again, it is possible to just rename your local branch with git branch -m main, and then reset which upstream branch it tracks with git branch --set-upstream main

From at 2023-03-28 04:34:28:

I agree with Zertrin's suggestion; renaming the current branch is the least intrusive option, as it touches nothing but the ref itself (and the HEAD symbolic ref), leaving both the index state and all of the local commits intact.

git branch -m main
git branch -u origin/main
git remote set-head origin -a

(The local and remote branches don't even need to be renamed at once; you could preemptively have a local main that still tracks origin/master or vice versa. Updating origin/HEAD using set-head is optional, but it lets you continue using origin as a shorter alias for origin/main.)

I wrote my own thing to do this. I'm not sure how well it deals with local changes, but I suspect it should do it correctly, regardless. It was done mostly to automate batch-renaming a bunch of repos, including the remote, so it has a bunch of code to deal with default branches on GitLab, for example:

The shell equivalent is in the source, but copied here for convenience:

       # rename the branch locally if not already done
       git show-ref refs/heads/master > /dev/null && \
         git branch --move master main
       git fetch origin
       # fix HEAD pointer for local remote branch
       git symbolic-ref refs/remotes/remote/HEAD refs/remotes/remote/main
       # push branch remotely if it doesn't exist (or fix tracking branch if it does)
       git branch -u origin/main || exit
       git push -u origin main
       # fix remote HEAD pointer on remote repository
       ssh git symbolic-ref HEAD main
       # delete old branch
       git push -d origin master


One of the things that various open source Git repositories are doing is changing their main branch from being called 'master' to being called 'main'.

This is nonsense makework promoted by MicroSoft and other large corporations to waste the time of people involved with Free Software and open source software.

Written on 27 March 2023.
« My pragmatic shift from PS/2 keyboards and mice to USB ones
An interesting yet ordinary consequence of ZFS using the ZIL »

Page tools: View Source, View Normal, Add Comment.
Login: Password:
Atom Syndication: Recent Comments.

Last modified: Mon Mar 27 22:07:55 2023
This dinky wiki is brought to you by the Insane Hackers Guild, Python sub-branch.