Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/master'
Browse files Browse the repository at this point in the history
* upstream/master:
  Set a default blame palette
  Handle blame output
  Refactor: module for working with format strings
  Add default-language option
  Refactor: syntax_highlight_and_paint_line function
  Refactor: eliminate format module
  Use dotted range notation in diff.submodule = short case
  Refactor: select next handled event with single disjunction expression (dandavison#701)
  More minimalistic display of submodule (short) diffs (dandavison#700)
  662 submodule diffs (dandavison#699)
  • Loading branch information
Kr1ss-XD committed Aug 29, 2021
2 parents a77e321 + 93aabce commit 224282b
Show file tree
Hide file tree
Showing 16 changed files with 721 additions and 193 deletions.
33 changes: 32 additions & 1 deletion 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"
chrono-humanize = "0.2.1"
ansi_colours = "1.0.4"
ansi_term = "0.12.1"
atty = "0.2.14"
Expand Down
45 changes: 45 additions & 0 deletions etc/examples/662-submodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#!/bin/bash
REPO_ROOT=/tmp/submodule-diff-example
REPO_DATE_FMT=%H:%M:%S.%N
mkdir ${REPO_ROOT} && git -C ${REPO_ROOT} init && cd ${REPO_ROOT}
date +${REPO_DATE_FMT} >> baserepo && git add baserepo && git commit -m "Base repo commit 1"

for sub in A B C; do
git init submodule${sub}
for iter in $(seq 1 4); do
date +${REPO_DATE_FMT} >> submodule${sub}/subcontent
git -C submodule${sub} add subcontent && git -C submodule${sub} commit -m "Submodule ${sub} initial commit $iter"
done
# Add initial submodule, message of "Submodule submoduleX 0000000...xxxxxxx (new submodule)", no individual commits
git submodule add ../bogus-url-${sub} submodule${sub} && \
git commit -m "Add submodule${sub}" # the diff handling for this is correct in delta

# Create additional submodule commits
for iter in $(seq 1 2); do
date +${REPO_DATE_FMT} >> submodule${sub}/subcontent
git -C submodule${sub} add subcontent && git -C submodule${sub} commit -m "Submodule ${sub} extra change ${iter}"
done
git add submodule${sub} && git commit -m "Update submodule${sub}"
done

git -C submoduleA reset --hard HEAD~4
git -C submoduleC reset --hard HEAD~2

for sub in B C; do
for iter in $(seq 1 3); do
date +${REPO_DATE_FMT} >> submodule${sub}/subcontent
git -C submodule${sub} add subcontent && git -C submodule${sub} commit -m "Submodule ${sub} stage change ${iter}"
done
done

# Add all submodule updates in single commit to test multiple submodule updates in single commit
git add submodule[A-C] && git commit -m "Update all submodules"

# submoduleA end state is only removed commits
# submoduleB end state is only added commits
# submoduleC is a mixture of removed and added commits (e.g. different branch)

# Manual, inspect superproject history via:
# "git -c diff.submodule=short log -p | delta --no-gitconfig"
# "git -c diff.submodule=log log -p | delta --no-gitconfig"
# "git -c diff.submodule=diff log -p | delta --no-gitconfig"
122 changes: 122 additions & 0 deletions src/blame.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
use chrono::{DateTime, FixedOffset};
use lazy_static::lazy_static;
use regex::Regex;

use crate::config;
use crate::delta;
use crate::format;

#[derive(Debug)]
pub struct BlameLine<'a> {
pub commit: &'a str,
pub author: &'a str,
pub time: DateTime<FixedOffset>,
pub line_number: usize,
pub code: &'a str,
}

// E.g.
//ea82f2d0 (Dan Davison 2021-08-22 18:20:19 -0700 120) let mut handled_line = self.handle_commit_meta_header_line()?

lazy_static! {
static ref BLAME_LINE_REGEX: Regex = Regex::new(
r"(?x)
^
(
[0-9a-f]{8} # commit hash
)
[\ ]
\( # open (
(
[^\ ].*[^\ ] # author name
)
[\ ]+
( # timestamp
[0-9]{4}-[0-9]{2}-[0-9]{2}\ [0-9]{2}:[0-9]{2}:[0-9]{2}\ [-+][0-9]{4}
)
[\ ]+
(
[0-9]+ # line number
)
\) # close )
(
.* # code, with leading space
)
$
"
)
.unwrap();
}

pub fn parse_git_blame_line<'a>(line: &'a str, timestamp_format: &str) -> Option<BlameLine<'a>> {
if let Some(caps) = BLAME_LINE_REGEX.captures(line) {
let commit = caps.get(1).unwrap().as_str();
let author = caps.get(2).unwrap().as_str();
let timestamp = caps.get(3).unwrap().as_str();
if let Ok(time) = DateTime::parse_from_str(timestamp, timestamp_format) {
let line_number_str = caps.get(4).unwrap().as_str();
if let Ok(line_number) = line_number_str.parse::<usize>() {
let code = caps.get(5).unwrap().as_str();
Some(BlameLine {
commit,
author,
time,
line_number,
code,
})
} else {
None
}
} else {
None
}
} else {
None
}
}

