What is a Git Submodule?

A Git submodule allows one Git repository to contain another Git repository as a child.

Instead of storing the files of the child repository, the parent repository stores a pointer to a specific commit of the child repository.

Example:

ExternBrain/
├── Capture/
├── Evergreen/
└── Publish/      <-- Separate Git repository
myproject.git
    └── Tracks:
        Publish -> commit a74f2ee

The parent repository does not contain the files of Publish.

It only remembers:

Publish should be at commit a74f2ee

Why Use a Submodule?

Without Submodule

ExternBrain/
├── Capture/
├── Evergreen/
└── Publish/

Everything belongs to one repository.

Pros:

  • Simpler

Cons:

  • Public notes mixed with private notes

  • Harder to publish separately

  • History becomes mixed


With Submodule

ExternBrain/
├── Capture/
├── Evergreen/
└── Publish/
    └── .git

Repositories:

myproject.git
publishednotes.git

Pros:

  • Clean separation

  • Independent histories

  • Easier publishing

  • Easier backup strategy


How Git Stores a Submodule

Check:

git ls-files --stage Publish

Example:

160000 a74f2ee... Publish

Meaning:

160000 = Gitlink (Submodule)

Unlike normal files:

100644 file.txt

The parent repository stores:

Publish -> a74f2ee

.gitmodules

Git stores submodule configuration in:

.gitmodules

Example:

[submodule "Publish"]
    path = Publish
    url = git@gitlab.com:domain/publishednotes.git

View:

cat .gitmodules

Common Commands

Show Submodules

git submodule status

Example:

a74f2ee Publish

Clone Repository With Submodules

Recommended:

git clone --recurse-submodules \
    git@gitlab.com:domain/myproject.git

Result:

ExternBrain/
└── Publish/

automatically populated.


Initialize Missing Submodules

If cloned without --recurse-submodules:

git submodule update --init --recursive

Git reads .gitmodules and clones the missing repositories.


Update Submodules

git submodule update --recursive

Synchronizes submodules to the commits recorded by the parent repository.


Daily Workflow

Modify Publish

cd Publish

Make changes.

git add .
git commit -m "Add SPI note"
git push

Update Parent Repository

Go back:

cd ..

Check status:

git status

Example:

modified: Publish

This means:

Publish moved to a newer commit.

Commit the new pointer:

git add Publish
git commit -m "Update Publish submodule pointer"
git push

Important Rule

Always commit in this order:

1. Commit inside submodule
2. Push submodule
3. Commit parent repository pointer
4. Push parent repository

Current ExternBrain Setup

ExternBrain/
├── Capture
├── Evergreen
└── Publish

Repositories:

myproject.git
└── tracks
    Publish -> commit XXXXXXX
 
publishednotes.git
└── contains published notes

Publication pipeline:

Obsidian

ExternBrain

Publish (submodule)

Quartz Build

notes.domain.com

Troubleshooting

Parent Shows “modified: Publish”

Example:

modified: Publish (modified content)

Cause:

Something changed inside Publish.

Fix:

cd Publish
git status

Commit or discard changes.

Then:

cd ..
git add Publish
git commit -m "Update submodule pointer"

Clone Missing Publish Content

Symptoms:

Publish/
    empty

Fix:

git submodule update --init --recursive

Useful Commands Cheat Sheet

# Show submodules
git submodule status
 
# Clone with submodules
git clone --recurse-submodules <repo>
 
# Initialize submodules
git submodule update --init --recursive
 
# Enter Publish
cd Publish
 
# Commit Publish changes
git add .
git commit -m "message"
git push
 
# Commit updated pointer in parent
cd ..
git add Publish
git commit -m "Update Publish submodule pointer"
git push
 
# Inspect submodule metadata
cat .gitmodules
 
# Verify gitlink
git ls-files --stage Publish