Open In App

Bare Repositories in Git

Improve
Improve
Like Article
Like
Save
Share
Report

Repositories in Git are a snapshot of the folder in which you are working on your project. You can track the progress and changes made to the project by making commits and also revert changes if not satisfactory.
Repositories can be divided into two types based on the usage on a server. These are:

  • Non-bare Repositories
  • Bare Repositories

What is a Non-bare repository?
A non-bare or default git repository has a .git folder, which is the backbone of the repository where all the important files for tracking the changes in the folders are stored. It stores the hashes of commits made in the branches and a file where the hash of the latest commit is stored.
The file structure of the default repository should look something like this:


    -- Default_Repo* 
|-- .git*
| |-- hooks*
| |-- info*
| |-- logs*
| |-- objects*
| |-- refs*
| |-- COMMIT_EDITMSG
| |-- config
| |-- description
| |-- HEAD
| |-- index
|-- example.txt
*: Folders

As you can see, the .git folder contains all the required files for tracking the project folder. The default repository is always used for local repositories.
 
What is a bare repository?
A bare repository is the same as default, but no commits can be made in a bare repository. The changes made in projects cannot be tracked by a bare repository as it doesn’t have a working tree. A working tree is a directory in which all the project files/sub-directories reside. Bare repository is essentially a .git folder with a specific folder where all the project files reside.
Practically speaking everything in the repository apart from .git is a part of working tree. To create a bare repository, navigate to the chosen directory in bash (for linux users) or command prompt (for windows users) and type:

 
>mkdir FileName.git 
>cd FileName.git
>git init –bare

Creating Bare Repository

The file structure of the bare repository should look like this:

-- BareRepo.git* 
|-- hooks*
|-- info*
|-- logs*
|-- objects*
|-- refs*
|-- COMMIT_EDITMSG
|-- config
|-- description
|-- HEAD
|-- index
*: Folders

Note: This is the exact same file structure of .git folder in non-bare repository

It is important to note that all bare repositories have .git extension (E.g. notice BareRepo.git). Since you cannot commit, or make changes to it, bare repositories are pretty useless on their own. But then why does it exist? When people collaborate to work on a project, they need a central repository where all the tracked changes are stored and prevent any conflict between the versions of the project on other’s computers. A central repository also means that any new contributor can clone the repository into a local one without getting any unsaved changes or conflicting work of others (in short, no mess). A central repository was strictly supposed to be something like a reference repository.

This requires one to use a remote repository as a central one, and initially, only Bare repositories could be used as remote repositories. With the latest changes in git, central repositories need not be bare, hence not many people know about it properly.
The only possible operations on the Bare Repository are Pushing or Cloning.
 
Using a Bare Repository
A bare repository is linked with a local repository, hence the files in .git of local repo should match with the files in the bare repo. First, create a bare repository (See section for the code snippet).
Then, create a local repository folder and clone the bare repository:

 
>cd C:/Users/example/repositories 
>git clone C:/Users/example/BareRepo.git
Cloning into 'BareRepo'...
warning: You appear to have cloned an empty repository.
done.

Don’t worry about the warning. The cloned repository will have the same name as that of the Bare Repository, navigate to that folder and add project files and commit changes. Then push the changes to the bare repository:

>git add * 
>git commit -m “First commit”
[master (root-commit) ffdf43f] First Commit
1 file changed, 1 insertion(+)
create mode 100644 example.txt
>git push c:/users/example/BareRepo.git
Enumerating objects: 3, done.
Counting objects: 100% (3/3), done.
Delta compression using up to 4 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 293 bytes | 97.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To c:/users/example /BareRepo.git
* [new branch] master -> master

And thus, your local repo has been linked to the Bare Repository. In case you already have some files in the project directory, directly initialize the project folder as a git repository, then push its changes to a bare repository (make sure that the Bare repository is not linked to any other project or is newly created). Another way is to clone your working project repository into a bare one:

>cd “Central Repositories” 
>git clone –bare ../../…./Default_Repo
Cloning into bare repository 'Default_Repo.git'...
done.

 
Why is only Bare Repository used as a Central Repository for syncing work?
Central Repositories use bare repositories only because git doesn’t allow you to push to a non-bare repository as the working tree will become inconsistent.
To demonstrate why you can’t push to a non-bare repository:

>cd C:/Users/example/repositories 
>mkdir RepoTest
>cd RepoTest
>git init
Initialized empty Git repository in C:/Users/example/repositories/RepoTest/.git/
>cd ../BareRepo
>git push ../RepoTest
Enumerating objects: 3, done.
Counting objects: 100% (3/3), done.
Delta compression using up to 4 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 293 bytes | 146.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0)
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: is denied, because it will make the index and work tree inconsistent
remote: with what you pushed, and will require 'git reset --hard' to match
remote: the work tree to HEAD.
remote:
remote: You can set the 'receive.denyCurrentBranch' configuration variable
remote: to 'ignore' or 'warn' in the remote repository to allow pushing into
remote: its current branch; however, this is not recommended unless you
remote: arranged to update its work tree to match what you pushed in some
remote: other way.
remote:
remote: To squelch this message and still keep the default behaviour, set
remote: 'receive.denyCurrentBranch' configuration variable to 'refuse'.
To ../RepoTest
! [remote rejected] master -> master (branch is currently checked out)
error: failed to push some refs to '../RepoTest'

But if you still want to be stubborn about it, you can read the warning and go to the non-bare repository where you wish to push and set receive.denyCurrentBranch to ignore and then push the changes.

>cd ../RepoTest 
>git config receive.denyCurrentBranch ignore
>cd ../Default_Repo
>git push ../RepoTest
Enumerating objects: 3, done.
Counting objects: 100% (3/3), done.
Delta compression using up to 4 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 293 bytes | 146.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To ../RepoTest
* [new branch] master -> master

But, using this will give you more problems, as you keeping pushing to remote repo, you’ll notice that only the commit head points to keeps changing (along with other files in .git), but your working tree will remain the same. The only way to remove the inconsistency of index and working tree is by using the command:

>git reset –hard 

Unless you want to do this every time you push changes to the remote repository, it is recommended to use a bare repository.
A bare repository takes much less space to store the same information along with the tracked changes than a non-bare repository. Hence, its storage consumption is the most efficient. Therefore, only a bare repository is suited to serve as a remote or central repository.



Last Updated : 29 Dec, 2019
Like Article
Save Article
Previous
Next
Share your thoughts in the comments
Similar Reads