Skip to content

Commit 6745f42

Browse files
committed
Display merge conflicts
1 parent e729406 commit 6745f42

File tree

18 files changed

+2134
-93
lines changed

18 files changed

+2134
-93
lines changed

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@
2727
[delta]
2828
navigate = true
2929

30+
[merge]
31+
conflictstyle = diff3
32+
3033
[diff]
3134
colorMoved = default
3235

etc/examples/822-hunk-header-within-merge-conflict.diff

Lines changed: 545 additions & 0 deletions
Large diffs are not rendered by default.

src/cli.rs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -435,6 +435,50 @@ pub struct Opt {
435435
/// (underline), 'ol' (overline), or the combination 'ul ol'.
436436
pub hunk_header_decoration_style: String,
437437

438+
#[structopt(long = "merge-conflict-begin-symbol", default_value = "▼")]
439+
/// A string that is repeated to form the line marking the beginning of a merge conflict region.
440+
pub merge_conflict_begin_symbol: String,
441+
442+
#[structopt(long = "merge-conflict-end-symbol", default_value = "▲")]
443+
/// A string that is repeated to form the line marking the end of a merge conflict region.
444+
pub merge_conflict_end_symbol: String,
445+
446+
#[structopt(
447+
long = "merge-conflict-ours-diff-header-style",
448+
default_value = "normal"
449+
)]
450+
/// Style (foreground, background, attributes) for the header above the diff between the
451+
/// ancestral commit and 'our' branch. See STYLES section.
452+
pub merge_conflict_ours_diff_header_style: String,
453+
454+
#[structopt(
455+
long = "merge-conflict-ours-diff-header-decoration-style",
456+
default_value = "box"
457+
)]
458+
/// Style (foreground, background, attributes) for the decoration of the header above the diff
459+
/// between the ancestral commit and 'our' branch. See STYLES section. The style string should
460+
/// contain one of the special attributes 'box', 'ul' (underline), 'ol' (overline), or the
461+
/// combination 'ul ol'.
462+
pub merge_conflict_ours_diff_header_decoration_style: String,
463+
464+
#[structopt(
465+
long = "merge-conflict-theirs-diff-header-style",
466+
default_value = "normal"
467+
)]
468+
/// Style (foreground, background, attributes) for the header above the diff between the
469+
/// ancestral commit and 'their' branch. See STYLES section.
470+
pub merge_conflict_theirs_diff_header_style: String,
471+
472+
#[structopt(
473+
long = "merge-conflict-theirs-diff-header-decoration-style",
474+
default_value = "box"
475+
)]
476+
/// Style (foreground, background, attributes) for the decoration of the header above the diff
477+
/// between the ancestral commit and 'their' branch. See STYLES section. The style string should
478+
/// contain one of the special attributes 'box', 'ul' (underline), 'ol' (overline), or the
479+
/// combination 'ul ol'.
480+
pub merge_conflict_theirs_diff_header_decoration_style: String,
481+
438482
#[structopt(long = "map-styles")]
439483
/// A string specifying a mapping styles encountered in raw input to desired
440484
/// output styles. An example is

