Renaming HEAD in Git

This document dates back to July 2020. In the end, we did change to main for our repositories.

We've discussed potentially moving from "master" to a different name for the primary branch in our repositories. This document isn't about the why or whether of that: I'm happy to discuss the politics any time, but not here. This document is just about what would be required to make such a change, and what downstream effects there might be.

A fair proportion (but by no means all) of the discussion elsewhere has settled on the name "main" as being both relatively inoffensive and starting with the same "ma" prefix, allowing the use of "ma<tab>" for branch name completion by clever shells in both the old and new situation, thus preserving muscle memory.

Example: Change the default branch name for new repositories from `master` to `main` · Issue #63 · github/roadmap

The "ma<tab>" completion convenience isn't as important for us because we already have branches called "maint-xxx". In retrospect, perhaps that wasn't the best choice (my bad). Obviously if we wanted to move to a place where "ma<tab>" worked, we could do so by renaming our maintenance branches (e.g., to "xxx-maint", "mnt-xxx" or something else), and maybe it's worth thinking about given that we're looking at some disruption anyway. It's largely independent to the choice of name for the primary branch, though.

I've performed this change (to main in each case) on a few repositories hosted in GitHub and GitLab, both on my own and some client repositories. GitHub and GitLab are a bit simpler than Gitolite because some of the operations required can be done in the GUI (e.g., changing the HEAD of the remote repository). I have enabled the symbolic-ref command on our Gitolite instance, though, so that this can all be done from the client.

Here's what migrating from "master" to "main" looks like, from the point of view of the person making that change:

git branch -m master main git push -u origin main ssh git@git.shibboleth.net symbolic-ref <reponame> HEAD refs/heads/main git push origin --delete master git fetch --prune origin git remote set-head origin -a

This means:

  1. Rename the local "master" branch to "main". The local repository no longer has a local "master" branch, and HEAD now refers to main.

  2. Push the new local "main" branch to the remote, and set it up for tracking. The remote repository now has both "master" and "main" branches.

  3. Change the remote repository's HEAD to main.

  4. Delete the remote master branch.

  5. Delete the local repository's knowledge of the remote master branch.

  6. Set the local repository's origin remote's HEAD from the remote (-a is "automatic").

Anyone who has an existing checked-out clone of the repository can do this to get back in sync if they don't want to just re-clone:

git fetch --prune origin git checkout main git remote set-head origin -a git branch -d master
  1. Delete the local repository's knowledge of the remote master branch. Acquire knowledge of the remote main branch.

  2. Move from the local master branch to the new local main branch; set it up for tracking.

  3. Set the local repository's origin remote's HEAD from the remote (-a is "automatic").

  4. Delete the local repository's master branch.

We also need to change the tests in the Gitolite configuration to make sure they cover main in the same way as master.

Git version 2.28 (released 2020-07-27 and available from MacPorts) has a configuration variable which if present controls the name of the HEAD branch when "git init" is called:

git config --global init.defaultBranch main

The version of git on http://ec2.shibboleth.net supplied by CentOS 8 is 2.18.2, so doesn't have this option. Git isn't provided by a module in RHEL 8, so I don't think that version is likely to change (my RHEL 8 system is up to 2.18.4, so it looks like they have decided to pin the 2.18 release and just follow security fixes). Of course we could build from source, but there's a maintenance burden there.

Downstream effects of a repository changing

  • Everyone who has a clone of the repository needs to go through the resyncing process, or re-clone.

  • Many Jenkins jobs reference */master explicitly, and will need to be changed. I don't see a reason they wouldn't work given that; I don't think they need the resync dance.

What about new repositories?

If you create a new personal repository in Gitolite by cloning one which does not exist:

  • The remote repository's HEAD is created by Git on the server (via "git init --bare"), and thus points to master (even though there is no such branch). This isn't configurable on our system, which is running the CentOS-supplied 2.18.4.

  • The cloned repository's HEAD is created by Git on the client. If you use init.defaultBranch then this might point to main, i.e., a different branch than on the server.

  • Doing the usual "git push -u origin main" to push the initial commit in the latter case will not change the remote HEAD, which will still point to master (a branch which does not exist).

  • Attempting a new clone at this point will result in "warning: remote HEAD refers to nonexistent ref, unable to checkout." although a local repository is created.

  • Doing "ssh git@git.shibboleth.net symbolic-ref iay/db-main HEAD refs/heads/main" does fix this, allowing cloning as expected.

The same issue comes up if the client creates a new repository with HEAD of main and then adds a remote through which it pushes to a wild repository on Gitolite: because the remote repository is still created using "git init --bare" on the server, its HEAD still references master and must be fixed with symbolic-ref before cloning is possible. There are some references to git-init and template directories and maybe some kind of post-init hook in various places, but I can't see how to use them in this context.

Other Issues

The Gitolite software itself assumes that the configuration repository has a master branch. From the author:

You'd have to patch lib/Gitolite/Hooks/PostUpdate.pm. It's a
trivial change, but it should work fine. Please test it out.

This repository isn't visible except to administrators (currently Scott and myself), though, so perhaps it's not as worth spending time on.

Other links