Git bare vs. non-bare repositories
Published on 2010-10-09.
The Git revision control system has something called a "bare" and a "non-bare" repository. This tutorial deals with the issue and also compares the Git design to the design of Mercurial and Bazaar.
Please note that this tutorial 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'.
Coming from a background in CVS and Mercurial I didn't understand this error, so I did some digging.
In the ideal world of distributed revision control there is no central repository. People just pull from whomever they want changes from and no push exist. That is actually how the Linux Kernel is being developed. No pushing, only pulling.
In the real world a central repository with push access is sometimes necessary and all the different distributed revision control systems allows this, even Git, 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 each other.
In Git the hidden sub-directory is called
.git, in Bazaar it is called
.bzr, and in Mercurial it is called
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 only 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, pull, 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 case more than one person is working on the repository, you always have to remember to pull from a "bare" repository before you push, otherwise clashes might occur in which someone has pushed to the repository since you pulled from it, and your local files are no longer in sync with the remote repository.
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 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 - 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 backup $ cd backup $ 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?
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 and turn an existing "bare" repository into a "non-bare" repository in Git?
Answer: You just clone it and delete the original.
If you have any comments or corrections please feel free to email them to me. Also, if you found this content useful consider supporting me on Patreon