OFFSET偏移
← All articlesOFFSET / FIELD NOTESNO. 093

Git's underlying model: objects, snapshots, and references

Use snapshots, the object database, and references to understand what Git operations change.

English translation · Code and commands follow the Chinese original.

IN THESE READING PATHSGit: from snapshots to collaboration and recovery ↗
About 6 min readStart reading

"Git is more than a version control tool; it is a philosophy of collaboration and data integrity."

1. Evolution: why is distributed version control such a leap forward?#

Before Git, mainstream tools such as SVN were centralized.

  • Centralized:
    • Logic: the server is the single source of truth.
    • Pain points: synchronization is limited when the server is unavailable, as are offline access to uncached history and server-side operations. Recovery from server backups is still possible; losing all data is not inevitable.
  • Distributed:
    • Logic: a normal full clone usually contains a local copy of reachable history; shallow and partial clones may be incomplete.
    • Advantages: fast offline operations, including committing and viewing history on a plane; almost instantaneous branch switching. Object verification helps detect content corruption, but does not replace authentication, signatures, or backups.

2. Storage model: snapshots vs deltas#

This is the key to understanding Git’s speed and reliability.

  • Delta model (traditional tools): similar to an incremental backup, recording which lines were added to file A in version 2. Underlying implementations may combine full versions with delta storage, and access costs depend on the implementation; one generalization cannot cover every traditional tool.
  • Snapshot model (Git): each commit records a “complete photograph” of the entire project at that moment.

Why do complete snapshots not cause storage to explode?

Git achieves logical full snapshots with physically incremental storage through three main mechanisms:

  1. Content addressing (blob reuse): Git stores data by content, not filename. If you have 1000 files but change only 1 in a new commit, Git creates new storage only for that file. The other 999 refer directly to existing data through hash pointers.
  2. Structural reuse of tree objects: directory structures are objects too. If nothing inside a subdirectory changes, the parent directory’s tree object directly references the old subdirectory hash, with no need to store an identical tree object again. Scanning working-tree state is a separate mechanism.
  3. Packfiles (compressed object packs): Git packs loose objects during maintenance and can also generate packs for transfers. Delta compression reduces the space occupied by similar objects. There is no fixed guarantee about the relationship between delta base objects and version order, and pushing does not necessarily run a full gc.

3. The core physical model: the three trees (the three areas)#

Understand how data moves among these “three trees,” and you understand 80% of common Git commands. At heart, Git moves snapshots between these areas.

graph LR
    WD[工作区 / Working Directory / 物理文件] 
    SA[暂存区 / Staging Area / Index / 待提交清单] 
    RP[本地仓库 / Repository / .git 目录]

    WD -- "git add (追踪/快照)" --> SA
    SA -- "git commit (永久存档)" --> RP
    RP -- "git checkout/switch (恢复到工作区)" --> WD
    SA -- "git restore (从暂存区撤销)" --> WD

A closer look: why have a staging area at all?

Many beginners think add is unnecessary. In fact, the staging area (index) is one of Git’s most elegant design choices:

  1. Decoupling changes from commits: you may have changed 10 places in your working tree, with 3 bug fixes and 7 new-feature changes.
    • You can first git add those 3 fixes and make a commit;
    • then add the remaining 7 changes and make another commit.
    • Result: an exceptionally clean history, with one clear theme per commit.
  2. A blueprint for the next snapshot: git commit does not simply grab files from your disk; it packages content according to the inventory in the staging area. This makes commits atomic and predictable.

Defining the three areas

  • Working directory: the actual files you see in your file manager.
  • Staging area: a simple index file (.git/index) recording the file fingerprints to include in the next commit.
  • Local repository: the database where Git stores its compressed objects and history (.git/objects).

4. Git’s atomic components: the object database#

The soul of Git lives in .git/objects.

  1. Blob: stores content, without a filename.
  2. Tree: stores structure, like a folder, recording filenames and hashes pointing to blobs.
  3. Commit: stores metadata, including the author, timestamp, parent commit IDs, and the tree it references.

Object hash: traditional SHA-1 repositories reference content using 40-character hexadecimal object IDs; SHA-256 repositories use a different length. Object IDs support addressing and corruption detection, but provide no absolute security guarantee and do not verify the committer’s identity.

5. The art of pointers: HEAD and branches#

Git does not store versions as separate folders on disk in the style of older tools. Its history is connected through linked pointers.

Commit chaining: how is history connected?

When you create a new commit, the object includes a field named parent.

  • Mechanism: an ordinary commit records one parent, an initial commit has no parent, and a merge commit may have several parents.
  • Result: following these links backward forms a directed acyclic graph (DAG) from the current snapshot all the way to the initial snapshot. This is what we call history.

Branch: a featherweight pointer

  • Essence: a branch is not a copy of the code, but a named reference to a commit. Loose refs can live in .git/refs/heads/, but packed-refs or other reference stores may also be used; not every implementation can be understood as a single text file.
  • Movement: when you commit code on the master/main branch, Git creates a new commit and automatically updates the pointer file’s contents to the new hash. The branch pointer automatically moves forward with your commits.

Default branch names

master and main make no difference to Git’s core mechanisms. You can specify the initial default name through git init -b main or init.defaultBranch. Hosting platforms and existing repositories configure their names separately; do not assume that every repository has standardized on main.

How branches differ and share data

  • How they differ: two branches, master and dev, differ only in the commits they point to.
  • How they share: if dev branched from master, all commit objects before the branching point are shared in full. Git only needs to store one copy of the data.
  • Divergence: when dev moves forward while master stays put or goes in another direction, their pointers begin to lead to distinct commits.

HEAD: your first-person viewpoint

  • HEAD is a special pointer to the branch you are currently on, typically: HEAD -> master -> Commit C.
  • Detached HEAD: if HEAD points directly to a commit rather than a branch, you are in a detached state. Commits made here are like clouds without an anchor; if you do not create a branch to retain them, Git’s garbage collection may eventually remove them.

6. The limits of “only adds, never removes”#

Git objects are generally not rewritten after creation. Operations such as reset or rebase first change references and reachability rather than immediately erasing old objects. Committed content can often be found through other branches or the local reflog.

But “committed” does not mean permanently backed up: references can be deleted, reflogs expire, GC can reclaim unreachable objects, and disks can fail. Recovery of working-tree edits that were never committed or staged is even less something the reflog can guarantee. Before dangerous operations, inspect git status and the diff, and keep an independent backup or an explicit recovery reference.

References & further reading

  1. Pro Git: Git objects ↗
  2. Pro Git: Packfiles ↗
  3. Git reference logs ↗

Adapted from technical notes. Examples use generic names.

Continue along a reading path

Git: from snapshots to collaboration and recovery

  1. 01Git's underlying model: objects, snapshots, and references
  2. 02From the first add to undoing a change.
  3. 03Git branches: merging, conflicts, and saving work in progress
  4. 04Remote collaboration with Git: fetching, tracking, and two remotes
  5. 05Advanced Git: rebasing, cherry-picking, and bisecting
  6. 06A Git scenario handbook: correcting, undoing, and recovering
CONTINUE READING / NEXT ARTICLEGit branches: merging, conflicts, and saving work in progress

Understand fast-forward and three-way merges, and use stash to manage unfinished work.