Skip to main content

git-roost

ci pypi npm license

top for git — every repo and worktree on the box, in one table, most actionable first.

roost answers what are my Claude sessions doing. git-roost answers the other half: what is actually in the trees they are working in. Sessions report intent; git reports what happened, and only one of those two can be wrong.

One file, no dependencies, Python 3.9+, macOS/Linux/Windows.

git-roost's table re-sorting as a synthetic fleet drifts, goes uncommitted, and gets pushed

The short ambient loop below is the same program, watching quietly:

git-roost's watch mode, idling until a tree goes dirty and climbs out of QUIET

Recorded against a synthetic fixture, not a real machine — see demo/ for how. Text fallback, same shape:

  REPO                    TREE                      BRANCH                       WORK   DRIFT  STASH  LAST  SUBJECT
MID-OPERATION
  copilot-money-mcp       fork-merge-and-repack     fork/merge-and-repack        17     ^1            1h    ** merge in progress, 3 conflict(s) **
DIVERGED
  counting-chicken-wings  docs-roadmap-refresh      docs/roadmap-refresh         clean  ^1v1   1      1h    Re-verify the roadmap against the tree
  llm-security-rules      pipeline-e2e              pipeline-e2e                 clean  ^4v8          3h    Add launch post draft
UNCOMMITTED
  llm-security-rules      fix-action-injection      fix/action-injection         2+1?   =             1h    fix: script injection via unquoted input
UNPUSHED
  copilot-money-mcp       fork-merge-and-repack     fork/merge-and-repack        clean  ^1            1h    test: registry-derived tool-count check
