1#![allow(missing_docs)]
16
17use std::borrow::Borrow;
18use std::collections::VecDeque;
19use std::iter;
20use std::mem;
21
22use bstr::BStr;
23use bstr::BString;
24use itertools::Itertools as _;
25
26use crate::diff::Diff;
27use crate::diff::DiffHunk;
28use crate::diff::DiffHunkKind;
29use crate::merge::Merge;
30
31#[derive(PartialEq, Eq, Clone, Debug)]
33pub struct DiffLine<'a> {
34 pub line_number: DiffLineNumber,
35 pub hunks: Vec<(DiffLineHunkSide, &'a BStr)>,
36}
37
38impl DiffLine<'_> {
39 pub fn has_left_content(&self) -> bool {
40 self.hunks
41 .iter()
42 .any(|&(side, _)| side != DiffLineHunkSide::Right)
43 }
44
45 pub fn has_right_content(&self) -> bool {
46 self.hunks
47 .iter()
48 .any(|&(side, _)| side != DiffLineHunkSide::Left)
49 }
50
51 pub fn is_unmodified(&self) -> bool {
52 self.hunks
53 .iter()
54 .all(|&(side, _)| side == DiffLineHunkSide::Both)
55 }
56
57 fn take(&mut self) -> Self {
58 DiffLine {
59 line_number: self.line_number,
60 hunks: mem::take(&mut self.hunks),
61 }
62 }
63}
64
65#[derive(Clone, Copy, Debug, Eq, PartialEq)]
66pub struct DiffLineNumber {
67 pub left: u32,
68 pub right: u32,
69}
70
71#[derive(Clone, Copy, Debug, Eq, PartialEq)]
73pub enum DiffLineHunkSide {
74 Both,
75 Left,
76 Right,
77}
78
79pub struct DiffLineIterator<'a, I> {
80 diff_hunks: iter::Fuse<I>,
81 current_line: DiffLine<'a>,
82 queued_lines: VecDeque<DiffLine<'a>>,
83}
84
85impl<'a, I> DiffLineIterator<'a, I>
86where
87 I: Iterator,
88 I::Item: Borrow<DiffHunk<'a>>,
89{
90 pub fn new(diff_hunks: I) -> Self {
92 let line_number = DiffLineNumber { left: 1, right: 1 };
93 Self::with_line_number(diff_hunks, line_number)
94 }
95
96 pub fn with_line_number(diff_hunks: I, line_number: DiffLineNumber) -> Self {
99 let current_line = DiffLine {
100 line_number,
101 hunks: vec![],
102 };
103 DiffLineIterator {
104 diff_hunks: diff_hunks.fuse(),
105 current_line,
106 queued_lines: VecDeque::new(),
107 }
108 }
109}
110
111impl<I> DiffLineIterator<'_, I> {
112 pub fn next_line_number(&self) -> DiffLineNumber {
115 let next_line = self.queued_lines.front().unwrap_or(&self.current_line);
116 next_line.line_number
117 }
118}
119
120impl<'a, I> Iterator for DiffLineIterator<'a, I>
121where
122 I: Iterator,
123 I::Item: Borrow<DiffHunk<'a>>,
124{
125 type Item = DiffLine<'a>;
126
127 fn next(&mut self) -> Option<Self::Item> {
128 while self.queued_lines.is_empty() {
131 let Some(hunk) = self.diff_hunks.next() else {
132 break;
133 };
134 let hunk = hunk.borrow();
135 match hunk.kind {
136 DiffHunkKind::Matching => {
137 debug_assert!(hunk.contents.iter().all_equal());
139 let text = hunk.contents[0];
140 let lines = text.split_inclusive(|b| *b == b'\n').map(BStr::new);
141 for line in lines {
142 self.current_line.hunks.push((DiffLineHunkSide::Both, line));
143 if line.ends_with(b"\n") {
144 self.queued_lines.push_back(self.current_line.take());
145 self.current_line.line_number.left += 1;
146 self.current_line.line_number.right += 1;
147 }
148 }
149 }
150 DiffHunkKind::Different => {
151 let [left_text, right_text] = hunk.contents[..]
152 .try_into()
153 .expect("hunk should have exactly two inputs");
154 let left_lines = left_text.split_inclusive(|b| *b == b'\n').map(BStr::new);
155 for left_line in left_lines {
156 self.current_line
157 .hunks
158 .push((DiffLineHunkSide::Left, left_line));
159 if left_line.ends_with(b"\n") {
160 self.queued_lines.push_back(self.current_line.take());
161 self.current_line.line_number.left += 1;
162 }
163 }
164 let mut right_lines =
165 right_text.split_inclusive(|b| *b == b'\n').map(BStr::new);
166 if right_text.starts_with(b"\n")
171 && self.current_line.hunks.is_empty()
172 && self
173 .queued_lines
174 .front()
175 .is_some_and(|queued| queued.has_right_content())
176 {
177 let blank_line = right_lines.next().unwrap();
178 assert_eq!(blank_line, b"\n");
179 self.current_line.line_number.right += 1;
180 }
181 for right_line in right_lines {
182 self.current_line
183 .hunks
184 .push((DiffLineHunkSide::Right, right_line));
185 if right_line.ends_with(b"\n") {
186 self.queued_lines.push_back(self.current_line.take());
187 self.current_line.line_number.right += 1;
188 }
189 }
190 }
191 }
192 }
193
194 if let Some(line) = self.queued_lines.pop_front() {
195 return Some(line);
196 }
197
198 if !self.current_line.hunks.is_empty() {
199 return Some(self.current_line.take());
200 }
201
202 None
203 }
204}
205
206#[derive(Clone, Debug, Eq, PartialEq)]
208pub struct ConflictDiffHunk<'input> {
209 pub kind: DiffHunkKind,
210 pub lefts: Merge<&'input BStr>,
211 pub rights: Merge<&'input BStr>,
212}
213
214pub fn conflict_diff_hunks<'input, I>(
218 diff_hunks: I,
219 num_lefts: usize,
220) -> impl Iterator<Item = ConflictDiffHunk<'input>>
221where
222 I: IntoIterator,
223 I::Item: Borrow<DiffHunk<'input>>,
224{
225 fn to_merge<'input>(contents: &[&'input BStr]) -> Merge<&'input BStr> {
226 if contents.iter().all_equal() {
229 Merge::resolved(contents[0])
230 } else {
231 Merge::from_vec(contents)
232 }
233 }
234
235 diff_hunks.into_iter().map(move |hunk| {
236 let hunk = hunk.borrow();
237 let (lefts, rights) = hunk.contents.split_at(num_lefts);
238 if let ([left], [right]) = (lefts, rights) {
239 ConflictDiffHunk {
241 kind: hunk.kind,
242 lefts: Merge::resolved(left),
243 rights: Merge::resolved(right),
244 }
245 } else {
246 let lefts = to_merge(lefts);
247 let rights = to_merge(rights);
248 let kind = match hunk.kind {
249 DiffHunkKind::Matching => DiffHunkKind::Matching,
250 DiffHunkKind::Different if lefts == rights => DiffHunkKind::Matching,
251 DiffHunkKind::Different => DiffHunkKind::Different,
252 };
253 ConflictDiffHunk {
254 kind,
255 lefts,
256 rights,
257 }
258 }
259 })
260}
261
262#[derive(PartialEq, Eq, Clone, Debug)]
265pub enum MergeResult {
266 Resolved(BString),
268 Conflict(Vec<Merge<BString>>),
270}
271
272pub fn merge_hunks<T: AsRef<[u8]>>(inputs: &Merge<T>) -> MergeResult {
276 merge_inner(inputs)
277}
278
279pub fn merge<T: AsRef<[u8]>>(inputs: &Merge<T>) -> Merge<BString> {
285 merge_inner(inputs)
286}
287
288pub fn try_merge<T: AsRef<[u8]>>(inputs: &Merge<T>) -> Option<BString> {
293 merge_inner(inputs)
294}
295
296fn merge_inner<'input, T: AsRef<[u8]>, B: FromMergeHunks<'input>>(inputs: &'input Merge<T>) -> B {
297 let num_diffs = inputs.removes().len();
301 let diff = Diff::by_line(inputs.removes().chain(inputs.adds()));
302 let hunks = resolve_diff_hunks(&diff, num_diffs);
303 B::from_hunks(hunks)
304}
305
306trait FromMergeHunks<'input>: Sized {
308 fn from_hunks<I: IntoIterator<Item = Merge<&'input BStr>>>(hunks: I) -> Self;
309}
310
311impl<'input> FromMergeHunks<'input> for MergeResult {
312 fn from_hunks<I: IntoIterator<Item = Merge<&'input BStr>>>(hunks: I) -> Self {
313 collect_hunks(hunks)
314 }
315}
316
317impl<'input> FromMergeHunks<'input> for Merge<BString> {
318 fn from_hunks<I: IntoIterator<Item = Merge<&'input BStr>>>(hunks: I) -> Self {
319 collect_merged(hunks)
320 }
321}
322
323impl<'input> FromMergeHunks<'input> for Option<BString> {
324 fn from_hunks<I: IntoIterator<Item = Merge<&'input BStr>>>(hunks: I) -> Self {
325 collect_resolved(hunks)
326 }
327}
328
329fn collect_hunks<'input>(hunks: impl IntoIterator<Item = Merge<&'input BStr>>) -> MergeResult {
332 let mut resolved_hunk = BString::new(vec![]);
333 let mut merge_hunks: Vec<Merge<BString>> = vec![];
334 for hunk in hunks {
335 if let Some(&content) = hunk.as_resolved() {
336 resolved_hunk.extend_from_slice(content);
337 } else {
338 if !resolved_hunk.is_empty() {
339 merge_hunks.push(Merge::resolved(resolved_hunk));
340 resolved_hunk = BString::new(vec![]);
341 }
342 merge_hunks.push(hunk.map(|&s| s.to_owned()));
343 }
344 }
345
346 if merge_hunks.is_empty() {
347 MergeResult::Resolved(resolved_hunk)
348 } else {
349 if !resolved_hunk.is_empty() {
350 merge_hunks.push(Merge::resolved(resolved_hunk));
351 }
352 MergeResult::Conflict(merge_hunks)
353 }
354}
355
356fn collect_merged<'input>(hunks: impl IntoIterator<Item = Merge<&'input BStr>>) -> Merge<BString> {
359 let mut maybe_resolved = Merge::resolved(BString::default());
360 for hunk in hunks {
361 if let Some(&content) = hunk.as_resolved() {
362 for buf in maybe_resolved.iter_mut() {
363 buf.extend_from_slice(content);
364 }
365 } else {
366 maybe_resolved = match maybe_resolved.into_resolved() {
367 Ok(content) => Merge::from_vec(vec![content; hunk.as_slice().len()]),
368 Err(conflict) => conflict,
369 };
370 assert_eq!(maybe_resolved.as_slice().len(), hunk.as_slice().len());
371 for (buf, s) in iter::zip(maybe_resolved.iter_mut(), hunk) {
372 buf.extend_from_slice(s);
373 }
374 }
375 }
376 maybe_resolved
377}
378
379fn collect_resolved<'input>(
381 hunks: impl IntoIterator<Item = Merge<&'input BStr>>,
382) -> Option<BString> {
383 hunks
384 .into_iter()
385 .map(|hunk| hunk.into_resolved().ok())
386 .collect()
387}
388
389fn resolve_diff_hunks<'a, 'input>(
391 diff: &'a Diff<'input>,
392 num_diffs: usize,
393) -> impl Iterator<Item = Merge<&'input BStr>> + use<'a, 'input> {
394 diff.hunks().map(move |diff_hunk| match diff_hunk.kind {
395 DiffHunkKind::Matching => {
396 debug_assert!(diff_hunk.contents.iter().all_equal());
397 Merge::resolved(diff_hunk.contents[0])
398 }
399 DiffHunkKind::Different => {
400 let merge = Merge::from_removes_adds(
401 diff_hunk.contents[..num_diffs].iter().copied(),
402 diff_hunk.contents[num_diffs..].iter().copied(),
403 );
404 match merge.resolve_trivial() {
405 Some(&content) => Merge::resolved(content),
406 None => merge,
407 }
408 }
409 })
410}
411
412#[cfg(test)]
413mod tests {
414 use indoc::indoc;
415
416 use super::*;
417
418 fn conflict<const N: usize>(values: [&[u8]; N]) -> Merge<BString> {
419 Merge::from_vec(values.map(hunk).to_vec())
420 }
421
422 fn resolved(value: &[u8]) -> Merge<BString> {
423 Merge::resolved(hunk(value))
424 }
425
426 fn hunk(data: &[u8]) -> BString {
427 data.into()
428 }
429
430 #[test]
431 fn test_diff_line_iterator_line_numbers() {
432 let mut line_iter = DiffLineIterator::with_line_number(
433 [DiffHunk::different(["a\nb", "c\nd\n"])].into_iter(),
434 DiffLineNumber { left: 1, right: 10 },
435 );
436 assert_eq!(
438 line_iter.next_line_number(),
439 DiffLineNumber { left: 1, right: 10 }
440 );
441 assert_eq!(
442 line_iter.next().unwrap(),
443 DiffLine {
444 line_number: DiffLineNumber { left: 1, right: 10 },
445 hunks: vec![(DiffLineHunkSide::Left, "a\n".as_ref())],
446 }
447 );
448 assert_eq!(
450 line_iter.next_line_number(),
451 DiffLineNumber { left: 2, right: 10 }
452 );
453 assert_eq!(
454 line_iter.next().unwrap(),
455 DiffLine {
456 line_number: DiffLineNumber { left: 2, right: 10 },
457 hunks: vec![
458 (DiffLineHunkSide::Left, "b".as_ref()),
459 (DiffLineHunkSide::Right, "c\n".as_ref()),
460 ],
461 }
462 );
463 assert_eq!(
465 line_iter.next_line_number(),
466 DiffLineNumber { left: 2, right: 11 }
467 );
468 assert_eq!(
469 line_iter.next().unwrap(),
470 DiffLine {
471 line_number: DiffLineNumber { left: 2, right: 11 },
472 hunks: vec![(DiffLineHunkSide::Right, "d\n".as_ref())],
473 }
474 );
475 assert_eq!(
477 line_iter.next_line_number(),
478 DiffLineNumber { left: 2, right: 12 }
479 );
480 assert!(line_iter.next().is_none());
481 assert_eq!(
482 line_iter.next_line_number(),
483 DiffLineNumber { left: 2, right: 12 }
484 );
485 }
486
487 #[test]
488 fn test_diff_line_iterator_blank_right_line_single_left() {
489 let mut line_iter = DiffLineIterator::new(
490 [
491 DiffHunk::matching(["a"].repeat(2)),
492 DiffHunk::different(["x\n", "\ny\n"]),
493 ]
494 .into_iter(),
495 );
496 assert_eq!(
497 line_iter.next().unwrap(),
498 DiffLine {
499 line_number: DiffLineNumber { left: 1, right: 1 },
500 hunks: vec![
501 (DiffLineHunkSide::Both, "a".as_ref()),
502 (DiffLineHunkSide::Left, "x\n".as_ref()),
503 ],
504 }
505 );
506 assert_eq!(
509 line_iter.next().unwrap(),
510 DiffLine {
511 line_number: DiffLineNumber { left: 2, right: 2 },
512 hunks: vec![(DiffLineHunkSide::Right, "y\n".as_ref())],
513 }
514 );
515 }
516
517 #[test]
518 fn test_diff_line_iterator_blank_right_line_multiple_lefts() {
519 let mut line_iter = DiffLineIterator::new(
520 [
521 DiffHunk::matching(["a"].repeat(2)),
522 DiffHunk::different(["x\n\n", "\ny\n"]),
523 ]
524 .into_iter(),
525 );
526 assert_eq!(
527 line_iter.next().unwrap(),
528 DiffLine {
529 line_number: DiffLineNumber { left: 1, right: 1 },
530 hunks: vec![
531 (DiffLineHunkSide::Both, "a".as_ref()),
532 (DiffLineHunkSide::Left, "x\n".as_ref()),
533 ],
534 }
535 );
536 assert_eq!(
537 line_iter.next().unwrap(),
538 DiffLine {
539 line_number: DiffLineNumber { left: 2, right: 1 },
540 hunks: vec![(DiffLineHunkSide::Left, "\n".as_ref())],
541 }
542 );
543 assert_eq!(
546 line_iter.next().unwrap(),
547 DiffLine {
548 line_number: DiffLineNumber { left: 3, right: 2 },
549 hunks: vec![(DiffLineHunkSide::Right, "y\n".as_ref())],
550 }
551 );
552 }
553
554 #[test]
555 fn test_diff_line_iterator_blank_right_line_after_non_empty_left() {
556 let mut line_iter = DiffLineIterator::new(
557 [
558 DiffHunk::matching(["a"].repeat(2)),
559 DiffHunk::different(["x\nz", "\ny\n"]),
560 ]
561 .into_iter(),
562 );
563 assert_eq!(
564 line_iter.next().unwrap(),
565 DiffLine {
566 line_number: DiffLineNumber { left: 1, right: 1 },
567 hunks: vec![
568 (DiffLineHunkSide::Both, "a".as_ref()),
569 (DiffLineHunkSide::Left, "x\n".as_ref()),
570 ],
571 }
572 );
573 assert_eq!(
574 line_iter.next().unwrap(),
575 DiffLine {
576 line_number: DiffLineNumber { left: 2, right: 1 },
577 hunks: vec![
578 (DiffLineHunkSide::Left, "z".as_ref()),
579 (DiffLineHunkSide::Right, "\n".as_ref()),
580 ],
581 }
582 );
583 assert_eq!(
584 line_iter.next().unwrap(),
585 DiffLine {
586 line_number: DiffLineNumber { left: 2, right: 2 },
587 hunks: vec![(DiffLineHunkSide::Right, "y\n".as_ref())],
588 }
589 );
590 }
591
592 #[test]
593 fn test_diff_line_iterator_blank_right_line_without_preceding_lines() {
594 let mut line_iter = DiffLineIterator::new([DiffHunk::different(["", "\ny\n"])].into_iter());
595 assert_eq!(
596 line_iter.next().unwrap(),
597 DiffLine {
598 line_number: DiffLineNumber { left: 1, right: 1 },
599 hunks: vec![(DiffLineHunkSide::Right, "\n".as_ref())],
600 }
601 );
602 assert_eq!(
603 line_iter.next().unwrap(),
604 DiffLine {
605 line_number: DiffLineNumber { left: 1, right: 2 },
606 hunks: vec![(DiffLineHunkSide::Right, "y\n".as_ref())],
607 }
608 );
609 }
610
611 #[test]
612 fn test_conflict_diff_hunks_no_conflicts() {
613 let diff_hunks = [
614 DiffHunk::matching(["a\n"].repeat(2)),
615 DiffHunk::different(["b\n", "c\n"]),
616 ];
617 let num_lefts = 1;
618 insta::assert_debug_snapshot!(
619 conflict_diff_hunks(&diff_hunks, num_lefts).collect_vec(), @r#"
620 [
621 ConflictDiffHunk {
622 kind: Matching,
623 lefts: Resolved(
624 "a\n",
625 ),
626 rights: Resolved(
627 "a\n",
628 ),
629 },
630 ConflictDiffHunk {
631 kind: Different,
632 lefts: Resolved(
633 "b\n",
634 ),
635 rights: Resolved(
636 "c\n",
637 ),
638 },
639 ]
640 "#);
641 }
642
643 #[test]
644 fn test_conflict_diff_hunks_simple_conflicts() {
645 let diff_hunks = [
646 DiffHunk::different(["a\n", "X\n", "b\n", "c\n"]),
648 DiffHunk::matching(["d\n"].repeat(4)),
649 DiffHunk::different(["e\n", "e\n", "e\n", "f\n"]),
651 ];
652 let num_lefts = 3;
653 insta::assert_debug_snapshot!(
654 conflict_diff_hunks(&diff_hunks, num_lefts).collect_vec(), @r#"
655 [
656 ConflictDiffHunk {
657 kind: Different,
658 lefts: Conflicted(
659 [
660 "a\n",
661 "X\n",
662 "b\n",
663 ],
664 ),
665 rights: Resolved(
666 "c\n",
667 ),
668 },
669 ConflictDiffHunk {
670 kind: Matching,
671 lefts: Resolved(
672 "d\n",
673 ),
674 rights: Resolved(
675 "d\n",
676 ),
677 },
678 ConflictDiffHunk {
679 kind: Different,
680 lefts: Resolved(
681 "e\n",
682 ),
683 rights: Resolved(
684 "f\n",
685 ),
686 },
687 ]
688 "#);
689 }
690
691 #[test]
692 fn test_conflict_diff_hunks_matching_conflicts() {
693 let diff_hunks = [
694 DiffHunk::different(["a\n", "X\n", "b\n", "a\n", "X\n", "b\n"]),
696 DiffHunk::matching(["c\n"].repeat(6)),
697 ];
698 let num_lefts = 3;
699 insta::assert_debug_snapshot!(
700 conflict_diff_hunks(&diff_hunks, num_lefts).collect_vec(), @r#"
701 [
702 ConflictDiffHunk {
703 kind: Matching,
704 lefts: Conflicted(
705 [
706 "a\n",
707 "X\n",
708 "b\n",
709 ],
710 ),
711 rights: Conflicted(
712 [
713 "a\n",
714 "X\n",
715 "b\n",
716 ],
717 ),
718 },
719 ConflictDiffHunk {
720 kind: Matching,
721 lefts: Resolved(
722 "c\n",
723 ),
724 rights: Resolved(
725 "c\n",
726 ),
727 },
728 ]
729 "#);
730 }
731
732 #[test]
733 fn test_conflict_diff_hunks_no_trivial_resolution() {
734 let diff_hunks = [DiffHunk::different(["a", "b", "a", "a"])];
735 let num_lefts = 1;
736 insta::assert_debug_snapshot!(
737 conflict_diff_hunks(&diff_hunks, num_lefts).collect_vec(), @r#"
738 [
739 ConflictDiffHunk {
740 kind: Different,
741 lefts: Resolved(
742 "a",
743 ),
744 rights: Conflicted(
745 [
746 "b",
747 "a",
748 "a",
749 ],
750 ),
751 },
752 ]
753 "#);
754 let num_lefts = 3;
755 insta::assert_debug_snapshot!(
756 conflict_diff_hunks(&diff_hunks, num_lefts).collect_vec(), @r#"
757 [
758 ConflictDiffHunk {
759 kind: Different,
760 lefts: Conflicted(
761 [
762 "a",
763 "b",
764 "a",
765 ],
766 ),
767 rights: Resolved(
768 "a",
769 ),
770 },
771 ]
772 "#);
773 }
774
775 #[test]
776 fn test_merge_single_hunk() {
777 assert_eq!(
779 merge_hunks(&conflict([b"", b"", b""])),
780 MergeResult::Resolved(hunk(b""))
781 );
782 assert_eq!(
784 merge_hunks(&conflict([b"a", b"a", b"a"])),
785 MergeResult::Resolved(hunk(b"a"))
786 );
787 assert_eq!(
789 merge_hunks(&conflict([b"", b"a\n", b"a\n"])),
790 MergeResult::Resolved(hunk(b""))
791 );
792 assert_eq!(
794 merge_hunks(&conflict([b"a\n", b"a\n", b""])),
795 MergeResult::Resolved(hunk(b""))
796 );
797 assert_eq!(
799 merge_hunks(&conflict([b"", b"a\n", b""])),
800 MergeResult::Resolved(hunk(b""))
801 );
802 assert_eq!(
804 merge_hunks(&conflict([b"a b", b"a", b"a"])),
805 MergeResult::Resolved(hunk(b"a b"))
806 );
807 assert_eq!(
809 merge_hunks(&conflict([b"a", b"a", b"a b"])),
810 MergeResult::Resolved(hunk(b"a b"))
811 );
812 assert_eq!(
814 merge_hunks(&conflict([b"a\n", b"", b"a\n", b"", b"a\n"])),
815 MergeResult::Resolved(hunk(b"a\n"))
816 );
817 assert_eq!(
819 merge_hunks(&conflict([b"b", b"a", b"b", b"", b"b"])),
820 MergeResult::Conflict(vec![conflict([b"b", b"a", b"b", b"", b"b"])])
821 );
822 assert_eq!(
824 merge_hunks(&conflict([b"", b"a\n", b"", b"a\n", b"", b"a\n", b""])),
825 MergeResult::Resolved(hunk(b""))
826 );
827 assert_eq!(
829 merge_hunks(&conflict([b"b\n", b"a\n", b"", b"a\n", b""])),
830 MergeResult::Conflict(vec![conflict([b"b\n", b"a\n", b"", b"a\n", b""])])
831 );
832 assert_eq!(
834 merge_hunks(&conflict([b"b", b"a", b"b", b"a", b"b"])),
835 MergeResult::Resolved(hunk(b"b"))
836 );
837 assert_eq!(
839 merge_hunks(&conflict([b"", b"a\n", b"b\n"])),
840 MergeResult::Conflict(vec![conflict([b"", b"a\n", b"b\n"])])
841 );
842 assert_eq!(
844 merge_hunks(&conflict([b"b\n", b"a\n", b""])),
845 MergeResult::Conflict(vec![conflict([b"b\n", b"a\n", b""])])
846 );
847 assert_eq!(
849 merge_hunks(&conflict([b"b", b"a", b"c"])),
850 MergeResult::Conflict(vec![conflict([b"b", b"a", b"c"])])
851 );
852 assert_eq!(
854 merge_hunks(&conflict([b"a", b"a", b"", b"a", b"a"])),
855 MergeResult::Resolved(hunk(b""))
856 );
857 assert_eq!(
859 merge_hunks(&conflict([b"b", b"a", b"a", b"a", b"b"])),
860 MergeResult::Resolved(hunk(b"b"))
861 );
862 assert_eq!(
864 merge_hunks(&conflict([b"b", b"a", b"a", b"a", b"c"])),
865 MergeResult::Conflict(vec![conflict([b"b", b"a", b"a", b"a", b"c"])])
866 );
867 assert_eq!(
871 merge_hunks(&conflict([b"b", b"a", b"a", b"b", b"c"])),
872 MergeResult::Resolved(hunk(b"c"))
873 );
874 assert_eq!(
876 merge_hunks(&conflict([b"c", b"a", b"d", b"b", b"e"])),
877 MergeResult::Conflict(vec![conflict([b"c", b"a", b"d", b"b", b"e"])])
878 );
879 assert_eq!(
881 merge_hunks(&conflict([b"c", b"a", b"c", b"b", b"c"])),
882 MergeResult::Conflict(vec![conflict([b"c", b"a", b"c", b"b", b"c"])])
883 );
884 }
885
886 #[test]
887 fn test_merge_multi_hunk() {
888 let inputs = conflict([b"a\nb\n", b"a\n", b"a\nc\n"]);
890 assert_eq!(
891 merge_hunks(&inputs),
892 MergeResult::Conflict(vec![resolved(b"a\n"), conflict([b"b\n", b"", b"c\n"])])
893 );
894 assert_eq!(merge(&inputs), conflict([b"a\nb\n", b"a\n", b"a\nc\n"]));
895 assert_eq!(try_merge(&inputs), None);
896
897 let inputs = conflict([b"a2\nb\nc\n", b"a\nb\nc\n", b"a\nb\nc2\n"]);
899 assert_eq!(
900 merge_hunks(&inputs),
901 MergeResult::Resolved(hunk(b"a2\nb\nc2\n"))
902 );
903 assert_eq!(merge(&inputs), resolved(b"a2\nb\nc2\n"));
904 assert_eq!(try_merge(&inputs), Some(hunk(b"a2\nb\nc2\n")));
905
906 let inputs = conflict([b"a\nb1\nc\n", b"a\nb\nc\n", b"a\nb2\nc\n"]);
908 assert_eq!(
909 merge_hunks(&inputs),
910 MergeResult::Conflict(vec![
911 resolved(b"a\n"),
912 conflict([b"b1\n", b"b\n", b"b2\n"]),
913 resolved(b"c\n"),
914 ])
915 );
916 assert_eq!(
917 merge(&inputs),
918 conflict([b"a\nb1\nc\n", b"a\nb\nc\n", b"a\nb2\nc\n"])
919 );
920 assert_eq!(try_merge(&inputs), None);
921
922 let inputs = conflict([b"a\nb\nc\n", b"a1\nb\nc\n", b"a2\nb\nc2\n"]);
924 assert_eq!(
925 merge_hunks(&inputs),
926 MergeResult::Conflict(vec![
927 conflict([b"a\n", b"a1\n", b"a2\n"]),
928 resolved(b"b\nc2\n"),
929 ])
930 );
931 assert_eq!(
932 merge(&inputs),
933 conflict([b"a\nb\nc2\n", b"a1\nb\nc2\n", b"a2\nb\nc2\n"])
934 );
935 assert_eq!(try_merge(&inputs), None);
936
937 let base = indoc! {b"
953 a {
954 p
955 }
956 "};
957 let left = indoc! {b"
958 a {
959 q
960 }
961
962 b {
963 x
964 }
965 "};
966 let right = indoc! {b"
967 a {
968 p
969 }
970
971 b {
972 x
973 }
974 "};
975 let merged = indoc! {b"
976 a {
977 q
978 }
979
980 b {
981 x
982 }
983
984 b {
985 x
986 }
987 "};
988 assert_eq!(merge(&conflict([left, base, right])), resolved(merged));
989 }
990}