Skip to content

bashandbone/submod

Repository files navigation

submod

Crates.io Documentation Static Badge Rust codecov

A lightweight, fast CLI tool for managing git submodules with advanced sparse checkout support. Built on top of gitoxide and git2 libraries for maximum performance and reliability.

πŸš€ Features

  • TOML-based configuration - Define submodules, sparse-checkout paths, and settings in a simple config file
  • Global defaults with overrides - Set project-wide submodule settings with per-submodule customization
  • Sparse checkout support - Efficiently checkout only the parts of submodules you need
  • Fast operations - Leverages gitoxide for high-performance git operations
  • Robust fallbacks - Automatic fallback to git2 and CLI when needed
  • Comprehensive commands - Add, check, init, update, reset, and sync submodules with ease
  • Developer-friendly - Clear status reporting and error messages

πŸ“‹ Table of Contents

πŸ”§ Installation

Using Cargo

cargo install submod

Using Mise

Mise is a project management tool and package manager that can manage your development environment.

# Global installation
mise use -g cargo:submod@latest

# Project-specific installation
mise use cargo:submod@latest

From Source

git clone https://github.com/yourusername/submod.git
cd submod
cargo install --path .

πŸš€ Quick Start

  1. Initialize a config file in your git repository:

    # Create a basic submod.toml configuration
    cat > submod.toml << EOF
    [defaults]
    ignore = "dirty"
    
    [my-submodule]
    path = "vendor/my-lib"
    url = "https://github.com/example/my-lib.git"
    sparse_paths = ["src/", "include/", "*.md"]
    EOF
  2. Initialize your submodules:

    submod init
  3. Check status:

    submod check

βš™οΈ Configuration

Create a submod.toml file in your repository root:

# Global defaults applied to all submodules
[defaults]
ignore = "dirty"          # ignore dirty state in status
update = "checkout"       # update method
branch = "main"          # default branch to track

# Individual submodule configuration
[vendor-utils]
path = "vendor/utils"
url = "https://github.com/example/utils.git"
sparse_paths = ["src/", "include/", "*.md"]
ignore = "all"           # override default ignore setting
active = true            # whether submodule is active

[my-submodule]
path = "libs/my-submodule"
url = "https://github.com/example/my-submodule.git"
sparse_paths = ["src/core/", "docs/"]
branch = "develop"       # track specific branch

Configuration Options

Global Defaults

  • ignore: How to handle dirty submodules (all, dirty, untracked, none)
  • update: Update strategy (checkout, rebase, merge, none, !command)
  • branch: Default branch to track (. for current superproject branch)
  • fetchRecurse: Fetch recursion (always, on-demand, never)

Per-Submodule Settings

  • path: Local path where submodule should be placed
  • url: Git repository URL
  • sparse_paths: Array of paths to include in sparse checkout
  • active: Whether the submodule is active (default: true)
  • All global defaults can be overridden per submodule

πŸ“– Commands

submod add

Add a new submodule to your configuration and repository:

submod add my-lib libs/my-lib https://github.com/example/my-lib.git \
  --sparse-paths "src/,include/" \
  --settings "ignore=all"

submod check

Check the status of all configured submodules:

submod check

submod init

Initialize all missing submodules:

submod init

submod update

Update all submodules to their latest commits:

submod update

submod reset

Hard reset submodules (stash changes, reset --hard, clean):

# Reset all submodules
submod reset --all

# Reset specific submodules
submod reset my-lib vendor-utils

submod sync

Run a complete sync (check + init + update):

submod sync

πŸ’» Usage Examples

Basic Workflow

# Start with checking current state
submod check

# Initialize any missing submodules
submod init

# Update everything to latest
submod update

# Or do it all at once
submod sync

Adding Submodules with Sparse Checkout

# Add a submodule that only checks out specific directories
submod add react-components src/components https://github.com/company/react-components.git \
  --sparse-paths "src/Button/,src/Input/,README.md"

Working with Different Configurations

# Use a custom config file
submod --config my-custom.toml check

# Check status with custom config
submod --config production.toml sync

Handling Problematic Submodules

# Reset a problematic submodule
submod reset my-problematic-submodule

# Check what's wrong
submod check

# Re-sync everything
submod sync

πŸ› οΈ Development

Prerequisites

  • Rust 1.87 or later
  • Git
  • Mise (recommended) - for tool management and task running

Quick Setup with Mise (Recommended)

# Clone the repository
git clone https://github.com/bashandbone/submod.git
cd submod

# Install mise if you haven't already
curl https://mise.run | sh

# Install all development tools and dependencies
mise install

# Build the project
mise run build
# or: mise run b (alias)

# Run tests
mise run test

# Run the full CI suite (build + lint + test)
mise run ci

