use std::cmp::max;
use std::collections::BTreeMap;
use std::iter::zip;
use std::sync::Arc;
use std::{iter, vec};
use futures::executor::block_on;
use futures::stream::StreamExt;
use futures::TryStreamExt;
use itertools::Itertools;
use crate::backend::{BackendError, BackendResult, ConflictId, MergedTreeId, TreeId, TreeValue};
use crate::matchers::{EverythingMatcher, Matcher};
use crate::merge::{Merge, MergeBuilder, MergedTreeValue};
use crate::repo_path::{RepoPath, RepoPathComponent, RepoPathJoin};
use crate::store::Store;
use crate::tree::{try_resolve_file_conflict, Tree, TreeMergeError};
use crate::tree_builder::TreeBuilder;
use crate::{backend, tree};
#[derive(PartialEq, Eq, Clone, Debug)]
pub enum MergedTree {
Legacy(Tree),
Merge(Merge<Tree>),
}
#[derive(PartialEq, Eq, Hash, Clone, Debug)]
pub enum MergedTreeVal<'a> {
Resolved(Option<&'a TreeValue>),
Conflict(MergedTreeValue),
}
impl MergedTreeVal<'_> {
fn to_merge(&self) -> MergedTreeValue {
match self {
MergedTreeVal::Resolved(value) => Merge::resolved(value.cloned()),
MergedTreeVal::Conflict(merge) => merge.clone(),
}
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct DiffSummary {
pub modified: Vec<RepoPath>,
pub added: Vec<RepoPath>,
pub removed: Vec<RepoPath>,
}
impl MergedTree {
pub fn resolved(tree: Tree) -> Self {
MergedTree::new(Merge::resolved(tree))
}
pub fn new(trees: Merge<Tree>) -> Self {
debug_assert!(!trees.removes().iter().any(|t| t.has_conflict()));
debug_assert!(!trees.adds().iter().any(|t| t.has_conflict()));
debug_assert!(itertools::chain(trees.removes(), trees.adds())
.map(|tree| tree.dir())
.all_equal());
debug_assert!(itertools::chain(trees.removes(), trees.adds())
.map(|tree| Arc::as_ptr(tree.store()))
.all_equal());
MergedTree::Merge(trees)
}
pub fn legacy(tree: Tree) -> Self {
MergedTree::Legacy(tree)
}
pub fn from_legacy_tree(tree: Tree) -> BackendResult<Self> {
let conflict_ids = tree.conflicts();
if conflict_ids.is_empty() {
return Ok(MergedTree::resolved(tree));
}
let mut max_num_removes = 0;
let store = tree.store();
let mut conflicts: Vec<(&RepoPath, MergedTreeValue)> = vec![];
for (path, conflict_id) in &conflict_ids {
let conflict = store.read_conflict(path, conflict_id)?;
max_num_removes = max(max_num_removes, conflict.removes().len());
conflicts.push((path, conflict));
}
let mut removes = vec![];
let mut adds = vec![store.tree_builder(tree.id().clone())];
for _ in 0..max_num_removes {
removes.push(store.tree_builder(tree.id().clone()));
adds.push(store.tree_builder(tree.id().clone()));
}
for (path, conflict) in conflicts {
let num_removes = conflict.removes().len();
for i in num_removes..max_num_removes {
removes[i].remove(path.clone());
adds[i + 1].remove(path.clone());
}
for (i, term) in conflict.removes().iter().enumerate() {
removes[i].set_or_remove(path.clone(), term.clone());
}
for (i, term) in conflict.adds().iter().enumerate() {
adds[i].set_or_remove(path.clone(), term.clone());
}
}
let write_tree = |builder: TreeBuilder| {
let tree_id = builder.write_tree();
store.get_tree(&RepoPath::root(), &tree_id)
};
let removed_trees = removes.into_iter().map(write_tree).try_collect()?;
let added_trees = adds.into_iter().map(write_tree).try_collect()?;
Ok(MergedTree::Merge(Merge::new(removed_trees, added_trees)))
}
pub fn dir(&self) -> &RepoPath {
match self {
MergedTree::Legacy(tree) => tree.dir(),
MergedTree::Merge(conflict) => conflict.adds()[0].dir(),
}
}
pub fn store(&self) -> &Arc<Store> {
match self {
MergedTree::Legacy(tree) => tree.store(),
MergedTree::Merge(trees) => trees.adds()[0].store(),
}
}
pub fn names<'a>(&'a self) -> Box<dyn Iterator<Item = &'a RepoPathComponent> + 'a> {
match self {
MergedTree::Legacy(tree) => Box::new(tree.data().names()),
MergedTree::Merge(conflict) => Box::new(all_tree_conflict_names(conflict)),
}
}
pub fn value(&self, basename: &RepoPathComponent) -> MergedTreeVal {
match self {
MergedTree::Legacy(tree) => match tree.value(basename) {
Some(TreeValue::Conflict(conflict_id)) => {
let path = tree.dir().join(basename);
let conflict = tree.store().read_conflict(&path, conflict_id).unwrap();
MergedTreeVal::Conflict(conflict)
}
other => MergedTreeVal::Resolved(other),
},
MergedTree::Merge(trees) => {
if let Some(tree) = trees.as_resolved() {
return MergedTreeVal::Resolved(tree.value(basename));
}
let value = trees.map(|tree| tree.value(basename));
if let Some(resolved) = value.resolve_trivial() {
return MergedTreeVal::Resolved(*resolved);
}
MergedTreeVal::Conflict(value.map(|x| x.cloned()))
}
}
}
pub fn resolve(&self) -> Result<Merge<Tree>, TreeMergeError> {
match self {
MergedTree::Legacy(tree) => Ok(Merge::resolved(tree.clone())),
MergedTree::Merge(trees) => merge_trees(trees),
}
}
pub fn conflicts(&self) -> impl Iterator<Item = (RepoPath, MergedTreeValue)> {
ConflictIterator::new(self.clone())
}
pub fn has_conflict(&self) -> bool {
match self {
MergedTree::Legacy(tree) => tree.has_conflict(),
MergedTree::Merge(trees) => !trees.is_resolved(),
}
}
pub fn sub_tree(&self, name: &RepoPathComponent) -> Option<MergedTree> {
if let MergedTree::Legacy(tree) = self {
tree.sub_tree(name).map(MergedTree::Legacy)
} else {
match self.value(name) {
MergedTreeVal::Resolved(Some(TreeValue::Tree(sub_tree_id))) => {
let subdir = self.dir().join(name);
Some(MergedTree::resolved(
self.store().get_tree(&subdir, sub_tree_id).unwrap(),
))
}
MergedTreeVal::Resolved(_) => None,
MergedTreeVal::Conflict(merge) => {
let merged_trees = merge.map(|value| match value {
Some(TreeValue::Tree(sub_tree_id)) => {
let subdir = self.dir().join(name);
self.store().get_tree(&subdir, sub_tree_id).unwrap()
}
_ => {
let subdir = self.dir().join(name);
Tree::null(self.store().clone(), subdir.clone())
}
});
Some(MergedTree::Merge(merged_trees))
}
}
}
}
pub fn path_value(&self, path: &RepoPath) -> MergedTreeValue {
assert_eq!(self.dir(), &RepoPath::root());
match path.split() {
Some((dir, basename)) => match self.sub_tree_recursive(dir.components()) {
None => Merge::absent(),
Some(tree) => tree.value(basename).to_merge(),
},
None => match self {
MergedTree::Legacy(tree) => Merge::normal(TreeValue::Tree(tree.id().clone())),
MergedTree::Merge(trees) => {
trees.map(|tree| Some(TreeValue::Tree(tree.id().clone())))
}
},
}
}
pub fn id(&self) -> MergedTreeId {
match self {
MergedTree::Legacy(tree) => MergedTreeId::Legacy(tree.id().clone()),
MergedTree::Merge(merge) => MergedTreeId::Merge(merge.map(|tree| tree.id().clone())),
}
}
fn sub_tree_recursive(&self, components: &[RepoPathComponent]) -> Option<MergedTree> {
if let Some((first, tail)) = components.split_first() {
tail.iter()
.try_fold(self.sub_tree(first)?, |tree, name| tree.sub_tree(name))
} else {
Some(self.clone())
}
}
fn entries_non_recursive(&self) -> TreeEntriesNonRecursiveIterator {
TreeEntriesNonRecursiveIterator::new(self)
}
pub fn entries(&self) -> TreeEntriesIterator<'static> {
TreeEntriesIterator::new(self.clone(), &EverythingMatcher)
}
pub fn entries_matching<'matcher>(
&self,
matcher: &'matcher dyn Matcher,
) -> TreeEntriesIterator<'matcher> {
TreeEntriesIterator::new(self.clone(), matcher)
}
pub fn diff<'matcher>(
&self,
other: &MergedTree,
matcher: &'matcher dyn Matcher,
) -> TreeDiffIterator<'matcher> {
TreeDiffIterator::new(self.clone(), other.clone(), matcher)
}
pub fn diff_summary(
&self,
other: &MergedTree,
matcher: &dyn Matcher,
) -> BackendResult<DiffSummary> {
let mut modified = vec![];
let mut added = vec![];
let mut removed = vec![];
for (file, diff) in self.diff(other, matcher) {
let (before, after) = diff?;
if before.is_absent() {
added.push(file);
} else if after.is_absent() {
removed.push(file);
} else {
modified.push(file);
}
}
modified.sort();
added.sort();
removed.sort();
Ok(DiffSummary {
modified,
added,
removed,
})
}
pub fn merge(
&self,
base: &MergedTree,
other: &MergedTree,
) -> Result<MergedTree, TreeMergeError> {
if let (MergedTree::Legacy(this), MergedTree::Legacy(base), MergedTree::Legacy(other)) =
(self, base, other)
{
let merged_tree = tree::merge_trees(this, base, other)?;
Ok(MergedTree::legacy(merged_tree))
} else {
let to_merge = |tree: &MergedTree| -> Result<Merge<Tree>, TreeMergeError> {
match tree {
MergedTree::Legacy(tree) => {
let MergedTree::Merge(tree) = MergedTree::from_legacy_tree(tree.clone())?
else {
unreachable!();
};
Ok(tree)
}
MergedTree::Merge(conflict) => Ok(conflict.clone()),
}
};
let nested = Merge::new(
vec![to_merge(base)?],
vec![to_merge(self)?, to_merge(other)?],
);
let tree = merge_trees(&nested.flatten().simplify())?;
let tree = tree.simplify();
if cfg!(debug_assertions) {
let re_merged = merge_trees(&tree).unwrap();
debug_assert_eq!(re_merged, tree);
}
Ok(MergedTree::Merge(tree))
}
}
}
fn all_tree_conflict_names(trees: &Merge<Tree>) -> impl Iterator<Item = &RepoPathComponent> {
itertools::chain(trees.removes(), trees.adds())
.map(|tree| tree.data().names())
.kmerge()
.dedup()
}
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 = &merge.adds()[0];
let store = base_tree.store();
let dir = base_tree.dir();
let mut new_tree = backend::Tree::default();
let mut conflicts = vec![];
for basename in all_tree_conflict_names(merge) {
let path_merge = merge.map(|tree| tree.value(basename).cloned());
let path = dir.join(basename);
let path_merge = merge_tree_values(store, &path, path_merge)?;
match path_merge.into_resolved() {
Ok(value) => {
new_tree.set_or_remove(basename, value);
}
Err(path_merge) => {
conflicts.push((basename, path_merge));
}
};
}
if conflicts.is_empty() {
let new_tree_id = store.write_tree(dir, new_tree)?;
Ok(Merge::resolved(new_tree_id))
} else {
let mut tree_removes = vec![];
for i in 0..merge.removes().len() {
for (basename, path_conflict) in &conflicts {
new_tree.set_or_remove(basename, path_conflict.removes()[i].clone());
}
let tree = store.write_tree(dir, new_tree.clone())?;
tree_removes.push(tree);
}
let mut tree_adds = vec![];
for i in 0..merge.adds().len() {
for (basename, path_conflict) in &conflicts {
new_tree.set_or_remove(basename, path_conflict.adds()[i].clone());
}
let tree = store.write_tree(dir, new_tree.clone())?;
tree_adds.push(tree);
}
Ok(Merge::new(tree_removes, tree_adds))
}
}
fn merge_tree_values(
store: &Arc<Store>,
path: &RepoPath,
values: MergedTreeValue,
) -> Result<MergedTreeValue, TreeMergeError> {
if let Some(resolved) = values.resolve_trivial() {
return Ok(Merge::resolved(resolved.clone()));
}
if let Some(trees) = values.to_tree_merge(store, path)? {
let merged_tree = merge_trees(&trees)?;
if merged_tree.as_resolved().map(|tree| tree.id()) == Some(store.empty_tree_id()) {
Ok(Merge::absent())
} else {
Ok(merged_tree.map(|tree| Some(TreeValue::Tree(tree.id().clone()))))
}
} else {
if let Some(resolved) = try_resolve_file_conflict(store, path, &values)? {
Ok(Merge::normal(resolved))
} else {
Ok(values)
}
}
}
struct TreeEntriesNonRecursiveIterator<'a> {
merged_tree: &'a MergedTree,
basename_iter: Box<dyn Iterator<Item = &'a RepoPathComponent> + 'a>,
}
impl<'a> TreeEntriesNonRecursiveIterator<'a> {
fn new(merged_tree: &'a MergedTree) -> Self {
TreeEntriesNonRecursiveIterator {
merged_tree,
basename_iter: merged_tree.names(),
}
}
}
impl<'a> Iterator for TreeEntriesNonRecursiveIterator<'a> {
type Item = (&'a RepoPathComponent, MergedTreeVal<'a>);
fn next(&mut self) -> Option<Self::Item> {
self.basename_iter.next().map(|basename| {
let value = self.merged_tree.value(basename);
(basename, value)
})
}
}
pub struct TreeEntriesIterator<'matcher> {
stack: Vec<TreeEntriesDirItem>,
matcher: &'matcher dyn Matcher,
}
struct TreeEntriesDirItem {
entry_iterator: TreeEntriesNonRecursiveIterator<'static>,
tree: Box<MergedTree>,
}
impl TreeEntriesDirItem {
fn new(tree: MergedTree) -> Self {
let tree = Box::new(tree);
let entry_iterator = tree.entries_non_recursive();
let entry_iterator: TreeEntriesNonRecursiveIterator<'static> =
unsafe { std::mem::transmute(entry_iterator) };
Self {
entry_iterator,
tree,
}
}
}
impl<'matcher> TreeEntriesIterator<'matcher> {
fn new(tree: MergedTree, matcher: &'matcher dyn Matcher) -> Self {
Self {
stack: vec![TreeEntriesDirItem::new(tree)],
matcher,
}
}
}
impl Iterator for TreeEntriesIterator<'_> {
type Item = (RepoPath, MergedTreeValue);
fn next(&mut self) -> Option<Self::Item> {
while let Some(top) = self.stack.last_mut() {
if let Some((name, value)) = top.entry_iterator.next() {
let path = top.tree.dir().join(name);
let value = value.to_merge();
if value.is_tree() {
if self.matcher.visit(&path).is_nothing() {
continue;
}
let tree_merge = value
.to_tree_merge(top.tree.store(), &path)
.unwrap()
.unwrap();
let merged_tree = MergedTree::Merge(tree_merge);
self.stack.push(TreeEntriesDirItem::new(merged_tree));
} else if self.matcher.matches(&path) {
return Some((path, value));
}
} else {
self.stack.pop();
}
}
None
}
}
struct ConflictEntriesNonRecursiveIterator<'a> {
merged_tree: &'a MergedTree,
basename_iter: Box<dyn Iterator<Item = &'a RepoPathComponent> + 'a>,
}
impl<'a> ConflictEntriesNonRecursiveIterator<'a> {
fn new(merged_tree: &'a MergedTree) -> Self {
let basename_iter: Box<dyn Iterator<Item = &'a RepoPathComponent> + 'a> = match merged_tree
{
MergedTree::Legacy(tree) => Box::new(
tree.entries_non_recursive()
.filter(|entry| matches!(entry.value(), &TreeValue::Conflict(_)))
.map(|entry| entry.name()),
),
MergedTree::Merge(trees) => {
if trees.is_resolved() {
Box::new(iter::empty())
} else {
Box::new(all_tree_conflict_names(trees))
}
}
};
ConflictEntriesNonRecursiveIterator {
merged_tree,
basename_iter,
}
}
}
impl<'a> Iterator for ConflictEntriesNonRecursiveIterator<'a> {
type Item = (&'a RepoPathComponent, MergedTreeValue);
fn next(&mut self) -> Option<Self::Item> {
for basename in self.basename_iter.by_ref() {
match self.merged_tree.value(basename) {
MergedTreeVal::Resolved(_) => {}
MergedTreeVal::Conflict(tree_values) => {
return Some((basename, tree_values));
}
}
}
None
}
}
struct ConflictsDirItem {
entry_iterator: ConflictEntriesNonRecursiveIterator<'static>,
tree: Box<MergedTree>,
}
impl ConflictsDirItem {
fn new(tree: MergedTree) -> Self {
let tree = Box::new(tree);
let entry_iterator = ConflictEntriesNonRecursiveIterator::new(&tree);
let entry_iterator: ConflictEntriesNonRecursiveIterator<'static> =
unsafe { std::mem::transmute(entry_iterator) };
Self {
entry_iterator,
tree,
}
}
}
enum ConflictIterator {
Legacy {
store: Arc<Store>,
conflicts_iter: vec::IntoIter<(RepoPath, ConflictId)>,
},
Merge {
stack: Vec<ConflictsDirItem>,
},
}
impl ConflictIterator {
fn new(tree: MergedTree) -> Self {
match tree {
MergedTree::Legacy(tree) => ConflictIterator::Legacy {
store: tree.store().clone(),
conflicts_iter: tree.conflicts().into_iter(),
},
MergedTree::Merge(_) => ConflictIterator::Merge {
stack: vec![ConflictsDirItem::new(tree)],
},
}
}
}
impl Iterator for ConflictIterator {
type Item = (RepoPath, MergedTreeValue);
fn next(&mut self) -> Option<Self::Item> {
match self {
ConflictIterator::Legacy {
store,
conflicts_iter,
} => {
if let Some((path, conflict_id)) = conflicts_iter.next() {
let conflict = store.read_conflict(&path, &conflict_id).unwrap();
Some((path, conflict))
} else {
None
}
}
ConflictIterator::Merge { stack } => {
while let Some(top) = stack.last_mut() {
if let Some((basename, tree_values)) = top.entry_iterator.next() {
let path = top.tree.dir().join(basename);
if let Some(trees) =
tree_values.to_tree_merge(top.tree.store(), &path).unwrap()
{
stack.push(ConflictsDirItem::new(MergedTree::Merge(trees)));
} else {
return Some((path, tree_values));
}
} else {
stack.pop();
}
}
None
}
}
}
}
struct TreeEntryDiffIterator<'a> {
before: &'a MergedTree,
after: &'a MergedTree,
basename_iter: Box<dyn Iterator<Item = &'a RepoPathComponent> + 'a>,
}
impl<'a> TreeEntryDiffIterator<'a> {
fn new(before: &'a MergedTree, after: &'a MergedTree) -> Self {
fn merge_iters<'a>(
iter1: impl Iterator<Item = &'a RepoPathComponent> + 'a,
iter2: impl Iterator<Item = &'a RepoPathComponent> + 'a,
) -> Box<dyn Iterator<Item = &'a RepoPathComponent> + 'a> {
Box::new(iter1.merge(iter2).dedup())
}
let basename_iter: Box<dyn Iterator<Item = &'a RepoPathComponent> + 'a> =
match (before, after) {
(MergedTree::Legacy(before), MergedTree::Legacy(after)) => {
merge_iters(before.data().names(), after.data().names())
}
(MergedTree::Merge(before), MergedTree::Legacy(after)) => {
merge_iters(all_tree_conflict_names(before), after.data().names())
}
(MergedTree::Legacy(before), MergedTree::Merge(after)) => {
merge_iters(before.data().names(), all_tree_conflict_names(after))
}
(MergedTree::Merge(before), MergedTree::Merge(after)) => merge_iters(
all_tree_conflict_names(before),
all_tree_conflict_names(after),
),
};
TreeEntryDiffIterator {
before,
after,
basename_iter,
}
}
}
impl<'a> Iterator for TreeEntryDiffIterator<'a> {
type Item = (&'a RepoPathComponent, MergedTreeVal<'a>, MergedTreeVal<'a>);
fn next(&mut self) -> Option<Self::Item> {
for basename in self.basename_iter.by_ref() {
let value_before = self.before.value(basename);
let value_after = self.after.value(basename);
if value_after != value_before {
return Some((basename, value_before, value_after));
}
}
None
}
}
pub struct TreeDiffIterator<'matcher> {
stack: Vec<TreeDiffItem>,
matcher: &'matcher dyn Matcher,
}
struct TreeDiffDirItem {
path: RepoPath,
entry_iterator: TreeEntryDiffIterator<'static>,
tree1: Box<MergedTree>,
tree2: Box<MergedTree>,
}
enum TreeDiffItem {
Dir(TreeDiffDirItem),
File(RepoPath, MergedTreeValue, MergedTreeValue),
}
impl<'matcher> TreeDiffIterator<'matcher> {
fn new(tree1: MergedTree, tree2: MergedTree, matcher: &'matcher dyn Matcher) -> Self {
let root_dir = RepoPath::root();
let mut stack = Vec::new();
if !matcher.visit(&root_dir).is_nothing() {
stack.push(TreeDiffItem::Dir(TreeDiffDirItem::new(
root_dir, tree1, tree2,
)));
};
Self { stack, matcher }
}
async fn single_tree(
store: &Arc<Store>,
dir: &RepoPath,
value: Option<&TreeValue>,
) -> BackendResult<Tree> {
match value {
Some(TreeValue::Tree(tree_id)) => store.get_tree_async(dir, tree_id).await,
_ => Ok(Tree::null(store.clone(), dir.clone())),
}
}
async fn tree(
tree: &MergedTree,
dir: &RepoPath,
values: &MergedTreeValue,
) -> BackendResult<MergedTree> {
let trees = if values.is_tree() {
let builder: MergeBuilder<Tree> = futures::stream::iter(values.iter())
.then(|value| Self::single_tree(tree.store(), dir, value.as_ref()))
.try_collect()
.await?;
builder.build()
} else {
Merge::resolved(Tree::null(tree.store().clone(), dir.clone()))
};
match tree {
MergedTree::Legacy(_) => Ok(MergedTree::Legacy(trees.into_resolved().unwrap())),
MergedTree::Merge(_) => Ok(MergedTree::Merge(trees)),
}
}
}
impl TreeDiffDirItem {
fn new(path: RepoPath, tree1: MergedTree, tree2: MergedTree) -> Self {
let tree1 = Box::new(tree1);
let tree2 = Box::new(tree2);
let iter: TreeEntryDiffIterator = TreeEntryDiffIterator::new(&tree1, &tree2);
let iter: TreeEntryDiffIterator<'static> = unsafe { std::mem::transmute(iter) };
Self {
path,
entry_iterator: iter,
tree1,
tree2,
}
}
}
impl Iterator for TreeDiffIterator<'_> {
type Item = (RepoPath, BackendResult<(MergedTreeValue, MergedTreeValue)>);
fn next(&mut self) -> Option<Self::Item> {
while let Some(top) = self.stack.last_mut() {
let (dir, (name, before, after)) = match top {
TreeDiffItem::Dir(dir) => {
if let Some((name, before, after)) = dir.entry_iterator.next() {
(dir, (name, before.to_merge(), after.to_merge()))
} else {
self.stack.pop().unwrap();
continue;
}
}
TreeDiffItem::File(..) => {
if let TreeDiffItem::File(name, before, after) = self.stack.pop().unwrap() {
return Some((name, Ok((before, after))));
} else {
unreachable!();
}
}
};
let path = dir.path.join(name);
let tree_before = before.is_tree();
let tree_after = after.is_tree();
let post_subdir =
if (tree_before || tree_after) && !self.matcher.visit(&path).is_nothing() {
let (before_tree, after_tree) = block_on(async {
let before_tree = Self::tree(dir.tree1.as_ref(), &path, &before);
let after_tree = Self::tree(dir.tree2.as_ref(), &path, &after);
futures::join!(before_tree, after_tree)
});
let before_tree = match before_tree {
Ok(tree) => tree,
Err(err) => {
return Some((path, Err(err)));
}
};
let after_tree = match after_tree {
Ok(tree) => tree,
Err(err) => {
return Some((path, Err(err)));
}
};
let subdir = TreeDiffDirItem::new(path.clone(), before_tree, after_tree);
self.stack.push(TreeDiffItem::Dir(subdir));
self.stack.len() - 1
} else {
self.stack.len()
};
if self.matcher.matches(&path) {
if !tree_before && tree_after {
if before.is_present() {
return Some((path, Ok((before, Merge::absent()))));
}
} else if tree_before && !tree_after {
if after.is_present() {
self.stack.insert(
post_subdir,
TreeDiffItem::File(path, Merge::absent(), after),
);
}
} else if !tree_before && !tree_after {
return Some((path, Ok((before, after))));
}
}
}
None
}
}
pub struct MergedTreeBuilder {
base_tree_id: MergedTreeId,
overrides: BTreeMap<RepoPath, MergedTreeValue>,
}
impl MergedTreeBuilder {
pub fn new(base_tree_id: MergedTreeId) -> Self {
MergedTreeBuilder {
base_tree_id,
overrides: BTreeMap::new(),
}
}
pub fn set_or_remove(&mut self, path: RepoPath, values: MergedTreeValue) {
if let MergedTreeId::Merge(_) = &self.base_tree_id {
assert!(!values
.iter()
.flatten()
.any(|value| matches!(value, TreeValue::Conflict(_))));
}
self.overrides.insert(path, values);
}
pub fn write_tree(self, store: &Arc<Store>) -> BackendResult<MergedTreeId> {
match self.base_tree_id.clone() {
MergedTreeId::Legacy(base_tree_id) => {
let mut tree_builder = TreeBuilder::new(store.clone(), base_tree_id);
for (path, values) in self.overrides {
let values = values.simplify();
match values.into_resolved() {
Ok(value) => {
tree_builder.set_or_remove(path, value);
}
Err(values) => {
let conflict_id = store.write_conflict(&path, &values)?;
tree_builder.set(path, TreeValue::Conflict(conflict_id));
}
}
}
let legacy_id = tree_builder.write_tree();
if store.use_tree_conflict_format() {
let legacy_tree = store.get_tree(&RepoPath::root(), &legacy_id)?;
let merged_tree = MergedTree::from_legacy_tree(legacy_tree)?;
Ok(merged_tree.id())
} else {
Ok(MergedTreeId::Legacy(legacy_id))
}
}
MergedTreeId::Merge(base_tree_ids) => {
let new_tree_ids = self.write_merged_trees(base_tree_ids, store)?;
Ok(MergedTreeId::Merge(new_tree_ids.simplify()))
}
}
}
fn write_merged_trees(
self,
mut base_tree_ids: Merge<TreeId>,
store: &Arc<Store>,
) -> Result<Merge<TreeId>, BackendError> {
let num_sides = self
.overrides
.values()
.map(|value| value.num_sides())
.max()
.unwrap_or(0);
base_tree_ids.pad_to(num_sides, store.empty_tree_id());
let mut tree_builders =
base_tree_ids.map(|base_tree_id| TreeBuilder::new(store.clone(), base_tree_id.clone()));
for (path, values) in self.overrides {
match values.into_resolved() {
Ok(value) => {
for builder in tree_builders.iter_mut() {
builder.set_or_remove(path.clone(), value.clone());
}
}
Err(mut values) => {
values.pad_to(num_sides, &None);
for (builder, value) in zip(tree_builders.iter_mut(), values) {
builder.set_or_remove(path.clone(), value);
}
}
}
}
let merge_builder: MergeBuilder<TreeId> = tree_builders
.into_iter()
.map(|builder| builder.write_tree())
.collect();
Ok(merge_builder.build())
}
}