How bat Retrieves and Displays Git Diff Information
bat discovers the closest Git repository using Repository::discover, generates a diff between the index and working directory via git2, and maps changed line numbers to color-coded highlights through the InteractivePrinter.
The bat command-line tool enhances standard file viewing with syntax highlighting and integrated Git diff visualization. When you run bat on a file within a Git repository, it can highlight added, removed, and modified lines directly in the terminal output. According to the sharkdp/bat source code, this functionality is only compiled when the git feature is enabled (#[cfg(feature = "git")]) and operates through a three-stage pipeline involving repository discovery, line change mapping, and conditional rendering.
Step 1: Repository Discovery and Diff Generation in src/diff.rs
The process begins in src/diff.rs with the diff::get_git_diff(path) function. This function first locates the containing repository using Repository::discover, which walks up the directory tree until it finds a .git folder.
Once discovered, bat converts the absolute file path into a repository-relative pathspec required by the Git2 library. It then configures a git2::DiffOptions object with context_lines(0) to ensure only the exact changed lines are reported initially. Finally, it calls repo.diff_index_to_workdir to produce a git2::Diff representing changes between the Git index and the working tree.
Step 2: Mapping Line Changes to a HashMap
After generating the diff, bat walks through the changes using diff.foreach. For each hunk, the code compares old_lines versus new_lines to classify changes into a LineChanges type, which is aliased as HashMap<u32, LineChange>.
The mapping logic categorizes each line into one of four states:
- Added – Lines that exist only in the working tree (new additions).
- RemovedAbove – Indicates the entire file was deleted (no new lines, line 1 is marked).
- RemovedBelow – Lines removed in-place where the new start equals 0.
- Modified – Any other mix of old and new lines representing content changes.
This LineChanges map is returned as Option<LineChanges> and provides the canonical record of which line numbers require highlighting during output.
Step 3: Integrating Diff Data in src/controller.rs
The print_input method in src/controller.rs determines when to fetch and apply Git diff information. The system checks two configuration flags before calling get_git_diff:
config.visible_lines.diff_mode()– True when running with--diff(which setsVisibleLines::DiffContext).config.style_components.changes()– True when using--style=changes.
When either condition is met and the input is an ordinary file (not stdin), the LineChanges are converted into LineRange objects that include configurable context lines around each change (controlled by VisibleLines::DiffContext(usize)). These ranges are passed to the InteractivePrinter, which renders added lines in green, removed lines in red, and modified lines in yellow.
Command-Line Usage Examples
You can trigger Git diff visualization using several flags:
# Show only lines around Git changes (default 3 lines of context)
bat --diff path/to/file.rs
# Show the full file with changed lines highlighted
bat --style=changes path/to/file.rs
# Customize the number of context lines around changes
bat --diff-context=5 path/to/file.rs
The underlying Rust logic that gates this behavior demonstrates the conditional check:
#[cfg(feature = "git")]
let line_changes = if config.visible_lines.diff_mode()
|| config.style_components.changes() {
match opened_input.kind {
crate::input::OpenedInputKind::OrdinaryFile(ref path) => get_git_diff(path),
_ => None,
}
} else {
None
};
Summary
- bat uses conditional compilation (
#[cfg(feature = "git")]) to include Git diff functionality only when explicitly enabled during the build process. - The
get_git_difffunction insrc/diff.rshandles repository discovery viaRepository::discoverand generates diffs usinggit2::DiffOptionswith zero context lines initially. - Line changes are categorized into Added, RemovedAbove, RemovedBelow, or Modified states and stored in a
HashMap<u32, LineChange>. - In
src/controller.rs, theprint_inputmethod conditionally retrieves diffs when--diffor--style=changesflags are active. - The
InteractivePrinterconsumes these line changes to color-code output (green for additions, red for deletions, yellow for modifications).
Frequently Asked Questions
How does bat detect if a file is inside a Git repository?
bat calls Repository::discover from the git2 crate, which traverses up the directory tree from the target file until it locates a .git directory. This establishes the repository context required to generate relative paths and compute accurate diffs.
What Git library does bat use for diff generation?
The implementation relies on the git2 crate, which provides Rust bindings for libgit2. Specifically, bat uses git2::DiffOptions to configure the diff parameters and repo.diff_index_to_workdir to compare the staged index against the working directory.
Why does bat set context_lines to 0 when generating the diff?
Setting context_lines(0) on the git2::DiffOptions object ensures that only the exact changed lines are reported from the Git library. bat handles context line display separately through its own VisibleLines::DiffContext configuration, allowing users to customize context size via flags like --diff-context without re-querying the repository.
How can I view only the changed lines without the full file content?
Use the --diff flag (or --diff-context=N for custom context size). This activates VisibleLines::DiffContext mode, which filters the output to show only line ranges surrounding Git modifications rather than rendering the entire file.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →