Skip to main content

gitmux

Manage multiple git repositories with ease. Clone, pull, push, and run commands across repos with a single command.

Features

  • YAML configuration — declarative repo management
  • Group management — organize repos into groups
  • Batch git operations — clone/pull/push across repos
  • Pre/post hooks — run commands before/after git operations (e.g., npm install after pull)
  • Template system — share hook configs across similar repos
  • Parallel execution — speed up operations with --parallel flag
  • Status overview — see all repos' git status at a glance
  • Arbitrary command execution — run any shell command across repos

Install

pip install gitmux

Quick Start

# Initialize config in current directory
gitmux init

# Add repos (default group if --group omitted)
gitmux add git@github.com:user/api-server.git --group backend
gitmux add git@github.com:user/auth-service.git --group backend

# Clone all repos
gitmux clone --all

# Pull a single repo
gitmux pull backend/api-server

# Pull entire group (parallel)
gitmux pull --group backend --parallel

# Check status of all repos
gitmux status

# Run command on a specific repo
gitmux exec "git checkout main" --target backend/api-server

Configuration

Config file lookup order (used for both reading and writing):

  1. --config / -c flag (explicit path)
  2. .gitmux.yaml in current directory
  3. ~/.gitmux.yaml (global fallback)
workspace: ~/projects

templates:
  node-app:
    post_pull:
      - npm install
    pre_push:
      - npm test

groups:
  backend:
    repos:
      - name: api-server
        url: git@github.com:user/api-server.git
        template: node-app
      - name: auth-service
        url: git@github.com:user/auth-service.git
        path: ~/custom/path/auth  # override default path
        hooks:
          post_pull:
            - pip install -r requirements.txt
  frontend:
    repos:
      - name: web-app
        url: git@github.com:user/web-app.git
        template: node-app

Path Resolution

  • With markets: {workspace}/{market}/{group}/{repo_name}
  • Legacy (no markets): {workspace}/{group}/{repo_name}
  • Override per-repo with the path field

Markets (optional top layer)

For managing multiple markets/projects in one config file (so a single MCP server can serve them all), wrap groups under a markets: layer:

workspace: /home/ryan/project/CIL/ReadOnly    # or bind via registry / --workspace
markets:
  mastercard:
    groups:
      base:
        repos:
          - name: map
            url: https://code.example.net/mastercard/base/map.git
            branches: { prod: "icbcMap-plan-*" }
      rebate:
        repos:
          - name: bonus
            branches: { prod: "bonus-plan-*", cmbProd: "cmbRebate-plan-*" }
  projectx:
    groups:
      core:
        repos: [ { name: svc, url: https://code.example.net/projectx/core/svc.git } ]
  • Target becomes three-segment: market/group/repo
    gitmux pull mastercard/rebate/bonus -b prod:~20260902
    gitmux status --market mastercard          # scope to a market
    gitmux status -m mastercard -g rebate      # a group within a market
    
  • Path layout: {workspace}/{market}/{group}/{repo}
  • Backward compatible: a config with a top-level groups: (no markets:) keeps the old two-segment targets (group/repo) and {workspace}/{group}/{repo} layout.

Workspace resolution (env placeholders & overrides)

The workspace value (and per-repo path) supports ${ENV_VAR} placeholders, so a config file can be a shareable "repo manifest" that contains no machine-specific path — each environment supplies the real location:

# mastercard.yaml — a portable document; the real path comes from the environment
workspace: ${MASTERCARD_WORKSPACE}
groups:
  rebate:
    repos:
      - name: bonus
        url: https://code.example.net/mastercard/rebate/bonus.git
        branches:
          prod: "bonus-plan-*"

Resolution precedence (highest first):

  1. --workspace / -w flag (CLI) or --workspace (MCP server)
  2. workspace: field in the config, with ${VAR} / ${VAR:-/default} expanded from the environment
  3. Registry binding — a local config → workspace map (see below)
  4. Otherwise → clear error (unset variable, no default, no flag, no binding)
# supply the path via environment
export MASTERCARD_WORKSPACE=/home/ryan/project/CIL/MasterCard/ReadOnly
gitmux pull rebate/bonus -b prod:~20260902 -c mastercard.yaml

# or override for one invocation
gitmux status -c mastercard.yaml --workspace /tmp/scratch

A literal path (workspace: /abs/path) still works unchanged. This lets one manifest map to different local workspaces per machine/container (e.g. your dev box vs. an agent container), and lets multiple manifests each declare their own ${..._WORKSPACE} var.

Workspace registry (bind once, no env vars)

For the cleanest setup, omit workspace from the manifest entirely (making it a pure, shareable repo document) and bind the local path once per machine. The binding lives in a machine-local registry — ~/.config/gitmux/workspaces.yaml — keyed by the config's absolute path. No environment variables involved.

# bind once on this machine
gitmux workspace set -c mastercard.yaml /home/ryan/project/CIL/MasterCard/ReadOnly

# from then on, --config alone resolves the workspace
gitmux pull rebate/bonus -b prod:~20260902 -c mastercard.yaml

gitmux workspace show                    # list all bindings + registry location
gitmux workspace show  -c mastercard.yaml   # effective workspace for one config
gitmux workspace unset -c mastercard.yaml   # remove a binding

Registry file:

# ~/.config/gitmux/workspaces.yaml  (machine-local; do not commit)
mappings:
  /home/ryan/gitmux/mastercard.yaml: /home/ryan/project/CIL/MasterCard/ReadOnly
  /home/ryan/gitmux/projectx.yaml:   /home/ryan/project/CIL/ProjectX/ReadOnly

Because the manifest carries no path, the same file works on your dev box and inside an agent container — each environment binds its own local path once (in the container, run gitmux workspace set / the workspace_set MCP tool with the container's mount path).

Branch Management

Configure named branch aliases per repo:

repos:
  - name: map
    url: https://code.example.com/base/map.git
    branches:
      prod: "bInfinite-plan-*"    # pattern (contains *)
      dev: "bInfinite-dev-main"   # fixed branch name

Usage:

gitmux pull map --branch dev           # checkout fixed branch → pull
gitmux pull map --branch prod:latest   # fetch → find newest matching branch → checkout → pull
gitmux pull map --branch prod:260515   # replace * → checkout bInfinite-plan-260515 → pull
gitmux pull map --branch prod:~260520  # fetch → find latest branch with date <= 260520 → checkout → pull
gitmux pull --group base --branch dev  # checkout fixed branch for all repos in group

Rules:

  • --branch <alias> — alias must be a fixed branch (no *), otherwise error
  • --branch <alias>:latest — alias must be a pattern (has *), picks newest by commit date
  • --branch <alias>:~<date> — alias must be a pattern, picks newest matching branch with date ≤ <date>
  • --branch <alias>:<value> — alias must be a pattern, replaces * with <value>

Hook System

Hooks run shell commands before/after git operations:

  • pre_clone, post_clone
  • pre_pull, post_pull
  • pre_push, post_push

Error handling:

  • Pre-hook failure → git operation is skipped
  • Post-hook failure → repo marked as failed

Template merging: Repo-level hooks override template hooks per hook type.

Commands

Command Description
gitmux init Create .gitmux.yaml in current dir (--global for ~/.gitmux.yaml)
gitmux add <url> --group <g> Add a repository (group auto-created)
gitmux remove <name> Remove a repository
gitmux list List all repositories
gitmux status [target] Show git status overview (defaults to all)
gitmux clone <target> Clone unclosed repositories
gitmux fetch <target> Fetch remote data (--branches to list branches)
gitmux pull <target> Pull latest changes
gitmux push <target> Push local commits
gitmux exec <cmd> Run command in repos (--target to specify)
gitmux group list List groups
gitmux group create <name> Create a group
gitmux group remove <name> Remove a group
gitmux workspace set <path> -c <cfg> Bind a config to a local workspace (registry)
gitmux workspace show [-c <cfg>] Show workspace bindings
gitmux workspace unset -c <cfg> Remove a config→workspace binding

Target Syntax

gitmux pull map           # repo 'map' in default group
gitmux pull base/map      # repo 'map' in group 'base'
gitmux pull --group base  # all repos in group 'base'
gitmux pull --all         # all repos (explicit)
gitmux pull               # error: specify target, --group, or --all

With a markets config, targets are three-segment and --market scopes operations:

gitmux pull mastercard/base/map      # repo 'map' in market/group
gitmux pull --market mastercard      # all repos in a market
gitmux pull -m mastercard -g rebate  # a group within a market

Note: gitmux add <url> without --group places the repo in the default group.

Common Options

  • --group, -g — operate on entire group
  • --market, -m — scope to a market (configs using the markets layer)
  • --all, -a — operate on all repositories (required for write operations without target)
  • --parallel, -p — run in parallel (clone/fetch/pull/push/exec)
  • --json — emit structured JSON instead of formatted tables (for scripts/CI; list, status, clone, fetch, pull, push, exec)
  • --workspace, -w — override the workspace directory (highest precedence; also settable via ${ENV_VAR} in the config)
  • --config, -c — custom config file path

MCP Server (for AI agents)

gitmux ships an MCP server so AI agents can manage repositories through a fixed set of tools instead of being granted shell access. The tool set is the capability boundary — there is deliberately no generic exec/shell tool, so an agent literally cannot run arbitrary commands.

Install

pip install "gitmux[mcp]"

Transport & security

  • stdio only, no authentication. The server runs as a child process of the agent on the same machine; the security boundary is the OS/process. There is no network listener, so no token is needed or used.
  • For remote/cross-machine use you would need an HTTP transport with auth — not provided here by design (keeps the local least-privilege model simple).

Run

gitmux-mcp                    # config lookup: ./.gitmux.yaml → ~/.gitmux.yaml
gitmux-mcp --config /path/to/.gitmux.yaml
gitmux-mcp --config /path/to/mastercard.yaml --workspace /repos/mastercard

The --workspace flag (or a ${ENV_VAR} placeholder in the config) lets one shared manifest resolve to the correct local path inside the agent's environment — e.g. a different path in a Crew container than on your dev machine.

Agent configuration (example)

Kiro / Claude Desktop style mcpServers entry:

{
  "mcpServers": {
    "gitmux": {
      "command": "gitmux-mcp",
      "args": ["--config", "/home/you/.gitmux.yaml"],
      "env": { "MASTERCARD_WORKSPACE": "/home/you/repos/mastercard" }
    }
  }
}

The config's workspace: ${MASTERCARD_WORKSPACE} is then resolved from env above (or pass --workspace /path in args instead). One MCP server per manifest keeps each market/project isolated — the agent only sees what that config exposes.

Exposed tools

Tool Kind Description
list_repos read List repos (optional group filter)
list_groups read List groups and repo counts
list_markets read List markets and group counts (empty for legacy configs)
status read Git status per repo (branch/clean/ahead/behind/last commit)
fetch read Fetch remotes (optional show_branches)
clone action Clone repos not yet cloned (idempotent)
pull action Pull; supports branch aliases (dev, prod:latest, prod:~260520, prod:260524)
push action Push local commits
add_repo config Add a repo declaration to the YAML (does not clone)
remove_repo config Remove a repo declaration (does not delete cloned dir)
init_config config Create a new config file (non-interactive)
workspace_set config Bind this config to a local workspace (registry)
workspace_show read Show workspace registry bindings
workspace_unset config Remove a config→workspace binding

Selection args on the git tools: pass one of target ("market/group/repo" with markets, or "group/repo" / "repo" legacy), market, group, or all_repos=True. Every tool returns structured JSON.

Not exposed: arbitrary shell/exec. If a specific command is ever needed, add a purpose-built, parameter-constrained tool rather than a generic shell escape hatch.

Development

git clone https://github.com/ryan/gitmux.git
cd gitmux
pip install -e ".[dev]"
pytest

Code Quality

Uses Ruff for linting and formatting:

ruff check .          # lint
ruff check --fix .    # auto-fix
ruff format .         # format

Rules: E, F, W, I (isort), N, UP (modern Python), B (bugbear), SIM.

License

MIT

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

gitmux-0.2.0.tar.gz (34.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

gitmux-0.2.0-py3-none-any.whl (29.2 kB view details)

Uploaded Python 3

File details

Details for the file gitmux-0.2.0.tar.gz.

File metadata

  • Download URL: gitmux-0.2.0.tar.gz
  • Upload date:
  • Size: 34.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for gitmux-0.2.0.tar.gz
Algorithm Hash digest
SHA256 5f727050bcb60d45c55f25d641525c65e7354bcdff58d9ccc53561ac02a0f5d0
MD5 a71232f3a75781c55eeb09ddc49a8989
BLAKE2b-256 fe81135f4d75181ef3bfc9e01264c7b66f948983196e3bba222dec8ad038a4ed

See more details on using hashes here.

Provenance

The following attestation bundles were made for gitmux-0.2.0.tar.gz:

Publisher: publish.yml on ryanwx/gitmux

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file gitmux-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: gitmux-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 29.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for gitmux-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 e0f96bc6fdb0dcf3dbb65bfebbba470acf21a0c5cbcf2b46eb7ea9cbcad3e909
MD5 4187c657e9b96bbbf0b8479094480c36
BLAKE2b-256 96b755cc6d64e630cdcfdec98b0da25dd8787b644ec974b0555f21fb164c6551

See more details on using hashes here.

Provenance

The following attestation bundles were made for gitmux-0.2.0-py3-none-any.whl:

Publisher: publish.yml on ryanwx/gitmux

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 files

0.1.1

2 files

0.1.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page