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¶
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¶
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):
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
mainandtestingbranches - 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)
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:
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:
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:
- Workflow missing
submodules: recursiveparameter - Referenced submodule commit not pushed to remote
Solution:
Fix 1: Add submodules parameter to workflow
Fix 2: Push missing submodule commits
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:
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
✅ 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:
- Check CI/CD logs - GitHub Actions provides detailed error messages
- Run the pre-push hook manually -
bash .git-hooks/pre-push-submodules.sh - Ask in discussions - bazzite-ai discussions
- Report a bug - bazzite-ai issues