Git bare vs. non-bare repositories

Posted by: Kim N. Lesmer on 09.10.2010
The Git revision control system has something called a "bare" and a "non-bare" repository. This article deals with the issue and also compares the Git design to the design of Mercurial and Bazaar.

NB! The article isn't relevant to Git prior to version 1.7.0.

The other day I was working with Git and I got the following error after having tried to push some changes back to a remote repository that I had created.

remote: error: refusing to update checked out branch: refs/heads/master
remote: error: By default, updating the current branch in a non-bare repository
remote: error: is denied, because it will make the index and work tree inconsistent
remote: error: with what you pushed, and will require 'git reset --hard' to match
remote: error: the work tree to HEAD.
remote: error:
remote: error: You can set 'receive.denyCurrentBranch' configuration variable to
remote: error: 'ignore' or 'warn' in the remote repository to allow pushing into
remote: error: its current branch; however, this is not recommended unless you
remote: error: arranged to update its work tree to match what you pushed in some
remote: error: other way.
remote: error:
remote: error: To squelch this message and still keep the default behaviour, set
remote: error: 'receive.denyCurrentBranch' configuration variable to 'refuse'.

I didn't understand this error and being used to how Mercurial works, this didn't make any sense, so I did some diggin.

In the ideal world of distributed revision control there is no central repository. People just pull from whomever they want changes from and no pushes exist. That is actually how the Linux Kernel is being developed.

In the real world a central repository with push access is sometimes necessary and all the different distributed revision control systems allows this, but the way they deal with a push are very different.

In a distributed revision control system you work with a local repository that contains both the working tree and the revision history.

In Git you can create such a repository with the following command:

$ mkdir my_repo
$ cd my_repo
$ git init

In Bazaar it is done the same way:

$ mkdir my_repo
$ cd my_repo
$ bzr init

In Mercurial a repository is created in almost the same way the only difference is that the init command creates the directory if it doesn't exist:

$ hg init my_repo

In all three example the working tree resides in the directory itself and the revision history and system files resides in a hidden sub-directory. People then normally pull changes from eachother.

In Git the hidden sub-directory is called .git, in Bazaar it is called .bzr, and in Mercurial it is called .hg.

In Mercurial and Bazaar when you initialize a repository it can serve as a remote repository by default, and anyone with write access to the repository can push changes into it.

In Git that's not possible unless the repository is initialized as a "bare" repository.

A "bare" repository in Git just contains the version control information and no working files (no tree) and it doesn't contain the special .git sub-directory. Instead, it contains all the contents of the .git sub-directory directly in the main directory itself.

A "non-bare" repository in Git is the same as the normal repository in Mercurial and Bazaar. It has a bunch of working files (the tree), and a hidden directory containing the version control information.

In Git (from version 1.7.0 and above) the repository has to be "bare" (no working files) in order to accept a push.

From a technical point of view you can (in theory) push and pull between repositories whether they are "bare" or not. Git has an index, which basically tells it what the head of the current branch looks like. If you push to a "non-bare" repository, Git will look at the working files, compare them to the index, and see that they differ - so it will think that the working files have changed.

"bare" repositories exist in Git as a way of having a central (mainly remote) repository that a number of people can push to. If you want to transfer changes from a "non-bare" repository to another, the correct way is to pull from the destination rather than push from the target.

In Git you should only use a "bare" repository to clone and pull from, and push to. It doesn't have a checked out tree, so it just does what the "server" notionally does in a centralized VCS - records commits, branches, etc when you push to it, and gives you the latest versions when you clone or pull from it.

In Mercurial any repository can serve as a remote repository as mentioned, but push changes only affects the version control and not the working tree unless someone physically access the remote repository (making it local from his or hers perspective) and updates the tree manually.

So in Git a push isn't possible unless the repository is "bare" (no working files) and in Mercurial a push is only affecting the version control (not any working files).

In Bazaar on the other hand any repository can serve as a remote repository, and any push changes also affects the working tree.

From a distributed technical point of view IMHO the Bazaar system is poorly designed and the way both Git and Mercurial addresses the issue is much safer.

In Git and Mercurial a repository with a working tree is expected to contain files that someone is working on. It isn't looked upon as a remote repository, but as a local distributed repository no matter where it resides physically. And this makes perfect sense from a distributed point of view.

In Mercurial if an empty repository is cloned and files are added and then pushed back into the remote repository then the remote repository will contain no working files (no tree) until and unless someone updates the repository manually.

If you want a Git repository to function like a remote backup, you have to create the repository as a "bare" repository. Meaning: Don't insist on having a working tree on your remote backup copy - only use it as a backup. It is not meant to contain any working files and it will not contain any.

A quick rule of thumb is to never push into a repository that has a work tree attached to it, until you REALLY know what you are doing no matter what distributed revision control system you are using.

As long as you don't need to physically work on the remote repository then having a working tree remotely doesn't make sense anyway and you don't need it. And if you do need to work on the remote repository then pull changes in rather than accept pushes.

Any repository that someone is working on is not something that should receive changes without their approval since such changes might create problems.

If you want to have Git working as a remote backup repository, you have to create the remote repository as a bare repository:

$ mkdir my_remote_backup_repo
$ cd my_remote_backup_repo
$ git --bare init

Now you can clone that remote repository and push files back into it, but you wont have a working tree in the remote location.

Question: How do I turn an existing "non-bare" repository into a "bare" repository in Git (taken from the Git wiki)?

Answer: A safe method is to let Git handle all the internal settings for you by doing something like this:

$ git clone --bare -l non_bare_repo new_bare_repo

Question: How do I do the opposite? (Turn an existing "bare" repository into a "non-bare" repository in Git)?

Answer: You just clone it and delete the original.

See http://sitaramc.github.com/concepts/bare.html for futher information on Git bare and non-bare repositories.

Linus Thorvalds gives a great talk about Git and distributed revision control on Google Talk.

If you have any comments or corrections feel free to email them to me.