2020-12-12 08:00:42 +00:00
|
|
|
// Copyright 2020 Google LLC
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// https://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
|
2021-04-07 06:33:38 +00:00
|
|
|
use std::collections::VecDeque;
|
2020-12-12 08:00:42 +00:00
|
|
|
use std::fmt::{Debug, Error, Formatter};
|
2021-01-19 06:00:40 +00:00
|
|
|
use std::ops::Range;
|
2020-12-12 08:00:42 +00:00
|
|
|
|
2021-06-30 15:57:49 +00:00
|
|
|
use itertools::Itertools;
|
|
|
|
|
2021-03-26 16:52:05 +00:00
|
|
|
use crate::diff;
|
2021-06-26 23:13:33 +00:00
|
|
|
use crate::diff::{Diff, DiffHunk};
|
2020-12-12 08:00:42 +00:00
|
|
|
|
|
|
|
#[derive(PartialEq, Eq, Clone, Debug)]
|
2021-01-18 08:53:44 +00:00
|
|
|
pub struct DiffLine<'a> {
|
2020-12-12 08:00:42 +00:00
|
|
|
pub left_line_number: u32,
|
|
|
|
pub right_line_number: u32,
|
|
|
|
pub has_left_content: bool,
|
|
|
|
pub has_right_content: bool,
|
2021-01-18 08:53:44 +00:00
|
|
|
pub hunks: Vec<DiffHunk<'a>>,
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
|
|
|
|
2021-01-18 08:53:44 +00:00
|
|
|
impl DiffLine<'_> {
|
2020-12-12 08:00:42 +00:00
|
|
|
fn reset_line(&mut self) {
|
|
|
|
self.has_left_content = false;
|
|
|
|
self.has_right_content = false;
|
|
|
|
self.hunks.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn is_unmodified(&self) -> bool {
|
|
|
|
self.hunks
|
|
|
|
.iter()
|
2021-06-26 17:01:29 +00:00
|
|
|
.all(|hunk| matches!(hunk, DiffHunk::Matching(_)))
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-07 06:33:38 +00:00
|
|
|
pub fn diff<'a>(left: &'a [u8], right: &'a [u8]) -> DiffLineIterator<'a> {
|
2021-06-27 05:05:26 +00:00
|
|
|
let diff_hunks = diff::diff(left, right);
|
|
|
|
DiffLineIterator::new(diff_hunks)
|
2021-04-07 06:33:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub struct DiffLineIterator<'a> {
|
2021-06-27 05:05:26 +00:00
|
|
|
diff_hunks: Vec<DiffHunk<'a>>,
|
2021-04-07 06:33:38 +00:00
|
|
|
current_pos: usize,
|
|
|
|
current_line: DiffLine<'a>,
|
|
|
|
queued_lines: VecDeque<DiffLine<'a>>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> DiffLineIterator<'a> {
|
2021-06-27 05:05:26 +00:00
|
|
|
fn new(diff_hunks: Vec<DiffHunk<'a>>) -> Self {
|
2021-04-07 06:33:38 +00:00
|
|
|
let current_line = DiffLine {
|
|
|
|
left_line_number: 1,
|
|
|
|
right_line_number: 1,
|
|
|
|
has_left_content: false,
|
|
|
|
has_right_content: false,
|
|
|
|
hunks: vec![],
|
|
|
|
};
|
|
|
|
DiffLineIterator {
|
2021-06-27 05:05:26 +00:00
|
|
|
diff_hunks,
|
2021-04-07 06:33:38 +00:00
|
|
|
current_pos: 0,
|
|
|
|
current_line,
|
|
|
|
queued_lines: VecDeque::new(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> Iterator for DiffLineIterator<'a> {
|
|
|
|
type Item = DiffLine<'a>;
|
|
|
|
|
|
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
|
|
// TODO: Should we attempt to interpret as utf-8 and otherwise break only at
|
|
|
|
// newlines?
|
2021-06-27 05:05:26 +00:00
|
|
|
while self.current_pos < self.diff_hunks.len() && self.queued_lines.is_empty() {
|
|
|
|
let hunk = &self.diff_hunks[self.current_pos];
|
2021-04-07 06:33:38 +00:00
|
|
|
self.current_pos += 1;
|
|
|
|
match hunk {
|
2021-06-27 05:05:26 +00:00
|
|
|
diff::DiffHunk::Matching(text) => {
|
2021-04-07 06:33:38 +00:00
|
|
|
let lines = text.split_inclusive(|b| *b == b'\n');
|
|
|
|
for line in lines {
|
|
|
|
self.current_line.has_left_content = true;
|
|
|
|
self.current_line.has_right_content = true;
|
2021-06-26 17:01:29 +00:00
|
|
|
self.current_line.hunks.push(DiffHunk::Matching(line));
|
2021-04-07 06:33:38 +00:00
|
|
|
if line.ends_with(b"\n") {
|
|
|
|
self.queued_lines.push_back(self.current_line.clone());
|
|
|
|
self.current_line.left_line_number += 1;
|
|
|
|
self.current_line.right_line_number += 1;
|
|
|
|
self.current_line.reset_line();
|
|
|
|
}
|
2021-03-26 16:52:05 +00:00
|
|
|
}
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
2021-06-27 05:05:26 +00:00
|
|
|
diff::DiffHunk::Different(contents) => {
|
|
|
|
let left = contents[0];
|
|
|
|
let right = contents[1];
|
2021-04-07 06:33:38 +00:00
|
|
|
let left_lines = left.split_inclusive(|b| *b == b'\n');
|
|
|
|
for left_line in left_lines {
|
|
|
|
self.current_line.has_left_content = true;
|
2021-06-26 17:01:29 +00:00
|
|
|
self.current_line
|
|
|
|
.hunks
|
|
|
|
.push(DiffHunk::Different(vec![left_line, b""]));
|
2021-04-07 06:33:38 +00:00
|
|
|
if left_line.ends_with(b"\n") {
|
|
|
|
self.queued_lines.push_back(self.current_line.clone());
|
|
|
|
self.current_line.left_line_number += 1;
|
|
|
|
self.current_line.reset_line();
|
|
|
|
}
|
2021-03-26 16:52:05 +00:00
|
|
|
}
|
2021-04-07 06:33:38 +00:00
|
|
|
let right_lines = right.split_inclusive(|b| *b == b'\n');
|
|
|
|
for right_line in right_lines {
|
|
|
|
self.current_line.has_right_content = true;
|
2021-06-26 17:01:29 +00:00
|
|
|
self.current_line
|
|
|
|
.hunks
|
|
|
|
.push(DiffHunk::Different(vec![b"", right_line]));
|
2021-04-07 06:33:38 +00:00
|
|
|
if right_line.ends_with(b"\n") {
|
|
|
|
self.queued_lines.push_back(self.current_line.clone());
|
|
|
|
self.current_line.right_line_number += 1;
|
|
|
|
self.current_line.reset_line();
|
|
|
|
}
|
2021-03-26 16:52:05 +00:00
|
|
|
}
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-04-07 06:33:38 +00:00
|
|
|
|
|
|
|
if let Some(line) = self.queued_lines.pop_front() {
|
|
|
|
return Some(line);
|
|
|
|
}
|
|
|
|
|
|
|
|
if !self.current_line.hunks.is_empty() {
|
|
|
|
let line = self.current_line.clone();
|
|
|
|
self.current_line.reset_line();
|
|
|
|
return Some(line);
|
|
|
|
}
|
|
|
|
|
|
|
|
None
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(PartialEq, Eq, Clone)]
|
|
|
|
pub enum MergeHunk {
|
|
|
|
Resolved(Vec<u8>),
|
|
|
|
Conflict {
|
2021-06-30 15:57:49 +00:00
|
|
|
removes: Vec<Vec<u8>>,
|
|
|
|
adds: Vec<Vec<u8>>,
|
2020-12-12 08:00:42 +00:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Debug for MergeHunk {
|
|
|
|
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
|
|
|
|
match self {
|
|
|
|
MergeHunk::Resolved(data) => f
|
|
|
|
.debug_tuple("Resolved")
|
|
|
|
.field(&String::from_utf8_lossy(data))
|
|
|
|
.finish(),
|
2021-06-30 15:57:49 +00:00
|
|
|
MergeHunk::Conflict { removes, adds } => f
|
2020-12-12 08:00:42 +00:00
|
|
|
.debug_struct("Conflict")
|
2021-06-30 15:57:49 +00:00
|
|
|
.field(
|
|
|
|
"removes",
|
|
|
|
&removes
|
|
|
|
.iter()
|
|
|
|
.map(|part| String::from_utf8_lossy(part))
|
|
|
|
.collect_vec(),
|
|
|
|
)
|
|
|
|
.field(
|
|
|
|
"adds",
|
|
|
|
&adds
|
|
|
|
.iter()
|
|
|
|
.map(|part| String::from_utf8_lossy(part))
|
|
|
|
.collect_vec(),
|
|
|
|
)
|
2020-12-12 08:00:42 +00:00
|
|
|
.finish(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
|
|
|
pub enum MergeResult {
|
|
|
|
Resolved(Vec<u8>),
|
|
|
|
Conflict(Vec<MergeHunk>),
|
|
|
|
}
|
|
|
|
|
2021-01-19 06:00:40 +00:00
|
|
|
/// A region where the base and two sides match.
|
|
|
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
|
|
|
struct SyncRegion {
|
|
|
|
base: Range<usize>,
|
|
|
|
left: Range<usize>,
|
|
|
|
right: Range<usize>,
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn merge(base: &[u8], left: &[u8], right: &[u8]) -> MergeResult {
|
2021-06-26 23:13:33 +00:00
|
|
|
let diff = Diff::for_tokenizer(&[base, left, right], &diff::find_line_ranges);
|
|
|
|
let mut resolved_hunk: Vec<u8> = vec![];
|
|
|
|
let mut merge_hunks: Vec<MergeHunk> = vec![];
|
|
|
|
for diff_hunk in diff.hunks() {
|
|
|
|
match diff_hunk {
|
|
|
|
DiffHunk::Matching(content) => {
|
|
|
|
resolved_hunk.extend(content);
|
|
|
|
}
|
|
|
|
DiffHunk::Different(content) => {
|
|
|
|
let base_content = content[0];
|
|
|
|
let left_content = content[1];
|
|
|
|
let right_content = content[2];
|
|
|
|
if left_content == base_content || left_content == right_content {
|
|
|
|
resolved_hunk.extend(right_content);
|
|
|
|
} else if right_content == base_content {
|
|
|
|
resolved_hunk.extend(left_content);
|
|
|
|
} else {
|
|
|
|
if !resolved_hunk.is_empty() {
|
|
|
|
merge_hunks.push(MergeHunk::Resolved(resolved_hunk));
|
|
|
|
resolved_hunk = vec![];
|
|
|
|
}
|
|
|
|
merge_hunks.push(MergeHunk::Conflict {
|
2021-06-30 15:57:49 +00:00
|
|
|
removes: vec![base_content.to_vec()],
|
|
|
|
adds: vec![left_content.to_vec(), right_content.to_vec()],
|
2021-06-26 23:13:33 +00:00
|
|
|
});
|
|
|
|
}
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-01-19 06:00:40 +00:00
|
|
|
|
2021-06-26 23:13:33 +00:00
|
|
|
if merge_hunks.is_empty() {
|
|
|
|
MergeResult::Resolved(resolved_hunk)
|
2020-12-12 08:00:42 +00:00
|
|
|
} else {
|
2021-06-26 23:13:33 +00:00
|
|
|
if !resolved_hunk.is_empty() {
|
|
|
|
merge_hunks.push(MergeHunk::Resolved(resolved_hunk));
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
2021-06-26 23:13:33 +00:00
|
|
|
MergeResult::Conflict(merge_hunks)
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_merge() {
|
|
|
|
assert_eq!(merge(b"", b"", b""), MergeResult::Resolved(b"".to_vec()));
|
|
|
|
assert_eq!(
|
|
|
|
merge(b"a", b"a", b"a"),
|
|
|
|
MergeResult::Resolved(b"a".to_vec())
|
|
|
|
);
|
|
|
|
assert_eq!(merge(b"a", b"", b"a"), MergeResult::Resolved(b"".to_vec()));
|
|
|
|
assert_eq!(merge(b"a", b"a", b""), MergeResult::Resolved(b"".to_vec()));
|
|
|
|
assert_eq!(merge(b"a", b"", b""), MergeResult::Resolved(b"".to_vec()));
|
|
|
|
assert_eq!(
|
|
|
|
merge(b"a", b"a b", b"a"),
|
|
|
|
MergeResult::Resolved(b"a b".to_vec())
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
merge(b"a", b"a", b"a b"),
|
|
|
|
MergeResult::Resolved(b"a b".to_vec())
|
|
|
|
);
|
|
|
|
assert_eq!(
|
2021-04-01 15:15:38 +00:00
|
|
|
merge(b"a\n", b"a\nb\n", b"a\nc\n"),
|
2020-12-12 08:00:42 +00:00
|
|
|
MergeResult::Conflict(vec![
|
2021-04-01 15:15:38 +00:00
|
|
|
MergeHunk::Resolved(b"a\n".to_vec()),
|
2020-12-12 08:00:42 +00:00
|
|
|
MergeHunk::Conflict {
|
2021-06-30 15:57:49 +00:00
|
|
|
removes: vec![b"".to_vec()],
|
|
|
|
adds: vec![b"b\n".to_vec(), b"c\n".to_vec()]
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
|
|
|
])
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
merge(b"a", b"b", b"a"),
|
|
|
|
MergeResult::Resolved(b"b".to_vec())
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
merge(b"a", b"a", b"b"),
|
|
|
|
MergeResult::Resolved(b"b".to_vec())
|
|
|
|
);
|
2021-01-19 06:00:40 +00:00
|
|
|
assert_eq!(
|
|
|
|
merge(b"a", b"", b"b"),
|
|
|
|
MergeResult::Conflict(vec![MergeHunk::Conflict {
|
2021-06-30 15:57:49 +00:00
|
|
|
removes: vec![b"a".to_vec()],
|
|
|
|
adds: vec![b"".to_vec(), b"b".to_vec()]
|
2021-01-19 06:00:40 +00:00
|
|
|
}])
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
merge(b"a", b"b", b""),
|
|
|
|
MergeResult::Conflict(vec![MergeHunk::Conflict {
|
2021-06-30 15:57:49 +00:00
|
|
|
removes: vec![b"a".to_vec()],
|
|
|
|
adds: vec![b"b".to_vec(), b"".to_vec()]
|
2021-01-19 06:00:40 +00:00
|
|
|
}])
|
|
|
|
);
|
2020-12-12 08:00:42 +00:00
|
|
|
assert_eq!(
|
|
|
|
merge(b"a", b"b", b"c"),
|
|
|
|
MergeResult::Conflict(vec![MergeHunk::Conflict {
|
2021-06-30 15:57:49 +00:00
|
|
|
removes: vec![b"a".to_vec()],
|
|
|
|
adds: vec![b"b".to_vec(), b"c".to_vec()]
|
2020-12-12 08:00:42 +00:00
|
|
|
}])
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|