use backend::*;
use patch::*;
use rand;
use record::{InodeUpdate, RecordState};
use std::collections::HashSet;
use std::path::Path;
use {Error, Result};
mod apply;
pub mod find_alive;
mod repair_deleted_context;
use diff;
use fs_representation::{RepoRoot, in_repo_root};
use output;
use output::ConflictingFile;
impl<U: Transaction, R> GenericTxn<U, R> {
pub fn internal_hash(&self, e: &Option<Hash>, internal: PatchId) -> PatchId {
match *e {
Some(Hash::None) => ROOT_PATCH_ID.clone(),
Some(ref h) => self.get_internal(h.as_ref()).unwrap().to_owned(),
None => internal.clone(),
}
}
pub fn internal_key(&self, key: &Key<Option<Hash>>, internal: PatchId) -> Key<PatchId> {
Key {
patch: self.internal_hash(&key.patch, internal),
line: key.line.clone(),
}
}
pub fn internal_key_unwrap(&self, key: &Key<Option<Hash>>) -> Key<PatchId> {
Key {
patch: self
.get_internal(key.patch.as_ref().unwrap().as_ref())
.unwrap()
.to_owned(),
line: key.line.clone(),
}
}
}
impl<'env, T: rand::Rng> MutTxn<'env, T> {
pub fn apply_patches<F, P: output::ToPrefixes>(
&mut self,
diff_algorithm: diff::Algorithm,
branch: &mut Branch,
r: &RepoRoot<impl AsRef<Path>>,
remote_patches: &[(Hash, Patch)],
partial_paths: P,
mut f: F,
) -> Result<Vec<ConflictingFile>>
where
F: FnMut(usize, &Hash),
{
let (pending, local_pending) = {
let mut record = RecordState::new();
self.record(diff_algorithm, &mut record, branch, r, &in_repo_root())?;
let (changes, local) = record.finish();
let mut p = UnsignedPatch::empty();
p.changes = changes
.into_iter()
.flat_map(|x| x.into_iter())
.map(|x| self.globalize_change(x))
.collect();
p.dependencies = self.dependencies(&branch, p.changes.iter());
(p.leave_unsigned(), local)
};
let mut new_patches_count = 0;
for &(ref p, ref patch) in remote_patches.iter() {
debug!("apply_patches: {:?}", p);
self.apply_patches_rec(branch, remote_patches, p, patch, &mut new_patches_count)?;
f(new_patches_count, p);
}
debug!("{} patches applied", new_patches_count);
if new_patches_count > 0 {
let partial_paths = partial_paths.to_prefixes(self, &branch);
self.output_changes_file(&branch, r)?;
debug!("output_repository");
self.output_partials(branch.name.as_str(), &partial_paths)?;
self.output_repository(branch, r, &partial_paths, &pending, &local_pending)
} else {
debug!("finished apply_patches");
Ok(Vec::new())
}
}
pub fn apply_patches_rec(
&mut self,
branch: &mut Branch,
patches: &[(Hash, Patch)],
patch_hash: &Hash,
patch: &Patch,
new_patches_count: &mut usize,
) -> Result<()> {
let internal = {
if let Some(internal) = self.get_internal(patch_hash.as_ref()) {
if self.get_patch(&branch.patches, internal).is_some() {
debug!(
"get_patch returned {:?}",
self.get_patch(&branch.patches, internal)
);
None
} else {
Some(internal.to_owned())
}
} else {
let internal = self.new_internal(patch_hash.as_ref());
Some(internal)
}
};
if let Some(internal) = internal {
info!(
"Now applying patch {:?} {:?} to branch {:?}",
patch.name, patch_hash, branch
);
if patch.dependencies().is_empty() {
info!("Patch {:?} has no dependencies", patch_hash);
}
for dep in patch.dependencies().iter() {
info!("Applying dependency {:?}", dep);
info!("dep hash {:?}", dep.to_base58());
let is_applied = {
if let Some(dep_internal) = self.get_internal(dep.as_ref()) {
self.get_patch(&branch.patches, dep_internal).is_some()
} else {
false
}
};
if !is_applied {
info!("Not applied");
if let Some(&(_, ref patch)) = patches.iter().find(|&&(ref a, _)| a == dep) {
self.apply_patches_rec(branch, patches, &dep, patch, new_patches_count)?;
} else {
error!("Dependency not found");
return Err(Error::MissingDependency(dep.to_owned()));
}
} else {
info!("Already applied");
}
let dep_internal = self.get_internal(dep.as_ref()).unwrap().to_owned();
self.put_revdep(dep_internal, internal)?;
self.put_dep(internal, dep_internal)?;
}
self.register_patch(internal, patch_hash, patch)?;
let now = branch.apply_counter;
branch.apply_counter += 1;
self.apply(branch, &patch, internal, now)?;
*new_patches_count += 1;
Ok(())
} else {
info!("Patch {:?} has already been applied", patch_hash);
Ok(())
}
}
pub fn apply_local_patch(
&mut self,
branch: &mut Branch,
working_copy: &RepoRoot<impl AsRef<Path>>,
hash: &Hash,
patch: &Patch,
inode_updates: &HashSet<InodeUpdate>,
is_pending: bool,
) -> Result<PatchId> {
info!("registering a patch with {} changes", patch.changes().len());
info!("dependencies: {:?}", patch.dependencies());
let internal: PatchId = self.new_internal(hash.as_ref());
for dep in patch.dependencies().iter() {
let dep_internal = self.get_internal(dep.as_ref()).unwrap().to_owned();
self.put_revdep(dep_internal, internal)?;
self.put_dep(internal, dep_internal)?;
}
self.register_patch(internal, hash, patch)?;
info!("Applying local patch");
let now = branch.apply_counter;
self.apply(branch, &patch, internal, now)?;
debug!("synchronizing tree: {:?}", inode_updates);
for update in inode_updates.iter() {
self.update_inode(&branch, internal, update)?;
}
debug!("committing branch");
if !is_pending {
debug!("not pending, adding to changes");
branch.apply_counter += 1;
self.output_changes_file(&branch, working_copy)?;
}
trace!("done apply_local_patch");
Ok(internal)
}
fn register_patch(&mut self, internal: PatchId, hash: &Hash, patch: &Patch) -> Result<()> {
self.put_external(internal, hash.as_ref())?;
self.put_internal(hash.as_ref(), internal)?;
for hunk in patch.changes() {
let inode = match *hunk {
Change::NewNodes { ref inode, .. } => inode,
Change::NewEdges { ref inode, .. } => inode,
};
let inode = self.internal_key(inode, internal);
self.put_touched_file(inode, internal)?;
}
Ok(())
}
fn update_inode(
&mut self,
branch: &Branch,
internal: PatchId,
update: &InodeUpdate,
) -> Result<()> {
match *update {
InodeUpdate::Add {
ref line,
ref meta,
inode,
} => {
let key = FileHeader {
metadata: *meta,
status: FileStatus::Ok,
key: Key {
patch: internal.clone(),
line: line.clone(),
},
};
if self.get_nodes(&branch, key.key, None).is_some() {
debug!("it's in here!: {:?} {:?}", key, inode);
self.replace_inodes(inode, key)?;
self.replace_revinodes(key.key, inode)?;
}
}
InodeUpdate::Deleted { inode } => {
debug!("deleted: {:?}", inode);
let header = self.get_inodes(inode).unwrap().clone();
debug!("deleted header: {:?}", header);
let flag =
EdgeFlags::PARENT_EDGE | EdgeFlags::FOLDER_EDGE | EdgeFlags::DELETED_EDGE;
if self
.iter_adjacent(&branch, header.key, flag, flag)
.any(|v| v.introduced_by == internal)
{
self.del_inodes(inode, Some(header))?;
self.del_revinodes(header.key, Some(inode))?;
if let Some(parent) = self.get_revtree(inode).map(|x| x.to_owned()) {
let parent = parent.as_file_id();
self.del_tree(&parent, None)?;
self.del_revtree(inode, None)?;
}
}
}
InodeUpdate::Moved { inode, metadata } => {
debug!("moved: {:?}", inode);
let mut header = self.get_inodes(inode).unwrap().clone();
debug!("moved header: {:?}", header);
let flag = EdgeFlags::PARENT_EDGE | EdgeFlags::FOLDER_EDGE;
if self
.iter_adjacent(&branch, header.key, flag, flag)
.any(|v| v.introduced_by == internal)
{
header.status = FileStatus::Ok;
header.metadata = metadata;
self.replace_inodes(inode, header)?;
self.replace_revinodes(header.key, inode)?;
}
}
}
Ok(())
}
}