hrd

command module
v0.12.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 12, 2026 License: MIT Imports: 7 Imported by: 0

README

hrd

Herd your repos. Run commands across them in parallel. Watch results stream in live.

hrd is a multi-repo manager for developers who work across many repositories and use both git and jj (Jujutsu). It keeps your repos organized into groups, runs VCS commands across all of them at once, and shows a live unified status dashboard — with full awareness of branches, bookmarks, remote tracking, ahead/behind counts, and conflicts.

CI codecov Go Report Card

Features

  • git and jj as first-class citizens — both backends are fully supported with native status parsing. Colocated repos (jj on top of git) are handled correctly.
  • Parallel execution — commands run concurrently across all matched repos, with results streaming in as each one completes.
  • Live status dashboardhrd ls shows a color-coded table of every repo's ref, remote sync state, dirty flag, and per-bookmark/branch badges, updating in real time.
  • Interactive TUIhrd (or hrd tui) opens a full-screen terminal UI for browsing repos, filtering by group or name, and dispatching commands across multiple repos with live streaming output.
  • Repo groups — group repos for easier filtering.
  • Three dispatch commandsgit, jj, and shell.
  • Shell completion — bash, zsh, and fish, with dynamic completion of repo and group names from your live config.
  • Extensible backend system — new VCS backends implement a single interface and self-register.

Install

Homebrew (macOS/Linux)
brew install hugoh/tap/hrd
Linux (deb/rpm)

Download the .deb or .rpm from the releases page and install with your package manager:

# Debian/Ubuntu
sudo apt install ./hrd_*.deb

# RHEL/Fedora
sudo dnf install ./hrd_*.rpm
mise
mise use -g github:hugoh/hrd
Go install
go install github.com/hugoh/hrd@latest
From source
git clone https://github.com/hugoh/hrd
cd hrd
go build -o hrd .

Quick start

# Track some repos
hrd repo add ~/dev/myproject ~/dev/infra
hrd repo add -n dotfiles ~/.local/share/chezmoi

# Or discover everything under a directory at once
hrd repo scan ~/dev
hrd repo scan -g work ~/work     # ...and group what it finds

# Start the TUI
hrd

# Add repos to groups
hrd repo group myproject work
hrd repo group infra work

# Live status across all repos in context
hrd ls

# Run a command across all repos
hrd fetch

# Or just the ones you care about right now
hrd jj dotfiles log

# Arbitrary shell commands
hrd shell -- 'echo $(basename $PWD): $(git rev-parse --short HEAD)'

Tip: Group names are displayed with an @ prefix (e.g., @work, @oss) to distinguish them from repo names. The @ is optional on input — hrd ls @work and hrd ls work both work.

Status dashboard

NAME        VCS STATUS                                                   
myproject   git main ↑2*  feat: add new feature (2 hours ago)            
dotfiles    jj  main ✓∅⇡5  config: update zshrc (1 day ago)              
infra       git feat/rework ↑1↓3  refactor: networking layer (3 days ago)
old-service jj  legacy ✗✓!‼  fix: critical bug (1 week ago)

Status symbols at a glance:

Symbol Meaning
Synced with remote
↑N N commits ahead of remote
↓N N commits behind remote
⇡N Working copy ahead of bookmark (local)
↑N↓N Diverged (ahead and behind)
Local only, no remote
Unresolved conflict
! Bookmark conflict (jj)
Remote was deleted
* Dirty working copy
? Unknown remote state

Interactive TUI

Run hrd (or hrd tui) to open the full-screen terminal UI:

  • Browse all tracked repos in a sortable table.
  • Filter by group with @ — type @work to show only work repos, or select individual repos with Space.
  • Type-to-filter with / — fuzzy-match repos by name as you type; Enter keeps the filter, Esc clears it.
  • Run VCS commands (status, diff, log, fetch, pull, push) from a single key press — results stream in live as each repo completes.
  • The command palette (:) gives access to every subcommand without leaving the TUI.
  • Shortcuts: S (status), l (log), d (diff), f (fetch), p (pull), P (push), @ (group picker), / (name filter), q or Esc (quit).

The TUI mirrors the CLI: the same backends, the same parallel execution, the same status parsing — just in an interactive, always-on view.

Command reference

NAME:
   hrd - manage multiple git and jj repositories

USAGE:
   hrd [global options] [command [command options]]

VERSION:
   dev

COMMANDS:
   repo        manage tracked repositories
   group       list repo groups
   ls          show status of repos
   ll          show status of repos with commit message and time
   status, st  show detailed status for repos (git status or jj status)
   diff        show diff for repos (git diff or jj diff)
   log         show log for repos (git log or jj log)
   fetch       fetch from remotes (git fetch or jj git fetch)
   pull        pull from remotes (git pull or jj git pull)
   push        push to remotes (git push or jj git push)
   shell       run an arbitrary shell command across repos
   tui, i      interactive terminal UI for browsing and running commands across repos
   jj          run a jj command across repos
   git         run a git command across repos
   help, h     Shows a list of commands or help for one command
   completion  Output shell completion script for bash, zsh, fish, or Powershell

GLOBAL OPTIONS:
   --config string, -c string  path to config file (default: "~/.config/hrd/config.toml")
   --help, -h                  show help
   --version, -v               print the version
Scoping and --

