In Git, what makes a repository bare, and why do servers host bare repositories?
answer
- Ask which of the two halves of a clone is missing
- No working tree means no index either
- Contents sit at the top level, not inside .git
- Pushing to a checked-out branch causes a mismatch
- There is a config key that refuses exactly that push
basics
~20 sA bare repository has no working tree and no index: the contents normally found in .git sit directly in the repository directory, with core.bare set to true. Servers use them because nothing should be checked out there, and pushing to a checked-out branch is refused by default.
solid answer
~50 s`git init --bare` creates a repository whose directory holds `HEAD`, `config`, `objects/`, `refs/`, `hooks/` and `info/` at the top level instead of nested inside a `.git` folder. There is no working tree, no `index`, and `core.bare` is `true` — by convention such a directory is named `something.git`. It exists purely to receive and serve objects and refs. That matters because a push updates refs on the remote but does not touch a working tree, so pushing to the branch that is currently checked out in a non-bare repository would leave the index and files disagreeing with HEAD. Git refuses that by default via `receive.denyCurrentBranch`. Bare repositories sidestep the problem entirely, which is why every server-side repository is bare. Server-side hooks still run there, and you can create one locally with `git clone --bare` or `git clone --mirror`.
code
console · 8 lines$ git init --bare repo.git
Initialized empty Git repository in /srv/git/repo.git/
$ ls repo.git
HEAD config description hooks info objects refs
$ git -C repo.git config core.bare
true
$ git -C repo.git rev-parse --is-bare-repository
truego deeper
Know that a bare repository has no checked-out files and that it is what a server hosts; git init --bare is the command that creates one.
Explain the layout difference, the absent index, core.bare, and why pushing into a non-bare repository's checked-out branch is refused by default.
Discuss operating one: server-side hooks for policy, --mirror for migrations and backups, and the failure mode when someone pushes into a working repository anyway.
Own the topology decision — where authoritative repositories live, how they are backed up and mirrored, and what policy is enforced at the receiving end rather than on developer machines.
## What "bare" actually means An ordinary clone has two parts: the repository (`.git`) and the working tree (your files). A **bare** repository is the first part without the second. Run `git init --bare repo.git` and list the directory: you see `HEAD`, `config`, `description`, `hooks/`, `info/`, `objects/` and `refs/` — exactly what would normally live inside `.git`, promoted to the top level. Two things are absent: the working tree and the `index`. There is nothing checked out, so there is nothing to stage. The repository's `config` records `core.bare = true`, which is how Git knows not to look for a working tree, and `git rev-parse --is-bare-repository` reports it. The `.git` suffix on the directory name (`project.git`) is a naming convention, not a mechanism. ## Why servers want it A push transfers objects and asks the receiving repository to update refs. It does **not** touch anyone's files. If the receiving repository were non-bare and you pushed to the branch it currently has checked out, its `HEAD` would move while its working tree and index stayed on the old commit — the next `git status` there would report a confusing mass of "changes" that are really the difference between two commits, and a careless `git reset --hard` would destroy work. Git protects against this: `receive.denyCurrentBranch` defaults to refusing such a push. Values include `refuse`, `warn`, `ignore` and `updateInstead`, the last of which makes the receiving repository update its working tree when it is clean. But the clean answer for anything acting as a shared hub is simply to have no working tree at all. There are secondary benefits: no working tree means less disk use and no risk of somebody editing files on the server, and it makes the repository's role unambiguous — it is storage, not a workspace. ## What still works in a bare repository Plenty. Object and ref inspection commands work normally: `git log`, `git show`, `git cat-file`, `git rev-parse`, `git for-each-ref`. Server-side hooks — `pre-receive`, `update`, `post-receive` — live in its `hooks/` directory and run on every push, which is where server-side policy is enforced. What does not work is anything that needs files on disk: `git status`, `git add`, `git checkout`, `git merge` into a working tree. Those need an index and a working tree, and there are none. ## Creating and converting - `git init --bare <dir>` — a new, empty hub. - `git clone --bare <url>` — a copy with the source's branches, without a working tree. - `git clone --mirror <url>` — like `--bare` but sets up a refspec that mirrors *all* refs and configures `remote.origin.mirror`, so a later `git fetch` updates them wholesale. This is the form to use for backups and migrations. Going the other way, you can point a normal repository at a bare one as a remote and push; there is nothing special about the client side. ## Common misconceptions That a bare repository is "read-only" — it is not; it is the normal target of pushes. That it lacks history or objects — it has exactly the same object database. That it cannot run hooks — server-side hooks are precisely what it runs. And that `--mirror` and `--bare` are synonyms — mirror additionally mirrors every ref namespace rather than just the branches a normal clone would take.
- What happens if you push to a branch that is currently checked out in a non-bare repository?By default Git refuses the push, because `receive.denyCurrentBranch` is set to refuse. Accepting it would move HEAD while leaving the index and working tree on the old commit, so the receiving repository would show a phantom set of reverse changes. Setting the key to `updateInstead` makes Git update the working tree when it is clean.
- How does git clone --mirror differ from git clone --bare?Both produce a repository with no working tree. `--mirror` additionally configures a refspec that maps all refs, and marks the remote as a mirror, so fetching updates every ref namespace rather than just the branches a normal clone would take. It is the right choice for backups and repository migrations.
- Which Git commands still work inside a bare repository?Everything that only reads objects and refs — `git log`, `git show`, `git cat-file`, `git for-each-ref`, `git rev-parse` — plus receiving pushes and running server-side hooks. Anything needing files on disk, such as `git status`, `git add` or `git checkout`, does not, because there is no index and no working tree.
A normal clone is a warehouse with a shop floor attached; a bare repository is the warehouse alone — goods come and go, but nobody browses the shelves in place.
saying these in an interview costs you the question
- Thinks a bare repository is read-only storage
- Believes it holds no objects or history
- Assumes hooks cannot run without a working tree
- Says the .git suffix is what makes it bare
- Confuses --mirror with --bare