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

  •  
  •