Skip to content
How Git Commits Work: Internal Storage and Architecture

How Git Commits Work: Internal Storage and Architecture

AT A GLANCE

Understanding how git commits work requires recognizing that Git records full project snapshots rather than incremental file diffs. Every commit creates an immutable object that links repository metadata to a explicit state of your file tree.

  • Git stores repository data as three primary object types: blobs for raw file contents, trees for directory structures, and commits for metadata.
  • Every commit receives a unique cryptographic identifier, formatted as a 40-character SHA-1 or 64-character SHA-256 checksum based on content and parent references.
  • Staging changes with git add updates the .git/index binary file before git commit writes the permanent commit object to disk.

While Git logically treats each commit as a complete snapshot, internal garbage collection compresses older objects into packfiles using delta storage to minimize total disk footprint.

What Is a Git Commit?

A Git commit is an immutable snapshot of your entire project repository at a specific point in time. Rather than storing a list of file changes, Git captures the structure of every tracked directory and the exact contents of every file. If a file has not changed between commits, Git does not duplicate the data; it simply creates a pointer to the existing stored file.

This approach allows Git to perform history operations rapidly. Reverting to an older commit or inspecting repository history does not require calculating changes backward through hundreds of delta patches. Git simply checks out the tree structure referenced by that specific commit snapshot.

Snapshots vs. Diffs: How Git Differs from Delta-Based VCS

Traditional version control systems like Subversion or CVS store version history as delta lists. These systems keep a base file and layer successive line-by-line file changes on top of it to reconstruct historical states.

  • Delta-Based Version Control: Stores initial file versions followed by a historical sequence of incremental patch diffs for each modified file.
  • Snapshot-Based Version Control (Git): Captures a full filesystem object graph for every commit, referencing identical files via shared cryptographic hashes.
  • Performance Trade-offs: Delta systems require less initial storage but demand expensive processing time to reconstruct older file states. Snapshot systems prioritize execution speed and branch agility while relying on background packing for storage efficiency.

How Git Commits Work Under the Hood (Git Objects)

Underneath the command line interface, Git functions as a content-addressable filesystem. This means that Git retrieves data based on the cryptographic hash of its contents rather than its original filename or file path. All repository data resides inside the hidden .git/objects directory as zlib-compressed files.

The Content-Addressable File System

When you store data in Git, the engine prepends a header containing the object type and byte size to the data payload. Git then calculates the SHA hash of this combined header and content block. The resulting string determines the exact storage location within the .git/objects folder.

The first two characters of the hash form a subdirectory name, while the remaining characters form the filename inside that subdirectory. For example, an object with hash e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 is written to .git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391. Because storage locations derive directly from content, identical file contents across different folders always share the same underlying object on disk.

Blobs, Trees, and Commit Objects

Git internals explained by the official Git Documentation rely on three core object types to construct repository history. Understanding how git stores data requires examining how these three building blocks reference one another.

  • Blob (Binary Large Object): Stores plain file content without file names, directory positions, or permissions. A blob contains only the raw bytes of a file.
  • Tree Object: Represents a directory listing. A tree entries list contains file mode permissions, object type (blob or subtree), SHA hash pointers, and original filenames.
  • Commit Object: Contains a pointer to a top-level root tree object, parent commit hashes, author details, committer details, timestamp data, and the commit message string.

How Hashes Identify Commits

Git traditionally identifies every object using a 160-bit SHA-1 hash represented as 40 hexadecimal characters. Modern Git repositories also support 256-bit SHA-256 hashes featuring 64 hexadecimal characters to guard against collision vulnerabilities.

Because the commit object header includes the top-level tree hash, author metadata, and the parent commit hash, modifying even a single character in a file or commit message alters every subsequent commit hash in the repository graph. This cryptographic linking ensures that commit history cannot be altered without changing the resulting commit hashes.

The Two-Phase Staging and Commit Workflow

Git separates saving work into two distinct operations: staging files and committing them. This design gives developers granular control over which modifications enter the version history.

Working Directory, Staging Area (Index), and Repository

