Git Internal Architecture and Data Modeling
Git Internal Directory Structure
Inside the .git Directory
When you run git init, Git creates a special hidden directory named .git. This isn't just another folder; it's the complete database and history for your entire project. If you copy this directory elsewhere, you have a full clone of the repository. Everything Git needs to track your project's history lives right here.
Let's explore this directory not with Git commands, but by looking directly at the files and folders within it. This gives us a physical map of how Git works under the hood.
.git/
├── HEAD
├── config
├── description
├── hooks/
├── info/
├── objects/
└── refs/
Core Files and Pointers
A few key files at the top level of .git act as the repository's main control panel.
The most important of these is the
HEADfile. It almost always contains a single line that answers the question: "What commit are we currently looking at?"
Usually, HEAD doesn't point directly to a commit. Instead, it points to a branch, which in turn points to a commit. This is called a because it refers to another reference by name. If you open the HEAD file while on the main branch, you'll see this:
ref: refs/heads/main
This tells us that HEAD is pointing to the main branch. To find the actual commit, Git will look inside the file at .git/refs/heads/main.
Next is the config file. This is where repository-specific settings are stored. Any configuration you set with git config without the --global flag ends up here. It might define remote repository URLs, track branches, or set user details just for this project.
[core]
repositoryformatversion = 0
filemode = true
bare = false
[remote "origin"]
url = git@github.com:user/project.git
fetch = +refs/heads/*:refs/remotes/origin/*
[branch "main"]
remote = origin
merge = refs/heads/main
The description file is a legacy feature, originally used by a web interface called GitWeb. Today, it's largely ignored by modern tools.
Key Directories
The subdirectories in .git organize the core components: the data itself, the references to that data, and scripts for customizing workflows.
hooks
noun
Scripts that Git can execute automatically before or after events such as commit, push, and receive.
The hooks directory is a powerful feature for automation. It comes populated with a dozen sample scripts (like pre-commit.sample and post-update.sample). If you remove the .sample extension, Git will execute these scripts at specific points in its lifecycle. This can be used to enforce code style, run tests before a commit, or notify a deployment server after a push.
The info directory contains a file called exclude. This file works just like a .gitignore file, but its contents are not version-controlled and shared with the team. It's the perfect place to ignore files specific to your local setup, like editor swap files or operating system artifacts.
Now we get to the heart of the repository: the objects and refs directories.
Think of
objectsas the database andrefsas the index to that database.
The objects directory is where Git stores all your data, every version of every file, every commit, and the directory structures themselves. Nothing in your project is stored as a plain file here. Instead, Git uses a system. Each piece of content is compressed and stored in an object file named with the SHA-1 hash of its contents.
To avoid having tens of thousands of files in one folder, Git uses the first two characters of the hash as a subdirectory name. So, an object with the hash a13b... would be stored at .git/objects/a1/3b....
The refs directory provides the human-readable names for those long, unfriendly hashes. Inside refs, you'll find heads and tags. The refs/heads directory contains one file for each local branch. The name of the file is the name of the branch, and the content of the file is the 40-character SHA-1 hash of the commit that branch currently points to.
So, when you switch branches, Git simply changes the HEAD file to point to a different file in refs/heads. When you make a new commit, Git creates new objects in the objects directory and updates the file in refs/heads for your current branch to point to the new commit's hash.
Understanding this physical layout demystifies many of Git's operations. A branch isn't a complex entity; it's just a tiny text file. The repository's history isn't a magical timeline; it's a collection of interlinked data objects on your filesystem.