The git, jj, and shell commands take an optional repo/group scope followed by the command to run. Everything after -- is passed to the subprocess verbatim — repo names in the command are never reinterpreted as scope:

hrd git myrepo @work -- log --oneline -5
hrd shell -- grep -r TODO .

Without --, the leading args that match repo or group names form the scope and the first non-matching arg starts the command (hrd git myrepo log works, but flags like --oneline need the -- form).

Repo discovery

hrd repo scan <dir>... walks each directory (default depth 5, tune with --depth) and tracks every git/jj repo it finds. Detected repos are not descended into, so vendored or nested checkouts stay untracked; hidden directories are skipped. --dry-run previews without saving, --group assigns everything found to a group. Name collisions fall back to <parent>-<dir>; already-tracked paths are left alone, so re-scanning is safe.

Status filters

ls, ll, the VCS subcommands (status, diff, log, fetch, pull, push), shell, git, and jj accept status filters that narrow the scope to repos in a given state (multiple flags are a union):

hrd push --ahead          # push only repos with unpushed commits
hrd pull --behind @work   # pull only the work repos that are behind
hrd ls --dirty --names    # script-friendly list of dirty repos
hrd shell --dirty -- git stash list
Flag Matches repos that…
--dirty have uncommitted changes in the working copy
--ahead are ahead of their remote, or have local-only work
--behind are behind their remote
Aliases

Define your own commands in the config and they become first-class subcommands — with repo/group scoping, -r, --interactive, status filters, and shell completion, and listed under hrd --help:

[aliases]
sync = "pull --rebase"
mkclean = "!make clean"
hrd sync @work          # git pull --rebase / jj git pull, per backend
hrd mkclean --dirty     # make clean, only in dirty repos
hrd sync -- --autostash # extra args append to the expansion

The first word decides the routing: git/jj pins a backend, ! (or sh) runs a shell command, anything else routes through each repo's own backend like the built-in subcommands. The TUI command bar completes and expands the same aliases. Aliases that would shadow a built-in command are ignored with a warning.

Exit codes
Code Meaning
0 All repos succeeded
1 The command ran but failed in at least one repo
2 Usage or config error (unknown repo, bad flags)

Configuration

Config lives at ~/.config/hrd/config.toml (respects $XDG_CONFIG_HOME).

[repos.dotfiles]
path = "/home/alice/.local/share/chezmoi"

[repos.myproject]
path = "/home/alice/dev/myproject"
groups = ["work"]

[repos.infra]
path = "/home/alice/dev/infra"
groups = ["work"]

[settings]
concurrency = 8

[aliases]
sync = "pull --rebase" # per-repo routing (git pull / jj git pull)
gpf = "git push --force-with-lease" # always that backend
mkclean = "!make clean" # "!" or "sh " prefix = shell command

Note: Groups are derived from the groups field on each repo. Group names are displayed with an @ prefix (e.g., @work) to distinguish them from repo names. The @ is optional on input — work and @work are treated identically.


Contributing

Contributions are very welcome! Please open an issue or submit a pull request. See development instructions.

Adding a backend

Implement the Backend interface in a new package, add a Register() function that calls backend.Register(), and call it from main.go's Run() function. The interface is four methods: Name, Detect, Status, and Run.


gita is the direct inspiration for hrd.

Jujutsu (jj) VCS motivated creating hrd with first-class non-git support.

Disclaimer

LLMs were used to put together the initial version.

Documentation

Overview

Package main is the hrd multi-repo manager entrypoint.

Directories

Path Synopsis
backends
git
Package git implements the git VCS backend.
Package git implements the git VCS backend.
jj
Package jj implements the jj (Jujutsu) VCS backend.
Package jj implements the jj (Jujutsu) VCS backend.
cmd
Package cmd wires together all mgr subcommands into a urfave/cli app.
Package cmd wires together all mgr subcommands into a urfave/cli app.
genreadme command
internal
backend
Package backend defines the VCS backend interface and shared types.
Package backend defines the VCS backend interface and shared types.
cmdspec
Package cmdspec parses the unified command grammar shared by the TUI command bar and config aliases: a backend prefix ("git ...", "jj ...") routes to that backend, a shell prefix ("!...", "sh ...", "shell ...") to the system shell, and anything else to per-repo VCS subcommand routing (each repo's own backend decides how to run it).
Package cmdspec parses the unified command grammar shared by the TUI command bar and config aliases: a backend prefix ("git ...", "jj ...") routes to that backend, a shell prefix ("!...", "sh ...", "shell ...") to the system shell, and anything else to per-repo VCS subcommand routing (each repo's own backend decides how to run it).
config
Package config handles loading, saving, and querying the hrd configuration file (~/.config/hrd/config.toml by default).
Package config handles loading, saving, and querying the hrd configuration file (~/.config/hrd/config.toml by default).
runner
Package runner executes VCS or shell commands across multiple repos in parallel, streaming results back through a channel as each completes.
Package runner executes VCS or shell commands across multiple repos in parallel, streaming results back through a channel as each completes.
theme
Package theme provides colored symbols and helpers for rendering VCS state.
Package theme provides colored symbols and helpers for rendering VCS state.
tui
Package tui provides the terminal UI for hrd, built on Bubble Tea.
Package tui provides the terminal UI for hrd, built on Bubble Tea.
ui

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL