Repository status
Status reads the same fetch.toml / fetch.yaml and state as
maintainer fetch, using the same workspace layout
and pins. It includes eligible remembered paths and explicitly pinned checkouts
such as ~/.dotfiles. It needs Git 2.18 or newer on
PATH, but no token or network: status performs no remote discovery or fetch
and writes no state. Its Git inspection disables optional locks, lazy fetch,
fsmonitor, external diff and text conversion, and otherwise trusts each
checkoutβs Git configuration as git status does, so configured filters may
run and have their own side effects. Ahead/behind counts describe local
upstream refs; run maintainer fetch --apply separately to refresh them.
Usage
maintainer status # navigable table in a terminal
maintainer status --format=plain # all rows, printable, no ANSI
maintainer status --format=json # all fields and raw counts
maintainer status --root ~/Development
maintainer status --owner kamilsk --owner octomation
maintainer status --config /path/to/fetch.toml
maintainer status --config="" --root ./reposThe command accepts no positional arguments. With no configuration, it uses
the default workspace layout under the current directory; --root selects
another root. This still reads the default fetch state file.
Table columns
Repository status Β· 3 checkouts Β· 2 uncommitted Β· 1 ahead Β· 1 behind Β· 1 locked Β· 0 orphan Β· 0 errors Β· local refs (no fetch)
REPOSITORY β BRANCH β UNCOMMITTED β STATUS β LOCK β PATH
kamilsk/dotfiles β main* β +39/-0 Β· 3f β synced β β ~/.dotfiles
kamilsk/egg β main* β +0/-0 β behind 409 β β public/kamilsk/egg
octomation/maintainer β fetcher β +12/-1 Β· 2f Β· ?1 β ahead 1 β π β public/octomation/maintainerThe header summarizes the rows: the checkouts, then how many have uncommitted changes (tracked changes or untracked files), are ahead or behind their upstream, carry the push lock, are orphans, and failed to be inspected. Each count is a number of rows, and one row can fall into several categories. A failed inspection counts as an error, and as locked or orphan when it is one, but never as clean or as uncommitted, ahead or behind: those are unknown. The row of a scan obstruction or of an unresolved pin is not a checkout; an obstruction counts as an error. JSON output stays an array of rows, without the summary.
Path is the last column in both plain and terminal tables. Checkouts inside
the effective workspace.root use paths relative to that root, including
relative pins and per-owner layouts. External overrides and remembered paths
under the home directory use ~/β¦; paths outside both root and home remain
absolute. A checkout at root is shown as ., and an external checkout at home
as ~. --root also changes the display base. Paths describe the actual local
checkout, including missing pins and orphans, rather than a future fetch target.
JSON path and the selected-path footer retain the full path.
* marks the default branch from local origin/HEAD, falling back to cached
fetch metadata. +/- counts tracked lines from HEAD to the combined current
index/worktree. f counts changed tracked files; ? counts untracked files,
! counts conflicted files. Binary changes have their own count. In an unborn
repository the line count describes staged files against the empty tree.
Untracked file contents are not added to line counts.
f, +/- and the binary count detect renames as Git does by default,
regardless of the status.renames and diff.renames settings. A staged rename
is one file in f, only its content changes reach +/-, and a renamed binary
file is one binary change, so a pure rename reads +0/-0 Β· 1f. Git pairs only
paths it tracks: a file moved without git add (git add -N is enough) is a
deleted file plus an untracked one, and a file rewritten beyond the similarity
threshold (50% by default) is a deleted file plus an added one. +/- compares
the worktree, so a staged rename whose working copy is then rewritten that far
stays one file in f but counts all of its lines as removed and added. Copies
count as added files.
synced means the current branch matches its upstream; it can still have
uncommitted work. A branch can be both ahead and behind. Detached HEAD,
unborn branches, missing upstream configuration and deleted upstream refs are
reported distinctly. Failures produce error rows, not zero/clean results. When
a Git command fails, its error row names the command and quotes Gitβs message,
such as git status: fatal: bad config line 1 in file .git/config (exit status 128).
A directory the scan cannot read and a Git repository at a prefix level of the
layout are error rows too, with error: scan: β¦ and their path; the other rows
are complete, because the scan continues past them (see
scan obstructions).
Lock shows π when any remote has a push URL exactly equal to no_push,
the marker set by the dotfiles helper git lock [remote] (origin by default).
Otherwise the cell is empty. It includes other remotes and multiple push URLs;
the marker does not imply that every push destination is blocked. Custom push
URLs, server permissions and hooks are not checked. After git unlock [remote],
run status again to refresh the indicator. Fetch and branch status are independent
of this marker. It is read separately after git status: when the inspection
fails, the marker remains visible as long as Git can still read the
configuration; a configuration that Git cannot read is an error row.
Interactive controls
The selected row and active column have separate highlights; the footer shows
the full path, HEAD commit and upstream. The table is a snapshot taken at startup;
run the command again to refresh it.
Use up/down or j/k for rows, Page Up/Down to scroll, g/G or Home/End for
first/last, and h/l to scroll horizontally. Resize adjusts the viewport.
Sorting
Select a column with Tab/Shift+Tab or left/right. Press s or Enter to sort by
that column, replacing the whole sort chain. Shift+s (S) or Shift+Enter adds
the column after the existing keys. Header clicks work the same way; Shift+click
adds a key. Repeated activation cycles ascending β descending β removed.
In additive mode, toggling a key keeps its priority; removing and re-adding it
moves it to the end. 0 clears all sort keys, retaining the current filter.
Headers show direction and precedence (Branch β1, Status β2). Sorting covers
the entire filtered collection, including rows outside the viewport. Repository
and branch compare case-insensitively. Path compares the displayed path text
case-sensitively. Uncommitted compares (added + deleted, changed files, untracked, binary, conflicts) numerically; status compares
(ahead + behind, ahead, behind, status text). Lock sorts unmarked rows first
ascending and marked rows first descending. Numeric error rows come last in
both directions. Ties use repository/path for deterministic order. Clearing
sorting restores repository/path order, or fuzzy relevance while searching.
Shift+Enter requires a terminal that reports modified keys. S works in legacy
terminals too. Some terminals reserve Shift+mouse for native text selection;
use S if Shift+click is intercepted.
Fuzzy filtering
Press / or click the search line and start typing. Matches update immediately;
characters may be non-contiguous and matching ignores case (dtf finds
dotfiles). Space-separated terms must all match, and can match different
fields: repository, branch, uncommitted/status text, lock icon (π), displayed
or full path, upstream, or commit.
With no explicit sort, results use fuzzy relevance; explicit keys take precedence.
The header shows matched/total counts, and an empty result is shown explicitly.
The summary has a line of its own under the title. While a filter is active, it
describes the matching rows and is labelled shown:; without a filter it is
labelled all:.
Enter or Escape leaves the input while keeping the filter. Ctrl+u clears the
input. Outside the input, Escape clears an active filter; otherwise it exits.
q and Ctrl-C exit from the table; Ctrl-C also exits from the input, where q
is ordinary text. Sorting preserves the selected checkout. While a filter hides
it, the first matching row is highlighted, and sorting keeps the highlighted
row; clearing the filter restores the selected checkout unless a different row
was selected.
All sort/filter state is in memory. Plain/JSON output retains the original
complete inventory; no repository or fetch state is changed.
Auto mode uses the interactive table when input and output are terminals and
TERM is set to a value other than dumb. Otherwise it uses the plain table.
--format=tui forces interactive mode and requires terminal input and output.
Workspace, pins and orphan rows
Per-repo paths select the active clone for mutation, while stale duplicates
remain visible as orphan. Their own branch, changes and
divergence are retained; plain output and TUI details identify the active path.
Orphan is a management warning, not a replacement for Git status or a read error.
Duplicates are checkouts of one owner/name, as named by their origin or by
state; orphan rows are not counted among them. Without a specific per-repo pin,
duplicates remain separate error rows; broad workspace pins do not choose a
winner. A checkout that neither names (no origin, another remote name, or a
local or file:// URL) is shown under its directory name and is never a
duplicate: two such checkouts with the same directory name are two ordinary
rows. The Path column distinguishes them; JSON and the selected-path footer
also retain their full paths.
ID-only pins without state can only use their
local origin for display identity: status cannot verify a GitHub ID offline.
Fetch performs that verification. A per-repo path that references a field
status does not know offline, such as .Owner in an ID-only rule before fetch
has recorded the repository, is an unresolved pin row. Status inspects
nothing in its place, and the row alone does not make the command fail:
REPOSITORY β BRANCH β UNCOMMITTED β STATUS β LOCK β PATH
id=42 β β β ? β unresolved pin β β ~/ext/{{.Owner}}/{{.Repo}}If origin contradicts known state, status reports the mismatch and suggests fetch. Missing pins remain error rows, and so does a per-repo path that is not a valid template: status shows the ruleβs path and the parse error and inspects nothing in its place. Ignore rules are respected; archived/fork filters do not hide local work.
An orphan row otherwise retains its Git status and adds a management warning. JSON
orphan_reason distinguishes duplicate-pin (another checkout is active),
out-of-scope (a remembered checkout is outside current management rules), and
remote-gone (fetch cached a GitHub 404 by repository ID: the repository was
deleted or access to it was lost). The last case includes
the cached check time. Status never probes GitHub or infers remote deletion
from stale refs. Search for orphan or its reason to find these rows.
Configuration
Status uses the same configuration as fetch, in order:
--config <path>;--config=""disables discovery.- An existing file named by
$MAINTAINER_FETCH_CONFIG. ./fetch.toml, then./fetch.yaml.$XDG_CONFIG_HOME/maintainer/fetch.toml, thenfetch.yaml, falling back to~/.config/maintainer/.
A key outside the config schema is invalid configuration, as it is for fetch.
The state location comes from defaults.state_file, or
$XDG_STATE_HOME/maintainer/fetch/state.json (fallback
~/.local/state/maintainer/fetch/state.json). Missing state is an empty inventory
of remembered checkouts. Status does not acquire a state lock or create state
directories. --config="" still loads state from the default location.
--root changes the workspace root and rebases relative pins.
State paths outside the current layout are report-only orphan rows,
not recursive scan roots; legacy explicit
per-repo pins remain supported. The default layout excludes research and
other unrelated branches without exclusion lists. No recursive submodule scan
or API discovery is performed.
Flags
| Flag | Default | Meaning |
|---|---|---|
--config <path> | auto | Config discovery override; empty disables discovery |
--root <path> | config, otherwise . | Override workspace root for this run |
--format auto|plain|json|tui | auto | Select output mode |
--owner <name> | all local owners | Filter rows by owner, case-insensitively; repeatable |
--concurrency <n> | config, otherwise 4 | Parallel Git inspections; explicit value must be positive |
--timeout <duration> | 0 | Whole-command time limit, including TUI; 0 is unlimited |
-h, --help | Show command help |
JSON output and exit codes
JSON is an array, including [] for an empty collection, with repository,
absolute path (an unresolved pin keeps the path of its rule as written),
branch, added, deleted,
changed_files, untracked, binary, conflicts, ahead, behind, status,
push_locked (boolean), and pinned. id, default_branch, commit,
upstream, visibility (public, private or internal, as the fetch state
recorded it), and error are omitted when unavailable or empty. Orphan rows also expose
orphan_reason, optional active_path, and remote_checked_at for cached
remote-gone observations saved by fetch apply. Text/TUI shows only orphan,
without bracketed reason codes; the reason remains in JSON and searchable.
For example, list changed checkouts with their full paths:
maintainer status --format=json | jq '.[] | select(.changed_files > 0 or .untracked > 0) | {repository, path}'List checkouts marked by git lock:
maintainer status --owner octomation --format=json | jq -c '.[] | select(.push_locked) | {repository, path}'
# {"repository":"octomation/maintainer","path":"/Users/me/Development/public/octomation/maintainer"}| Code | Meaning |
|---|---|
0 | Successful snapshot, including dirty, diverged, orphan and unresolved pin rows |
1 | Git inspection, discovery, state or output failure |
2 | Usage error or invalid configuration, including TUI without terminal I/O |
A usage error names its cause in stderr and exits 2 before any checkout is
read: an unknown flag, a flag value that does not parse, such as
--concurrency=abc, an unknown --format, a --concurrency that is not
positive, a negative --timeout, or an unexpected argument.
Individual checkout errors remain in the output alongside successful rows,
then the command exits with 1. A fatal configuration, state or discovery
failure can prevent the snapshot from being rendered.