Hashing and Object IDs
Git identifies stored objects by values derived from their data. This design is called content addressing: an object’s address depends on what the object contains, not on a sequential database ID or a filename chosen by a user.
1. What a hash function does
Section titled “1. What a hash function does”A hash function accepts input of any size and produces a fixed-size result:
input bytes ── hash function ──> fixed-size hashThe properties that matter for this Git mental model are:
- the same input under the same algorithm produces the same result;
- a small input change normally produces a very different result;
- the output is fixed in size even when inputs have different sizes;
- the operation is designed to be one-way rather than reversible;
- finding two different useful inputs with the same result should be computationally difficult.
A hash is not a human-readable summary. It is a compact identifier calculated from exact bytes. A newline, encoding change, or line-ending change can alter those bytes and therefore alter the result.
2. Hashing is not encryption
Section titled “2. Hashing is not encryption”Encryption and hashing solve different problems:
Encryption:data + key → ciphertextciphertext + key → original data
Hashing:data → hashhash ─X→ original dataEncryption is reversible for an authorized party holding the required key. Hashing does not provide a decryption key.
This does not make a Git object secret. Anyone who has the repository’s
object data may be able to read the original content directly. Predictable
values can also be guessed and hashed for comparison. Never treat an object
ID or .gitignore as protection for passwords, tokens, or private keys.
3. Content addressing in Git
Section titled “3. Content addressing in Git”At a simplified level, Git addresses an object from its type, content length, and content:
<type> <size>\0<content> │ └── hash calculation → Object IDFor file content stored as a Blob, the conceptual input looks like:
blob 7\0backendThe header matters: Git does not merely hash the raw file bytes. However, two files with identical content still have the same Blob type, the same size, and the same content, so their complete hash inputs match.
Object construction and direct storage inspection are covered in later lessons. The important boundary here is that an Object ID represents exact object data.
4. Filenames are not part of Blob identity
Section titled “4. Filenames are not part of Blob identity”Consider two paths:
a.txt → backendb.txt → backendTheir names differ, but their file bytes are identical. Git can have both paths refer to the same Blob Object ID:
a.txt ─┐ ├──> Blob: 98cd9e...b.txt ─┘Names, paths, and file modes are represented by another object structure, covered in the next checkpoint. The Blob represents content, not its name or location.
This distinction prevents unnecessary duplication across:
- files with different names but identical content;
- consecutive commits in which a file did not change;
- branches that share most of their project state;
- any snapshots that refer to the same stored content.
5. Objects are replaced, not edited in place
Section titled “5. Objects are replaced, not edited in place”Changing backend to backend! changes the exact bytes and size. The new
data therefore calculates to another Object ID:
backend → 98cd9e...backend! → e8a8d0...Git does not open the old object and modify its identity in place. New object data receives a new identity; existing snapshots can continue referring to the old object.
If the file later returns to the exact bytes backend, the calculation
returns the original ID again. If that object remains available, Git can
reuse it.
6. Object IDs and integrity
Section titled “6. Object IDs and integrity”Because an expected ID is derived from expected data, Git can recompute the hash while reading an object:
stored object data ↓ recomputecalculated ID ── compare ── expected IDIf a byte is corrupted, the calculated value normally differs. The mismatch shows that the data no longer matches the identity by which it was addressed. Verification does not need to create a replacement object; it can detect the inconsistency by calculation and comparison.
An integrity match answers a limited question: “Do these bytes match this Object ID?” It does not prove that the content is safe, correct, reviewed, or authored by a trusted person.
7. SHA-1 and SHA-256 object formats
Section titled “7. SHA-1 and SHA-256 object formats”Most established Git repositories use the historical SHA-1 object format.
Its full IDs are displayed as 40 hexadecimal characters. Git also supports a
newer SHA-256 object format, whose full IDs use 64 hexadecimal characters.
Check a repository’s format:
git rev-parse --show-object-formatTypical output:
sha1This one option is used here only to identify the repository format. Broader
rev-parse behavior belongs to the later plumbing lesson.
The text of a SHA-1 ID and a SHA-256 ID is not interchangeable. Even when they ultimately identify equivalent logical content in differently formatted repositories, the algorithms produce different identifiers.
SHA-1 has known collision weaknesses and is not a good choice for designing new security systems. Git has hardened collision handling and an object-format transition path, but collisions are not mathematically impossible. The safe mental model is practical content identity with integrity defenses—not perfect secrecy or an absolute mathematical guarantee.
8. A controlled experiment
Section titled “8. A controlled experiment”In a disposable repository, create two files with the exact same bytes:
printf "backend" > hash-a.txtprintf "backend" > hash-b.txt
git hash-object hash-a.txtgit hash-object hash-b.txtBoth calculations should match. Now change one byte sequence:
printf "backend!" > hash-b.txtgit hash-object hash-b.txtThe ID changes. Restore the exact original bytes:
printf "backend" > hash-b.txtgit hash-object hash-b.txtThe original ID returns. Plain git hash-object <path> calculates and prints
the would-be Blob ID; without the write option, this experiment does not by
itself request storage of a new object. Writing and examining objects directly
belongs to Checkpoint 5.3.
Using printf rather than a casual echo avoids an unintended newline. The
bytes must be truly identical; visually similar text is not enough.
9. Common mistakes
Section titled “9. Common mistakes”- Calling a hash encrypted content.
- Assuming an Object ID hides or protects a secret.
- Including a filename in Blob identity.
- Saying Git edits an existing object when content changes.
- Comparing SHA-1 and SHA-256 ID text as if they used one format.
- Assuming a hash match proves authorship, safety, or correctness.
- Treating collisions as mathematically impossible.
- Forgetting that encoding and newline differences change the bytes.
- Assuming plain
git hash-objectalways writes an object to the database.
10. Summary
Section titled “10. Summary”same exact object data → same ID in the same object formatchanged object data → different ID in normal operationsame content, different name → same Blob ID can be reusedhash → one-way identifier, not encryptionID mismatch on recompute → integrity problem detectedSHA-1 → historical 40-hex object formatSHA-256 → newer 64-hex object format11. Knowledge check
Section titled “11. Knowledge check”Why can two differently named files share one Blob Object ID?
Blob identity is based on the Blob header and content. The filename is stored outside the Blob, so identical file bytes produce identical Blob data.
What happens when a file returns to its exact earlier content?
The same object data calculates to the same Object ID, so Git can refer to the earlier object again if it is still available.
What does an integrity mismatch prove?
It proves that the available data does not match the expected Object ID. It does not identify the cause or establish whether the intended content was safe or trustworthy.