Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle git blame #761

Merged
merged 4 commits into from
Nov 16, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ name = "delta"
path = "src/main.rs"

[dependencies]
chrono = "0.4.19"
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a security bug in this library (flagged by cargo audit). But I think there's nothing we can do about our exposure other than wait for upstream changes because syntect uses the library, and we can't replace syntect.

trishume/syntect#385

chrono-humanize = "0.2.1"
ansi_colours = "1.0.4"
ansi_term = "0.12.1"
atty = "0.2.14"
Expand Down
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
diff = delta
show = delta
log = delta
blame = delta
reflog = delta

[interactive]
Expand Down Expand Up @@ -63,11 +64,13 @@ Code evolves, and we all spend time studying diffs. Delta aims to make this both
- [Choosing colors (styles)](#choosing-colors-styles)
- [Line numbers](#line-numbers)
- [Side-by-side view](#side-by-side-view)
- [git blame](#git-blame)
- ["Features": named groups of settings](#features-named-groups-of-settings)
- [Custom themes](#custom-themes)
- [diff-highlight and diff-so-fancy emulation](#diff-highlight-and-diff-so-fancy-emulation)
- [--color-moved support](#--color-moved-support)
- [Navigation keybindings for large diffs](#navigation-keybindings-for-large-diffs)
- [Git blame](#git-blame-1)
- [24 bit color (truecolor)](#24-bit-color-truecolor)
- [Using Delta with GNU Screen](#using-delta-with-gnu-screen)
- [Using Delta on Windows](#using-delta-on-windows)
Expand Down Expand Up @@ -152,6 +155,7 @@ Here's what `git show` can look like with git configured to use delta:
- `diff-highlight` and `diff-so-fancy` emulation modes
- Stylable box/line decorations to draw attention to commit, file and hunk header sections.
- Support for Git's `--color-moved` feature.
- Customizable `git blame` with syntax highlighting (`--hyperlinks` formats commits as links to GitHub/GitLab/Bitbucket etc)
- Code can be copied directly from the diff (`-/+` markers are removed by default).
- `n` and `N` keybindings to move between files in large diffs, and between diffs in `log -p` views (`--navigate`)
- Commit hashes can be formatted as terminal [hyperlinks](https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda) to the GitHub/GitLab/Bitbucket page (`--hyperlinks`).
Expand Down Expand Up @@ -407,6 +411,10 @@ In contrast, the long replacement line in the right panel overflows by almost an
For control over the details of line wrapping, see `--wrap-max-lines`, `--wrap-left-symbol`, `--wrap-right-symbol`, `--wrap-right-percent`, `--wrap-right-prefix-symbol`, `--inline-hint-style`.
Line wrapping was implemented by @th1000s.

### git blame

Set delta as the pager for `blame` in the `[pager]` section of your gitconfig. See the example at the [top of the page](#get-started).

### "Features": named groups of settings

All delta options can go under the `[delta]` section in your git config file. However, you can also use named "features" to keep things organized: these are sections in git config like `[delta "my-feature"]`. Here's an example using two custom features:
Expand Down Expand Up @@ -493,6 +501,12 @@ In order to support this feature, Delta has to look at the raw colors it receive

Use the `navigate` feature to activate navigation keybindings. In this mode, pressing `n` will jump forward to the next file in the diff, and `N` will jump backwards. If you are viewing multiple commits (e.g. via `git log -p`) then navigation will also visit commit boundaries.

### Git blame

Delta will render `git blame` output in its own way, if you have `pager.blame = delta` set in your `gitconfig`. Example:

<table><tr><td><img width=300px src="https://user-images.githubusercontent.com/52205/140330813-91b198af-c396-4c5e-9a30-26ee35b78e7e.png" alt="image" /></td></tr></table>

### 24 bit color (truecolor)

Delta looks best if your terminal application supports 24 bit colors. See https://github.com/termstandard/colors#readme. For example, on MacOS, iTerm2 supports 24-bit colors but Terminal.app does not.
Expand Down
21 changes: 21 additions & 0 deletions src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,27 @@ pub struct Opt {
/// (underline), 'ol' (overline), or the combination 'ul ol'.
pub hunk_header_decoration_style: String,

/// Format string for git blame commit metadata. Available placeholders are
/// "{timestamp}", "{author}", and "{commit}".
#[structopt(
long = "blame-format",
default_value = "{timestamp:<15} {author:<15.14} {commit:<8} │ "
)]
pub blame_format: String,

/// Background colors used for git blame lines (space-separated string).
/// Lines added by the same commit are painted with the same color; colors
/// are recycled as needed.
#[structopt(long = "blame-palette")]
pub blame_palette: Option<String>,

/// Format of `git blame` timestamp in raw git output received by delta.
#[structopt(
long = "blame-timestamp-format",
default_value = "%Y-%m-%d %H:%M:%S %z"
)]
pub blame_timestamp_format: String,

/// Default language used for syntax highlighting when this cannot be
/// inferred from a filename. It will typically make sense to set this in
/// per-repository git config (.git/config)
Expand Down
6 changes: 6 additions & 0 deletions src/color.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,3 +162,9 @@ const DARK_THEME_PLUS_COLOR_256: Color = Color::Fixed(22);
const DARK_THEME_PLUS_EMPH_COLOR: Color = Color::RGB(0x00, 0x60, 0x00);

const DARK_THEME_PLUS_EMPH_COLOR_256: Color = Color::Fixed(28);

// blame

pub const LIGHT_THEME_BLAME_PALETTE: &[&str] = &["#FFFFFF", "#DDDDDD", "#BBBBBB"];

pub const DARK_THEME_BLAME_PALETTE: &[&str] = &["#000000", "#222222", "#444444"];
25 changes: 25 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ fn adapt_wrap_max_lines_argument(arg: String) -> usize {
pub struct Config {
pub available_terminal_width: usize,
pub background_color_extends_to_terminal_width: bool,
pub blame_format: String,
pub blame_palette: Vec<String>,
pub blame_timestamp_format: String,
pub color_only: bool,
pub commit_regex: Regex,
pub commit_style: Style,
Expand Down Expand Up @@ -213,6 +216,8 @@ impl From<cli::Opt> for Config {
_ => *style::GIT_DEFAULT_PLUS_STYLE,
};

let blame_palette = make_blame_palette(opt.blame_palette, opt.computed.is_light_mode);

let file_added_label = opt.file_added_label;
let file_copied_label = opt.file_copied_label;
let file_modified_label = opt.file_modified_label;
Expand Down Expand Up @@ -257,6 +262,9 @@ impl From<cli::Opt> for Config {
background_color_extends_to_terminal_width: opt
.computed
.background_color_extends_to_terminal_width,
blame_format: opt.blame_format,
blame_palette,
blame_timestamp_format: opt.blame_timestamp_format,
commit_style,
color_only: opt.color_only,
commit_regex,
Expand Down Expand Up @@ -603,6 +611,23 @@ fn make_commit_file_hunk_header_styles(opt: &cli::Opt) -> (Style, Style, Style,
)
}

fn make_blame_palette(blame_palette: Option<String>, is_light_mode: bool) -> Vec<String> {
match (blame_palette, is_light_mode) {
(Some(string), _) => string
.split_whitespace()
.map(|s| s.to_owned())
.collect::<Vec<String>>(),
(None, true) => color::LIGHT_THEME_BLAME_PALETTE
.iter()
.map(|s| s.to_string())
.collect::<Vec<String>>(),
(None, false) => color::DARK_THEME_BLAME_PALETTE
.iter()
.map(|s| s.to_string())
.collect::<Vec<String>>(),
}
}

/// Did the user supply `option` on the command line?
pub fn user_supplied_option(option: &str, arg_matches: &clap::ArgMatches) -> bool {
arg_matches.occurrences_of(option) > 0
Expand Down
7 changes: 6 additions & 1 deletion src/delta.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use std::borrow::Cow;
use std::collections::HashMap;
use std::io::BufRead;
use std::io::Write;

Expand All @@ -13,14 +14,15 @@ use crate::style::DecorationStyle;

#[derive(Clone, Debug, PartialEq)]
pub enum State {
CommitMeta, // In commit metadata section
CommitMeta, // In commit metadata section
FileMeta, // In diff metadata section, between (possible) commit metadata and first hunk
HunkHeader(String, String), // In hunk metadata line (line, raw_line)
HunkZero, // In hunk; unchanged line
HunkMinus(Option<String>), // In hunk; removed line (raw_line)
HunkPlus(Option<String>), // In hunk; added line (raw_line)
SubmoduleLog, // In a submodule section, with gitconfig diff.submodule = log
SubmoduleShort(String), // In a submodule section, with gitconfig diff.submodule = short
Blame(String, Option<String>), // In a line of `git blame` output (commit, repeat_blame_line).
Unknown,
// The following elements are created when a line is wrapped to display it:
HunkZeroWrapped, // Wrapped unchanged line
Expand Down Expand Up @@ -67,6 +69,7 @@ pub struct StateMachine<'a> {
// avoid emitting the file meta header line twice (#245).
pub current_file_pair: Option<(String, String)>,
pub handled_file_meta_header_line_file_pair: Option<(String, String)>,
pub blame_commit_colors: HashMap<String, String>,
}

pub fn delta<I>(lines: ByteLines<I>, writer: &mut dyn Write, config: &Config) -> std::io::Result<()>
Expand All @@ -92,6 +95,7 @@ impl<'a> StateMachine<'a> {
handled_file_meta_header_line_file_pair: None,
painter: Painter::new(writer, config),
config,
blame_commit_colors: HashMap::new(),
}
}

Expand All @@ -116,6 +120,7 @@ impl<'a> StateMachine<'a> {
|| self.handle_submodule_log_line()?
|| self.handle_submodule_short_line()?
|| self.handle_hunk_line()?
|| self.handle_blame_line()?
|| self.should_skip_line()
|| self.emit_line_unchanged()?;
}
Expand Down
31 changes: 27 additions & 4 deletions src/features/line_numbers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -266,14 +266,22 @@ fn format_and_paint_line_number_field<'a>(
.as_ref()
.unwrap_or(&Align::Center);
match placeholder.placeholder {
Some(Placeholder::NumberMinus) => ansi_strings.push(styles[Minus].paint(
format_line_number(line_numbers[Minus], alignment_spec, width, None, config),
)),
Some(Placeholder::NumberMinus) => {
ansi_strings.push(styles[Minus].paint(format_line_number(
line_numbers[Minus],
alignment_spec,
width,
placeholder.precision,
None,
config,
)))
}
Some(Placeholder::NumberPlus) => {
ansi_strings.push(styles[Plus].paint(format_line_number(
line_numbers[Plus],
alignment_spec,
width,
placeholder.precision,
Some(plus_file),
config,
)))
Expand All @@ -292,10 +300,11 @@ fn format_line_number(
line_number: Option<usize>,
alignment: &Align,
width: usize,
precision: Option<usize>,
plus_file: Option<&str>,
config: &config::Config,
) -> String {
let pad = |n| format::pad(n, width, alignment);
let pad = |n| format::pad(n, width, alignment, precision);
match (line_number, config.hyperlinks, plus_file) {
(None, _, _) => pad(""),
(Some(n), true, Some(file)) => {
Expand Down Expand Up @@ -332,6 +341,7 @@ pub mod tests {
placeholder: Some(Placeholder::NumberMinus),
alignment_spec: None,
width: None,
precision: None,
suffix: "".into(),
prefix_len: 0,
suffix_len: 0,
Expand All @@ -348,6 +358,7 @@ pub mod tests {
placeholder: Some(Placeholder::NumberPlus),
alignment_spec: None,
width: Some(4),
precision: None,
suffix: "".into(),
prefix_len: 0,
suffix_len: 0,
Expand All @@ -364,6 +375,7 @@ pub mod tests {
placeholder: Some(Placeholder::NumberPlus),
alignment_spec: Some(Align::Right),
width: Some(4),
precision: None,
suffix: "".into(),
prefix_len: 0,
suffix_len: 0,
Expand All @@ -380,6 +392,7 @@ pub mod tests {
placeholder: Some(Placeholder::NumberPlus),
alignment_spec: Some(Align::Right),
width: Some(4),
precision: None,
suffix: "".into(),
prefix_len: 0,
suffix_len: 0,
Expand All @@ -396,6 +409,7 @@ pub mod tests {
placeholder: Some(Placeholder::NumberPlus),
alignment_spec: Some(Align::Right),
width: Some(4),
precision: None,
suffix: "@@".into(),
prefix_len: 2,
suffix_len: 2,
Expand All @@ -413,6 +427,7 @@ pub mod tests {
placeholder: Some(Placeholder::NumberMinus),
alignment_spec: Some(Align::Left),
width: Some(3),
precision: None,
suffix: "@@---{np:_>4}**".into(),
prefix_len: 2,
suffix_len: 15,
Expand All @@ -422,6 +437,7 @@ pub mod tests {
placeholder: Some(Placeholder::NumberPlus),
alignment_spec: Some(Align::Right),
width: Some(4),
precision: None,
suffix: "**".into(),
prefix_len: 5,
suffix_len: 2,
Expand All @@ -439,6 +455,7 @@ pub mod tests {
placeholder: None,
alignment_spec: None,
width: None,
precision: None,
suffix: "__@@---**".into(),
prefix_len: 0,
suffix_len: 9,
Expand All @@ -455,12 +472,14 @@ pub mod tests {
placeholder: Some(Placeholder::NumberMinus),
alignment_spec: Some(Align::Left),
width: Some(4),
precision: None,
suffix: "|".into(),
prefix_len: 2,
suffix_len: 1,
}]
);
}

#[test]
fn test_line_number_format_odd_width_two() {
assert_eq!(
Expand All @@ -475,6 +494,7 @@ pub mod tests {
placeholder: Some(Placeholder::NumberMinus),
alignment_spec: Some(Align::Left),
width: Some(4),
precision: None,
suffix: "+{np:<4}|".into(),
prefix_len: 2,
suffix_len: 9,
Expand All @@ -484,6 +504,7 @@ pub mod tests {
placeholder: Some(Placeholder::NumberPlus),
alignment_spec: Some(Align::Left),
width: Some(4),
precision: None,
suffix: "|".into(),
prefix_len: 1,
suffix_len: 1,
Expand All @@ -500,6 +521,7 @@ pub mod tests {
placeholder: None,
alignment_spec: None,
width: None,
precision: None,
suffix: "|++|".into(),
prefix_len: 1,
suffix_len: 4,
Expand All @@ -522,6 +544,7 @@ pub mod tests {
placeholder: Some(Placeholder::NumberMinus),
alignment_spec: None,
width: None,
precision: None,
suffix: long.into(),
suffix_len: long.len(),
},]
Expand Down
Loading