lazy_static! {
pub static ref BLAME_PLACEHOLDER_REGEX: Regex =
format::make_placeholder_regex(&["timestamp", "author", "commit"]);
}

pub fn format_blame_metadata(
format_data: &[format::FormatStringPlaceholderData],
blame: &BlameLine,
config: &config::Config,
) -> String {
let mut s = String::new();
let mut suffix = "";
for placeholder in format_data {
s.push_str(placeholder.prefix);

let alignment_spec = placeholder.alignment_spec.unwrap_or("<");
let width = placeholder.width.unwrap_or(15);

let pad = |s| format::pad(s, width, alignment_spec);
match placeholder.placeholder {
Some("timestamp") => s.push_str(&pad(
&chrono_humanize::HumanTime::from(blame.time).to_string()
)),
Some("author") => s.push_str(&pad(blame.author)),
Some("commit") => s.push_str(&pad(&delta::format_raw_line(blame.commit, config))),
None => {}
Some(_) => unreachable!(),
}
suffix = placeholder.suffix;
}
s.push_str(suffix);
s
}

#[test]
fn test_blame_line_regex() {
for line in &[
"ea82f2d0 (Dan Davison 2021-08-22 18:20:19 -0700 120) let mut handled_line = self.handle_commit_meta_header_line()?",
"b2257cfa (Dan Davison 2020-07-18 15:34:43 -0400 1) use std::borrow::Cow;"
] {
let caps = BLAME_LINE_REGEX.captures(line);
assert!(caps.is_some());
assert!(parse_git_blame_line(line, "%Y-%m-%d %H:%M:%S %z").is_some());
}
}
27 changes: 27 additions & 0 deletions src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,33 @@ 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} {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", default_value = "#FFFFFF #DDDDDD #BBBBBB")]
pub blame_palette: 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)
#[structopt(long = "default-language")]
pub default_language: Option<String>,

/// The regular expression used to decide what a word is for the within-line highlight
/// algorithm. For less fine-grained matching than the default try --word-diff-regex="\S+"
/// --max-line-distance=1.0 (this is more similar to `git --word-diff`).
Expand Down
15 changes: 14 additions & 1 deletion src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,15 @@ use crate::style::{self, Style};
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 commit_style: Style,
pub color_only: bool,
pub commit_regex: Regex,
pub cwd_relative_to_repo_root: Option<String>,
pub decorations_width: cli::Width,
pub default_language: Option<String>,
pub diff_stat_align_width: usize,
pub error_exit_code: i32,
pub file_added_label: String,
Expand Down Expand Up @@ -99,7 +103,8 @@ impl Config {
State::HunkPlus(_) => &self.plus_style,
State::CommitMeta => &self.commit_style,
State::FileMeta => &self.file_style,
State::HunkHeader => &self.hunk_header_style,
State::HunkHeader(_, _) => &self.hunk_header_style,
State::SubmoduleLog => &self.file_style,
_ => delta_unreachable("Unreachable code reached in get_style."),
}
}
Expand Down Expand Up @@ -200,11 +205,19 @@ 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: opt
.blame_palette
.split_whitespace()
.map(|s| s.to_owned())
.collect::<Vec<String>>(),
blame_timestamp_format: opt.blame_timestamp_format,
commit_style,
color_only: opt.color_only,
commit_regex,
cwd_relative_to_repo_root: std::env::var("GIT_PREFIX").ok(),
decorations_width: opt.computed.decorations_width,
default_language: opt.default_language,
diff_stat_align_width: opt.diff_stat_align_width,
error_exit_code: 2, // Use 2 for error because diff uses 0 and 1 for non-error.
file_added_label,
Expand Down

0 comments on commit 224282b

Please sign in to comment.