# GitButler CLI Output Formatting Modes: Human, Shell, JSON, and None Explained

> Master GitButler CLI output with Human, Shell, JSON, and None modes. Learn to control command results using format flags or environment variables for better workflows.

- Repository: [GitButler/gitbutler](https://github.com/gitbutlerapp/gitbutler)
- Tags: deep-dive
- Published: 2026-02-19

---

**GitButler supports four distinct CLI output formatting modes—Human, Shell, JSON, and None—that control how command results are rendered, selectable via `--format` flags or the `BUT_OUTPUT_FORMAT` environment variable.**

The `gitbutlerapp/gitbutler` repository implements a flexible output system in its Rust-based CLI (the `but` command) to accommodate both interactive terminal users and automation scripts. Understanding these **GitButler CLI output formatting modes** allows you to integrate the tool seamlessly into shell scripts, CI pipelines, or interactive workflows.

## The Four Output Format Modes

GitButler’s output behavior is governed by the `OutputFormat` enum defined in [`crates/but/src/args/mod.rs`](https://github.com/gitbutlerapp/gitbutler/blob/main/crates/but/src/args/mod.rs). Each variant corresponds to a specific use case, from rich human-readable text to complete silence.

### Human Mode (Default Terminal Output)

**Human** mode produces verbose, color-rich text designed for direct reading. This is the default when `--format` is omitted and stdout is attached to a terminal. In this mode, GitButler uses pagers when appropriate and includes formatting enhancements like colors and indentation to improve readability.

### Shell Mode (Script-Friendly Plain Text)

**Shell** mode emits minimal, plain-text output ideal for capturing into shell variables. When stdout is redirected or when explicitly selected with `--format shell`, GitButler strips decorative formatting and returns concise strings that can be directly assigned in bash scripts.

### JSON Mode (Machine-Readable Structured Data)

**JSON** mode outputs structured, machine-readable data via `out.write_value()`. Enabled with `--json` or `--format json`, this mode suppresses normal `write!` calls and requires commands to explicitly serialize data structures. This is the preferred format for integration with tools like `jq` or for programmatic processing.

### None Mode (Silent Operation)

**None** mode silences all standard output, equivalent to redirecting to `/dev/null`. Selected with `--format none`, this mode is useful for "fire-and-forget" operations where you only care about the exit status, not the command feedback.

## How Output Format Selection Works

The format selection logic resides in [`crates/but/src/args/mod.rs`](https://github.com/gitbutlerapp/gitbutler/blob/main/crates/but/src/args/mod.rs), where the `OutputFormat` enum and CLI arguments are defined:

```rust
pub enum OutputFormat {
    #[default] Human,
    Shell,
    Json,
    None,
}

```

The CLI exposes multiple control mechanisms for this setting:

```rust
#[clap(
    long,
    short = 'f',
    env = "BUT_OUTPUT_FORMAT",
    conflicts_with = "json",
    default_value = "human"
)]
pub format: OutputFormat,

```

- **`--format <mode>`** (or `-f`) sets the mode directly.
- **`BUT_OUTPUT_FORMAT`** environment variable provides persistent configuration for scripts.
- **`--json`** convenience flag forces JSON mode and conflicts with `--format`.

The entry point in [`crates/but/src/lib.rs`](https://github.com/gitbutlerapp/gitbutler/blob/main/crates/but/src/lib.rs) resolves the final format by checking for the JSON flag first:

```rust
let output_format = if args.json { OutputFormat::Json } else { args.format };

```

When no format is specified, GitButler automatically selects **Human** if stdout is a terminal, or **Shell** if stdout is redirected (e.g., piped to another program).

## The OutputChannel Implementation

All output routing flows through the `OutputChannel` struct in [`crates/but/src/utils/output_channel.rs`](https://github.com/gitbutlerapp/gitbutler/blob/main/crates/but/src/utils/output_channel.rs). This centralizes write operations and enforces format-specific behavior:

```rust
match self.format {
    OutputFormat::Human | OutputFormat::Shell => {
        // write to pager or stdout (human-readable)
    }
    OutputFormat::Json | OutputFormat::None => {
        // JSON is written via `write_value`; otherwise writes are ignored
        Ok(())
    }
}

```

Commands use specialized methods to emit data appropriately:
- `out.for_human()` and `out.for_shell()` for text output.
- `out.write_value(&value)` for JSON serialization.

The `OutputChannel` also implements JSON buffering via `start_json_buffering()` and `take_json_buffer()` to support the `--status-after` flag, which combines mutation results with subsequent workspace status in a single JSON payload.

## Practical Examples

### Interactive Human Output

```bash

# Default colored output for terminal reading

but status

```

### Shell Variable Capture

```bash

# Capture branch removal confirmation for scripting

result=$(but rm-branch myfeature --format shell)
echo "Removed branch: $result"

```

### JSON Processing with jq

```bash

# Extract branch names and HEAD commits

but list-branches --json | jq '.[] | {name: .name, head: .head}'

```

Or configure via environment variable:

```bash
export BUT_OUTPUT_FORMAT=json
but list-branches | jq '.[] | .name'

```

### Silent Execution

```bash

# Execute without any output

but prune --format none

```

### Combined Status After Mutation

```bash

# Rebase and append workspace status in single JSON output

but rebase myfeature --status-after --json

```

## Summary

- **Four modes** control GitButler CLI output: Human (rich text), Shell (plain text), JSON (structured data), and None (silent).
- **Configuration** uses `--format`, `-f`, `--json`, or the `BUT_OUTPUT_FORMAT` environment variable.
- **Automatic selection** defaults to Human for terminals and Shell for redirected stdout.
- **OutputChannel** in [`crates/but/src/utils/output_channel.rs`](https://github.com/gitbutlerapp/gitbutler/blob/main/crates/but/src/utils/output_channel.rs) centralizes format-specific routing and JSON buffering.
- **JSON mode** requires explicit `write_value()` calls and supports `--status-after` for combined output payloads.

## Frequently Asked Questions

### How do I force JSON output in GitButler?

Use the `--json` flag for any command, or set the `BUT_OUTPUT_FORMAT=json` environment variable. According to the source code in [`crates/but/src/args/mod.rs`](https://github.com/gitbutlerapp/gitbutler/blob/main/crates/but/src/args/mod.rs), the `--json` flag takes precedence and conflicts with the `--format` option, ensuring unambiguous machine-readable output.

### What is the difference between Human and Shell mode?

**Human** mode includes colors, pagination, and verbose formatting intended for direct terminal reading, while **Shell** mode strips all decorations to produce minimal plain text suitable for variable capture in scripts. The `OutputChannel` routes both through stdout but applies different formatting layers based on the selected variant.

### How does GitButler handle output when stdout is redirected?

When stdout is not attached to a terminal and no format is explicitly specified, GitButler automatically selects **Shell** mode. This logic is implemented in the argument handling code within [`crates/but/src/lib.rs`](https://github.com/gitbutlerapp/gitbutler/blob/main/crates/but/src/lib.rs), ensuring that piped or redirected commands receive parseable plain text by default.

### Can I combine --status-after with JSON formatting?

Yes. When using `--status-after` with `--json`, GitButler temporarily buffers the mutation's JSON output via `start_json_buffering()` and `take_json_buffer()` methods in `OutputChannel`, then combines it with the subsequent workspace status into a single JSON payload. This allows atomic operations to report both their result and the new repository state in one machine-readable structure.