forked from mirrors/jj
conflicts: rename Conflict<T>
to Merge<T>
Since `Conflict<T>` can also represent a non-conflict state (a single term), `Merge<T>` seems like better name. Thanks to @ilyagr for the suggestion in https://github.com/martinvonz/jj/pull/1774#discussion_r1257547709 Sorry about the churn. It would have been better if I thought of this name before I introduced `Conflict<T>`.
This commit is contained in:
parent
d858db7e85
commit
ecc030848d
18 changed files with 269 additions and 274 deletions
|
@ -33,7 +33,7 @@ use indexmap::{IndexMap, IndexSet};
|
|||
use itertools::Itertools;
|
||||
use jj_lib::backend::{CommitId, ObjectId, TreeValue};
|
||||
use jj_lib::commit::Commit;
|
||||
use jj_lib::conflicts::Conflict;
|
||||
use jj_lib::conflicts::Merge;
|
||||
use jj_lib::dag_walk::topo_order_reverse;
|
||||
use jj_lib::git_backend::GitBackend;
|
||||
use jj_lib::matchers::EverythingMatcher;
|
||||
|
@ -2703,7 +2703,7 @@ fn cmd_chmod(ui: &mut Ui, command: &CommandHelper, args: &ChmodArgs) -> Result<(
|
|||
));
|
||||
}
|
||||
let new_conflict_id =
|
||||
store.write_conflict(&repo_path, &Conflict::new(new_removes, new_adds))?;
|
||||
store.write_conflict(&repo_path, &Merge::new(new_removes, new_adds))?;
|
||||
TreeValue::Conflict(new_conflict_id)
|
||||
}
|
||||
Some(_) => return Err(user_error_with_path("Found neither a file nor a conflict")),
|
||||
|
@ -2805,7 +2805,7 @@ fn cmd_resolve(
|
|||
|
||||
#[instrument(skip_all)]
|
||||
fn print_conflicted_paths(
|
||||
conflicts: &[(RepoPath, Conflict<Option<TreeValue>>)],
|
||||
conflicts: &[(RepoPath, Merge<Option<TreeValue>>)],
|
||||
formatter: &mut dyn Formatter,
|
||||
workspace_command: &WorkspaceCommandHelper,
|
||||
) -> Result<(), CommandError> {
|
||||
|
|
|
@ -244,7 +244,7 @@ pub fn run_mergetool(
|
|||
None => return Err(ConflictResolveError::PathNotFound(repo_path.clone())),
|
||||
};
|
||||
let conflict = tree.store().read_conflict(repo_path, &conflict_id)?;
|
||||
let file_conflict = conflict.to_file_conflict().ok_or_else(|| {
|
||||
let file_merge = conflict.to_file_merge().ok_or_else(|| {
|
||||
let mut summary_bytes: Vec<u8> = vec![];
|
||||
conflict
|
||||
.describe(&mut summary_bytes)
|
||||
|
@ -255,13 +255,13 @@ pub fn run_mergetool(
|
|||
)
|
||||
})?;
|
||||
// We only support conflicts with 2 sides (3-way conflicts)
|
||||
if file_conflict.adds().len() > 2 {
|
||||
if file_merge.adds().len() > 2 {
|
||||
return Err(ConflictResolveError::ConflictTooComplicated {
|
||||
path: repo_path.clone(),
|
||||
sides: file_conflict.adds().len(),
|
||||
sides: file_merge.adds().len(),
|
||||
});
|
||||
};
|
||||
let content = file_conflict.extract_as_single_hunk(tree.store(), repo_path);
|
||||
let content = file_merge.extract_as_single_hunk(tree.store(), repo_path);
|
||||
|
||||
let editor = get_merge_tool_from_settings(ui, settings)?;
|
||||
let initial_output_content: Vec<u8> = if editor.merge_tool_edits_conflict_markers {
|
||||
|
|
|
@ -23,7 +23,7 @@ use std::vec::Vec;
|
|||
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::conflicts;
|
||||
use crate::conflicts::Merge;
|
||||
use crate::content_hash::ContentHash;
|
||||
use crate::repo_path::{RepoPath, RepoPathComponent};
|
||||
|
||||
|
@ -148,7 +148,7 @@ content_hash! {
|
|||
pub struct Commit {
|
||||
pub parents: Vec<CommitId>,
|
||||
pub predecessors: Vec<CommitId>,
|
||||
pub root_tree: conflicts::Conflict<TreeId>,
|
||||
pub root_tree: Merge<TreeId>,
|
||||
/// Indicates that there this commit uses the new tree-level conflict format, which means
|
||||
/// that if `root_tree` is not a conflict, we know that we won't have to walk it to
|
||||
/// determine if there are conflicts.
|
||||
|
@ -397,7 +397,7 @@ pub fn make_root_commit(root_change_id: ChangeId, empty_tree_id: TreeId) -> Comm
|
|||
Commit {
|
||||
parents: vec![],
|
||||
predecessors: vec![],
|
||||
root_tree: conflicts::Conflict::resolved(empty_tree_id),
|
||||
root_tree: Merge::resolved(empty_tree_id),
|
||||
uses_tree_conflict_format: false,
|
||||
change_id: root_change_id,
|
||||
description: String::new(),
|
||||
|
|
|
@ -18,7 +18,7 @@ use std::sync::Arc;
|
|||
|
||||
use crate::backend::{self, BackendResult, ChangeId, CommitId, Signature, TreeId};
|
||||
use crate::commit::Commit;
|
||||
use crate::conflicts::Conflict;
|
||||
use crate::conflicts::Merge;
|
||||
use crate::repo::{MutableRepo, Repo};
|
||||
use crate::settings::{JJRng, UserSettings};
|
||||
|
||||
|
@ -45,7 +45,7 @@ impl CommitBuilder<'_> {
|
|||
parents,
|
||||
predecessors: vec![],
|
||||
// TODO(#1624): set this when appropriate
|
||||
root_tree: Conflict::from_legacy_tree_id(tree_id),
|
||||
root_tree: Merge::from_legacy_tree_id(tree_id),
|
||||
uses_tree_conflict_format: false,
|
||||
change_id,
|
||||
description: String::new(),
|
||||
|
@ -108,7 +108,7 @@ impl CommitBuilder<'_> {
|
|||
}
|
||||
|
||||
pub fn set_tree(mut self, tree_id: TreeId) -> Self {
|
||||
self.commit.root_tree = Conflict::from_legacy_tree_id(tree_id);
|
||||
self.commit.root_tree = Merge::from_legacy_tree_id(tree_id);
|
||||
self
|
||||
}
|
||||
|
||||
|
|
|
@ -37,34 +37,34 @@ const CONFLICT_DIFF_LINE: &[u8] = b"%%%%%%%\n";
|
|||
const CONFLICT_MINUS_LINE: &[u8] = b"-------\n";
|
||||
const CONFLICT_PLUS_LINE: &[u8] = b"+++++++\n";
|
||||
|
||||
/// A generic representation of conflicting values.
|
||||
/// A generic representation of merged values.
|
||||
///
|
||||
/// There is exactly one more `adds()` than `removes()`. When interpreted as a
|
||||
/// series of diffs, the conflict's (i+1)-st add is matched with the i-th
|
||||
/// series of diffs, the merge's (i+1)-st add is matched with the i-th
|
||||
/// remove. The zeroth add is considered a diff from the non-existent state.
|
||||
#[derive(PartialEq, Eq, Hash, Clone, Debug)]
|
||||
pub struct Conflict<T> {
|
||||
pub struct Merge<T> {
|
||||
removes: Vec<T>,
|
||||
adds: Vec<T>,
|
||||
}
|
||||
|
||||
impl<T> Conflict<T> {
|
||||
impl<T> Merge<T> {
|
||||
pub fn new(removes: Vec<T>, adds: Vec<T>) -> Self {
|
||||
assert_eq!(adds.len(), removes.len() + 1);
|
||||
Conflict { removes, adds }
|
||||
Merge { removes, adds }
|
||||
}
|
||||
|
||||
/// Creates a `Conflict` with a single resolved value.
|
||||
/// Creates a `Merge` with a single resolved value.
|
||||
pub fn resolved(value: T) -> Self {
|
||||
Conflict::new(vec![], vec![value])
|
||||
Merge::new(vec![], vec![value])
|
||||
}
|
||||
|
||||
/// Create a `Conflict` from a `removes` and `adds`, padding with `None` to
|
||||
/// Create a `Merge` from a `removes` and `adds`, padding with `None` to
|
||||
/// make sure that there is exactly one more `adds` than `removes`.
|
||||
pub fn from_legacy_form(
|
||||
removes: impl IntoIterator<Item = T>,
|
||||
adds: impl IntoIterator<Item = T>,
|
||||
) -> Conflict<Option<T>> {
|
||||
) -> Merge<Option<T>> {
|
||||
let mut removes = removes.into_iter().map(Some).collect_vec();
|
||||
let mut adds = adds.into_iter().map(Some).collect_vec();
|
||||
while removes.len() + 1 < adds.len() {
|
||||
|
@ -73,7 +73,7 @@ impl<T> Conflict<T> {
|
|||
while adds.len() < removes.len() + 1 {
|
||||
adds.push(None);
|
||||
}
|
||||
Conflict::new(removes, adds)
|
||||
Merge::new(removes, adds)
|
||||
}
|
||||
|
||||
/// Returns the removes and adds as a pair.
|
||||
|
@ -89,13 +89,13 @@ impl<T> Conflict<T> {
|
|||
&self.adds
|
||||
}
|
||||
|
||||
/// Whether this conflict is resolved. Does not resolve trivial conflicts.
|
||||
/// Whether this merge is resolved. Does not resolve trivial merges.
|
||||
pub fn is_resolved(&self) -> bool {
|
||||
self.removes.is_empty()
|
||||
}
|
||||
|
||||
/// Returns the resolved value, if this conflict is resolved. Does not
|
||||
/// resolve trivial conflicts.
|
||||
/// Returns the resolved value, if this merge is resolved. Does not
|
||||
/// resolve trivial merges.
|
||||
pub fn as_resolved(&self) -> Option<&T> {
|
||||
if let [value] = &self.adds[..] {
|
||||
Some(value)
|
||||
|
@ -104,7 +104,7 @@ impl<T> Conflict<T> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Simplify the conflict by joining diffs like A->B and B->C into A->C.
|
||||
/// Simplify the merge by joining diffs like A->B and B->C into A->C.
|
||||
/// Also drops trivial diffs like A->A.
|
||||
pub fn simplify(mut self) -> Self
|
||||
where
|
||||
|
@ -133,38 +133,35 @@ impl<T> Conflict<T> {
|
|||
trivial_merge(&self.removes, &self.adds)
|
||||
}
|
||||
|
||||
/// Creates a new conflict by applying `f` to each remove and add.
|
||||
pub fn map<'a, U>(&'a self, mut f: impl FnMut(&'a T) -> U) -> Conflict<U> {
|
||||
/// Creates a new merge by applying `f` to each remove and add.
|
||||
pub fn map<'a, U>(&'a self, mut f: impl FnMut(&'a T) -> U) -> Merge<U> {
|
||||
self.maybe_map(|term| Some(f(term))).unwrap()
|
||||
}
|
||||
|
||||
/// Creates a new conflict by applying `f` to each remove and add, returning
|
||||
/// Creates a new merge by applying `f` to each remove and add, returning
|
||||
/// `None if `f` returns `None` for any of them.
|
||||
pub fn maybe_map<'a, U>(
|
||||
&'a self,
|
||||
mut f: impl FnMut(&'a T) -> Option<U>,
|
||||
) -> Option<Conflict<U>> {
|
||||
pub fn maybe_map<'a, U>(&'a self, mut f: impl FnMut(&'a T) -> Option<U>) -> Option<Merge<U>> {
|
||||
let removes = self.removes.iter().map(&mut f).collect::<Option<_>>()?;
|
||||
let adds = self.adds.iter().map(&mut f).collect::<Option<_>>()?;
|
||||
Some(Conflict { removes, adds })
|
||||
Some(Merge { removes, adds })
|
||||
}
|
||||
|
||||
/// Creates a new conflict by applying `f` to each remove and add, returning
|
||||
/// Creates a new merge by applying `f` to each remove and add, returning
|
||||
/// `Err if `f` returns `Err` for any of them.
|
||||
pub fn try_map<'a, U, E>(
|
||||
&'a self,
|
||||
mut f: impl FnMut(&'a T) -> Result<U, E>,
|
||||
) -> Result<Conflict<U>, E> {
|
||||
) -> Result<Merge<U>, E> {
|
||||
let removes = self.removes.iter().map(&mut f).try_collect()?;
|
||||
let adds = self.adds.iter().map(&mut f).try_collect()?;
|
||||
Ok(Conflict { removes, adds })
|
||||
Ok(Merge { removes, adds })
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Conflict<Option<T>> {
|
||||
/// Creates lists of `removes` and `adds` from a `Conflict` by dropping
|
||||
impl<T> Merge<Option<T>> {
|
||||
/// Creates lists of `removes` and `adds` from a `Merge` by dropping
|
||||
/// `None` values. Note that the conversion is lossy: the order of `None`
|
||||
/// values is not preserved when converting back to a `Conflict`.
|
||||
/// values is not preserved when converting back to a `Merge`.
|
||||
pub fn into_legacy_form(self) -> (Vec<T>, Vec<T>) {
|
||||
(
|
||||
self.removes.into_iter().flatten().collect(),
|
||||
|
@ -173,8 +170,8 @@ impl<T> Conflict<Option<T>> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T> Conflict<Conflict<T>> {
|
||||
/// Flattens a nested conflict into a regular conflict.
|
||||
impl<T> Merge<Merge<T>> {
|
||||
/// Flattens a nested merge into a regular merge.
|
||||
///
|
||||
/// Let's say we have a 3-way merge of 3-way merges like this:
|
||||
///
|
||||
|
@ -183,11 +180,11 @@ impl<T> Conflict<Conflict<T>> {
|
|||
/// 1 2
|
||||
/// 0
|
||||
///
|
||||
/// Flattening that results in this 9-way conflict:
|
||||
/// Flattening that results in this 9-way merge:
|
||||
///
|
||||
/// 4 5 0 7 8
|
||||
/// 3 2 1 6
|
||||
pub fn flatten(mut self) -> Conflict<T> {
|
||||
pub fn flatten(mut self) -> Merge<T> {
|
||||
self.removes.reverse();
|
||||
self.adds.reverse();
|
||||
let mut result = self.adds.pop().unwrap();
|
||||
|
@ -207,20 +204,20 @@ impl<T> Conflict<Conflict<T>> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: ContentHash> ContentHash for Conflict<T> {
|
||||
impl<T: ContentHash> ContentHash for Merge<T> {
|
||||
fn hash(&self, state: &mut impl digest::Update) {
|
||||
self.removes().hash(state);
|
||||
self.adds().hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl Conflict<TreeId> {
|
||||
// Creates a resolved conflict for a legacy tree id (same as
|
||||
// `Conflict::resolved()`).
|
||||
impl Merge<TreeId> {
|
||||
// Creates a resolved merge for a legacy tree id (same as
|
||||
// `Merge::resolved()`).
|
||||
// TODO(#1624): delete when all callers have been updated to support tree-level
|
||||
// conflicts
|
||||
pub fn from_legacy_tree_id(value: TreeId) -> Self {
|
||||
Conflict {
|
||||
Merge {
|
||||
removes: vec![],
|
||||
adds: vec![value],
|
||||
}
|
||||
|
@ -233,18 +230,18 @@ impl Conflict<TreeId> {
|
|||
}
|
||||
}
|
||||
|
||||
impl Conflict<Option<TreeValue>> {
|
||||
/// Create a `Conflict` from a `backend::Conflict`, padding with `None` to
|
||||
impl Merge<Option<TreeValue>> {
|
||||
/// Create a `Merge` from a `backend::Conflict`, padding with `None` to
|
||||
/// make sure that there is exactly one more `adds()` than `removes()`.
|
||||
pub fn from_backend_conflict(conflict: backend::Conflict) -> Self {
|
||||
let removes = conflict.removes.into_iter().map(|term| term.value);
|
||||
let adds = conflict.adds.into_iter().map(|term| term.value);
|
||||
Conflict::from_legacy_form(removes, adds)
|
||||
Merge::from_legacy_form(removes, adds)
|
||||
}
|
||||
|
||||
/// Creates a `backend::Conflict` from a `Conflict` by dropping `None`
|
||||
/// Creates a `backend::Conflict` from a `Merge` by dropping `None`
|
||||
/// values. Note that the conversion is lossy: the order of `None` values is
|
||||
/// not preserved when converting back to a `Conflict`.
|
||||
/// not preserved when converting back to a `Merge`.
|
||||
pub fn into_backend_conflict(self) -> backend::Conflict {
|
||||
let (removes, adds) = self.into_legacy_form();
|
||||
let removes = removes
|
||||
|
@ -264,17 +261,17 @@ impl Conflict<Option<TreeValue>> {
|
|||
path: &RepoPath,
|
||||
output: &mut dyn Write,
|
||||
) -> std::io::Result<()> {
|
||||
if let Some(file_conflict) = self.to_file_conflict() {
|
||||
let content = file_conflict.extract_as_single_hunk(store, path);
|
||||
if let Some(file_merge) = self.to_file_merge() {
|
||||
let content = file_merge.extract_as_single_hunk(store, path);
|
||||
materialize_merge_result(&content, output)
|
||||
} else {
|
||||
// Unless all terms are regular files, we can't do much better than to try to
|
||||
// describe the conflict.
|
||||
// describe the merge.
|
||||
self.describe(output)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_file_conflict(&self) -> Option<Conflict<Option<FileId>>> {
|
||||
pub fn to_file_merge(&self) -> Option<Merge<Option<FileId>>> {
|
||||
self.maybe_map(|term| match term {
|
||||
None => Some(None),
|
||||
Some(TreeValue::File {
|
||||
|
@ -303,9 +300,9 @@ impl Conflict<Option<TreeValue>> {
|
|||
store: &Store,
|
||||
path: &RepoPath,
|
||||
content: &[u8],
|
||||
) -> BackendResult<Option<Conflict<Option<TreeValue>>>> {
|
||||
) -> BackendResult<Option<Merge<Option<TreeValue>>>> {
|
||||
// TODO: Check that the conflict only involves files and convert it to a
|
||||
// `Conflict<Option<FileId>>` so we can remove the wildcard pattern in the loops
|
||||
// `Merge<Option<FileId>>` so we can remove the wildcard pattern in the loops
|
||||
// further down.
|
||||
|
||||
// First check if the new content is unchanged compared to the old content. If
|
||||
|
@ -344,7 +341,7 @@ impl Conflict<Option<TreeValue>> {
|
|||
}
|
||||
}
|
||||
// Now write the new files contents we found by parsing the file
|
||||
// with conflict markers. Update the Conflict object with the new
|
||||
// with conflict markers. Update the Merge object with the new
|
||||
// FileIds.
|
||||
let mut new_removes = vec![];
|
||||
for (i, buf) in removed_content.iter().enumerate() {
|
||||
|
@ -392,30 +389,30 @@ impl Conflict<Option<TreeValue>> {
|
|||
}
|
||||
}
|
||||
}
|
||||
Ok(Some(Conflict::new(new_removes, new_adds)))
|
||||
Ok(Some(Merge::new(new_removes, new_adds)))
|
||||
}
|
||||
}
|
||||
|
||||
impl Conflict<Option<FileId>> {
|
||||
pub fn extract_as_single_hunk(&self, store: &Store, path: &RepoPath) -> Conflict<ContentHunk> {
|
||||
impl Merge<Option<FileId>> {
|
||||
pub fn extract_as_single_hunk(&self, store: &Store, path: &RepoPath) -> Merge<ContentHunk> {
|
||||
self.map(|term| get_file_contents(store, path, term))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Conflict<Option<T>>
|
||||
impl<T> Merge<Option<T>>
|
||||
where
|
||||
T: Borrow<TreeValue>,
|
||||
{
|
||||
/// If every non-`None` term of a `Conflict<Option<TreeValue>>`
|
||||
/// If every non-`None` term of a `Merge<Option<TreeValue>>`
|
||||
/// is a `TreeValue::Tree`, this converts it to
|
||||
/// a `Conflict<Tree>`, with empty trees instead of
|
||||
/// a `Merge<Tree>`, with empty trees instead of
|
||||
/// any `None` terms. Otherwise, returns `None`.
|
||||
pub fn to_tree_conflict(
|
||||
pub fn to_tree_merge(
|
||||
&self,
|
||||
store: &Arc<Store>,
|
||||
dir: &RepoPath,
|
||||
) -> Result<Option<Conflict<Tree>>, BackendError> {
|
||||
let tree_id_conflict = self.maybe_map(|term| match term {
|
||||
) -> Result<Option<Merge<Tree>>, BackendError> {
|
||||
let tree_id_merge = self.maybe_map(|term| match term {
|
||||
None => Some(None),
|
||||
Some(value) => {
|
||||
if let TreeValue::Tree(id) = value.borrow() {
|
||||
|
@ -425,7 +422,7 @@ where
|
|||
}
|
||||
}
|
||||
});
|
||||
if let Some(tree_id_conflict) = tree_id_conflict {
|
||||
if let Some(tree_id_merge) = tree_id_merge {
|
||||
let get_tree = |id: &Option<&TreeId>| -> Result<Tree, BackendError> {
|
||||
if let Some(id) = id {
|
||||
store.get_tree(dir, id)
|
||||
|
@ -433,7 +430,7 @@ where
|
|||
Ok(Tree::null(store.clone(), dir.clone()))
|
||||
}
|
||||
};
|
||||
Ok(Some(tree_id_conflict.try_map(get_tree)?))
|
||||
Ok(Some(tree_id_merge.try_map(get_tree)?))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
|
@ -511,7 +508,7 @@ fn write_diff_hunks(hunks: &[DiffHunk], file: &mut dyn Write) -> std::io::Result
|
|||
}
|
||||
|
||||
pub fn materialize_merge_result(
|
||||
single_hunk: &Conflict<ContentHunk>,
|
||||
single_hunk: &Merge<ContentHunk>,
|
||||
output: &mut dyn Write,
|
||||
) -> std::io::Result<()> {
|
||||
let removed_slices = single_hunk
|
||||
|
@ -608,7 +605,7 @@ pub fn parse_conflict(
|
|||
input: &[u8],
|
||||
num_removes: usize,
|
||||
num_adds: usize,
|
||||
) -> Option<Vec<Conflict<ContentHunk>>> {
|
||||
) -> Option<Vec<Merge<ContentHunk>>> {
|
||||
if input.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
@ -625,7 +622,7 @@ pub fn parse_conflict(
|
|||
if hunk.removes().len() == num_removes && hunk.adds().len() == num_adds {
|
||||
let resolved_slice = &input[resolved_start..conflict_start.unwrap()];
|
||||
if !resolved_slice.is_empty() {
|
||||
hunks.push(Conflict::resolved(ContentHunk(resolved_slice.to_vec())));
|
||||
hunks.push(Merge::resolved(ContentHunk(resolved_slice.to_vec())));
|
||||
}
|
||||
hunks.push(hunk);
|
||||
resolved_start = pos + line.len();
|
||||
|
@ -639,7 +636,7 @@ pub fn parse_conflict(
|
|||
None
|
||||
} else {
|
||||
if resolved_start < input.len() {
|
||||
hunks.push(Conflict::resolved(ContentHunk(
|
||||
hunks.push(Merge::resolved(ContentHunk(
|
||||
input[resolved_start..].to_vec(),
|
||||
)));
|
||||
}
|
||||
|
@ -647,7 +644,7 @@ pub fn parse_conflict(
|
|||
}
|
||||
}
|
||||
|
||||
fn parse_conflict_hunk(input: &[u8]) -> Conflict<ContentHunk> {
|
||||
fn parse_conflict_hunk(input: &[u8]) -> Merge<ContentHunk> {
|
||||
enum State {
|
||||
Diff,
|
||||
Minus,
|
||||
|
@ -688,7 +685,7 @@ fn parse_conflict_hunk(input: &[u8]) -> Conflict<ContentHunk> {
|
|||
adds.last_mut().unwrap().0.extend_from_slice(rest);
|
||||
} else {
|
||||
// Doesn't look like a conflict
|
||||
return Conflict::resolved(ContentHunk(vec![]));
|
||||
return Merge::resolved(ContentHunk(vec![]));
|
||||
}
|
||||
}
|
||||
State::Minus => {
|
||||
|
@ -699,81 +696,78 @@ fn parse_conflict_hunk(input: &[u8]) -> Conflict<ContentHunk> {
|
|||
}
|
||||
State::Unknown => {
|
||||
// Doesn't look like a conflict
|
||||
return Conflict::resolved(ContentHunk(vec![]));
|
||||
return Merge::resolved(ContentHunk(vec![]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Conflict::new(removes, adds)
|
||||
Merge::new(removes, adds)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn c<T: Clone>(removes: &[T], adds: &[T]) -> Conflict<T> {
|
||||
Conflict::new(removes.to_vec(), adds.to_vec())
|
||||
fn c<T: Clone>(removes: &[T], adds: &[T]) -> Merge<T> {
|
||||
Merge::new(removes.to_vec(), adds.to_vec())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_legacy_form_conversion() {
|
||||
fn test_equivalent<T>(legacy_form: (Vec<T>, Vec<T>), conflict: Conflict<Option<T>>)
|
||||
fn test_equivalent<T>(legacy_form: (Vec<T>, Vec<T>), merge: Merge<Option<T>>)
|
||||
where
|
||||
T: Clone + PartialEq + std::fmt::Debug,
|
||||
{
|
||||
assert_eq!(conflict.clone().into_legacy_form(), legacy_form);
|
||||
assert_eq!(
|
||||
Conflict::from_legacy_form(legacy_form.0, legacy_form.1),
|
||||
conflict
|
||||
);
|
||||
assert_eq!(merge.clone().into_legacy_form(), legacy_form);
|
||||
assert_eq!(Merge::from_legacy_form(legacy_form.0, legacy_form.1), merge);
|
||||
}
|
||||
// Non-conflict
|
||||
test_equivalent((vec![], vec![0]), Conflict::new(vec![], vec![Some(0)]));
|
||||
test_equivalent((vec![], vec![0]), Merge::new(vec![], vec![Some(0)]));
|
||||
// Regular 3-way conflict
|
||||
test_equivalent(
|
||||
(vec![0], vec![1, 2]),
|
||||
Conflict::new(vec![Some(0)], vec![Some(1), Some(2)]),
|
||||
Merge::new(vec![Some(0)], vec![Some(1), Some(2)]),
|
||||
);
|
||||
// Modify/delete conflict
|
||||
test_equivalent(
|
||||
(vec![0], vec![1]),
|
||||
Conflict::new(vec![Some(0)], vec![Some(1), None]),
|
||||
Merge::new(vec![Some(0)], vec![Some(1), None]),
|
||||
);
|
||||
// Add/add conflict
|
||||
test_equivalent(
|
||||
(vec![], vec![0, 1]),
|
||||
Conflict::new(vec![None], vec![Some(0), Some(1)]),
|
||||
Merge::new(vec![None], vec![Some(0), Some(1)]),
|
||||
);
|
||||
// 5-way conflict
|
||||
test_equivalent(
|
||||
(vec![0, 1], vec![2, 3, 4]),
|
||||
Conflict::new(vec![Some(0), Some(1)], vec![Some(2), Some(3), Some(4)]),
|
||||
Merge::new(vec![Some(0), Some(1)], vec![Some(2), Some(3), Some(4)]),
|
||||
);
|
||||
// 5-way delete/delete conflict
|
||||
test_equivalent(
|
||||
(vec![0, 1], vec![]),
|
||||
Conflict::new(vec![Some(0), Some(1)], vec![None, None, None]),
|
||||
Merge::new(vec![Some(0), Some(1)], vec![None, None, None]),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_as_resolved() {
|
||||
assert_eq!(Conflict::new(vec![], vec![0]).as_resolved(), Some(&0));
|
||||
// Even a trivially resolvable conflict is not resolved
|
||||
assert_eq!(Conflict::new(vec![0], vec![0, 1]).as_resolved(), None);
|
||||
assert_eq!(Merge::new(vec![], vec![0]).as_resolved(), Some(&0));
|
||||
// Even a trivially resolvable merge is not resolved
|
||||
assert_eq!(Merge::new(vec![0], vec![0, 1]).as_resolved(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_simplify() {
|
||||
// 1-way "conflict"
|
||||
// 1-way merge
|
||||
assert_eq!(c(&[], &[0]).simplify(), c(&[], &[0]));
|
||||
// 3-way conflict
|
||||
// 3-way merge
|
||||
assert_eq!(c(&[0], &[0, 0]).simplify(), c(&[], &[0]));
|
||||
assert_eq!(c(&[0], &[0, 1]).simplify(), c(&[], &[1]));
|
||||
assert_eq!(c(&[0], &[1, 0]).simplify(), c(&[], &[1]));
|
||||
assert_eq!(c(&[0], &[1, 1]).simplify(), c(&[0], &[1, 1]));
|
||||
assert_eq!(c(&[0], &[1, 2]).simplify(), c(&[0], &[1, 2]));
|
||||
// 5-way conflict
|
||||
// 5-way merge
|
||||
assert_eq!(c(&[0, 0], &[0, 0, 0]).simplify(), c(&[], &[0]));
|
||||
assert_eq!(c(&[0, 0], &[0, 0, 1]).simplify(), c(&[], &[1]));
|
||||
assert_eq!(c(&[0, 0], &[0, 1, 0]).simplify(), c(&[], &[1]));
|
||||
|
@ -833,31 +827,31 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn test_conflict_invariants() {
|
||||
fn test_merge_invariants() {
|
||||
fn check_invariants(removes: &[u32], adds: &[u32]) {
|
||||
let conflict = Conflict::new(removes.to_vec(), adds.to_vec());
|
||||
let merge = Merge::new(removes.to_vec(), adds.to_vec());
|
||||
// `simplify()` is idempotent
|
||||
assert_eq!(
|
||||
conflict.clone().simplify().simplify(),
|
||||
conflict.clone().simplify(),
|
||||
"simplify() not idempotent for {conflict:?}"
|
||||
merge.clone().simplify().simplify(),
|
||||
merge.clone().simplify(),
|
||||
"simplify() not idempotent for {merge:?}"
|
||||
);
|
||||
// `resolve_trivial()` is unaffected by `simplify()`
|
||||
assert_eq!(
|
||||
conflict.clone().simplify().resolve_trivial(),
|
||||
conflict.resolve_trivial(),
|
||||
"simplify() changed result of resolve_trivial() for {conflict:?}"
|
||||
merge.clone().simplify().resolve_trivial(),
|
||||
merge.resolve_trivial(),
|
||||
"simplify() changed result of resolve_trivial() for {merge:?}"
|
||||
);
|
||||
}
|
||||
// 1-way "conflict"
|
||||
// 1-way merge
|
||||
check_invariants(&[], &[0]);
|
||||
for i in 0..=1 {
|
||||
for j in 0..=i + 1 {
|
||||
// 3-way conflict
|
||||
// 3-way merge
|
||||
check_invariants(&[0], &[i, j]);
|
||||
for k in 0..=j + 1 {
|
||||
for l in 0..=k + 1 {
|
||||
// 5-way conflict
|
||||
// 5-way merge
|
||||
check_invariants(&[0, i], &[j, k, l]);
|
||||
}
|
||||
}
|
||||
|
@ -870,9 +864,9 @@ mod tests {
|
|||
fn increment(i: &i32) -> i32 {
|
||||
i + 1
|
||||
}
|
||||
// 1-way conflict
|
||||
// 1-way merge
|
||||
assert_eq!(c(&[], &[1]).map(increment), c(&[], &[2]));
|
||||
// 3-way conflict
|
||||
// 3-way merge
|
||||
assert_eq!(c(&[1], &[3, 5]).map(increment), c(&[2], &[4, 6]));
|
||||
}
|
||||
|
||||
|
@ -885,10 +879,10 @@ mod tests {
|
|||
None
|
||||
}
|
||||
}
|
||||
// 1-way conflict
|
||||
// 1-way merge
|
||||
assert_eq!(c(&[], &[1]).maybe_map(sqrt), Some(c(&[], &[1])));
|
||||
assert_eq!(c(&[], &[-1]).maybe_map(sqrt), None);
|
||||
// 3-way conflict
|
||||
// 3-way merge
|
||||
assert_eq!(c(&[1], &[4, 9]).maybe_map(sqrt), Some(c(&[1], &[2, 3])));
|
||||
assert_eq!(c(&[-1], &[4, 9]).maybe_map(sqrt), None);
|
||||
assert_eq!(c(&[1], &[-4, 9]).maybe_map(sqrt), None);
|
||||
|
@ -903,10 +897,10 @@ mod tests {
|
|||
Err(())
|
||||
}
|
||||
}
|
||||
// 1-way conflict
|
||||
// 1-way merge
|
||||
assert_eq!(c(&[], &[1]).try_map(sqrt), Ok(c(&[], &[1])));
|
||||
assert_eq!(c(&[], &[-1]).try_map(sqrt), Err(()));
|
||||
// 3-way conflict
|
||||
// 3-way merge
|
||||
assert_eq!(c(&[1], &[4, 9]).try_map(sqrt), Ok(c(&[1], &[2, 3])));
|
||||
assert_eq!(c(&[-1], &[4, 9]).try_map(sqrt), Err(()));
|
||||
assert_eq!(c(&[1], &[-4, 9]).try_map(sqrt), Err(()));
|
||||
|
@ -914,16 +908,16 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_flatten() {
|
||||
// 1-way conflict of 1-way conflict
|
||||
// 1-way merge of 1-way merge
|
||||
assert_eq!(c(&[], &[c(&[], &[0])]).flatten(), c(&[], &[0]));
|
||||
// 1-way conflict of 3-way conflict
|
||||
// 1-way merge of 3-way merge
|
||||
assert_eq!(c(&[], &[c(&[0], &[1, 2])]).flatten(), c(&[0], &[1, 2]));
|
||||
// 3-way conflict of 1-way conflicts
|
||||
// 3-way merge of 1-way merges
|
||||
assert_eq!(
|
||||
c(&[c(&[], &[0])], &[c(&[], &[1]), c(&[], &[2])]).flatten(),
|
||||
c(&[0], &[1, 2])
|
||||
);
|
||||
// 3-way conflict of 3-way conflicts
|
||||
// 3-way merge of 3-way merges
|
||||
assert_eq!(
|
||||
c(&[c(&[0], &[1, 2])], &[c(&[3], &[4, 5]), c(&[6], &[7, 8])]).flatten(),
|
||||
c(&[3, 2, 1, 6], &[4, 5, 0, 7, 8])
|
||||
|
|
|
@ -20,7 +20,7 @@ use std::ops::Range;
|
|||
|
||||
use itertools::Itertools;
|
||||
|
||||
use crate::conflicts::Conflict;
|
||||
use crate::conflicts::Merge;
|
||||
use crate::diff;
|
||||
use crate::diff::{Diff, DiffHunk};
|
||||
use crate::merge::trivial_merge;
|
||||
|
@ -157,7 +157,7 @@ impl Debug for ContentHunk {
|
|||
#[derive(PartialEq, Eq, Clone, Debug)]
|
||||
pub enum MergeResult {
|
||||
Resolved(ContentHunk),
|
||||
Conflict(Vec<Conflict<ContentHunk>>),
|
||||
Conflict(Vec<Merge<ContentHunk>>),
|
||||
}
|
||||
|
||||
/// A region where the base and two sides match.
|
||||
|
@ -179,7 +179,7 @@ pub fn merge(removes: &[&[u8]], adds: &[&[u8]]) -> MergeResult {
|
|||
|
||||
let diff = Diff::for_tokenizer(&diff_inputs, &diff::find_line_ranges);
|
||||
let mut resolved_hunk = ContentHunk(vec![]);
|
||||
let mut merge_hunks: Vec<Conflict<ContentHunk>> = vec![];
|
||||
let mut merge_hunks: Vec<Merge<ContentHunk>> = vec![];
|
||||
for diff_hunk in diff.hunks() {
|
||||
match diff_hunk {
|
||||
DiffHunk::Matching(content) => {
|
||||
|
@ -190,10 +190,10 @@ pub fn merge(removes: &[&[u8]], adds: &[&[u8]]) -> MergeResult {
|
|||
resolved_hunk.0.extend(*resolved);
|
||||
} else {
|
||||
if !resolved_hunk.0.is_empty() {
|
||||
merge_hunks.push(Conflict::resolved(resolved_hunk));
|
||||
merge_hunks.push(Merge::resolved(resolved_hunk));
|
||||
resolved_hunk = ContentHunk(vec![]);
|
||||
}
|
||||
merge_hunks.push(Conflict::new(
|
||||
merge_hunks.push(Merge::new(
|
||||
parts[..num_diffs]
|
||||
.iter()
|
||||
.map(|part| ContentHunk(part.to_vec()))
|
||||
|
@ -212,7 +212,7 @@ pub fn merge(removes: &[&[u8]], adds: &[&[u8]]) -> MergeResult {
|
|||
MergeResult::Resolved(resolved_hunk)
|
||||
} else {
|
||||
if !resolved_hunk.0.is_empty() {
|
||||
merge_hunks.push(Conflict::resolved(resolved_hunk));
|
||||
merge_hunks.push(Merge::resolved(resolved_hunk));
|
||||
}
|
||||
MergeResult::Conflict(merge_hunks)
|
||||
}
|
||||
|
@ -268,7 +268,7 @@ mod tests {
|
|||
// One side modified, two sides added
|
||||
assert_eq!(
|
||||
merge(&[b"a", b""], &[b"b", b"b", b"b"]),
|
||||
MergeResult::Conflict(vec![Conflict::new(
|
||||
MergeResult::Conflict(vec![Merge::new(
|
||||
vec![hunk(b"a"), hunk(b"")],
|
||||
vec![hunk(b"b"), hunk(b"b"), hunk(b"b")]
|
||||
)])
|
||||
|
@ -281,7 +281,7 @@ mod tests {
|
|||
// One side modified, two sides removed
|
||||
assert_eq!(
|
||||
merge(&[b"a\n", b"a\n"], &[b"b\n", b"", b""]),
|
||||
MergeResult::Conflict(vec![Conflict::new(
|
||||
MergeResult::Conflict(vec![Merge::new(
|
||||
vec![hunk(b"a\n"), hunk(b"a\n")],
|
||||
vec![hunk(b"b\n"), hunk(b""), hunk(b"")]
|
||||
)])
|
||||
|
@ -294,7 +294,7 @@ mod tests {
|
|||
// One side removed, one side modified
|
||||
assert_eq!(
|
||||
merge(&[b"a\n"], &[b"", b"b\n"]),
|
||||
MergeResult::Conflict(vec![Conflict::new(
|
||||
MergeResult::Conflict(vec![Merge::new(
|
||||
vec![hunk(b"a\n")],
|
||||
vec![hunk(b""), hunk(b"b\n")]
|
||||
)])
|
||||
|
@ -302,7 +302,7 @@ mod tests {
|
|||
// One side modified, one side removed
|
||||
assert_eq!(
|
||||
merge(&[b"a\n"], &[b"b\n", b""]),
|
||||
MergeResult::Conflict(vec![Conflict::new(
|
||||
MergeResult::Conflict(vec![Merge::new(
|
||||
vec![hunk(b"a\n")],
|
||||
vec![hunk(b"b\n"), hunk(b"")]
|
||||
)])
|
||||
|
@ -310,7 +310,7 @@ mod tests {
|
|||
// Two sides modified in different ways
|
||||
assert_eq!(
|
||||
merge(&[b"a"], &[b"b", b"c"]),
|
||||
MergeResult::Conflict(vec![Conflict::new(
|
||||
MergeResult::Conflict(vec![Merge::new(
|
||||
vec![hunk(b"a")],
|
||||
vec![hunk(b"b"), hunk(b"c")]
|
||||
)])
|
||||
|
@ -328,7 +328,7 @@ 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![Conflict::new(
|
||||
MergeResult::Conflict(vec![Merge::new(
|
||||
vec![hunk(b"a"), hunk(b"a")],
|
||||
vec![hunk(b"b"), hunk(b"a"), hunk(b"c")]
|
||||
)])
|
||||
|
@ -343,7 +343,7 @@ 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![Conflict::new(
|
||||
MergeResult::Conflict(vec![Merge::new(
|
||||
vec![hunk(b"a"), hunk(b"b")],
|
||||
vec![hunk(b"c"), hunk(b"d"), hunk(b"e")]
|
||||
)])
|
||||
|
@ -351,7 +351,7 @@ mod tests {
|
|||
// Two sides made the same change, third side made a different change
|
||||
assert_eq!(
|
||||
merge(&[b"a", b"b"], &[b"c", b"c", b"c"]),
|
||||
MergeResult::Conflict(vec![Conflict::new(
|
||||
MergeResult::Conflict(vec![Merge::new(
|
||||
vec![hunk(b"a"), hunk(b"b")],
|
||||
vec![hunk(b"c"), hunk(b"c"), hunk(b"c")]
|
||||
)])
|
||||
|
@ -364,8 +364,8 @@ mod tests {
|
|||
assert_eq!(
|
||||
merge(&[b"a\n"], &[b"a\nb\n", b"a\nc\n"]),
|
||||
MergeResult::Conflict(vec![
|
||||
Conflict::resolved(hunk(b"a\n")),
|
||||
Conflict::new(vec![hunk(b"")], vec![hunk(b"b\n"), hunk(b"c\n")])
|
||||
Merge::resolved(hunk(b"a\n")),
|
||||
Merge::new(vec![hunk(b"")], vec![hunk(b"b\n"), hunk(b"c\n")])
|
||||
])
|
||||
);
|
||||
// Two sides changed different lines: no conflict
|
||||
|
@ -377,9 +377,9 @@ mod tests {
|
|||
assert_eq!(
|
||||
merge(&[b"a\nb\nc\n"], &[b"a\nb1\nc\n", b"a\nb2\nc\n"]),
|
||||
MergeResult::Conflict(vec![
|
||||
Conflict::resolved(hunk(b"a\n")),
|
||||
Conflict::new(vec![hunk(b"b\n")], vec![hunk(b"b1\n"), hunk(b"b2\n")]),
|
||||
Conflict::resolved(hunk(b"c\n"))
|
||||
Merge::resolved(hunk(b"a\n")),
|
||||
Merge::new(vec![hunk(b"b\n")], vec![hunk(b"b1\n"), hunk(b"b2\n")]),
|
||||
Merge::resolved(hunk(b"c\n"))
|
||||
])
|
||||
);
|
||||
// One side changes a line and adds a block after. The other side just adds the
|
||||
|
|
|
@ -32,7 +32,7 @@ use crate::backend::{
|
|||
ChangeId, Commit, CommitId, Conflict, ConflictId, ConflictTerm, FileId, MillisSinceEpoch,
|
||||
ObjectId, Signature, SymlinkId, Timestamp, Tree, TreeId, TreeValue,
|
||||
};
|
||||
use crate::conflicts;
|
||||
use crate::conflicts::Merge;
|
||||
use crate::file_util::{IoResultExt as _, PathError};
|
||||
use crate::lock::FileLock;
|
||||
use crate::repo_path::{RepoPath, RepoPathComponent};
|
||||
|
@ -223,7 +223,7 @@ fn commit_from_git_without_root_parent(commit: &git2::Commit) -> Commit {
|
|||
let tree_id = TreeId::from_bytes(commit.tree_id().as_bytes());
|
||||
// If this commit is a conflict, we'll update the root tree later, when we read
|
||||
// the extra metadata.
|
||||
let root_tree = conflicts::Conflict::resolved(tree_id);
|
||||
let root_tree = Merge::resolved(tree_id);
|
||||
let description = commit.message().unwrap_or("<no message>").to_owned();
|
||||
let author = signature_from_git(commit.author());
|
||||
let committer = signature_from_git(commit.committer());
|
||||
|
@ -304,7 +304,7 @@ fn deserialize_extras(commit: &mut Commit, bytes: &[u8]) {
|
|||
match proto.root_tree {
|
||||
Some(crate::protos::git_store::commit::RootTree::Conflict(proto_conflict)) => {
|
||||
assert!(commit.uses_tree_conflict_format);
|
||||
commit.root_tree = conflicts::Conflict::new(
|
||||
commit.root_tree = Merge::new(
|
||||
proto_conflict
|
||||
.removes
|
||||
.iter()
|
||||
|
@ -765,7 +765,7 @@ impl Backend for GitBackend {
|
|||
/// `.jjconflict-base-N` subtrees. This ensure that the parts are not GC'd.
|
||||
fn write_tree_conflict(
|
||||
repo: &git2::Repository,
|
||||
conflict: &conflicts::Conflict<TreeId>,
|
||||
conflict: &Merge<TreeId>,
|
||||
) -> Result<Oid, BackendError> {
|
||||
let mut builder = repo.treebuilder(None).unwrap();
|
||||
let mut add_tree_entry = |name, tree_id: &TreeId| {
|
||||
|
@ -988,7 +988,7 @@ mod tests {
|
|||
let mut commit = Commit {
|
||||
parents: vec![],
|
||||
predecessors: vec![],
|
||||
root_tree: conflicts::Conflict::resolved(backend.empty_tree_id().clone()),
|
||||
root_tree: Merge::resolved(backend.empty_tree_id().clone()),
|
||||
uses_tree_conflict_format: false,
|
||||
change_id: ChangeId::from_hex("abc123"),
|
||||
description: "".to_string(),
|
||||
|
@ -1058,7 +1058,7 @@ mod tests {
|
|||
TreeId::from_bytes(tree_builder.write().unwrap().as_bytes())
|
||||
};
|
||||
|
||||
let root_tree = conflicts::Conflict::new(
|
||||
let root_tree = Merge::new(
|
||||
vec![crete_tree(0), crete_tree(1)],
|
||||
vec![crete_tree(2), crete_tree(3), crete_tree(4)],
|
||||
);
|
||||
|
@ -1103,7 +1103,7 @@ mod tests {
|
|||
|
||||
// When writing a single tree using the new format, it's represented by a
|
||||
// regular git tree.
|
||||
commit.root_tree = conflicts::Conflict::resolved(crete_tree(5));
|
||||
commit.root_tree = Merge::resolved(crete_tree(5));
|
||||
let read_commit_id = backend.write_commit(commit.clone()).unwrap().0;
|
||||
let read_commit = backend.read_commit(&read_commit_id).unwrap();
|
||||
assert_eq!(read_commit, commit);
|
||||
|
@ -1131,7 +1131,7 @@ mod tests {
|
|||
let commit = Commit {
|
||||
parents: vec![store.root_commit_id().clone()],
|
||||
predecessors: vec![],
|
||||
root_tree: conflicts::Conflict::resolved(store.empty_tree_id().clone()),
|
||||
root_tree: Merge::resolved(store.empty_tree_id().clone()),
|
||||
uses_tree_conflict_format: false,
|
||||
change_id: ChangeId::new(vec![]),
|
||||
description: "initial".to_string(),
|
||||
|
@ -1155,7 +1155,7 @@ mod tests {
|
|||
let mut commit1 = Commit {
|
||||
parents: vec![store.root_commit_id().clone()],
|
||||
predecessors: vec![],
|
||||
root_tree: conflicts::Conflict::resolved(store.empty_tree_id().clone()),
|
||||
root_tree: Merge::resolved(store.empty_tree_id().clone()),
|
||||
uses_tree_conflict_format: false,
|
||||
change_id: ChangeId::new(vec![]),
|
||||
description: "initial".to_string(),
|
||||
|
|
|
@ -30,7 +30,7 @@ use crate::backend::{
|
|||
ConflictId, ConflictTerm, FileId, MillisSinceEpoch, ObjectId, Signature, SymlinkId, Timestamp,
|
||||
Tree, TreeId, TreeValue,
|
||||
};
|
||||
use crate::conflicts;
|
||||
use crate::conflicts::Merge;
|
||||
use crate::content_hash::blake2b_hash;
|
||||
use crate::file_util::persist_content_addressed_temp_file;
|
||||
use crate::repo_path::{RepoPath, RepoPathComponent};
|
||||
|
@ -311,7 +311,7 @@ fn commit_from_proto(proto: crate::protos::local_store::Commit) -> Commit {
|
|||
let parents = proto.parents.into_iter().map(CommitId::new).collect();
|
||||
let predecessors = proto.predecessors.into_iter().map(CommitId::new).collect();
|
||||
let conflict = proto.root_tree.unwrap();
|
||||
let root_tree = conflicts::Conflict::new(
|
||||
let root_tree = Merge::new(
|
||||
conflict.removes.into_iter().map(TreeId::new).collect(),
|
||||
conflict.adds.into_iter().map(TreeId::new).collect(),
|
||||
);
|
||||
|
|
|
@ -22,7 +22,7 @@ use itertools::Itertools;
|
|||
|
||||
use crate::backend;
|
||||
use crate::backend::{ConflictId, TreeValue};
|
||||
use crate::conflicts::Conflict;
|
||||
use crate::conflicts::Merge;
|
||||
use crate::repo_path::{RepoPath, RepoPathComponent, RepoPathJoin};
|
||||
use crate::store::Store;
|
||||
use crate::tree::{try_resolve_file_conflict, Tree, TreeMergeError};
|
||||
|
@ -35,7 +35,7 @@ pub enum MergedTree {
|
|||
Legacy(Tree),
|
||||
/// A merge of multiple trees, or just a single tree. The individual trees
|
||||
/// have no path-level conflicts.
|
||||
Merge(Conflict<Tree>),
|
||||
Merge(Merge<Tree>),
|
||||
}
|
||||
|
||||
/// The value at a given path in a `MergedTree`.
|
||||
|
@ -43,20 +43,20 @@ pub enum MergedTree {
|
|||
pub enum MergedTreeValue<'a> {
|
||||
/// A single non-conflicted value.
|
||||
Resolved(Option<&'a TreeValue>),
|
||||
/// TODO: Make this a `Conflict<Option<&'a TreeValue>>` (reference to the
|
||||
/// TODO: Make this a `Merge<Option<&'a TreeValue>>` (reference to the
|
||||
/// value) once we have removed the `MergedTree::Legacy` variant.
|
||||
Conflict(Conflict<Option<TreeValue>>),
|
||||
Conflict(Merge<Option<TreeValue>>),
|
||||
}
|
||||
|
||||
impl MergedTree {
|
||||
/// Creates a new `MergedTree` representing a single tree without conflicts.
|
||||
pub fn resolved(tree: Tree) -> Self {
|
||||
MergedTree::new(Conflict::resolved(tree))
|
||||
MergedTree::new(Merge::resolved(tree))
|
||||
}
|
||||
|
||||
/// Creates a new `MergedTree` representing a merge of a set of trees. The
|
||||
/// individual trees must not have any conflicts.
|
||||
pub fn new(conflict: Conflict<Tree>) -> Self {
|
||||
pub fn new(conflict: Merge<Tree>) -> Self {
|
||||
debug_assert!(!conflict.removes().iter().any(|t| t.has_conflict()));
|
||||
debug_assert!(!conflict.adds().iter().any(|t| t.has_conflict()));
|
||||
debug_assert!(itertools::chain(conflict.removes(), conflict.adds())
|
||||
|
@ -85,7 +85,7 @@ impl MergedTree {
|
|||
// build `2*num_removes + 1` trees
|
||||
let mut max_num_removes = 0;
|
||||
let store = tree.store();
|
||||
let mut conflicts: Vec<(&RepoPath, Conflict<Option<TreeValue>>)> = vec![];
|
||||
let mut conflicts: Vec<(&RepoPath, Merge<Option<TreeValue>>)> = vec![];
|
||||
for (path, conflict_id) in &conflict_ids {
|
||||
let conflict = store.read_conflict(path, conflict_id).unwrap();
|
||||
max_num_removes = max(max_num_removes, conflict.removes().len());
|
||||
|
@ -126,7 +126,7 @@ impl MergedTree {
|
|||
store.get_tree(&RepoPath::root(), &tree_id).unwrap()
|
||||
};
|
||||
|
||||
MergedTree::Merge(Conflict::new(
|
||||
MergedTree::Merge(Merge::new(
|
||||
removes.into_iter().map(write_tree).collect(),
|
||||
adds.into_iter().map(write_tree).collect(),
|
||||
))
|
||||
|
@ -179,9 +179,9 @@ impl MergedTree {
|
|||
/// automatically resolved and leaving the rest unresolved. The returned
|
||||
/// conflict will either be resolved or have the same number of sides as
|
||||
/// the input.
|
||||
pub fn resolve(&self) -> Result<Conflict<Tree>, TreeMergeError> {
|
||||
pub fn resolve(&self) -> Result<Merge<Tree>, TreeMergeError> {
|
||||
match self {
|
||||
MergedTree::Legacy(tree) => Ok(Conflict::resolved(tree.clone())),
|
||||
MergedTree::Legacy(tree) => Ok(Merge::resolved(tree.clone())),
|
||||
MergedTree::Merge(conflict) => merge_trees(conflict),
|
||||
}
|
||||
}
|
||||
|
@ -191,7 +191,7 @@ impl MergedTree {
|
|||
/// all sides are trees, so tree/file conflicts will be reported as a single
|
||||
/// conflict, not one for each path in the tree.
|
||||
// TODO: Restrict this by a matcher (or add a separate method for that).
|
||||
pub fn conflicts(&self) -> impl Iterator<Item = (RepoPath, Conflict<Option<TreeValue>>)> {
|
||||
pub fn conflicts(&self) -> impl Iterator<Item = (RepoPath, Merge<Option<TreeValue>>)> {
|
||||
ConflictIterator::new(self.clone())
|
||||
}
|
||||
|
||||
|
@ -204,19 +204,19 @@ impl MergedTree {
|
|||
}
|
||||
}
|
||||
|
||||
fn all_tree_conflict_names(conflict: &Conflict<Tree>) -> impl Iterator<Item = &RepoPathComponent> {
|
||||
fn all_tree_conflict_names(conflict: &Merge<Tree>) -> impl Iterator<Item = &RepoPathComponent> {
|
||||
itertools::chain(conflict.removes(), conflict.adds())
|
||||
.map(|tree| tree.data().names())
|
||||
.kmerge()
|
||||
.dedup()
|
||||
}
|
||||
|
||||
fn merge_trees(conflict: &Conflict<Tree>) -> Result<Conflict<Tree>, TreeMergeError> {
|
||||
if let Some(tree) = conflict.resolve_trivial() {
|
||||
return Ok(Conflict::resolved(tree.clone()));
|
||||
fn merge_trees(merge: &Merge<Tree>) -> Result<Merge<Tree>, TreeMergeError> {
|
||||
if let Some(tree) = merge.resolve_trivial() {
|
||||
return Ok(Merge::resolved(tree.clone()));
|
||||
}
|
||||
|
||||
let base_tree = &conflict.adds()[0];
|
||||
let base_tree = &merge.adds()[0];
|
||||
let store = base_tree.store();
|
||||
let dir = base_tree.dir();
|
||||
// Keep resolved entries in `new_tree` and conflicted entries in `conflicts` to
|
||||
|
@ -224,24 +224,24 @@ fn merge_trees(conflict: &Conflict<Tree>) -> Result<Conflict<Tree>, TreeMergeErr
|
|||
// any conflicts.
|
||||
let mut new_tree = backend::Tree::default();
|
||||
let mut conflicts = vec![];
|
||||
for basename in all_tree_conflict_names(conflict) {
|
||||
let path_conflict = conflict.map(|tree| tree.value(basename).cloned());
|
||||
let path_conflict = merge_tree_values(store, dir, path_conflict)?;
|
||||
if let Some(value) = path_conflict.as_resolved() {
|
||||
for basename in all_tree_conflict_names(merge) {
|
||||
let path_merge = merge.map(|tree| tree.value(basename).cloned());
|
||||
let path_merge = merge_tree_values(store, dir, path_merge)?;
|
||||
if let Some(value) = path_merge.as_resolved() {
|
||||
new_tree.set_or_remove(basename, value.clone());
|
||||
} else {
|
||||
conflicts.push((basename, path_conflict));
|
||||
conflicts.push((basename, path_merge));
|
||||
};
|
||||
}
|
||||
if conflicts.is_empty() {
|
||||
let new_tree_id = store.write_tree(dir, new_tree)?;
|
||||
Ok(Conflict::resolved(new_tree_id))
|
||||
Ok(Merge::resolved(new_tree_id))
|
||||
} else {
|
||||
// For each side of the conflict, overwrite the entries in `new_tree` with the
|
||||
// values from `conflicts`. Entries that are not in `conflicts` will remain
|
||||
// unchanged and will be reused for each side.
|
||||
let mut tree_removes = vec![];
|
||||
for i in 0..conflict.removes().len() {
|
||||
for i in 0..merge.removes().len() {
|
||||
for (basename, path_conflict) in &conflicts {
|
||||
new_tree.set_or_remove(basename, path_conflict.removes()[i].clone());
|
||||
}
|
||||
|
@ -249,7 +249,7 @@ fn merge_trees(conflict: &Conflict<Tree>) -> Result<Conflict<Tree>, TreeMergeErr
|
|||
tree_removes.push(tree);
|
||||
}
|
||||
let mut tree_adds = vec![];
|
||||
for i in 0..conflict.adds().len() {
|
||||
for i in 0..merge.adds().len() {
|
||||
for (basename, path_conflict) in &conflicts {
|
||||
new_tree.set_or_remove(basename, path_conflict.adds()[i].clone());
|
||||
}
|
||||
|
@ -257,7 +257,7 @@ fn merge_trees(conflict: &Conflict<Tree>) -> Result<Conflict<Tree>, TreeMergeErr
|
|||
tree_adds.push(tree);
|
||||
}
|
||||
|
||||
Ok(Conflict::new(tree_removes, tree_adds))
|
||||
Ok(Merge::new(tree_removes, tree_adds))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -268,18 +268,18 @@ fn merge_trees(conflict: &Conflict<Tree>) -> Result<Conflict<Tree>, TreeMergeErr
|
|||
fn merge_tree_values(
|
||||
store: &Arc<Store>,
|
||||
path: &RepoPath,
|
||||
conflict: Conflict<Option<TreeValue>>,
|
||||
) -> Result<Conflict<Option<TreeValue>>, TreeMergeError> {
|
||||
conflict: Merge<Option<TreeValue>>,
|
||||
) -> Result<Merge<Option<TreeValue>>, TreeMergeError> {
|
||||
if let Some(resolved) = conflict.resolve_trivial() {
|
||||
return Ok(Conflict::resolved(resolved.clone()));
|
||||
return Ok(Merge::resolved(resolved.clone()));
|
||||
}
|
||||
|
||||
if let Some(tree_conflict) = conflict.to_tree_conflict(store, path)? {
|
||||
if let Some(tree_conflict) = conflict.to_tree_merge(store, path)? {
|
||||
// If all sides are trees or missing, merge the trees recursively, treating
|
||||
// missing trees as empty.
|
||||
let merged_tree = merge_trees(&tree_conflict)?;
|
||||
if merged_tree.as_resolved().map(|tree| tree.id()) == Some(store.empty_tree_id()) {
|
||||
Ok(Conflict::resolved(None))
|
||||
Ok(Merge::resolved(None))
|
||||
} else {
|
||||
Ok(merged_tree.map(|tree| Some(TreeValue::Tree(tree.id().clone()))))
|
||||
}
|
||||
|
@ -287,7 +287,7 @@ fn merge_tree_values(
|
|||
// Try to resolve file conflicts by merging the file contents. Treats missing
|
||||
// files as empty.
|
||||
if let Some(resolved) = try_resolve_file_conflict(store, path, &conflict)? {
|
||||
Ok(Conflict::resolved(Some(resolved)))
|
||||
Ok(Merge::resolved(Some(resolved)))
|
||||
} else {
|
||||
// Failed to merge the files, or the paths are not files
|
||||
Ok(conflict)
|
||||
|
@ -325,7 +325,7 @@ impl<'a> ConflictEntriesNonRecursiveIterator<'a> {
|
|||
}
|
||||
|
||||
impl<'a> Iterator for ConflictEntriesNonRecursiveIterator<'a> {
|
||||
type Item = (&'a RepoPathComponent, Conflict<Option<TreeValue>>);
|
||||
type Item = (&'a RepoPathComponent, Merge<Option<TreeValue>>);
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
for basename in self.basename_iter.by_ref() {
|
||||
|
@ -387,7 +387,7 @@ impl ConflictIterator {
|
|||
}
|
||||
|
||||
impl Iterator for ConflictIterator {
|
||||
type Item = (RepoPath, Conflict<Option<TreeValue>>);
|
||||
type Item = (RepoPath, Merge<Option<TreeValue>>);
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
match self {
|
||||
|
@ -409,7 +409,7 @@ impl Iterator for ConflictIterator {
|
|||
let path = top.tree.dir().join(basename);
|
||||
// TODO: propagate errors
|
||||
if let Some(tree_conflict) =
|
||||
conflict.to_tree_conflict(top.tree.store(), &path).unwrap()
|
||||
conflict.to_tree_merge(top.tree.store(), &path).unwrap()
|
||||
{
|
||||
// If all sides are trees or missing, descend into the merged tree
|
||||
stack.push(ConflictsDirItem::new(MergedTree::Merge(tree_conflict)));
|
||||
|
|
|
@ -21,7 +21,7 @@ use once_cell::sync::Lazy;
|
|||
use thiserror::Error;
|
||||
|
||||
use crate::backend::{id_type, CommitId, ObjectId, Timestamp};
|
||||
use crate::conflicts::Conflict;
|
||||
use crate::conflicts::Merge;
|
||||
|
||||
content_hash! {
|
||||
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Hash)]
|
||||
|
@ -56,7 +56,7 @@ id_type!(pub OperationId);
|
|||
content_hash! {
|
||||
#[derive(PartialEq, Eq, Hash, Clone, Debug)]
|
||||
pub struct RefTarget {
|
||||
conflict: Conflict<Option<CommitId>>,
|
||||
merge: Merge<Option<CommitId>>,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -69,7 +69,7 @@ impl Default for RefTarget {
|
|||
impl RefTarget {
|
||||
/// Creates non-conflicting target pointing to no commit.
|
||||
pub fn absent() -> Self {
|
||||
Self::from_conflict(Conflict::resolved(None))
|
||||
Self::from_merge(Merge::resolved(None))
|
||||
}
|
||||
|
||||
/// Returns non-conflicting target pointing to no commit.
|
||||
|
@ -82,7 +82,7 @@ impl RefTarget {
|
|||
|
||||
/// Creates non-conflicting target pointing to a commit.
|
||||
pub fn normal(id: CommitId) -> Self {
|
||||
Self::from_conflict(Conflict::resolved(Some(id)))
|
||||
Self::from_merge(Merge::resolved(Some(id)))
|
||||
}
|
||||
|
||||
/// Creates target from removed/added ids.
|
||||
|
@ -90,22 +90,22 @@ impl RefTarget {
|
|||
removed_ids: impl IntoIterator<Item = CommitId>,
|
||||
added_ids: impl IntoIterator<Item = CommitId>,
|
||||
) -> Self {
|
||||
Self::from_conflict(Conflict::from_legacy_form(removed_ids, added_ids))
|
||||
Self::from_merge(Merge::from_legacy_form(removed_ids, added_ids))
|
||||
}
|
||||
|
||||
pub fn from_conflict(conflict: Conflict<Option<CommitId>>) -> Self {
|
||||
RefTarget { conflict }
|
||||
pub fn from_merge(merge: Merge<Option<CommitId>>) -> Self {
|
||||
RefTarget { merge }
|
||||
}
|
||||
|
||||
/// Returns id if this target is non-conflicting and points to a commit.
|
||||
pub fn as_normal(&self) -> Option<&CommitId> {
|
||||
let maybe_id = self.conflict.as_resolved()?;
|
||||
let maybe_id = self.merge.as_resolved()?;
|
||||
maybe_id.as_ref()
|
||||
}
|
||||
|
||||
/// Returns true if this target points to no commit.
|
||||
pub fn is_absent(&self) -> bool {
|
||||
matches!(self.conflict.as_resolved(), Some(None))
|
||||
matches!(self.merge.as_resolved(), Some(None))
|
||||
}
|
||||
|
||||
/// Returns true if this target points to any commit. Conflicting target is
|
||||
|
@ -116,19 +116,19 @@ impl RefTarget {
|
|||
|
||||
/// Whether this target has conflicts.
|
||||
pub fn has_conflict(&self) -> bool {
|
||||
!self.conflict.is_resolved()
|
||||
!self.merge.is_resolved()
|
||||
}
|
||||
|
||||
pub fn removed_ids(&self) -> impl Iterator<Item = &CommitId> {
|
||||
self.conflict.removes().iter().flatten()
|
||||
self.merge.removes().iter().flatten()
|
||||
}
|
||||
|
||||
pub fn added_ids(&self) -> impl Iterator<Item = &CommitId> {
|
||||
self.conflict.adds().iter().flatten()
|
||||
self.merge.adds().iter().flatten()
|
||||
}
|
||||
|
||||
pub fn as_conflict(&self) -> &Conflict<Option<CommitId>> {
|
||||
&self.conflict
|
||||
pub fn as_conflict(&self) -> &Merge<Option<CommitId>> {
|
||||
&self.merge
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
#![allow(missing_docs)]
|
||||
|
||||
use crate::backend::CommitId;
|
||||
use crate::conflicts::Conflict;
|
||||
use crate::conflicts::Merge;
|
||||
use crate::index::Index;
|
||||
use crate::merge::trivial_merge;
|
||||
use crate::op_store::{BranchTarget, RefTarget, RefTargetOptionExt};
|
||||
|
@ -30,31 +30,31 @@ pub fn merge_ref_targets(
|
|||
return resolved.clone();
|
||||
}
|
||||
|
||||
let conflict = Conflict::new(
|
||||
let merge = Merge::new(
|
||||
vec![base.as_conflict().clone()],
|
||||
vec![left.as_conflict().clone(), right.as_conflict().clone()],
|
||||
)
|
||||
.flatten()
|
||||
.simplify();
|
||||
|
||||
if conflict.is_resolved() {
|
||||
RefTarget::from_conflict(conflict)
|
||||
if merge.is_resolved() {
|
||||
RefTarget::from_merge(merge)
|
||||
} else {
|
||||
let conflict = merge_ref_targets_non_trivial(index, conflict);
|
||||
RefTarget::from_conflict(conflict)
|
||||
let merge = merge_ref_targets_non_trivial(index, merge);
|
||||
RefTarget::from_merge(merge)
|
||||
}
|
||||
}
|
||||
|
||||
fn merge_ref_targets_non_trivial(
|
||||
index: &dyn Index,
|
||||
conflict: Conflict<Option<CommitId>>,
|
||||
) -> Conflict<Option<CommitId>> {
|
||||
conflict: Merge<Option<CommitId>>,
|
||||
) -> Merge<Option<CommitId>> {
|
||||
let (mut removes, mut adds) = conflict.take();
|
||||
while let Some((remove_index, add_index)) = find_pair_to_remove(index, &removes, &adds) {
|
||||
removes.remove(remove_index);
|
||||
adds.remove(add_index);
|
||||
}
|
||||
Conflict::new(removes, adds)
|
||||
Merge::new(removes, adds)
|
||||
}
|
||||
|
||||
fn find_pair_to_remove(
|
||||
|
|
|
@ -25,7 +25,7 @@ use tempfile::{NamedTempFile, PersistError};
|
|||
use thiserror::Error;
|
||||
|
||||
use crate::backend::{CommitId, MillisSinceEpoch, ObjectId, Timestamp};
|
||||
use crate::conflicts::Conflict;
|
||||
use crate::conflicts::Merge;
|
||||
use crate::content_hash::blake2b_hash;
|
||||
use crate::file_util::persist_content_addressed_temp_file;
|
||||
use crate::op_store::{
|
||||
|
@ -426,7 +426,7 @@ fn ref_target_from_proto(maybe_proto: Option<crate::protos::op_store::RefTarget>
|
|||
|term: crate::protos::op_store::ref_conflict::Term| term.value.map(CommitId::new);
|
||||
let removes = conflict.removes.into_iter().map(term_from_proto).collect();
|
||||
let adds = conflict.adds.into_iter().map(term_from_proto).collect();
|
||||
RefTarget::from_conflict(Conflict::new(removes, adds))
|
||||
RefTarget::from_merge(Merge::new(removes, adds))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -556,7 +556,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_ref_target_change_delete_order_roundtrip() {
|
||||
let target = RefTarget::from_conflict(Conflict::new(
|
||||
let target = RefTarget::from_merge(Merge::new(
|
||||
vec![Some(CommitId::from_hex("111111"))],
|
||||
vec![Some(CommitId::from_hex("222222")), None],
|
||||
));
|
||||
|
@ -564,7 +564,7 @@ mod tests {
|
|||
assert_eq!(ref_target_from_proto(maybe_proto), target);
|
||||
|
||||
// If it were legacy format, order of None entry would be lost.
|
||||
let target = RefTarget::from_conflict(Conflict::new(
|
||||
let target = RefTarget::from_merge(Merge::new(
|
||||
vec![Some(CommitId::from_hex("111111"))],
|
||||
vec![None, Some(CommitId::from_hex("222222"))],
|
||||
));
|
||||
|
|
|
@ -19,14 +19,15 @@ use std::collections::HashMap;
|
|||
use std::io::Read;
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
use crate::backend;
|
||||
use crate::backend::{
|
||||
Backend, BackendResult, ChangeId, CommitId, ConflictId, FileId, SymlinkId, TreeId, TreeValue,
|
||||
};
|
||||
use crate::commit::Commit;
|
||||
use crate::conflicts::Merge;
|
||||
use crate::repo_path::RepoPath;
|
||||
use crate::tree::Tree;
|
||||
use crate::tree_builder::TreeBuilder;
|
||||
use crate::{backend, conflicts};
|
||||
|
||||
/// Wraps the low-level backend and makes it return more convenient types. Also
|
||||
/// adds caching.
|
||||
|
@ -159,15 +160,15 @@ impl Store {
|
|||
&self,
|
||||
path: &RepoPath,
|
||||
id: &ConflictId,
|
||||
) -> BackendResult<conflicts::Conflict<Option<TreeValue>>> {
|
||||
) -> BackendResult<Merge<Option<TreeValue>>> {
|
||||
let backend_conflict = self.backend.read_conflict(path, id)?;
|
||||
Ok(conflicts::Conflict::from_backend_conflict(backend_conflict))
|
||||
Ok(Merge::from_backend_conflict(backend_conflict))
|
||||
}
|
||||
|
||||
pub fn write_conflict(
|
||||
&self,
|
||||
path: &RepoPath,
|
||||
contents: &conflicts::Conflict<Option<TreeValue>>,
|
||||
contents: &Merge<Option<TreeValue>>,
|
||||
) -> BackendResult<ConflictId> {
|
||||
self.backend
|
||||
.write_conflict(path, &contents.clone().into_backend_conflict())
|
||||
|
|
|
@ -27,7 +27,7 @@ use crate::backend::{
|
|||
BackendError, ConflictId, FileId, ObjectId, TreeEntriesNonRecursiveIterator, TreeEntry, TreeId,
|
||||
TreeValue,
|
||||
};
|
||||
use crate::conflicts::Conflict;
|
||||
use crate::conflicts::Merge;
|
||||
use crate::files::MergeResult;
|
||||
use crate::matchers::{EverythingMatcher, Matcher};
|
||||
use crate::merge::trivial_merge;
|
||||
|
@ -565,19 +565,19 @@ fn merge_tree_value(
|
|||
_ => {
|
||||
// Start by creating a Conflict object. Conflicts can cleanly represent a single
|
||||
// resolved state, the absence of a state, or a conflicted state.
|
||||
let conflict = Conflict::new(
|
||||
let conflict = Merge::new(
|
||||
vec![maybe_base.cloned()],
|
||||
vec![maybe_side1.cloned(), maybe_side2.cloned()],
|
||||
);
|
||||
let filename = dir.join(basename);
|
||||
let conflict = simplify_conflict(store, &filename, conflict)?;
|
||||
if let Some(value) = conflict.as_resolved() {
|
||||
let merge = simplify_conflict(store, &filename, conflict)?;
|
||||
if let Some(value) = merge.as_resolved() {
|
||||
return Ok(value.clone());
|
||||
}
|
||||
if let Some(tree_value) = try_resolve_file_conflict(store, &filename, &conflict)? {
|
||||
if let Some(tree_value) = try_resolve_file_conflict(store, &filename, &merge)? {
|
||||
Some(tree_value)
|
||||
} else {
|
||||
let conflict_id = store.write_conflict(&filename, &conflict)?;
|
||||
let conflict_id = store.write_conflict(&filename, &merge)?;
|
||||
Some(TreeValue::Conflict(conflict_id))
|
||||
}
|
||||
}
|
||||
|
@ -587,7 +587,7 @@ fn merge_tree_value(
|
|||
pub fn try_resolve_file_conflict(
|
||||
store: &Store,
|
||||
filename: &RepoPath,
|
||||
conflict: &Conflict<Option<TreeValue>>,
|
||||
conflict: &Merge<Option<TreeValue>>,
|
||||
) -> Result<Option<TreeValue>, TreeMergeError> {
|
||||
// If there are any non-file or any missing parts in the conflict, we can't
|
||||
// merge it. We check early so we don't waste time reading file contents if
|
||||
|
@ -657,8 +657,8 @@ pub fn try_resolve_file_conflict(
|
|||
fn simplify_conflict(
|
||||
store: &Store,
|
||||
path: &RepoPath,
|
||||
conflict: Conflict<Option<TreeValue>>,
|
||||
) -> Result<Conflict<Option<TreeValue>>, BackendError> {
|
||||
conflict: Merge<Option<TreeValue>>,
|
||||
) -> Result<Merge<Option<TreeValue>>, BackendError> {
|
||||
// Important cases to simplify:
|
||||
//
|
||||
// D
|
||||
|
@ -692,7 +692,7 @@ fn simplify_conflict(
|
|||
|
||||
let expanded = conflict.try_map(|term| match term {
|
||||
Some(TreeValue::Conflict(id)) => store.read_conflict(path, id),
|
||||
_ => Ok(Conflict::resolved(term.clone())),
|
||||
_ => Ok(Merge::resolved(term.clone())),
|
||||
})?;
|
||||
Ok(expanded.flatten().simplify())
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
// limitations under the License.
|
||||
|
||||
use jj_lib::backend::{FileId, TreeValue};
|
||||
use jj_lib::conflicts::{parse_conflict, Conflict};
|
||||
use jj_lib::conflicts::{parse_conflict, Merge};
|
||||
use jj_lib::repo::Repo;
|
||||
use jj_lib::repo_path::RepoPath;
|
||||
use jj_lib::store::Store;
|
||||
|
@ -67,7 +67,7 @@ line 5
|
|||
|
||||
// The left side should come first. The diff should be use the smaller (right)
|
||||
// side, and the left side should be a snapshot.
|
||||
let conflict = Conflict::new(
|
||||
let conflict = Merge::new(
|
||||
vec![Some(file_value(&base_id))],
|
||||
vec![Some(file_value(&left_id)), Some(file_value(&right_id))],
|
||||
);
|
||||
|
@ -91,7 +91,7 @@ line 5
|
|||
);
|
||||
// Swap the positive terms in the conflict. The diff should still use the right
|
||||
// side, but now the right side should come first.
|
||||
let conflict = Conflict::new(
|
||||
let conflict = Merge::new(
|
||||
vec![Some(file_value(&base_id))],
|
||||
vec![Some(file_value(&right_id)), Some(file_value(&left_id))],
|
||||
);
|
||||
|
@ -160,7 +160,7 @@ line 3
|
|||
|
||||
// The order of (a, b, c) should be preserved. For all cases, the "a" side
|
||||
// should be a snapshot.
|
||||
let conflict = Conflict::new(
|
||||
let conflict = Merge::new(
|
||||
vec![Some(file_value(&base_id)), Some(file_value(&base_id))],
|
||||
vec![
|
||||
Some(file_value(&a_id)),
|
||||
|
@ -188,7 +188,7 @@ line 3
|
|||
line 3
|
||||
"###
|
||||
);
|
||||
let conflict = Conflict::new(
|
||||
let conflict = Merge::new(
|
||||
vec![Some(file_value(&base_id)), Some(file_value(&base_id))],
|
||||
vec![
|
||||
Some(file_value(&c_id)),
|
||||
|
@ -216,7 +216,7 @@ line 3
|
|||
line 3
|
||||
"###
|
||||
);
|
||||
let conflict = Conflict::new(
|
||||
let conflict = Merge::new(
|
||||
vec![Some(file_value(&base_id)), Some(file_value(&base_id))],
|
||||
vec![
|
||||
Some(file_value(&c_id)),
|
||||
|
@ -283,7 +283,7 @@ line 5 right
|
|||
",
|
||||
);
|
||||
|
||||
let conflict = Conflict::new(
|
||||
let conflict = Merge::new(
|
||||
vec![Some(file_value(&base_id))],
|
||||
vec![Some(file_value(&left_id)), Some(file_value(&right_id))],
|
||||
);
|
||||
|
@ -320,7 +320,7 @@ line 5 right
|
|||
@r###"
|
||||
Some(
|
||||
[
|
||||
Conflict {
|
||||
Merge {
|
||||
removes: [
|
||||
"line 1\nline 2\n",
|
||||
],
|
||||
|
@ -329,13 +329,13 @@ line 5 right
|
|||
"line 1 right\nline 2\n",
|
||||
],
|
||||
},
|
||||
Conflict {
|
||||
Merge {
|
||||
removes: [],
|
||||
adds: [
|
||||
"line 3\n",
|
||||
],
|
||||
},
|
||||
Conflict {
|
||||
Merge {
|
||||
removes: [
|
||||
"line 4\nline 5\n",
|
||||
],
|
||||
|
@ -386,7 +386,7 @@ line 5
|
|||
);
|
||||
|
||||
// left modifies a line, right deletes the same line.
|
||||
let conflict = Conflict::new(
|
||||
let conflict = Merge::new(
|
||||
vec![Some(file_value(&base_id))],
|
||||
vec![
|
||||
Some(file_value(&modified_id)),
|
||||
|
@ -408,7 +408,7 @@ line 5
|
|||
);
|
||||
|
||||
// right modifies a line, left deletes the same line.
|
||||
let conflict = Conflict::new(
|
||||
let conflict = Merge::new(
|
||||
vec![Some(file_value(&base_id))],
|
||||
vec![
|
||||
Some(file_value(&deleted_id)),
|
||||
|
@ -430,7 +430,7 @@ line 5
|
|||
);
|
||||
|
||||
// modify/delete conflict at the file level
|
||||
let conflict = Conflict::new(
|
||||
let conflict = Merge::new(
|
||||
vec![Some(file_value(&base_id))],
|
||||
vec![Some(file_value(&modified_id)), None],
|
||||
);
|
||||
|
@ -488,13 +488,13 @@ line 5
|
|||
@r###"
|
||||
Some(
|
||||
[
|
||||
Conflict {
|
||||
Merge {
|
||||
removes: [],
|
||||
adds: [
|
||||
"line 1\n",
|
||||
],
|
||||
},
|
||||
Conflict {
|
||||
Merge {
|
||||
removes: [
|
||||
"line 2\nline 3\nline 4\n",
|
||||
],
|
||||
|
@ -503,7 +503,7 @@ line 5
|
|||
"right\n",
|
||||
],
|
||||
},
|
||||
Conflict {
|
||||
Merge {
|
||||
removes: [],
|
||||
adds: [
|
||||
"line 5\n",
|
||||
|
@ -542,13 +542,13 @@ line 5
|
|||
@r###"
|
||||
Some(
|
||||
[
|
||||
Conflict {
|
||||
Merge {
|
||||
removes: [],
|
||||
adds: [
|
||||
"line 1\n",
|
||||
],
|
||||
},
|
||||
Conflict {
|
||||
Merge {
|
||||
removes: [
|
||||
"line 2\nline 3\nline 4\n",
|
||||
"line 2\nline 3\nline 4\n",
|
||||
|
@ -559,7 +559,7 @@ line 5
|
|||
"line 2\nforward\nline 3\nline 4\n",
|
||||
],
|
||||
},
|
||||
Conflict {
|
||||
Merge {
|
||||
removes: [],
|
||||
adds: [
|
||||
"line 5\n",
|
||||
|
@ -650,7 +650,7 @@ fn test_update_conflict_from_content() {
|
|||
let base_file_id = testutils::write_file(store, &path, "line 1\nline 2\nline 3\n");
|
||||
let left_file_id = testutils::write_file(store, &path, "left 1\nline 2\nleft 3\n");
|
||||
let right_file_id = testutils::write_file(store, &path, "right 1\nline 2\nright 3\n");
|
||||
let conflict = Conflict::new(
|
||||
let conflict = Merge::new(
|
||||
vec![Some(file_value(&base_file_id))],
|
||||
vec![
|
||||
Some(file_value(&left_file_id)),
|
||||
|
@ -691,7 +691,7 @@ fn test_update_conflict_from_content() {
|
|||
let new_right_file_id = testutils::write_file(store, &path, "resolved 1\nline 2\nright 3\n");
|
||||
assert_eq!(
|
||||
new_conflict,
|
||||
Conflict::new(
|
||||
Merge::new(
|
||||
vec![Some(file_value(&new_base_file_id))],
|
||||
vec![
|
||||
Some(file_value(&new_left_file_id)),
|
||||
|
@ -709,7 +709,7 @@ fn test_update_conflict_from_content_modify_delete() {
|
|||
let path = RepoPath::from_internal_string("dir/file");
|
||||
let before_file_id = testutils::write_file(store, &path, "line 1\nline 2 before\nline 3\n");
|
||||
let after_file_id = testutils::write_file(store, &path, "line 1\nline 2 after\nline 3\n");
|
||||
let conflict = Conflict::new(
|
||||
let conflict = Merge::new(
|
||||
vec![Some(file_value(&before_file_id))],
|
||||
vec![Some(file_value(&after_file_id)), None],
|
||||
);
|
||||
|
@ -746,7 +746,7 @@ fn test_update_conflict_from_content_modify_delete() {
|
|||
|
||||
assert_eq!(
|
||||
new_conflict,
|
||||
Conflict::new(
|
||||
Merge::new(
|
||||
vec![Some(file_value(&new_base_file_id))],
|
||||
vec![Some(file_value(&new_left_file_id)), None]
|
||||
)
|
||||
|
@ -756,7 +756,7 @@ fn test_update_conflict_from_content_modify_delete() {
|
|||
fn materialize_conflict_string(
|
||||
store: &Store,
|
||||
path: &RepoPath,
|
||||
conflict: &Conflict<Option<TreeValue>>,
|
||||
conflict: &Merge<Option<TreeValue>>,
|
||||
) -> String {
|
||||
let mut result: Vec<u8> = vec![];
|
||||
conflict.materialize(store, path, &mut result).unwrap();
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
|
||||
use itertools::Itertools;
|
||||
use jj_lib::backend::{FileId, TreeValue};
|
||||
use jj_lib::conflicts::Conflict;
|
||||
use jj_lib::conflicts::Merge;
|
||||
use jj_lib::merged_tree::{MergedTree, MergedTreeValue};
|
||||
use jj_lib::repo::Repo;
|
||||
use jj_lib::repo_path::{RepoPath, RepoPathComponent, RepoPathJoin};
|
||||
|
@ -45,7 +45,7 @@ fn test_from_legacy_tree() {
|
|||
let file2_v1_id = write_file(store.as_ref(), &file2_path, "file2_v1");
|
||||
let file2_v2_id = write_file(store.as_ref(), &file2_path, "file2_v2");
|
||||
let file2_v3_id = write_file(store.as_ref(), &file2_path, "file2_v3");
|
||||
let file2_conflict = Conflict::new(
|
||||
let file2_conflict = Merge::new(
|
||||
vec![Some(file_value(&file2_v1_id))],
|
||||
vec![
|
||||
Some(file_value(&file2_v2_id)),
|
||||
|
@ -59,7 +59,7 @@ fn test_from_legacy_tree() {
|
|||
let file3_path = RepoPath::from_internal_string("modify_delete");
|
||||
let file3_v1_id = write_file(store.as_ref(), &file3_path, "file3_v1");
|
||||
let file3_v2_id = write_file(store.as_ref(), &file3_path, "file3_v2");
|
||||
let file3_conflict = Conflict::new(
|
||||
let file3_conflict = Merge::new(
|
||||
vec![Some(file_value(&file3_v1_id))],
|
||||
vec![Some(file_value(&file3_v2_id)), None],
|
||||
);
|
||||
|
@ -70,7 +70,7 @@ fn test_from_legacy_tree() {
|
|||
let file4_path = RepoPath::from_internal_string("add_add");
|
||||
let file4_v1_id = write_file(store.as_ref(), &file4_path, "file4_v1");
|
||||
let file4_v2_id = write_file(store.as_ref(), &file4_path, "file4_v2");
|
||||
let file4_conflict = Conflict::new(
|
||||
let file4_conflict = Merge::new(
|
||||
vec![None],
|
||||
vec![
|
||||
Some(file_value(&file4_v1_id)),
|
||||
|
@ -87,7 +87,7 @@ fn test_from_legacy_tree() {
|
|||
let file5_v3_id = write_file(store.as_ref(), &file5_path, "file5_v3");
|
||||
let file5_v4_id = write_file(store.as_ref(), &file5_path, "file5_v4");
|
||||
let file5_v5_id = write_file(store.as_ref(), &file5_path, "file5_v5");
|
||||
let file5_conflict = Conflict::new(
|
||||
let file5_conflict = Merge::new(
|
||||
vec![
|
||||
Some(file_value(&file5_v1_id)),
|
||||
Some(file_value(&file5_v2_id)),
|
||||
|
@ -130,7 +130,7 @@ fn test_from_legacy_tree() {
|
|||
// file2: 3-way conflict
|
||||
assert_eq!(
|
||||
merged_tree.value(&file2_path.components()[0]),
|
||||
MergedTreeValue::Conflict(Conflict::new(
|
||||
MergedTreeValue::Conflict(Merge::new(
|
||||
vec![Some(file_value(&file2_v1_id)), None],
|
||||
vec![
|
||||
Some(file_value(&file2_v2_id)),
|
||||
|
@ -142,7 +142,7 @@ fn test_from_legacy_tree() {
|
|||
// file3: modify/delete conflict
|
||||
assert_eq!(
|
||||
merged_tree.value(&file3_path.components()[0]),
|
||||
MergedTreeValue::Conflict(Conflict::new(
|
||||
MergedTreeValue::Conflict(Merge::new(
|
||||
vec![Some(file_value(&file3_v1_id)), None],
|
||||
vec![Some(file_value(&file3_v2_id)), None, None],
|
||||
))
|
||||
|
@ -150,7 +150,7 @@ fn test_from_legacy_tree() {
|
|||
// file4: add/add conflict
|
||||
assert_eq!(
|
||||
merged_tree.value(&file4_path.components()[0]),
|
||||
MergedTreeValue::Conflict(Conflict::new(
|
||||
MergedTreeValue::Conflict(Merge::new(
|
||||
vec![None, None],
|
||||
vec![
|
||||
Some(file_value(&file4_v1_id)),
|
||||
|
@ -162,7 +162,7 @@ fn test_from_legacy_tree() {
|
|||
// file5: 5-way conflict
|
||||
assert_eq!(
|
||||
merged_tree.value(&file5_path.components()[0]),
|
||||
MergedTreeValue::Conflict(Conflict::new(
|
||||
MergedTreeValue::Conflict(Merge::new(
|
||||
vec![
|
||||
Some(file_value(&file5_v1_id)),
|
||||
Some(file_value(&file5_v2_id)),
|
||||
|
@ -236,7 +236,7 @@ fn test_resolve_success() {
|
|||
],
|
||||
);
|
||||
|
||||
let tree = MergedTree::new(Conflict::new(vec![base1], vec![side1, side2]));
|
||||
let tree = MergedTree::new(Merge::new(vec![base1], vec![side1, side2]));
|
||||
let resolved = tree.resolve().unwrap();
|
||||
let resolved_tree = resolved.as_resolved().unwrap().clone();
|
||||
assert_eq!(
|
||||
|
@ -260,7 +260,7 @@ fn test_resolve_root_becomes_empty() {
|
|||
let side1 = testutils::create_tree(repo, &[(&path2, "base1")]);
|
||||
let side2 = testutils::create_tree(repo, &[(&path1, "base1")]);
|
||||
|
||||
let tree = MergedTree::new(Conflict::new(vec![base1], vec![side1, side2]));
|
||||
let tree = MergedTree::new(Merge::new(vec![base1], vec![side1, side2]));
|
||||
let resolved = tree.resolve().unwrap();
|
||||
assert_eq!(resolved.as_resolved().unwrap().id(), store.empty_tree_id());
|
||||
}
|
||||
|
@ -287,11 +287,11 @@ fn test_resolve_with_conflict() {
|
|||
let expected_side2 =
|
||||
testutils::create_tree(repo, &[(&trivial_path, "side1"), (&conflict_path, "side2")]);
|
||||
|
||||
let tree = MergedTree::new(Conflict::new(vec![base1], vec![side1, side2]));
|
||||
let tree = MergedTree::new(Merge::new(vec![base1], vec![side1, side2]));
|
||||
let resolved_tree = tree.resolve().unwrap();
|
||||
assert_eq!(
|
||||
resolved_tree,
|
||||
Conflict::new(vec![expected_base1], vec![expected_side1, expected_side2])
|
||||
Merge::new(vec![expected_base1], vec![expected_side1, expected_side2])
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -368,13 +368,13 @@ fn test_conflict_iterator() {
|
|||
],
|
||||
);
|
||||
|
||||
let tree = MergedTree::new(Conflict::new(
|
||||
let tree = MergedTree::new(Merge::new(
|
||||
vec![base1.clone()],
|
||||
vec![side1.clone(), side2.clone()],
|
||||
));
|
||||
let conflicts = tree.conflicts().collect_vec();
|
||||
let conflict_at = |path: &RepoPath| {
|
||||
Conflict::new(
|
||||
Merge::new(
|
||||
vec![base1.path_value(path)],
|
||||
vec![side1.path_value(path), side2.path_value(path)],
|
||||
)
|
||||
|
@ -439,13 +439,13 @@ fn test_conflict_iterator_higher_arity() {
|
|||
&[(&two_sided_path, "side3"), (&three_sided_path, "side3")],
|
||||
);
|
||||
|
||||
let tree = MergedTree::new(Conflict::new(
|
||||
let tree = MergedTree::new(Merge::new(
|
||||
vec![base1.clone(), base2.clone()],
|
||||
vec![side1.clone(), side2.clone(), side3.clone()],
|
||||
));
|
||||
let conflicts = tree.conflicts().collect_vec();
|
||||
let conflict_at = |path: &RepoPath| {
|
||||
Conflict::new(
|
||||
Merge::new(
|
||||
vec![base1.path_value(path), base2.path_value(path)],
|
||||
vec![
|
||||
side1.path_value(path),
|
||||
|
@ -474,7 +474,7 @@ fn test_conflict_iterator_higher_arity() {
|
|||
vec![
|
||||
(
|
||||
two_sided_path.clone(),
|
||||
Conflict::new(
|
||||
Merge::new(
|
||||
vec![base2.path_value(&two_sided_path)],
|
||||
vec![
|
||||
side1.path_value(&two_sided_path),
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use jj_lib::conflicts::Conflict;
|
||||
use jj_lib::conflicts::Merge;
|
||||
use jj_lib::op_store::RefTarget;
|
||||
use jj_lib::refs::merge_ref_targets;
|
||||
use jj_lib::repo::Repo;
|
||||
|
@ -167,7 +167,7 @@ fn test_merge_ref_targets() {
|
|||
// Left removed, right moved forward
|
||||
assert_eq!(
|
||||
merge_ref_targets(index, RefTarget::absent_ref(), &target1, &target3),
|
||||
RefTarget::from_conflict(Conflict::new(
|
||||
RefTarget::from_merge(Merge::new(
|
||||
vec![Some(commit1.id().clone())],
|
||||
vec![None, Some(commit3.id().clone())],
|
||||
))
|
||||
|
@ -176,7 +176,7 @@ fn test_merge_ref_targets() {
|
|||
// Right removed, left moved forward
|
||||
assert_eq!(
|
||||
merge_ref_targets(index, &target3, &target1, RefTarget::absent_ref()),
|
||||
RefTarget::from_conflict(Conflict::new(
|
||||
RefTarget::from_merge(Merge::new(
|
||||
vec![Some(commit1.id().clone())],
|
||||
vec![Some(commit3.id().clone()), None],
|
||||
))
|
||||
|
|
|
@ -22,7 +22,7 @@ use std::sync::Arc;
|
|||
|
||||
use itertools::Itertools;
|
||||
use jj_lib::backend::{ObjectId, TreeId, TreeValue};
|
||||
use jj_lib::conflicts::Conflict;
|
||||
use jj_lib::conflicts::Merge;
|
||||
use jj_lib::fsmonitor::FsmonitorKind;
|
||||
use jj_lib::op_store::{OperationId, WorkspaceId};
|
||||
use jj_lib::repo::{ReadonlyRepo, Repo};
|
||||
|
@ -117,7 +117,7 @@ fn test_checkout_file_transitions(use_git: bool) {
|
|||
let base_file_id = testutils::write_file(store, path, "base file contents");
|
||||
let left_file_id = testutils::write_file(store, path, "left file contents");
|
||||
let right_file_id = testutils::write_file(store, path, "right file contents");
|
||||
let conflict = Conflict::new(
|
||||
let conflict = Merge::new(
|
||||
vec![Some(TreeValue::File {
|
||||
id: base_file_id,
|
||||
executable: false,
|
||||
|
|
Loading…
Reference in a new issue