Skip to content

Git Submodule Workflow

This document explains how to work with git submodules in the bazzite-ai repository.

Overview

The bazzite-ai repository uses git submodules to manage two separate codebases:

  • plugins (plugins/) - Claude Code skills and MCP server implementations
  • notebooks (notebooks/) - Jupyter notebooks for AI/ML experiments and documentation
Submodules are separate git repositories referenced at specific commits. When you clone bazzite-ai, the submodules are not automatically initialized unless you use `git clone --recurse-submodules`.

Architecture

Why Submodules?

Separation of Concerns:

  • Main repo: OS configuration, build system, documentation
  • plugins: Reusable skills and tools (can be used independently)
  • notebooks: Experiment notebooks (can be shared across projects)

Independent Development:

  • Each submodule has its own commit history
  • Can be versioned and released independently
  • Multiple projects can reference the same submodule

Clean Repository Structure:

  • Prevents mixing OS builds with experiment code
  • Makes CI/CD clearer (separate workflows per concern)
  • Easier to grant different access permissions

Repository URLs

Submodule URL Purpose
plugins git@github.com:atrawog/bazzite-ai-plugins.git Claude Code skills, MCP servers
notebooks git@github.com:atrawog/bazzite-ai-notebooks.git Jupyter notebooks for experiments

The Correct Workflow

**Critical Rule:** Always push submodule commits BEFORE pushing parent repo commits that reference them. Violating this causes CI/CD failures.

Step-by-Step: Making Changes

1. Make Changes in Submodule

# Navigate to submodule directory
cd notebooks/  # or plugins/

# Make your changes
vim finetuning/my-experiment.ipynb

# Commit in the submodule
git add finetuning/my-experiment.ipynb
git commit -m "Feat: Add new finetuning experiment"

2. CRITICAL: Push Submodule First

# Still in notebooks/ or plugins/
git push origin main
If you skip this step, the next step will fail with the pre-push hook, or worse, cause CI/CD failures if you bypass the hook.

3. Update Parent Repo Pointer

# Return to parent repo root
cd ..

# Git automatically detects submodule changes
git status
# Shows:
#   modified:   notebooks (new commits)

# Stage the submodule update
git add notebooks/

# Commit the parent repo update
git commit -m "Feat: Update notebooks submodule with finetuning experiment"

4. Push Parent Repo

# Pre-push hook validates submodule commits exist remotely
git push origin testing

Automation and Validation

Pre-Push Hook

The repository includes a pre-push hook that validates all submodule commits exist in their remotes before allowing you to push the parent repo.

Location: .git-hooks/pre-push-submodules.sh

Activation:

# Link the hook (done once during setup)
ln -sf ../../.git-hooks/pre-push-submodules.sh .git/hooks/pre-push
chmod +x .git-hooks/pre-push-submodules.sh

Behavior:

  • Runs automatically before every git push
  • Checks each submodule commit referenced by the parent repo
  • Blocks the push if any submodule commit is missing from its remote
  • Provides clear instructions on how to fix the issue

Example Output (when validation fails):

=== Validating submodule commits ===

Checking submodule: notebooks
  Referenced commit: 8191c487fe1afa9aa1e7115761ead6bf4edea79b
  Remote: git@github.com:atrawog/bazzite-ai-notebooks.git
  ❌ MISSING: Commit not found in remote

════════════════════════════════════════════════════════════════
❌ PRE-PUSH VALIDATION FAILED
════════════════════════════════════════════════════════════════

One or more submodule commits have not been pushed to their
remote repositories. This will cause CI/CD failures when GitHub
Actions tries to checkout the code.

Missing commits:
  • notebooks: 8191c487fe1afa9aa1e7115761ead6bf4edea79b

To fix this:

  1. cd notebooks
  2. git push origin HEAD:main
  3. cd ..
  4. git push  # Retry parent repo push

Bypassing the Hook:

**NEVER bypass the pre-push hook** unless you absolutely understand the consequences. Bypassing will break CI/CD.

If you must bypass (e.g., fixing a broken CI state):

git push --no-verify

CI/CD Validation

The repository includes a GitHub Actions workflow that provides an additional validation layer.

Workflow: .github/workflows/validate-submodules.yml

Behavior:

  • Runs on every push to main and testing branches
  • Runs on every pull request
  • Validates submodule commits exist in remotes
  • Blocks other workflows if validation fails
  • Shows clear error messages in GitHub UI

Benefits:

  • Catches issues even if developers bypass local hooks
  • Prevents cascading CI/CD failures
  • Provides visibility in pull requests

Common Operations

Cloning the Repository

Option 1: Clone with submodules (recommended)

git clone --recurse-submodules git@github.com:atrawog/bazzite-ai.git

Option 2: Initialize submodules after cloning

git clone git@github.com:atrawog/bazzite-ai.git
cd bazzite-ai
git submodule update --init --recursive

Updating Submodules

Pull latest changes in submodules:

# Update all submodules to their latest commits on tracked branches
git submodule update --remote --merge

# Or update a specific submodule
cd notebooks/
git pull origin main
cd ..

# Commit the submodule update in parent repo
git add notebooks/
git commit -m "Feat: Update notebooks submodule to latest"
git push

Checking Submodule Status

View current submodule commits:

git submodule status
# Output:
#   8191c487fe1afa9aa1e7115761ead6bf4edea79b notebooks (heads/main)
#   397dbbbcd538f31357a1977bcc9926fd970ba6be plugins (heads/main)

Check if submodule commits exist remotely:

# Run the pre-push hook manually
bash .git-hooks/pre-push-submodules.sh

Switching Branches

When switching branches in the parent repo, submodules may need to be updated:

# Switch branch in parent repo
git checkout main

# Update submodules to match the new branch
git submodule update --init --recursive

Making a Pull Request

Before creating a pull request:
1. Ensure all submodule commits are pushed to their remotes
2. Run `bash .git-hooks/pre-push-submodules.sh` to validate
3. Push the parent repo
4. CI will automatically validate submodules in the PR

Troubleshooting

Error: "fatal: remote error: upload-pack: not our ref ..."

Symptom:

fatal: remote error: upload-pack: not our ref 8191c487fe1afa9aa1e7115761ead6bf4edea79b
fatal: Fetched in submodule path 'notebooks', but it did not contain 8191c487...

Cause: The parent repo references a submodule commit that doesn't exist in the remote repository.

Solution:

# 1. Check which commits are unpushed
cd notebooks/  # or plugins/
git status
# Shows: "Your branch is ahead of 'origin/main' by N commits"

# 2. Push the missing commits
git push origin main

# 3. Return to parent and verify
cd ..
bash .git-hooks/pre-push-submodules.sh

# 4. Push parent repo
git push origin testing

Error: "Submodule path '...': checked out '...'"

Symptom: After pulling the parent repo, git shows a different commit in the submodule.

Cause: The parent repo's submodule pointer was updated, but your local submodule is still on an older commit.

Solution:

# Update submodules to match parent repo pointers
git submodule update --init --recursive

Error: "You have uncommitted changes in submodule ..."

Symptom: Git prevents operations because you have uncommitted changes in a submodule.

Solution:

# Option 1: Commit the changes
cd notebooks/  # or plugins/
git add .
git commit -m "Feat: Description"
git push origin main
cd ..
git add notebooks/
git commit -m "Feat: Update notebooks submodule"

# Option 2: Stash the changes
cd notebooks/
git stash
cd ..

CI Workflow Failing on Checkout

Symptom: GitHub Actions workflow fails during the checkout step with submodule errors.

Cause: Either:

  1. Workflow missing submodules: recursive parameter
  2. Referenced submodule commit not pushed to remote

Solution:

Fix 1: Add submodules parameter to workflow

- name: Checkout
  uses: actions/checkout@v4
  with:
    submodules: recursive  # Add this line

Fix 2: Push missing submodule commits

# See "Error: fatal: remote error: upload-pack" section above

Pre-Push Hook Not Running

Symptom: The pre-push hook doesn't run when you git push.

Cause: The hook is not linked or not executable.

Solution:

# Link the hook
ln -sf ../../.git-hooks/pre-push-submodules.sh .git/hooks/pre-push

# Make it executable
chmod +x .git-hooks/pre-push-submodules.sh

# Verify it works
bash .git-hooks/pre-push-submodules.sh

Submodule Directory is Empty

Symptom: After cloning, plugins/ or notebooks/ directories are empty.

Cause: Submodules were not initialized during clone.

Solution:

# Initialize and fetch all submodules
git submodule update --init --recursive

Best Practices

DO

✅ Always push submodule commits before parent repo commits

✅ Use the pre-push hook - It prevents 99% of issues

✅ Run git submodule status regularly to check synchronization

✅ Update submodules after pulling parent repo changes

git pull && git submodule update --init --recursive

✅ Test changes locally in both submodule and parent repo before pushing

✅ Use descriptive commit messages in both submodule and parent commits

DON'T

❌ Never push parent repo without pushing submodule commits first

❌ Never use git push --no-verify unless absolutely necessary

❌ Don't modify .gitmodules without understanding the consequences

❌ Don't commit submodule pointers unless you've actually changed the submodule

❌ Don't forget to initialize submodules after cloning or switching branches

Reference

Git Commands Quick Reference

# Clone with submodules
git clone --recurse-submodules <url>

# Initialize submodules after clone
git submodule update --init --recursive

# Check submodule status
git submodule status

# Update submodules to latest remote commits
git submodule update --remote --merge

# Pull latest parent repo and update submodules
git pull && git submodule update --init --recursive

# Push submodule changes
cd <submodule-path>
git push origin main
cd ..

# Validate submodule commits before pushing
bash .git-hooks/pre-push-submodules.sh

# Push parent repo
git push origin testing

Files and Locations

File/Directory Purpose
.gitmodules Submodule configuration (URLs, paths)
.git/modules/ Submodule git repositories
.git-hooks/pre-push-submodules.sh Pre-push validation hook
.git/hooks/pre-push Symlink to pre-push hook (when activated)
.github/workflows/validate-submodules.yml CI/CD submodule validation

Additional Resources

Getting Help

If you encounter issues not covered in this guide:

  1. Check CI/CD logs - GitHub Actions provides detailed error messages
  2. Run the pre-push hook manually - bash .git-hooks/pre-push-submodules.sh
  3. Ask in discussions - bazzite-ai discussions
  4. Report a bug - bazzite-ai issues
When reporting submodule issues, always include:
- Output of `git submodule status`
- Output of `bash .git-hooks/pre-push-submodules.sh`
- Relevant CI/CD logs (if applicable)