git-loom 0.25.0

A Git CLI tool that weaves together multiple feature branches into integration branches
# status

Show the branch-aware commit graph. This is the default command when running `git loom` with no arguments.

## Usage

```
git loom [status] [-f [COMMIT...]] [N]
```

### Arguments

| Argument | Description |
|----------|-------------|
| `N` | Number of commits to show at and before the base (default: [`loom.statusContext`](../configuration.md#loomstatuscontext), else 1) |

### Options

| Option | Description |
|--------|-------------|
| `-f, --files [COMMIT...]` | Show files changed in each commit, optionally filtered to specific commits |
| `-a, --all` | Show all branches including hidden ones |

## Output

The status displays a branch-aware commit graph using UTF-8 box-drawing characters, showing commits grouped by feature branch:

```
╭─ [local changes]
│   !! conflicted.rs
│    M file.txt
│   A  new_file.rs
│    ⁕ untracked.txt
│
│╭─ fb [feature-b] ✓
│●    mqt  d0472f9 Fix bug in feature B
│●    pkz  7a067a9 Start feature B
├╯
│
│╭─ fa [feature-a] ↑
│●    rsv  2ee61e1 Add feature A
├╯
│
● ff1b247 (upstream) [origin/main] Initial commit
```

### Sections

The graph is rendered top-to-bottom with these sections:

1. **Local changes** — shown only if the working tree has modifications, new files, or deletions. Files are split into three groups:
   - **Conflicted files** are shown first with a `!!` marker in bold red (filename also bold red). These appear during an in-progress rebase or merge.
   - **Tracked changes** are listed next with a 2-char `XY` status matching `git status --short` (index green, worktree red).
   - **Untracked files** are listed last with a `⁕` marker (magenta). When there are more than 5 untracked files, they are displayed in a multi-column grid layout sized to the terminal width.

2. **Feature branches** — each branch is rendered as a side branch with its name in brackets, followed by its commits, closed with `├╯`. A remote tracking indicator appears after the closing `]` when an upstream has been configured for the branch.

3. **Loose commits** — commits not belonging to any feature branch, shown on the main integration line.

4. **Upstream marker** — the merge-base between HEAD and the upstream tracking branch.

### Symbols

| Symbol | Meaning |
|--------|---------|
| `╭─` | Start of a section |
| `├─` | Start of a subsequent branch in a stack |
| `│` | Integration line continuation |
| `││` | Continuation between stacked branches |
| `●` | A commit |
| `├╯` | End of a side branch |
| `!!` | Conflicted file marker (bold red) |
| `⁕` | Untracked file marker (magenta) |
| `⏫` | Upstream has new commits |
| `·` | Context commit before the base (dimmed) |
| `✓` | Branch remote is in sync (green) |
| `↑` | Branch tip differs from its remote (yellow) |
| `✗` | Branch remote is gone (red) |

### Short IDs

Each branch, commit, and file in the output is assigned a short ID — a compact identifier you can use with other *git-loom* commands. What you see in the status is what you type.

A commit's short ID comes first on its line, in a fixed column, followed by the abbreviated hash, so ID and hash sit together at the head of the line. Commits that carry a `Change-Id` trailer (every commit loom creates, see [`loom.changeId`](../configuration.md#loomchangeid)) get a **persistent** ID made of the letters `k`–`z`, such as `mqt`: it is derived from the Change-Id, not from the hash, so it survives `update`, `fold`, `swap`, `split`, and every other rewrite. Any longer prefix of the ID also works, and so does the full `Change-Id` value. A commit without a Change-Id — made with plain `git commit`, or cherry-picked from elsewhere — falls back to a hex prefix of its hash, such as `3a`, which changes whenever the commit is rewritten.

IDs are the shortest prefix that tells commits apart. When a new commit happens to share the first letters of an existing one, both IDs grow by a letter and the old shorter form stops resolving, so a stale ID can never point at the wrong commit.

## Showing Files

Use `-f` to show the files changed in each commit:

```
git loom status -f
```

```
│╭─ fa [feature-a]
│●    mqt  2ee61e1 Add feature A
│┊      mqt:0 M  src/feature.rs
│┊      mqt:1 A  tests/feature_test.rs
├╯
```

To show files for specific commits only, pass their short IDs or git hashes after `-f`:

```
git loom status -f mqt
git loom status -f mqt pkz
git loom status -f abc1234
```

Only the listed commits display their file list; all other commits are rendered normally. Unknown identifiers are silently ignored.

## Branch Topologies

### Independent branches

Each feature branch forks from the integration line independently:

```
│╭─ fb [feature-b]
│●    mqt  d0472f9 Fix bug in feature B
├╯
│
│╭─ fa [feature-a]
│●    rsv  2ee61e1 Add feature A
├╯
```

### Stacked branches

Feature-b is stacked on top of feature-a:

```
│╭─ fb [feature-b]
│●    mqt  4e046ab Second commit on feature-b
│●    pkz  0b85ca7 First commit on feature-b
││
│├─ fa [feature-a]
│●    rsv  caa87a9 Second commit on feature-a
│●    tqn  18faee8 First commit on feature-a
├╯
```

### Co-located branches

Multiple branches pointing to the same commit:

```
│╭─ fv [feature-a-v2]
│├─ fa [feature-a]
│●    rsv  2ee61e1 Add feature A
├╯
```

### Upstream ahead

When upstream has new commits beyond the common base:

```
●    mqt  abc1234 Fix typo
│
│●  [origin/main] ⏫ 3 new commits
├╯ 204e309 (common base) 2025-07-06 Merge pull request #10
```

### Context commits

Show history before the base with a positional argument (`git loom 3` or `git loom status 3`):

```
● ff1b247 (upstream) [origin/main] Initial commit
· abc1234 2025-07-05 Previous work
· def5678 2025-07-04 Earlier change
```

Context commits are dimmed and display-only (no short ID, not actionable). The default is `loom.statusContext`, falling back to 1: the base alone, no extra context.

## Hidden Branches

Branches whose names start with the configured prefix (default: `local-`) are hidden from the status output by default. Both the branch section and its commits are fully suppressed — they do not appear as loose commits either.

This is useful for keeping local-only branches (personal configuration, secrets) out of the status view without removing them from the integration branch.

```bash
git loom --all          # show all branches including hidden
git loom status --all   # same, explicit
```

The hidden prefix is configurable (see [Configuration](../configuration.md#loomhidebranchpattern)).

## Theming

The graph colors adapt to the terminal background via the global `--theme` flag:

```bash
git loom --theme light status   # Light terminal background
git loom --theme dark status    # Dark terminal background
git loom --theme auto status    # Auto-detect (default)
```

See [Configuration](../configuration.md#--theme) for details.

## Agent mode

With the global `--agent` flag, `status` emits the graph as JSON on stdout — one
line, nothing else — while the rendered tree goes to stderr with the rest of the
human output. Tools read stdout alone; see [agent](agent.md#agent-mode---agent).

```bash
git loom status --agent            # one JSON line on stdout, the tree on stderr
git loom status --agent 2>/dev/null | jq   # machine stream only
```

The graph sits under `graph` in the `ok` object and mirrors the tree — same
sections, same order, same short IDs:

```json
{"status":"ok","graph":{
  "schema": 1,
  "integration_branch": "integration",
  "cwd_prefix": "",
  "local_changes": {
    "id": "zz",
    "files": [{"id":"ma","path":"src/main.rs","index":"M","worktree":" ",
               "state":"tracked"}]
  },
  "branches": [
    {"names": [{"id":"fu","name":"feature-ui","remote":null}],
     "stacked_on": "feature-api", "stacked_on_hidden": false,
     "commits": [{"id":"qvn","hash":"9c1d044","oid":"9c1d0448…",
                  "subject":"feat(ui): settings panel","change_id":"I9c1d…",
                  "files":[]}]}
  ],
  "loose_commits": [],
  "upstream": {"label":"origin/main","base_hash":"7f3e9b0","base_oid":"7f3e9b0c…",
               "base_subject":"chore(release): 2.4.0","base_date":"2026-09-14",
               "commits_ahead":0},
  "context_commits": []
}}
```

| Field | Meaning |
|---|---|
| `schema` | Bumped on any breaking change to this shape. |
| `branches[]` | Branch groups in render order: empty ones first, then each stack top-down. `names[]` holds co-located branches sharing one tip. |
| `stacked_on` | The branch directly below in the stack, or `null` — named as a [stacked push](push.md) names it (a co-located group's last `names` entry). |
| `stacked_on_hidden` | `true` when the branch below is [hidden](#hidden-branches): a push refuses this group, and `stacked_on` is `null` unless `--all` shows it. |
| `remote` | `synced` / `different` / `gone`, or `null` when never pushed — the `✓` / `↑` / `✗` indicators. |
| `state` | `conflicted`, `tracked` or `untracked`; `index`/`worktree` carry the raw `XY` characters. |
| `commits[]` | Newest first. Each branch lists only the commits it owns. |
| `files[]` | Populated by `-f`; ids are `<commit id>:<n>` counting from 0. |

`-f`, `--all` and the context count work exactly as they do on the tree.

## Prerequisites

- Must be on a local branch (not detached HEAD)
- Branch must have an upstream tracking branch configured