I was on a test branch and by mistake I run
git checkout masTER
git checkout test
git checkout master
This problem occurs on any case-preserving but case-insensitive file system. By default, then, it occurs on Macs and Windows boxes, and not on Linux and the like.
Git itself is case-sensitive, pretty much everywhere. A file named
master.txt is independent of one named
masTER.txt, and therefore a Git tree object will keep the two as separate items, and Git's index (also known as the cache or the staging-area) considers these separate files. There is a
core.ignoreCase setting in
git config meant to help it recognize when the underlying OS disagrees, but in general Git thinks of these as different.
Since these file names are stored inside files—repository objects of type tree, stored in
.git/objects/, and Git's index, stored in
.git/index1—and not merely handed over to the OS to store, they can be, and therefore are, case-sensitive, even if the OS is not.2
This is true for branch names, and remote-tracking branch and tag names, as well. Since these names are kept in the file
.git/packed-refs, they can be case-sensitive. (Git also stores its idea of your current branch in
.git/HEAD, as a text string, which is therefore case-sensitive.) But these names are not always kept in
.git/packed-refs. In fact, they're often not in
.git/packed-refs at all. An active branch name—one that is being modified—winds up stored in an individual file in
.git/refs/heads/, such as
.git/refs/heads/master. An active remote-tracking branch name winds up in
If the OS is case-insensitive, this means that branches named
masTER are different when they're packed, but become the same once they become active!
It gets worse though. A name that was inactive gets packed: moved into
.git/packed-refs. If the name now becomes active, it appears in both
.git/packed-refs and (assuming it's a branch name)
.git/refs/heads/. This means that an inactive name is case-sensitive, and an active name is case-insensitive, and you can get both happening at the same time so that you have a
masTER and a
master that are different (because both appear in
.git/packed-refs where we can tell them apart) and, simultaneously, the same! What this means—how Git behaves when you're in this bizarre Schrödinger's-cat3 situation—is not at all clear. Obviously it's a bad idea, though. (I managed to put my test repo into this state where I had both
masTER by doing
git pack-refs --all and then
git checkout -b masTER. I did not push it any further though.)
The OSes (see footnote 2 again) presenting the problem today are case-insensitive but case-preserving. This means that once you have a file named
masTER, attempts to use or create a file named
mAsTeR, and so on, all refer instead to the existing file
Some of these OSes allow you to rename the file with just a case-change (
mv masTER master for instance), so
git branch -m masTER master might do the trick, if Git would just issue an OS-level "rename" operation. Alas, in practice Git checks first, and finds that it can access a branch named
master (which is of course the file named
$ git branch -m masTER master fatal: A branch named 'master' already exists.
Hence, the fix is to rename the branch twice. First we move it to a name that the OS cannot find, that does not match any existing case-folded branch names. You can just pick any unlikely name and hope it is not in use, or you can run
git branch (or
git for-each-ref refs/heads) to check whether the unlikely name you just picked is in fact in use. In any case, if you rename the current (wrong-case) branch to this new unused name that does not, even under silly case-folding rules, match some existing name, that will, as a side effect, delete the old, wrong-case-but-OS-insists-on-matching-it name entirely:
$ git branch -m masTER tmp
Now that the old "wrong-case-but" name is thoroughly eradicated, now you can rename the branch back, with the correct case:
$ git branch -m tmp master
Note that this also takes care of stripping out the wrong-case name from
.git/packed-refs, if it got in there.
1These paths are just defaults. The new extra-worktree feature changes where some of these files are found, sometimes, and Git has environment variables that can override some of them.
2Technically, modern OSes do case-sensitivity on a per-file-system basis, rather than globally. So where this says "if the OS is case-X" you can mentally substitute in "if the file system I'm using is case-X".
They may get it wrong. Consider German, where the word "straße" (street) is written this way in lowercase, but in uppercase, becomes "STRASSE". If we compare one character at a time with a case-insensitive character comparison, we have a problem when we hit the eszet ß as it converts to not one but two uppercase S-es.
This Python3 session shows that MacOS does indeed not believe that ß = SS:
>>> s 'straße' >>> with open(s, 'w') as stream: ... stream.write('street\n') ... 7 >>> os.listdir() 'file_абвгде' # oops, that's one I was using for testing proftpd >>> os.listdir() # should clean out my tmp dir more often ... 'straße' >>> open('STRASSE') Traceback (most recent call last): File "<stdin>", line 1, in <module> FileNotFoundError: [Errno 2] No such file or directory: 'STRASSE' >>> open('STRAßE').read() 'street\n'
It's things like this that sometimes convince me that computers should never interact with humans, and vice versa. :-)
4Older case-insensitive OSes—and at the time, this really was entire OSes—simply folded all names to one case, invariably uppercase. Hence asking to create
masTER actually created
MASTER instead. This is at least as wrong as case-preserving-yet-folding. It is, however, less confusing: you can at least tell just how the OS will mangle your data. The choice of case was rather shouty, of course. Admittedly, in the days of the old uppercase-only Teletypes, they had an excuse.