# hawkshaw A focused, fullscreen, **side-by-side** git review tool. `hawkshaw` walks you through the *unstaged* changes in a working tree, one file at a time, and lets you render a verdict on every **hunk** before you commit. When it exits, the index is staged exactly as you decided. You run `git commit` yourself. The scope stays narrow on purpose. There is no branch management, no log browsing, no sidebar. You read the change, you decide, you move on, with vim and `bat` muscle memory.

hawkshaw: a focused, fullscreen, side-by-side git review tool

## Why I review a lot of AI-generated code. You do the same. The terminal tools that already exist pull in two directions. Full git clients like lazygit and gitui do everything, so they put a busy general-purpose interface between you and the code. `git diff` and `git add -p` do too little: no real scrolling, no fullscreen reading mode. And their keyboard navigation is not great. hawkshaw takes one job. It puts a large diff in front of you, hunk by hunk, and keeps the interface out of the way while you work through it. ## Install ### Prebuilt binary ```sh curl -fsSL https://raw.githubusercontent.com/stojanovic/hawkshaw/main/install.sh | sh ``` The script detects your OS and architecture (Linux and macOS, x86_64 and arm64), downloads the matching binary from the latest GitHub release, verifies its SHA-256 against the release checksums before installing anything, and puts `hawkshaw` in `~/.local/bin` (or `/usr/local/bin` when run as root). ### Docker (recommended) Running hawkshaw in Docker is the recommended setup. Pin both the script and the binary to the same tag, so nothing moves underneath you: ```dockerfile RUN curl -fsSL https://github.com/stojanovic/hawkshaw/releases/download/v0.1.0/install.sh \ | VERSION=0.1.0 PREFIX=/usr/local sh ``` ### From source You need a Rust toolchain. There is no system `libgit2` to install first, since `git2` builds a bundled copy. ```sh git clone https://github.com/stojanovic/hawkshaw.git cd hawkshaw cargo install --path . ``` That puts the `hawkshaw` binary in `~/.cargo/bin`. ## Usage Run it inside a git repository: ```sh hawkshaw ``` It snapshots the working tree at launch, taking modified, untracked, and deleted files. Anything already staged is out of scope. The first file opens fullscreen, and your verdicts hit the index as you make them. The layout follows the terminal width by default: side-by-side at **120 columns** or wider, unified inline below that. Pin it if you would rather decide yourself. See [Configuration](#configuration). Ignored files disappear the same way `git status` makes them disappear. That covers the in-repo `.gitignore` and `.git/info/exclude`, and your **global** ignore configuration too: `core.excludesfile`, including a `~`-relative path, plus the conventional `~/.config/git/ignore`, `~/.gitignore_global`, and `~/.gitignore`. Once every hunk has a verdict, hawkshaw prints a short summary and closes the case. No need to press `q`. ```sh hawkshaw --help # keymap and options hawkshaw --version ``` ## Keys | Keys | Action | |------|--------| | `j` / `k`, `↓` / `↑` | scroll a line | | `F` / `B` | full page down / up | | `Ctrl-d` / `Ctrl-u`, `Space` / `b` | half page down / up | | `g` / `G`, `Home` / `End` | top / bottom | | `J` / `K` | next / previous hunk | | `h` / `l`, `←` / `→` | previous / next file | | `t` | cycle view: auto, split, inline | | `y` / `n` | stage / skip the current hunk | | `Y` / `N` | stage / skip every remaining hunk in the file | | `dd` | discard the current hunk (confirm) | | `DD` | discard the whole file back to HEAD (confirm) | | `u` | undo the last verdict | | `c` | copy the file path and current hunk (as a diff) to the clipboard | | `r` | refresh (re-read git status) | | `i` / `I` | add an untracked file, or its directory, to the root `.gitignore`, then refresh | | `?` | help overlay | | `q` | quit (warns if hunks are undecided) | Discards destroy work and always ask first. For an untracked file, a discard deletes the file. Binary files, images among them, do not split into hunks. hawkshaw shows a whole-file placeholder that takes the same verdict keys: `y`/`Y` stages the complete file, `n`/`N` skips it, `dd`/`DD` discards it after confirmation. `c` copies the current file path followed by its hunk as a unified diff, ready to paste into an AI assistant when you want a second opinion on the change. It writes through the OSC 52 terminal escape, so the system clipboard picks it up even over SSH, on terminals that support it: iTerm2, kitty, WezTerm, Alacritty, foot, and tmux with `set-clipboard on`. ## Presentation

▶ Click to watch the demo

- **Two layouts, one keystroke.** The **side-by-side** split puts the old side on the left, the new side on the right, and aligns changed lines across the gap. The **unified inline** view stacks `-` over `+` in one full-width column for narrower terminals, showing each changed block part by part: every removed line, then every added line, the way `git diff` reads. A reformatted block stays legible either way. Width picks the layout by default, and `t` cycles it. - **Dark focused diff bands.** The focused hunk keeps its syntax highlighting over dark green and red backgrounds. Everything else recedes to plain syntax-coloured text with only the gutters tinted, so your eye lands where the decision is. The `minimal` theme drops the band and leans on the left border alone. - **Intra-line highlighting** marks the words that actually changed, in a lighter green or red patch that still lets the syntax colours read through. - **Syntax highlighting** through `syntect`, with dual old and new line-number gutters. - **Viewport virtualization.** Only the visible window ever renders, so a 10,000-line diff scrolls as fast as a ten-line one. ## Configuration hawkshaw reads an optional TOML config file, by default from `~/.hawkshaw/config.toml`. Point it somewhere else with `--config`: ```sh hawkshaw --config ./review.toml hawkshaw --config=./review.toml ``` The file takes two optional keys: | Key | Values | Default | Meaning | |-----|--------|---------|---------| | `theme` | `default`, `minimal`, `noir` | `default` | colour scheme | | `view` | `auto`, `split`, `inline` | `auto` | diff layout | **`theme`** picks the palette. `default` is a dark, syntax-friendly scheme with green and red backgrounds on the focused changed lines. `minimal` is high-contrast *text* with no background fills, marking the focused hunk with the cursor bar and underlining intra-line changes in bold. `noir` goes black-and-white, film-noir greyscale, monochrome down to the syntax, where the added and removed sides read apart by brightness instead of colour. `high-contrast` still works as an alias of `default`. **`view`** sets the layout. `auto`, the default, reads the terminal or pane width and takes the side-by-side **split** at 120 columns or wider, unified **inline** below that. It re-adapts live as you resize, a tmux split included. `split` and `inline` pin one layout. The `t` key cycles the preference at runtime, from `auto` to `split` to `inline`. Both keys are optional. An omitted key falls back to its default, and a missing config file is fine, since the defaults apply throughout. An invalid config, an unknown value or malformed TOML, makes hawkshaw exit with a clear error before it starts rather than quietly ignoring what you wrote. ```toml # ~/.hawkshaw/config.toml theme = "noir" view = "inline" ``` ## How it's built Rust, synchronous, no async. `ratatui` and `crossterm` drive the TUI, `git2` (libgit2) reads status and diffs and applies your verdicts, `syntect` handles highlighting, and `similar` does line pairing and intra-line change detection. The crate splits into a thin binary and a fully testable library. CI enforces **100% line and function coverage**. ## The name I built hawkshaw (no, Claude Code and Codex built it) while working on [Competitor Tracker](https://competitortracker.io), an app that tracks your competitors and sends you a list of the most important changes every Monday. Competitor Tracker speaks in a film-noir detective voice, so the same noir detective theme carried over to this tool. *Hawkshaw* is an old slang word for a detective. It goes back to the detective character Hawkshaw in Tom Taylor's 1863 play *The Ticket-of-Leave Man*, and it later titled Gus Mager's early twentieth-century newspaper comic strip *Hawkshaw the Detective*. Of all the noir-detective names on the shortlist, this one sounds good, types easily, sticks in memory, and is unique enough. ## Contributing Bug reports and pull requests are welcome. Read [CONTRIBUTING.md](CONTRIBUTING.md) first, particularly the part about the coverage gate, which turns down any code that arrives without a test. ## License MIT. See [LICENSE](LICENSE).