summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDan Davison <dandavison7@gmail.com>2021-11-29 20:54:48 -0500
committerDan Davison <dandavison7@gmail.com>2021-12-05 11:25:05 -0500
commit6745f42ddadeccfa30628c70d39b8f9abbff35f0 (patch)
tree19cdd504eba5632d6bb0d6a72559b1ee1774c057
parente7294060ef3b8af0d2307eea4359123232f85646 (diff)
Display merge conflicts
-rw-r--r--README.md3
-rw-r--r--etc/examples/822-hunk-header-within-merge-conflict.diff545
-rw-r--r--src/cli.rs44
-rw-r--r--src/config.rs11
-rw-r--r--src/delta.rs43
-rw-r--r--src/features/side_by_side.rs15
-rw-r--r--src/handlers/diff_header_diff.rs5
-rw-r--r--src/handlers/draw.rs2
-rw-r--r--src/handlers/hunk.rs81
-rw-r--r--src/handlers/hunk_header.rs27
-rw-r--r--src/handlers/merge_conflict.rs1340
-rw-r--r--src/handlers/mod.rs1
-rw-r--r--src/options/set.rs6
-rw-r--r--src/paint.rs63
-rw-r--r--src/parse_styles.rs24
-rw-r--r--src/subcommands/show_colors.rs6
-rw-r--r--src/tests/test_example_diffs.rs6
-rw-r--r--src/wrapping.rs5
18 files changed, 2134 insertions, 93 deletions
diff --git a/README.md b/README.md
index d7f3c8b6..6d420049 100644
--- a/README.md
+++ b/README.md
@@ -27,6 +27,9 @@
[delta]
navigate = true
+[merge]
+ conflictstyle = diff3
+
[diff]
colorMoved = default
diff --git a/etc/examples/822-hunk-header-within-merge-conflict.diff b/etc/examples/822-hunk-header-within-merge-conflict.diff
new file mode 100644
index 00000000..4709bc5a
--- /dev/null
+++ b/etc/examples/822-hunk-header-within-merge-conflict.diff
@@ -0,0 +1,545 @@
+diff --cc src/delta.rs
+index 9a65aaa,ab08d84..0000000
+--- a/src/delta.rs
++++ b/src/delta.rs
+@@@ -21,7 -21,7 +21,13 @@@ pub enum State
+ HunkZero(DiffType), // In hunk; unchanged line (prefix)
+ HunkMinus(DiffType, Option<String>), // In hunk; removed line (diff_type, raw_line)
+ HunkPlus(DiffType, Option<String>), // In hunk; added line (diff_type, raw_line)
+++<<<<<<< HEAD
+ + MergeConflict(MergeParents, merge_conflict::MergeConflictCommit),
+++||||||| parent of b2b28c8... Display merge conflict branches
+++ MergeConflict(merge_conflict::Source),
+++=======
++ MergeConflict(merge_conflict::MergeConflictCommit),
+++>>>>>>> b2b28c8... Display merge conflict branches
+ 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).
+diff --cc src/handlers/hunk.rs
+index 26cb288,7df74ae..0000000
+--- a/src/handlers/hunk.rs
++++ b/src/handlers/hunk.rs
+@@@ -141,28 -141,19 +141,45 @@@ fn new_line_state(new_line: &str, prev_
+ | HunkZero(Unified)
+ | HunkPlus(Unified, _)
+ | HunkHeader(Unified, _, _) => Unified,
+++<<<<<<< HEAD
+ + HunkHeader(Combined(Number(n), InMergeConflict::No), _, _) => {
+ + Combined(Number(*n), InMergeConflict::No)
+ + }
+ + // The prefixes are specific to the previous line, but the number of merge parents remains
+ + // equal to the prefix length.
+ + HunkHeader(Combined(Prefix(prefix), InMergeConflict::No), _, _) => {
+ + Combined(Number(prefix.len()), InMergeConflict::No)
+ + }
+ + HunkMinus(Combined(Prefix(prefix), in_merge_conflict), _)
+ + | HunkZero(Combined(Prefix(prefix), in_merge_conflict))
+ + | HunkPlus(Combined(Prefix(prefix), in_merge_conflict), _) => {
+ + Combined(Number(prefix.len()), in_merge_conflict.clone())
+ + }
+ + _ => delta_unreachable(&format!(
+ + "Unexpected state in new_line_state: {:?}",
+ + prev_state
+ + )),
+++||||||| parent of b2b28c8... Display merge conflict branches
+++ HunkHeader(Combined(Number(n)), _, _) => Combined(Number(*n)),
+++ HunkMinus(Combined(Prefix(prefix)), _)
+++ | HunkZero(Combined(Prefix(prefix)))
+++ | HunkPlus(Combined(Prefix(prefix)), _) => Combined(Number(prefix.len())),
+++ _ => delta_unreachable(&format!("diff_type: unexpected state: {:?}", prev_state)),
+++=======
++ HunkHeader(Combined(Number(n)), _, _) => Combined(Number(*n)),
++ // The prefixes are specific to the previous line, but the number of merge parents remains
++ // equal to the prefix length.
++ HunkHeader(Combined(Prefix(prefix)), _, _)
++ | HunkMinus(Combined(Prefix(prefix)), _)
++ | HunkZero(Combined(Prefix(prefix)))
++ | HunkPlus(Combined(Prefix(prefix)), _) => Combined(Number(prefix.len())),
++ _ => delta_unreachable(&format!("diff_type: unexpected state: {:?}", prev_state)),
+++>>>>>>> b2b28c8... Display merge conflict branches
+ };
+
+ - let (prefix_char, prefix) = match diff_type {
+ - Unified => (new_line.chars().next(), None),
+ - Combined(Number(n_parents)) => {
+ + let (prefix_char, prefix, in_merge_conflict) = match diff_type {
+ + Unified => (new_line.chars().next(), None, None),
+ + Combined(Number(n_parents), in_merge_conflict) => {
+ let prefix = &new_line[..min(n_parents, new_line.len())];
+ let prefix_char = match prefix.chars().find(|c| c == &'-' || c == &'+') {
+ Some(c) => Some(c),
+diff --cc src/handlers/merge_conflict.rs
+index a956f2e,3a7e7b9..0000000
+--- a/src/handlers/merge_conflict.rs
++++ b/src/handlers/merge_conflict.rs
+@@@ -1,9 -1,10 +1,18 @@@
+ -use std::cmp::min;
+ use std::ops::{Index, IndexMut};
+
+++<<<<<<< HEAD
+ use super::draw;
+ use crate::cli;
+ use crate::config::{self, delta_unreachable};
+ +use crate::delta::{DiffType, InMergeConflict, MergeParents, State, StateMachine};
+++||||||| parent of b2b28c8... Display merge conflict branches
++ use crate::delta::{DiffType, MergeParents, State, StateMachine};
+++=======
+++use super::draw;
+++use crate::cli;
+++use crate::config::{self, delta_unreachable};
+++use crate::delta::{DiffType, MergeParents, State, StateMachine};
+++>>>>>>> b2b28c8... Display merge conflict branches
+ use crate::minusplus::MinusPlus;
+ use crate::paint;
+ use crate::style::DecorationStyle;
+@@@ -28,7 -29,8 +37,15 @@@ pub type MergeConflictCommitNames = Mer
+ impl<'a> StateMachine<'a> {
+ pub fn handle_merge_conflict_line(&mut self) -> std::io::Result<bool> {
+ use DiffType::*;
+++<<<<<<< HEAD
+ + use MergeConflictCommit::*;
+++||||||| parent of b2b28c8... Display merge conflict branches
+++ use MergeParents::*;
+++ use Source::*;
+++=======
++ use MergeConflictCommit::*;
++ use MergeParents::*;
+++>>>>>>> b2b28c8... Display merge conflict branches
+ use State::*;
+
+ let mut handled_line = false;
+@@@ -36,36 -38,28 +53,113 @@@
+ return Ok(handled_line);
+ }
+
+++<<<<<<< HEAD
+ + match self.state.clone() {
+ + HunkHeader(Combined(merge_parents, InMergeConflict::No), _, _)
+ + | HunkMinus(Combined(merge_parents, InMergeConflict::No), _)
+ + | HunkZero(Combined(merge_parents, InMergeConflict::No))
+ + | HunkPlus(Combined(merge_parents, InMergeConflict::No), _) => {
+ + handled_line = self.enter_merge_conflict(&merge_parents)
+ + }
+ + MergeConflict(merge_parents, Ours) => {
+ + handled_line = self.enter_ancestral(&merge_parents)
+ + || self.enter_theirs(&merge_parents)
+ + || self.exit_merge_conflict(&merge_parents)?
+ + || self.store_line(
+ + Ours,
+ + HunkPlus(Combined(merge_parents, InMergeConflict::Yes), None),
+ + );
+++||||||| parent of b2b28c8... Display merge conflict branches
+++ // TODO: don't allocate on heap at this point
+++ let prefix = self.line[..min(self.line.len(), 2)].to_string();
+++ let diff_type = Combined(Prefix(prefix));
+++
+++ match self.state {
+++ // The only transition into a merge conflict is HunkZero => MergeConflict(Ours)
+++ // TODO: shouldn't this be HunkZero(Some(_))?
+++ HunkZero(_) => {
+++ if self.line.starts_with("++<<<<<<<") {
+++ self.state = MergeConflict(Ours);
+++ handled_line = true
+++ }
+ + }
+++ MergeConflict(Ours) => {
+++ if self.line.starts_with("++|||||||") {
+++ self.state = MergeConflict(Ancestral);
+++ } else if self.line.starts_with("++=======") {
+++ self.state = MergeConflict(Theirs);
+++ } else if self.line.starts_with("++>>>>>>>") {
+++ self.paint_buffered_merge_conflict_lines(diff_type)?;
+++ } else {
+++ let line = self.painter.prepare(&self.line, diff_type.n_parents());
+++ self.painter.merge_conflict_lines[Ours].push((line, HunkPlus(diff_type, None)));
+++ }
+++ handled_line = true
+++=======
++ // TODO: don't allocate on heap at this point
++ let prefix = self.line[..min(self.line.len(), 2)].to_string();
++ let diff_type = Combined(Prefix(prefix));
++
++ match self.state {
++ // The only transition into a merge conflict is HunkZero => MergeConflict(Ours)
++ // TODO: shouldn't this be HunkZero(Some(_))?
++ HunkZero(_) => handled_line = self.enter_merge_conflict(),
++ MergeConflict(Ours) => {
++ handled_line = self.enter_ancestral()
++ || self.enter_theirs()
++ || self.exit_merge_conflict(diff_type.clone())?
++ || self.store_line(Ours, HunkPlus(diff_type, None));
+++>>>>>>> b2b28c8... Display merge conflict branches
++ }
+++<<<<<<< HEAD
+ + MergeConflict(merge_parents, Ancestral) => {
+ + handled_line = self.enter_theirs(&merge_parents)
+ + || self.exit_merge_conflict(&merge_parents)?
+ + || self.store_line(
+ + Ancestral,
+ + HunkMinus(Combined(merge_parents, InMergeConflict::Yes), None),
+ + );
+++||||||| parent of b2b28c8... Display merge conflict branches
+++ MergeConflict(Ancestral) => {
+++ if self.line.starts_with("++=======") {
+++ self.state = MergeConflict(Theirs);
+++ } else if self.line.starts_with("++>>>>>>>") {
+++ self.paint_buffered_merge_conflict_lines(diff_type)?;
+++ } else {
+++ let line = self.painter.prepare(&self.line, diff_type.n_parents());
+++ self.painter.merge_conflict_lines[Ancestral]
+++ .push((line, HunkMinus(diff_type, None)));
+++ }
+++ handled_line = true
+++=======
++ MergeConflict(Ancestral) => {
++ handled_line = self.enter_theirs()
++ || self.exit_merge_conflict(diff_type.clone())?
++ || self.store_line(Ancestral, HunkMinus(diff_type, None));
+++>>>>>>> b2b28c8... Display merge conflict branches
+ }
+++<<<<<<< HEAD
+ + MergeConflict(merge_parents, Theirs) => {
+ + handled_line = self.exit_merge_conflict(&merge_parents)?
+ + || self.store_line(
+ + Theirs,
+ + HunkPlus(Combined(merge_parents, InMergeConflict::Yes), None),
+ + );
+++||||||| parent of b2b28c8... Display merge conflict branches
+++ MergeConflict(Theirs) => {
+++ if self.line.starts_with("++>>>>>>>") {
+++ self.paint_buffered_merge_conflict_lines(diff_type)?;
+++ } else {
+++ let line = self.painter.prepare(&self.line, diff_type.n_parents());
+++ self.painter.merge_conflict_lines[Theirs]
+++ .push((line, HunkPlus(diff_type, None)));
+++ }
+++ handled_line = true
+++=======
++ MergeConflict(Theirs) => {
++ handled_line = self.exit_merge_conflict(diff_type.clone())?
++ || self.store_line(Theirs, HunkPlus(diff_type, None));
+++>>>>>>> b2b28c8... Display merge conflict branches
+ }
+ _ => {}
+ }
+@@@ -73,65 -67,60 +167,124 @@@
+ Ok(handled_line)
+ }
+
+++<<<<<<< HEAD
+ + fn enter_merge_conflict(&mut self, merge_parents: &MergeParents) -> bool {
+ + use State::*;
+ + if let Some(commit) = parse_merge_marker(&self.line, "++<<<<<<<") {
+ + self.state = MergeConflict(merge_parents.clone(), Ours);
+ + self.painter.merge_conflict_commit_names[Ours] = Some(commit.to_string());
+ + true
+ + } else {
+ + false
+ + }
+ + }
+ +
+ + fn enter_ancestral(&mut self, merge_parents: &MergeParents) -> bool {
+ + use State::*;
+ + if let Some(commit) = parse_merge_marker(&self.line, "++|||||||") {
+ + self.state = MergeConflict(merge_parents.clone(), Ancestral);
+ + self.painter.merge_conflict_commit_names[Ancestral] = Some(commit.to_string());
+ + true
+ + } else {
+ + false
+ + }
+ + }
+ +
+ + fn enter_theirs(&mut self, merge_parents: &MergeParents) -> bool {
+ + use State::*;
+ + if self.line.starts_with("++=======") {
+ + self.state = MergeConflict(merge_parents.clone(), Theirs);
+ + true
+ + } else {
+ + false
+ + }
+ + }
+ +
+ + fn exit_merge_conflict(&mut self, merge_parents: &MergeParents) -> std::io::Result<bool> {
+ + if let Some(commit) = parse_merge_marker(&self.line, "++>>>>>>>") {
+ + self.painter.merge_conflict_commit_names[Theirs] = Some(commit.to_string());
+ + self.paint_buffered_merge_conflict_lines(merge_parents)?;
+ + Ok(true)
+ + } else {
+ + Ok(false)
+ + }
+ + }
+ +
+ + fn store_line(&mut self, commit: MergeConflictCommit, state: State) -> bool {
+ + use State::*;
+ + if let HunkMinus(diff_type, _) | HunkZero(diff_type) | HunkPlus(diff_type, _) = &state {
+ + let line = self.painter.prepare(&self.line, diff_type.n_parents());
+ + self.painter.merge_conflict_lines[commit].push((line, state));
+ + true
+ + } else {
+ + delta_unreachable(&format!("Invalid state: {:?}", state))
+ + }
+ + }
+ +
+ + fn paint_buffered_merge_conflict_lines(
+ + &mut self,
+ + merge_parents: &MergeParents,
+ + ) -> std::io::Result<()> {
+ + use DiffType::*;
+ + use State::*;
+++||||||| parent of b2b28c8... Display merge conflict branches
+++ fn paint_buffered_merge_conflict_lines(&mut self, diff_type: DiffType) -> std::io::Result<()> {
+++=======
++ fn enter_merge_conflict(&mut self) -> bool {
++ use State::*;
++ if let Some(commit) = parse_merge_marker(&self.line, "++<<<<<<<") {
++ self.state = MergeConflict(Ours);
++ self.painter.merge_conflict_commit_names[Ours] = Some(commit.to_string());
++ true
++ } else {
++ false
++ }
++ }
++
++ fn enter_ancestral(&mut self) -> bool {
++ use State::*;
++ if let Some(commit) = parse_merge_marker(&self.line, "++|||||||") {
++ self.state = MergeConflict(Ancestral);
++ self.painter.merge_conflict_commit_names[Ancestral] = Some(commit.to_string());
++ true
++ } else {
++ false
++ }
++ }
++
++ fn enter_theirs(&mut self) -> bool {
++ use State::*;
++ if self.line.starts_with("++=======") {
++ self.state = MergeConflict(Theirs);
++ true
++ } else {
++ false
++ }
++ }
++
++ fn exit_merge_conflict(&mut self, diff_type: DiffType) -> std::io::Result<bool> {
++ if let Some(commit) = parse_merge_marker(&self.line, "++>>>>>>>") {
++ self.painter.merge_conflict_commit_names[Theirs] = Some(commit.to_string());
++ self.paint_buffered_merge_conflict_lines(diff_type)?;
++ Ok(true)
++ } else {
++ Ok(false)
++ }
++ }
++
++ fn store_line(&mut self, commit: MergeConflictCommit, state: State) -> bool {
++ use State::*;
++ if let HunkMinus(diff_type, _) | HunkZero(diff_type) | HunkPlus(diff_type, _) = &state {
++ let line = self.painter.prepare(&self.line, diff_type.n_parents());
++ self.painter.merge_conflict_lines[commit].push((line, state));
++ true
++ } else {
++ delta_unreachable(&format!("Invalid state: {:?}", state))
++ }
++ }
++
++ fn paint_buffered_merge_conflict_lines(&mut self, diff_type: DiffType) -> std::io::Result<()> {
+++>>>>>>> b2b28c8... Display merge conflict branches
+ self.painter.emit()?;
+
+ write_merge_conflict_bar("▼", &mut self.painter, self.config)?;
+@@@ -163,6 -152,6 +316,7 @@@
+ }
+ }
+
+++<<<<<<< HEAD
+ fn write_subhunk_header(
+ derived_commit_type: &MergeConflictCommit,
+ decoration_style: &str,
+@@@ -197,63 -186,63 +351,170 @@@
+ Ok(())
+ }
+
++ #[allow(unused)]
++ fn write_merge_conflict_line(
++ painter: &mut paint::Painter,
++ config: &config::Config,
++ ) -> std::io::Result<()> {
++ let (mut draw_fn, _pad, decoration_ansi_term_style) = draw::get_draw_function(
++ DecorationStyle::from_str("bold ol", config.true_color, config.git_config.as_ref()),
++ );
++ draw_fn(
++ painter.writer,
++ "",
++ "",
++ &config.decorations_width,
++ config.hunk_header_style,
++ decoration_ansi_term_style,
++ )?;
++ Ok(())
++ }
+++||||||| parent of b2b28c8... Display merge conflict branches
+++pub use Source::*;
+++=======
+++fn write_subhunk_header(
+++ derived_commit_type: &MergeConflictCommit,
+++ decoration_style: &str,
+++ painter: &mut paint::Painter,
+++ config: &config::Config,
+++) -> std::io::Result<()> {
+++ let (mut draw_fn, pad, decoration_ansi_term_style) =
+++ draw::get_draw_function(DecorationStyle::from_str(
+++ decoration_style,
+++ config.true_color,
+++ config.git_config.as_ref(),
+++ ));
+++ let derived_commit_name = &painter.merge_conflict_commit_names[derived_commit_type];
+++ let text = if let Some(_ancestral_commit) = &painter.merge_conflict_commit_names[Ancestral] {
+++ format!(
+++ "ancestor {} {}{}",
+++ config.right_arrow,
+++ derived_commit_name.as_deref().unwrap_or("?"),
+++ if pad { " " } else { "" }
+++ )
+++ } else {
+++ derived_commit_name.as_deref().unwrap_or("?").to_string()
+++ };
+++ draw_fn(
+++ painter.writer,
+++ &text,
+++ &text,
+++ &config.decorations_width,
+++ config.hunk_header_style,
+++ decoration_ansi_term_style,
+++ )?;
+++ Ok(())
+++}
+++>>>>>>> b2b28c8... Display merge conflict branches
++
+++<<<<<<< HEAD
++ fn write_merge_conflict_bar(
++ s: &str,
++ painter: &mut paint::Painter,
++ config: &config::Config,
++ ) -> std::io::Result<()> {
++ if let cli::Width::Fixed(width) = config.decorations_width {
++ writeln!(painter.writer, "{}", s.repeat(width))?;
++ }
++ Ok(())
++ }
++
++ fn parse_merge_marker<'a>(line: &'a str, marker: &str) -> Option<&'a str> {
++ match line.strip_prefix(marker) {
++ Some(suffix) => {
++ let suffix = suffix.trim();
++ if !suffix.is_empty() {
++ Some(suffix)
++ } else {
++ None
++ }
++ }
++ None => None,
++ }
++ }
++
++ pub use MergeConflictCommit::*;
++
++ impl<T> Index<MergeConflictCommit> for MergeConflictCommits<T> {
++ type Output = T;
++ fn index(&self, commit: MergeConflictCommit) -> &Self::Output {
++ match commit {
+++||||||| parent of b2b28c8... Display merge conflict branches
+++impl Index<Source> for MergeConflictLines {
+++ type Output = Vec<(String, State)>;
+++ fn index(&self, source: Source) -> &Self::Output {
+++ match source {
+++=======
+ +#[allow(unused)]
+ +fn write_merge_conflict_line(
+ + painter: &mut paint::Painter,
+ + config: &config::Config,
+ +) -> std::io::Result<()> {
+ + let (mut draw_fn, _pad, decoration_ansi_term_style) = draw::get_draw_function(
+ + DecorationStyle::from_str("bold ol", config.true_color, config.git_config.as_ref()),
+ + );
+ + draw_fn(
+ + painter.writer,
+ + "",
+ + "",
+ + &config.decorations_width,
+ + config.hunk_header_style,
+ + decoration_ansi_term_style,
+ + )?;
+ + Ok(())
+ +}
+ +
+ +fn write_merge_conflict_bar(
+ + s: &str,
+ + painter: &mut paint::Painter,
+ + config: &config::Config,
+ +) -> std::io::Result<()> {
+ + if let cli::Width::Fixed(width) = config.decorations_width {
+ + writeln!(painter.writer, "{}", s.repeat(width))?;
+ + }
+ + Ok(())
+ +}
+ +
+ +fn parse_merge_marker<'a>(line: &'a str, marker: &str) -> Option<&'a str> {
+ + match line.strip_prefix(marker) {
+ + Some(suffix) => {
+ + let suffix = suffix.trim();
+ + if !suffix.is_empty() {
+ + Some(suffix)
+ + } else {
+ + None
+ + }
+ + }
+ + None => None,
+ + }
+ +}
+ +
+ +pub use MergeConflictCommit::*;
+ +
+ +impl<T> Index<MergeConflictCommit> for MergeConflictCommits<T> {
+ + type Output = T;
+ + fn index(&self, commit: MergeConflictCommit) -> &Self::Output {
+ + match commit {
+ + Ours => &self.ours,
+ + Ancestral => &self.ancestral,
+ + Theirs => &self.theirs,
+ + }
+ + }
+ +}
+ +
+++impl<T> Index<&MergeConflictCommit> for MergeConflictCommits<T> {
+++ type Output = T;
+++ fn index(&self, commit: &MergeConflictCommit) -> &Self::Output {
+++ match commit {
+++>>>>>>> b2b28c8... Display merge conflict branches
++ Ours => &self.ours,
++ Ancestral => &self.ancestral,
++ Theirs => &self.theirs,
++ }
++ }
++ }
++
+++<<<<<<< HEAD
+ impl<T> Index<&MergeConflictCommit> for MergeConflictCommits<T> {
+ type Output = T;
+ fn index(&self, commit: &MergeConflictCommit) -> &Self::Output {
+@@@ -268,6 -257,6 +529,15 @@@
+ impl<T> IndexMut<MergeConflictCommit> for MergeConflictCommits<T> {
+ fn index_mut(&mut self, commit: MergeConflictCommit) -> &mut Self::Output {
+ match commit {
+++||||||| parent of b2b28c8... Display merge conflict branches
+++impl IndexMut<Source> for MergeConflictLines {
+++ fn index_mut(&mut self, source: Source) -> &mut Self::Output {
+++ match source {
+++=======
+++impl<T> IndexMut<MergeConflictCommit> for MergeConflictCommits<T> {
+++ fn index_mut(&mut self, commit: MergeConflictCommit) -> &mut Self::Output {
+++ match commit {
+++>>>>>>> b2b28c8... Display merge conflict branches
+ Ours => &mut self.ours,
+ Ancestral => &mut self.ancestral,
+ Theirs => &mut self.theirs,
diff --git a/src/cli.rs b/src/cli.rs
index ee0f8272..19b200ff 100644
--- a/src/cli.rs
+++ b/src/cli.rs
@@ -435,6 +435,50 @@ pub struct Opt {
/// (underline), 'ol' (overline), or the combination 'ul ol'.
pub hunk_header_decoration_style: String,
+ #[structopt(long = "merge-conflict-begin-symbol", default_value = "▼")]
+ /// A string that is repeated to form the line marking the beginning of a merge conflict region.
+ pub merge_conflict_begin_symbol: String,
+
+ #[structopt(long = "merge-conflict-end-symbol", default_value = "▲")]
+ /// A string that is repeated to form the line marking the end of a merge conflict region.
+ pub merge_conflict_end_symbol: String,
+
+ #[structopt(
+ long = "merge-conflict-ours-diff-header-style",
+ default_value = "normal"
+ )]
+ /// Style (foreground, background, attributes) for the header above the diff between the
+ /// ancestral commit and 'our' branch. See STYLES section.
+ pub merge_conflict_ours_diff_header_style: String,
+
+ #[structopt(
+ long = "merge-conflict-ours-diff-header-decoration-style",
+ default_value = "box"
+ )]
+ /// Style (foreground, background, attributes) for the decoration of the header above the diff
+ /// between the ancestral commit and 'our' branch. See STYLES section. The style string should
+ /// contain one of the special attributes 'box', 'ul' (underline), 'ol' (overline), or the
+ /// combination 'ul ol'.
+ pub merge_conflict_ours_diff_header_decoration_style: String,
+
+ #[structopt(
+ long = "merge-conflict-theirs-diff-header-style",
+ default_value = "normal"
+ )]
+ /// Style (foreground, background, attributes) for the header above the diff between the
+ /// ancestral commit and 'their' branch. See STYLES section.
+ pub merge_conflict_theirs_diff_header_style: String,
+
+ #[structopt(
+ long = "merge-conflict-theirs-diff-header-decoration-style",
+ default_value = "box"
+ )]
+ /// Style (foreground, background, attributes) for the decoration of the header above the diff
+ /// between the ancestral commit and 'their' branch. See STYLES section. The style string should
+ /// contain one of the special attributes 'box', 'ul' (underline), 'ol' (overline), or the
+ /// combination 'ul ol'.
+ pub merge_conflict_theirs_diff_header_decoration_style: String,
+
#[structopt(long = "map-styles")]
/// A string specifying a mapping styles encountered in raw input to desired
/// output styles. An example is
diff --git a/src/config.rs b/src/config.rs
index 5858afe5..b9f14643 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -87,6 +87,7 @@ pub struct Config {
pub grep_match_line_style: Style,
pub grep_match_word_style: Style,
pub grep_separator_symbol: String,
+ pub handle_merge_conflicts: bool,
pub hunk_header_file_style: Style,
pub hunk_header_line_number_style: Style,
pub hunk_header_style_include_file_path: bool,
@@ -110,6 +111,10 @@ pub struct Config {
pub max_line_distance_for_naively_paired_lines: f64,
pub max_line_distance: f64,
pub max_line_length: usize,
+ pub merge_conflict_begin_symbol: String,
+ pub merge_conflict_ours_diff_header_style: Style,
+ pub merge_conflict_theirs_diff_header_style: Style,
+ pub merge_conflict_end_symbol: String,
pub minus_emph_style: Style,
pub minus_empty_line_marker_style: Style,
pub minus_file: Option<PathBuf>,
@@ -260,6 +265,7 @@ impl From<cli::Opt> for Config {
grep_match_line_style: styles["grep-match-line-style"],
grep_match_word_style: styles["grep-match-word-style"],
grep_separator_symbol: opt.grep_separator_symbol,
+ handle_merge_conflicts: !opt.raw,
hunk_header_file_style: styles["hunk-header-file-style"],
hunk_header_line_number_style: styles["hunk-header-line-number-style"],
hunk_header_style: styles["hunk-header-style"],
@@ -320,6 +326,11 @@ impl From<cli::Opt> for Config {
)
}
},
+ merge_conflict_begin_symbol: opt.merge_conflict_begin_symbol,
+ merge_conflict_ours_diff_header_style: styles["merge-conflict-ours-diff-header-style"],
+ merge_conflict_theirs_diff_header_style: styles
+ ["merge-conflict-theirs-diff-header-style"],
+ merge_conflict_end_symbol: opt.merge_conflict_end_symbol,
minus_emph_style: styles["minus-emph-style"],
minus_empty_line_marker_style: styles["minus-empty-line-marker-style"],
minus_file: opt.minus_file,
diff --git a/src/delta.rs b/src/delta.rs
index 3f1a79d4..24877dfa 100644
--- a/src/delta.rs
+++ b/src/delta.rs
@@ -6,25 +6,27 @@ use std::io::Write;
use bytelines::ByteLines;
use crate::ansi;
+use crate::config::delta_unreachable;
use crate::config::Config;
use crate::features;
-use crate::handlers;
+use crate::handlers::{self, merge_conflict};
use crate::paint::Painter;
use crate::style::DecorationStyle;
#[derive(Clone, Debug, PartialEq)]
pub enum State {
- CommitMeta, // In commit metadata section
+ CommitMeta, // In commit metadata section
DiffHeader(DiffType), // In diff metadata section, between (possible) commit metadata and first hunk
- HunkHeader(DiffType