Git Under the Hood: How Git Stores Your Data inside `.git` Objects
.git/objects by hand.1. The History Hook: Built in a Weekend by Linus Torvalds
In April 2005, relationships broke down between the Linux kernel development team and BitMover, the company offering the proprietary BitKeeper revision control system.
Linus Torvalds retreated for a week and wrote the core codebase of Git. His goal was not to build a traditional revision control system like SVN or CVS. He designed Git as a content-addressable object store built around a Directed Acyclic Graph (DAG).
Understanding Git internals demystifies commands like rebase, cherry-pick, reset, and reflog forever.
"Git is essentially a simple content-addressable filesystem with a VCS user interface written on top of it."
2. The Four Sacred Git Objects
Every file, directory, and commit inside your repository is stored as a compressed zlib object inside .git/objects/. Each object is identified by a unique 40-character SHA-1 (or SHA-256) hash calculated directly from its content:
1. Blob (Binary Large Object): Stores raw file content text. Blobs do NOT store filenames, permissions, or timestamps—only raw bytes.
2. Tree: Represents a directory folder. A Tree object maps filenames and file permissions to their corresponding Blob SHA-1 hashes or child Tree hashes.
3. Commit: A small text object pointing to a top-level root Tree hash, containing parent commit hashes, author metadata, timestamp, and commit message.
4. Annotated Tag: A persistent pointer to a specific commit hash signed with GPG metadata.
# Terminal Lab: Inspecting raw Git objects in your repository
# Write a string directly into Git's object store as a Blob:
echo "Hello InfoHub Readers" | git hash-object -w --stdin
# Output SHA-1 hash: 86b45e2c7a...
# Inspect the type of object:
git cat-file -t 86b45e2c7a
# Print the raw uncompressed content of the object:
git cat-file -p 86b45e2c7a3. What is a Branch, Really?
In older version control systems like SVN, creating a branch meant copying an entire directory folder of files. In Git, a branch is literally nothing more than a 41-byte text file inside .git/refs/heads/main containing the 40-character SHA hash of a commit!
When you run git branch feature-login, Git simply writes a new 41-byte text file named .git/refs/heads/feature-login pointing to your current commit. That is why branch creation in Git takes less than 1 millisecond regardless of repository size.
4. Rebuilding a Git Commit Manually from Scratch in Bash
Here is how low-level Git plumbing commands let you build a commit without ever calling git add or git commit:
# Step 1: Create a raw blob object for a file named "index.js"
BLOB_HASH=$(echo "console.log('Hello');" | git hash-object -w --stdin)
# Step 2: Write a tree object linking "index.js" to our blob hash
TREE_HASH=$(git mktree <<EOF
100644 blob $BLOB_HASH index.js
EOF
)
# Step 3: Write a commit object pointing to our new tree
COMMIT_HASH=$(echo "Initial manual commit" | git commit-tree $TREE_HASH)
# Step 4: Update main branch pointer to our new commit!
git update-ref refs/heads/main $COMMIT_HASH