skip to content

What are the main entries inside a Git repository's .git directory, and what does each hold?

level: middleimportance: must knowfreq 52%

answer

  1. Start with the one-line file every command reads
  2. Name the staging area's file
  3. Where do branches physically live?
  4. Which subdirectory records ref movements?
  5. Which ignore file never gets committed?

basics

~20 s

HEAD names the current branch, config holds repository-local settings, index is the staging area, objects/ is the object database, refs/ holds branches and tags, logs/ holds the reflogs, hooks/ holds hook scripts, and info/exclude holds unshared ignore patterns.

solid answer

~40 s

The essentials are: `HEAD`, a one-line symbolic ref such as `ref: refs/heads/main` (or a raw commit ID when detached); `config`, the repository-local configuration including remotes and branch upstreams; `index`, the binary staging area; `objects/`, the content-addressed database of blobs, trees, commits and tags, plus `objects/pack/` for packfiles; `refs/`, holding `heads/`, `tags/` and `remotes/` — often collapsed into the `packed-refs` file at the top level; `logs/`, the reflogs for HEAD and each ref; `hooks/`, executable hook scripts (Git ships `.sample` versions); and `info/exclude`, ignore patterns local to this clone that are never committed. You will also see transient files such as `COMMIT_EDITMSG`, `ORIG_HEAD`, `MERGE_HEAD` and `FETCH_HEAD` recording in-progress or recent operations.

code

console · 6 lines
console
$ ls .git
COMMIT_EDITMSG  HEAD  config  description  hooks  index  info  logs  objects  packed-refs  refs
$ cat .git/HEAD
ref: refs/heads/main
$ cat .git/refs/heads/main
9f1c2ab3d4e5f60718293a4b5c6d7e8f90123456

go deeper

for a junior

Be able to name a handful: HEAD, config, objects/, refs/, and know that the staging area is a file inside .git rather than a hidden copy of your project.

for a middle

Walk the listing entry by entry and say what each is for, including that refs may be packed into one file and that reflogs are local-only.

for a senior

Show operational fluency: reading .git to diagnose a stuck state, knowing which files are transient markers, and knowing which writes must go through commands rather than an editor.

for a principal

Own the consequences at scale — why hooks cannot be distributed through the repository itself, and what repository-local state must never be assumed present on a fresh clone.

## Why this gets asked It separates people who have opened `.git` from people who have only run commands against it. Everything Git does is reading and writing these files, so being able to name them means the mental model is concrete rather than magical. ## The top-level files **`HEAD`** — a single line. Normally `ref: refs/heads/main`, meaning "the current branch is main". In a detached checkout it contains a raw commit ID instead. Almost every command starts by reading this file. **`config`** — the repository-local configuration in INI format: remote URLs and fetch refspecs, per-branch upstream settings, and any local overrides of user or system settings. This file is per-clone and never committed. **`index`** — a binary file, the staging area. It lists every tracked path with its blob ID, file mode and stat data. It is what `git add` writes and what `git commit` turns into a tree; it is also why `git status` is fast, because cached stat data lets Git skip unchanged files. **`packed-refs`** — refs that have been packed into one file rather than kept as individual loose files, for repositories with many branches and tags. **`description`** — a legacy one-liner used by the `gitweb` viewer; it plays no part in ordinary use. Transient markers appear as needed: `COMMIT_EDITMSG` holds the message you last edited, `ORIG_HEAD` records where HEAD was before a potentially disruptive operation such as a merge or reset, `MERGE_HEAD` exists while a merge is in progress, and `FETCH_HEAD` records what the last fetch retrieved. ## The directories **`objects/`** — the database. Loose objects are stored one file per object under a two-character prefix directory taken from the object's hash; packfiles and their indexes live in `objects/pack/`. This is the only place your content actually exists. **`refs/`** — names pointing at object IDs. `refs/heads/` holds local branches, `refs/tags/` holds tags, `refs/remotes/` holds remote-tracking refs. Each loose ref is a file containing an object ID. **`logs/`** — reflogs. `logs/HEAD` records every movement of HEAD, and `logs/refs/heads/<branch>` records movements of each branch. These are local, are not fetched or pushed, and are what makes recovering an overwritten branch tip possible. **`hooks/`** — executable scripts Git runs at defined points. A fresh repository contains only disabled `.sample` files; a hook becomes active when a correctly named executable file exists here. Note that `.git/hooks` is not committed, which is why teams reach for shared hook directories or a hook manager instead. **`info/`** — repository-local metadata that is not committed, most importantly `info/exclude`, which holds ignore patterns that apply to this clone only and never appear in a commit. ## Reading it safely Inspecting these files is harmless: `cat .git/HEAD`, `cat .git/refs/heads/main`, `ls .git/objects` all just read. Writing them by hand is not — object files are compressed, `index` is a binary format, and refs have update semantics involving the reflog. The supported way to write is through commands such as `git update-ref` and `git symbolic-ref`. One more useful fact: the location of this directory is not fixed. `git rev-parse --git-dir` prints where Git decided it is, `--show-toplevel` prints the working tree root, and `--is-bare-repository` tells you whether there is a working tree at all. ## The frequent mistakes Saying that `.git` contains "the previous versions of the files as files" — it contains objects addressed by hash, not a per-version folder tree. Saying that hooks are shared with clones — they are not, because `.git` is not part of any commit. And confusing `.git/info/exclude` with a committed ignore file: both express ignore patterns, but only one of them travels to other clones.

  • What is inside .git/HEAD, and how does it change in a detached checkout?
    Normally a single line such as `ref: refs/heads/main`, making HEAD a symbolic ref to the current branch. In a detached checkout it holds a raw commit ID instead, so commits advance HEAD directly and no branch moves with them.
  • Why do branches sometimes have no file under .git/refs/heads?
    They have been packed into the `packed-refs` file at the top of `.git`, which stores many refs as lines in one file instead of one file each. Git reads loose refs first and falls back to `packed-refs`, so both forms behave identically.
  • Why are hooks in .git/hooks not shared with everyone who clones the repository?
    Because `.git` is the repository's own storage, not tracked content — nothing inside it is part of any commit, so a clone gets only the sample files. Teams that want shared hooks commit them to a directory in the working tree and point Git at it, or use a hook manager.
  • What does .git/ORIG_HEAD record?
    Where HEAD pointed just before a potentially disruptive operation such as a merge, rebase or hard reset. It gives you a quick handle for undoing that operation, though the reflog carries the same information with more history behind it.

saying these in an interview costs you the question

  • Thinks .git stores one folder per past version
  • Believes hooks are shared through a clone
  • Confuses the index file with the working tree
  • Says branches are stored inside commit objects
  • Edits files under .git by hand to fix state

context