Back to Articles

Serie: Rendering GUI-Quality Git Graphs in Your Terminal with Image Protocols

[ View on GitHub ]

Serie: Rendering GUI-Quality Git Graphs in Your Terminal with Image Protocols

Hook

While most terminal Git clients still draw commit graphs with ASCII characters like it’s 1985, Serie takes a radically different approach: it renders actual graphical images directly in your terminal using protocols designed for displaying photos and screenshots.

Context

Anyone who’s stared at git log --graph --oneline knows the pain. The ASCII art commit graphs become incomprehensible tangles once your repository has more than a handful of branches. GUI clients like GitKraken solve this with beautiful, anti-aliased graphs, but they yank you out of your terminal workflow and consume hundreds of megabytes of RAM for what should be a simple visualization task.

The terminal Git client landscape has long been split between two worlds: lightweight CLI tools with unreadable ASCII graphs, or heavy GUI applications that feel like context-switching to a different planet. Tools like tig and lazygit improved the situation with interactive TUIs, but they’re still constrained by character-based rendering—your commit graph is still composed of slashes, pipes, and asterisks. Serie bridges this gap by exploiting a lesser-known capability of modern terminal emulators: the ability to render actual images inline with text, the same way your terminal can display a PNG when you SSH into a server and view a chart.

Technical Insight

Serie’s architecture hinges on a surprisingly underutilized feature of terminal emulators like iTerm2 and Kitty: image protocols that allow applications to send bitmap data directly to the terminal for rendering. This is the same mechanism that lets you preview images in ranger or display plots in Jupyter notebooks running in a terminal.

Under the hood, Serie uses the git2 Rust crate to read repository data, then constructs an in-memory graph representation using either chronological or topological ordering algorithms. But instead of rendering this graph with Unicode box-drawing characters like traditional TUIs, it generates an actual raster image using graphics libraries, then transmits this image to the terminal using either iTerm2’s Inline Images Protocol (a base64-encoded escape sequence) or Kitty’s Terminal Graphics Protocol (a more efficient chunked transmission format).

The application structure follows a typical Ratatui pattern with a main event loop, but with a crucial difference. Here’s a simplified view of how Serie renders a commit graph:

// Pseudo-code based on Serie's architecture
fn render_commit_graph(commits: &[Commit], terminal: &Terminal) -> Result<()> {
    // Generate graph layout
    let graph = build_graph_layout(commits)?;
    
    // Render to an in-memory image buffer
    let image_buffer = render_graph_to_image(&graph, &config)?;
    
    // Detect terminal capabilities
    match detect_image_protocol() {
        Protocol::Kitty => {
            // Use Kitty's graphics protocol for efficient transmission
            send_kitty_image(terminal, &image_buffer)?;
        }
        Protocol::ITerm2 => {
            // Fall back to iTerm2's inline image protocol
            send_iterm2_image(terminal, &image_buffer)?;
        }
        Protocol::None => {
            // Gracefully degrade to ASCII
            render_ascii_fallback(terminal, &graph)?;
        }
    }
    Ok(())
}

What makes this approach powerful is that Serie gets all the benefits of programmatic image generation—anti-aliased lines, smooth curves, custom colors, arbitrary fonts—while remaining a terminal application. The commit graph isn’t constrained by character cells or Unicode limitations. Want to render a merge commit with a perfect Bézier curve? No problem. Want to use different line thicknesses to indicate different branch types? Trivial with image rendering, impossible with ASCII.

The Ratatui integration is particularly clever. Ratatui doesn’t natively support image rendering (it’s a text-based TUI framework), so Serie essentially reserves a “blank” area in the TUI layout where it injects the image using raw terminal escape sequences. This means the interactive parts of the UI (keyboard navigation, commit details, diff viewer) use Ratatui’s normal widget rendering, while the commit graph appears as a seamlessly integrated image.

Serie also demonstrates thoughtful configuration design. Users can define external commands for viewing diffs, customize keybindings, and adjust graph rendering parameters through a TOML config file:

[graph]
style = "rounded"  # or "square", "line"
order = "chronological"  # or "topological"

[commands]
diff = "delta --side-by-side"
open = "vim {file}"

[keybindings]
quit = "q"
commit_detail = "Enter"
scroll_up = "k"

This configuration-driven approach means Serie can adapt to different workflows without requiring code changes. The external command system is particularly smart: it uses placeholder variables that get substituted at runtime, allowing integration with any CLI tool that accepts file paths or commit references.

The graph rendering itself supports multiple ordering algorithms because Git’s commit history isn’t linear. Chronological ordering shows commits in timestamp order (what actually happened), while topological ordering shows commits in parent-child relationship order (what Git considers the logical history). Serie lets you toggle between these views, which is crucial for understanding complex repository histories with long-running feature branches or cherry-picks.

Gotcha

Serie’s biggest limitation is terminal compatibility. It only works with terminal emulators that support iTerm2 or Kitty image protocols, which immediately excludes a large portion of users. If you’re on a standard Linux terminal, Windows Terminal, or—critically—working inside tmux or Zellij, Serie falls back to ASCII rendering, which defeats its entire value proposition. The tmux limitation is particularly painful for many developers whose entire workflow is built around terminal multiplexers. While there are workarounds (like using Kitty’s multiplexer features or running Serie outside tmux), these require changing your established workflow.

Notably, Serie doesn’t support Sixel graphics, which is actually more widely supported than either iTerm2 or Kitty protocols across terminal emulators (it’s supported in xterm, mlterm, foot, and even Windows Terminal via extensions). This feels like a missed opportunity for broader compatibility. Additionally, Serie is strictly read-only. You can visualize your repository’s history beautifully, but you can’t stage files, create commits, or perform rebases. This means Serie is always a companion tool, never a replacement for your Git CLI or a full-featured TUI like lazygit. You’ll find yourself constantly switching between Serie for visualization and other tools for actual Git operations.

Verdict

Use Serie if you work primarily in Kitty or iTerm2 (or compatible terminals like WezTerm with Kitty protocol support) and you regularly work with repositories that have complex branching histories where standard ASCII graphs become unreadable. It’s perfect for developers doing code archaeology, reviewing merge histories, or trying to understand how different feature branches relate to each other. The visual clarity is genuinely transformative for these use cases. Skip Serie if you rely on tmux/Zellij for your workflow, work primarily over SSH to remote servers, use standard terminal emulators without image protocol support, or need to perform Git operations beyond visualization. Also skip it if you’re looking for a general-purpose Git TUI—Serie is a specialized visualization tool, not a Git client replacement. For those situations, stick with lazygit or gitui for universal compatibility, or just accept the overhead of a GUI client like Fork if you truly need beautiful graphs everywhere.