Daniel Tull: Blog

Renaming git's default master branch

Monday, 08 June 2020

The Internet Engineering Task Force documents the issue of two pairs of terms common in computing. One of the pairs is around the terms master and slave, which it states “is an oppressive metaphor that will and should never become fully detached from history.” It’s questionable whether git’s use of master derives from this usage, I’m not sure whether it matters because the connotation could exist regardless and the use of master is only a convention which we shouldn’t feel the need to hold on to.

When you initialise a new repository, git creates a branch named master for us by default. In this post, I will look at how we can change this default behaviour to make our environments more inclusive and welcoming by removing potentially troubling language choices.

9th June update: If you’re looking to convert existing repositories, Scott Hanselman has a posted this helpful guide: Easily rename your Git default branch from master to main.

What does git init do anyway?

When creating a new repository, git will copy the contents of a template directory into the .git directory where the repository details go on to live. The standard template directory lives in /usr/local/share/git-core/templates if you’ve installed it with homebrew on the Mac.

There’s a configuration called init.templateDir that will allow us to set a custom template directory for git to use. For years my friend Abizer has preached about git’s model under the hood and how it is entirely based on files, so it’s likely that there’s a file we can modify that will change the default branch name.

If we take a look at the standard templates directory, it has the following structure:

/usr/local/share/git-core/templates
├── description
├── hooks
│   ├── applypatch-msg.sample
│   ├── commit-msg.sample
│   ├── fsmonitor-watchman.sample
│   ├── post-update.sample
│   ├── pre-applypatch.sample
│   ├── pre-commit.sample
│   ├── pre-merge-commit.sample
│   ├── pre-push.sample
│   ├── pre-rebase.sample
│   ├── pre-receive.sample
│   ├── prepare-commit-msg.sample
│   └── update.sample
└── info
    └── exclude

If we make a new directory, do a git init inside of it and then inspect the .git directory, we see the contents of this template directory, with the addition of some new directories where objects and references (branches/tags) will be stored, as well as two new files:

  • config: where configurations for this clone of the repository are stored
  • HEAD: tells git what branch, tag or commit is currently checked out

The contents of the HEAD file tells git that master is the currently checked out reference:

ref: refs/heads/master

refs/heads/master is a reference to a file which will contain the hash for the commit the master branch points to, however refs/heads/master doesn’t exist yet because there isn’t a commit for it to reference. We can see that if we make a commit, the file gets created with its contents being the hash of the commit. Therefore in a fresh repository, only the HEAD file references a “master” branch.

Taking a look at the git source code which creates the default files, we can see that after copying the template directory, the setup process creates the HEAD file “if it does not exist yet”, which tells us that if our template contained a HEAD file already, it should not get overwritten by git’s setup process1.

Setting up a new template

We start by making the template directory and set it in the global git config. I have decided to place mine in my home directory at ~/.git-template. We can use the following command to add the setting:

git config --global --add init.templateDir "~/.git-template"

I started by copying the contents of the standard git template directory, in case I need the hooks templates in future repositories. In the template directory we can then add a file named HEAD with the following contents, making sure to add a line break because unix tools always seem to want to end with a line break.

ref: refs/heads/main

I’ve decided to call my new default branch “main”, partly because it fulfills the description, but also because it also starts with “ma” so when my brain automatically goes to type these first characters and press tab to autocomplete, it will autocomplete to main instead.

If this is all set up correctly, the next time you perform a git init, you’ll see that the branch is main, or whatever name you chose for your default branch.

You’re also welcome to use my git-template repository as a starting point. This was actually the first repository I published which was initialised with my custom git template and I learned that when pushing a new repository, GitHub will take the first branch you push to be its default, so you don’t need to change any configurations there.

  1. As an aside, this sounds a lot smarter than what I actually did, which was to just to search for “refs/heads/master” in a text editor and look for the code that made the most sense for initialising a git repository. 🙂