Managing code in Git involves transferring data across three internal zones. Each zone represents a different state of your project files.

  • Working Directory: The local filesystem directory on your computer where you view and edit project files directly.
  • Staging Area (Index): A binary file located at .git/index that acts as a draft manifest, building the precise tree structure for the upcoming commit.
  • Repository (.git directory): The persistent object database storing all committed blobs, trees, metadata, and branch pointer references.

Step-by-Step: What Happens When You Run `git commit`

Executing a commit command triggers a precise sequence of low-level plumbing operations within the Git engine.

  1. Running git add writes new or modified working directory files into the object database as compressed blob objects.
  2. Git updates the binary .git/index file to map directory file paths to these new blob SHA hashes.
  3. When you execute git commit, Git reads the staged index file and generates one or more tree objects representing the directory hierarchy.
  4. Git constructs a new commit object payload containing the top-level root tree hash, parent commit hash, author info, timestamp, and message text.
  5. Git writes the commit object to .git/objects and updates the current branch reference file inside .git/refs/heads/ to point directly to the new commit hash.

Common `git commit` Commands and Options

While the internal process remains uniform, flags attached to the commit command alter how Git populates the staging index prior to creating the object.

Writing Clear Commit Messages (`-m`)

The -m flag allows passing a short summary message directly from the command line interface without opening an interactive text editor. For example, git commit -m "Fix user authentication session timeout bug" bypasses the default editor prompt.

When writing multi-line commit messages, omitting the -m flag launches your configured text editor (such as Vim or VS Code). The Linux Foundation kernel development guidelines recommend keeping the first summary line under 50 characters, followed by a blank line and detailed explanatory body text wrapped at 72 characters.

Bypassing the Staging Area (`-a`)

The -a or --all flag instructs Git to automatically stage modified and deleted files that are already tracked by the repository before running the commit. This removes the need to execute git add manually for existing files.

However, the -a flag does not automatically include untracked files. New files created in your working directory must still be explicitly registered using git add before Git will incorporate them into a commit.

Updating the Previous Commit (`–amend`)

The --amend flag allows modifying the most recent commit on your active branch. It incorporates currently staged changes and permits updating the commit log message.

  • Content Updates: Adds newly staged files directly into the previous commit’s tree structure without creating a separate history entry.
  • Message Correction: Re-opens the editor to update typos or formatting issues in the latest commit message string.
  • Hash Replacement: Generates a completely new commit object with a different SHA hash, replacing the previous tip of the branch reference. Never amend commits that have already been pushed to shared remote repositories.

Commit Metadata and History Structure

Every commit object records structural context alongside file snapshots. This metadata enables team collaboration tracking and dependency ordering across complex projects.

Author vs. Committer Information

Git maintains an explicit distinction between the individual who wrote the code and the individual who committed it to the repository history.

The Author represents the developer who wrote the original code modifications, recorded alongside the GIT_AUTHOR_DATE timestamp. The Committer represents the person who created the actual commit object, recorded with the GIT_COMMITTER_DATE timestamp. These values match during normal local development but diverge when applying third-party patches or cherry-picking commits across branches.

Parent References and the Commit Graph (DAG)

Git history forms a Directed Acyclic Graph (DAG) built through parent commit hash references stored inside each commit object header.

Standard commits contain exactly one parent hash pointing back to the preceding commit state. Root commits (the initial commit in a repository) contain zero parent references. Merge commits created by joining two branches contain two or more parent commit hashes, explicitly recording the point where distinct development lineages converged.

Git Commit Best Practices

Structuring commits effectively keeps project history readable, simplifies code reviews, and streamlines troubleshooting with automated debugging tools like git bisect.

  • Make Atomic Commits: Group related file edits into single focused commits that address one bug fix or feature addition. Avoid combining unrelated changes into single massive snapshots.
  • Write Imperative Commit Titles: Start summary lines with imperative verbs such as “Add feature” or “Fix memory leak” rather than past tense descriptions.
  • Verify Staged Files: Run git status and git diff --staged before committing to inspect staged modifications and catch unintended debug files.
  • Keep History Linear When Appropriate: Use git rebase on local feature branches before merging to eliminate unnecessary merge commits and keep branch topology clean.