BEHIND
  roost                   (primary)                 main                         clean  v2            1h    Bump version to 0.3 (#3)
ACTIVE
  shunt-ai-power          (primary)                 main                         clean  =             3m    docs+fix: wall-power wording

QUIET (8)  blog/chicken-fest . copilot-money-mcp/(primary) . repo-security-ci/(primary) . ...

27 tree(s) across 11 repo(s)  |  2 with uncommitted work  |  1 mid-operation

Install

The distribution, the command, the module and the repo are all the bare git-roost — pick whichever channel is already on the box.

pipx install git-roost          # or: pip install --user git-roost
npm install -g git-roost        # if Node is what you have
brew install gmhoward9289-ops/tap/git-roost

Or just take the file. It is one script with no dependencies, so curl and chmod +x is a complete install:

curl -fsSLO https://raw.githubusercontent.com/gmhoward9289-ops/git-roost/main/git_roost.py
chmod +x git_roost.py && ./git_roost.py

Installed under any of those names, git roost works too: git dispatches an unknown subcommand to a git-<name> on PATH.

The man page installs to <prefix>/share/man/man1. A system or Homebrew install puts that on the default MANPATH; a venv or pipx install does not, so man git-roost there needs MANPATH help.

Why

lazygit, gitui and tig are all excellent and all single-repo. They answer "what is happening in this repo". They do not answer "which of my thirty trees is diverged, which has uncommitted work nobody has looked at, and which one is stuck half way through a rebase" — which is the question you have the moment more than one thing is working in parallel.

Usage

git-roost                # one table, most actionable first
git-roost -1             # render once and exit (the default; --once)
git-roost -w             # redraw every 3s (the top view)
git-roost --log          # commit feed across every repo, newest first
git-roost --all          # expand the QUIET group
git-roost --json         # records, for piping somewhere else
git-roost --root ~/src   # look somewhere other than ~/GitHub (repeatable)

Watch mode takes keys:

Key Action
? the keymap
r refresh now
s sort: recent / repo / work
f filter: all / uncommitted / mid-operation
a expand or collapse QUIET
q quit

Sort cycles within a group and never across one. The group order is the whole argument this tool makes — cost of ignoring, not recency or size — so a sort that let an ACTIVE tree float above a MID-OPERATION one would be quietly answering a different question.

Keys need a terminal. Piped, redirected, or on a box with neither termios nor msvcrt, watch mode degrades to the plain timer redraw rather than failing, and the default one-shot render touches no terminal settings at all — which is what keeps git-roost | less and git-roost --json | jq safe.

Reading the table

Groups are ordered by what it costs to ignore them, not by how interesting they look.

Group Meaning
MID-OPERATION Stuck part-way through a rebase, merge, cherry-pick, revert or bisect. This is the one that most needs a human, and it looks identical to an idle tree in every other column.
DIVERGED Ahead and behind. Someone is going to resolve a conflict later.
UNCOMMITTED Tracked changes sitting unsaved.
UNPUSHED Commits that exist only on this machine.
BEHIND Someone else moved on without you.
ACTIVE Committed within the hour, otherwise clean.
QUIET Collapsed to one line. --all expands it.

Columns:

  • WORKclean, 3 tracked changes, +2? untracked, 3+2? both. Untracked files get a marker but never a group of their own: they are mostly scratch output, and one tree here carries a dozen permanently.
  • DRIFT= in sync, ^2 ahead, v3 behind, ^2v3 diverged, - unknown. - means unknown, never in sync.
  • STASH — stash entries, blank when there are none.
  • LAST — age of the last commit.

Finding the baseline

Drift needs something to measure against. The chain is:

  1. the branch's own upstream, when it has one;
  2. origin/HEAD;
  3. if the repo has exactly one remote, that remote's HEAD, then its main or master;
  4. otherwise -.

Steps 1 and 2 are the ones that matter most and are also the easiest to get wrong in opposite directions. Branches that are never pushed have no upstream, so an upstream-only check calls every one of them clean. And origin is a convention, not a guarantee — one repo here has a single remote named deploy, and an origin-only lookup filed a tree that was nine commits behind under QUIET.

Step 3 stops at a single remote on purpose. With two remotes there is no way to know which is authoritative, and a confident wrong baseline is worse than -.

Configuration

Two environment variables, both about how hard to push a slow disk rather than about git.

Variable Default What it does
GIT_ROOST_TIMEOUT 5 seconds any single git call may take before it is abandoned
GIT_ROOST_WORKERS 12 how many trees are scanned in parallel

The timeout is a ceiling on one call, not on the scan. A tree behind a stalled network mount is dropped rather than allowed to hold the whole table hostage — in a watch loop, one unreachable repo would otherwise stop every other repo from redrawing.

Read-only by construction

Every git invocation goes through one function that refuses anything outside an allowlist of read-only plumbing. It cannot mutate a tree, an index or a ref — not because it happens not to, but because the call raises.

The allowlist is keyed on (subcommand, first argument), not the subcommand alone, because the subcommand alone does not settle it: stash list reports but stash pop mutates and stash clear destroys; config --get reads but config <key> <value> writes; symbolic-ref --short REF reads but symbolic-ref HEAD REF rewrites HEAD. A subcommand-level allowlist admits all of those writes, and an earlier version of this file did.

The test suite asserts the policy directly, asserts each of those dangerous forms is refused, and asserts that reading a repo leaves its status and HEAD byte-identical.

This matters because the tool is meant to sit in a watch loop over every repo on the machine, unattended.

Performance

A full scan is parallel across trees, and repo-level facts (stashes, remote refs) are computed once per repo rather than once per worktree.

Measured on 27 trees across 11 repos: ~0.68s per redraw, comfortably inside the default 3s watch interval. Tunable with GIT_ROOST_WORKERS and GIT_ROOST_TIMEOUT.

The family

Four tools, one shape: single file, no dependencies, stdlib unittest, and a table ordered by what it costs to ignore.

  • roosttop for Claude Code: per-session context burn, models, and the subagents a session spawned.
  • git-roost — this one. The other half of the same question: not what the sessions say they are doing, but what their trees actually contain.
  • leghorn — sessions joined to worktrees and real git state, CI, and a commit feed.
  • legbar — both lanes on one screen, over one discovery layer.

git-roost is the one that needs no Claude Code, no sessions and no ~/.claude at all. It reads git and nothing else, which is why it is the one worth running on a box that has never seen an agent.

Tests

python -m unittest discover -s tests

Stdlib unittest, so it runs with nothing installed; pytest collects it unchanged.

License

Apache-2.0

Release files for git-roost 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for git-roost 0.2.0
File Size Uploaded
git_roost-0.2.0.tar.gz 48.5 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for git-roost 0.2.0
File Interpreter ABI Platform
git_roost-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 74.6 kB

Release files / git_roost-0.2.0.tar.gz

Download URL git_roost-0.2.0.tar.gz
Size 48.5 kB
Tags Source
SHA-256 checksum
How to use checksums
a4810e348b269cd08e9374042e2fc1c5085d9e5eab6aa42f89af6cb1f599eb2c
BLAKE2b-256 checksum
How to use checksums
844c1162a0a43bb9f00af7547bf260f17fff279925dbaa288735ec801aeb17ce
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 19, 2026.

Transparency log

Release files / git_roost-0.2.0-py3-none-any.whl

Download URL git_roost-0.2.0-py3-none-any.whl
Size 26.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
1546bc22f64a1476dcd43463423342ef829b6369c3bada4449bdb5ea4c288470
BLAKE2b-256 checksum
How to use checksums
cc8702b49320779d1755a84118ec6f4547cc47e68c6b3159d441c9cd2e850fa7
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 19, 2026.

Transparency log

Release history Release notifications | RSS feed

0.9.1

2 release files

0.9.0

2 release files

0.8.0

2 release files

0.7.0

2 release files

0.6.0

2 release files

0.5.1

2 release files

0.5.0

2 release files

0.4.2

2 release files

0.4.1

2 release files

0.4.0

2 release files

0.3.1

2 release files

0.3.0

2 release files

This release

0.2.0 This release

2 release 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