How bat Integrates with Git to Show File Modifications

bat uses the optional git feature to query repositories for file changes and decorates the gutter with symbols (+, ‾, _, ~) indicating added, removed-above, removed-below, and modified lines.

The bat command-line tool by sharkdp/bat enhances standard file viewing with syntax highlighting and seamless Git integration. When you view files within a Git repository, bat automatically detects modifications and displays visual indicators in the gutter, making it easy to spot changes without running separate diff commands. This integration works through a four-stage pipeline that isolates Git logic in dedicated modules while keeping the feature optional via Cargo features.

Stage 1: Detecting the Repository and Computing the Diff

The Git integration begins in src/diff.rs, where the get_git_diff function discovers the nearest Git repository and calculates per-line changes. When Controller::print_input processes a file, it calls get_git_diff only if the git feature is enabled and the user requested diff information via --diff or by including the changes style component.

The function uses Repository::discover to find the repository root, then executes repo.diff_index_to_workdir with DiffOptions limited to the specific file. The resulting git2::Diff is walked to extract hunks, which are converted into LineChange variants (Added, RemovedAbove, RemovedBelow, Modified). These are stored in a HashMap<u32, LineChange> (aliased as LineChanges) mapping line numbers to their change status.

Stage 2: Propagating Changes to the Printer

Once the diff is computed, Controller::print_input creates an InteractivePrinter instance and passes the line_changes data to InteractivePrinter::new. The printer stores this map in self.line_changes, making the Git modification data available during the rendering phase. This propagation occurs in src/controller.rs and ensures that the diff information travels from the repository detection layer to the output formatting layer without coupling the Git logic to the printing implementation.

Stage 3: Configuring Gutter Decorations

During printer initialization in src/printer.rs, the InteractivePrinter builds a list of gutter decorations. The LineChangesDecoration::new constructor is called only if the changes style component is enabled and the line_changes map contains at least one entry. This conditional setup ensures that the Git symbols only appear when relevant, avoiding unnecessary overhead for files without modifications or when the user disables the feature via --style=plain.

Stage 4: Rendering the Modification Symbols

The actual symbol generation happens in src/decorations.rs within LineChangesDecoration::generate. For each line processed by InteractivePrinter::print_line, the decoration looks up the current line number in printer.line_changes. If a LineChange exists, the function returns the corresponding cached symbol: + for added lines, ‾ for removed-above, _ for removed-below, and ~ for modified lines. These symbols are painted using the colors defined in Colors::git_added, Colors::git_removed, and Colors::git_modified. If no change exists for the line, a blank space is printed instead.

Using bat's Git Integration

The Git diff feature is enabled by default in official binaries. You can control it through command-line flags and style options.

To view a file with Git modification markers:

bat src/main.rs

If src/main.rs has uncommitted changes, the gutter will display:


 1  │ fn main() {                # unchanged line

+2  │     println!("new line");  # added line

‾3  │                             # line that was removed above the current line

~4  │     // modified line      # modified line

To show only changed lines with context:

bat --diff src/main.rs

To disable Git decorations and show plain output:

bat --style=plain src/main.rs

When building from source, ensure the git feature is enabled:

cargo build --features git --release

Key Source Files and Architecture

The Git integration is isolated in specific modules to maintain clean separation of concerns:

  • src/diff.rs — Contains get_git_diff which uses libgit2 via the git2 crate to discover repositories and compute file-specific diffs, returning LineChanges (a HashMap<u32, LineChange>).

  • src/controller.rs — Orchestrates the printing process. Controller::print_input calls get_git_diff conditionally and passes the resulting line_changes to InteractivePrinter::new.

  • src/printer.rs — Implements InteractivePrinter which stores line_changes and conditionally initializes LineChangesDecoration when the changes style component is active.

  • src/decorations.rs — Defines LineChangesDecoration and its generate method, which maps LineChange variants to symbols (+, ‾, _, ~) and applies Git-specific colors.

This architecture keeps the optional Git dependency isolated behind the git Cargo feature, ensuring that users who do not need Git integration can build a lighter binary.

Summary

  • bat integrates with Git through an optional git feature that uses the git2 crate to detect repositories and compute file-specific diffs.
  • Four-stage pipeline: Repository detection and diff computation (src/diff.rs), propagation to printer (src/controller.rs), decoration configuration (src/printer.rs), and symbol rendering (src/decorations.rs).
  • Visual indicators: The gutter displays + for added lines, ‾ for removed-above, _ for removed-below, and ~ for modified lines, colored according to Git status.
  • User control: Enable with --diff or style components, disable with --style=plain, and compile with cargo build --features git.

Frequently Asked Questions

How do I enable Git integration when building bat from source?

When compiling bat from the sharkdp/bat repository, you must explicitly enable the git feature flag using Cargo. Run cargo build --features git --release to include the Git diff functionality. Official pre-built binaries already have this feature enabled by default.

What do the symbols in the gutter mean when viewing a Git-tracked file?

The gutter symbols indicate line-level changes between the working directory and the Git index. A plus sign (+) marks added lines, a tilde (~) indicates modified lines, an overline (‾) shows that lines were removed above the current line, and an underscore (_) shows lines removed below. These symbols are defined in src/decorations.rs within the LineChangesDecoration::generate method.

Can I use bat's Git integration without the git feature enabled?

No, the Git integration requires the optional git feature to be enabled at compile time. Without this feature, get_git_diff in src/diff.rs is not available, and the InteractivePrinter will not receive line change data. You can verify if your binary supports Git by running bat --list-features or attempting to use the --diff flag.

How does bat handle files that are not tracked by Git or repositories without modifications?

When a file has no Git modifications or is not inside a repository, get_git_diff returns None and the LineChanges map remains empty. In src/printer.rs, the LineChangesDecoration is only added to the decoration list when the map contains entries. Consequently, the gutter displays only line numbers (or blank spaces) without modification symbols, ensuring clean output for unchanged files.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →