diffy_imara/merge/
mod.rs

1use imara_diff::sources::{byte_lines_with_terminator, lines_with_terminator};
2
3use crate::{
4    diff::DiffOptions,
5    range::{DiffRange, Range, SliceLike},
6    utils::InternedMergeInput,
7    Algorithm,
8};
9use std::{cmp, fmt};
10
11#[cfg(test)]
12mod tests;
13
14const DEFAULT_CONFLICT_MARKER_LENGTH: usize = 7;
15
16enum Diff3Range<'ancestor, 'ours, 'theirs, T: ?Sized> {
17    Equal(Range<'ancestor, T>, Range<'ours, T>, Range<'theirs, T>),
18    Ancestor(Range<'ancestor, T>),
19    AncestorOurs(Range<'ancestor, T>, Range<'ours, T>),
20    AncestorTheirs(Range<'ancestor, T>, Range<'theirs, T>),
21    Ours(Range<'ours, T>),
22    Theirs(Range<'theirs, T>),
23}
24
25impl<T: ?Sized + fmt::Debug + SliceLike> fmt::Debug for Diff3Range<'_, '_, '_, T> {
26    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
27        match self {
28            Diff3Range::Equal(range, ..) => write!(f, "Equal: {:?}", range.as_slice()),
29            Diff3Range::Ancestor(range) => write!(f, "Ancestor: {:?}", range.as_slice()),
30            Diff3Range::AncestorOurs(range, ..) => {
31                write!(f, "AncestorOurs: {:?}", range.as_slice())
32            }
33            Diff3Range::AncestorTheirs(range, ..) => {
34                write!(f, "AncestorTheirs: {:?}", range.as_slice())
35            }
36            Diff3Range::Ours(range) => write!(f, "Ours: {:?}", range.as_slice()),
37            Diff3Range::Theirs(range) => write!(f, "Theirs: {:?}", range.as_slice()),
38        }
39    }
40}
41
42impl<T: ?Sized> Copy for Diff3Range<'_, '_, '_, T> {}
43
44impl<T: ?Sized> Clone for Diff3Range<'_, '_, '_, T> {
45    fn clone(&self) -> Self {
46        *self
47    }
48}
49
50enum MergeRange<'ancestor, 'ours, 'theirs, T: ?Sized> {
51    Equal(Range<'ancestor, T>, Range<'ours, T>, Range<'theirs, T>),
52    Conflict(Range<'ancestor, T>, Range<'ours, T>, Range<'theirs, T>),
53    Ours(Range<'ours, T>),
54    Theirs(Range<'theirs, T>),
55    Both(Range<'ours, T>, Range<'theirs, T>),
56}
57
58impl<T: ?Sized + fmt::Debug + SliceLike> fmt::Debug for MergeRange<'_, '_, '_, T> {
59    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
60        match self {
61            MergeRange::Equal(range, ..) => write!(f, "Equal: {:?}", range.as_slice()),
62            MergeRange::Conflict(ancestor, ours, theirs) => write!(
63                f,
64                "Conflict: ancestor: {:?} ours: {:?} theirs: {:?}",
65                ancestor.as_slice(),
66                ours.as_slice(),
67                theirs.as_slice()
68            ),
69            MergeRange::Ours(range) => write!(f, "Ours: {:?}", range.as_slice()),
70            MergeRange::Theirs(range) => write!(f, "Theirs: {:?}", range.as_slice()),
71            MergeRange::Both(ours, theirs) => write!(
72                f,
73                "Both: ours: {:?} theirs: {:?}",
74                ours.as_slice(),
75                theirs.as_slice()
76            ),
77        }
78    }
79}
80
81impl<T: ?Sized> Copy for MergeRange<'_, '_, '_, T> {}
82
83impl<T: ?Sized> Clone for MergeRange<'_, '_, '_, T> {
84    fn clone(&self) -> Self {
85        *self
86    }
87}
88
89/// Style used when rendering a conflict
90#[derive(Copy, Clone, Debug, Default)]
91pub enum ConflictStyle {
92    /// Renders conflicting lines from both files, separated by conflict markers.
93    ///
94    /// ```console
95    /// <<<<<<< A
96    /// lines in file A
97    /// =======
98    /// lines in file B
99    /// >>>>>>> B
100    /// ```
101    Merge,
102
103    /// Renders conflicting lines from both files including lines from the original files,
104    /// separated by conflict markers.
105    ///
106    /// ```console
107    /// <<<<<<< A
108    /// lines in file A
109    /// ||||||| Original
110    /// lines in Original file
111    /// =======
112    /// lines in file B
113    /// >>>>>>> B
114    /// ```
115    #[default]
116    Diff3,
117}
118
119/// A collection of options for modifying the way a merge is performed
120#[derive(Debug)]
121pub struct MergeOptions {
122    /// The length of the conflict markers used when displaying a merge conflict
123    conflict_marker_length: usize,
124    /// The conflict style used when displaying a merge conflict
125    style: ConflictStyle,
126    algorithm: Algorithm,
127}
128
129impl MergeOptions {
130    /// Constructs a new `MergeOptions` with default settings
131    ///
132    /// ## Defaults
133    /// * conflict_marker_length = 7
134    /// * style = ConflictStyle::Diff3
135    /// * algorithm = Algorithm::Histogram
136    pub fn new() -> Self {
137        Self {
138            conflict_marker_length: DEFAULT_CONFLICT_MARKER_LENGTH,
139            style: ConflictStyle::Diff3,
140            algorithm: Algorithm::Histogram,
141        }
142    }
143
144    /// Set the length of the conflict markers used when displaying a merge conflict
145    pub fn set_conflict_marker_length(&mut self, conflict_marker_length: usize) -> &mut Self {
146        self.conflict_marker_length = conflict_marker_length;
147        self
148    }
149
150    /// Set the conflict style used when displaying a merge conflict
151    pub fn set_conflict_style(&mut self, style: ConflictStyle) -> &mut Self {
152        self.style = style;
153        self
154    }
155
156    /// Set the algorithm used to perform the internal diffs ("ancestor" with "ours", "ancestor" with
157    /// "theirs").
158    pub fn set_algorithm(&mut self, algorithm: Algorithm) -> &mut Self {
159        self.algorithm = algorithm;
160        self
161    }
162
163    /// Merge two files, given a common ancestor, based on the configured options
164    /// The algorithm set in [`Self::algorithm`] is used for computing the diff between `ancestor`
165    /// and `ours`, and between `ancestor` and `theirs`
166    pub fn merge<'a>(
167        &self,
168        ancestor: &'a str,
169        ours: &'a str,
170        theirs: &'a str,
171    ) -> Result<String, String> {
172        let input = InternedMergeInput::new(ancestor, ours, theirs);
173        let ancestor_lines: Vec<_> = lines_with_terminator(ancestor).collect();
174        let our_lines: Vec<_> = lines_with_terminator(ours).collect();
175        let their_lines: Vec<_> = lines_with_terminator(theirs).collect();
176
177        let mut opts = DiffOptions::new();
178        opts.set_algorithm(self.algorithm);
179
180        let our_solution = opts.diff_tokens(&input.base, &input.left, input.interner.num_tokens());
181        let their_solution =
182            opts.diff_tokens(&input.base, &input.right, input.interner.num_tokens());
183
184        let merged = merge_solutions(&our_solution, &their_solution);
185        let mut merge = diff3_range_to_merge_range(&merged);
186
187        cleanup_conflicts(&mut merge);
188
189        output_result(
190            &ancestor_lines,
191            &our_lines,
192            &their_lines,
193            &merge,
194            self.conflict_marker_length,
195            self.style,
196        )
197    }
198
199    /// Perform a 3-way merge between potentially non-utf8 texts
200    pub fn merge_bytes<'a>(
201        &self,
202        ancestor: &'a [u8],
203        ours: &'a [u8],
204        theirs: &'a [u8],
205    ) -> Result<Vec<u8>, Vec<u8>> {
206        let input = InternedMergeInput::new(ancestor, ours, theirs);
207        let ancestor_lines: Vec<_> = byte_lines_with_terminator(ancestor).collect();
208        let our_lines: Vec<_> = byte_lines_with_terminator(ours).collect();
209        let their_lines: Vec<_> = byte_lines_with_terminator(theirs).collect();
210
211        let mut opts = DiffOptions::new();
212        opts.set_algorithm(self.algorithm);
213
214        let our_solution = opts.diff_tokens(&input.base, &input.left, input.interner.num_tokens());
215        let their_solution =
216            opts.diff_tokens(&input.base, &input.right, input.interner.num_tokens());
217
218        let merged = merge_solutions(&our_solution, &their_solution);
219        let mut merge = diff3_range_to_merge_range(&merged);
220
221        cleanup_conflicts(&mut merge);
222
223        output_result_bytes(
224            &ancestor_lines,
225            &our_lines,
226            &their_lines,
227            &merge,
228            self.conflict_marker_length,
229            self.style,
230        )
231    }
232}
233
234impl Default for MergeOptions {
235    fn default() -> Self {
236        Self::new()
237    }
238}
239
240/// Merge two files given a common ancestor.
241///
242/// Returns `Ok(String)` upon a successful merge.
243/// Returns `Err(String)` if there were conflicts, with the conflicting
244/// regions marked with conflict markers.
245///
246/// ## Merging two files without conflicts
247/// ```
248/// # use diffy_imara::merge;
249/// let original = "\
250/// Devotion
251/// Dominion
252/// Odium
253/// Preservation
254/// Ruin
255/// Cultivation
256/// Honor
257/// Endowment
258/// Autonomy
259/// Ambition
260/// ";
261/// let a = "\
262/// Odium
263/// Preservation
264/// Ruin
265/// Cultivation
266/// Endowment
267/// Autonomy
268/// ";
269/// let b = "\
270/// Devotion
271/// Dominion
272/// Odium
273/// Harmony
274/// Cultivation
275/// Honor
276/// Endowment
277/// Autonomy
278/// Ambition
279/// ";
280///
281/// let expected = "\
282/// Odium
283/// Harmony
284/// Cultivation
285/// Endowment
286/// Autonomy
287/// ";
288///
289/// assert_eq!(merge(original, a, b).unwrap(), expected);
290/// ```
291pub fn merge<'a>(ancestor: &'a str, ours: &'a str, theirs: &'a str) -> Result<String, String> {
292    MergeOptions::default().merge(ancestor, ours, theirs)
293}
294
295/// Perform a 3-way merge between potentially non-utf8 texts
296pub fn merge_bytes<'a>(
297    ancestor: &'a [u8],
298    ours: &'a [u8],
299    theirs: &'a [u8],
300) -> Result<Vec<u8>, Vec<u8>> {
301    MergeOptions::default().merge_bytes(ancestor, ours, theirs)
302}
303
304fn merge_solutions<'ancestor, 'ours, 'theirs, T: ?Sized + SliceLike>(
305    our_solution: &[DiffRange<'ancestor, 'ours, T>],
306    their_solution: &[DiffRange<'ancestor, 'theirs, T>],
307) -> Vec<Diff3Range<'ancestor, 'ours, 'theirs, T>> {
308    let mut our_solution = our_solution.iter().copied();
309    let mut their_solution = their_solution.iter().copied();
310    let mut ours = our_solution.next();
311    let mut theirs = their_solution.next();
312
313    let mut solution = Vec::new();
314
315    while ours.is_some() || theirs.is_some() {
316        use DiffRange as DR;
317        let merge_range = match (ours, theirs) {
318            //
319            // Inserts can't easily be checked to see if they match each other
320            //
321            (Some(DR::Insert(range)), _) => {
322                ours.take();
323                Diff3Range::Ours(range)
324            }
325            (_, Some(DR::Insert(range))) => {
326                theirs.take();
327                Diff3Range::Theirs(range)
328            }
329
330            (Some(DR::Equal(ancestor1, our_range)), Some(DR::Equal(ancestor2, their_range))) => {
331                assert_eq!(ancestor1.offset(), ancestor2.offset());
332                let len = cmp::min(ancestor1.len(), ancestor2.len());
333
334                shrink_front(&mut ours, len);
335                shrink_front(&mut theirs, len);
336
337                Diff3Range::Equal(
338                    ancestor1.slice(..len),
339                    our_range.slice(..len),
340                    their_range.slice(..len),
341                )
342            }
343
344            (Some(DR::Equal(ancestor1, our_range)), Some(DR::Delete(ancestor2))) => {
345                assert_eq!(ancestor1.offset(), ancestor2.offset());
346                let len = cmp::min(ancestor1.len(), ancestor2.len());
347
348                shrink_front(&mut ours, len);
349                shrink_front(&mut theirs, len);
350
351                Diff3Range::AncestorOurs(ancestor1.slice(..len), our_range.slice(..len))
352            }
353
354            (Some(DR::Delete(ancestor1)), Some(DR::Equal(ancestor2, their_range))) => {
355                assert_eq!(ancestor1.offset(), ancestor2.offset());
356                let len = cmp::min(ancestor1.len(), ancestor2.len());
357
358                shrink_front(&mut ours, len);
359                shrink_front(&mut theirs, len);
360
361                Diff3Range::AncestorTheirs(ancestor2.slice(..len), their_range.slice(..len))
362            }
363
364            (Some(DR::Delete(ancestor1)), Some(DR::Delete(ancestor2))) => {
365                assert_eq!(ancestor1.offset(), ancestor2.offset());
366                let len = cmp::min(ancestor1.len(), ancestor2.len());
367
368                shrink_front(&mut ours, len);
369                shrink_front(&mut theirs, len);
370
371                Diff3Range::Ancestor(ancestor1.slice(..len))
372            }
373
374            //
375            // Unreachable cases
376            //
377            (Some(DR::Equal(..) | DR::Delete(..)), None)
378            | (None, Some(DR::Equal(..) | DR::Delete(_)))
379            | (None, None) => unreachable!("Equal/Delete should match up"),
380        };
381
382        solution.push(merge_range);
383
384        if ours.map_or(true, |range| range.is_empty()) {
385            ours = our_solution.next();
386        }
387        if theirs.map_or(true, |range| range.is_empty()) {
388            theirs = their_solution.next();
389        }
390    }
391
392    solution
393}
394
395fn shrink_front<T: ?Sized + SliceLike>(maybe_range: &mut Option<DiffRange<T>>, len: usize) {
396    if let Some(range) = maybe_range {
397        range.shrink_front(len)
398    }
399}
400
401fn diff3_range_to_merge_range<'ancestor, 'ours, 'theirs, T: ?Sized + SliceLike>(
402    solution: &[Diff3Range<'ancestor, 'ours, 'theirs, T>],
403) -> Vec<MergeRange<'ancestor, 'ours, 'theirs, T>> {
404    let mut ancestor: Option<Range<'ancestor, T>> = None;
405    let mut ours: Option<Range<'ours, T>> = None;
406    let mut theirs: Option<Range<'theirs, T>> = None;
407
408    let mut merge = Vec::new();
409
410    for &diff3 in solution {
411        match diff3 {
412            Diff3Range::Equal(ancestor_range, our_range, their_range) => {
413                if let Some(merge_range) =
414                    create_merge_range(ancestor.take(), ours.take(), theirs.take())
415                {
416                    merge.push(merge_range);
417                }
418                merge.push(MergeRange::Equal(ancestor_range, our_range, their_range));
419            }
420            Diff3Range::Ancestor(range) => {
421                set_or_merge_range(&mut ancestor, range);
422                set_or_merge_range(&mut ours, Range::empty());
423                set_or_merge_range(&mut theirs, Range::empty());
424            }
425            Diff3Range::AncestorOurs(ancestor_range, our_range) => {
426                set_or_merge_range(&mut ancestor, ancestor_range);
427                set_or_merge_range(&mut ours, our_range);
428            }
429            Diff3Range::AncestorTheirs(ancestor_range, their_range) => {
430                set_or_merge_range(&mut ancestor, ancestor_range);
431                set_or_merge_range(&mut theirs, their_range);
432            }
433            Diff3Range::Ours(range) => set_or_merge_range(&mut ours, range),
434            Diff3Range::Theirs(range) => set_or_merge_range(&mut theirs, range),
435        }
436    }
437
438    if let Some(merge_range) = create_merge_range(ancestor.take(), ours.take(), theirs.take()) {
439        merge.push(merge_range);
440    }
441
442    merge
443}
444
445fn set_or_merge_range<'a, T: ?Sized>(range1: &mut Option<Range<'a, T>>, range2: Range<'a, T>) {
446    if let Some(range1) = range1 {
447        if range1.is_empty() {
448            *range1 = range2;
449        } else if !range2.is_empty() {
450            assert_eq!(range1.offset() + range1.len(), range2.offset());
451            range1.grow_down(range2.len());
452        }
453    } else {
454        *range1 = Some(range2);
455    }
456}
457
458fn create_merge_range<'ancestor, 'ours, 'theirs, T: ?Sized + SliceLike>(
459    ancestor: Option<Range<'ancestor, T>>,
460    ours: Option<Range<'ours, T>>,
461    theirs: Option<Range<'theirs, T>>,
462) -> Option<MergeRange<'ancestor, 'ours, 'theirs, T>> {
463    match (ancestor, ours, theirs) {
464        (_, None, None) => None,
465
466        (None, Some(ours), None) => Some(MergeRange::Ours(ours)),
467        (None, None, Some(theirs)) => Some(MergeRange::Theirs(theirs)),
468
469        (ancestor, ours, theirs) => Some(MergeRange::Conflict(
470            ancestor.unwrap_or_default(),
471            ours.unwrap_or_default(),
472            theirs.unwrap_or_default(),
473        )),
474    }
475}
476
477#[allow(clippy::needless_lifetimes)]
478fn cleanup_conflicts<'ancestor, 'ours, 'theirs, T: ?Sized + SliceLike + PartialEq>(
479    solution: &mut [MergeRange<'ancestor, 'ours, 'theirs, T>],
480) {
481    // TODO this could probably be more sophisticated:
482    // e.g. run the diff algorithm on the conflict area
483    for merge in solution {
484        if let MergeRange::Conflict(ancestor, ours, theirs) = *merge {
485            // If the ranges in the conflict end up being the same on both sides then we can
486            // eliminate the conflict
487            if ours.as_slice() == theirs.as_slice() {
488                *merge = MergeRange::Both(ours, theirs);
489            // If either ours or theirs exactly matches ancestor then we can also eliminate the
490            // conflict
491            } else if ancestor.as_slice() == ours.as_slice() {
492                *merge = MergeRange::Theirs(theirs);
493            } else if ancestor.as_slice() == theirs.as_slice() {
494                *merge = MergeRange::Ours(ours);
495            }
496        }
497    }
498}
499
500fn output_result<'a, T: ?Sized>(
501    ancestor: &[&'a str],
502    ours: &[&'a str],
503    theirs: &[&'a str],
504    merge: &[MergeRange<T>],
505    marker_len: usize,
506    style: ConflictStyle,
507) -> Result<String, String> {
508    let mut conflicts = 0;
509    let mut output = String::new();
510
511    for (range_idx, merge_range) in merge.iter().enumerate() {
512        match merge_range {
513            MergeRange::Equal(range, ..) => {
514                add_lines(&mut output, &ancestor[range.range()]);
515            }
516            MergeRange::Conflict(ancestor_range, ours_range, theirs_range) => {
517                let ancestor = &ancestor[ancestor_range.range()];
518                let ours = &ours[ours_range.range()];
519                let theirs = &theirs[theirs_range.range()];
520
521                if range_idx == merge.len() - 1 {
522                    let ancestor_last_ends_with_newline =
523                        ancestor.last().map_or(false, |l| l.ends_with('\n'));
524                    let ours_last_ends_with_newline =
525                        ours.last().map_or(false, |l| l.ends_with('\n'));
526                    let theirs_last_ends_with_newline =
527                        theirs.last().map_or(false, |l| l.ends_with('\n'));
528
529                    let (add_after_lines, add_after_right_marker) =
530                        if ancestor_last_ends_with_newline
531                            && ours_last_ends_with_newline
532                            && theirs_last_ends_with_newline
533                        {
534                            (false, true)
535                        } else {
536                            (true, false)
537                        };
538
539                    add_conflict_marker(&mut output, '<', marker_len, Some("ours"));
540                    add_lines(&mut output, ours);
541                    if add_after_lines {
542                        output.push('\n');
543                    }
544
545                    if let ConflictStyle::Diff3 = style {
546                        add_conflict_marker(&mut output, '|', marker_len, Some("original"));
547                        add_lines(&mut output, ancestor);
548                        if add_after_lines {
549                            output.push('\n');
550                        }
551                    }
552
553                    add_conflict_marker(&mut output, '=', marker_len, None);
554
555                    add_lines(&mut output, theirs);
556                    if add_after_lines {
557                        output.push('\n');
558                    }
559                    add_conflict_marker(&mut output, '>', marker_len, Some("theirs"));
560                    if !add_after_right_marker {
561                        output
562                            .pop()
563                            .expect("a `\n` is always added by the `add_conflict_marker` above");
564                    }
565                } else {
566                    add_conflict_marker(&mut output, '<', marker_len, Some("ours"));
567                    add_lines(&mut output, ours);
568
569                    if let ConflictStyle::Diff3 = style {
570                        add_conflict_marker(&mut output, '|', marker_len, Some("original"));
571                        add_lines(&mut output, ancestor);
572                    }
573
574                    add_conflict_marker(&mut output, '=', marker_len, None);
575                    add_lines(&mut output, theirs);
576                    add_conflict_marker(&mut output, '>', marker_len, Some("theirs"));
577                }
578
579                conflicts += 1;
580            }
581            MergeRange::Ours(range) => {
582                add_lines(&mut output, &ours[range.range()]);
583            }
584            MergeRange::Theirs(range) => {
585                add_lines(&mut output, &theirs[range.range()]);
586            }
587            MergeRange::Both(range, _) => {
588                add_lines(&mut output, &ours[range.range()]);
589            }
590        }
591    }
592
593    if conflicts != 0 {
594        Err(output)
595    } else {
596        Ok(output)
597    }
598}
599
600fn add_lines(dest: &mut String, lines: &[&str]) {
601    dest.extend(lines.iter().copied());
602}
603
604fn add_conflict_marker(
605    output: &mut String,
606    marker: char,
607    marker_len: usize,
608    filename: Option<&str>,
609) {
610    for _ in 0..marker_len {
611        output.push(marker);
612    }
613
614    if let Some(filename) = filename {
615        output.push(' ');
616        output.push_str(filename);
617    }
618    output.push('\n');
619}
620
621fn output_result_bytes<'a, T: ?Sized>(
622    ancestor: &[&'a [u8]],
623    ours: &[&'a [u8]],
624    theirs: &[&'a [u8]],
625    merge: &[MergeRange<T>],
626    marker_len: usize,
627    style: ConflictStyle,
628) -> Result<Vec<u8>, Vec<u8>> {
629    let mut conflicts = 0;
630    let mut output: Vec<u8> = Vec::new();
631
632    for (range_idx, merge_range) in merge.iter().enumerate() {
633        match merge_range {
634            MergeRange::Equal(range, ..) => {
635                add_lines_bytes(&mut output, &ancestor[range.range()]);
636            }
637            MergeRange::Conflict(ancestor_range, ours_range, theirs_range) => {
638                let ancestor = &ancestor[ancestor_range.range()];
639                let ours = &ours[ours_range.range()];
640                let theirs = &theirs[theirs_range.range()];
641
642                if range_idx == merge.len() - 1 {
643                    let ancestor_last_ends_with_newline =
644                        ancestor.last().map_or(false, |l| l.ends_with(b"\n"));
645                    let ours_last_ends_with_newline =
646                        ours.last().map_or(false, |l| l.ends_with(b"\n"));
647                    let theirs_last_ends_with_newline =
648                        theirs.last().map_or(false, |l| l.ends_with(b"\n"));
649
650                    let (add_after_lines, add_after_right_marker) =
651                        if ancestor_last_ends_with_newline
652                            && ours_last_ends_with_newline
653                            && theirs_last_ends_with_newline
654                        {
655                            (false, true)
656                        } else {
657                            (true, false)
658                        };
659
660                    add_conflict_marker_bytes(&mut output, b'<', marker_len, Some(b"ours"));
661                    add_lines_bytes(&mut output, ours);
662                    if add_after_lines {
663                        output.push(b'\n');
664                    }
665
666                    if let ConflictStyle::Diff3 = style {
667                        add_conflict_marker_bytes(&mut output, b'|', marker_len, Some(b"original"));
668                        add_lines_bytes(&mut output, ancestor);
669                        if add_after_lines {
670                            output.push(b'\n');
671                        }
672                    }
673
674                    add_conflict_marker_bytes(&mut output, b'=', marker_len, None);
675
676                    add_lines_bytes(&mut output, theirs);
677                    if add_after_lines {
678                        output.push(b'\n');
679                    }
680                    add_conflict_marker_bytes(&mut output, b'>', marker_len, Some(b"theirs"));
681                    if !add_after_right_marker {
682                        output.pop().expect(
683                            "a `\n` is always added by the `add_conflict_marker_bytes` above",
684                        );
685                    }
686                } else {
687                    add_conflict_marker_bytes(&mut output, b'<', marker_len, Some(b"ours"));
688                    add_lines_bytes(&mut output, ours);
689
690                    if let ConflictStyle::Diff3 = style {
691                        add_conflict_marker_bytes(&mut output, b'|', marker_len, Some(b"original"));
692                        add_lines_bytes(&mut output, ancestor);
693                    }
694
695                    add_conflict_marker_bytes(&mut output, b'=', marker_len, None);
696                    add_lines_bytes(&mut output, theirs);
697                    add_conflict_marker_bytes(&mut output, b'>', marker_len, Some(b"theirs"));
698                }
699
700                conflicts += 1;
701            }
702            MergeRange::Ours(range) => {
703                add_lines_bytes(&mut output, &ours[range.range()]);
704            }
705            MergeRange::Theirs(range) => {
706                add_lines_bytes(&mut output, &theirs[range.range()]);
707            }
708            MergeRange::Both(range, _) => {
709                add_lines_bytes(&mut output, &ours[range.range()]);
710            }
711        }
712    }
713
714    if conflicts != 0 {
715        Err(output)
716    } else {
717        Ok(output)
718    }
719}
720
721fn add_lines_bytes(output: &mut Vec<u8>, lines: &[&[u8]]) {
722    lines.iter().for_each(|line| output.extend_from_slice(line));
723}
724
725fn add_conflict_marker_bytes(
726    output: &mut Vec<u8>,
727    marker: u8,
728    marker_len: usize,
729    filename: Option<&[u8]>,
730) {
731    for _ in 0..marker_len {
732        output.push(marker);
733    }
734
735    if let Some(filename) = filename {
736        output.push(b' ');
737        output.extend_from_slice(filename);
738    }
739    output.push(b'\n');
740}