Understanding Git's Use of SHA-1 and SHA-256
How content-addressable storage, object headers, and Merkle DAG trees create an immutable, tamper-evident history.
Git is an immutable content-addressable filesystem where every file, directory tree, and commit is stored by the cryptographic hash of its contents rather than its human-readable path. While Git originally standardized on 160-bit SHA-1 hashes to detect repository corruption and build a tamper-evident commit DAG, modern releases have introduced native support for 256-bit SHA-256 object formatting to safeguard against collision attacks.
In Git, everything is an object addressed by hash(header + content). A 14-byte file "Hello, World!\n" prepended with "blob 14\0" produces the exact SHA-1 object ID 8ab686eafeb1f44702738c8b0f24f2567c36da6d. Because commit objects embed the hashes of parent commits and root directory trees, modifying even one character in an old commit breaks every subsequent commit hash in the history.
01. The Plain-Language Analogy
The Three-Ring Binder vs. The Interlocking Stone Arch
In an ordinary operating system directory, editing a text document is like unclipping a three-ring binder, swapping page 14 with a new sheet, and snapping the ring shut. The binder's spine and cover remain unchanged, leaving zero record that a substitution ever occurred.
In Git, every file is cast into a solid granite brick, with its weight, volume, and composition stamped onto its face as a cryptographic hash. The directory (tree) is a ledger listing the brick hashes. The commit is a keystone mortared directly on top of the stones below. If you try to swap a single granite brick from three years ago, the mortar joint no longer fits, the keystone fractures, and the entire archway collapses unless every subsequent stone in the wall is re-carved from scratch.
02. Step-by-Step Mechanics of Git's Object Database
Git manages exactly four fundamental object types inside the .git/objects/ directory, all formatted with a standardized header before hashing:
blob)
Stores pure raw file content. Blobs store zero metadata—no filenames, no directory paths, and no execution permissions.
Format: blob <content_size_in_bytes>\0<file_content>.
tree)
Represents a directory. A tree serializes a list of directory entries, where each entry consists of file permissions (e.g., 100644 for normal files, 100755 for executables), the entry name, a null byte, and the 20-byte binary hash of the target blob or child tree.
commit)Points to a root tree object and one or more parent commit hashes. It captures the author and committer names, email addresses, UNIX timestamps, timezones, and the commit message.
tag)Provides a permanent annotated reference to an explicit commit, embedding a tagger signature, timestamp, and optional GPG/PGP cryptographic signature.
When Git writes object 8ab686eafeb1f44702738c8b0f24f2567c36da6d to disk, it compresses the payload with zlib and places it in .git/objects/8a/b686eafeb1f44702738c8b0f24f2567c36da6d. The first 2 hex characters serve as the directory name to prevent traditional filesystems from hitting performance bottlenecks with tens of thousands of files in a single folder.
03. Worked Example: Reproducing a Real Git Object Hash
Let us calculate a Git blob hash from scratch using standard command-line utilities and verify it against Git's internal engine:
The commit hash 4f57f52... is the SHA-1 of the exact UTF-8 text above prepended with "commit 251\0".
04. Common Misconceptions Corrected
Linus Torvalds selected SHA-1 primarily as an integrity checksum to detect hardware corruption and accidental transmission errors, not as an unbreachable digital signature. However, because Git's security model assumes collision resistance, the 2017 SHAttered collision spurred Git to add hardened collision-detecting SHA-1 (sha1dc) and engineer a long-term transition to SHA-256.
The blob hash depends strictly on the file bytes, completely ignoring its filename. If you rename main.rs to app.rs without modifying its code, Git does not write a single new blob; it only updates the tree object to point the new filename to the existing blob hash.
Unlike older VCS tools (CVS, SVN) that stored deltas, Git stores complete snapshots. Each commit references a full tree representing the entire repository state. Git only computes deltas during background garbage collection (git gc) to pack redundant objects into space-efficient .pack archives.
05. Git Object Format Comparison: SHA-1 vs. SHA-256
| Property | Git Legacy (SHA-1) | Git Modern (SHA-256) | Significance |
|---|---|---|---|
| Hash Bit Length | 160 bits (20 bytes) | 256 bits (32 bytes) | Hex string length increases from 40 to 64 chars |
| Collision Resistance | Broken (2^63 SHAttered) | Secure (2^128 operations) | Eliminates possibility of crafted colliding commit objects |
| Mitigation in Place | sha1dc (Collision Detection) | Native Algorithm Security | Detects shattered attack patterns on object import |
| Initialization Flag | git init | git init --object-format=sha256 | Supported natively in Git 2.29+ |
06. Primary Sources & Official References
Official Git Documentation
- → Pro Git: Section 10.2 Git Internals - Git Objects (Git-SCM.com)
- → Git Documentation: git-hash-object Manual (Git-SCM.com)
Cryptographic Standards & Research
- → NIST FIPS 180-4: Secure Hash Standard (SHA-1 & SHA-256) (NIST)
- → SHAttered: The First Practical Collision Attack Against SHA-1 (CWI Amsterdam & Google)