diff --git a/lib/src/conflicts.rs b/lib/src/conflicts.rs index f4a5c6fcc..6ed19c5ba 100644 --- a/lib/src/conflicts.rs +++ b/lib/src/conflicts.rs @@ -19,7 +19,7 @@ use itertools::Itertools; use crate::backend::{BackendResult, Conflict, ConflictId, ConflictPart, TreeValue}; use crate::diff::{find_line_ranges, Diff, DiffHunk}; use crate::files; -use crate::files::{MergeHunk, MergeResult}; +use crate::files::{ConflictHunk, MergeHunk, MergeResult}; use crate::repo_path::RepoPath; use crate::store::Store; @@ -133,7 +133,7 @@ pub fn materialize_conflict( conflict: &Conflict, output: &mut dyn Write, ) -> std::io::Result<()> { - match extract_file_conflict_data(store, path, conflict) { + match extract_file_conflict_as_single_hunk(store, path, conflict) { None => { // Unless all parts are regular files, we can't do much better than to try to // describe the conflict. @@ -143,18 +143,12 @@ pub fn materialize_conflict( } } -/// The contents of every version of the file that participates in a conflict. -pub struct FileConflictData { - removes: Vec>, - adds: Vec>, -} - /// Only works if all parts of the conflict are regular, non-executable files -pub fn extract_file_conflict_data( +pub fn extract_file_conflict_as_single_hunk( store: &Store, path: &RepoPath, conflict: &Conflict, -) -> Option { +) -> Option { let file_adds = file_parts(&conflict.adds); let file_removes = file_parts(&conflict.removes); if file_adds.len() != conflict.adds.len() || file_removes.len() != conflict.removes.len() { @@ -169,14 +163,14 @@ pub fn extract_file_conflict_data( .map(|part| get_file_contents(store, path, part)) .collect_vec(); - Some(FileConflictData { + Some(ConflictHunk { removes: removed_content, adds: added_content, }) } pub fn materialize_merge_result( - single_hunk: &FileConflictData, + single_hunk: &ConflictHunk, output: &mut dyn Write, ) -> std::io::Result<()> { let removed_slices = single_hunk.removes.iter().map(Vec::as_slice).collect_vec(); @@ -192,10 +186,10 @@ pub fn materialize_merge_result( MergeHunk::Resolved(content) => { output.write_all(&content)?; } - MergeHunk::Conflict { + MergeHunk::Conflict(ConflictHunk { mut removes, mut adds, - } => { + }) => { output.write_all(CONFLICT_START_LINE)?; while !removes.is_empty() && !adds.is_empty() { let left = &removes[0]; @@ -279,7 +273,7 @@ pub fn parse_conflict(input: &[u8], num_removes: usize, num_adds: usize) -> Opti let conflict_body = &input[conflict_start.unwrap() + CONFLICT_START_LINE.len()..pos]; let hunk = parse_conflict_hunk(conflict_body); match &hunk { - MergeHunk::Conflict { removes, adds } + MergeHunk::Conflict(ConflictHunk { removes, adds }) if removes.len() == num_removes && adds.len() == num_adds => { let resolved_slice = &input[resolved_start..conflict_start.unwrap()]; @@ -363,7 +357,7 @@ fn parse_conflict_hunk(input: &[u8]) -> MergeHunk { } } - MergeHunk::Conflict { removes, adds } + MergeHunk::Conflict(ConflictHunk { removes, adds }) } /// Returns `None` if there are no conflict markers in `content`. @@ -399,7 +393,7 @@ pub fn update_conflict_from_content( buf.extend_from_slice(&slice); } } - MergeHunk::Conflict { removes, adds } => { + MergeHunk::Conflict(ConflictHunk { removes, adds }) => { for (i, buf) in removes.iter().enumerate() { removed_content[i].extend_from_slice(buf); } diff --git a/lib/src/files.rs b/lib/src/files.rs index bd3c865b7..724d0a969 100644 --- a/lib/src/files.rs +++ b/lib/src/files.rs @@ -141,13 +141,16 @@ impl<'a> Iterator for DiffLineIterator<'a> { } } +#[derive(PartialEq, Eq, Clone)] +pub struct ConflictHunk { + pub removes: Vec>, + pub adds: Vec>, +} + #[derive(PartialEq, Eq, Clone)] pub enum MergeHunk { Resolved(Vec), - Conflict { - removes: Vec>, - adds: Vec>, - }, + Conflict(ConflictHunk), } impl Debug for MergeHunk { @@ -157,7 +160,7 @@ impl Debug for MergeHunk { .debug_tuple("Resolved") .field(&String::from_utf8_lossy(data)) .finish(), - MergeHunk::Conflict { removes, adds } => f + MergeHunk::Conflict(ConflictHunk { removes, adds }) => f .debug_struct("Conflict") .field( "removes", @@ -267,7 +270,7 @@ pub fn merge(removes: &[&[u8]], adds: &[&[u8]]) -> MergeResult { } // Include the unfiltered lists of removed and added here, so the caller // knows which part corresponds to which input. - merge_hunks.push(MergeHunk::Conflict { + merge_hunks.push(MergeHunk::Conflict(ConflictHunk { removes: parts[..num_removes] .iter() .map(|part| part.to_vec()) @@ -276,7 +279,7 @@ pub fn merge(removes: &[&[u8]], adds: &[&[u8]]) -> MergeResult { .iter() .map(|part| part.to_vec()) .collect_vec(), - }); + })); } } } @@ -341,10 +344,10 @@ mod tests { // One side modified, two sides added assert_eq!( merge(&[b"a"], &[b"b", b"b", b"b"]), - MergeResult::Conflict(vec![MergeHunk::Conflict { + MergeResult::Conflict(vec![MergeHunk::Conflict(ConflictHunk { removes: vec![b"a".to_vec()], adds: vec![b"b".to_vec(), b"b".to_vec(), b"b".to_vec()] - }]) + })]) ); // All sides removed same content assert_eq!( @@ -354,10 +357,10 @@ mod tests { // One side modified, two sides removed assert_eq!( merge(&[b"a\n", b"a\n", b"a\n"], &[b""]), - MergeResult::Conflict(vec![MergeHunk::Conflict { + MergeResult::Conflict(vec![MergeHunk::Conflict(ConflictHunk { removes: vec![b"a\n".to_vec(), b"a\n".to_vec(), b"a\n".to_vec()], adds: vec![b"".to_vec()] - }]) + })]) ); // Three sides made the same change assert_eq!( @@ -367,45 +370,45 @@ mod tests { // One side unchanged, one side added assert_eq!( merge(&[b"a\n"], &[b"a\nb\n"]), - MergeResult::Conflict(vec![MergeHunk::Conflict { + MergeResult::Conflict(vec![MergeHunk::Conflict(ConflictHunk { removes: vec![b"".to_vec()], adds: vec![b"b\n".to_vec()] - }]) + })]) ); // Two sides left one line unchanged, and added conflicting additional lines assert_eq!( merge(&[b"a\n"], &[b"a\nb\n", b"a\nc\n"]), MergeResult::Conflict(vec![ MergeHunk::Resolved(b"a\n".to_vec()), - MergeHunk::Conflict { + MergeHunk::Conflict(ConflictHunk { removes: vec![b"".to_vec()], adds: vec![b"b\n".to_vec(), b"c\n".to_vec()] - } + }) ]) ); // One side removed, one side modified assert_eq!( merge(&[b"a\n"], &[b"", b"b\n"]), - MergeResult::Conflict(vec![MergeHunk::Conflict { + MergeResult::Conflict(vec![MergeHunk::Conflict(ConflictHunk { removes: vec![b"a\n".to_vec()], adds: vec![b"".to_vec(), b"b\n".to_vec()] - }]) + })]) ); // One side modified, one side removed assert_eq!( merge(&[b"a\n"], &[b"b\n", b""]), - MergeResult::Conflict(vec![MergeHunk::Conflict { + MergeResult::Conflict(vec![MergeHunk::Conflict(ConflictHunk { removes: vec![b"a\n".to_vec()], adds: vec![b"b\n".to_vec(), b"".to_vec()] - }]) + })]) ); // Two sides modified in different ways assert_eq!( merge(&[b"a"], &[b"b", b"c"]), - MergeResult::Conflict(vec![MergeHunk::Conflict { + MergeResult::Conflict(vec![MergeHunk::Conflict(ConflictHunk { removes: vec![b"a".to_vec()], adds: vec![b"b".to_vec(), b"c".to_vec()] - }]) + })]) ); // Two of three sides don't change, third side changes assert_eq!( @@ -420,10 +423,10 @@ mod tests { // One side unchanged, two other sides make the different change assert_eq!( merge(&[b"a", b"a"], &[b"b", b"a", b"c"]), - MergeResult::Conflict(vec![MergeHunk::Conflict { + MergeResult::Conflict(vec![MergeHunk::Conflict(ConflictHunk { removes: vec![b"a".to_vec(), b"a".to_vec()], adds: vec![b"b".to_vec(), b"a".to_vec(), b"c".to_vec()] - }]) + })]) ); // Merge of an unresolved conflict and another branch, where the other branch // undid the change from one of the inputs to the unresolved conflict in the @@ -435,10 +438,10 @@ mod tests { // Merge of an unresolved conflict and another branch. assert_eq!( merge(&[b"a", b"b"], &[b"c", b"d", b"e"]), - MergeResult::Conflict(vec![MergeHunk::Conflict { + MergeResult::Conflict(vec![MergeHunk::Conflict(ConflictHunk { removes: vec![b"a".to_vec(), b"b".to_vec()], adds: vec![b"c".to_vec(), b"d".to_vec(), b"e".to_vec()] - }]) + })]) ); } } diff --git a/lib/tests/test_conflicts.rs b/lib/tests/test_conflicts.rs index 64a93be19..96759bf3f 100644 --- a/lib/tests/test_conflicts.rs +++ b/lib/tests/test_conflicts.rs @@ -14,7 +14,7 @@ use jujutsu_lib::backend::{Conflict, ConflictPart, TreeValue}; use jujutsu_lib::conflicts::{materialize_conflict, parse_conflict, update_conflict_from_content}; -use jujutsu_lib::files::MergeHunk; +use jujutsu_lib::files::{ConflictHunk, MergeHunk}; use jujutsu_lib::repo_path::RepoPath; use jujutsu_lib::store::Store; use testutils::TestRepo; @@ -307,10 +307,10 @@ line 5 ), Some(vec![ MergeHunk::Resolved(b"line 1\n".to_vec()), - MergeHunk::Conflict { + MergeHunk::Conflict(ConflictHunk { removes: vec![b"line 2\nline 3\nline 4\n".to_vec()], adds: vec![b"line 2\nleft\nline 4\n".to_vec(), b"right\n".to_vec()] - }, + }), MergeHunk::Resolved(b"line 5\n".to_vec()) ]) ) @@ -342,7 +342,7 @@ line 5 ), Some(vec![ MergeHunk::Resolved(b"line 1\n".to_vec()), - MergeHunk::Conflict { + MergeHunk::Conflict(ConflictHunk { removes: vec![ b"line 2\nline 3\nline 4\n".to_vec(), b"line 2\nline 3\nline 4\n".to_vec() @@ -352,7 +352,7 @@ line 5 b"right\n".to_vec(), b"line 2\nforward\nline 3\nline 4\n".to_vec() ] - }, + }), MergeHunk::Resolved(b"line 5\n".to_vec()) ]) )