Skip to content

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.

A hash function accepts input of any size and produces a fixed-size result:

input bytes ── hash function ──> fixed-size hash

The 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.

Encryption and hashing solve different problems:

Encryption:
data + key → ciphertext
ciphertext + key → original data
Hashing:
data → hash
hash ─X→ original data

Encryption 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.

At a simplified level, Git addresses an object from its type, content length, and content:

<type> <size>\0<content>
│
└── hash calculation → Object ID

For file content stored as a Blob, the conceptual input looks like:

blob 7\0backend

The 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 → backend
b.txt → backend

Their 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.

Because an expected ID is derived from expected data, Git can recompute the hash while reading an object:

stored object data
↓ recompute
calculated ID ── compare ── expected ID

If 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.

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:

Terminal window
git rev-parse --show-object-format

Typical output:

sha1

This 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.

In a disposable repository, create two files with the exact same bytes:

Terminal window
printf "backend" > hash-a.txt
printf "backend" > hash-b.txt
git hash-object hash-a.txt
git hash-object hash-b.txt

Both calculations should match. Now change one byte sequence:

Terminal window
printf "backend!" > hash-b.txt
git hash-object hash-b.txt

The ID changes. Restore the exact original bytes:

Terminal window
printf "backend" > hash-b.txt
git hash-object hash-b.txt

The 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.

  • 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-object always writes an object to the database.
same exact object data → same ID in the same object format
changed object data → different ID in normal operation
same content, different name → same Blob ID can be reused
hash → one-way identifier, not encryption
ID mismatch on recompute → integrity problem detected
SHA-1 → historical 40-hex object format
SHA-256 → newer 64-hex object format
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.