By admin , 29 April 2026

What "bare" means

A bare repository contains only the contents of .git - no working tree, no checkout. It is what you push to and clone from. Every Git server (GitHub, GitLab, your own) ultimately stores bare repositories.

Creating one

mkdir myproject.git
cd myproject.git
git init --bare

The .git suffix on the directory is a convention indicating bareness. The directory contents look like the inside of any .git: HEAD, config, objects/, refs/.

Pushing to it

cd ~/work/myproject
git remote add origin /path/to/myproject.git
git push -u origin main

Local paths work; so do SSH and HTTPS URLs once you put the bare repo on a server.

Sharing on a server

scp -r myproject.git user@server:/srv/git/
# On the server:
chgrp -R devs /srv/git/myproject.git
chmod -R g+rwX /srv/git/myproject.git
find /srv/git/myproject.git -type d -exec chmod g+s {} +

Group ownership and the setgid bit ensure new objects inherit the group, so multiple developers can push.

Update config:

# /srv/git/myproject.git/config
[core]
    sharedRepository = group

Or:

git init --bare --shared=group myproject.git

Cloning

git clone user@server:/srv/git/myproject.git

SSH versus HTTP

For small teams, SSH access plus git-shell as the user's login shell gives a clean, secure setup. For HTTP, you need a small server like git-http-backend behind nginx or Apache - more setup, easier authentication.

Self-hosting tools

For more than a handful of repositories, run Gitea, Forgejo, GitLab Community Edition, or similar. They give you a UI, issue tracking, and merge requests on top of the bare-repo storage model.

Mirrors and backups

# Create a mirror clone
git clone --mirror <url> backup.git
# Periodically refresh
cd backup.git
git remote update --prune

A mirror clone is like a bare clone, but it tracks all refs - branches, tags, notes - identically. It is the right tool for backup or migration staging.

Hooks on the bare side

Bare repos can run server-side hooks: pre-receive, update, post-receive. Use these for policy enforcement (signed commits, branch protection) and post-push triggers (deploy, notify):

# /srv/git/myproject.git/hooks/post-receive
#!/usr/bin/env bash
while read old new ref; do
  if [ "$ref" = "refs/heads/main" ]; then
    cd /srv/www/myproject &&
      git --git-dir=. fetch /srv/git/myproject.git main &&
      git --git-dir=. reset --hard FETCH_HEAD
  fi
done

This auto-deploys main on push. (Production needs more care, but the principle is right.)

Why bare matters

Pushing to a non-bare repo is risky - it bypasses the working tree and can corrupt the receiving repo's index. Bare-by-design servers prevent this. Whenever you set up a shared repo, make it bare.