Git Internals
Repository Layout, .git, and GIT_DIR
Understanding working trees, gitdirs, common directories, and worktrees helps explain where Git really stores repository state.
- Readers building a durable Git mental model
- Developers who keep running into history, ref, or recovery confusion
- Comfort reading basic Git output
- A rough idea of commits, branches, and HEAD
- Learning low-level terms without connecting them to commands
- Collapsing objects, refs, and working state into one concept
At first, many people treat a Git repository as “my project folder plus a hidden .git directory.”
That is good enough until you hit cases like:
- bare repositories
- linked worktrees
- separated gitdirs
- explicitly setting
GIT_DIR
At that point, you need a stronger model of what the repository actually is.
The most common shape
The usual local repository has:
- a working tree with checked-out files
- a
.git/directory containing repository state
In that default setup, it feels natural to say “the repo lives in .git.” That is often true, but it is not the only valid shape.
What usually lives in .git/
Inside .git/, you commonly find:
objects/refs/HEADindexconfiglogs/hooks/
These correspond to:
- the object database
- branch and tag refs
- the current HEAD target
- the staging index
- repository-local config
- reflogs
- hooks
So .git/ is not an opaque mystery folder. It is the operational core of the repository state.
What a gitdir really means
Git does not only care about a directory literally named .git.
What it really cares about is the current gitdir.
In the most common case:
- the gitdir happens to be
.git/
But in more advanced layouts, the .git entry in a working tree may be a small file pointing to the real gitdir elsewhere.
That becomes especially visible with git worktree.
Why bare repositories matter
A bare repository has no normal working tree.
It is usually used on servers or as a central receiving repository.
Its main traits are:
- no checked-out working files
- the repository directory itself acts as the gitdir
- it is well suited for receiving pushes
That helps explain why a server-side repository often looks different from a local dev clone.
Why linked worktrees change your model
With git worktree, one repository can back multiple working trees.
That means:
- the working trees are separate
- the underlying object storage is largely shared
- each worktree still has its own working-state context
If you keep thinking “repository equals the .git directory in this folder,” this setup starts to feel confusing quickly.
A working tree is where you edit files. The gitdir is where Git tracks repository state such as objects, refs, indexes, and logs.
What GIT_DIR and GIT_WORK_TREE tell you
Git lets you override discovery with environment variables:
GIT_DIRsays where the repository state livesGIT_WORK_TREEsays which directory should be treated as the working tree
So Git is not forced to rely on “find .git in the current path.” It can operate in more flexible layouts when asked to.
Why this affects command behavior
Many commands depend on understanding which repository context they are operating in:
statusaddcommitworktreerev-parse --git-dir
If you do not understand the difference between working tree and gitdir, multi-worktree and scripted cases become much harder to reason about.
A conclusion worth keeping
The working tree tells you where files are edited. The gitdir tells Git where repository state actually lives.