Available Mise Tasks

# Build the project
mise run build          # or: mise run b

# Run tests
mise run test

# Lint with clippy
mise run lint

# Run full CI pipeline
mise run ci

# Clean build artifacts
mise run clean

# Cut a new release (maintainers only)
mise run release

Git Hooks with hk

This project uses hk for automated git hooks that ensure code quality:

# Install git hooks (done automatically with mise install)
hk install

# Run pre-commit checks manually
hk run pre-commit

# Run all linters and checks
hk check

# Auto-fix issues where possible
hk fix

# Run CI checks locally
hk run ci

The pre-commit hooks automatically run:

  • cargo fmt - Code formatting
  • cargo clippy - Linting
  • cargo test - Test suite
  • typos - Spell checking
  • prettier - TOML/YAML formatting
  • cargo deny - Security and license auditing

Manual Setup (Alternative)

If you prefer not to use mise:

# Clone the repository
git clone https://github.com/bashandbone/submod.git
cd submod

# Install Rust if needed
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

# Build the project
cargo build

# Run tests
cargo test
# or hk run test

# Or use the comprehensive test runner
./scripts/run-tests.sh --verbose

Running Tests

# Using mise (recommended)
mise run test           # Run all tests
mise run ci             # Run full CI suite

# Using hk
hk run test                 # Run tests only
hk run ci                   # Run CI checks

# Using cargo directly
cargo test              # Run all tests
cargo test --test integration_tests  # Integration tests only

# Using the test script
./scripts/run-tests.sh --verbose     # Comprehensive reporting
./scripts/run-tests.sh --performance # Include performance tests
./scripts/run-tests.sh --filter sparse_checkout  # Filter tests

Project Structure

submod/
β”œβ”€β”€ src/
β”‚   β”œβ”€β”€ main.rs              # CLI entry point
β”‚   β”œβ”€β”€ commands.rs          # Command definitions
β”‚   β”œβ”€β”€ config.rs            # TOML configuration handling
β”‚   └── gitoxide_manager.rs  # Core submodule operations
β”œβ”€β”€ tests/                   # Integration tests
β”œβ”€β”€ sample_config/           # Example configurations
β”œβ”€β”€ scripts/                 # Development scripts
└── docs/                    # Documentation

🀝 Contributing

We welcome contributions! Please see our Contributing Guide for details.

Quick Contributing Steps

  1. Fork the repository
  2. Create a feature branch: git checkout -b feature/amazing-feature
  3. Set up development environment: mise install (installs all tools and git hooks)
  4. Make your changes and add tests if applicable
  5. Commit your changes: git commit -m 'Add amazing feature' (hooks run automatically)
  6. Push to your branch: git push origin feature/amazing-feature (they'll actually run again in check mode, so they need to pass)
  7. Open a Pull Request

Development Guidelines

  • Follow Rust best practices and idioms
  • Add tests for new functionality. I'm not big on unit tests, but integration tests are essential.
  • Update documentation for user-facing changes
  • Use conventional commit messages
  • Run mise run ci or hk run ci before submitting PR
  • Pre-commit hooks will automatically format code and run basic checks
  • All automated checks must pass before PR can be merged

πŸ” Troubleshooting

Common Issues

Submodule not initializing:

# Check if the URL is accessible
git ls-remote <submodule-url>

# Verify your configuration
submod check

Sparse checkout not working:

  • Ensure paths in sparse_paths are relative to the submodule root
  • Check that the submodule repository contains the specified paths
  • Verify sparse checkout is enabled: git config core.sparseCheckout in the submodule

Permission issues:

  • Ensure you have proper SSH keys set up for private repositories
  • Check if your Git credentials are configured correctly

πŸ“‹ Motivation

Managing git submodules, especially with sparse checkouts, can be complex and error-prone. Traditional git submodule commands require multiple steps and careful attention to configuration details.

This tool was created to:

  • Reduce barriers to contribution - Make it easier for new developers to work with projects using submodules
  • Simplify complex workflows - Handle initialization, updates, and sparse checkout configuration automatically
  • Provide better tooling - Clear status reporting and error messages
  • Leverage modern Git libraries - Use gitoxide for better performance and reliability

The tool is actively used in multiple projects at @knitli and @plainlicense, where submodules are essential for sharing core functionality across repositories.

πŸ“„ License

This project is licensed under the Plain MIT License.

πŸ™ Acknowledgments

  • gitoxide - Fast and safe pure Rust implementation of Git
  • git2-rs - Rust bindings to libgit2
  • clap - Command line argument parser

Homepage β€’ Documentation β€’ Crate

Made with ❀️ for the Rust and Git communities

Packages

No packages published

Contributors 2

  •  
  •