Programming

Where does Git store the SHA1 of the commit for a submodule

27 September 2026 · 6 min read

Where does Git store the SHA1 of the commit for a submodule

Navigating the intricacies of Git can be a journey of discovery, especially when dealing with advanced features like submodules. These powerful tools allow you to embed one Git repository inside another, maintaining separate version histories while integrating them into a larger project. A common point of confusion for developers, however, revolves around how Git manages the relationship between the main project (often called the superproject) and its embedded submodules. Specifically, a frequently asked question is: Where does Git store the SHA1 of the commit for a submodule? Understanding this mechanism is fundamental to effectively managing dependencies and ensuring consistent builds across development environments. This deep dive will clarify Git’s unique approach to tracking submodule versions, demystifying the ‘Gitlink’ and its role in your repository’s history.

Understanding Git Submodules and Their Purpose

Git submodules are essentially pointers to specific commits in external repositories. They enable developers to include and manage external projects within their main project, treating them as integral parts of the build without having to copy their entire codebase. This is particularly useful for managing libraries, plugins, or shared components that are developed independently but are crucial for the primary application’s functionality. For instance, a web application might include a common UI library or a backend framework as a submodule.

The primary benefit of using submodules lies in maintaining a clean separation of concerns. Each submodule has its own independent Git history, branches, and tags. This allows different teams or projects to work on these components in isolation, while the superproject merely references a specific, tested version of that component. When the superproject is cloned, Git fetches the submodule repositories and checks out the exact commit specified by the superproject, ensuring that everyone working on the main project is using the same version of its dependencies.

The Superproject-Submodule Relationship

The relationship between a superproject and its submodules is critical to grasp. Unlike a simple copy-paste, a submodule reference is a live link. The superproject doesn’t store the contents of the submodule; instead, it stores a reference to a particular commit SHA-1 within the submodule’s repository. This means that if the submodule’s repository changes, the superproject won’t automatically update. It requires an explicit action to pull new changes into the submodule and then update the superproject’s reference to that new commit. This manual update process is a deliberate design choice, ensuring stability and preventing unexpected breaking changes from submodule updates.

To answer directly, Git stores the SHA1 of the commit for a submodule within the superproject’s Git index as a special entry known as a “Gitlink.” This Gitlink isn’t a regular file or directory; it’s a unique type of tree entry, specifically a “commit object” with a mode of 160000. When you add a submodule, Git records the path to the submodule and the exact SHA-1 of the commit you checked out in that submodule. This information is then committed into the superproject’s history, just like any other file change. Consequently, every commit in your superproject uniquely specifies the exact versions of all its submodules.

This mechanism is crucial for reproducible builds. If you check out an older commit of your superproject, Git will automatically know which specific commit of each submodule it needs to checkout. This ensures that your entire project, including all its dependencies, reverts to a precise historical state. This level of precise version control for submodules is a core strength, differentiating Git’s approach from simpler dependency management systems.

The Role of the Git Index

The Git index, often called the staging area, plays a pivotal role in this process. When you add a submodule using git submodule add, Git performs several actions: it clones the submodule repository into the specified path, checks out the default branch, and then adds a Gitlink entry to the superproject’s index. This entry contains the path to the submodule and the SHA-1 of the commit checked out within it. When you then run git commit in the superproject, this Gitlink entry—including the submodule’s precise commit SHA-1—is saved into the superproject’s commit history. This is why you’ll see submodule changes appear as a single line in git status, indicating a change to the submodule’s recorded commit.

A “Gitlink” is a special mode entry in Git’s tree objects, represented by the mode 160000. While regular files have modes like 100644 (plain file) or 100755 (executable file), and directories have 040000, the 160000 mode signifies a reference to another Git repository’s commit object. It tells Git, “this entry is not a file or a directory, but a specific commit SHA-1 from another Git repository.” When Git encounters a 160000 entry, it knows to treat it as a submodule reference, allowing it to navigate to the correct commit within that embedded repository. This elegant solution ensures that the superproject only stores a lightweight pointer, not the entire submodule’s content, making the superproject repository much smaller and faster to clone.

Inspecting Submodule Commit References

Understanding where the SHA1 is stored is one thing; being able to verify it in practice is another. Git provides several commands to inspect the state of your submodules and see the exact commit SHA-1 they are pointing to. This is crucial for debugging, ensuring consistency, and understanding the version control for submodules within your project.

Using git status and git diff

When you modify a submodule (e.g., by pulling new changes within the submodule directory), git status in the superproject will report the submodule as “modified.” For example, it might show modified: your/submodule/path (new commits). If you then run git diff, you’ll see a unique output for the submodule, indicating the change in the recorded commit SHA-1. It won’t show the file-by-file changes within the submodule itself, but rather a diff of the old and new SHA-1s. This output clearly illustrates that the superproject is tracking the state of the submodule as defined by its specific commit.

$ git diff diff --git a/your/submodule/path b/your/submodule/path index old_sha1..new_sha1 160000 --- a/your/submodule/path +++ b/your/submodule/path @@ -1 +1 @@ -Subproject commit old_sha1 +Subproject commit new_sha1

This output precisely shows the transition from old_sha1 to new_sha1, confirming that the superproject’s record of the submodule’s commit has changed. This is the explicit proof of where Git stores the SHA1 of the commit for a submodule: directly in its index and then its history.

Diving into .gitmodules

It’s important to clarify the role of the .gitmodules file. While it’s central to submodule management, it does not store the SHA-1 of the submodule’s current commit. Instead, the .gitmodules file is a plain text configuration file located at the root of the superproject. Its purpose is to define the submodule’s name, path relative to the superproject, and its remote URL. For example:

[submodule "my_library"] path = lib/my_library
<b>Question & Answer : </b><br></br><p>I know that when you add a submodule to a git repository it tracks a particular commit of that submodule referenced by its sha1.</p> <p>I'm trying to find where this sha1 value is stored.</p> <p>The .gitmodules and .git/config files only show the paths for the submodule, but not the sha1 of the commit.</p> <p>The <a href="http://git-scm.com/docs/git-submodule">git-submodule(1)</a> reference only speaks of a gitlink entry and the <a href="http://www.kernel.org/pub/software/scm/git/docs/gitmodules.html">gitmodules(5)</a> reference doesn't say anything about this either.</p>
<br></br><p>It is stored in Git's object database directly. The tree object for the directory where the submodule lives will have an entry for the submodule's commit (this is the so-called "gitlink").</p> <p>Try doing git ls-tree master <path-to-directory-containing-submodule> (or just git ls-tree master if the submodule lives in the top-level directory).</p>