src/config.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ pub struct Config {
8787
pub grep_match_line_style: Style,
8888
pub grep_match_word_style: Style,
8989
pub grep_separator_symbol: String,
90+
pub handle_merge_conflicts: bool,
9091
pub hunk_header_file_style: Style,
9192
pub hunk_header_line_number_style: Style,
9293
pub hunk_header_style_include_file_path: bool,
@@ -110,6 +111,10 @@ pub struct Config {
110111
pub max_line_distance_for_naively_paired_lines: f64,
111112
pub max_line_distance: f64,
112113
pub max_line_length: usize,
114+
pub merge_conflict_begin_symbol: String,
115+
pub merge_conflict_ours_diff_header_style: Style,
116+
pub merge_conflict_theirs_diff_header_style: Style,
117+
pub merge_conflict_end_symbol: String,
113118
pub minus_emph_style: Style,
114119
pub minus_empty_line_marker_style: Style,
115120
pub minus_file: Option<PathBuf>,
@@ -260,6 +265,7 @@ impl From<cli::Opt> for Config {
260265
grep_match_line_style: styles["grep-match-line-style"],
261266
grep_match_word_style: styles["grep-match-word-style"],
262267
grep_separator_symbol: opt.grep_separator_symbol,
268+
handle_merge_conflicts: !opt.raw,
263269
hunk_header_file_style: styles["hunk-header-file-style"],
264270
hunk_header_line_number_style: styles["hunk-header-line-number-style"],
265271
hunk_header_style: styles["hunk-header-style"],
@@ -320,6 +326,11 @@ impl From<cli::Opt> for Config {
320326
)
321327
}
322328
},
329+
merge_conflict_begin_symbol: opt.merge_conflict_begin_symbol,
330+
merge_conflict_ours_diff_header_style: styles["merge-conflict-ours-diff-header-style"],
331+
merge_conflict_theirs_diff_header_style: styles
332+
["merge-conflict-theirs-diff-header-style"],
333+
merge_conflict_end_symbol: opt.merge_conflict_end_symbol,
323334
minus_emph_style: styles["minus-emph-style"],
324335
minus_empty_line_marker_style: styles["minus-empty-line-marker-style"],
325336
minus_file: opt.minus_file,

src/delta.rs

Lines changed: 33 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,25 +6,27 @@ use std::io::Write;
66
use bytelines::ByteLines;
77

88
use crate::ansi;
9+
use crate::config::delta_unreachable;
910
use crate::config::Config;
1011
use crate::features;
11-
use crate::handlers;
12+
use crate::handlers::{self, merge_conflict};
1213
use crate::paint::Painter;
1314
use crate::style::DecorationStyle;
1415

1516
#[derive(Clone, Debug, PartialEq)]
1617
pub enum State {
17-
CommitMeta, // In commit metadata section
18+
CommitMeta, // In commit metadata section
1819
DiffHeader(DiffType), // In diff metadata section, between (possible) commit metadata and first hunk
19-
HunkHeader(DiffType, String, String), // In hunk metadata line (line, raw_line)
20-
HunkZero(Option<String>), // In hunk; unchanged line (prefix)
21-
HunkMinus(Option<String>, Option<String>), // In hunk; removed line (prefix, raw_line)
22-
HunkPlus(Option<String>, Option<String>), // In hunk; added line (prefix, raw_line)
23-
SubmoduleLog, // In a submodule section, with gitconfig diff.submodule = log
20+
HunkHeader(DiffType, String, String), // In hunk metadata line (diff_type, line, raw_line)
21+
HunkZero(DiffType), // In hunk; unchanged line (prefix)
22+
HunkMinus(DiffType, Option<String>), // In hunk; removed line (diff_type, raw_line)
23+
HunkPlus(DiffType, Option<String>), // In hunk; added line (diff_type, raw_line)
24+
MergeConflict(MergeParents, merge_conflict::MergeConflictCommit),
25+
SubmoduleLog, // In a submodule section, with gitconfig diff.submodule = log
2426
SubmoduleShort(String), // In a submodule section, with gitconfig diff.submodule = short
2527
Blame(String, Option<String>), // In a line of `git blame` output (commit, repeat_blame_line).
26-
GitShowFile, // In a line of `git show $revision:./path/to/file.ext` output
27-
Grep, // In a line of `git grep` output
28+
GitShowFile, // In a line of `git show $revision:./path/to/file.ext` output
29+
Grep, // In a line of `git grep` output
2830
Unknown,
2931
// The following elements are created when a line is wrapped to display it:
3032
HunkZeroWrapped, // Wrapped unchanged line
@@ -35,7 +37,27 @@ pub enum State {
3537
#[derive(Clone, Debug, PartialEq)]
3638
pub enum DiffType {
3739
Unified,
38-
Combined(usize), // number of parent commits: https://git-scm.com/docs/git-diff#_combined_diff_format
40+
Combined(MergeParents), // https://git-scm.com/docs/git-diff#_combined_diff_format
41+
}
42+
43+
#[derive(Clone, Debug, PartialEq)]
44+
pub enum MergeParents {
45+
Number(usize), // Number of parent commits == (number of @s in hunk header) - 1
46+
Prefix(String), // Hunk line prefix, length == number of parent commits
47+
Unknown,
48+
}
49+
50+
impl DiffType {
51+
pub fn n_parents(&self) -> usize {
52+
use DiffType::*;
53+
use MergeParents::*;
54+
match self {
55+
Combined(Prefix(prefix)) => prefix.len(),
56+
Combined(Number(n_parents)) => *n_parents,
57+
Unified => 1,
58+
Combined(Unknown) => delta_unreachable("Number of merge parents must be known."),
59+
}
60+
}
3961
}
4062

4163
#[derive(Debug, PartialEq)]
@@ -131,6 +153,7 @@ impl<'a> StateMachine<'a> {
131153
|| self.handle_diff_header_misc_line()?
132154
|| self.handle_submodule_log_line()?
133155
|| self.handle_submodule_short_line()?
156+
|| self.handle_merge_conflict_line()?
134157
|| self.handle_hunk_line()?
135158
|| self.handle_git_show_file_line()?
136159
|| self.handle_blame_line()?

src/features/side_by_side.rs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use unicode_segmentation::UnicodeSegmentation;
55
use crate::ansi;
66
use crate::cli;
77
use crate::config::{self, delta_unreachable, Config};
8+
use crate::delta::DiffType;
89
use crate::delta::State;
910
use crate::edits;
1011
use crate::features::{line_numbers, OptionValueFunction};
@@ -180,7 +181,7 @@ pub fn paint_minus_and_plus_lines_side_by_side(
180181
&lines_have_homolog[Left],
181182
match minus_line_index {
182183
Some(i) => &line_states[Left][i],
183-
None => &State::HunkMinus(None, None),
184+
None => &State::HunkMinus(DiffType::Unified, None),
184185
},
185186
&mut Some(line_numbers_data),
186187
bg_should_fill[Left],
@@ -201,7 +202,7 @@ pub fn paint_minus_and_plus_lines_side_by_side(
201202
&lines_have_homolog[Right],
202203
match plus_line_index {
203204
Some(i) => &line_states[Right][i],
204-
None => &State::HunkPlus(None, None),
205+
None => &State::HunkPlus(DiffType::Unified, None),
205206
},
206207
&mut Some(line_numbers_data),
207208
bg_should_fill[Right],
@@ -222,7 +223,7 @@ pub fn paint_zero_lines_side_by_side<'a>(
222223
painted_prefix: Option<ansi_term::ANSIString>,
223224
background_color_extends_to_terminal_width: BgShouldFill,
224225
) {
225-
let states = vec![State::HunkZero(None)];
226+
let states = vec![State::HunkZero(DiffType::Unified)];
226227

227228
let (states, syntax_style_sections, diff_style_sections) = wrap_zero_block(
228229
config,
@@ -418,8 +419,12 @@ fn paint_minus_or_plus_panel_line<'a>(
418419
)
419420
} else {
420421
let opposite_state = match state {
421-
State::HunkMinus(_, s) => State::HunkPlus(None, s.clone()),
422-
State::HunkPlus(_, s) => State::HunkMinus(None, s.clone()),
422+
State::HunkMinus(DiffType::Unified, s) => {
423+
State::HunkPlus(DiffType::Unified, s.clone())
424+
}
425+
State::HunkPlus(DiffType::Unified, s) => {
426+
State::HunkMinus(DiffType::Unified, s.clone())
427+
}
423428
_ => unreachable!(),
424429
};
425430
(

src/handlers/diff_header_diff.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::delta::{DiffType, State, StateMachine};
1+
use crate::delta::{DiffType, MergeParents, State, StateMachine};
22

33
impl<'a> StateMachine<'a> {
44
#[inline]
@@ -14,7 +14,8 @@ impl<'a> StateMachine<'a> {
1414
self.painter.paint_buffered_minus_and_plus_lines();
1515
self.state =
1616
if self.line.starts_with("diff --cc ") || self.line.starts_with("diff --combined ") {
17-
State::DiffHeader(DiffType::Combined(2)) // We will confirm the number of parents when we see the hunk header
17+
// We will determine the number of parents when we see the hunk header.
18+
State::DiffHeader(DiffType::Combined(MergeParents::Unknown))
1819
} else {
1920
State::DiffHeader(DiffType::Unified)
2021
};

src/handlers/draw.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ fn write_no_decoration(
5353

5454
/// Write text to stream, surrounded by a box, leaving the cursor just
5555
/// beyond the bottom right corner.
56-
fn write_boxed(
56+
pub fn write_boxed(
5757
writer: &mut dyn Write,
5858
text: &str,
5959
raw_text: &str,

0 commit comments

Comments
 (0)